From b8f36f35d51abf5ce1800114cc4ff72546606b16 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Wed, 1 Jul 2020 15:31:24 +0700 Subject: [PATCH] fix HTTP request writing if the Request.Body reads data and returns EOF --- http3/request_writer.go | 23 +++++++++++++++-------- http3/request_writer_test.go | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/http3/request_writer.go b/http3/request_writer.go index 1b99002d5..6720ff52f 100644 --- a/http3/request_writer.go +++ b/http3/request_writer.go @@ -57,14 +57,13 @@ func (w *requestWriter) WriteRequest(str quic.Stream, req *http.Request, gzip bo defer req.Body.Close() b := make([]byte, bodyCopyBufferSize) for { - n, err := req.Body.Read(b) - if err == io.EOF { - break - } - if err != nil { - str.CancelWrite(quic.ErrorCode(errorRequestCanceled)) - w.logger.Errorf("Error writing request: %s", err) - return + n, rerr := req.Body.Read(b) + if n == 0 { + if rerr == nil { + continue + } else if rerr == io.EOF { + break + } } buf := &bytes.Buffer{} (&dataFrame{Length: uint64(n)}).Write(buf) @@ -76,6 +75,14 @@ func (w *requestWriter) WriteRequest(str quic.Stream, req *http.Request, gzip bo w.logger.Errorf("Error writing request: %s", err) return } + if rerr != nil { + if rerr == io.EOF { + break + } + str.CancelWrite(quic.ErrorCode(errorRequestCanceled)) + w.logger.Errorf("Error writing request: %s", rerr) + return + } } str.Close() }() diff --git a/http3/request_writer_test.go b/http3/request_writer_test.go index 28a18790a..83c77204b 100644 --- a/http3/request_writer_test.go +++ b/http3/request_writer_test.go @@ -16,6 +16,12 @@ import ( . "github.com/onsi/gomega" ) +type foobarReader struct{} + +func (r *foobarReader) Read(b []byte) (int, error) { + return copy(b, []byte("foobar")), io.EOF +} + var _ = Describe("Request Writer", func() { var ( rw *requestWriter @@ -85,6 +91,23 @@ var _ = Describe("Request Writer", func() { Expect(frame.(*dataFrame).Length).To(BeEquivalentTo(6)) }) + It("writes a POST request, if the Body returns an EOF immediately", func() { + closed := make(chan struct{}) + str.EXPECT().Close().Do(func() { close(closed) }) + req, err := http.NewRequest("POST", "https://quic.clemente.io/upload.html", &foobarReader{}) + Expect(err).ToNot(HaveOccurred()) + Expect(rw.WriteRequest(str, req, false)).To(Succeed()) + + Eventually(closed).Should(BeClosed()) + headerFields := decode(strBuf) + Expect(headerFields).To(HaveKeyWithValue(":method", "POST")) + + frame, err := parseNextFrame(strBuf) + Expect(err).ToNot(HaveOccurred()) + Expect(frame).To(BeAssignableToTypeOf(&dataFrame{})) + Expect(frame.(*dataFrame).Length).To(BeEquivalentTo(6)) + }) + It("sends cookies", func() { str.EXPECT().Close() req, err := http.NewRequest("GET", "https://quic.clemente.io/", nil)