request.go 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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, 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 "content-length":
  23. contentLengthStr = h.Value
  24. default:
  25. if !h.IsPseudo() {
  26. httpHeaders.Add(h.Name, h.Value)
  27. }
  28. }
  29. }
  30. // concatenate cookie headers, see https://tools.ietf.org/html/rfc6265#section-5.4
  31. if len(httpHeaders["Cookie"]) > 0 {
  32. httpHeaders.Set("Cookie", strings.Join(httpHeaders["Cookie"], "; "))
  33. }
  34. isConnect := method == http.MethodConnect
  35. if isConnect {
  36. if path != "" || authority == "" {
  37. return nil, errors.New(":path must be empty and :authority must not be empty")
  38. }
  39. } else if len(path) == 0 || len(authority) == 0 || len(method) == 0 {
  40. return nil, errors.New(":path, :authority and :method must not be empty")
  41. }
  42. var u *url.URL
  43. var requestURI string
  44. var err error
  45. if isConnect {
  46. u = &url.URL{Host: authority}
  47. requestURI = authority
  48. } else {
  49. u, err = url.ParseRequestURI(path)
  50. if err != nil {
  51. return nil, err
  52. }
  53. requestURI = path
  54. }
  55. var contentLength int64
  56. if len(contentLengthStr) > 0 {
  57. contentLength, err = strconv.ParseInt(contentLengthStr, 10, 64)
  58. if err != nil {
  59. return nil, err
  60. }
  61. }
  62. return &http.Request{
  63. Method: method,
  64. URL: u,
  65. Proto: "HTTP/3",
  66. ProtoMajor: 3,
  67. ProtoMinor: 0,
  68. Header: httpHeaders,
  69. Body: nil,
  70. ContentLength: contentLength,
  71. Host: authority,
  72. RequestURI: requestURI,
  73. TLS: &tls.ConnectionState{},
  74. }, nil
  75. }
  76. func hostnameFromRequest(req *http.Request) string {
  77. if req.URL != nil {
  78. return req.URL.Host
  79. }
  80. return ""
  81. }