headers.go raw
1 package rest
2
3 import (
4 "net/url"
5 "regexp"
6 "strings"
7 )
8
9 var (
10 commaRegexp = regexp.MustCompile(`,\s{0,}`)
11 valueCommaRegexp = regexp.MustCompile(`([^"]),`)
12 equalRegexp = regexp.MustCompile(` *= *`)
13 keyRegexp = regexp.MustCompile(`[a-z*]+`)
14 linkRegexp = regexp.MustCompile(`\A<(.+)>;(.+)\z`)
15 semiRegexp = regexp.MustCompile(`; +`)
16 valRegexp = regexp.MustCompile(`"+([^"]+)"+`)
17 )
18
19 // Links represents a Link Header, keyed by the Rel attribute
20 type Links map[string]*Link
21
22 // Link has a URI and its relation (next/prev/last/etc)
23 type Link struct {
24 URI string
25 Rel string
26 Extra map[string]string
27 }
28
29 // Next gets the URI for "next", if present
30 func (l Links) Next() string {
31 for k, v := range l {
32 if k == "next" {
33 return v.URI
34 }
35 }
36 return ""
37 }
38
39 // ParseLink parses a Link header value into a Links, mainly cribbed from:
40 // https://github.com/peterhellberg/link/blob/master/link.go
41 // The forceHTTPS parameter will rewrite any HTTP URLs it finds to HTTPS.
42 func ParseLink(s string, forceHTTPS bool) Links {
43 if s == "" {
44 return nil
45 }
46
47 links := Links{}
48
49 s = valueCommaRegexp.ReplaceAllString(s, "$1")
50
51 for _, l := range commaRegexp.Split(s, -1) {
52 linkMatches := linkRegexp.FindAllStringSubmatch(l, -1)
53
54 if len(linkMatches) == 0 {
55 return nil
56 }
57
58 pieces := linkMatches[0]
59
60 // Make sure we have a reasonable URL
61 uri := ""
62 if url, err := url.ParseRequestURI(pieces[1]); err == nil {
63
64 // See PLAT-188
65 if forceHTTPS && url.Scheme == "http" {
66 url.Scheme = "https"
67 }
68
69 uri = url.String()
70 }
71
72 link := &Link{URI: uri, Extra: map[string]string{}}
73
74 for _, extra := range semiRegexp.Split(pieces[2], -1) {
75 vals := equalRegexp.Split(extra, -1)
76
77 key := keyRegexp.FindString(vals[0])
78 val := valRegexp.FindStringSubmatch(vals[1])[1]
79
80 if key == "rel" {
81 vals := strings.Split(val, " ")
82 rels := []string{vals[0]}
83
84 if len(vals) > 1 {
85 for _, v := range vals[1:] {
86 if !strings.HasPrefix(v, "http") {
87 rels = append(rels, v)
88 }
89 }
90 }
91
92 rel := strings.Join(rels, " ")
93
94 link.Rel = rel
95 links[rel] = link
96 } else {
97 link.Extra[key] = val
98 }
99 }
100 }
101
102 return links
103 }
104