main.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Package linkheader provides functions for parsing HTTP Link headers
  2. package linkheader
  3. import (
  4. "fmt"
  5. "strings"
  6. )
  7. // A Link is a single URL and related parameters
  8. type Link struct {
  9. URL string
  10. Rel string
  11. Params map[string]string
  12. }
  13. // HasParam returns if a Link has a particular parameter or not
  14. func (l Link) HasParam(key string) bool {
  15. for p := range l.Params {
  16. if p == key {
  17. return true
  18. }
  19. }
  20. return false
  21. }
  22. // Param returns the value of a parameter if it exists
  23. func (l Link) Param(key string) string {
  24. for k, v := range l.Params {
  25. if key == k {
  26. return v
  27. }
  28. }
  29. return ""
  30. }
  31. // String returns the string representation of a link
  32. func (l Link) String() string {
  33. p := make([]string, 0, len(l.Params))
  34. for k, v := range l.Params {
  35. p = append(p, fmt.Sprintf("%s=\"%s\"", k, v))
  36. }
  37. if l.Rel != "" {
  38. p = append(p, fmt.Sprintf("%s=\"%s\"", "rel", l.Rel))
  39. }
  40. return fmt.Sprintf("<%s>; %s", l.URL, strings.Join(p, "; "))
  41. }
  42. // Links is a slice of Link structs
  43. type Links []Link
  44. // FilterByRel filters a group of Links by the provided Rel attribute
  45. func (l Links) FilterByRel(r string) Links {
  46. links := make(Links, 0)
  47. for _, link := range l {
  48. if link.Rel == r {
  49. links = append(links, link)
  50. }
  51. }
  52. return links
  53. }
  54. // String returns the string representation of multiple Links
  55. // for use in HTTP responses etc
  56. func (l Links) String() string {
  57. if l == nil {
  58. return fmt.Sprint(nil)
  59. }
  60. var strs []string
  61. for _, link := range l {
  62. strs = append(strs, link.String())
  63. }
  64. return strings.Join(strs, ", ")
  65. }
  66. // Parse parses a raw Link header in the form:
  67. // <url>; rel="foo", <url>; rel="bar"; wat="dis"
  68. // returning a slice of Link structs
  69. func Parse(raw string) Links {
  70. var links Links
  71. // One chunk: <url>; rel="foo"
  72. for _, chunk := range strings.Split(raw, ",") {
  73. link := Link{URL: "", Rel: "", Params: make(map[string]string)}
  74. // Figure out what each piece of the chunk is
  75. for _, piece := range strings.Split(chunk, ";") {
  76. piece = strings.Trim(piece, " ")
  77. if piece == "" {
  78. continue
  79. }
  80. // URL
  81. if piece[0] == '<' && piece[len(piece)-1] == '>' {
  82. link.URL = strings.Trim(piece, "<>")
  83. continue
  84. }
  85. // Params
  86. key, val := parseParam(piece)
  87. if key == "" {
  88. continue
  89. }
  90. // Special case for rel
  91. if strings.ToLower(key) == "rel" {
  92. link.Rel = val
  93. } else {
  94. link.Params[key] = val
  95. }
  96. }
  97. if link.URL != "" {
  98. links = append(links, link)
  99. }
  100. }
  101. return links
  102. }
  103. // ParseMultiple is like Parse, but accepts a slice of headers
  104. // rather than just one header string
  105. func ParseMultiple(headers []string) Links {
  106. links := make(Links, 0)
  107. for _, header := range headers {
  108. links = append(links, Parse(header)...)
  109. }
  110. return links
  111. }
  112. // parseParam takes a raw param in the form key="val" and
  113. // returns the key and value as seperate strings
  114. func parseParam(raw string) (key, val string) {
  115. parts := strings.SplitN(raw, "=", 2)
  116. if len(parts) == 1 {
  117. return parts[0], ""
  118. }
  119. if len(parts) != 2 {
  120. return "", ""
  121. }
  122. key = parts[0]
  123. val = strings.Trim(parts[1], "\"")
  124. return key, val
  125. }