From b8bc008ba962688a8f1a1aa56843bafdccb54d77 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Sun, 26 Feb 2017 16:41:37 +0700 Subject: [PATCH 1/3] simplify tests of the streamFrameSorter --- stream_frame_sorter_test.go | 118 +++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 57 deletions(-) diff --git a/stream_frame_sorter_test.go b/stream_frame_sorter_test.go index f8b70c2c..22c17415 100644 --- a/stream_frame_sorter_test.go +++ b/stream_frame_sorter_test.go @@ -8,18 +8,18 @@ import ( . "github.com/onsi/gomega" ) -func compareGapValues(gapList *utils.ByteIntervalList, expectedGaps []utils.ByteInterval) { - Expect(gapList.Len()).To(Equal(len(expectedGaps))) - var i int - for gap := gapList.Front(); gap != nil; gap = gap.Next() { - Expect(gap.Value).To(Equal(expectedGaps[i])) - i++ - } -} - var _ = Describe("StreamFrame sorter", func() { var s *streamFrameSorter + checkGaps := func(expectedGaps []utils.ByteInterval) { + Expect(s.gaps.Len()).To(Equal(len(expectedGaps))) + var i int + for gap := s.gaps.Front(); gap != nil; gap = gap.Next() { + Expect(gap.Value).To(Equal(expectedGaps[i])) + i++ + } + } + BeforeEach(func() { s = newStreamFrameSorter() }) @@ -102,8 +102,10 @@ var _ = Describe("StreamFrame sorter", func() { } err := s.Push(f) Expect(err).ToNot(HaveOccurred()) - Expect(s.gaps.Len()).To(Equal(2)) - Expect(s.gaps.Front().Value).To(Equal(utils.ByteInterval{Start: 0, End: 10})) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 10}, + {Start: 16, End: protocol.MaxByteCount}, + }) }) It("correctly sets the first gap for a frame with offset 0", func() { @@ -113,8 +115,9 @@ var _ = Describe("StreamFrame sorter", func() { } err := s.Push(f) Expect(err).ToNot(HaveOccurred()) - Expect(s.gaps.Len()).To(Equal(1)) - Expect(s.gaps.Front().Value).To(Equal(utils.ByteInterval{Start: 6, End: protocol.MaxByteCount})) + checkGaps([]utils.ByteInterval{ + {Start: 6, End: protocol.MaxByteCount}, + }) }) It("finds the two gaps", func() { @@ -130,12 +133,11 @@ var _ = Describe("StreamFrame sorter", func() { } err = s.Push(f2) Expect(err).ToNot(HaveOccurred()) - Expect(s.gaps.Len()).To(Equal(3)) - el := s.gaps.Front() // first gap - Expect(el.Value).To(Equal(utils.ByteInterval{Start: 0, End: 10})) - el = el.Next() // second gap - Expect(el.Value).To(Equal(utils.ByteInterval{Start: 16, End: 20})) - Expect(s.gaps.Back().Value.Start).To(Equal(protocol.ByteCount(26))) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 10}, + {Start: 16, End: 20}, + {Start: 26, End: protocol.MaxByteCount}, + }) }) It("finds the two gaps in reverse order", func() { @@ -151,12 +153,11 @@ var _ = Describe("StreamFrame sorter", func() { } err = s.Push(f2) Expect(err).ToNot(HaveOccurred()) - Expect(s.gaps.Len()).To(Equal(3)) - el := s.gaps.Front() // first gap - Expect(el.Value).To(Equal(utils.ByteInterval{Start: 0, End: 10})) - el = el.Next() // second gap - Expect(el.Value).To(Equal(utils.ByteInterval{Start: 16, End: 20})) - Expect(s.gaps.Back().Value).To(Equal(utils.ByteInterval{Start: 26, End: protocol.MaxByteCount})) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 10}, + {Start: 16, End: 20}, + {Start: 26, End: protocol.MaxByteCount}, + }) }) It("shrinks a gap when it is partially filled", func() { @@ -172,9 +173,10 @@ var _ = Describe("StreamFrame sorter", func() { } err = s.Push(f2) Expect(err).ToNot(HaveOccurred()) - Expect(s.gaps.Len()).To(Equal(2)) - Expect(s.gaps.Front().Value).To(Equal(utils.ByteInterval{Start: 0, End: 4})) - Expect(s.gaps.Back().Value).To(Equal(utils.ByteInterval{Start: 14, End: protocol.MaxByteCount})) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 4}, + {Start: 14, End: protocol.MaxByteCount}, + }) }) It("deletes a gap at the beginning, when it is filled", func() { @@ -190,8 +192,9 @@ var _ = Describe("StreamFrame sorter", func() { } err = s.Push(f2) Expect(err).ToNot(HaveOccurred()) - Expect(s.gaps.Len()).To(Equal(1)) - Expect(s.gaps.Front().Value).To(Equal(utils.ByteInterval{Start: 10, End: protocol.MaxByteCount})) + checkGaps([]utils.ByteInterval{ + {Start: 10, End: protocol.MaxByteCount}, + }) }) It("deletes a gap in the middle, when it is filled", func() { @@ -213,9 +216,10 @@ var _ = Describe("StreamFrame sorter", func() { } err = s.Push(f3) Expect(err).ToNot(HaveOccurred()) - Expect(s.gaps.Len()).To(Equal(1)) - Expect(s.gaps.Front().Value).To(Equal(utils.ByteInterval{Start: 15, End: protocol.MaxByteCount})) Expect(s.queuedFrames).To(HaveLen(3)) + checkGaps([]utils.ByteInterval{ + {Start: 15, End: protocol.MaxByteCount}, + }) }) It("splits a gap into two", func() { @@ -231,30 +235,30 @@ var _ = Describe("StreamFrame sorter", func() { } err = s.Push(f2) Expect(err).ToNot(HaveOccurred()) - Expect(s.gaps.Len()).To(Equal(3)) - el := s.gaps.Front() // first gap - Expect(el.Value).To(Equal(utils.ByteInterval{Start: 0, End: 50})) - el = el.Next() // second gap - Expect(el.Value).To(Equal(utils.ByteInterval{Start: 56, End: 100})) - Expect(s.gaps.Back().Value).To(Equal(utils.ByteInterval{Start: 104, End: protocol.MaxByteCount})) Expect(s.queuedFrames).To(HaveLen(2)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 50}, + {Start: 56, End: 100}, + {Start: 104, End: protocol.MaxByteCount}, + }) }) Context("Overlapping Stream Data detection", func() { - var expectedGaps []utils.ByteInterval + // create gaps: 0-5, 10-15, 20-25, 30-inf + expectedGaps := []utils.ByteInterval{ + {Start: 0, End: 5}, + {Start: 10, End: 15}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + } + BeforeEach(func() { - // create gaps: 0-5, 10-15, 15-20, 30-inf - expectedGaps = expectedGaps[:0] - expectedGaps = append(expectedGaps, utils.ByteInterval{Start: 0, End: 5}) err := s.Push(&frames.StreamFrame{Offset: 5, Data: []byte("12345")}) Expect(err).ToNot(HaveOccurred()) - expectedGaps = append(expectedGaps, utils.ByteInterval{Start: 10, End: 15}) err = s.Push(&frames.StreamFrame{Offset: 15, Data: []byte("12345")}) Expect(err).ToNot(HaveOccurred()) - expectedGaps = append(expectedGaps, utils.ByteInterval{Start: 20, End: 25}) err = s.Push(&frames.StreamFrame{Offset: 25, Data: []byte("12345")}) Expect(err).ToNot(HaveOccurred()) - expectedGaps = append(expectedGaps, utils.ByteInterval{Start: 30, End: protocol.MaxByteCount}) }) It("rejects a frame with offset 0 that overlaps at the end", func() { @@ -265,7 +269,7 @@ var _ = Describe("StreamFrame sorter", func() { err := s.Push(f) Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(0))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("rejects a frame that overlaps at the end", func() { @@ -277,7 +281,7 @@ var _ = Describe("StreamFrame sorter", func() { err := s.Push(f) Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(4))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("rejects a frame that completely fills a gap, but overlaps at the end", func() { @@ -289,7 +293,7 @@ var _ = Describe("StreamFrame sorter", func() { err := s.Push(f) Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(10))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("rejects a frame that overlaps at the beginning", func() { @@ -301,7 +305,7 @@ var _ = Describe("StreamFrame sorter", func() { err := s.Push(f) Expect(err).To(MatchError("OverlappingStreamData: start of gap in stream chunk")) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(8))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("rejects a frame that overlaps at the beginning and at the end, starting in a gap", func() { @@ -313,7 +317,7 @@ var _ = Describe("StreamFrame sorter", func() { err := s.Push(f) Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(2))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("rejects a frame that overlaps at the beginning and at the end, starting in data already received", func() { @@ -325,7 +329,7 @@ var _ = Describe("StreamFrame sorter", func() { err := s.Push(f) Expect(err).To(MatchError("OverlappingStreamData: start of gap in stream chunk")) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(8))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("rejects a frame that completely covers two gaps", func() { @@ -337,7 +341,7 @@ var _ = Describe("StreamFrame sorter", func() { err := s.Push(f) Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(10))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) }) @@ -358,14 +362,14 @@ var _ = Describe("StreamFrame sorter", func() { It("detects a complete duplicate frame", func() { err := s.Push(&frames.StreamFrame{Offset: 0, Data: []byte("12345")}) Expect(err).To(MatchError(errDuplicateStreamData)) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("does not modify data when receiving a duplicate", func() { err := s.Push(&frames.StreamFrame{Offset: 0, Data: []byte("67890")}) Expect(err).To(MatchError(errDuplicateStreamData)) Expect(s.queuedFrames[0].Data).To(Equal([]byte("12345"))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("detects a duplicate frame that is smaller than the original, starting at the beginning", func() { @@ -373,7 +377,7 @@ var _ = Describe("StreamFrame sorter", func() { err := s.Push(&frames.StreamFrame{Offset: 10, Data: []byte("12")}) Expect(err).To(MatchError(errDuplicateStreamData)) Expect(s.queuedFrames[10].DataLen()).To(Equal(protocol.ByteCount(5))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("detects a duplicate frame that is smaller than the original, somewhere in the middle", func() { @@ -382,7 +386,7 @@ var _ = Describe("StreamFrame sorter", func() { Expect(err).To(MatchError(errDuplicateStreamData)) Expect(s.queuedFrames[0].DataLen()).To(Equal(protocol.ByteCount(5))) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(1))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) It("detects a duplicate frame that is smaller than the original, with aligned end", func() { @@ -391,7 +395,7 @@ var _ = Describe("StreamFrame sorter", func() { Expect(err).To(MatchError(errDuplicateStreamData)) Expect(s.queuedFrames[0].DataLen()).To(Equal(protocol.ByteCount(5))) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(8))) - compareGapValues(s.gaps, expectedGaps) + checkGaps(expectedGaps) }) }) From 556efdc99d23155c86ef47f4c12a10fe1c6ff31b Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 27 Feb 2017 10:47:59 +0700 Subject: [PATCH 2/3] code refactoring in streamFrameSorter --- stream_frame_sorter.go | 93 +++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 47 deletions(-) diff --git a/stream_frame_sorter.go b/stream_frame_sorter.go index 7a95f1f1..541fdbdf 100644 --- a/stream_frame_sorter.go +++ b/stream_frame_sorter.go @@ -17,7 +17,7 @@ type streamFrameSorter struct { var ( errTooManyGapsInReceivedStreamData = errors.New("Too many gaps in received StreamFrame data") - errDuplicateStreamData = errors.New("Overlapping Stream Data") + errDuplicateStreamData = errors.New("Duplicate Stream Data") errEmptyStreamData = errors.New("Stream Data empty") ) @@ -31,15 +31,7 @@ func newStreamFrameSorter() *streamFrameSorter { } func (s *streamFrameSorter) Push(frame *frames.StreamFrame) error { - _, ok := s.queuedFrames[frame.Offset] - if ok { - return errDuplicateStreamData - } - - start := frame.Offset - end := frame.Offset + frame.DataLen() - - if start == end { + if frame.DataLen() == 0 { if frame.FinBit { s.queuedFrames[frame.Offset] = frame return nil @@ -47,50 +39,57 @@ func (s *streamFrameSorter) Push(frame *frames.StreamFrame) error { return errEmptyStreamData } - var foundInGap bool + if _, ok := s.queuedFrames[frame.Offset]; ok { + return errDuplicateStreamData + } - for gap := s.gaps.Front(); gap != nil; gap = gap.Next() { - // the complete frame lies before or after the gap - if end <= gap.Value.Start || start > gap.Value.End { - continue - } + start := frame.Offset + end := frame.Offset + frame.DataLen() - if start < gap.Value.Start { - return qerr.Error(qerr.OverlappingStreamData, "start of gap in stream chunk") - } + if end <= s.gaps.Front().Value.Start { + return errDuplicateStreamData + } - if start < gap.Value.End && end > gap.Value.End { - return qerr.Error(qerr.OverlappingStreamData, "end of gap in stream chunk") - } - - foundInGap = true - - if start == gap.Value.Start { - if end == gap.Value.End { - s.gaps.Remove(gap) - break - } - if end < gap.Value.End { - gap.Value.Start = end - break - } - } - - if end == gap.Value.End { - gap.Value.End = start - break - } - - if end < gap.Value.End { - intv := utils.ByteInterval{Start: end, End: gap.Value.End} - s.gaps.InsertAfter(intv, gap) - gap.Value.End = start + // skip all gaps that are before this stream frame + var gap *utils.ByteIntervalElement + for gap = s.gaps.Front(); gap != nil; gap = gap.Next() { + if end > gap.Value.Start && start <= gap.Value.End { break } } - if !foundInGap { - return errDuplicateStreamData + if gap == nil { + return errors.New("StreamFrameSorter BUG: no gap found") + } + + if start < gap.Value.Start { + return qerr.Error(qerr.OverlappingStreamData, "start of gap in stream chunk") + } + + if end > gap.Value.End { + return qerr.Error(qerr.OverlappingStreamData, "end of gap in stream chunk") + } + + if start == gap.Value.Start { + if end == gap.Value.End { + // the frame completely fills this gap + // delete the gap + s.gaps.Remove(gap) + } else if end < gap.Value.End { + // the frame covers the beginning of the gap + // adjust the Start value to shrink the gap + gap.Value.Start = end + } + } else if end == gap.Value.End { + // the frame covers the end of the gap + // adjust the End value to shrink the gap + gap.Value.End = start + } else { + // the frame lies within the current gap, splitting it into two + // insert a new gap and adjust the current one + intv := utils.ByteInterval{Start: end, End: gap.Value.End} + s.gaps.InsertAfter(intv, gap) + gap.Value.End = start } if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps { From e674016312318be285a833470d51735bb03a3899 Mon Sep 17 00:00:00 2001 From: Marten Seemann Date: Mon, 27 Feb 2017 13:54:51 +0700 Subject: [PATCH 3/3] accept overlapping stream data --- stream_frame_sorter.go | 74 ++++++++-- stream_frame_sorter_test.go | 266 ++++++++++++++++++++++++++++-------- stream_test.go | 14 +- 3 files changed, 279 insertions(+), 75 deletions(-) diff --git a/stream_frame_sorter.go b/stream_frame_sorter.go index 541fdbdf..99237936 100644 --- a/stream_frame_sorter.go +++ b/stream_frame_sorter.go @@ -5,7 +5,6 @@ import ( "github.com/lucas-clemente/quic-go/frames" "github.com/lucas-clemente/quic-go/protocol" - "github.com/lucas-clemente/quic-go/qerr" "github.com/lucas-clemente/quic-go/utils" ) @@ -39,13 +38,20 @@ func (s *streamFrameSorter) Push(frame *frames.StreamFrame) error { return errEmptyStreamData } - if _, ok := s.queuedFrames[frame.Offset]; ok { - return errDuplicateStreamData + var wasCut bool + if oldFrame, ok := s.queuedFrames[frame.Offset]; ok { + if frame.DataLen() <= oldFrame.DataLen() { + return errDuplicateStreamData + } + frame.Data = frame.Data[oldFrame.DataLen():] + frame.Offset += oldFrame.DataLen() + wasCut = true } start := frame.Offset end := frame.Offset + frame.DataLen() + // the frame is a duplicate. Ignore it if end <= s.gaps.Front().Value.Start { return errDuplicateStreamData } @@ -63,39 +69,77 @@ func (s *streamFrameSorter) Push(frame *frames.StreamFrame) error { } if start < gap.Value.Start { - return qerr.Error(qerr.OverlappingStreamData, "start of gap in stream chunk") + add := gap.Value.Start - start + frame.Offset += add + start += add + frame.Data = frame.Data[add:] + wasCut = true } - if end > gap.Value.End { - return qerr.Error(qerr.OverlappingStreamData, "end of gap in stream chunk") + // find the highest gaps whose Start lies before the end of the frame + endGap := gap + for end >= endGap.Value.End { + nextEndGap := endGap.Next() + if nextEndGap == nil { + return errors.New("StreamFrameSorter BUG: no end gap found") + } + if endGap != gap { + s.gaps.Remove(endGap) + } + if end <= nextEndGap.Value.Start { + break + } + // delete queued frames completely covered by the current frame + delete(s.queuedFrames, endGap.Value.End) + endGap = nextEndGap + } + + if end > endGap.Value.End { + cutLen := end - endGap.Value.End + len := frame.DataLen() - cutLen + end -= cutLen + frame.Data = frame.Data[:len] + wasCut = true } if start == gap.Value.Start { - if end == gap.Value.End { + if end >= gap.Value.End { // the frame completely fills this gap // delete the gap s.gaps.Remove(gap) - } else if end < gap.Value.End { + } + if end < endGap.Value.End { // the frame covers the beginning of the gap // adjust the Start value to shrink the gap - gap.Value.Start = end + endGap.Value.Start = end } - } else if end == gap.Value.End { + } else if end == endGap.Value.End { // the frame covers the end of the gap // adjust the End value to shrink the gap gap.Value.End = start } else { - // the frame lies within the current gap, splitting it into two - // insert a new gap and adjust the current one - intv := utils.ByteInterval{Start: end, End: gap.Value.End} - s.gaps.InsertAfter(intv, gap) - gap.Value.End = start + if gap == endGap { + // the frame lies within the current gap, splitting it into two + // insert a new gap and adjust the current one + intv := utils.ByteInterval{Start: end, End: gap.Value.End} + s.gaps.InsertAfter(intv, gap) + gap.Value.End = start + } else { + gap.Value.End = start + endGap.Value.Start = end + } } if s.gaps.Len() > protocol.MaxStreamFrameSorterGaps { return errTooManyGapsInReceivedStreamData } + if wasCut { + data := make([]byte, frame.DataLen()) + copy(data, frame.Data) + frame.Data = data + } + s.queuedFrames[frame.Offset] = frame return nil } diff --git a/stream_frame_sorter_test.go b/stream_frame_sorter_test.go index 22c17415..4bf1c2ae 100644 --- a/stream_frame_sorter_test.go +++ b/stream_frame_sorter_test.go @@ -1,6 +1,8 @@ package quic import ( + "bytes" + "github.com/lucas-clemente/quic-go/frames" "github.com/lucas-clemente/quic-go/protocol" "github.com/lucas-clemente/quic-go/utils" @@ -245,13 +247,6 @@ var _ = Describe("StreamFrame sorter", func() { Context("Overlapping Stream Data detection", func() { // create gaps: 0-5, 10-15, 20-25, 30-inf - expectedGaps := []utils.ByteInterval{ - {Start: 0, End: 5}, - {Start: 10, End: 15}, - {Start: 20, End: 25}, - {Start: 30, End: protocol.MaxByteCount}, - } - BeforeEach(func() { err := s.Push(&frames.StreamFrame{Offset: 5, Data: []byte("12345")}) Expect(err).ToNot(HaveOccurred()) @@ -259,143 +254,304 @@ var _ = Describe("StreamFrame sorter", func() { Expect(err).ToNot(HaveOccurred()) err = s.Push(&frames.StreamFrame{Offset: 25, Data: []byte("12345")}) Expect(err).ToNot(HaveOccurred()) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 5}, + {Start: 10, End: 15}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) }) - It("rejects a frame with offset 0 that overlaps at the end", func() { + It("cuts a frame with offset 0 that overlaps at the end", func() { f := &frames.StreamFrame{ Offset: 0, Data: []byte("foobar"), } err := s.Push(f) - Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) - Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(0))) - checkGaps(expectedGaps) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(0))) + Expect(s.queuedFrames[0].Data).To(Equal([]byte("fooba"))) + Expect(s.queuedFrames[0].Data).To(HaveCap(5)) + checkGaps([]utils.ByteInterval{ + {Start: 10, End: 15}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) }) - It("rejects a frame that overlaps at the end", func() { - // 4 to 6 + It("cuts a frame that overlaps at the end", func() { + // 4 to 7 f := &frames.StreamFrame{ Offset: 4, - Data: []byte("12"), + Data: []byte("foo"), } err := s.Push(f) - Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) - Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(4))) - checkGaps(expectedGaps) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(4))) + Expect(s.queuedFrames[4].Data).To(Equal([]byte("f"))) + Expect(s.queuedFrames[4].Data).To(HaveCap(1)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 4}, + {Start: 10, End: 15}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) }) - It("rejects a frame that completely fills a gap, but overlaps at the end", func() { + It("cuts a frame that completely fills a gap, but overlaps at the end", func() { // 10 to 16 f := &frames.StreamFrame{ Offset: 10, Data: []byte("foobar"), } err := s.Push(f) - Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) - Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(10))) - checkGaps(expectedGaps) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(10))) + Expect(s.queuedFrames[10].Data).To(Equal([]byte("fooba"))) + Expect(s.queuedFrames[10].Data).To(HaveCap(5)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 5}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) }) - It("rejects a frame that overlaps at the beginning", func() { + It("cuts a frame that overlaps at the beginning", func() { // 8 to 14 f := &frames.StreamFrame{ Offset: 8, Data: []byte("foobar"), } err := s.Push(f) - Expect(err).To(MatchError("OverlappingStreamData: start of gap in stream chunk")) + Expect(err).ToNot(HaveOccurred()) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(8))) - checkGaps(expectedGaps) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(10))) + Expect(s.queuedFrames[10].Data).To(Equal([]byte("obar"))) + Expect(s.queuedFrames[10].Data).To(HaveCap(4)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 5}, + {Start: 14, End: 15}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) }) - It("rejects a frame that overlaps at the beginning and at the end, starting in a gap", func() { - // 2 to 11 + It("processes a frame that overlaps at the beginning and at the end, starting in a gap", func() { + // 2 to 12 f := &frames.StreamFrame{ Offset: 2, - Data: []byte("123456789"), + Data: []byte("1234567890"), } err := s.Push(f) - Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) - Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(2))) - checkGaps(expectedGaps) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(5))) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(2))) + Expect(s.queuedFrames[2].Data).To(Equal([]byte("1234567890"))) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 2}, + {Start: 12, End: 15}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) }) - It("rejects a frame that overlaps at the beginning and at the end, starting in data already received", func() { + It("processes a frame that overlaps at the beginning and at the end, starting in a gap, ending in data", func() { + // 2 to 17 + f := &frames.StreamFrame{ + Offset: 2, + Data: []byte("123456789012345"), + } + err := s.Push(f) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(5))) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(2))) + Expect(s.queuedFrames[2].Data).To(Equal([]byte("1234567890123"))) + Expect(s.queuedFrames[2].Data).To(HaveCap(13)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 2}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) + }) + + It("processes a frame that overlaps at the beginning and at the end, starting in a gap, ending in data", func() { + // 5 to 22 + f := &frames.StreamFrame{ + Offset: 5, + Data: []byte("12345678901234567"), + } + err := s.Push(f) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(5))) + Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(15))) + Expect(s.queuedFrames[10].Data).To(Equal([]byte("678901234567"))) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 5}, + {Start: 22, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) + }) + + It("processes a frame that closes multiple gaps", func() { + // 2 to 27 + f := &frames.StreamFrame{ + Offset: 2, + Data: bytes.Repeat([]byte{'e'}, 25), + } + err := s.Push(f) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(5))) + Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(15))) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(25))) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(2))) + Expect(s.queuedFrames[2].Data).To(Equal(bytes.Repeat([]byte{'e'}, 23))) + Expect(s.queuedFrames[2].Data).To(HaveCap(23)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 2}, + {Start: 30, End: protocol.MaxByteCount}, + }) + }) + + It("processes a frame that closes multiple gaps", func() { + // 5 to 27 + f := &frames.StreamFrame{ + Offset: 5, + Data: bytes.Repeat([]byte{'d'}, 22), + } + err := s.Push(f) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(5))) + Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(15))) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(25))) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(10))) + Expect(s.queuedFrames[10].Data).To(Equal(bytes.Repeat([]byte{'d'}, 15))) + Expect(s.queuedFrames[10].Data).To(HaveCap(15)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 5}, + {Start: 30, End: protocol.MaxByteCount}, + }) + }) + + It("processes a frame that covers multiple gaps and ends at the end of a gap", func() { + // 1 to 15 + f := &frames.StreamFrame{ + Offset: 1, + Data: bytes.Repeat([]byte{'f'}, 14), + } + err := s.Push(f) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(1))) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(15))) + Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(5))) + Expect(s.queuedFrames[1].Data).To(Equal(f.Data)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 1}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) + }) + + It("processes a frame that closes all gaps (except for the last one)", func() { + // 0 to 32 + f := &frames.StreamFrame{ + Offset: 0, + Data: bytes.Repeat([]byte{'f'}, 32), + } + err := s.Push(f) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).To(HaveLen(1)) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(0))) + Expect(s.queuedFrames[0].Data).To(Equal(f.Data)) + checkGaps([]utils.ByteInterval{ + {Start: 32, End: protocol.MaxByteCount}, + }) + }) + + It("cuts a frame that overlaps at the beginning and at the end, starting in data already received", func() { // 8 to 17 f := &frames.StreamFrame{ Offset: 8, Data: []byte("123456789"), } err := s.Push(f) - Expect(err).To(MatchError("OverlappingStreamData: start of gap in stream chunk")) + Expect(err).ToNot(HaveOccurred()) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(8))) - checkGaps(expectedGaps) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(10))) + Expect(s.queuedFrames[10].Data).To(Equal([]byte("34567"))) + Expect(s.queuedFrames[10].Data).To(HaveCap(5)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 5}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) }) - It("rejects a frame that completely covers two gaps", func() { + It("cuts a frame that completely covers two gaps", func() { // 10 to 20 f := &frames.StreamFrame{ Offset: 10, Data: []byte("1234567890"), } err := s.Push(f) - Expect(err).To(MatchError("OverlappingStreamData: end of gap in stream chunk")) - Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(10))) - checkGaps(expectedGaps) + Expect(err).ToNot(HaveOccurred()) + Expect(s.queuedFrames).To(HaveKey(protocol.ByteCount(10))) + Expect(s.queuedFrames[10].Data).To(Equal([]byte("12345"))) + Expect(s.queuedFrames[10].Data).To(HaveCap(5)) + checkGaps([]utils.ByteInterval{ + {Start: 0, End: 5}, + {Start: 20, End: 25}, + {Start: 30, End: protocol.MaxByteCount}, + }) }) }) - Context("Duplicate data detection", func() { - var expectedGaps []utils.ByteInterval + Context("duplicate data", func() { + expectedGaps := []utils.ByteInterval{ + {Start: 5, End: 10}, + {Start: 15, End: protocol.MaxByteCount}, + } BeforeEach(func() { // create gaps: 5-10, 15-20, 25-inf - expectedGaps = expectedGaps[:0] - expectedGaps = append(expectedGaps, utils.ByteInterval{Start: 5, End: 10}) err := s.Push(&frames.StreamFrame{Offset: 0, Data: []byte("12345")}) Expect(err).ToNot(HaveOccurred()) - expectedGaps = append(expectedGaps, utils.ByteInterval{Start: 15, End: protocol.MaxByteCount}) err = s.Push(&frames.StreamFrame{Offset: 10, Data: []byte("12345")}) Expect(err).ToNot(HaveOccurred()) + checkGaps(expectedGaps) }) - It("detects a complete duplicate frame", func() { - err := s.Push(&frames.StreamFrame{Offset: 0, Data: []byte("12345")}) - Expect(err).To(MatchError(errDuplicateStreamData)) + AfterEach(func() { + // check that the gaps were not modified checkGaps(expectedGaps) }) It("does not modify data when receiving a duplicate", func() { - err := s.Push(&frames.StreamFrame{Offset: 0, Data: []byte("67890")}) + err := s.Push(&frames.StreamFrame{Offset: 0, Data: []byte("fffff")}) Expect(err).To(MatchError(errDuplicateStreamData)) - Expect(s.queuedFrames[0].Data).To(Equal([]byte("12345"))) - checkGaps(expectedGaps) + Expect(s.queuedFrames[0].Data).ToNot(Equal([]byte("fffff"))) }) It("detects a duplicate frame that is smaller than the original, starting at the beginning", func() { // 10 to 12 err := s.Push(&frames.StreamFrame{Offset: 10, Data: []byte("12")}) Expect(err).To(MatchError(errDuplicateStreamData)) - Expect(s.queuedFrames[10].DataLen()).To(Equal(protocol.ByteCount(5))) - checkGaps(expectedGaps) + Expect(s.queuedFrames[10].Data).To(HaveLen(5)) }) It("detects a duplicate frame that is smaller than the original, somewhere in the middle", func() { // 1 to 4 err := s.Push(&frames.StreamFrame{Offset: 1, Data: []byte("123")}) Expect(err).To(MatchError(errDuplicateStreamData)) - Expect(s.queuedFrames[0].DataLen()).To(Equal(protocol.ByteCount(5))) + Expect(s.queuedFrames[0].Data).To(HaveLen(5)) Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(1))) - checkGaps(expectedGaps) }) It("detects a duplicate frame that is smaller than the original, with aligned end", func() { // 3 to 5 err := s.Push(&frames.StreamFrame{Offset: 3, Data: []byte("12")}) Expect(err).To(MatchError(errDuplicateStreamData)) - Expect(s.queuedFrames[0].DataLen()).To(Equal(protocol.ByteCount(5))) - Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(8))) - checkGaps(expectedGaps) + Expect(s.queuedFrames[0].Data).To(HaveLen(5)) + Expect(s.queuedFrames).ToNot(HaveKey(protocol.ByteCount(3))) }) }) diff --git a/stream_test.go b/stream_test.go index efe99c9e..4ece7d85 100644 --- a/stream_test.go +++ b/stream_test.go @@ -281,19 +281,23 @@ var _ = Describe("Stream", func() { Expect(b).To(Equal([]byte{0xDE, 0xAD, 0xBE, 0xEF})) }) - It("rejects a StreamFrames with an overlapping data range", func() { + It("doesn't rejects a StreamFrames with an overlapping data range", func() { frame1 := frames.StreamFrame{ Offset: 0, - Data: []byte("ab"), + Data: []byte("foob"), } frame2 := frames.StreamFrame{ - Offset: 1, - Data: []byte("xy"), + Offset: 2, + Data: []byte("obar"), } err := str.AddStreamFrame(&frame1) Expect(err).ToNot(HaveOccurred()) err = str.AddStreamFrame(&frame2) - Expect(err).To(MatchError("OverlappingStreamData: start of gap in stream chunk")) + b := make([]byte, 6) + n, err := str.Read(b) + Expect(err).ToNot(HaveOccurred()) + Expect(n).To(Equal(6)) + Expect(b).To(Equal([]byte("foobar"))) }) It("calls onData", func() {