New upstream version 1.0.0
Stephen Gelman
4 years ago
0 | MIT License | |
1 | ||
2 | Copyright (c) 2019 Stephen Gelman | |
3 | ||
4 | Permission is hereby granted, free of charge, to any person obtaining a copy | |
5 | of this software and associated documentation files (the "Software"), to deal | |
6 | in the Software without restriction, including without limitation the rights | |
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
8 | copies of the Software, and to permit persons to whom the Software is | |
9 | furnished to do so, subject to the following conditions: | |
10 | ||
11 | The above copyright notice and this permission notice shall be included in all | |
12 | copies or substantial portions of the Software. | |
13 | ||
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
20 | SOFTWARE. |
0 | # cookiejarparser | |
1 | ||
2 | cookiejarparser is a Go library that parses a curl (netscape) cookiejar file into a Go http.CookieJar. | |
3 | ||
4 | ## Usage | |
5 | ||
6 | Assuming you have a netscape/curl style cookie jar made with something like: | |
7 | ``` | |
8 | $ curl -c cookies.txt -v https://github.com | |
9 | ``` | |
10 | That cookiejar can be used when making a web request using the following code: | |
11 | ```golang | |
12 | package main | |
13 | ||
14 | import ( | |
15 | "fmt" | |
16 | "io/ioutil" | |
17 | "log" | |
18 | "net/http" | |
19 | ||
20 | "github.com/ssgelm/cookiejarparser" | |
21 | ) | |
22 | ||
23 | func main() { | |
24 | cookies, err := cookiejarparser.LoadCookieJarFile("cookies.txt") | |
25 | if err != nil { | |
26 | log.Fatal(err) | |
27 | } | |
28 | ||
29 | client := &http.Client{ | |
30 | Jar: cookies, | |
31 | } | |
32 | resp, err := client.Get("https://github.com") | |
33 | if err != nil { | |
34 | log.Fatal(err) | |
35 | } | |
36 | defer resp.Body.Close() | |
37 | respData, err := ioutil.ReadAll(resp.Body) | |
38 | fmt.Println(string(respData)) | |
39 | } | |
40 | ``` | |
41 | ||
42 | ## License | |
43 | ||
44 | MIT |
0 | package cookiejarparser | |
1 | ||
2 | import ( | |
3 | "bufio" | |
4 | "fmt" | |
5 | "net/http" | |
6 | "net/http/cookiejar" | |
7 | "net/url" | |
8 | "os" | |
9 | "strconv" | |
10 | "strings" | |
11 | "time" | |
12 | ||
13 | "golang.org/x/net/publicsuffix" | |
14 | ) | |
15 | ||
16 | const httpOnlyPrefix = "#HttpOnly_" | |
17 | ||
18 | func parseCookieLine(cookieLine string, lineNum int) (*http.Cookie, error) { | |
19 | var err error | |
20 | cookieLineHttpOnly := false | |
21 | if strings.HasPrefix(cookieLine, httpOnlyPrefix) { | |
22 | cookieLineHttpOnly = true | |
23 | cookieLine = strings.TrimPrefix(cookieLine, httpOnlyPrefix) | |
24 | } | |
25 | ||
26 | if strings.HasPrefix(cookieLine, "#") || cookieLine == "" { | |
27 | return nil, nil | |
28 | } | |
29 | ||
30 | cookieFields := strings.Split(cookieLine, "\t") | |
31 | ||
32 | if len(cookieFields) < 6 || len(cookieFields) > 7 { | |
33 | return nil, fmt.Errorf("incorrect number of fields in line %d. Expected 6 or 7, got %d.", lineNum, len(cookieFields)) | |
34 | } | |
35 | ||
36 | for i, v := range cookieFields { | |
37 | cookieFields[i] = strings.TrimSpace(v) | |
38 | } | |
39 | ||
40 | cookie := &http.Cookie{ | |
41 | Domain: cookieFields[0], | |
42 | Path: cookieFields[2], | |
43 | Name: cookieFields[5], | |
44 | HttpOnly: cookieLineHttpOnly, | |
45 | } | |
46 | cookie.Secure, err = strconv.ParseBool(cookieFields[3]) | |
47 | if err != nil { | |
48 | return nil, err | |
49 | } | |
50 | expiresInt, err := strconv.ParseInt(cookieFields[4], 10, 64) | |
51 | if err != nil { | |
52 | return nil, err | |
53 | } | |
54 | if expiresInt > 0 { | |
55 | cookie.Expires = time.Unix(expiresInt, 0) | |
56 | } | |
57 | ||
58 | if len(cookieFields) == 7 { | |
59 | cookie.Value = cookieFields[6] | |
60 | } | |
61 | ||
62 | return cookie, nil | |
63 | } | |
64 | ||
65 | // LoadCookieJarFile takes a path to a curl (netscape) cookie jar file and crates a go http.CookieJar with the contents | |
66 | func LoadCookieJarFile(path string) (http.CookieJar, error) { | |
67 | jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) | |
68 | if err != nil { | |
69 | return nil, err | |
70 | } | |
71 | ||
72 | file, err := os.Open(path) | |
73 | if err != nil { | |
74 | return nil, err | |
75 | } | |
76 | defer file.Close() | |
77 | ||
78 | lineNum := 1 | |
79 | scanner := bufio.NewScanner(file) | |
80 | for scanner.Scan() { | |
81 | cookieLine := scanner.Text() | |
82 | cookie, err := parseCookieLine(cookieLine, lineNum) | |
83 | if cookie == nil { | |
84 | continue | |
85 | } | |
86 | if err != nil { | |
87 | return nil, err | |
88 | } | |
89 | ||
90 | var cookieScheme string | |
91 | if cookie.Secure { | |
92 | cookieScheme = "https" | |
93 | } else { | |
94 | cookieScheme = "http" | |
95 | } | |
96 | cookieUrl := &url.URL{ | |
97 | Scheme: cookieScheme, | |
98 | Host: cookie.Domain, | |
99 | } | |
100 | ||
101 | cookies := jar.Cookies(cookieUrl) | |
102 | cookies = append(cookies, cookie) | |
103 | jar.SetCookies(cookieUrl, cookies) | |
104 | ||
105 | lineNum++ | |
106 | } | |
107 | ||
108 | return jar, nil | |
109 | } |
0 | package cookiejarparser | |
1 | ||
2 | import ( | |
3 | "encoding/json" | |
4 | "net/http" | |
5 | "net/http/cookiejar" | |
6 | "net/url" | |
7 | "reflect" | |
8 | "testing" | |
9 | ||
10 | "golang.org/x/net/publicsuffix" | |
11 | ) | |
12 | ||
13 | func TestParseCookieLine(t *testing.T) { | |
14 | // normal | |
15 | cookie, err := parseCookieLine("example.com FALSE / FALSE 0 test_cookie 1", 1) | |
16 | sampleCookie := &http.Cookie{ | |
17 | Domain: "example.com", | |
18 | Path: "/", | |
19 | Name: "test_cookie", | |
20 | Value: "1", | |
21 | HttpOnly: false, | |
22 | Secure: false, | |
23 | } | |
24 | ||
25 | if !reflect.DeepEqual(cookie, sampleCookie) || err != nil { | |
26 | c1, _ := json.Marshal(cookie) | |
27 | c2, _ := json.Marshal(sampleCookie) | |
28 | ||
29 | t.Errorf("Parsing normal cookie failed. Expected:\n cookie: %s err: nil,\ngot:\n cookie: %s err: %s", c2, c1, err) | |
30 | } | |
31 | // httponly | |
32 | cookieHttp, err := parseCookieLine("#HttpOnly_example.com FALSE / FALSE 0 test_cookie_httponly 1", 1) | |
33 | sampleCookieHttp := &http.Cookie{ | |
34 | Domain: "example.com", | |
35 | Path: "/", | |
36 | Name: "test_cookie_httponly", | |
37 | Value: "1", | |
38 | HttpOnly: true, | |
39 | Secure: false, | |
40 | } | |
41 | ||
42 | if !reflect.DeepEqual(cookieHttp, sampleCookieHttp) || err != nil { | |
43 | c1, _ := json.Marshal(cookieHttp) | |
44 | c2, _ := json.Marshal(sampleCookieHttp) | |
45 | ||
46 | t.Errorf("Parsing httpOnly cookie failed. Expected:\n cookie: %s err: nil,\ngot:\n cookie: %s err: %s", c2, c1, err) | |
47 | } | |
48 | ||
49 | // comment | |
50 | cookieComment, err := parseCookieLine("# This is a comment", 1) | |
51 | if cookieComment != nil || err != nil { | |
52 | t.Errorf("Parsing comment failed. Expected cookie: nil err: nil, got cookie: %s err: %s", cookie, err) | |
53 | } | |
54 | ||
55 | cookieBlank, err := parseCookieLine("", 1) | |
56 | if cookieBlank != nil || err != nil { | |
57 | t.Errorf("Parsing blank line failed. Expected cookie: nil err: nil, got cookie: %s err: %s", cookie, err) | |
58 | } | |
59 | } | |
60 | ||
61 | func TestLoadCookieJarFile(t *testing.T) { | |
62 | exampleURL := &url.URL{ | |
63 | Scheme: "http", | |
64 | Host: "example.com", | |
65 | } | |
66 | sampleCookies := []*http.Cookie{ | |
67 | { | |
68 | Domain: "example.com", | |
69 | Path: "/", | |
70 | Name: "test_cookie", | |
71 | Value: "1", | |
72 | HttpOnly: false, | |
73 | Secure: false, | |
74 | }, | |
75 | { | |
76 | Domain: "example.com", | |
77 | Path: "/", | |
78 | Name: "test_cookie_httponly", | |
79 | Value: "1", | |
80 | HttpOnly: true, | |
81 | Secure: false, | |
82 | }, | |
83 | } | |
84 | sampleCookieJar, _ := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List}) | |
85 | sampleCookieJar.SetCookies(exampleURL, sampleCookies) | |
86 | ||
87 | cookieJar, err := LoadCookieJarFile("data/cookies.txt") | |
88 | ||
89 | c1, _ := json.Marshal(cookieJar.Cookies(exampleURL)) | |
90 | c2, _ := json.Marshal(sampleCookieJar.Cookies(exampleURL)) | |
91 | ||
92 | if !reflect.DeepEqual(c1, c2) || err != nil { | |
93 | t.Errorf("Cookie jar creation failed. Expected:\n cookieJar: %s err: nil,\ngot:\n cookieJar: %s err: %s", c2, c1, err) | |
94 | } | |
95 | } |
0 | # Test cookie file | |
1 | ||
2 | example.com FALSE / FALSE 0 test_cookie 1 | |
3 | #HttpOnly_example.com FALSE / FALSE 0 test_cookie_httponly 1 |
0 | module github.com/ssgelm/cookiejarparser | |
1 | ||
2 | go 1.13 | |
3 | ||
4 | require golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a |
0 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | |
1 | golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a h1:Yu34BogBivvmu7SAzHHaB9nZWH5D1C+z3F1jyIaYZSQ= | |
2 | golang.org/x/net v0.0.0-20191027093000-83d349e8ac1a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | |
3 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | |
4 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= |