request_reader.go 2.1 KB

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