649 lines
22 KiB
Go
649 lines
22 KiB
Go
|
package quic
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"io"
|
||
|
"runtime"
|
||
|
"time"
|
||
|
|
||
|
"github.com/golang/mock/gomock"
|
||
|
"github.com/lucas-clemente/quic-go/internal/mocks"
|
||
|
"github.com/lucas-clemente/quic-go/internal/protocol"
|
||
|
"github.com/lucas-clemente/quic-go/internal/wire"
|
||
|
|
||
|
. "github.com/onsi/ginkgo"
|
||
|
. "github.com/onsi/gomega"
|
||
|
"github.com/onsi/gomega/gbytes"
|
||
|
)
|
||
|
|
||
|
var _ = Describe("Receive Stream", func() {
|
||
|
const streamID protocol.StreamID = 1337
|
||
|
|
||
|
var (
|
||
|
str *receiveStream
|
||
|
strWithTimeout io.Reader // str wrapped with gbytes.TimeoutReader
|
||
|
mockFC *mocks.MockStreamFlowController
|
||
|
mockSender *MockStreamSender
|
||
|
)
|
||
|
|
||
|
BeforeEach(func() {
|
||
|
mockSender = NewMockStreamSender(mockCtrl)
|
||
|
mockFC = mocks.NewMockStreamFlowController(mockCtrl)
|
||
|
str = newReceiveStream(streamID, mockSender, mockFC)
|
||
|
|
||
|
timeout := scaleDuration(250 * time.Millisecond)
|
||
|
strWithTimeout = gbytes.TimeoutReader(str, timeout)
|
||
|
})
|
||
|
|
||
|
It("gets stream id", func() {
|
||
|
Expect(str.StreamID()).To(Equal(protocol.StreamID(1337)))
|
||
|
})
|
||
|
|
||
|
Context("reading", func() {
|
||
|
It("reads a single STREAM frame", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4))
|
||
|
mockFC.EXPECT().HasWindowUpdate()
|
||
|
frame := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
b := make([]byte, 4)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(4))
|
||
|
Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF}))
|
||
|
})
|
||
|
|
||
|
It("reads a single STREAM frame in multiple goes", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2))
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2))
|
||
|
mockFC.EXPECT().HasWindowUpdate().Times(2)
|
||
|
frame := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
b := make([]byte, 2)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(2))
|
||
|
Expect(b).To(Equal([]byte{0xDE, 0xAD}))
|
||
|
n, err = strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(2))
|
||
|
Expect(b).To(Equal([]byte{0xBE, 0xEF}))
|
||
|
})
|
||
|
|
||
|
It("reads all data available", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2)
|
||
|
mockFC.EXPECT().HasWindowUpdate().Times(2)
|
||
|
frame1 := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xDE, 0xAD},
|
||
|
}
|
||
|
frame2 := wire.StreamFrame{
|
||
|
Offset: 2,
|
||
|
Data: []byte{0xBE, 0xEF},
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame1)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleStreamFrame(&frame2)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
b := make([]byte, 6)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(4))
|
||
|
Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF, 0x00, 0x00}))
|
||
|
})
|
||
|
|
||
|
It("assembles multiple STREAM frames", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2)
|
||
|
mockFC.EXPECT().HasWindowUpdate().Times(2)
|
||
|
frame1 := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xDE, 0xAD},
|
||
|
}
|
||
|
frame2 := wire.StreamFrame{
|
||
|
Offset: 2,
|
||
|
Data: []byte{0xBE, 0xEF},
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame1)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleStreamFrame(&frame2)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
b := make([]byte, 4)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(4))
|
||
|
Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF}))
|
||
|
})
|
||
|
|
||
|
It("waits until data is available", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2))
|
||
|
mockFC.EXPECT().HasWindowUpdate()
|
||
|
go func() {
|
||
|
defer GinkgoRecover()
|
||
|
frame := wire.StreamFrame{Data: []byte{0xDE, 0xAD}}
|
||
|
time.Sleep(10 * time.Millisecond)
|
||
|
err := str.handleStreamFrame(&frame)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
}()
|
||
|
b := make([]byte, 2)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(2))
|
||
|
})
|
||
|
|
||
|
It("handles STREAM frames in wrong order", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2)
|
||
|
mockFC.EXPECT().HasWindowUpdate().Times(2)
|
||
|
frame1 := wire.StreamFrame{
|
||
|
Offset: 2,
|
||
|
Data: []byte{0xBE, 0xEF},
|
||
|
}
|
||
|
frame2 := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xDE, 0xAD},
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame1)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleStreamFrame(&frame2)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
b := make([]byte, 4)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(4))
|
||
|
Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF}))
|
||
|
})
|
||
|
|
||
|
It("ignores duplicate STREAM frames", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2)
|
||
|
mockFC.EXPECT().HasWindowUpdate().Times(2)
|
||
|
frame1 := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xDE, 0xAD},
|
||
|
}
|
||
|
frame2 := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0x13, 0x37},
|
||
|
}
|
||
|
frame3 := wire.StreamFrame{
|
||
|
Offset: 2,
|
||
|
Data: []byte{0xBE, 0xEF},
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame1)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleStreamFrame(&frame2)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleStreamFrame(&frame3)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
b := make([]byte, 4)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(4))
|
||
|
Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF}))
|
||
|
})
|
||
|
|
||
|
It("doesn't rejects a STREAM frames with an overlapping data range", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2))
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4))
|
||
|
mockFC.EXPECT().HasWindowUpdate().Times(2)
|
||
|
frame1 := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte("foob"),
|
||
|
}
|
||
|
frame2 := wire.StreamFrame{
|
||
|
Offset: 2,
|
||
|
Data: []byte("obar"),
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame1)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleStreamFrame(&frame2)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
b := make([]byte, 6)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(6))
|
||
|
Expect(b).To(Equal([]byte("foobar")))
|
||
|
})
|
||
|
|
||
|
It("passes on errors from the streamFrameSorter", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), false)
|
||
|
err := str.handleStreamFrame(&wire.StreamFrame{StreamID: streamID}) // STREAM frame without data
|
||
|
Expect(err).To(MatchError(errEmptyStreamData))
|
||
|
})
|
||
|
|
||
|
It("calls the onHasWindowUpdate callback, when the a MAX_STREAM_DATA should be sent", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), false)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(6))
|
||
|
mockFC.EXPECT().HasWindowUpdate().Return(true)
|
||
|
mockSender.EXPECT().onHasWindowUpdate(streamID)
|
||
|
frame1 := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte("foobar"),
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame1)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
b := make([]byte, 6)
|
||
|
_, err = strWithTimeout.Read(b)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
})
|
||
|
|
||
|
Context("deadlines", func() {
|
||
|
It("the deadline error has the right net.Error properties", func() {
|
||
|
Expect(errDeadline.Temporary()).To(BeTrue())
|
||
|
Expect(errDeadline.Timeout()).To(BeTrue())
|
||
|
Expect(errDeadline).To(MatchError("deadline exceeded"))
|
||
|
})
|
||
|
|
||
|
It("returns an error when Read is called after the deadline", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), false).AnyTimes()
|
||
|
f := &wire.StreamFrame{Data: []byte("foobar")}
|
||
|
err := str.handleStreamFrame(f)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
str.SetReadDeadline(time.Now().Add(-time.Second))
|
||
|
b := make([]byte, 6)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).To(MatchError(errDeadline))
|
||
|
Expect(n).To(BeZero())
|
||
|
})
|
||
|
|
||
|
It("unblocks after the deadline", func() {
|
||
|
deadline := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
||
|
str.SetReadDeadline(deadline)
|
||
|
b := make([]byte, 6)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).To(MatchError(errDeadline))
|
||
|
Expect(n).To(BeZero())
|
||
|
Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(10*time.Millisecond)))
|
||
|
})
|
||
|
|
||
|
It("doesn't unblock if the deadline is changed before the first one expires", func() {
|
||
|
deadline1 := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
||
|
deadline2 := time.Now().Add(scaleDuration(100 * time.Millisecond))
|
||
|
str.SetReadDeadline(deadline1)
|
||
|
go func() {
|
||
|
defer GinkgoRecover()
|
||
|
time.Sleep(scaleDuration(20 * time.Millisecond))
|
||
|
str.SetReadDeadline(deadline2)
|
||
|
// make sure that this was actually execute before the deadline expires
|
||
|
Expect(time.Now()).To(BeTemporally("<", deadline1))
|
||
|
}()
|
||
|
runtime.Gosched()
|
||
|
b := make([]byte, 10)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).To(MatchError(errDeadline))
|
||
|
Expect(n).To(BeZero())
|
||
|
Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond)))
|
||
|
})
|
||
|
|
||
|
It("unblocks earlier, when a new deadline is set", func() {
|
||
|
deadline1 := time.Now().Add(scaleDuration(200 * time.Millisecond))
|
||
|
deadline2 := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
||
|
go func() {
|
||
|
defer GinkgoRecover()
|
||
|
time.Sleep(scaleDuration(10 * time.Millisecond))
|
||
|
str.SetReadDeadline(deadline2)
|
||
|
// make sure that this was actually execute before the deadline expires
|
||
|
Expect(time.Now()).To(BeTemporally("<", deadline2))
|
||
|
}()
|
||
|
str.SetReadDeadline(deadline1)
|
||
|
runtime.Gosched()
|
||
|
b := make([]byte, 10)
|
||
|
_, err := strWithTimeout.Read(b)
|
||
|
Expect(err).To(MatchError(errDeadline))
|
||
|
Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(25*time.Millisecond)))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
Context("closing", func() {
|
||
|
Context("with FIN bit", func() {
|
||
|
It("returns EOFs", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), true)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4))
|
||
|
mockFC.EXPECT().HasWindowUpdate()
|
||
|
str.handleStreamFrame(&wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
||
|
FinBit: true,
|
||
|
})
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
b := make([]byte, 4)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
Expect(n).To(Equal(4))
|
||
|
Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF}))
|
||
|
n, err = strWithTimeout.Read(b)
|
||
|
Expect(n).To(BeZero())
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
})
|
||
|
|
||
|
It("handles out-of-order frames", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), true)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2)
|
||
|
mockFC.EXPECT().HasWindowUpdate().Times(2)
|
||
|
frame1 := wire.StreamFrame{
|
||
|
Offset: 2,
|
||
|
Data: []byte{0xBE, 0xEF},
|
||
|
FinBit: true,
|
||
|
}
|
||
|
frame2 := wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xDE, 0xAD},
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame1)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleStreamFrame(&frame2)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
b := make([]byte, 4)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
Expect(n).To(Equal(4))
|
||
|
Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF}))
|
||
|
n, err = strWithTimeout.Read(b)
|
||
|
Expect(n).To(BeZero())
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
})
|
||
|
|
||
|
It("returns EOFs with partial read", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), true)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2))
|
||
|
mockFC.EXPECT().HasWindowUpdate()
|
||
|
err := str.handleStreamFrame(&wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
Data: []byte{0xde, 0xad},
|
||
|
FinBit: true,
|
||
|
})
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
b := make([]byte, 4)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
Expect(n).To(Equal(2))
|
||
|
Expect(b[:n]).To(Equal([]byte{0xde, 0xad}))
|
||
|
})
|
||
|
|
||
|
It("handles immediate FINs", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(0))
|
||
|
mockFC.EXPECT().HasWindowUpdate()
|
||
|
err := str.handleStreamFrame(&wire.StreamFrame{
|
||
|
Offset: 0,
|
||
|
FinBit: true,
|
||
|
})
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
b := make([]byte, 4)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(n).To(BeZero())
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
It("closes when CloseRemote is called", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(0))
|
||
|
mockFC.EXPECT().HasWindowUpdate()
|
||
|
str.CloseRemote(0)
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
b := make([]byte, 8)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(n).To(BeZero())
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
})
|
||
|
})
|
||
|
|
||
|
Context("closing for shutdown", func() {
|
||
|
testErr := errors.New("test error")
|
||
|
|
||
|
It("immediately returns all reads", func() {
|
||
|
done := make(chan struct{})
|
||
|
b := make([]byte, 4)
|
||
|
go func() {
|
||
|
defer GinkgoRecover()
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(n).To(BeZero())
|
||
|
Expect(err).To(MatchError(testErr))
|
||
|
close(done)
|
||
|
}()
|
||
|
Consistently(done).ShouldNot(BeClosed())
|
||
|
str.closeForShutdown(testErr)
|
||
|
Eventually(done).Should(BeClosed())
|
||
|
})
|
||
|
|
||
|
It("errors for all following reads", func() {
|
||
|
str.closeForShutdown(testErr)
|
||
|
b := make([]byte, 1)
|
||
|
n, err := strWithTimeout.Read(b)
|
||
|
Expect(n).To(BeZero())
|
||
|
Expect(err).To(MatchError(testErr))
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
Context("stream cancelations", func() {
|
||
|
Context("canceling read", func() {
|
||
|
It("unblocks Read", func() {
|
||
|
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
||
|
done := make(chan struct{})
|
||
|
go func() {
|
||
|
defer GinkgoRecover()
|
||
|
_, err := strWithTimeout.Read([]byte{0})
|
||
|
Expect(err).To(MatchError("Read on stream 1337 canceled with error code 1234"))
|
||
|
close(done)
|
||
|
}()
|
||
|
Consistently(done).ShouldNot(BeClosed())
|
||
|
err := str.CancelRead(1234)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Eventually(done).Should(BeClosed())
|
||
|
})
|
||
|
|
||
|
It("doesn't allow further calls to Read", func() {
|
||
|
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
||
|
err := str.CancelRead(1234)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
_, err = strWithTimeout.Read([]byte{0})
|
||
|
Expect(err).To(MatchError("Read on stream 1337 canceled with error code 1234"))
|
||
|
})
|
||
|
|
||
|
It("does nothing when CancelRead is called twice", func() {
|
||
|
mockSender.EXPECT().queueControlFrame(gomock.Any())
|
||
|
err := str.CancelRead(1234)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.CancelRead(2345)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
_, err = strWithTimeout.Read([]byte{0})
|
||
|
Expect(err).To(MatchError("Read on stream 1337 canceled with error code 1234"))
|
||
|
})
|
||
|
|
||
|
It("doesn't send a RST_STREAM frame, if the FIN was already read", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), true)
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(6))
|
||
|
mockFC.EXPECT().HasWindowUpdate()
|
||
|
// no calls to mockSender.queueControlFrame
|
||
|
err := str.handleStreamFrame(&wire.StreamFrame{
|
||
|
StreamID: streamID,
|
||
|
Data: []byte("foobar"),
|
||
|
FinBit: true,
|
||
|
})
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
_, err = strWithTimeout.Read(make([]byte, 100))
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
err = str.CancelRead(1234)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
})
|
||
|
|
||
|
It("queues a STOP_SENDING frame, for IETF QUIC", func() {
|
||
|
mockSender.EXPECT().queueControlFrame(&wire.StopSendingFrame{
|
||
|
StreamID: streamID,
|
||
|
ErrorCode: 1234,
|
||
|
})
|
||
|
err := str.CancelRead(1234)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
})
|
||
|
|
||
|
It("doesn't queue a STOP_SENDING frame, for gQUIC", func() {
|
||
|
|
||
|
})
|
||
|
})
|
||
|
|
||
|
Context("receiving RST_STREAM frames", func() {
|
||
|
rst := &wire.RstStreamFrame{
|
||
|
StreamID: streamID,
|
||
|
ByteOffset: 42,
|
||
|
ErrorCode: 1234,
|
||
|
}
|
||
|
|
||
|
It("unblocks Read", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true)
|
||
|
done := make(chan struct{})
|
||
|
go func() {
|
||
|
defer GinkgoRecover()
|
||
|
_, err := strWithTimeout.Read([]byte{0})
|
||
|
Expect(err).To(MatchError("Stream 1337 was reset with error code 1234"))
|
||
|
Expect(err).To(BeAssignableToTypeOf(streamCanceledError{}))
|
||
|
Expect(err.(streamCanceledError).Canceled()).To(BeTrue())
|
||
|
Expect(err.(streamCanceledError).ErrorCode()).To(Equal(protocol.ApplicationErrorCode(1234)))
|
||
|
close(done)
|
||
|
}()
|
||
|
Consistently(done).ShouldNot(BeClosed())
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
str.handleRstStreamFrame(rst)
|
||
|
Eventually(done).Should(BeClosed())
|
||
|
})
|
||
|
|
||
|
It("doesn't allow further calls to Read", func() {
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true)
|
||
|
err := str.handleRstStreamFrame(rst)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
_, err = strWithTimeout.Read([]byte{0})
|
||
|
Expect(err).To(MatchError("Stream 1337 was reset with error code 1234"))
|
||
|
Expect(err).To(BeAssignableToTypeOf(streamCanceledError{}))
|
||
|
Expect(err.(streamCanceledError).Canceled()).To(BeTrue())
|
||
|
Expect(err.(streamCanceledError).ErrorCode()).To(Equal(protocol.ApplicationErrorCode(1234)))
|
||
|
})
|
||
|
|
||
|
It("errors when receiving a RST_STREAM with an inconsistent offset", func() {
|
||
|
testErr := errors.New("already received a different final offset before")
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true).Return(testErr)
|
||
|
err := str.handleRstStreamFrame(rst)
|
||
|
Expect(err).To(MatchError(testErr))
|
||
|
})
|
||
|
|
||
|
It("ignores duplicate RST_STREAM frames", func() {
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true).Times(2)
|
||
|
err := str.handleRstStreamFrame(rst)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleRstStreamFrame(rst)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
})
|
||
|
|
||
|
It("doesn't do anyting when it was closed for shutdown", func() {
|
||
|
str.closeForShutdown(nil)
|
||
|
err := str.handleRstStreamFrame(rst)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
})
|
||
|
|
||
|
Context("for gQUIC", func() {
|
||
|
BeforeEach(func() {
|
||
|
str.version = versionGQUICFrames
|
||
|
})
|
||
|
|
||
|
It("unblocks Read when receiving a RST_STREAM frame with non-zero error code", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(42), true)
|
||
|
readReturned := make(chan struct{})
|
||
|
go func() {
|
||
|
defer GinkgoRecover()
|
||
|
_, err := strWithTimeout.Read([]byte{0})
|
||
|
Expect(err).To(MatchError("Stream 1337 was reset with error code 1234"))
|
||
|
Expect(err).To(BeAssignableToTypeOf(streamCanceledError{}))
|
||
|
Expect(err.(streamCanceledError).Canceled()).To(BeTrue())
|
||
|
Expect(err.(streamCanceledError).ErrorCode()).To(Equal(protocol.ApplicationErrorCode(1234)))
|
||
|
close(readReturned)
|
||
|
}()
|
||
|
Consistently(readReturned).ShouldNot(BeClosed())
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID)
|
||
|
err := str.handleRstStreamFrame(rst)
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Eventually(readReturned).Should(BeClosed())
|
||
|
})
|
||
|
|
||
|
It("continues reading until the end when receiving a RST_STREAM frame with error code 0", func() {
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), true).Times(2)
|
||
|
gomock.InOrder(
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4)),
|
||
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)),
|
||
|
mockSender.EXPECT().onStreamCompleted(streamID),
|
||
|
)
|
||
|
mockFC.EXPECT().HasWindowUpdate().Times(2)
|
||
|
readReturned := make(chan struct{})
|
||
|
go func() {
|
||
|
defer GinkgoRecover()
|
||
|
n, err := strWithTimeout.Read(make([]byte, 4))
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Expect(n).To(Equal(4))
|
||
|
n, err = strWithTimeout.Read(make([]byte, 4))
|
||
|
Expect(err).To(MatchError(io.EOF))
|
||
|
Expect(n).To(Equal(2))
|
||
|
close(readReturned)
|
||
|
}()
|
||
|
Consistently(readReturned).ShouldNot(BeClosed())
|
||
|
err := str.handleStreamFrame(&wire.StreamFrame{
|
||
|
StreamID: streamID,
|
||
|
Data: []byte("foobar"),
|
||
|
FinBit: true,
|
||
|
})
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
err = str.handleRstStreamFrame(&wire.RstStreamFrame{
|
||
|
StreamID: streamID,
|
||
|
ByteOffset: 6,
|
||
|
ErrorCode: 0,
|
||
|
})
|
||
|
Expect(err).ToNot(HaveOccurred())
|
||
|
Eventually(readReturned).Should(BeClosed())
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
})
|
||
|
|
||
|
Context("flow control", func() {
|
||
|
It("errors when a STREAM frame causes a flow control violation", func() {
|
||
|
testErr := errors.New("flow control violation")
|
||
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(8), false).Return(testErr)
|
||
|
frame := wire.StreamFrame{
|
||
|
Offset: 2,
|
||
|
Data: []byte("foobar"),
|
||
|
}
|
||
|
err := str.handleStreamFrame(&frame)
|
||
|
Expect(err).To(MatchError(testErr))
|
||
|
})
|
||
|
|
||
|
It("gets a window update", func() {
|
||
|
mockFC.EXPECT().GetWindowUpdate().Return(protocol.ByteCount(0x100))
|
||
|
Expect(str.getWindowUpdate()).To(Equal(protocol.ByteCount(0x100)))
|
||
|
})
|
||
|
})
|
||
|
})
|