diff --git a/send_stream.go b/send_stream.go index ba6e0a34120..44270bfdaf1 100644 --- a/send_stream.go +++ b/send_stream.go @@ -117,13 +117,19 @@ func (s *sendStream) Write(p []byte) (int, error) { // When the user now calls Close(), this is much more likely to happen before we popped that last STREAM frame, // allowing us to set the FIN bit on that frame (instead of sending an empty STREAM frame with FIN). if s.canBufferStreamFrame() && len(s.dataForWriting) > 0 { - f := wire.GetStreamFrame() - f.Offset = s.writeOffset - f.StreamID = s.streamID - f.DataLenPresent = true - f.Data = f.Data[:len(s.dataForWriting)] - copy(f.Data, s.dataForWriting) - s.nextFrame = f + if s.nextFrame == nil { + f := wire.GetStreamFrame() + f.Offset = s.writeOffset + f.StreamID = s.streamID + f.DataLenPresent = true + f.Data = f.Data[:len(s.dataForWriting)] + copy(f.Data, s.dataForWriting) + s.nextFrame = f + } else { + l := len(s.nextFrame.Data) + s.nextFrame.Data = s.nextFrame.Data[:l+len(s.dataForWriting)] + copy(s.nextFrame.Data[l:], s.dataForWriting) + } s.dataForWriting = nil bytesWritten = len(p) copied = true @@ -176,7 +182,11 @@ func (s *sendStream) Write(p []byte) (int, error) { } func (s *sendStream) canBufferStreamFrame() bool { - return s.nextFrame == nil && protocol.ByteCount(len(s.dataForWriting)) <= protocol.MaxReceivePacketSize + var l protocol.ByteCount + if s.nextFrame != nil { + l = s.nextFrame.DataLen() + } + return l+protocol.ByteCount(len(s.dataForWriting)) <= protocol.MaxReceivePacketSize } // popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream diff --git a/send_stream_test.go b/send_stream_test.go index 409f65c61e1..b4ee9e4cc98 100644 --- a/send_stream_test.go +++ b/send_stream_test.go @@ -124,6 +124,29 @@ var _ = Describe("Send Stream", func() { Eventually(done).Should(BeClosed()) }) + It("bundles small writes", func() { + done := make(chan struct{}) + go func() { + defer GinkgoRecover() + mockSender.EXPECT().onHasStreamData(streamID).Times(2) + n, err := strWithTimeout.Write([]byte("foo")) + Expect(err).ToNot(HaveOccurred()) + Expect(n).To(Equal(3)) + n, err = strWithTimeout.Write([]byte("bar")) + Expect(err).ToNot(HaveOccurred()) + Expect(n).To(Equal(3)) + close(done) + }() + Eventually(done).Should(BeClosed()) // both Write calls returned without any data having been dequeued yet + mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount) + mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6)) + frame, _ := str.popStreamFrame(protocol.MaxByteCount) + f := frame.Frame.(*wire.StreamFrame) + Expect(f.Offset).To(BeZero()) + Expect(f.FinBit).To(BeFalse()) + Expect(f.Data).To(Equal([]byte("foobar"))) + }) + It("writes and gets data in multiple turns, for large writes", func() { mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(5) var totalBytesSent protocol.ByteCount