1103 lines
36 KiB
Go
1103 lines
36 KiB
Go
package quic
|
|
|
|
import (
|
|
"errors"
|
|
"io"
|
|
"runtime"
|
|
"strconv"
|
|
"time"
|
|
|
|
"os"
|
|
|
|
"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("Stream", func() {
|
|
const streamID protocol.StreamID = 1337
|
|
|
|
var (
|
|
str *stream
|
|
strWithTimeout io.ReadWriter // str wrapped with gbytes.Timeout{Reader,Writer}
|
|
onDataCalled bool
|
|
|
|
resetCalled bool
|
|
resetCalledForStream protocol.StreamID
|
|
resetCalledAtOffset protocol.ByteCount
|
|
|
|
mockFC *mocks.MockStreamFlowController
|
|
)
|
|
|
|
// in the tests for the stream deadlines we set a deadline
|
|
// and wait to make an assertion when Read / Write was unblocked
|
|
// on the CIs, the timing is a lot less precise, so scale every duration by this factor
|
|
scaleDuration := func(t time.Duration) time.Duration {
|
|
scaleFactor := 1
|
|
if f, err := strconv.Atoi(os.Getenv("TIMESCALE_FACTOR")); err == nil { // parsing "" errors, so this works fine if the env is not set
|
|
scaleFactor = f
|
|
}
|
|
Expect(scaleFactor).ToNot(BeZero())
|
|
return time.Duration(scaleFactor) * t
|
|
}
|
|
|
|
onData := func() {
|
|
onDataCalled = true
|
|
}
|
|
|
|
onReset := func(id protocol.StreamID, offset protocol.ByteCount) {
|
|
resetCalled = true
|
|
resetCalledForStream = id
|
|
resetCalledAtOffset = offset
|
|
}
|
|
|
|
BeforeEach(func() {
|
|
onDataCalled = false
|
|
resetCalled = false
|
|
mockFC = mocks.NewMockStreamFlowController(mockCtrl)
|
|
str = newStream(streamID, onData, onReset, mockFC, protocol.VersionWhatever)
|
|
|
|
timeout := scaleDuration(250 * time.Millisecond)
|
|
strWithTimeout = struct {
|
|
io.Reader
|
|
io.Writer
|
|
}{
|
|
gbytes.TimeoutReader(str, timeout),
|
|
gbytes.TimeoutWriter(str, timeout),
|
|
}
|
|
})
|
|
|
|
It("gets stream id", func() {
|
|
Expect(str.StreamID()).To(Equal(protocol.StreamID(1337)))
|
|
})
|
|
|
|
Context("reading", func() {
|
|
It("reads a single StreamFrame", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4))
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
}
|
|
err := str.AddStreamFrame(&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 StreamFrame in multiple goes", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2))
|
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2))
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
}
|
|
err := str.AddStreamFrame(&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)
|
|
frame1 := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD},
|
|
}
|
|
frame2 := wire.StreamFrame{
|
|
Offset: 2,
|
|
Data: []byte{0xBE, 0xEF},
|
|
}
|
|
err := str.AddStreamFrame(&frame1)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
err = str.AddStreamFrame(&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 StreamFrames", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(2), false)
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(2)).Times(2)
|
|
frame1 := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD},
|
|
}
|
|
frame2 := wire.StreamFrame{
|
|
Offset: 2,
|
|
Data: []byte{0xBE, 0xEF},
|
|
}
|
|
err := str.AddStreamFrame(&frame1)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
err = str.AddStreamFrame(&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))
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
frame := wire.StreamFrame{Data: []byte{0xDE, 0xAD}}
|
|
time.Sleep(10 * time.Millisecond)
|
|
err := str.AddStreamFrame(&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 StreamFrames 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)
|
|
frame1 := wire.StreamFrame{
|
|
Offset: 2,
|
|
Data: []byte{0xBE, 0xEF},
|
|
}
|
|
frame2 := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD},
|
|
}
|
|
err := str.AddStreamFrame(&frame1)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
err = str.AddStreamFrame(&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 StreamFrames", 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)
|
|
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.AddStreamFrame(&frame1)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
err = str.AddStreamFrame(&frame2)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
err = str.AddStreamFrame(&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 StreamFrames 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))
|
|
frame1 := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte("foob"),
|
|
}
|
|
frame2 := wire.StreamFrame{
|
|
Offset: 2,
|
|
Data: []byte("obar"),
|
|
}
|
|
err := str.AddStreamFrame(&frame1)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
err = str.AddStreamFrame(&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("calls onData", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(4))
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
}
|
|
str.AddStreamFrame(&frame)
|
|
b := make([]byte, 4)
|
|
_, err := strWithTimeout.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(onDataCalled).To(BeTrue())
|
|
})
|
|
|
|
It("sets the read offset", func() {
|
|
str.SetReadOffset(0x42)
|
|
Expect(str.readOffset).To(Equal(protocol.ByteCount(0x42)))
|
|
Expect(str.frameQueue.readPosition).To(Equal(protocol.ByteCount(0x42)))
|
|
})
|
|
|
|
Context("deadlines", func() {
|
|
It("the deadline error has the right net.Error properties", func() {
|
|
Expect(errDeadline.Temporary()).To(BeTrue())
|
|
Expect(errDeadline.Timeout()).To(BeTrue())
|
|
})
|
|
|
|
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.AddStreamFrame(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)))
|
|
})
|
|
|
|
It("sets a read deadline, when SetDeadline is called", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), false).AnyTimes()
|
|
f := &wire.StreamFrame{Data: []byte("foobar")}
|
|
err := str.AddStreamFrame(f)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
str.SetDeadline(time.Now().Add(-time.Second))
|
|
b := make([]byte, 6)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(err).To(MatchError(errDeadline))
|
|
Expect(n).To(BeZero())
|
|
})
|
|
})
|
|
|
|
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))
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
FinBit: true,
|
|
}
|
|
str.AddStreamFrame(&frame)
|
|
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)
|
|
frame1 := wire.StreamFrame{
|
|
Offset: 2,
|
|
Data: []byte{0xBE, 0xEF},
|
|
FinBit: true,
|
|
}
|
|
frame2 := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD},
|
|
}
|
|
err := str.AddStreamFrame(&frame1)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
err = str.AddStreamFrame(&frame2)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
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))
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD},
|
|
FinBit: true,
|
|
}
|
|
err := str.AddStreamFrame(&frame)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
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))
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{},
|
|
FinBit: true,
|
|
}
|
|
err := str.AddStreamFrame(&frame)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
b := make([]byte, 4)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(io.EOF))
|
|
})
|
|
})
|
|
|
|
Context("when CloseRemote is called", func() {
|
|
It("closes", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(0))
|
|
str.CloseRemote(0)
|
|
b := make([]byte, 8)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(io.EOF))
|
|
})
|
|
|
|
It("doesn't cancel the context", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
str.CloseRemote(0)
|
|
Expect(str.Context().Done()).ToNot(BeClosed())
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("cancelling the stream", 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.Cancel(testErr)
|
|
Eventually(done).Should(BeClosed())
|
|
})
|
|
|
|
It("errors for all following reads", func() {
|
|
str.Cancel(testErr)
|
|
b := make([]byte, 1)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(testErr))
|
|
})
|
|
|
|
It("cancels the context", func() {
|
|
Expect(str.Context().Done()).ToNot(BeClosed())
|
|
str.Cancel(testErr)
|
|
Expect(str.Context().Done()).To(BeClosed())
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("resetting", func() {
|
|
testErr := errors.New("testErr")
|
|
|
|
Context("reset by the peer", func() {
|
|
It("continues reading after receiving a remote error", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(10), true)
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
}
|
|
str.AddStreamFrame(&frame)
|
|
str.RegisterRemoteError(testErr, 10)
|
|
b := make([]byte, 4)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(4))
|
|
})
|
|
|
|
It("reads a delayed StreamFrame that arrives after receiving a remote error", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), true)
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
|
str.RegisterRemoteError(testErr, 4)
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
}
|
|
err := str.AddStreamFrame(&frame)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
b := make([]byte, 4)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(4))
|
|
})
|
|
|
|
It("returns the error if reading past the offset of the frame received", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(8), true)
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
}
|
|
str.AddStreamFrame(&frame)
|
|
str.RegisterRemoteError(testErr, 8)
|
|
b := make([]byte, 10)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(b[0:4]).To(Equal(frame.Data))
|
|
Expect(err).To(MatchError(testErr))
|
|
Expect(n).To(Equal(4))
|
|
})
|
|
|
|
It("returns an EOF when reading past the offset, if the stream received a finbit", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), true)
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(8), true)
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
FinBit: true,
|
|
}
|
|
str.AddStreamFrame(&frame)
|
|
str.RegisterRemoteError(testErr, 8)
|
|
b := make([]byte, 10)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(b[:4]).To(Equal(frame.Data))
|
|
Expect(err).To(MatchError(io.EOF))
|
|
Expect(n).To(Equal(4))
|
|
})
|
|
|
|
It("continues reading in small chunks after receiving a remote error", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), true)
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), true)
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
FinBit: true,
|
|
}
|
|
str.AddStreamFrame(&frame)
|
|
str.RegisterRemoteError(testErr, 4)
|
|
b := make([]byte, 3)
|
|
_, err := strWithTimeout.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(b).To(Equal([]byte{0xde, 0xad, 0xbe}))
|
|
b = make([]byte, 3)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(err).To(MatchError(io.EOF))
|
|
Expect(b[:1]).To(Equal([]byte{0xef}))
|
|
Expect(n).To(Equal(1))
|
|
})
|
|
|
|
It("doesn't inform the flow controller about bytes read after receiving the remote error", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(4), false)
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(10), true)
|
|
// No AddBytesRead()
|
|
frame := wire.StreamFrame{
|
|
Offset: 0,
|
|
StreamID: 5,
|
|
Data: []byte{0xDE, 0xAD, 0xBE, 0xEF},
|
|
}
|
|
str.AddStreamFrame(&frame)
|
|
str.RegisterRemoteError(testErr, 10)
|
|
b := make([]byte, 3)
|
|
_, err := strWithTimeout.Read(b)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("stops writing after receiving a remote error", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(10), true)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(testErr))
|
|
close(done)
|
|
}()
|
|
str.RegisterRemoteError(testErr, 10)
|
|
Eventually(done).Should(BeClosed())
|
|
})
|
|
|
|
It("returns how much was written when recieving a remote error", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(10), true)
|
|
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999))
|
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(4))
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).To(MatchError(testErr))
|
|
Expect(n).To(Equal(4))
|
|
close(done)
|
|
}()
|
|
|
|
Eventually(func() []byte { data, _ := str.GetDataForWriting(4); return data }).ShouldNot(BeEmpty())
|
|
str.RegisterRemoteError(testErr, 10)
|
|
Eventually(done).Should(BeClosed())
|
|
})
|
|
|
|
It("calls onReset when receiving a remote error", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
done := make(chan struct{})
|
|
str.writeOffset = 0x1000
|
|
go func() {
|
|
_, _ = strWithTimeout.Write([]byte("foobar"))
|
|
close(done)
|
|
}()
|
|
str.RegisterRemoteError(testErr, 0)
|
|
Expect(resetCalled).To(BeTrue())
|
|
Expect(resetCalledForStream).To(Equal(protocol.StreamID(1337)))
|
|
Expect(resetCalledAtOffset).To(Equal(protocol.ByteCount(0x1000)))
|
|
Eventually(done).Should(BeClosed())
|
|
})
|
|
|
|
It("doesn't call onReset if it already sent a FIN", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
str.Close()
|
|
_, sentFin := str.GetDataForWriting(1000)
|
|
Expect(sentFin).To(BeTrue())
|
|
str.RegisterRemoteError(testErr, 0)
|
|
Expect(resetCalled).To(BeFalse())
|
|
})
|
|
|
|
It("doesn't call onReset if the stream was reset locally before", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
str.Reset(testErr)
|
|
Expect(resetCalled).To(BeTrue())
|
|
resetCalled = false
|
|
str.RegisterRemoteError(testErr, 0)
|
|
Expect(resetCalled).To(BeFalse())
|
|
})
|
|
|
|
It("doesn't call onReset twice, when it gets two remote errors", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
str.RegisterRemoteError(testErr, 0)
|
|
Expect(resetCalled).To(BeTrue())
|
|
resetCalled = false
|
|
str.RegisterRemoteError(testErr, 0)
|
|
Expect(resetCalled).To(BeFalse())
|
|
})
|
|
})
|
|
|
|
Context("reset locally", func() {
|
|
It("stops writing", func() {
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(testErr))
|
|
close(done)
|
|
}()
|
|
Consistently(done).ShouldNot(BeClosed())
|
|
str.Reset(testErr)
|
|
Expect(str.GetDataForWriting(6)).To(BeNil())
|
|
Eventually(done).Should(BeClosed())
|
|
})
|
|
|
|
It("doesn't allow further writes", func() {
|
|
str.Reset(testErr)
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(testErr))
|
|
Expect(str.GetDataForWriting(6)).To(BeNil())
|
|
})
|
|
|
|
It("stops reading", func() {
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
b := make([]byte, 4)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(testErr))
|
|
close(done)
|
|
}()
|
|
Consistently(done).ShouldNot(BeClosed())
|
|
str.Reset(testErr)
|
|
Eventually(done).Should(BeClosed())
|
|
})
|
|
|
|
It("doesn't allow further reads", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(6), false)
|
|
str.AddStreamFrame(&wire.StreamFrame{
|
|
Data: []byte("foobar"),
|
|
})
|
|
str.Reset(testErr)
|
|
b := make([]byte, 6)
|
|
n, err := strWithTimeout.Read(b)
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(testErr))
|
|
})
|
|
|
|
It("calls onReset", func() {
|
|
str.writeOffset = 0x1000
|
|
str.Reset(testErr)
|
|
Expect(resetCalled).To(BeTrue())
|
|
Expect(resetCalledForStream).To(Equal(protocol.StreamID(1337)))
|
|
Expect(resetCalledAtOffset).To(Equal(protocol.ByteCount(0x1000)))
|
|
})
|
|
|
|
It("doesn't call onReset if it already sent a FIN", func() {
|
|
str.Close()
|
|
_, sentFin := str.GetDataForWriting(1000)
|
|
Expect(sentFin).To(BeTrue())
|
|
str.Reset(testErr)
|
|
Expect(resetCalled).To(BeFalse())
|
|
})
|
|
|
|
It("doesn't call onReset if the stream was reset remotely before", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
str.RegisterRemoteError(testErr, 0)
|
|
Expect(resetCalled).To(BeTrue())
|
|
resetCalled = false
|
|
str.Reset(testErr)
|
|
Expect(resetCalled).To(BeFalse())
|
|
})
|
|
|
|
It("doesn't call onReset twice", func() {
|
|
str.Reset(testErr)
|
|
Expect(resetCalled).To(BeTrue())
|
|
resetCalled = false
|
|
str.Reset(testErr)
|
|
Expect(resetCalled).To(BeFalse())
|
|
})
|
|
|
|
It("cancels the context", func() {
|
|
Expect(str.Context().Done()).ToNot(BeClosed())
|
|
str.Reset(testErr)
|
|
Expect(str.Context().Done()).To(BeClosed())
|
|
})
|
|
})
|
|
})
|
|
|
|
Context("writing", func() {
|
|
It("writes and gets all data at once", func() {
|
|
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999))
|
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(6))
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(6))
|
|
close(done)
|
|
}()
|
|
Eventually(func() []byte {
|
|
str.mutex.Lock()
|
|
defer str.mutex.Unlock()
|
|
return str.dataForWriting
|
|
}).Should(Equal([]byte("foobar")))
|
|
Consistently(done).ShouldNot(BeClosed())
|
|
Expect(onDataCalled).To(BeTrue())
|
|
Expect(str.HasDataForWriting()).To(BeTrue())
|
|
data, sendFin := str.GetDataForWriting(1000)
|
|
Expect(data).To(Equal([]byte("foobar")))
|
|
Expect(sendFin).To(BeFalse())
|
|
Expect(str.writeOffset).To(Equal(protocol.ByteCount(6)))
|
|
Expect(str.dataForWriting).To(BeNil())
|
|
Eventually(done).Should(BeClosed())
|
|
})
|
|
|
|
It("writes and gets data in two turns", func() {
|
|
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999)).Times(2)
|
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3)).Times(2)
|
|
done := make(chan struct{})
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(6))
|
|
close(done)
|
|
}()
|
|
Eventually(func() []byte {
|
|
str.mutex.Lock()
|
|
defer str.mutex.Unlock()
|
|
return str.dataForWriting
|
|
}).Should(Equal([]byte("foobar")))
|
|
Consistently(done).ShouldNot(BeClosed())
|
|
Expect(str.HasDataForWriting()).To(BeTrue())
|
|
data, sendFin := str.GetDataForWriting(3)
|
|
Expect(data).To(Equal([]byte("foo")))
|
|
Expect(sendFin).To(BeFalse())
|
|
Expect(str.writeOffset).To(Equal(protocol.ByteCount(3)))
|
|
Expect(str.dataForWriting).ToNot(BeNil())
|
|
Expect(str.HasDataForWriting()).To(BeTrue())
|
|
data, sendFin = str.GetDataForWriting(3)
|
|
Expect(data).To(Equal([]byte("bar")))
|
|
Expect(sendFin).To(BeFalse())
|
|
Expect(str.writeOffset).To(Equal(protocol.ByteCount(6)))
|
|
Expect(str.dataForWriting).To(BeNil())
|
|
Expect(str.HasDataForWriting()).To(BeFalse())
|
|
Eventually(done).Should(BeClosed())
|
|
})
|
|
|
|
It("getDataForWriting returns nil if no data is available", func() {
|
|
Expect(str.GetDataForWriting(1000)).To(BeNil())
|
|
})
|
|
|
|
It("copies the slice while writing", func() {
|
|
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999))
|
|
mockFC.EXPECT().AddBytesSent(protocol.ByteCount(3))
|
|
s := []byte("foo")
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
n, err := strWithTimeout.Write(s)
|
|
Expect(err).ToNot(HaveOccurred())
|
|
Expect(n).To(Equal(3))
|
|
}()
|
|
Eventually(func() bool { return str.HasDataForWriting() }).Should(BeTrue())
|
|
s[0] = 'v'
|
|
Expect(str.GetDataForWriting(3)).To(Equal([]byte("foo")))
|
|
})
|
|
|
|
It("returns when given a nil input", func() {
|
|
n, err := strWithTimeout.Write(nil)
|
|
Expect(n).To(BeZero())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
It("returns when given an empty slice", func() {
|
|
n, err := strWithTimeout.Write([]byte(""))
|
|
Expect(n).To(BeZero())
|
|
Expect(err).ToNot(HaveOccurred())
|
|
})
|
|
|
|
Context("deadlines", func() {
|
|
It("returns an error when Write is called after the deadline", func() {
|
|
str.SetWriteDeadline(time.Now().Add(-time.Second))
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).To(MatchError(errDeadline))
|
|
Expect(n).To(BeZero())
|
|
})
|
|
|
|
It("unblocks after the deadline", func() {
|
|
deadline := time.Now().Add(scaleDuration(50 * time.Millisecond))
|
|
str.SetWriteDeadline(deadline)
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).To(MatchError(errDeadline))
|
|
Expect(n).To(BeZero())
|
|
Expect(time.Now()).To(BeTemporally("~", deadline, scaleDuration(20*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.SetWriteDeadline(deadline1)
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
time.Sleep(scaleDuration(20 * time.Millisecond))
|
|
str.SetWriteDeadline(deadline2)
|
|
// make sure that this was actually execute before the deadline expires
|
|
Expect(time.Now()).To(BeTemporally("<", deadline1))
|
|
}()
|
|
runtime.Gosched()
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
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.SetWriteDeadline(deadline2)
|
|
// make sure that this was actually execute before the deadline expires
|
|
Expect(time.Now()).To(BeTemporally("<", deadline2))
|
|
}()
|
|
str.SetWriteDeadline(deadline1)
|
|
runtime.Gosched()
|
|
_, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).To(MatchError(errDeadline))
|
|
Expect(time.Now()).To(BeTemporally("~", deadline2, scaleDuration(20*time.Millisecond)))
|
|
})
|
|
|
|
It("sets a read deadline, when SetDeadline is called", func() {
|
|
str.SetDeadline(time.Now().Add(-time.Second))
|
|
n, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).To(MatchError(errDeadline))
|
|
Expect(n).To(BeZero())
|
|
})
|
|
})
|
|
|
|
Context("closing", func() {
|
|
It("doesn't allow writes after it has been closed", func() {
|
|
str.Close()
|
|
_, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).To(MatchError("write on closed stream 1337"))
|
|
})
|
|
|
|
It("allows FIN", func() {
|
|
str.Close()
|
|
Expect(str.HasDataForWriting()).To(BeTrue())
|
|
data, sendFin := str.GetDataForWriting(1000)
|
|
Expect(data).To(BeEmpty())
|
|
Expect(sendFin).To(BeTrue())
|
|
})
|
|
|
|
It("does not allow FIN when there's still data", func() {
|
|
mockFC.EXPECT().SendWindowSize().Return(protocol.ByteCount(9999)).Times(2)
|
|
mockFC.EXPECT().AddBytesSent(gomock.Any()).Times(2)
|
|
str.dataForWriting = []byte("foobar")
|
|
str.Close()
|
|
Expect(str.HasDataForWriting()).To(BeTrue())
|
|
data, sendFin := str.GetDataForWriting(3)
|
|
Expect(data).To(Equal([]byte("foo")))
|
|
Expect(sendFin).To(BeFalse())
|
|
data, sendFin = str.GetDataForWriting(3)
|
|
Expect(data).To(Equal([]byte("bar")))
|
|
Expect(sendFin).To(BeTrue())
|
|
})
|
|
|
|
It("does not allow FIN when the stream is not closed", func() {
|
|
Expect(str.HasDataForWriting()).To(BeFalse())
|
|
_, sendFin := str.GetDataForWriting(3)
|
|
Expect(sendFin).To(BeFalse())
|
|
})
|
|
|
|
It("does not allow FIN after an error", func() {
|
|
str.Cancel(errors.New("test"))
|
|
Expect(str.HasDataForWriting()).To(BeFalse())
|
|
data, sendFin := str.GetDataForWriting(1000)
|
|
Expect(data).To(BeEmpty())
|
|
Expect(sendFin).To(BeFalse())
|
|
})
|
|
|
|
It("does not allow FIN twice", func() {
|
|
str.Close()
|
|
Expect(str.HasDataForWriting()).To(BeTrue())
|
|
data, sendFin := str.GetDataForWriting(1000)
|
|
Expect(data).To(BeEmpty())
|
|
Expect(sendFin).To(BeTrue())
|
|
Expect(str.HasDataForWriting()).To(BeFalse())
|
|
data, sendFin = str.GetDataForWriting(1000)
|
|
Expect(data).To(BeEmpty())
|
|
Expect(sendFin).To(BeFalse())
|
|
})
|
|
})
|
|
|
|
Context("cancelling", func() {
|
|
testErr := errors.New("test")
|
|
|
|
It("returns errors when the stream is cancelled", func() {
|
|
str.Cancel(testErr)
|
|
n, err := strWithTimeout.Write([]byte("foo"))
|
|
Expect(n).To(BeZero())
|
|
Expect(err).To(MatchError(testErr))
|
|
})
|
|
|
|
It("doesn't get data for writing if an error occurred", func() {
|
|
go func() {
|
|
defer GinkgoRecover()
|
|
_, err := strWithTimeout.Write([]byte("foobar"))
|
|
Expect(err).To(MatchError(testErr))
|
|
}()
|
|
Eventually(func() []byte { return str.dataForWriting }).ShouldNot(BeNil())
|
|
Expect(str.HasDataForWriting()).To(BeTrue())
|
|
str.Cancel(testErr)
|
|
data, sendFin := str.GetDataForWriting(6)
|
|
Expect(data).To(BeNil())
|
|
Expect(sendFin).To(BeFalse())
|
|
Expect(str.HasDataForWriting()).To(BeFalse())
|
|
})
|
|
})
|
|
})
|
|
|
|
It("errors when a StreamFrames 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.AddStreamFrame(&frame)
|
|
Expect(err).To(MatchError(testErr))
|
|
})
|
|
|
|
Context("closing", func() {
|
|
testErr := errors.New("testErr")
|
|
|
|
finishReading := func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
err := str.AddStreamFrame(&wire.StreamFrame{FinBit: true})
|
|
Expect(err).ToNot(HaveOccurred())
|
|
b := make([]byte, 100)
|
|
_, err = strWithTimeout.Read(b)
|
|
Expect(err).To(MatchError(io.EOF))
|
|
}
|
|
|
|
It("is finished after it is canceled", func() {
|
|
str.Cancel(testErr)
|
|
Expect(str.Finished()).To(BeTrue())
|
|
})
|
|
|
|
It("is not finished if it is only closed for writing", func() {
|
|
str.Close()
|
|
_, sentFin := str.GetDataForWriting(1000)
|
|
Expect(sentFin).To(BeTrue())
|
|
Expect(str.Finished()).To(BeFalse())
|
|
})
|
|
|
|
It("cancels the context after it is closed", func() {
|
|
Expect(str.Context().Done()).ToNot(BeClosed())
|
|
str.Close()
|
|
Expect(str.Context().Done()).To(BeClosed())
|
|
})
|
|
|
|
It("is not finished if it is only closed for reading", func() {
|
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(0))
|
|
finishReading()
|
|
Expect(str.Finished()).To(BeFalse())
|
|
})
|
|
|
|
It("is finished after receiving a RST and sending one", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
// this directly sends a rst
|
|
str.RegisterRemoteError(testErr, 0)
|
|
Expect(str.rstSent.Get()).To(BeTrue())
|
|
Expect(str.Finished()).To(BeTrue())
|
|
})
|
|
|
|
It("cancels the context after receiving a RST", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(0), true)
|
|
Expect(str.Context().Done()).ToNot(BeClosed())
|
|
str.RegisterRemoteError(testErr, 0)
|
|
Expect(str.Context().Done()).To(BeClosed())
|
|
})
|
|
|
|
It("is finished after being locally reset and receiving a RST in response", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(13), true)
|
|
str.Reset(testErr)
|
|
Expect(str.Finished()).To(BeFalse())
|
|
str.RegisterRemoteError(testErr, 13)
|
|
Expect(str.Finished()).To(BeTrue())
|
|
})
|
|
|
|
It("is finished after finishing writing and receiving a RST", func() {
|
|
mockFC.EXPECT().UpdateHighestReceived(protocol.ByteCount(13), true)
|
|
str.Close()
|
|
_, sentFin := str.GetDataForWriting(1000)
|
|
Expect(sentFin).To(BeTrue())
|
|
str.RegisterRemoteError(testErr, 13)
|
|
Expect(str.Finished()).To(BeTrue())
|
|
})
|
|
|
|
It("is finished after finishing reading and being locally reset", func() {
|
|
mockFC.EXPECT().AddBytesRead(protocol.ByteCount(0))
|
|
finishReading()
|
|
Expect(str.Finished()).To(BeFalse())
|
|
str.Reset(testErr)
|
|
Expect(str.Finished()).To(BeTrue())
|
|
})
|
|
})
|
|
|
|
Context("flow control", func() {
|
|
It("says when it's flow control blocked", func() {
|
|
mockFC.EXPECT().IsBlocked().Return(false)
|
|
Expect(str.IsFlowControlBlocked()).To(BeFalse())
|
|
mockFC.EXPECT().IsBlocked().Return(true)
|
|
Expect(str.IsFlowControlBlocked()).To(BeTrue())
|
|
})
|
|
|
|
It("updates the flow control window", func() {
|
|
mockFC.EXPECT().UpdateSendWindow(protocol.ByteCount(0x42))
|
|
str.UpdateSendWindow(0x42)
|
|
})
|
|
|
|
It("gets a window update", func() {
|
|
mockFC.EXPECT().GetWindowUpdate().Return(protocol.ByteCount(0x100))
|
|
Expect(str.GetWindowUpdate()).To(Equal(protocol.ByteCount(0x100)))
|
|
})
|
|
})
|
|
})
|