From 6dd297379bde2d9a3a8338457f086bc2ea868a43 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Thu, 5 Jan 2017 10:53:46 +0700 Subject: [PATCH] set EndStream header in HeadersFrame --- h2quic/client.go | 7 +++++-- h2quic/client_test.go | 15 +++++++++++++++ h2quic/request_writer.go | 3 ++- h2quic/request_writer_test.go | 24 ++++++++++++++++++++---- 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/h2quic/client.go b/h2quic/client.go index 3b646b5a..14c69aa2 100644 --- a/h2quic/client.go +++ b/h2quic/client.go @@ -155,6 +155,8 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { return nil, errors.New("h2quic Client BUG: Do called for the wrong client") } + hasBody := (req.Body != nil) + c.mutex.Lock() c.highestOpenedStream += 2 dataStreamID := c.highestOpenedStream @@ -176,7 +178,9 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { if !c.t.disableCompression() && req.Header.Get("Accept-Encoding") == "" && req.Header.Get("Range") == "" && req.Method != "HEAD" { requestedGzip = true } - err = c.requestWriter.WriteRequest(req, dataStreamID, requestedGzip) + // TODO: add support for trailers + endStream := !hasBody + err = c.requestWriter.WriteRequest(req, dataStreamID, endStream, requestedGzip) if err != nil { c.Close(err) return nil, err @@ -217,7 +221,6 @@ func (c *Client) Do(req *http.Request) (*http.Response, error) { } res.Request = req - // TODO: correctly handle gzipped responses return res, nil } diff --git a/h2quic/client_test.go b/h2quic/client_test.go index 45007eb8..b893e2ae 100644 --- a/h2quic/client_test.go +++ b/h2quic/client_test.go @@ -204,6 +204,21 @@ var _ = Describe("Client", func() { }) }) + It("sets the EndStream header for requests without a body", func() { + go func() { client.Do(request) }() + Eventually(func() []byte { return headerStream.dataWritten.Bytes() }).ShouldNot(BeNil()) + mhf := getRequest(headerStream.dataWritten.Bytes()) + Expect(mhf.HeadersFrame.StreamEnded()).To(BeTrue()) + }) + + It("sets the EndStream header to false for requests with a body", func() { + request.Body = &mockBody{} + go func() { client.Do(request) }() + Eventually(func() []byte { return headerStream.dataWritten.Bytes() }).ShouldNot(BeNil()) + mhf := getRequest(headerStream.dataWritten.Bytes()) + Expect(mhf.HeadersFrame.StreamEnded()).To(BeFalse()) + }) + Context("gzip compression", func() { var gzippedData []byte // a gzipped foobar var response *http.Response diff --git a/h2quic/request_writer.go b/h2quic/request_writer.go index ec4b8cef..e837b0f5 100644 --- a/h2quic/request_writer.go +++ b/h2quic/request_writer.go @@ -34,7 +34,7 @@ func newRequestWriter(headerStream utils.Stream) *requestWriter { return rw } -func (w *requestWriter) WriteRequest(req *http.Request, dataStreamID protocol.StreamID, requestGzip bool) error { +func (w *requestWriter) WriteRequest(req *http.Request, dataStreamID protocol.StreamID, endStream, requestGzip bool) error { // TODO: add support for trailers // TODO: add support for gzip compression // TODO: write continuation frames, if the header frame is too long @@ -47,6 +47,7 @@ func (w *requestWriter) WriteRequest(req *http.Request, dataStreamID protocol.St return h2framer.WriteHeaders(http2.HeadersFrameParam{ StreamID: uint32(dataStreamID), EndHeaders: true, + EndStream: endStream, BlockFragment: w.hbuf.Bytes(), Priority: http2.PriorityParam{Weight: 0xff}, }) diff --git a/h2quic/request_writer_test.go b/h2quic/request_writer_test.go index 826ad828..24e0d1a7 100644 --- a/h2quic/request_writer_test.go +++ b/h2quic/request_writer_test.go @@ -44,7 +44,7 @@ var _ = Describe("Request", func() { It("writes a GET request", func() { req, err := http.NewRequest("GET", "https://quic.clemente.io/index.html?foo=bar", nil) Expect(err).ToNot(HaveOccurred()) - rw.WriteRequest(req, 1337, false) + rw.WriteRequest(req, 1337, true, false) headerFrame, headerFields := decode(headerStream.dataWritten.Bytes()) Expect(headerFrame.StreamID).To(Equal(uint32(1337))) Expect(headerFrame.HasPriority()).To(BeTrue()) @@ -55,10 +55,26 @@ var _ = Describe("Request", func() { Expect(headerFields).ToNot(HaveKey("accept-encoding")) }) + It("sets the EndStream header", func() { + req, err := http.NewRequest("GET", "https://quic.clemente.io/", nil) + Expect(err).ToNot(HaveOccurred()) + rw.WriteRequest(req, 1337, true, false) + headerFrame, _ := decode(headerStream.dataWritten.Bytes()) + Expect(headerFrame.StreamEnded()).To(BeTrue()) + }) + + It("doesn't set the EndStream header, if requested", func() { + req, err := http.NewRequest("GET", "https://quic.clemente.io/", nil) + Expect(err).ToNot(HaveOccurred()) + rw.WriteRequest(req, 1337, false, false) + headerFrame, _ := decode(headerStream.dataWritten.Bytes()) + Expect(headerFrame.StreamEnded()).To(BeFalse()) + }) + It("requests gzip compression, if requested", func() { req, err := http.NewRequest("GET", "https://quic.clemente.io/index.html?foo=bar", nil) Expect(err).ToNot(HaveOccurred()) - rw.WriteRequest(req, 1337, true) + rw.WriteRequest(req, 1337, true, true) _, headerFields := decode(headerStream.dataWritten.Bytes()) Expect(headerFields).To(HaveKeyWithValue("accept-encoding", "gzip")) }) @@ -68,7 +84,7 @@ var _ = Describe("Request", func() { form.Add("foo", "bar") req, err := http.NewRequest("POST", "https://quic.clemente.io/upload.html", strings.NewReader(form.Encode())) Expect(err).ToNot(HaveOccurred()) - rw.WriteRequest(req, 5, false) + rw.WriteRequest(req, 5, true, false) _, headerFields := decode(headerStream.dataWritten.Bytes()) Expect(headerFields).To(HaveKeyWithValue(":method", "POST")) Expect(headerFields).To(HaveKey("content-length")) @@ -90,7 +106,7 @@ var _ = Describe("Request", func() { } req.AddCookie(cookie1) req.AddCookie(cookie2) - rw.WriteRequest(req, 11, false) + rw.WriteRequest(req, 11, true, false) _, headerFields := decode(headerStream.dataWritten.Bytes()) Expect(headerFields).To(HaveKeyWithValue("cookie", "Cookie #1=Value #1; Cookie #2=Value #2")) })