New Upstream Snapshot - golang-github-peterhellberg-link
Ready changes
Summary
Merged new upstream version: 1.2.0 (was: 1.0.0).
Resulting package
Built on 2022-12-14T05:57 (took 7m11s)
The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:
apt install -t fresh-snapshots golang-github-peterhellberg-link-dev
Lintian Result
Diff
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 72f01b6..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,7 +0,0 @@
-language: go
-
-go:
- - 1.6
- - 1.5.3
-
-sudo: false
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..c38db83
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2015-2022 Peter Hellberg https://c7.se
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
+OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
diff --git a/README.md b/README.md
index 6b897ca..0ce2677 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,8 @@
# link
-[![Build Status](https://travis-ci.org/peterhellberg/link.svg?branch=master)](https://travis-ci.org/peterhellberg/link)
-[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/peterhellberg/link)
+[![Build status](https://github.com/peterhellberg/link/actions/workflows/test.yml/badge.svg?branch=master)](https://github.com/peterhellberg/link/actions/workflows/test.yml)
+[![Go Report Card](https://goreportcard.com/badge/github.com/peterhellberg/link)](https://goreportcard.com/report/github.com/peterhellberg/link)
+[![GoDoc](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://pkg.go.dev/github.com/peterhellberg/link)
[![License MIT](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://github.com/peterhellberg/link#license-mit)
Parses **Link** headers used for pagination, as defined in [RFC 5988](https://tools.ietf.org/html/rfc5988).
@@ -59,7 +60,7 @@ func main() {
## License (MIT)
-Copyright (c) 2015-2016 [Peter Hellberg](http://c7.se/)
+Copyright (c) 2015-2022 [Peter Hellberg](https://c7.se)
> Permission is hereby granted, free of charge, to any person obtaining
> a copy of this software and associated documentation files (the
diff --git a/debian/changelog b/debian/changelog
index cbd786e..66de762 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-github-peterhellberg-link (1.2.0-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Wed, 14 Dec 2022 05:52:13 -0000
+
golang-github-peterhellberg-link (1.0.0-6) unstable; urgency=medium
[ Debian Janitor ]
diff --git a/doc.go b/doc.go
index f3a5f17..b35e7a8 100644
--- a/doc.go
+++ b/doc.go
@@ -1,40 +1,38 @@
/*
-
Package link parses Link headers used for pagination, as defined in RFC 5988
-Installation
+# Installation
Just go get the package:
- go get -u github.com/peterhellberg/link
+ go get -u github.com/peterhellberg/link
-Usage
+# Usage
A small usage example
- package main
-
- import (
- "fmt"
- "net/http"
-
- "github.com/peterhellberg/link"
- )
-
- func main() {
- for _, l := range link.Parse(`<https://example.com/?page=2>; rel="next"; foo="bar"`) {
- fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
- // URI: "https://example.com/?page=2", Rel: "next", Extra: map[foo:bar]
- }
-
- if resp, err := http.Get("https://api.github.com/search/code?q=Println+user:golang"); err == nil {
- for _, l := range link.ParseResponse(resp) {
- fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
- // URI: "https://api.github.com/search/code?q=Println+user%3Agolang&page=2", Rel: "next", Extra: map[]
- // URI: "https://api.github.com/search/code?q=Println+user%3Agolang&page=34", Rel: "last", Extra: map[]
- }
- }
- }
-
+ package main
+
+ import (
+ "fmt"
+ "net/http"
+
+ "github.com/peterhellberg/link"
+ )
+
+ func main() {
+ for _, l := range link.Parse(`<https://example.com/?page=2>; rel="next"; foo="bar"`) {
+ fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
+ // URI: "https://example.com/?page=2", Rel: "next", Extra: map[foo:bar]
+ }
+
+ if resp, err := http.Get("https://api.github.com/search/code?q=Println+user:golang"); err == nil {
+ for _, l := range link.ParseResponse(resp) {
+ fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
+ // URI: "https://api.github.com/search/code?q=Println+user%3Agolang&page=2", Rel: "next", Extra: map[]
+ // URI: "https://api.github.com/search/code?q=Println+user%3Agolang&page=34", Rel: "last", Extra: map[]
+ }
+ }
+ }
*/
package link
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..0c5be93
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,3 @@
+module github.com/peterhellberg/link
+
+go 1.17
diff --git a/link.go b/link.go
index 8c2f025..0cdb48e 100644
--- a/link.go
+++ b/link.go
@@ -12,11 +12,11 @@ var (
equalRegexp = regexp.MustCompile(` *= *`)
keyRegexp = regexp.MustCompile(`[a-z*]+`)
linkRegexp = regexp.MustCompile(`\A<(.+)>;(.+)\z`)
- semiRegexp = regexp.MustCompile(`; +`)
+ semiRegexp = regexp.MustCompile(`;\s{0,}`)
valRegexp = regexp.MustCompile(`"+([^"]+)"+`)
)
-// Group returned by Parse, contains multiple links indexed by "rel"
+// Group returned by Parse, contains multiple links indexed by "rel".
type Group map[string]*Link
// Link contains a Link item with URI, Rel, and other non-URI components in Extra.
@@ -26,12 +26,12 @@ type Link struct {
Extra map[string]string
}
-// String returns the URI
+// String returns the URI.
func (l *Link) String() string {
return l.URI
}
-// ParseRequest parses the provided *http.Request into a Group
+// ParseRequest parses the provided *http.Request into a Group.
func ParseRequest(req *http.Request) Group {
if req == nil {
return nil
@@ -40,7 +40,7 @@ func ParseRequest(req *http.Request) Group {
return ParseHeader(req.Header)
}
-// ParseResponse parses the provided *http.Response into a Group
+// ParseResponse parses the provided *http.Response into a Group.
func ParseResponse(resp *http.Response) Group {
if resp == nil {
return nil
@@ -49,7 +49,7 @@ func ParseResponse(resp *http.Response) Group {
return ParseHeader(resp.Header)
}
-// ParseHeader retrieves the Link header from the provided http.Header and parses it into a Group
+// ParseHeader retrieves the Link header from the provided http.Header and parses it into a Group.
func ParseHeader(h http.Header) Group {
if headers, found := h["Link"]; found {
return Parse(strings.Join(headers, ", "))
@@ -58,7 +58,7 @@ func ParseHeader(h http.Header) Group {
return nil
}
-// Parse parses the provided string into a Group
+// Parse parses the provided string into a Group.
func Parse(s string) Group {
if s == "" {
return nil
@@ -82,8 +82,17 @@ func Parse(s string) Group {
for _, extra := range semiRegexp.Split(pieces[2], -1) {
vals := equalRegexp.Split(extra, -1)
+ if len(vals) != 2 {
+ continue
+ }
+
+ val := strings.TrimSpace(vals[1])
key := keyRegexp.FindString(vals[0])
- val := valRegexp.FindStringSubmatch(vals[1])[1]
+ vsm := valRegexp.FindStringSubmatch(vals[1])
+
+ if len(vsm) == 2 {
+ val = vsm[1]
+ }
if key == "rel" {
vals := strings.Split(val, " ")
diff --git a/link_test.go b/link_test.go
index a4a5e17..a31df10 100644
--- a/link_test.go
+++ b/link_test.go
@@ -7,6 +7,8 @@ import (
)
func TestLinkString(t *testing.T) {
+ t.Parallel()
+
l := Parse(`<https://example.com/?page=2>; rel="next"; title="foo"`)["next"]
if got, want := l.String(), "https://example.com/?page=2"; got != want {
@@ -15,6 +17,8 @@ func TestLinkString(t *testing.T) {
}
func TestParseRequest(t *testing.T) {
+ t.Parallel()
+
req, _ := http.NewRequest("GET", "", nil)
req.Header.Set("Link", `<https://example.com/?page=2>; rel="next"`)
@@ -42,6 +46,8 @@ func TestParseRequest(t *testing.T) {
}
func TestParseResponse(t *testing.T) {
+ t.Parallel()
+
resp := &http.Response{Header: http.Header{}}
resp.Header.Set("Link", `<https://example.com/?page=2>; rel="next"`)
@@ -69,6 +75,8 @@ func TestParseResponse(t *testing.T) {
}
func TestParseHeader_single(t *testing.T) {
+ t.Parallel()
+
h := http.Header{}
h.Set("Link", `<https://example.com/?page=2>; rel="next"`)
@@ -92,6 +100,8 @@ func TestParseHeader_single(t *testing.T) {
}
func TestParseHeader_multiple(t *testing.T) {
+ t.Parallel()
+
h := http.Header{}
h.Add("Link", `<https://example.com/?page=2>; rel="next",<https://example.com/?page=34>; rel="last"`)
@@ -127,6 +137,8 @@ func TestParseHeader_multiple(t *testing.T) {
}
func TestParseHeader_multiple_headers(t *testing.T) {
+ t.Parallel()
+
h := http.Header{}
h.Add("Link", `<https://example.com/?page=2>; rel="next",<https://example.com/?page=34>; rel="last"`)
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) {
}
func TestParseHeader_extra(t *testing.T) {
+ t.Parallel()
+
h := http.Header{}
h.Add("Link", `<https://example.com/?page=2>; rel="next"; title="foo"`)
@@ -182,24 +196,32 @@ func TestParseHeader_extra(t *testing.T) {
}
func TestParseHeader_noLink(t *testing.T) {
+ t.Parallel()
+
if ParseHeader(http.Header{}) != nil {
t.Fatalf(`Parse(http.Header{}) != nil`)
}
}
func TestParseHeader_nilHeader(t *testing.T) {
+ t.Parallel()
+
if ParseHeader(nil) != nil {
t.Fatalf(`ParseHeader(nil) != nil`)
}
}
func TestParse_emptyString(t *testing.T) {
+ t.Parallel()
+
if Parse("") != nil {
t.Fatalf(`Parse("") != nil`)
}
}
func TestParse_valuesWithComma(t *testing.T) {
+ t.Parallel()
+
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"`)
if got, want := len(g), 5; got != want {
@@ -216,6 +238,8 @@ func TestParse_valuesWithComma(t *testing.T) {
}
func TestParse_rfc5988Example1(t *testing.T) {
+ t.Parallel()
+
g := Parse(`<http://example.com/TheBook/chapter2>; rel="previous"; title="previous chapter"`)
if got, want := len(g), 1; got != want {
@@ -232,6 +256,8 @@ func TestParse_rfc5988Example1(t *testing.T) {
}
func TestParse_rfc5988Example2(t *testing.T) {
+ t.Parallel()
+
g := Parse(`</>; rel="http://example.net/foo"`)
if got, want := len(g), 1; got != want {
@@ -250,11 +276,15 @@ func TestParse_rfc5988Example2(t *testing.T) {
}
func TestParse_rfc5988Example3(t *testing.T) {
+ t.Parallel()
+
// Extended notation is not supported yet
// g := Parse(`</TheBook/chapter2>; rel="previous"; title*=UTF-8'de'letztes%20Kapitel, </TheBook/chapter4>; rel="next"; title*=UTF-8'de'n%c3%a4chstes%20Kapitel`)
}
func TestParse_rfc5988Example4(t *testing.T) {
+ t.Parallel()
+
// Extension relation types are ignored for now
g := Parse(`<http://example.org/>; rel="start http://example.net/relation/other"`)
@@ -271,7 +301,39 @@ func TestParse_rfc5988Example4(t *testing.T) {
}
}
+func TestParse_noQuotesExample(t *testing.T) {
+ g := Parse(`</example.png>;rel=preload;as=image`)
+
+ if got, want := len(g), 1; got != want {
+ t.Fatalf(`len(g) = %d, want %d`, got, want)
+ }
+
+ if g["preload"] == nil {
+ t.Fatalf(`g["preload"] == nil`)
+ }
+
+ l := *g["preload"]
+
+ if got, want := l.URI, "/example.png"; got != want {
+ t.Fatalf("l.URI = %q, want = %q", got, want)
+ }
+
+ if got, want := l.Rel, "preload"; got != want {
+ t.Fatalf("l.Rel = %q, want = %q", got, want)
+ }
+
+ if l.Extra == nil {
+ t.Fatalf("l.Extra == nil")
+ }
+
+ if got, want := l.Extra["as"], "image"; got != want {
+ t.Fatalf("l.Extra[\"as\"] = %q, want %q", got, want)
+ }
+}
+
func TestParse_fuzzCrashers(t *testing.T) {
+ t.Parallel()
+
Parse("0")
}
@@ -281,3 +343,12 @@ func ExampleParse() {
fmt.Printf("URI: %q, Rel: %q, Extra: %+v\n", l.URI, l.Rel, l.Extra)
// Output: URI: "https://example.com/?page=2", Rel: "next", Extra: map[title:foo]
}
+
+func FuzzParse(f *testing.F) {
+ f.Add(`<https://example.com/?page=2>; rel="next"; title="foo"`)
+ f.Add(`<http://example.org/>; rel="start http://example.net/relation/other"`)
+
+ f.Fuzz(func(t *testing.T, s string) {
+ Parse(s)
+ })
+}
diff --git a/testdata/fuzz/FuzzParse/14f0384ffafb238c2f9dc0b3b66dc3444a3affba58db575b0c2a6ef15c2f45bc b/testdata/fuzz/FuzzParse/14f0384ffafb238c2f9dc0b3b66dc3444a3affba58db575b0c2a6ef15c2f45bc
new file mode 100644
index 0000000..d28219d
--- /dev/null
+++ b/testdata/fuzz/FuzzParse/14f0384ffafb238c2f9dc0b3b66dc3444a3affba58db575b0c2a6ef15c2f45bc
@@ -0,0 +1,2 @@
+go test fuzz v1
+string("<0>;=")
diff --git a/testdata/fuzz/FuzzParse/c303690a5e3c3f39bcaa59d99712f61acc06e48d042e669ad5778b2e470654b0 b/testdata/fuzz/FuzzParse/c303690a5e3c3f39bcaa59d99712f61acc06e48d042e669ad5778b2e470654b0
new file mode 100644
index 0000000..bcb487b
--- /dev/null
+++ b/testdata/fuzz/FuzzParse/c303690a5e3c3f39bcaa59d99712f61acc06e48d042e669ad5778b2e470654b0
@@ -0,0 +1,2 @@
+go test fuzz v1
+string("<0>;0")
Debdiff
[The following lists of changes regard files as different if they have different names, permissions or owners.]
Files in second set of .debs but not in first
-rw-r--r-- root/root /usr/share/gocode/src/github.com/peterhellberg/link/go.mod -rw-r--r-- root/root /usr/share/gocode/src/github.com/peterhellberg/link/testdata/fuzz/FuzzParse/14f0384ffafb238c2f9dc0b3b66dc3444a3affba58db575b0c2a6ef15c2f45bc -rw-r--r-- root/root /usr/share/gocode/src/github.com/peterhellberg/link/testdata/fuzz/FuzzParse/c303690a5e3c3f39bcaa59d99712f61acc06e48d042e669ad5778b2e470654b0
No differences were encountered in the control files