request.go 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. package http3
  2. import (
  3. "crypto/tls"
  4. "errors"
  5. "net/http"
  6. "net/url"
  7. "strconv"
  8. "strings"
  9. "github.com/marten-seemann/qpack"
  10. )
  11. func requestFromHeaders(headers []qpack.HeaderField) (*http.Request, error) {
  12. var path, authority, method, protocol, scheme, contentLengthStr string
  13. httpHeaders := http.Header{}
  14. for _, h := range headers {
  15. switch h.Name {
  16. case ":path":
  17. path = h.Value
  18. case ":method":
  19. method = h.Value
  20. case ":authority":
  21. authority = h.Value
  22. case ":protocol":
  23. protocol = h.Value
  24. case ":scheme":
  25. scheme = h.Value
  26. case "content-length":
  27. contentLengthStr = h.Value
  28. default:
  29. if !h.IsPseudo() {
  30. httpHeaders.Add(h.Name, h.Value)
  31. }
  32. }
  33. }
  34. // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4
  35. if len(httpHeaders["Cookie"]) > 0 {
  36. httpHeaders.Set("Cookie", strings.Join(httpHeaders["Cookie"], "; "))
  37. }
  38. isConnect := method == http.MethodConnect
  39. // Extended CONNECT, see https://datatracker.ietf.org/doc/html/rfc8441#section-4
  40. isExtendedConnected := isConnect && protocol != ""
  41. if isExtendedConnected {
  42. if scheme == "" || path == "" || authority == "" {
  43. return nil, errors.New("extended CONNECT: :scheme, :path and :authority must not be empty")
  44. }
  45. } else if isConnect {
  46. if path != "" || authority == "" { // normal CONNECT
  47. return nil, errors.New(":path must be empty and :authority must not be empty")
  48. }
  49. } else if len(path) == 0 || len(authority) == 0 || len(method) == 0 {
  50. return nil, errors.New(":path, :authority and :method must not be empty")
  51. }
  52. var u *url.URL
  53. var requestURI string
  54. var err error
  55. if isConnect {
  56. u = &url.URL{}
  57. if isExtendedConnected {
  58. u, err = url.ParseRequestURI(path)
  59. if err != nil {
  60. return nil, err
  61. }
  62. } else {
  63. u.Path = path
  64. }
  65. u.Scheme = scheme
  66. u.Host = authority
  67. requestURI = authority
  68. } else {
  69. protocol = "HTTP/3.0"
  70. u, err = url.ParseRequestURI(path)
  71. if err != nil {
  72. return nil, err
  73. }
  74. requestURI = path
  75. }
  76. var contentLength int64
  77. if len(contentLengthStr) > 0 {
  78. contentLength, err = strconv.ParseInt(contentLengthStr, 10, 64)
  79. if err != nil {
  80. return nil, err
  81. }
  82. }
  83. return &http.Request{
  84. Method: method,
  85. URL: u,
  86. Proto: protocol,
  87. ProtoMajor: 3,
  88. ProtoMinor: 0,
  89. Header: httpHeaders,
  90. Body: nil,
  91. ContentLength: contentLength,
  92. Host: authority,
  93. RequestURI: requestURI,
  94. TLS: &tls.ConnectionState{},
  95. }, nil
  96. }
  97. func hostnameFromRequest(req *http.Request) string {
  98. if req.URL != nil {
  99. return req.URL.Host
  100. }
  101. return ""
  102. }