autoneg.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. // Package gold implements several LD standards
  2. // Copyright 2011 The Go Authors. All rights reserved.
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. //
  6. // The functions in this package implement the behaviour specified in
  7. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
  8. //
  9. // This deviates from RFC2616 in one respect. When a client sets their
  10. // Accept header to "*" (which is illegal) it will be interpreted as "*/*".
  11. // This has been observed in the wild, and the choice was made in the
  12. // spirit of being liberal in values that are accepted from the 'net.
  13. package gold
  14. import (
  15. "errors"
  16. "sort"
  17. "strconv"
  18. "strings"
  19. )
  20. // Accept structure is used to represent a clause in an HTTP Accept Header.
  21. type Accept struct {
  22. Type, SubType string
  23. Q float32
  24. Params map[string]string
  25. }
  26. // For internal use, so that we can use the sort interface.
  27. type acceptSorter []Accept
  28. func (accept acceptSorter) Len() int {
  29. return len(accept)
  30. }
  31. // purposely sorts "backwards" so we have the most appropriate
  32. // (largest q-value) at the beginning of the list.
  33. func (accept acceptSorter) Less(i, j int) bool {
  34. ai, aj := accept[i], accept[j]
  35. if ai.Q > aj.Q {
  36. return true
  37. }
  38. if ai.Type != "*" && aj.Type == "*" {
  39. return true
  40. }
  41. if ai.SubType != "*" && aj.SubType == "*" {
  42. return true
  43. }
  44. return false
  45. }
  46. func (accept acceptSorter) Swap(i, j int) {
  47. accept[i], accept[j] = accept[j], accept[i]
  48. }
  49. // AcceptList is a sorted list of clauses from an Accept header.
  50. type AcceptList []Accept
  51. // Negotiate the most appropriate contentType given the list of alternatives.
  52. // Returns an error if no alternative is acceptable.
  53. func (al AcceptList) Negotiate(alternatives ...string) (contentType string, err error) {
  54. asp := make([][]string, 0, len(alternatives))
  55. for _, ctype := range alternatives {
  56. asp = append(asp, strings.SplitN(ctype, "/", 2))
  57. }
  58. for _, clause := range al {
  59. for i, ctsp := range asp {
  60. if clause.Type == ctsp[0] && clause.SubType == ctsp[1] {
  61. contentType = alternatives[i]
  62. return
  63. }
  64. if clause.Type == ctsp[0] && clause.SubType == "*" {
  65. contentType = alternatives[i]
  66. return
  67. }
  68. if clause.Type == "*" && clause.SubType == "*" {
  69. contentType = alternatives[i]
  70. return
  71. }
  72. }
  73. }
  74. err = errors.New("No acceptable alternatives")
  75. return
  76. }
  77. // Parse an Accept Header string returning a sorted list of clauses.
  78. func parseAccept(header string) (accept []Accept, err error) {
  79. header = strings.Trim(header, " ")
  80. if len(header) == 0 {
  81. accept = make([]Accept, 0)
  82. return
  83. }
  84. parts := strings.SplitN(header, ",", -1)
  85. accept = make([]Accept, 0, len(parts))
  86. for _, part := range parts {
  87. part := strings.Trim(part, " ")
  88. a := Accept{}
  89. a.Params = make(map[string]string)
  90. a.Q = 1.0
  91. mrp := strings.SplitN(part, ";", -1)
  92. mediaRange := mrp[0]
  93. sp := strings.SplitN(mediaRange, "/", -1)
  94. a.Type = strings.Trim(sp[0], " ")
  95. switch {
  96. case len(sp) == 1 && a.Type == "*":
  97. // The case where the Accept header is just "*" is strictly speaking
  98. // invalid but is seen in the wild. We take it to be equivalent to
  99. // "*/*"
  100. a.SubType = "*"
  101. case len(sp) == 2:
  102. a.SubType = strings.Trim(sp[1], " ")
  103. default:
  104. err = errors.New("Invalid media range in " + part)
  105. return
  106. }
  107. if len(mrp) == 1 {
  108. accept = append(accept, a)
  109. continue
  110. }
  111. for _, param := range mrp[1:] {
  112. sp := strings.SplitN(param, "=", 2)
  113. if len(sp) != 2 {
  114. err = errors.New("Invalid parameter in " + part)
  115. return
  116. }
  117. token := strings.Trim(sp[0], " ")
  118. if token == "q" {
  119. q, _ := strconv.ParseFloat(sp[1], 32)
  120. a.Q = float32(q)
  121. } else {
  122. a.Params[token] = strings.Trim(sp[1], " ")
  123. }
  124. }
  125. accept = append(accept, a)
  126. }
  127. sorter := acceptSorter(accept)
  128. sort.Sort(sorter)
  129. return
  130. }
  131. // Parse the Accept header and return a sorted list of clauses. If the Accept header
  132. // is present but empty this will be an empty list. If the header is not present it will
  133. // default to a wildcard: */*. Returns an error if the Accept header is ill-formed.
  134. func (req *httpRequest) Accept() (al AcceptList, err error) {
  135. var accept string
  136. headers, ok := req.Header["Accept"]
  137. if ok && len(headers) > 0 {
  138. // if multiple Accept headers are specified just take the first one
  139. // such a client would be quite broken...
  140. accept = headers[0]
  141. } else {
  142. // default if not present
  143. accept = "*/*"
  144. }
  145. al, err = parseAccept(accept)
  146. return
  147. }