github.com/mikelsr/quic-go@v0.36.1-0.20230701132136-1d9415b66898/http3/request_test.go (about) 1 package http3 2 3 import ( 4 "net/http" 5 "net/url" 6 7 . "github.com/onsi/ginkgo/v2" 8 . "github.com/onsi/gomega" 9 "github.com/quic-go/qpack" 10 ) 11 12 var _ = Describe("Request", func() { 13 It("populates request", func() { 14 headers := []qpack.HeaderField{ 15 {Name: ":path", Value: "/foo"}, 16 {Name: ":authority", Value: "quic.clemente.io"}, 17 {Name: ":method", Value: "GET"}, 18 {Name: "content-length", Value: "42"}, 19 } 20 req, err := requestFromHeaders(headers) 21 Expect(err).NotTo(HaveOccurred()) 22 Expect(req.Method).To(Equal("GET")) 23 Expect(req.URL.Path).To(Equal("/foo")) 24 Expect(req.URL.Host).To(BeEmpty()) 25 Expect(req.Proto).To(Equal("HTTP/3.0")) 26 Expect(req.ProtoMajor).To(Equal(3)) 27 Expect(req.ProtoMinor).To(BeZero()) 28 Expect(req.ContentLength).To(Equal(int64(42))) 29 Expect(req.Header).To(BeEmpty()) 30 Expect(req.Body).To(BeNil()) 31 Expect(req.Host).To(Equal("quic.clemente.io")) 32 Expect(req.RequestURI).To(Equal("/foo")) 33 }) 34 35 It("parses path with leading double slashes", func() { 36 headers := []qpack.HeaderField{ 37 {Name: ":path", Value: "//foo"}, 38 {Name: ":authority", Value: "quic.clemente.io"}, 39 {Name: ":method", Value: "GET"}, 40 } 41 req, err := requestFromHeaders(headers) 42 Expect(err).NotTo(HaveOccurred()) 43 Expect(req.Header).To(BeEmpty()) 44 Expect(req.Body).To(BeNil()) 45 Expect(req.URL.Path).To(Equal("//foo")) 46 Expect(req.URL.Host).To(BeEmpty()) 47 Expect(req.Host).To(Equal("quic.clemente.io")) 48 Expect(req.RequestURI).To(Equal("//foo")) 49 }) 50 51 It("concatenates the cookie headers", func() { 52 headers := []qpack.HeaderField{ 53 {Name: ":path", Value: "/foo"}, 54 {Name: ":authority", Value: "quic.clemente.io"}, 55 {Name: ":method", Value: "GET"}, 56 {Name: "cookie", Value: "cookie1=foobar1"}, 57 {Name: "cookie", Value: "cookie2=foobar2"}, 58 } 59 req, err := requestFromHeaders(headers) 60 Expect(err).NotTo(HaveOccurred()) 61 Expect(req.Header).To(Equal(http.Header{ 62 "Cookie": []string{"cookie1=foobar1; cookie2=foobar2"}, 63 })) 64 }) 65 66 It("handles Other headers", func() { 67 headers := []qpack.HeaderField{ 68 {Name: ":path", Value: "/foo"}, 69 {Name: ":authority", Value: "quic.clemente.io"}, 70 {Name: ":method", Value: "GET"}, 71 {Name: "cache-control", Value: "max-age=0"}, 72 {Name: "duplicate-header", Value: "1"}, 73 {Name: "duplicate-header", Value: "2"}, 74 } 75 req, err := requestFromHeaders(headers) 76 Expect(err).NotTo(HaveOccurred()) 77 Expect(req.Header).To(Equal(http.Header{ 78 "Cache-Control": []string{"max-age=0"}, 79 "Duplicate-Header": []string{"1", "2"}, 80 })) 81 }) 82 83 It("errors with missing path", func() { 84 headers := []qpack.HeaderField{ 85 {Name: ":authority", Value: "quic.clemente.io"}, 86 {Name: ":method", Value: "GET"}, 87 } 88 _, err := requestFromHeaders(headers) 89 Expect(err).To(MatchError(":path, :authority and :method must not be empty")) 90 }) 91 92 It("errors with missing method", func() { 93 headers := []qpack.HeaderField{ 94 {Name: ":path", Value: "/foo"}, 95 {Name: ":authority", Value: "quic.clemente.io"}, 96 } 97 _, err := requestFromHeaders(headers) 98 Expect(err).To(MatchError(":path, :authority and :method must not be empty")) 99 }) 100 101 It("errors with missing authority", func() { 102 headers := []qpack.HeaderField{ 103 {Name: ":path", Value: "/foo"}, 104 {Name: ":method", Value: "GET"}, 105 } 106 _, err := requestFromHeaders(headers) 107 Expect(err).To(MatchError(":path, :authority and :method must not be empty")) 108 }) 109 110 Context("regular HTTP CONNECT", func() { 111 It("handles CONNECT method", func() { 112 headers := []qpack.HeaderField{ 113 {Name: ":authority", Value: "quic.clemente.io"}, 114 {Name: ":method", Value: http.MethodConnect}, 115 } 116 req, err := requestFromHeaders(headers) 117 Expect(err).NotTo(HaveOccurred()) 118 Expect(req.Method).To(Equal(http.MethodConnect)) 119 Expect(req.RequestURI).To(Equal("quic.clemente.io")) 120 }) 121 122 It("errors with missing authority in CONNECT method", func() { 123 headers := []qpack.HeaderField{ 124 {Name: ":method", Value: http.MethodConnect}, 125 } 126 _, err := requestFromHeaders(headers) 127 Expect(err).To(MatchError(":path must be empty and :authority must not be empty")) 128 }) 129 130 It("errors with extra path in CONNECT method", func() { 131 headers := []qpack.HeaderField{ 132 {Name: ":path", Value: "/foo"}, 133 {Name: ":authority", Value: "quic.clemente.io"}, 134 {Name: ":method", Value: http.MethodConnect}, 135 } 136 _, err := requestFromHeaders(headers) 137 Expect(err).To(MatchError(":path must be empty and :authority must not be empty")) 138 }) 139 }) 140 141 Context("Extended CONNECT", func() { 142 It("handles Extended CONNECT method", func() { 143 headers := []qpack.HeaderField{ 144 {Name: ":protocol", Value: "webtransport"}, 145 {Name: ":scheme", Value: "ftp"}, 146 {Name: ":method", Value: http.MethodConnect}, 147 {Name: ":authority", Value: "quic.clemente.io"}, 148 {Name: ":path", Value: "/foo?val=1337"}, 149 } 150 req, err := requestFromHeaders(headers) 151 Expect(err).NotTo(HaveOccurred()) 152 Expect(req.Method).To(Equal(http.MethodConnect)) 153 Expect(req.Proto).To(Equal("webtransport")) 154 Expect(req.URL.String()).To(Equal("ftp://quic.clemente.io/foo?val=1337")) 155 Expect(req.URL.Query().Get("val")).To(Equal("1337")) 156 }) 157 158 It("errors with missing scheme", func() { 159 headers := []qpack.HeaderField{ 160 {Name: ":protocol", Value: "webtransport"}, 161 {Name: ":method", Value: http.MethodConnect}, 162 {Name: ":authority", Value: "quic.clemente.io"}, 163 {Name: ":path", Value: "/foo"}, 164 } 165 _, err := requestFromHeaders(headers) 166 Expect(err).To(MatchError("extended CONNECT: :scheme, :path and :authority must not be empty")) 167 }) 168 }) 169 170 Context("extracting the hostname from a request", func() { 171 var url *url.URL 172 173 BeforeEach(func() { 174 var err error 175 url, err = url.Parse("https://quic.clemente.io:1337") 176 Expect(err).ToNot(HaveOccurred()) 177 }) 178 179 It("uses req.URL.Host", func() { 180 req := &http.Request{URL: url} 181 Expect(hostnameFromRequest(req)).To(Equal("quic.clemente.io:1337")) 182 }) 183 184 It("uses req.URL.Host even if req.Host is available", func() { 185 req := &http.Request{ 186 Host: "www.example.org", 187 URL: url, 188 } 189 Expect(hostnameFromRequest(req)).To(Equal("quic.clemente.io:1337")) 190 }) 191 192 It("returns an empty hostname if nothing is set", func() { 193 Expect(hostnameFromRequest(&http.Request{})).To(BeEmpty()) 194 }) 195 }) 196 })