mirror of
https://github.com/strongdm/comply
synced 2024-08-28 10:03:33 +00:00
152 lines
3.0 KiB
Go
152 lines
3.0 KiB
Go
|
// Package linkheader provides functions for parsing HTTP Link headers
|
||
|
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 {
|
||
|
for p := range l.Params {
|
||
|
if p == key {
|
||
|
return true
|
||
|
}
|
||
|
}
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// Param returns the value of a parameter if it exists
|
||
|
func (l Link) Param(key string) string {
|
||
|
for k, v := range l.Params {
|
||
|
if key == k {
|
||
|
return v
|
||
|
}
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// String returns the string representation of a link
|
||
|
func (l Link) String() string {
|
||
|
|
||
|
p := make([]string, 0, len(l.Params))
|
||
|
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, "; "))
|
||
|
}
|
||
|
|
||
|
// Links is a slice of Link structs
|
||
|
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
|
||
|
}
|
||
|
|
||
|
// 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)
|
||
|
}
|
||
|
|
||
|
var strs []string
|
||
|
for _, link := range l {
|
||
|
strs = append(strs, link.String())
|
||
|
}
|
||
|
return strings.Join(strs, ", ")
|
||
|
}
|
||
|
|
||
|
// 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
|
||
|
|
||
|
// 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
|
||
|
} else {
|
||
|
link.Params[key] = val
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if link.URL != "" {
|
||
|
links = append(links, link)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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) == 1 {
|
||
|
return parts[0], ""
|
||
|
}
|
||
|
if len(parts) != 2 {
|
||
|
return "", ""
|
||
|
}
|
||
|
|
||
|
key = parts[0]
|
||
|
val = strings.Trim(parts[1], "\"")
|
||
|
|
||
|
return key, val
|
||
|
|
||
|
}
|