headers.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. package http3
  2. import (
  3. "errors"
  4. "fmt"
  5. "net/http"
  6. "net/url"
  7. "strconv"
  8. "strings"
  9. "golang.org/x/net/http/httpguts"
  10. "github.com/quic-go/qpack"
  11. )
  12. type header struct {
  13. // Pseudo header fields defined in RFC 9114
  14. Path string
  15. Method string
  16. Authority string
  17. Scheme string
  18. Status string
  19. // for Extended connect
  20. Protocol string
  21. // parsed and deduplicated
  22. ContentLength int64
  23. // all non-pseudo headers
  24. Headers http.Header
  25. }
  26. func parseHeaders(headers []qpack.HeaderField, isRequest bool) (header, error) {
  27. hdr := header{Headers: make(http.Header, len(headers))}
  28. var readFirstRegularHeader, readContentLength bool
  29. var contentLengthStr string
  30. for _, h := range headers {
  31. // field names need to be lowercase, see section 4.2 of RFC 9114
  32. if strings.ToLower(h.Name) != h.Name {
  33. return header{}, fmt.Errorf("header field is not lower-case: %s", h.Name)
  34. }
  35. if !httpguts.ValidHeaderFieldValue(h.Value) {
  36. return header{}, fmt.Errorf("invalid header field value for %s: %q", h.Name, h.Value)
  37. }
  38. if h.IsPseudo() {
  39. if readFirstRegularHeader {
  40. // all pseudo headers must appear before regular header fields, see section 4.3 of RFC 9114
  41. return header{}, fmt.Errorf("received pseudo header %s after a regular header field", h.Name)
  42. }
  43. var isResponsePseudoHeader bool // pseudo headers are either valid for requests or for responses
  44. switch h.Name {
  45. case ":path":
  46. hdr.Path = h.Value
  47. case ":method":
  48. hdr.Method = h.Value
  49. case ":authority":
  50. hdr.Authority = h.Value
  51. case ":protocol":
  52. hdr.Protocol = h.Value
  53. case ":scheme":
  54. hdr.Scheme = h.Value
  55. case ":status":
  56. hdr.Status = h.Value
  57. isResponsePseudoHeader = true
  58. default:
  59. return header{}, fmt.Errorf("unknown pseudo header: %s", h.Name)
  60. }
  61. if isRequest && isResponsePseudoHeader {
  62. return header{}, fmt.Errorf("invalid request pseudo header: %s", h.Name)
  63. }
  64. if !isRequest && !isResponsePseudoHeader {
  65. return header{}, fmt.Errorf("invalid response pseudo header: %s", h.Name)
  66. }
  67. } else {
  68. if !httpguts.ValidHeaderFieldName(h.Name) {
  69. return header{}, fmt.Errorf("invalid header field name: %q", h.Name)
  70. }
  71. readFirstRegularHeader = true
  72. switch h.Name {
  73. case "content-length":
  74. // Ignore duplicate Content-Length headers.
  75. // Fail if the duplicates differ.
  76. if !readContentLength {
  77. readContentLength = true
  78. contentLengthStr = h.Value
  79. } else if contentLengthStr != h.Value {
  80. return header{}, fmt.Errorf("contradicting content lengths (%s and %s)", contentLengthStr, h.Value)
  81. }
  82. default:
  83. hdr.Headers.Add(h.Name, h.Value)
  84. }
  85. }
  86. }
  87. if len(contentLengthStr) > 0 {
  88. // use ParseUint instead of ParseInt, so that parsing fails on negative values
  89. cl, err := strconv.ParseUint(contentLengthStr, 10, 63)
  90. if err != nil {
  91. return header{}, fmt.Errorf("invalid content length: %w", err)
  92. }
  93. hdr.Headers.Set("Content-Length", contentLengthStr)
  94. hdr.ContentLength = int64(cl)
  95. }
  96. return hdr, nil
  97. }
  98. func requestFromHeaders(headerFields []qpack.HeaderField) (*http.Request, error) {
  99. hdr, err := parseHeaders(headerFields, true)
  100. if err != nil {
  101. return nil, err
  102. }
  103. // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4
  104. if len(hdr.Headers["Cookie"]) > 0 {
  105. hdr.Headers.Set("Cookie", strings.Join(hdr.Headers["Cookie"], "; "))
  106. }
  107. isConnect := hdr.Method == http.MethodConnect
  108. // Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4
  109. isExtendedConnected := isConnect && hdr.Protocol != ""
  110. if isExtendedConnected {
  111. if hdr.Scheme == "" || hdr.Path == "" || hdr.Authority == "" {
  112. return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty")
  113. }
  114. } else if isConnect {
  115. if hdr.Path != "" || hdr.Authority == "" { // normal CONNECT
  116. return nil, errors.New(":path must be empty and :authority must not be empty")
  117. }
  118. } else if len(hdr.Path) == 0 || len(hdr.Authority) == 0 || len(hdr.Method) == 0 {
  119. return nil, errors.New(":path, :authority and :method must not be empty")
  120. }
  121. var u *url.URL
  122. var requestURI string
  123. var protocol string
  124. if isConnect {
  125. u = &url.URL{}
  126. if isExtendedConnected {
  127. u, err = url.ParseRequestURI(hdr.Path)
  128. if err != nil {
  129. return nil, err
  130. }
  131. } else {
  132. u.Path = hdr.Path
  133. }
  134. u.Scheme = hdr.Scheme
  135. u.Host = hdr.Authority
  136. requestURI = hdr.Authority
  137. protocol = hdr.Protocol
  138. } else {
  139. protocol = "HTTP/3.0"
  140. u, err = url.ParseRequestURI(hdr.Path)
  141. if err != nil {
  142. return nil, fmt.Errorf("invalid content length: %w", err)
  143. }
  144. requestURI = hdr.Path
  145. }
  146. return &http.Request{
  147. Method: hdr.Method,
  148. URL: u,
  149. Proto: protocol,
  150. ProtoMajor: 3,
  151. ProtoMinor: 0,
  152. Header: hdr.Headers,
  153. Body: nil,
  154. ContentLength: hdr.ContentLength,
  155. Host: hdr.Authority,
  156. RequestURI: requestURI,
  157. }, nil
  158. }
  159. func hostnameFromRequest(req *http.Request) string {
  160. if req.URL != nil {
  161. return req.URL.Host
  162. }
  163. return ""
  164. }
  165. func responseFromHeaders(headerFields []qpack.HeaderField) (*http.Response, error) {
  166. hdr, err := parseHeaders(headerFields, false)
  167. if err != nil {
  168. return nil, err
  169. }
  170. if hdr.Status == "" {
  171. return nil, errors.New("missing status field")
  172. }
  173. rsp := &http.Response{
  174. Proto: "HTTP/3.0",
  175. ProtoMajor: 3,
  176. Header: hdr.Headers,
  177. ContentLength: hdr.ContentLength,
  178. }
  179. status, err := strconv.Atoi(hdr.Status)
  180. if err != nil {
  181. return nil, fmt.Errorf("invalid status code: %w", err)
  182. }
  183. rsp.StatusCode = status
  184. rsp.Status = hdr.Status + " " + http.StatusText(status)
  185. return rsp, nil
  186. }