headers.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. package http3
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "net/textproto"
  7. "net/url"
  8. "strconv"
  9. "strings"
  10. "golang.org/x/net/http/httpguts"
  11. "github.com/quic-go/qpack"
  12. )
  13. type header struct {
  14. // Pseudo header fields defined in RFC 9114
  15. Path string
  16. Method string
  17. Authority string
  18. Scheme string
  19. Status string
  20. // for Extended connect
  21. Protocol string
  22. // parsed and deduplicated. -1 if no Content-Length header is sent
  23. ContentLength int64
  24. // all non-pseudo headers
  25. Headers http.Header
  26. }
  27. // connection-specific header fields must not be sent on HTTP/3
  28. var invalidHeaderFields = [...]string{
  29. "connection",
  30. "keep-alive",
  31. "proxy-connection",
  32. "transfer-encoding",
  33. "upgrade",
  34. }
  35. func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) {
  36. hdr := header{Headers: make(http.Header, len(headers))}
  37. var readFirstRegularHeader, readContentLength bool
  38. var contentLengthStr string
  39. for _, h := range headers {
  40. // field names need to be lowercase, see section 4.2 of RFC 9114
  41. if strings.ToLower(h.Name) != h.Name {
  42. return header{}, fmt.Errorf("header field is not lower-case: %s", h.Name)
  43. }
  44. if !httpguts.ValidHeaderFieldValue(h.Value) {
  45. return header{}, fmt.Errorf("invalid header field value for %s: %q", h.Name, h.Value)
  46. }
  47. if h.IsPseudo() {
  48. if readFirstRegularHeader {
  49. // all pseudo headers must appear before regular header fields, see section 4.3 of RFC 9114
  50. return header{}, fmt.Errorf("received pseudo header %s after a regular header field", h.Name)
  51. }
  52. var isResponsePseudoHeader bool // pseudo headers are either valid for requests or for responses
  53. switch h.Name {
  54. case ":path":
  55. hdr.Path = h.Value
  56. case ":method":
  57. hdr.Method = h.Value
  58. case ":authority":
  59. hdr.Authority = h.Value
  60. case ":protocol":
  61. hdr.Protocol = h.Value
  62. case ":scheme":
  63. hdr.Scheme = h.Value
  64. case ":status":
  65. hdr.Status = h.Value
  66. isResponsePseudoHeader = true
  67. default:
  68. return header{}, fmt.Errorf("unknown pseudo header: %s", h.Name)
  69. }
  70. if isRequest && isResponsePseudoHeader {
  71. return header{}, fmt.Errorf("invalid request pseudo header: %s", h.Name)
  72. }
  73. if !isRequest && !isResponsePseudoHeader {
  74. return header{}, fmt.Errorf("invalid response pseudo header: %s", h.Name)
  75. }
  76. } else {
  77. if !httpguts.ValidHeaderFieldName(h.Name) {
  78. return header{}, fmt.Errorf("invalid header field name: %q", h.Name)
  79. }
  80. for _, invalidField := range invalidHeaderFields {
  81. if h.Name == invalidField {
  82. return header{}, fmt.Errorf("invalid header field name: %q", h.Name)
  83. }
  84. }
  85. if h.Name == "te" && h.Value != "trailers" {
  86. return header{}, fmt.Errorf("invalid TE header field value: %q", h.Value)
  87. }
  88. readFirstRegularHeader = true
  89. switch h.Name {
  90. case "content-length":
  91. // Ignore duplicate Content-Length headers.
  92. // Fail if the duplicates differ.
  93. if !readContentLength {
  94. readContentLength = true
  95. contentLengthStr = h.Value
  96. } else if contentLengthStr != h.Value {
  97. return header{}, fmt.Errorf("contradicting content lengths (%s and %s)", contentLengthStr, h.Value)
  98. }
  99. default:
  100. hdr.Headers.Add(h.Name, h.Value)
  101. }
  102. }
  103. }
  104. hdr.ContentLength = -1
  105. if len(contentLengthStr) > 0 {
  106. // use ParseUint instead of ParseInt, so that parsing fails on negative values
  107. cl, err := strconv.ParseUint(contentLengthStr, 10, 63)
  108. if err != nil {
  109. return header{}, fmt.Errorf("invalid content length: %w", err)
  110. }
  111. hdr.Headers.Set("Content-Length", contentLengthStr)
  112. hdr.ContentLength = int64(cl)
  113. }
  114. return hdr, nil
  115. }
  116. func parseTrailers(headers []qpack.HeaderField) (http.Header, error) {
  117. h := make(http.Header, len(headers))
  118. for _, field := range headers {
  119. if field.IsPseudo() {
  120. return nil, fmt.Errorf("http3: received pseudo header in trailer: %s", field.Name)
  121. }
  122. h.Add(field.Name, field.Value)
  123. }
  124. return h, nil
  125. }
  126. func requestFromHeaders(headerFields []qpack.HeaderField) (*http.Request, error) {
  127. hdr, err := parseHeaders(headerFields, true)
  128. if err != nil {
  129. return nil, err
  130. }
  131. // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4
  132. if len(hdr.Headers["Cookie"]) > 0 {
  133. hdr.Headers.Set("Cookie", strings.Join(hdr.Headers["Cookie"], "; "))
  134. }
  135. isConnect := hdr.Method == http.MethodConnect
  136. // Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4
  137. isExtendedConnected := isConnect && hdr.Protocol != ""
  138. if isExtendedConnected {
  139. if hdr.Scheme == "" || hdr.Path == "" || hdr.Authority == "" {
  140. return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty")
  141. }
  142. } else if isConnect {
  143. if hdr.Path != "" || hdr.Authority == "" { // normal CONNECT
  144. return nil, errors.New(":path must be empty and :authority must not be empty")
  145. }
  146. } else if len(hdr.Path) == 0 || len(hdr.Authority) == 0 || len(hdr.Method) == 0 {
  147. return nil, errors.New(":path, :authority and :method must not be empty")
  148. }
  149. if !isExtendedConnected && len(hdr.Protocol) > 0 {
  150. return nil, errors.New(":protocol must be empty")
  151. }
  152. var u *url.URL
  153. var requestURI string
  154. protocol := "HTTP/3.0"
  155. if isConnect {
  156. u = &url.URL{}
  157. if isExtendedConnected {
  158. u, err = url.ParseRequestURI(hdr.Path)
  159. if err != nil {
  160. return nil, err
  161. }
  162. protocol = hdr.Protocol
  163. } else {
  164. u.Path = hdr.Path
  165. }
  166. u.Scheme = hdr.Scheme
  167. u.Host = hdr.Authority
  168. requestURI = hdr.Authority
  169. } else {
  170. u, err = url.ParseRequestURI(hdr.Path)
  171. if err != nil {
  172. return nil, fmt.Errorf("invalid content length: %w", err)
  173. }
  174. requestURI = hdr.Path
  175. }
  176. return &http.Request{
  177. Method: hdr.Method,
  178. URL: u,
  179. Proto: protocol,
  180. ProtoMajor: 3,
  181. ProtoMinor: 0,
  182. Header: hdr.Headers,
  183. Body: nil,
  184. ContentLength: hdr.ContentLength,
  185. Host: hdr.Authority,
  186. RequestURI: requestURI,
  187. }, nil
  188. }
  189. func hostnameFromURL(url *url.URL) string {
  190. if url != nil {
  191. return url.Host
  192. }
  193. return ""
  194. }
  195. // updateResponseFromHeaders sets up http.Response as an HTTP/3 response,
  196. // using the decoded qpack header filed.
  197. // It is only called for the HTTP header (and not the HTTP trailer).
  198. // It takes an http.Response as an argument to allow the caller to set the trailer later on.
  199. func updateResponseFromHeaders(rsp *http.Response, headerFields []qpack.HeaderField) error {
  200. hdr, err := parseHeaders(headerFields, false)
  201. if err != nil {
  202. return err
  203. }
  204. if hdr.Status == "" {
  205. return errors.New("missing status field")
  206. }
  207. rsp.Proto = "HTTP/3.0"
  208. rsp.ProtoMajor = 3
  209. rsp.Header = hdr.Headers
  210. processTrailers(rsp)
  211. rsp.ContentLength = hdr.ContentLength
  212. status, err := strconv.Atoi(hdr.Status)
  213. if err != nil {
  214. return fmt.Errorf("invalid status code: %w", err)
  215. }
  216. rsp.StatusCode = status
  217. rsp.Status = hdr.Status + " " + http.StatusText(status)
  218. return nil
  219. }
  220. // processTrailers initializes the rsp.Trailer map, and adds keys for every announced header value.
  221. // The Trailer header is removed from the http.Response.Header map.
  222. // It handles both duplicate as well as comma-separated values for the Trailer header.
  223. // For example:
  224. //
  225. // Trailer: Trailer1, Trailer2
  226. // Trailer: Trailer3
  227. //
  228. // Will result in a http.Response.Trailer map containing the keys "Trailer1", "Trailer2", "Trailer3".
  229. func processTrailers(rsp *http.Response) {
  230. rawTrailers, ok := rsp.Header["Trailer"]
  231. if !ok {
  232. return
  233. }
  234. rsp.Trailer = make(http.Header)
  235. for _, rawVal := range rawTrailers {
  236. for _, val := range strings.Split(rawVal, ",") {
  237. rsp.Trailer[http.CanonicalHeaderKey(textproto.TrimString(val))] = nil
  238. }
  239. }
  240. delete(rsp.Header, "Trailer")
  241. }