mirror of
https://github.com/refraction-networking/uquic.git
synced 2025-04-04 12:47:36 +03:00
make it more likely that a STREAM frame is bundled with the FIN
This commit is contained in:
parent
81daa8afd3
commit
9905774c40
2 changed files with 359 additions and 127 deletions
150
send_stream.go
150
send_stream.go
|
@ -46,7 +46,8 @@ type sendStream struct {
|
||||||
finSent bool // set when a STREAM_FRAME with FIN bit has been sent
|
finSent bool // set when a STREAM_FRAME with FIN bit has been sent
|
||||||
completed bool // set when this stream has been reported to the streamSender as completed
|
completed bool // set when this stream has been reported to the streamSender as completed
|
||||||
|
|
||||||
dataForWriting []byte
|
dataForWriting []byte // during a Write() call, this slice is the part of p that still needs to be sent out
|
||||||
|
nextFrame *wire.StreamFrame
|
||||||
|
|
||||||
writeChan chan struct{}
|
writeChan chan struct{}
|
||||||
deadline time.Time
|
deadline time.Time
|
||||||
|
@ -108,8 +109,27 @@ func (s *sendStream) Write(p []byte) (int, error) {
|
||||||
notifiedSender bool
|
notifiedSender bool
|
||||||
)
|
)
|
||||||
for {
|
for {
|
||||||
|
var copied bool
|
||||||
|
var deadline time.Time
|
||||||
|
// As soon as dataForWriting becomes smaller than a certain size x, we copy all the data to a STREAM frame (s.nextFrame),
|
||||||
|
// which can the be popped the next time we assemble a packet.
|
||||||
|
// This allows us to return Write() when all data but x bytes have been sent out.
|
||||||
|
// 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
|
||||||
|
s.dataForWriting = nil
|
||||||
|
bytesWritten = len(p)
|
||||||
|
copied = true
|
||||||
|
} else {
|
||||||
bytesWritten = len(p) - len(s.dataForWriting)
|
bytesWritten = len(p) - len(s.dataForWriting)
|
||||||
deadline := s.deadline
|
deadline = s.deadline
|
||||||
if !deadline.IsZero() {
|
if !deadline.IsZero() {
|
||||||
if !time.Now().Before(deadline) {
|
if !time.Now().Before(deadline) {
|
||||||
s.dataForWriting = nil
|
s.dataForWriting = nil
|
||||||
|
@ -123,12 +143,17 @@ func (s *sendStream) Write(p []byte) (int, error) {
|
||||||
if s.dataForWriting == nil || s.canceledWrite || s.closedForShutdown {
|
if s.dataForWriting == nil || s.canceledWrite || s.closedForShutdown {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
if !notifiedSender {
|
if !notifiedSender {
|
||||||
s.sender.onHasStreamData(s.streamID) // must be called without holding the mutex
|
s.sender.onHasStreamData(s.streamID) // must be called without holding the mutex
|
||||||
notifiedSender = true
|
notifiedSender = true
|
||||||
}
|
}
|
||||||
|
if copied {
|
||||||
|
s.mutex.Lock()
|
||||||
|
break
|
||||||
|
}
|
||||||
if deadline.IsZero() {
|
if deadline.IsZero() {
|
||||||
<-s.writeChan
|
<-s.writeChan
|
||||||
} else {
|
} else {
|
||||||
|
@ -149,6 +174,10 @@ func (s *sendStream) Write(p []byte) (int, error) {
|
||||||
return bytesWritten, nil
|
return bytesWritten, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *sendStream) canBufferStreamFrame() bool {
|
||||||
|
return s.nextFrame == nil && protocol.ByteCount(len(s.dataForWriting)) <= protocol.MaxReceivePacketSize
|
||||||
|
}
|
||||||
|
|
||||||
// popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream
|
// popStreamFrame returns the next STREAM frame that is supposed to be sent on this stream
|
||||||
// maxBytes is the maximum length this frame (including frame header) will have.
|
// maxBytes is the maximum length this frame (including frame header) will have.
|
||||||
func (s *sendStream) popStreamFrame(maxBytes protocol.ByteCount) (*ackhandler.Frame, bool /* has more data to send */) {
|
func (s *sendStream) popStreamFrame(maxBytes protocol.ByteCount) (*ackhandler.Frame, bool /* has more data to send */) {
|
||||||
|
@ -182,6 +211,63 @@ func (s *sendStream) popNewOrRetransmittedStreamFrame(maxBytes protocol.ByteCoun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(s.dataForWriting) == 0 && s.nextFrame == nil {
|
||||||
|
if s.finishedWriting && !s.finSent {
|
||||||
|
s.finSent = true
|
||||||
|
return &wire.StreamFrame{
|
||||||
|
StreamID: s.streamID,
|
||||||
|
Offset: s.writeOffset,
|
||||||
|
DataLenPresent: true,
|
||||||
|
FinBit: true,
|
||||||
|
}, false
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
sendWindow := s.flowController.SendWindowSize()
|
||||||
|
if sendWindow == 0 {
|
||||||
|
if isBlocked, offset := s.flowController.IsNewlyBlocked(); isBlocked {
|
||||||
|
s.sender.queueControlFrame(&wire.StreamDataBlockedFrame{
|
||||||
|
StreamID: s.streamID,
|
||||||
|
DataLimit: offset,
|
||||||
|
})
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
return nil, true
|
||||||
|
}
|
||||||
|
|
||||||
|
f, hasMoreData := s.popNewStreamFrame(maxBytes, sendWindow)
|
||||||
|
if dataLen := f.DataLen(); dataLen > 0 {
|
||||||
|
s.writeOffset += f.DataLen()
|
||||||
|
s.flowController.AddBytesSent(f.DataLen())
|
||||||
|
}
|
||||||
|
f.FinBit = s.finishedWriting && s.dataForWriting == nil && s.nextFrame == nil && !s.finSent
|
||||||
|
if f.FinBit {
|
||||||
|
s.finSent = true
|
||||||
|
}
|
||||||
|
return f, hasMoreData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *sendStream) popNewStreamFrame(maxBytes, sendWindow protocol.ByteCount) (*wire.StreamFrame, bool) {
|
||||||
|
if s.nextFrame != nil {
|
||||||
|
nextFrame := s.nextFrame
|
||||||
|
s.nextFrame = nil
|
||||||
|
|
||||||
|
maxDataLen := utils.MinByteCount(sendWindow, nextFrame.MaxDataLen(maxBytes, s.version))
|
||||||
|
if nextFrame.DataLen() > maxDataLen {
|
||||||
|
s.nextFrame = wire.GetStreamFrame()
|
||||||
|
s.nextFrame.StreamID = s.streamID
|
||||||
|
s.nextFrame.Offset = s.writeOffset + maxDataLen
|
||||||
|
s.nextFrame.Data = s.nextFrame.Data[:nextFrame.DataLen()-maxDataLen]
|
||||||
|
s.nextFrame.DataLenPresent = true
|
||||||
|
copy(s.nextFrame.Data, nextFrame.Data[maxDataLen:])
|
||||||
|
nextFrame.Data = nextFrame.Data[:maxDataLen]
|
||||||
|
} else {
|
||||||
|
s.signalWrite()
|
||||||
|
}
|
||||||
|
return nextFrame, s.nextFrame != nil || s.dataForWriting != nil
|
||||||
|
}
|
||||||
|
|
||||||
f := wire.GetStreamFrame()
|
f := wire.GetStreamFrame()
|
||||||
f.FinBit = false
|
f.FinBit = false
|
||||||
f.StreamID = s.streamID
|
f.StreamID = s.streamID
|
||||||
|
@ -189,8 +275,7 @@ func (s *sendStream) popNewOrRetransmittedStreamFrame(maxBytes protocol.ByteCoun
|
||||||
f.DataLenPresent = true
|
f.DataLenPresent = true
|
||||||
f.Data = f.Data[:0]
|
f.Data = f.Data[:0]
|
||||||
|
|
||||||
hasMoreData := s.popNewStreamFrame(f, maxBytes)
|
hasMoreData := s.popNewStreamFrameWithoutBuffer(f, maxBytes, sendWindow)
|
||||||
|
|
||||||
if len(f.Data) == 0 && !f.FinBit {
|
if len(f.Data) == 0 && !f.FinBit {
|
||||||
f.PutBack()
|
f.PutBack()
|
||||||
return nil, hasMoreData
|
return nil, hasMoreData
|
||||||
|
@ -198,33 +283,14 @@ func (s *sendStream) popNewOrRetransmittedStreamFrame(maxBytes protocol.ByteCoun
|
||||||
return f, hasMoreData
|
return f, hasMoreData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sendStream) popNewStreamFrame(f *wire.StreamFrame, maxBytes protocol.ByteCount) bool {
|
func (s *sendStream) popNewStreamFrameWithoutBuffer(f *wire.StreamFrame, maxBytes, sendWindow protocol.ByteCount) bool {
|
||||||
maxDataLen := f.MaxDataLen(maxBytes, s.version)
|
maxDataLen := f.MaxDataLen(maxBytes, s.version)
|
||||||
if maxDataLen == 0 { // a STREAM frame must have at least one byte of data
|
if maxDataLen == 0 { // a STREAM frame must have at least one byte of data
|
||||||
return s.dataForWriting != nil
|
return s.dataForWriting != nil || s.nextFrame != nil || s.finishedWriting
|
||||||
}
|
}
|
||||||
s.getDataForWriting(f, maxDataLen)
|
s.getDataForWriting(f, utils.MinByteCount(maxDataLen, sendWindow))
|
||||||
if len(f.Data) == 0 && !f.FinBit {
|
|
||||||
// this can happen if:
|
return s.dataForWriting != nil || s.nextFrame != nil || s.finishedWriting
|
||||||
// - popStreamFrame is called but there's no data for writing
|
|
||||||
// - there's data for writing, but the stream is stream-level flow control blocked
|
|
||||||
// - there's data for writing, but the stream is connection-level flow control blocked
|
|
||||||
if s.dataForWriting == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if isBlocked, offset := s.flowController.IsNewlyBlocked(); isBlocked {
|
|
||||||
s.sender.queueControlFrame(&wire.StreamDataBlockedFrame{
|
|
||||||
StreamID: s.streamID,
|
|
||||||
DataLimit: offset,
|
|
||||||
})
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if f.FinBit {
|
|
||||||
s.finSent = true
|
|
||||||
}
|
|
||||||
return s.dataForWriting != nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sendStream) maybeGetRetransmission(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool /* has more retransmissions */) {
|
func (s *sendStream) maybeGetRetransmission(maxBytes protocol.ByteCount) (*wire.StreamFrame, bool /* has more retransmissions */) {
|
||||||
|
@ -245,29 +311,19 @@ func (s *sendStream) hasData() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sendStream) getDataForWriting(f *wire.StreamFrame, maxBytes protocol.ByteCount) {
|
func (s *sendStream) getDataForWriting(f *wire.StreamFrame, maxBytes protocol.ByteCount) {
|
||||||
if s.dataForWriting == nil {
|
if protocol.ByteCount(len(s.dataForWriting)) <= maxBytes {
|
||||||
f.FinBit = s.finishedWriting && !s.finSent
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
maxBytes = utils.MinByteCount(maxBytes, s.flowController.SendWindowSize())
|
|
||||||
if maxBytes == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if protocol.ByteCount(len(s.dataForWriting)) > maxBytes {
|
|
||||||
f.Data = f.Data[:maxBytes]
|
|
||||||
copy(f.Data, s.dataForWriting)
|
|
||||||
s.dataForWriting = s.dataForWriting[maxBytes:]
|
|
||||||
} else {
|
|
||||||
f.Data = f.Data[:len(s.dataForWriting)]
|
f.Data = f.Data[:len(s.dataForWriting)]
|
||||||
copy(f.Data, s.dataForWriting)
|
copy(f.Data, s.dataForWriting)
|
||||||
s.dataForWriting = nil
|
s.dataForWriting = nil
|
||||||
s.signalWrite()
|
s.signalWrite()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
f.Data = f.Data[:maxBytes]
|
||||||
|
copy(f.Data, s.dataForWriting)
|
||||||
|
s.dataForWriting = s.dataForWriting[maxBytes:]
|
||||||
|
if s.canBufferStreamFrame() {
|
||||||
|
s.signalWrite()
|
||||||
}
|
}
|
||||||
s.writeOffset += f.DataLen()
|
|
||||||
s.flowController.AddBytesSent(f.DataLen())
|
|
||||||
f.FinBit = s.finishedWriting && s.dataForWriting == nil && !s.finSent
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *sendStream) frameAcked(f wire.Frame) {
|
func (s *sendStream) frameAcked(f wire.Frame) {
|
||||||
|
@ -357,7 +413,7 @@ func (s *sendStream) cancelWriteImpl(errorCode protocol.ApplicationErrorCode, wr
|
||||||
|
|
||||||
func (s *sendStream) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) {
|
func (s *sendStream) handleMaxStreamDataFrame(frame *wire.MaxStreamDataFrame) {
|
||||||
s.mutex.Lock()
|
s.mutex.Lock()
|
||||||
hasStreamData := s.dataForWriting != nil
|
hasStreamData := s.dataForWriting != nil || s.nextFrame != nil
|
||||||
s.mutex.Unlock()
|
s.mutex.Unlock()
|
||||||
|
|
||||||
s.flowController.UpdateSendWindow(frame.ByteOffset)
|
s.flowController.UpdateSendWindow(frame.ByteOffset)
|
||||||
|
|
|
@ -37,13 +37,33 @@ var _ = Describe("Send Stream", func() {
|
||||||
strWithTimeout = gbytes.TimeoutWriter(str, timeout)
|
strWithTimeout = gbytes.TimeoutWriter(str, timeout)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expectedFrameHeaderLen := func(offset protocol.ByteCount) protocol.ByteCount {
|
||||||
|
return (&wire.StreamFrame{
|
||||||
|
StreamID: streamID,
|
||||||
|
Offset: offset,
|
||||||
|
DataLenPresent: true,
|
||||||
|
}).Length(protocol.VersionWhatever)
|
||||||
|
}
|
||||||
|
|
||||||
waitForWrite := func() {
|
waitForWrite := func() {
|
||||||
EventuallyWithOffset(0, func() []byte {
|
EventuallyWithOffset(0, func() bool {
|
||||||
str.mutex.Lock()
|
str.mutex.Lock()
|
||||||
data := str.dataForWriting
|
hasData := str.dataForWriting != nil || str.nextFrame != nil
|
||||||
str.mutex.Unlock()
|
str.mutex.Unlock()
|
||||||
return data
|
return hasData
|
||||||
}).ShouldNot(BeEmpty())
|
}).Should(BeTrue())
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataAtOffset := func(offset, length protocol.ByteCount) []byte {
|
||||||
|
b := make([]byte, length)
|
||||||
|
for i := protocol.ByteCount(0); i < length; i++ {
|
||||||
|
b[i] = uint8(offset + i)
|
||||||
|
}
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
getData := func(length protocol.ByteCount) []byte {
|
||||||
|
return getDataAtOffset(0, length)
|
||||||
}
|
}
|
||||||
|
|
||||||
It("gets stream id", func() {
|
It("gets stream id", func() {
|
||||||
|
@ -52,19 +72,19 @@ var _ = Describe("Send Stream", func() {
|
||||||
|
|
||||||
Context("writing", func() {
|
Context("writing", func() {
|
||||||
It("writes and gets all data at once", func() {
|
It("writes and gets all data at once", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999))
|
|
||||||
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6))
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
defer close(done)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
n, err := strWithTimeout.Write([]byte("foobar"))
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(n).To(Equal(6))
|
Expect(n).To(Equal(6))
|
||||||
close(done)
|
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
frame, _ := str.popStreamFrame(1000)
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount)
|
||||||
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6))
|
||||||
|
frame, _ := str.popStreamFrame(protocol.MaxByteCount)
|
||||||
f := frame.Frame.(*wire.StreamFrame)
|
f := frame.Frame.(*wire.StreamFrame)
|
||||||
Expect(f.Data).To(Equal([]byte("foobar")))
|
Expect(f.Data).To(Equal([]byte("foobar")))
|
||||||
Expect(f.FinBit).To(BeFalse())
|
Expect(f.FinBit).To(BeFalse())
|
||||||
|
@ -76,26 +96,25 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("writes and gets data in two turns", func() {
|
It("writes and gets data in two turns", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
|
||||||
frameHeaderLen := protocol.ByteCount(4)
|
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999)).Times(2)
|
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any() /* protocol.ByteCount(3)*/).Times(2)
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
n, err := strWithTimeout.Write([]byte("foobar"))
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(n).To(Equal(6))
|
Expect(n).To(Equal(6))
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
frame, _ := str.popStreamFrame(3 + frameHeaderLen)
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2)
|
||||||
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3)).Times(2)
|
||||||
|
frame, _ := str.popStreamFrame(expectedFrameHeaderLen(0) + 3)
|
||||||
f := frame.Frame.(*wire.StreamFrame)
|
f := frame.Frame.(*wire.StreamFrame)
|
||||||
Expect(f.Data).To(Equal([]byte("foo")))
|
|
||||||
Expect(f.FinBit).To(BeFalse())
|
|
||||||
Expect(f.Offset).To(BeZero())
|
Expect(f.Offset).To(BeZero())
|
||||||
|
Expect(f.FinBit).To(BeFalse())
|
||||||
|
Expect(f.Data).To(Equal([]byte("foo")))
|
||||||
Expect(f.DataLenPresent).To(BeTrue())
|
Expect(f.DataLenPresent).To(BeTrue())
|
||||||
frame, _ = str.popStreamFrame(100)
|
frame, _ = str.popStreamFrame(protocol.MaxByteCount)
|
||||||
f = frame.Frame.(*wire.StreamFrame)
|
f = frame.Frame.(*wire.StreamFrame)
|
||||||
Expect(f.Data).To(Equal([]byte("bar")))
|
Expect(f.Data).To(Equal([]byte("bar")))
|
||||||
Expect(f.FinBit).To(BeFalse())
|
Expect(f.FinBit).To(BeFalse())
|
||||||
|
@ -105,6 +124,85 @@ var _ = Describe("Send Stream", func() {
|
||||||
Eventually(done).Should(BeClosed())
|
Eventually(done).Should(BeClosed())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("writes and gets data in multiple turns, for large writes", func() {
|
||||||
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(5)
|
||||||
|
var totalBytesSent protocol.ByteCount
|
||||||
|
mockFC.EXPECT().AddBytesSent(gomock.Any()).Do(func(l protocol.ByteCount) { totalBytesSent += l }).Times(5)
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
n, err := strWithTimeout.Write(getData(5000))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(n).To(Equal(5000))
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
waitForWrite()
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
frame, _ := str.popStreamFrame(1100)
|
||||||
|
f := frame.Frame.(*wire.StreamFrame)
|
||||||
|
Expect(f.Offset).To(BeNumerically("~", 1100*i, 10*i))
|
||||||
|
Expect(f.FinBit).To(BeFalse())
|
||||||
|
Expect(f.Data).To(Equal(getDataAtOffset(f.Offset, f.DataLen())))
|
||||||
|
Expect(f.DataLenPresent).To(BeTrue())
|
||||||
|
}
|
||||||
|
Expect(totalBytesSent).To(Equal(protocol.ByteCount(5000)))
|
||||||
|
Eventually(done).Should(BeClosed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("unblocks Write as soon as a STREAM frame can be buffered", func() {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
defer close(done)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
_, err := strWithTimeout.Write(getData(protocol.MaxReceivePacketSize + 3))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}()
|
||||||
|
waitForWrite()
|
||||||
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2)
|
||||||
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2))
|
||||||
|
frame, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0) + 2)
|
||||||
|
Expect(hasMoreData).To(BeTrue())
|
||||||
|
f := frame.Frame.(*wire.StreamFrame)
|
||||||
|
Expect(f.DataLen()).To(Equal(protocol.ByteCount(2)))
|
||||||
|
Consistently(done).ShouldNot(BeClosed())
|
||||||
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(1))
|
||||||
|
frame, hasMoreData = str.popStreamFrame(expectedFrameHeaderLen(1) + 1)
|
||||||
|
Expect(hasMoreData).To(BeTrue())
|
||||||
|
f = frame.Frame.(*wire.StreamFrame)
|
||||||
|
Expect(f.DataLen()).To(Equal(protocol.ByteCount(1)))
|
||||||
|
Eventually(done).Should(BeClosed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("only unblocks Write once a previously buffered STREAM frame has been fully dequeued", func() {
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
_, err := strWithTimeout.Write([]byte("foobar"))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
defer close(done)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
_, err := str.Write(getData(protocol.MaxReceivePacketSize))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}()
|
||||||
|
waitForWrite()
|
||||||
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2)
|
||||||
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2))
|
||||||
|
frame, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0) + 2)
|
||||||
|
Expect(hasMoreData).To(BeTrue())
|
||||||
|
f := frame.Frame.(*wire.StreamFrame)
|
||||||
|
Expect(f.Data).To(Equal([]byte("fo")))
|
||||||
|
Consistently(done).ShouldNot(BeClosed())
|
||||||
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(4))
|
||||||
|
frame, hasMoreData = str.popStreamFrame(expectedFrameHeaderLen(2) + 4)
|
||||||
|
Expect(hasMoreData).To(BeTrue())
|
||||||
|
f = frame.Frame.(*wire.StreamFrame)
|
||||||
|
Expect(f.Data).To(Equal([]byte("obar")))
|
||||||
|
Eventually(done).Should(BeClosed())
|
||||||
|
})
|
||||||
|
|
||||||
It("popStreamFrame returns nil if no data is available", func() {
|
It("popStreamFrame returns nil if no data is available", func() {
|
||||||
frame, hasMoreData := str.popStreamFrame(1000)
|
frame, hasMoreData := str.popStreamFrame(1000)
|
||||||
Expect(frame).To(BeNil())
|
Expect(frame).To(BeNil())
|
||||||
|
@ -112,43 +210,45 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("says if it has more data for writing", func() {
|
It("says if it has more data for writing", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999)).Times(2)
|
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2)
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
defer close(done)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
n, err := strWithTimeout.Write(bytes.Repeat([]byte{0}, 100))
|
n, err := strWithTimeout.Write(bytes.Repeat([]byte{0}, 100))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(n).To(Equal(100))
|
Expect(n).To(Equal(100))
|
||||||
close(done)
|
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2)
|
||||||
|
mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2)
|
||||||
frame, hasMoreData := str.popStreamFrame(50)
|
frame, hasMoreData := str.popStreamFrame(50)
|
||||||
Expect(frame).ToNot(BeNil())
|
Expect(frame).ToNot(BeNil())
|
||||||
|
Expect(frame.Frame.(*wire.StreamFrame).FinBit).To(BeFalse())
|
||||||
Expect(hasMoreData).To(BeTrue())
|
Expect(hasMoreData).To(BeTrue())
|
||||||
frame, hasMoreData = str.popStreamFrame(1000)
|
frame, hasMoreData = str.popStreamFrame(protocol.MaxByteCount)
|
||||||
Expect(frame).ToNot(BeNil())
|
Expect(frame).ToNot(BeNil())
|
||||||
|
Expect(frame.Frame.(*wire.StreamFrame).FinBit).To(BeFalse())
|
||||||
Expect(hasMoreData).To(BeFalse())
|
Expect(hasMoreData).To(BeFalse())
|
||||||
frame, _ = str.popStreamFrame(1000)
|
frame, _ = str.popStreamFrame(protocol.MaxByteCount)
|
||||||
Expect(frame).To(BeNil())
|
Expect(frame).To(BeNil())
|
||||||
Eventually(done).Should(BeClosed())
|
Eventually(done).Should(BeClosed())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("copies the slice while writing", func() {
|
It("copies the slice while writing", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
|
||||||
frameHeaderSize := protocol.ByteCount(4)
|
frameHeaderSize := protocol.ByteCount(4)
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999)).Times(2)
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2)
|
||||||
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(1))
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(1))
|
||||||
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2))
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(2))
|
||||||
s := []byte("foo")
|
s := []byte("foo")
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
defer close(done)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
n, err := strWithTimeout.Write(s)
|
n, err := strWithTimeout.Write(s)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(n).To(Equal(3))
|
Expect(n).To(Equal(3))
|
||||||
close(done)
|
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
frame, _ := str.popStreamFrame(frameHeaderSize + 1)
|
frame, _ := str.popStreamFrame(frameHeaderSize + 1)
|
||||||
|
@ -178,7 +278,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
It("cancels the context when Close is called", func() {
|
It("cancels the context when Close is called", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
Expect(str.Context().Done()).ToNot(BeClosed())
|
Expect(str.Context().Done()).ToNot(BeClosed())
|
||||||
str.Close()
|
Expect(str.Close()).To(Succeed())
|
||||||
Expect(str.Context().Done()).To(BeClosed())
|
Expect(str.Context().Done()).To(BeClosed())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -190,13 +290,13 @@ var _ = Describe("Send Stream", func() {
|
||||||
StreamID: streamID,
|
StreamID: streamID,
|
||||||
DataLimit: 12,
|
DataLimit: 12,
|
||||||
})
|
})
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
defer close(done)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
_, err := str.Write([]byte("foobar"))
|
_, err := str.Write([]byte("foobar"))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
close(done)
|
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
f, hasMoreData := str.popStreamFrame(1000)
|
f, hasMoreData := str.popStreamFrame(1000)
|
||||||
|
@ -208,22 +308,20 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("says that it doesn't have any more data, when it is flow control blocked", func() {
|
It("says that it doesn't have any more data, when it is flow control blocked", func() {
|
||||||
frameHeaderSize := protocol.ByteCount(4)
|
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
|
||||||
|
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
defer close(done)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
_, err := str.Write([]byte("foobar"))
|
_, err := str.Write([]byte("foobar"))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
close(done)
|
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
|
|
||||||
// first pop a STREAM frame of the maximum size allowed by flow control
|
// first pop a STREAM frame of the maximum size allowed by flow control
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(3))
|
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(3))
|
||||||
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3))
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3))
|
||||||
f, hasMoreData := str.popStreamFrame(frameHeaderSize + 3)
|
f, hasMoreData := str.popStreamFrame(expectedFrameHeaderLen(0) + 3)
|
||||||
Expect(f).ToNot(BeNil())
|
Expect(f).ToNot(BeNil())
|
||||||
Expect(hasMoreData).To(BeTrue())
|
Expect(hasMoreData).To(BeTrue())
|
||||||
|
|
||||||
|
@ -256,7 +354,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
deadline := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
deadline := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
||||||
str.SetWriteDeadline(deadline)
|
str.SetWriteDeadline(deadline)
|
||||||
n, err := strWithTimeout.Write([]byte("foobar"))
|
n, err := strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError(errDeadline))
|
Expect(err).To(MatchError(errDeadline))
|
||||||
Expect(n).To(BeZero())
|
Expect(n).To(BeZero())
|
||||||
Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond)))
|
Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond)))
|
||||||
|
@ -268,7 +366,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, err := str.Write([]byte("foobar"))
|
_, err := str.Write(getData(5000))
|
||||||
Expect(err).To(MatchError(errDeadline))
|
Expect(err).To(MatchError(errDeadline))
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
@ -278,8 +376,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns the number of bytes written, when the deadline expires", func() {
|
It("returns the number of bytes written, when the deadline expires", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes()
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(10000)).AnyTimes()
|
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
||||||
deadline := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
deadline := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
||||||
str.SetWriteDeadline(deadline)
|
str.SetWriteDeadline(deadline)
|
||||||
|
@ -287,11 +384,12 @@ var _ = Describe("Send Stream", func() {
|
||||||
writeReturned := make(chan struct{})
|
writeReturned := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
defer close(writeReturned)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
var err error
|
var err error
|
||||||
n, err = strWithTimeout.Write(bytes.Repeat([]byte{0}, 100))
|
n, err = strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError(errDeadline))
|
Expect(err).To(MatchError(errDeadline))
|
||||||
Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond)))
|
Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*time.Millisecond)))
|
||||||
close(writeReturned)
|
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
frame, hasMoreData := str.popStreamFrame(50)
|
frame, hasMoreData := str.popStreamFrame(50)
|
||||||
|
@ -302,17 +400,17 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("doesn't pop any data after the deadline expired", func() {
|
It("doesn't pop any data after the deadline expired", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes()
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(10000)).AnyTimes()
|
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
||||||
deadline := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
deadline := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
||||||
str.SetWriteDeadline(deadline)
|
str.SetWriteDeadline(deadline)
|
||||||
writeReturned := make(chan struct{})
|
writeReturned := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, err := strWithTimeout.Write(bytes.Repeat([]byte{0}, 100))
|
defer close(writeReturned)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
_, err := strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError(errDeadline))
|
Expect(err).To(MatchError(errDeadline))
|
||||||
close(writeReturned)
|
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
frame, hasMoreData := str.popStreamFrame(50)
|
frame, hasMoreData := str.popStreamFrame(50)
|
||||||
|
@ -339,7 +437,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
n, err := strWithTimeout.Write([]byte("foobar"))
|
n, err := strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError(errDeadline))
|
Expect(err).To(MatchError(errDeadline))
|
||||||
Expect(n).To(BeZero())
|
Expect(n).To(BeZero())
|
||||||
Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond)))
|
Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond)))
|
||||||
|
@ -361,7 +459,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
}()
|
}()
|
||||||
str.SetWriteDeadline(deadline1)
|
str.SetWriteDeadline(deadline1)
|
||||||
runtime.Gosched()
|
runtime.Gosched()
|
||||||
_, err := strWithTimeout.Write([]byte("foobar"))
|
_, err := strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError(errDeadline))
|
Expect(err).To(MatchError(errDeadline))
|
||||||
Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond)))
|
Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond)))
|
||||||
Eventually(done).Should(BeClosed())
|
Eventually(done).Should(BeClosed())
|
||||||
|
@ -383,7 +481,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, err := strWithTimeout.Write([]byte("foobar"))
|
_, err := strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError("test done"))
|
Expect(err).To(MatchError("test done"))
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
@ -412,27 +510,55 @@ var _ = Describe("Send Stream", func() {
|
||||||
f := frame.Frame.(*wire.StreamFrame)
|
f := frame.Frame.(*wire.StreamFrame)
|
||||||
Expect(f.Data).To(BeEmpty())
|
Expect(f.Data).To(BeEmpty())
|
||||||
Expect(f.FinBit).To(BeTrue())
|
Expect(f.FinBit).To(BeTrue())
|
||||||
|
Expect(f.DataLenPresent).To(BeTrue())
|
||||||
Expect(hasMoreData).To(BeFalse())
|
Expect(hasMoreData).To(BeFalse())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("doesn't send a FIN when there's still data", func() {
|
It("doesn't send a FIN when there's still data", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
const frameHeaderLen protocol.ByteCount = 4
|
||||||
frameHeaderLen := protocol.ByteCount(4)
|
mockSender.EXPECT().onHasStreamData(streamID).Times(2)
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999)).Times(2)
|
_, err := strWithTimeout.Write([]byte("foobar"))
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2)
|
Expect(err).ToNot(HaveOccurred())
|
||||||
str.dataForWriting = []byte("foobar")
|
|
||||||
Expect(str.Close()).To(Succeed())
|
Expect(str.Close()).To(Succeed())
|
||||||
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).Times(2)
|
||||||
|
mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2)
|
||||||
frame, _ := str.popStreamFrame(3 + frameHeaderLen)
|
frame, _ := str.popStreamFrame(3 + frameHeaderLen)
|
||||||
Expect(frame).ToNot(BeNil())
|
Expect(frame).ToNot(BeNil())
|
||||||
f := frame.Frame.(*wire.StreamFrame)
|
f := frame.Frame.(*wire.StreamFrame)
|
||||||
Expect(f.Data).To(Equal([]byte("foo")))
|
Expect(f.Data).To(Equal([]byte("foo")))
|
||||||
Expect(f.FinBit).To(BeFalse())
|
Expect(f.FinBit).To(BeFalse())
|
||||||
frame, _ = str.popStreamFrame(100)
|
frame, _ = str.popStreamFrame(protocol.MaxByteCount)
|
||||||
f = frame.Frame.(*wire.StreamFrame)
|
f = frame.Frame.(*wire.StreamFrame)
|
||||||
Expect(f.Data).To(Equal([]byte("bar")))
|
Expect(f.Data).To(Equal([]byte("bar")))
|
||||||
Expect(f.FinBit).To(BeTrue())
|
Expect(f.FinBit).To(BeTrue())
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("doesn't send a FIN when there's still data, for long writes", func() {
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
defer close(done)
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
_, err := strWithTimeout.Write(getData(5000))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
Expect(str.Close()).To(Succeed())
|
||||||
|
}()
|
||||||
|
waitForWrite()
|
||||||
|
for i := 1; i <= 5; i++ {
|
||||||
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount)
|
||||||
|
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
||||||
|
if i == 5 {
|
||||||
|
Eventually(done).Should(BeClosed())
|
||||||
|
}
|
||||||
|
frame, _ := str.popStreamFrame(1100)
|
||||||
|
Expect(frame).ToNot(BeNil())
|
||||||
|
f := frame.Frame.(*wire.StreamFrame)
|
||||||
|
Expect(f.Data).To(Equal(getDataAtOffset(f.Offset, f.DataLen())))
|
||||||
|
Expect(f.FinBit).To(Equal(i == 5)) // the last frame should have the FIN bit set
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
It("doesn't allow FIN after it is closed for shutdown", func() {
|
It("doesn't allow FIN after it is closed for shutdown", func() {
|
||||||
str.closeForShutdown(errors.New("test"))
|
str.closeForShutdown(errors.New("test"))
|
||||||
f, hasMoreData := str.popStreamFrame(1000)
|
f, hasMoreData := str.popStreamFrame(1000)
|
||||||
|
@ -470,13 +596,13 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("doesn't get data for writing if an error occurred", func() {
|
It("doesn't get data for writing if an error occurred", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount)
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999))
|
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, err := strWithTimeout.Write(bytes.Repeat([]byte{0}, 500))
|
_, err := strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError(testErr))
|
Expect(err).To(MatchError(testErr))
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
@ -510,7 +636,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
|
|
||||||
It("says when it has data for sending", func() {
|
It("says when it has data for sending", func() {
|
||||||
mockFC.EXPECT().UpdateSendWindow(gomock.Any())
|
mockFC.EXPECT().UpdateSendWindow(gomock.Any())
|
||||||
mockSender.EXPECT().onHasStreamData(streamID).Times(2) // once for Write, once for the MAX_STREAM_DATA frame
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
|
@ -519,6 +645,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
waitForWrite()
|
waitForWrite()
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
str.handleMaxStreamDataFrame(&wire.MaxStreamDataFrame{
|
str.handleMaxStreamDataFrame(&wire.MaxStreamDataFrame{
|
||||||
StreamID: streamID,
|
StreamID: streamID,
|
||||||
ByteOffset: 42,
|
ByteOffset: 42,
|
||||||
|
@ -545,8 +672,8 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("unblocks Write", func() {
|
It("unblocks Write", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
|
||||||
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount)
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount)
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
||||||
writeReturned := make(chan struct{})
|
writeReturned := make(chan struct{})
|
||||||
|
@ -554,7 +681,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
var err error
|
var err error
|
||||||
n, err = strWithTimeout.Write(bytes.Repeat([]byte{0}, 100))
|
n, err = strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError("Write on stream 1337 canceled with error code 1234"))
|
Expect(err).To(MatchError("Write on stream 1337 canceled with error code 1234"))
|
||||||
close(writeReturned)
|
close(writeReturned)
|
||||||
}()
|
}()
|
||||||
|
@ -567,14 +694,36 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("doesn't pop STREAM frames after being canceled", func() {
|
It("doesn't pop STREAM frames after being canceled", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID)
|
|
||||||
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount)
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount)
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
||||||
writeReturned := make(chan struct{})
|
writeReturned := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, err := strWithTimeout.Write(bytes.Repeat([]byte{0}, 100))
|
strWithTimeout.Write(getData(100))
|
||||||
|
close(writeReturned)
|
||||||
|
}()
|
||||||
|
waitForWrite()
|
||||||
|
frame, hasMoreData := str.popStreamFrame(50)
|
||||||
|
Expect(hasMoreData).To(BeTrue())
|
||||||
|
Expect(frame).ToNot(BeNil())
|
||||||
|
str.CancelWrite(1234)
|
||||||
|
frame, hasMoreData = str.popStreamFrame(10)
|
||||||
|
Expect(frame).To(BeNil())
|
||||||
|
Expect(hasMoreData).To(BeFalse())
|
||||||
|
Eventually(writeReturned).Should(BeClosed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("doesn't pop STREAM frames after being canceled, for large writes", func() {
|
||||||
|
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount)
|
||||||
|
mockFC.EXPECT().AddBytesSent(gomock.Any())
|
||||||
|
writeReturned := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
_, err := strWithTimeout.Write(getData(5000))
|
||||||
Expect(err).To(MatchError("Write on stream 1337 canceled with error code 1234"))
|
Expect(err).To(MatchError("Write on stream 1337 canceled with error code 1234"))
|
||||||
close(writeReturned)
|
close(writeReturned)
|
||||||
}()
|
}()
|
||||||
|
@ -645,7 +794,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, err := str.Write([]byte("foobar"))
|
_, err := str.Write(getData(5000))
|
||||||
Expect(err).To(MatchError("stream 1337 was reset with error code 123"))
|
Expect(err).To(MatchError("stream 1337 was reset with error code 123"))
|
||||||
Expect(err).To(BeAssignableToTypeOf(streamCanceledError{}))
|
Expect(err).To(BeAssignableToTypeOf(streamCanceledError{}))
|
||||||
Expect(err.(streamCanceledError).Canceled()).To(BeTrue())
|
Expect(err.(streamCanceledError).Canceled()).To(BeTrue())
|
||||||
|
@ -733,7 +882,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
})
|
})
|
||||||
|
|
||||||
It("queues lost STREAM frames", func() {
|
It("queues lost STREAM frames", func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID).Times(2)
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999))
|
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999))
|
||||||
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6))
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6))
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
@ -750,6 +899,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
Expect(frame.Frame.(*wire.StreamFrame).Data).To(Equal([]byte("foobar")))
|
Expect(frame.Frame.(*wire.StreamFrame).Data).To(Equal([]byte("foobar")))
|
||||||
|
|
||||||
// now lose the frame
|
// now lose the frame
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
frame.OnLost(frame.Frame)
|
frame.OnLost(frame.Frame)
|
||||||
newFrame, _ := str.popStreamFrame(protocol.MaxByteCount)
|
newFrame, _ := str.popStreamFrame(protocol.MaxByteCount)
|
||||||
Expect(newFrame).ToNot(BeNil())
|
Expect(newFrame).ToNot(BeNil())
|
||||||
|
@ -775,16 +925,16 @@ var _ = Describe("Send Stream", func() {
|
||||||
|
|
||||||
Context("determining when a stream is completed", func() {
|
Context("determining when a stream is completed", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
mockSender.EXPECT().onHasStreamData(streamID).AnyTimes()
|
|
||||||
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes()
|
mockFC.EXPECT().SendWindowSize().Return(protocol.MaxByteCount).AnyTimes()
|
||||||
mockFC.EXPECT().AddBytesSent(gomock.Any()).AnyTimes()
|
mockFC.EXPECT().AddBytesSent(gomock.Any()).AnyTimes()
|
||||||
})
|
})
|
||||||
|
|
||||||
It("says when a stream is completed", func() {
|
It("says when a stream is completed", func() {
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, err := strWithTimeout.Write(make([]byte, 1000))
|
_, err := strWithTimeout.Write(make([]byte, 100))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
@ -793,7 +943,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
// get a bunch of small frames (max. 20 bytes)
|
// get a bunch of small frames (max. 20 bytes)
|
||||||
var frames []ackhandler.Frame
|
var frames []ackhandler.Frame
|
||||||
for {
|
for {
|
||||||
frame, hasMoreData := str.popStreamFrame(200)
|
frame, hasMoreData := str.popStreamFrame(20)
|
||||||
if frame == nil {
|
if frame == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -811,6 +961,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now close the stream and acknowledge the FIN.
|
// Now close the stream and acknowledge the FIN.
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
Expect(str.Close()).To(Succeed())
|
Expect(str.Close()).To(Succeed())
|
||||||
frame, _ := str.popStreamFrame(protocol.MaxByteCount)
|
frame, _ := str.popStreamFrame(protocol.MaxByteCount)
|
||||||
Expect(frame).ToNot(BeNil())
|
Expect(frame).ToNot(BeNil())
|
||||||
|
@ -818,12 +969,36 @@ var _ = Describe("Send Stream", func() {
|
||||||
frame.OnAcked(frame.Frame)
|
frame.OnAcked(frame.Frame)
|
||||||
})
|
})
|
||||||
|
|
||||||
It("doesn't say it's completed when there are frames waiting to be retransmitted", func() {
|
It("says when a stream is completed, if Close() is called before popping the frame", func() {
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID).Times(2)
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
_, err := strWithTimeout.Write(make([]byte, 10))
|
_, err := strWithTimeout.Write(make([]byte, 100))
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
waitForWrite()
|
||||||
|
Eventually(done).Should(BeClosed())
|
||||||
|
Expect(str.Close()).To(Succeed())
|
||||||
|
|
||||||
|
frame, hasMoreData := str.popStreamFrame(protocol.MaxByteCount)
|
||||||
|
Expect(hasMoreData).To(BeFalse())
|
||||||
|
Expect(frame).ToNot(BeNil())
|
||||||
|
Expect(frame.Frame.(*wire.StreamFrame).FinBit).To(BeTrue())
|
||||||
|
|
||||||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||||||
|
frame.OnAcked(frame.Frame)
|
||||||
|
})
|
||||||
|
|
||||||
|
It("doesn't say it's completed when there are frames waiting to be retransmitted", func() {
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
_, err := strWithTimeout.Write(getData(100))
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
Expect(str.Close()).To(Succeed())
|
Expect(str.Close()).To(Succeed())
|
||||||
close(done)
|
close(done)
|
||||||
}()
|
}()
|
||||||
|
@ -832,7 +1007,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
// get a bunch of small frames (max. 20 bytes)
|
// get a bunch of small frames (max. 20 bytes)
|
||||||
var frames []ackhandler.Frame
|
var frames []ackhandler.Frame
|
||||||
for {
|
for {
|
||||||
frame, _ := str.popStreamFrame(protocol.MaxByteCount)
|
frame, _ := str.popStreamFrame(20)
|
||||||
if frame == nil {
|
if frame == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -847,6 +1022,7 @@ var _ = Describe("Send Stream", func() {
|
||||||
for _, f := range frames[1:] {
|
for _, f := range frames[1:] {
|
||||||
f.OnAcked(f.Frame)
|
f.OnAcked(f.Frame)
|
||||||
}
|
}
|
||||||
|
mockSender.EXPECT().onHasStreamData(streamID)
|
||||||
frames[0].OnLost(frames[0].Frame)
|
frames[0].OnLost(frames[0].Frame)
|
||||||
|
|
||||||
// get the retransmission and acknowledge it
|
// get the retransmission and acknowledge it
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue