linkheader/main.go

149 lines
3.0 KiB
Go
Raw Normal View History

2016-03-28 22:49:59 +02:00
// Package linkheader provides functions for parsing HTTP Link headers
2015-12-10 15:33:58 +01:00
package linkheader
import (
"fmt"
"strings"
)
// A Link is a single URL and related parameters
type Link struct {
URL string
Rel string
Params map[string]string
}
// HasParam returns if a Link has a particular parameter or not
func (l Link) HasParam(key string) bool {
2016-02-07 01:12:53 +01:00
for p := range l.Params {
2015-12-10 15:33:58 +01:00
if p == key {
return true
}
}
return false
}
// Param returns the value of a parameter if it exists
func (l Link) Param(key string) string {
2015-12-10 15:33:58 +01:00
for k, v := range l.Params {
if key == k {
return v
2015-12-10 15:33:58 +01:00
}
}
return ""
2015-12-10 15:33:58 +01:00
}
2016-06-17 01:32:30 +02:00
// String returns the string representation of a link
2016-06-17 01:18:15 +02:00
func (l Link) String() string {
p := make([]string, 0, len(l.Params))
2016-06-17 01:18:15 +02:00
for k, v := range l.Params {
p = append(p, fmt.Sprintf("%s=\"%s\"", k, v))
}
if l.Rel != "" {
p = append(p, fmt.Sprintf("%s=\"%s\"", "rel", l.Rel))
}
return fmt.Sprintf("<%s>; %s", l.URL, strings.Join(p, "; "))
}
2016-02-07 01:12:53 +01:00
// Links is a slice of Link structs
2015-12-10 15:33:58 +01:00
type Links []Link
// FilterByRel filters a group of Links by the provided Rel attribute
func (l Links) FilterByRel(r string) Links {
links := make(Links, 0)
for _, link := range l {
if link.Rel == r {
links = append(links, link)
}
}
return links
}
2016-06-17 01:32:30 +02:00
// String returns the string representation of multiple Links
// for use in HTTP responses etc
func (l Links) String() string {
if l == nil {
return fmt.Sprint(nil)
}
2016-06-20 01:20:27 +02:00
var strs []string
2016-06-17 01:32:30 +02:00
for _, link := range l {
strs = append(strs, link.String())
}
return strings.Join(strs, ", ")
}
2015-12-10 15:33:58 +01:00
// Parse parses a raw Link header in the form:
// <url>; rel="foo", <url>; rel="bar"; wat="dis"
// returning a slice of Link structs
func Parse(raw string) Links {
var links Links
2015-12-10 15:33:58 +01:00
// One chunk: <url>; rel="foo"
for _, chunk := range strings.Split(raw, ",") {
link := Link{URL: "", Rel: "", Params: make(map[string]string)}
// Figure out what each piece of the chunk is
for _, piece := range strings.Split(chunk, ";") {
piece = strings.Trim(piece, " ")
if piece == "" {
continue
}
// URL
if piece[0] == '<' && piece[len(piece)-1] == '>' {
link.URL = strings.Trim(piece, "<>")
continue
}
// Params
key, val := parseParam(piece)
if key == "" {
continue
}
// Special case for rel
if strings.ToLower(key) == "rel" {
link.Rel = val
2017-05-05 18:35:55 +02:00
} else {
link.Params[key] = val
2015-12-10 15:33:58 +01:00
}
}
if link.URL != "" {
links = append(links, link)
}
2015-12-10 15:33:58 +01:00
}
return links
}
// ParseMultiple is like Parse, but accepts a slice of headers
// rather than just one header string
func ParseMultiple(headers []string) Links {
links := make(Links, 0)
for _, header := range headers {
links = append(links, Parse(header)...)
}
return links
}
// parseParam takes a raw param in the form key="val" and
// returns the key and value as seperate strings
func parseParam(raw string) (key, val string) {
parts := strings.SplitN(raw, "=", 2)
if len(parts) != 2 {
return "", ""
}
key = parts[0]
val = strings.Trim(parts[1], "\"")
return key, val
}