github.com/metacubex/quic-go@v0.44.1-0.20240520163451-20b689a59136/http3/request_writer_test.go (about)

     1  package http3
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"net/http"
     7  
     8  	mockquic "github.com/metacubex/quic-go/internal/mocks/quic"
     9  
    10  	"github.com/quic-go/qpack"
    11  	"go.uber.org/mock/gomock"
    12  
    13  	. "github.com/onsi/ginkgo/v2"
    14  	. "github.com/onsi/gomega"
    15  )
    16  
    17  var _ = Describe("Request Writer", func() {
    18  	var (
    19  		rw     *requestWriter
    20  		str    *mockquic.MockStream
    21  		strBuf *bytes.Buffer
    22  	)
    23  
    24  	decode := func(str io.Reader) map[string]string {
    25  		fp := frameParser{r: str}
    26  		frame, err := fp.ParseNext()
    27  		ExpectWithOffset(1, err).ToNot(HaveOccurred())
    28  		ExpectWithOffset(1, frame).To(BeAssignableToTypeOf(&headersFrame{}))
    29  		headersFrame := frame.(*headersFrame)
    30  		data := make([]byte, headersFrame.Length)
    31  		_, err = io.ReadFull(str, data)
    32  		ExpectWithOffset(1, err).ToNot(HaveOccurred())
    33  		decoder := qpack.NewDecoder(nil)
    34  		hfs, err := decoder.DecodeFull(data)
    35  		ExpectWithOffset(1, err).ToNot(HaveOccurred())
    36  		values := make(map[string]string)
    37  		for _, hf := range hfs {
    38  			values[hf.Name] = hf.Value
    39  		}
    40  		return values
    41  	}
    42  
    43  	BeforeEach(func() {
    44  		rw = newRequestWriter()
    45  		strBuf = &bytes.Buffer{}
    46  		str = mockquic.NewMockStream(mockCtrl)
    47  		str.EXPECT().Write(gomock.Any()).DoAndReturn(strBuf.Write).AnyTimes()
    48  	})
    49  
    50  	It("writes a GET request", func() {
    51  		req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/index.html?foo=bar", nil)
    52  		Expect(err).ToNot(HaveOccurred())
    53  		Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed())
    54  		headerFields := decode(strBuf)
    55  		Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io"))
    56  		Expect(headerFields).To(HaveKeyWithValue(":method", "GET"))
    57  		Expect(headerFields).To(HaveKeyWithValue(":path", "/index.html?foo=bar"))
    58  		Expect(headerFields).To(HaveKeyWithValue(":scheme", "https"))
    59  		Expect(headerFields).ToNot(HaveKey("accept-encoding"))
    60  	})
    61  
    62  	It("rejects invalid host headers", func() {
    63  		req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/index.html?foo=bar", nil)
    64  		Expect(err).ToNot(HaveOccurred())
    65  		req.Host = "foo@bar" // @ is invalid
    66  		Expect(rw.WriteRequestHeader(str, req, false)).To(MatchError("http3: invalid Host header"))
    67  	})
    68  
    69  	It("sends cookies", func() {
    70  		req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/", nil)
    71  		Expect(err).ToNot(HaveOccurred())
    72  		cookie1 := &http.Cookie{
    73  			Name:  "Cookie #1",
    74  			Value: "Value #1",
    75  		}
    76  		cookie2 := &http.Cookie{
    77  			Name:  "Cookie #2",
    78  			Value: "Value #2",
    79  		}
    80  		req.AddCookie(cookie1)
    81  		req.AddCookie(cookie2)
    82  		Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed())
    83  		headerFields := decode(strBuf)
    84  		Expect(headerFields).To(HaveKeyWithValue("cookie", `Cookie #1="Value #1"; Cookie #2="Value #2"`))
    85  	})
    86  
    87  	It("adds the header for gzip support", func() {
    88  		req, err := http.NewRequest(http.MethodGet, "https://quic.clemente.io/", nil)
    89  		Expect(err).ToNot(HaveOccurred())
    90  		Expect(rw.WriteRequestHeader(str, req, true)).To(Succeed())
    91  		headerFields := decode(strBuf)
    92  		Expect(headerFields).To(HaveKeyWithValue("accept-encoding", "gzip"))
    93  	})
    94  
    95  	It("writes a CONNECT request", func() {
    96  		req, err := http.NewRequest(http.MethodConnect, "https://quic.clemente.io/", nil)
    97  		Expect(err).ToNot(HaveOccurred())
    98  		Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed())
    99  		headerFields := decode(strBuf)
   100  		Expect(headerFields).To(HaveKeyWithValue(":method", "CONNECT"))
   101  		Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io"))
   102  		Expect(headerFields).ToNot(HaveKey(":path"))
   103  		Expect(headerFields).ToNot(HaveKey(":scheme"))
   104  		Expect(headerFields).ToNot(HaveKey(":protocol"))
   105  	})
   106  
   107  	It("writes an Extended CONNECT request", func() {
   108  		req, err := http.NewRequest(http.MethodConnect, "https://quic.clemente.io/foobar", nil)
   109  		Expect(err).ToNot(HaveOccurred())
   110  		req.Proto = "webtransport"
   111  		Expect(rw.WriteRequestHeader(str, req, false)).To(Succeed())
   112  		headerFields := decode(strBuf)
   113  		Expect(headerFields).To(HaveKeyWithValue(":authority", "quic.clemente.io"))
   114  		Expect(headerFields).To(HaveKeyWithValue(":method", "CONNECT"))
   115  		Expect(headerFields).To(HaveKeyWithValue(":path", "/foobar"))
   116  		Expect(headerFields).To(HaveKeyWithValue(":scheme", "https"))
   117  		Expect(headerFields).To(HaveKeyWithValue(":protocol", "webtransport"))
   118  	})
   119  })