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