Skip to content

Commit 2b804ae

Browse files
committed
Replace testify/assert with standard testing library and add cookie option support
- Remove testify/assert dependency from getoken_test.go, use standard testing.T methods - Remove composite literal type declarations (TestArgs{} -> {}) - Add -b/--cookie option support for passing cookies via name=value or cookie file - Implement cookie file parsing with Netscape format support - Add hasHeader helper to check for existing headers - Add createCookieHeader method to build Cookie header from -b options
1 parent 5209915 commit 2b804ae

File tree

3 files changed

+180
-10
lines changed

3 files changed

+180
-10
lines changed

getoken_test.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package pcurl
22

33
import (
4-
"github.com/stretchr/testify/assert"
54
"testing"
65
)
76

@@ -12,16 +11,18 @@ func Test_GetArgsTokenFail(t *testing.T) {
1211
}
1312

1413
for _, v := range []TestArgs{
15-
TestArgs{
14+
{
1615
in: `'hello`,
1716
},
18-
TestArgs{
17+
{
1918
in: `"hello`,
2019
},
2120
} {
2221

2322
_, err := GetArgsToken(v.in)
24-
assert.Error(t, err)
23+
if err == nil {
24+
t.Errorf("expected error, got nil, input=%q", v.in)
25+
}
2526
}
2627
}
2728

@@ -33,26 +34,38 @@ func Test_GetArgsToken(t *testing.T) {
3334
}
3435

3536
for _, v := range []TestArgs{
36-
TestArgs{
37+
{
3738
in: `curl -XGET "http://192.168.6.100:9200/eval-log/_search" -H 'Content-Type: application/json' -d'{ "query": { "match": { "level": "error" } }}'`,
3839
need: []string{`curl`, `-XGET`, "http://192.168.6.100:9200/eval-log/_search", "-H", `Content-Type: application/json`, `-d{ "query": { "match": { "level": "error" } }}`},
3940
},
40-
TestArgs{
41+
{
4142
in: `curl --location --request DELETE '192.168.5.213:10010/delete/rule?appkey=xx' --header 'Content-Type: text/plain' --data-raw '{"type":"region","region":"bj","business":"asr","protocol":"private","connect":416}'`,
4243
need: []string{`curl`, `--location`, `--request`, `DELETE`, `192.168.5.213:10010/delete/rule?appkey=xx`, `--header`, `Content-Type: text/plain`, `--data-raw`, `{"type":"region","region":"bj","business":"asr","protocol":"private","connect":416}`},
4344
},
44-
TestArgs{
45+
{
4546
in: `'{"s":"{\"s\":\"S\"}"}'`,
4647
need: []string{`{"s":"{\"s\":\"S\"}"}`},
4748
},
48-
TestArgs{
49+
{
4950
in: `curl 'http://xxx.cc/admin/index.php?act=admin' -H 'Connection: keep-alive' -H 'Cache-Control: max-age=0' -H 'Upgrade-Insecure-Requests: 1' -H 'User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/80.0.3987.87 Chrome/80.0.3987.87 Safari/537.36' -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9' -H 'Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7' -H 'Cookie: username=admin; token=b7ea3ec643e4ea4871dfe515c559d28bc0d23b6d9d6b22daf206f1de9aff13aeaa51591323199; addinfo=%7B%22chkadmin%22%3A1%2C%22chkarticle%22%3A1%2C%22levelname%22%3A%22%5Cu7ba1%5Cu7406%5Cu5458%22%2C%22userid%22%3A%221%22%2C%22useralias%22%3A%22admin%22%7D; timezone=8; Hm_lvt_12d9f8f1740b76bb88c6691ea1672d8b=1589004902,1589265192,1589341266,1589717172; Hm_lpvt_12d9f8f1740b76bb88c6691ea1672d8b=1589719525'`,
5051
need: []string{`curl`, `http://xxx.cc/admin/index.php?act=admin`, `-H`, `Connection: keep-alive`, `-H`, `Cache-Control: max-age=0`, `-H`, `Upgrade-Insecure-Requests: 1`, `-H`, `User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/80.0.3987.87 Chrome/80.0.3987.87 Safari/537.36`, `-H`, `Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3;q=0.9`, `-H`, `Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7`, `-H`, `Cookie: username=admin; token=b7ea3ec643e4ea4871dfe515c559d28bc0d23b6d9d6b22daf206f1de9aff13aeaa51591323199; addinfo=%7B%22chkadmin%22%3A1%2C%22chkarticle%22%3A1%2C%22levelname%22%3A%22%5Cu7ba1%5Cu7406%5Cu5458%22%2C%22userid%22%3A%221%22%2C%22useralias%22%3A%22admin%22%7D; timezone=8; Hm_lvt_12d9f8f1740b76bb88c6691ea1672d8b=1589004902,1589265192,1589341266,1589717172; Hm_lpvt_12d9f8f1740b76bb88c6691ea1672d8b=1589719525`},
5152
},
5253
} {
5354

5455
got, err := GetArgsToken(v.in)
55-
assert.NoError(t, err)
56-
assert.Equal(t, v.need, got)
56+
if err != nil {
57+
t.Fatalf("unexpected error: %v, input=%q", err, v.in)
58+
}
59+
60+
if len(got) != len(v.need) {
61+
t.Errorf("len not equal, got=%d, want=%d, input=%q, gotSlice=%v, wantSlice=%v", len(got), len(v.need), v.in, got, v.need)
62+
continue
63+
}
64+
65+
for i := range v.need {
66+
if got[i] != v.need[i] {
67+
t.Errorf("index %d not equal, got=%q, want=%q, input=%q", i, got[i], v.need[i], v.in)
68+
}
69+
}
5770
}
5871
}

pcurl.go

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package pcurl
44

55
import (
6+
"bufio"
67
"errors"
78
"net/http"
89
"net/url"
@@ -19,6 +20,7 @@ type Curl struct {
1920
Method string `clop:"-X; --request" usage:"Specify request command to use"`
2021
Get bool `clop:"-G; --get" usage:"Put the post data in the URL and use GET"`
2122
Header []string `clop:"-H; --header" usage:"Pass custom header(s) to server"`
23+
Cookie []string `clop:"-b; --cookie" usage:"Pass data to Cookie header or read from cookie file"`
2224
Data string `clop:"-d; --data" usage:"HTTP POST data"`
2325
DataRaw string `clop:"--data-raw" usage:"HTTP POST data, '@' allowed"`
2426
Form []string `clop:"-F; --form" usage:"Specify multipart MIME data"`
@@ -90,6 +92,72 @@ func (c *Curl) createHeader() []string {
9092
return header
9193
}
9294

95+
func (c *Curl) hasHeader(header []string, name string) bool {
96+
for i := 0; i+1 < len(header); i += 2 {
97+
if strings.EqualFold(header[i], name) {
98+
return true
99+
}
100+
}
101+
102+
return false
103+
}
104+
105+
func (c *Curl) createCookieHeader() ([]string, error) {
106+
if len(c.Cookie) == 0 {
107+
return nil, nil
108+
}
109+
110+
cookieParts := make([]string, 0, len(c.Cookie))
111+
112+
for _, v := range c.Cookie {
113+
// 如果包含 '=',优先认为是 name=value 形式
114+
if strings.Contains(v, "=") {
115+
cookieParts = append(cookieParts, v)
116+
continue
117+
}
118+
119+
// 否则认为是 cookie 文件路径
120+
fd, err := os.Open(v)
121+
if err != nil {
122+
return nil, err
123+
}
124+
125+
scanner := bufio.NewScanner(fd)
126+
for scanner.Scan() {
127+
line := strings.TrimSpace(scanner.Text())
128+
if len(line) == 0 || strings.HasPrefix(line, "#") {
129+
continue
130+
}
131+
132+
// 兼容 Netscape cookie 文件格式
133+
// <domain> <flag> <path> <secure> <expiration> <name> <value>
134+
fields := strings.Split(line, "\t")
135+
if len(fields) >= 7 {
136+
name := fields[5]
137+
value := fields[6]
138+
cookieParts = append(cookieParts, name+"="+value)
139+
continue
140+
}
141+
142+
// 兜底:如果包含 '=',直接当作 name=value
143+
if strings.Contains(line, "=") {
144+
cookieParts = append(cookieParts, line)
145+
}
146+
}
147+
148+
fd.Close()
149+
if err := scanner.Err(); err != nil {
150+
return nil, err
151+
}
152+
}
153+
154+
if len(cookieParts) == 0 {
155+
return nil, nil
156+
}
157+
158+
return []string{"Cookie", strings.Join(cookieParts, "; ")}, nil
159+
}
160+
93161
func (c *Curl) getBodyEncodeAndObj() (string, any, error) {
94162
name := c.findHighestPriority()
95163
if name == bodyURLEncode {
@@ -233,6 +301,10 @@ func (c *Curl) Request() (req *http.Request, err error) {
233301
c.setMethod()
234302

235303
header := c.createHeader()
304+
cookieHeader, err := c.createCookieHeader()
305+
if err != nil {
306+
return nil, err
307+
}
236308

237309
switch c.findHighestPriority() {
238310
case bodyURLEncode:
@@ -267,6 +339,10 @@ func (c *Curl) Request() (req *http.Request, err error) {
267339
header = append(header, "Accept-Encoding", "deflate, gzip")
268340
}
269341

342+
if len(cookieHeader) > 0 && !c.hasHeader(header, "Cookie") {
343+
header = append(header, cookieHeader...)
344+
}
345+
270346
if len(dataRaw) > 0 {
271347
data = dataRaw
272348
}

pcurl_cookie_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package pcurl
2+
3+
import (
4+
"os"
5+
"path/filepath"
6+
"testing"
7+
8+
"github.com/guonaihong/gout"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
// curl -b / --cookie
13+
func Test_Cookie_Option(t *testing.T) {
14+
type testCase struct {
15+
name string
16+
curlSlice []string
17+
need H
18+
}
19+
20+
// 准备一个临时 cookie 文件(Netscape 格式)
21+
dir := t.TempDir()
22+
cookieFile := filepath.Join(dir, "cookie.txt")
23+
content := "# Netscape HTTP Cookie File\n" +
24+
"example.com\tTRUE\t/\tFALSE\t0\tfoo\tbar\n" +
25+
"example.com\tTRUE\t/\tFALSE\t0\tbaz\tqux\n"
26+
assert.NoError(t, os.WriteFile(cookieFile, []byte(content), 0o644))
27+
28+
for _, c := range []testCase{
29+
{
30+
name: "single name=value",
31+
curlSlice: []string{"curl", "-X", "GET", "-b", "a=1"},
32+
need: H{
33+
"Cookie": "a=1",
34+
},
35+
},
36+
{
37+
name: "multi -b name=value",
38+
curlSlice: []string{"curl", "-X", "GET", "-b", "a=1", "-b", "b=2"},
39+
need: H{
40+
"Cookie": "a=1; b=2",
41+
},
42+
},
43+
{
44+
name: "cookie file",
45+
curlSlice: []string{"curl", "-X", "GET", "-b", cookieFile},
46+
need: H{
47+
"Cookie": "foo=bar; baz=qux",
48+
},
49+
},
50+
{
51+
name: "cookie file and name=value",
52+
curlSlice: []string{"curl", "-X", "GET", "-b", cookieFile, "-b", "c=3"},
53+
need: H{
54+
"Cookie": "foo=bar; baz=qux; c=3",
55+
},
56+
},
57+
{
58+
name: "-H Cookie override -b",
59+
curlSlice: []string{"curl", "-X", "GET", "-b", "a=1", "-H", "Cookie: x=y"},
60+
need: H{
61+
"Cookie": "x=y",
62+
},
63+
},
64+
} {
65+
66+
// 创建测试服务端
67+
ts := createGeneralHeader(c.need, t)
68+
69+
// 解析curl表达式
70+
req, err := ParseSlice(append(c.curlSlice, ts.URL)).Request()
71+
assert.NoError(t, err, c.name)
72+
73+
var got H
74+
code := 0
75+
// 发送请求
76+
err = gout.New().SetRequest(req).Debug(true).Code(&code).BindJSON(&got).Do()
77+
assert.NoError(t, err, c.name)
78+
assert.Equal(t, 200, code, c.name)
79+
assert.Equal(t, c.need, got, c.name)
80+
}
81+
}

0 commit comments

Comments
 (0)