github.com/quic-go/quic-go@v0.44.0/http3/http_stream_test.go (about)

     1  package http3
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"math"
     7  	"net/http"
     8  
     9  	mockquic "github.com/quic-go/quic-go/internal/mocks/quic"
    10  	"github.com/quic-go/quic-go/internal/protocol"
    11  	"github.com/quic-go/quic-go/internal/qerr"
    12  
    13  	"github.com/quic-go/qpack"
    14  
    15  	. "github.com/onsi/ginkgo/v2"
    16  	. "github.com/onsi/gomega"
    17  	"go.uber.org/mock/gomock"
    18  )
    19  
    20  func getDataFrame(data []byte) []byte {
    21  	b := (&dataFrame{Length: uint64(len(data))}).Append(nil)
    22  	return append(b, data...)
    23  }
    24  
    25  var _ = Describe("Stream", func() {
    26  	Context("reading", func() {
    27  		var (
    28  			str           Stream
    29  			qstr          *mockquic.MockStream
    30  			buf           *bytes.Buffer
    31  			errorCbCalled bool
    32  		)
    33  
    34  		BeforeEach(func() {
    35  			buf = &bytes.Buffer{}
    36  			errorCbCalled = false
    37  			qstr = mockquic.NewMockStream(mockCtrl)
    38  			qstr.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes()
    39  			qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes()
    40  			conn := mockquic.NewMockEarlyConnection(mockCtrl)
    41  			conn.EXPECT().CloseWithError(gomock.Any(), gomock.Any()).Do(func(qerr.ApplicationErrorCode, string) error {
    42  				errorCbCalled = true
    43  				return nil
    44  			}).AnyTimes()
    45  			str = newStream(qstr, newConnection(conn, false, protocol.PerspectiveClient, nil), nil)
    46  		})
    47  
    48  		It("reads DATA frames in a single run", func() {
    49  			buf.Write(getDataFrame([]byte("foobar")))
    50  			b := make([]byte, 6)
    51  			n, err := str.Read(b)
    52  			Expect(err).ToNot(HaveOccurred())
    53  			Expect(n).To(Equal(6))
    54  			Expect(b).To(Equal([]byte("foobar")))
    55  		})
    56  
    57  		It("reads DATA frames in multiple runs", func() {
    58  			buf.Write(getDataFrame([]byte("foobar")))
    59  			b := make([]byte, 3)
    60  			n, err := str.Read(b)
    61  			Expect(err).ToNot(HaveOccurred())
    62  			Expect(n).To(Equal(3))
    63  			Expect(b).To(Equal([]byte("foo")))
    64  			n, err = str.Read(b)
    65  			Expect(err).ToNot(HaveOccurred())
    66  			Expect(n).To(Equal(3))
    67  			Expect(b).To(Equal([]byte("bar")))
    68  		})
    69  
    70  		It("reads DATA frames into too large buffers", func() {
    71  			buf.Write(getDataFrame([]byte("foobar")))
    72  			b := make([]byte, 10)
    73  			n, err := str.Read(b)
    74  			Expect(err).ToNot(HaveOccurred())
    75  			Expect(n).To(Equal(6))
    76  			Expect(b[:n]).To(Equal([]byte("foobar")))
    77  		})
    78  
    79  		It("reads DATA frames into too large buffers, in multiple runs", func() {
    80  			buf.Write(getDataFrame([]byte("foobar")))
    81  			b := make([]byte, 4)
    82  			n, err := str.Read(b)
    83  			Expect(err).ToNot(HaveOccurred())
    84  			Expect(n).To(Equal(4))
    85  			Expect(b).To(Equal([]byte("foob")))
    86  			n, err = str.Read(b)
    87  			Expect(err).ToNot(HaveOccurred())
    88  			Expect(n).To(Equal(2))
    89  			Expect(b[:n]).To(Equal([]byte("ar")))
    90  		})
    91  
    92  		It("reads multiple DATA frames", func() {
    93  			buf.Write(getDataFrame([]byte("foo")))
    94  			buf.Write(getDataFrame([]byte("bar")))
    95  			b := make([]byte, 6)
    96  			n, err := str.Read(b)
    97  			Expect(err).ToNot(HaveOccurred())
    98  			Expect(n).To(Equal(3))
    99  			Expect(b[:n]).To(Equal([]byte("foo")))
   100  			n, err = str.Read(b)
   101  			Expect(err).ToNot(HaveOccurred())
   102  			Expect(n).To(Equal(3))
   103  			Expect(b[:n]).To(Equal([]byte("bar")))
   104  		})
   105  
   106  		It("skips HEADERS frames", func() {
   107  			b := getDataFrame([]byte("foo"))
   108  			b = (&headersFrame{Length: 10}).Append(b)
   109  			b = append(b, make([]byte, 10)...)
   110  			b = append(b, getDataFrame([]byte("bar"))...)
   111  			buf.Write(b)
   112  			r := make([]byte, 6)
   113  			n, err := io.ReadFull(str, r)
   114  			Expect(err).ToNot(HaveOccurred())
   115  			Expect(n).To(Equal(6))
   116  			Expect(r).To(Equal([]byte("foobar")))
   117  		})
   118  
   119  		It("errors when it can't parse the frame", func() {
   120  			buf.Write([]byte("invalid"))
   121  			_, err := str.Read([]byte{0})
   122  			Expect(err).To(HaveOccurred())
   123  		})
   124  
   125  		It("errors on unexpected frames, and calls the error callback", func() {
   126  			b := (&settingsFrame{}).Append(nil)
   127  			buf.Write(b)
   128  			_, err := str.Read([]byte{0})
   129  			Expect(err).To(MatchError("peer sent an unexpected frame: *http3.settingsFrame"))
   130  			Expect(errorCbCalled).To(BeTrue())
   131  		})
   132  	})
   133  
   134  	Context("writing", func() {
   135  		It("writes data frames", func() {
   136  			buf := &bytes.Buffer{}
   137  			qstr := mockquic.NewMockStream(mockCtrl)
   138  			qstr.EXPECT().Write(gomock.Any()).DoAndReturn(buf.Write).AnyTimes()
   139  			str := newStream(qstr, nil, nil)
   140  			str.Write([]byte("foo"))
   141  			str.Write([]byte("foobar"))
   142  
   143  			fp := frameParser{r: buf}
   144  			f, err := fp.ParseNext()
   145  			Expect(err).ToNot(HaveOccurred())
   146  			Expect(f).To(Equal(&dataFrame{Length: 3}))
   147  			b := make([]byte, 3)
   148  			_, err = io.ReadFull(buf, b)
   149  			Expect(err).ToNot(HaveOccurred())
   150  			Expect(b).To(Equal([]byte("foo")))
   151  
   152  			fp = frameParser{r: buf}
   153  			f, err = fp.ParseNext()
   154  			Expect(err).ToNot(HaveOccurred())
   155  			Expect(f).To(Equal(&dataFrame{Length: 6}))
   156  			b = make([]byte, 6)
   157  			_, err = io.ReadFull(buf, b)
   158  			Expect(err).ToNot(HaveOccurred())
   159  			Expect(b).To(Equal([]byte("foobar")))
   160  		})
   161  	})
   162  })
   163  
   164  var _ = Describe("Request Stream", func() {
   165  	var str *requestStream
   166  	var qstr *mockquic.MockStream
   167  
   168  	BeforeEach(func() {
   169  		qstr = mockquic.NewMockStream(mockCtrl)
   170  		requestWriter := newRequestWriter()
   171  		conn := mockquic.NewMockEarlyConnection(mockCtrl)
   172  		str = newRequestStream(
   173  			newStream(qstr, newConnection(conn, false, protocol.PerspectiveClient, nil), nil),
   174  			requestWriter,
   175  			make(chan struct{}),
   176  			qpack.NewDecoder(func(qpack.HeaderField) {}),
   177  			true,
   178  			math.MaxUint64,
   179  		)
   180  	})
   181  
   182  	It("refuses to read before having read the response", func() {
   183  		_, err := str.Read(make([]byte, 100))
   184  		Expect(err).To(MatchError("http3: invalid use of RequestStream.Read: need to call ReadResponse first"))
   185  	})
   186  
   187  	It("prevents duplicate calls to SendRequestHeader", func() {
   188  		req, err := http.NewRequest(http.MethodGet, "https://quic-go.net", nil)
   189  		Expect(err).ToNot(HaveOccurred())
   190  		qstr.EXPECT().Write(gomock.Any()).AnyTimes()
   191  		Expect(str.SendRequestHeader(req)).To(Succeed())
   192  		Expect(str.SendRequestHeader(req)).To(MatchError("http3: invalid duplicate use of SendRequestHeader"))
   193  	})
   194  
   195  	It("reads after the response", func() {
   196  		req, err := http.NewRequest(http.MethodGet, "https://quic-go.net", nil)
   197  		Expect(err).ToNot(HaveOccurred())
   198  		qstr.EXPECT().Write(gomock.Any()).AnyTimes()
   199  		Expect(str.SendRequestHeader(req)).To(Succeed())
   200  
   201  		buf := bytes.NewBuffer(encodeResponse(200))
   202  		buf.Write((&dataFrame{Length: 6}).Append(nil))
   203  		buf.Write([]byte("foobar"))
   204  		qstr.EXPECT().Read(gomock.Any()).DoAndReturn(buf.Read).AnyTimes()
   205  		rsp, err := str.ReadResponse()
   206  		Expect(err).ToNot(HaveOccurred())
   207  		Expect(rsp.StatusCode).To(Equal(200))
   208  		b := make([]byte, 10)
   209  		n, err := str.Read(b)
   210  		Expect(err).ToNot(HaveOccurred())
   211  		Expect(n).To(Equal(6))
   212  		Expect(b[:n]).To(Equal([]byte("foobar")))
   213  	})
   214  })