Skip to content

Commit 11ac4d1

Browse files
Merge pull request #7 from peterhellberg/test/add-fuzz-testing
test: Add fuzz testing
2 parents 8ba0eda + 25d1504 commit 11ac4d1

File tree

5 files changed

+88
-35
lines changed

5 files changed

+88
-35
lines changed

doc.go

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,38 @@
11
/*
2-
32
Package link parses Link headers used for pagination, as defined in RFC 5988
43
5-
Installation
4+
# Installation
65
76
Just go get the package:
87
9-
go get -u github.com/peterhellberg/link
8+
go get -u github.com/peterhellberg/link
109
11-
Usage
10+
# Usage
1211
1312
A small usage example
1413
15-
package main
16-
17-
import (
18-
"fmt"
19-
"net/http"
20-
21-
"github.com/peterhellberg/link"
22-
)
23-
24-
func main() {
25-
for _, l := range link.Parse(`<https://example.com/?page=2>; rel="next"; foo="bar"`) {
26-
fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
27-
// URI: "https://example.com/?page=2", Rel: "next", Extra: map[foo:bar]
28-
}
29-
30-
if resp, err := http.Get("https://api.github.com/search/code?q=Println+user:golang"); err == nil {
31-
for _, l := range link.ParseResponse(resp) {
32-
fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
33-
// URI: "https://api.github.com/search/code?q=Println+user%3Agolang&page=2", Rel: "next", Extra: map[]
34-
// URI: "https://api.github.com/search/code?q=Println+user%3Agolang&page=34", Rel: "last", Extra: map[]
35-
}
36-
}
37-
}
38-
14+
package main
15+
16+
import (
17+
"fmt"
18+
"net/http"
19+
20+
"github.com/peterhellberg/link"
21+
)
22+
23+
func main() {
24+
for _, l := range link.Parse(`<https://example.com/?page=2>; rel="next"; foo="bar"`) {
25+
fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
26+
// URI: "https://example.com/?page=2", Rel: "next", Extra: map[foo:bar]
27+
}
28+
29+
if resp, err := http.Get("https://api.github.com/search/code?q=Println+user:golang"); err == nil {
30+
for _, l := range link.ParseResponse(resp) {
31+
fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
32+
// URI: "https://api.github.com/search/code?q=Println+user%3Agolang&page=2", Rel: "next", Extra: map[]
33+
// URI: "https://api.github.com/search/code?q=Println+user%3Agolang&page=34", Rel: "last", Extra: map[]
34+
}
35+
}
36+
}
3937
*/
4038
package link

link.go

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ var (
1616
valRegexp = regexp.MustCompile(`"+([^"]+)"+`)
1717
)
1818

19-
// Group returned by Parse, contains multiple links indexed by "rel"
19+
// Group returned by Parse, contains multiple links indexed by "rel".
2020
type Group map[string]*Link
2121

2222
// Link contains a Link item with URI, Rel, and other non-URI components in Extra.
@@ -26,12 +26,12 @@ type Link struct {
2626
Extra map[string]string
2727
}
2828

29-
// String returns the URI
29+
// String returns the URI.
3030
func (l *Link) String() string {
3131
return l.URI
3232
}
3333

34-
// ParseRequest parses the provided *http.Request into a Group
34+
// ParseRequest parses the provided *http.Request into a Group.
3535
func ParseRequest(req *http.Request) Group {
3636
if req == nil {
3737
return nil
@@ -40,7 +40,7 @@ func ParseRequest(req *http.Request) Group {
4040
return ParseHeader(req.Header)
4141
}
4242

43-
// ParseResponse parses the provided *http.Response into a Group
43+
// ParseResponse parses the provided *http.Response into a Group.
4444
func ParseResponse(resp *http.Response) Group {
4545
if resp == nil {
4646
return nil
@@ -49,7 +49,7 @@ func ParseResponse(resp *http.Response) Group {
4949
return ParseHeader(resp.Header)
5050
}
5151

52-
// ParseHeader retrieves the Link header from the provided http.Header and parses it into a Group
52+
// ParseHeader retrieves the Link header from the provided http.Header and parses it into a Group.
5353
func ParseHeader(h http.Header) Group {
5454
if headers, found := h["Link"]; found {
5555
return Parse(strings.Join(headers, ", "))
@@ -58,7 +58,7 @@ func ParseHeader(h http.Header) Group {
5858
return nil
5959
}
6060

61-
// Parse parses the provided string into a Group
61+
// Parse parses the provided string into a Group.
6262
func Parse(s string) Group {
6363
if s == "" {
6464
return nil
@@ -82,8 +82,18 @@ func Parse(s string) Group {
8282
for _, extra := range semiRegexp.Split(pieces[2], -1) {
8383
vals := equalRegexp.Split(extra, -1)
8484

85+
if len(vals) != 2 {
86+
continue
87+
}
88+
8589
key := keyRegexp.FindString(vals[0])
86-
val := valRegexp.FindStringSubmatch(vals[1])[1]
90+
vsm := valRegexp.FindStringSubmatch(vals[1])
91+
92+
if len(vsm) != 2 {
93+
continue
94+
}
95+
96+
val := vsm[1]
8797

8898
if key == "rel" {
8999
vals := strings.Split(val, " ")

link_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
)
88

99
func TestLinkString(t *testing.T) {
10+
t.Parallel()
11+
1012
l := Parse(`<https://example.com/?page=2>; rel="next"; title="foo"`)["next"]
1113

1214
if got, want := l.String(), "https://example.com/?page=2"; got != want {
@@ -15,6 +17,8 @@ func TestLinkString(t *testing.T) {
1517
}
1618

1719
func TestParseRequest(t *testing.T) {
20+
t.Parallel()
21+
1822
req, _ := http.NewRequest("GET", "", nil)
1923
req.Header.Set("Link", `<https://example.com/?page=2>; rel="next"`)
2024

@@ -42,6 +46,8 @@ func TestParseRequest(t *testing.T) {
4246
}
4347

4448
func TestParseResponse(t *testing.T) {
49+
t.Parallel()
50+
4551
resp := &http.Response{Header: http.Header{}}
4652
resp.Header.Set("Link", `<https://example.com/?page=2>; rel="next"`)
4753

@@ -69,6 +75,8 @@ func TestParseResponse(t *testing.T) {
6975
}
7076

7177
func TestParseHeader_single(t *testing.T) {
78+
t.Parallel()
79+
7280
h := http.Header{}
7381
h.Set("Link", `<https://example.com/?page=2>; rel="next"`)
7482

@@ -92,6 +100,8 @@ func TestParseHeader_single(t *testing.T) {
92100
}
93101

94102
func TestParseHeader_multiple(t *testing.T) {
103+
t.Parallel()
104+
95105
h := http.Header{}
96106
h.Add("Link", `<https://example.com/?page=2>; rel="next",<https://example.com/?page=34>; rel="last"`)
97107

@@ -127,6 +137,8 @@ func TestParseHeader_multiple(t *testing.T) {
127137
}
128138

129139
func TestParseHeader_multiple_headers(t *testing.T) {
140+
t.Parallel()
141+
130142
h := http.Header{}
131143
h.Add("Link", `<https://example.com/?page=2>; rel="next",<https://example.com/?page=34>; rel="last"`)
132144
h.Add("Link", `<https://example.com/?page=foo>; rel="foo",<https://example.com/?page=bar>; rel="bar"`)
@@ -163,6 +175,8 @@ func TestParseHeader_multiple_headers(t *testing.T) {
163175
}
164176

165177
func TestParseHeader_extra(t *testing.T) {
178+
t.Parallel()
179+
166180
h := http.Header{}
167181
h.Add("Link", `<https://example.com/?page=2>; rel="next"; title="foo"`)
168182

@@ -182,24 +196,32 @@ func TestParseHeader_extra(t *testing.T) {
182196
}
183197

184198
func TestParseHeader_noLink(t *testing.T) {
199+
t.Parallel()
200+
185201
if ParseHeader(http.Header{}) != nil {
186202
t.Fatalf(`Parse(http.Header{}) != nil`)
187203
}
188204
}
189205

190206
func TestParseHeader_nilHeader(t *testing.T) {
207+
t.Parallel()
208+
191209
if ParseHeader(nil) != nil {
192210
t.Fatalf(`ParseHeader(nil) != nil`)
193211
}
194212
}
195213

196214
func TestParse_emptyString(t *testing.T) {
215+
t.Parallel()
216+
197217
if Parse("") != nil {
198218
t.Fatalf(`Parse("") != nil`)
199219
}
200220
}
201221

202222
func TestParse_valuesWithComma(t *testing.T) {
223+
t.Parallel()
224+
203225
g := Parse(`<//www.w3.org/wiki/LinkHeader>; rel="original latest-version",<//www.w3.org/wiki/Special:TimeGate/LinkHeader>; rel="timegate",<//www.w3.org/wiki/Special:TimeMap/LinkHeader>; rel="timemap"; type="application/link-format"; from="Mon, 03 Sep 2007 14:52:48 GMT"; until="Tue, 16 Jun 2015 22:59:23 GMT",<//www.w3.org/wiki/index.php?title=LinkHeader&oldid=10152>; rel="first memento"; datetime="Mon, 03 Sep 2007 14:52:48 GMT",<//www.w3.org/wiki/index.php?title=LinkHeader&oldid=84697>; rel="last memento"; datetime="Tue, 16 Jun 2015 22:59:23 GMT"`)
204226

205227
if got, want := len(g), 5; got != want {
@@ -216,6 +238,8 @@ func TestParse_valuesWithComma(t *testing.T) {
216238
}
217239

218240
func TestParse_rfc5988Example1(t *testing.T) {
241+
t.Parallel()
242+
219243
g := Parse(`<http://example.com/TheBook/chapter2>; rel="previous"; title="previous chapter"`)
220244

221245
if got, want := len(g), 1; got != want {
@@ -232,6 +256,8 @@ func TestParse_rfc5988Example1(t *testing.T) {
232256
}
233257

234258
func TestParse_rfc5988Example2(t *testing.T) {
259+
t.Parallel()
260+
235261
g := Parse(`</>; rel="http://example.net/foo"`)
236262

237263
if got, want := len(g), 1; got != want {
@@ -250,11 +276,15 @@ func TestParse_rfc5988Example2(t *testing.T) {
250276
}
251277

252278
func TestParse_rfc5988Example3(t *testing.T) {
279+
t.Parallel()
280+
253281
// Extended notation is not supported yet
254282
// g := Parse(`</TheBook/chapter2>; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, </TheBook/chapter4>; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel`)
255283
}
256284

257285
func TestParse_rfc5988Example4(t *testing.T) {
286+
t.Parallel()
287+
258288
// Extension relation types are ignored for now
259289
g := Parse(`<http://example.org/>; rel="start http://example.net/relation/other"`)
260290

@@ -272,6 +302,8 @@ func TestParse_rfc5988Example4(t *testing.T) {
272302
}
273303

274304
func TestParse_fuzzCrashers(t *testing.T) {
305+
t.Parallel()
306+
275307
Parse("0")
276308
}
277309

@@ -281,3 +313,12 @@ func ExampleParse() {
281313
fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
282314
// Output: URI: "https://example.com/?page=2", Rel: "next", Extra: map[title:foo]
283315
}
316+
317+
func FuzzParse(f *testing.F) {
318+
f.Add(`<https://example.com/?page=2>; rel="next"; title="foo"`)
319+
f.Add(`<http://example.org/>; rel="start http://example.net/relation/other"`)
320+
321+
f.Fuzz(func(t *testing.T, s string) {
322+
Parse(s)
323+
})
324+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
go test fuzz v1
2+
string("<0>;=")
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
go test fuzz v1
2+
string("<0>;0")

0 commit comments

Comments
 (0)