diff --git a/internal/wire/crypto_frame.go b/internal/wire/crypto_frame.go index eeafea974..dd814fa51 100644 --- a/internal/wire/crypto_frame.go +++ b/internal/wire/crypto_frame.go @@ -69,3 +69,34 @@ func (f *CryptoFrame) MaxDataLen(maxSize protocol.ByteCount) protocol.ByteCount } return maxDataLen } + +// MaybeSplitOffFrame splits a frame such that it is not bigger than n bytes. +// It returns if the frame was actually split. +// The frame might not be split if: +// * the size is large enough to fit the whole frame +// * the size is too small to fit even a 1-byte frame. In that case, the frame returned is nil. +func (f *CryptoFrame) MaybeSplitOffFrame(maxSize protocol.ByteCount, version protocol.VersionNumber) (*CryptoFrame, bool /* was splitting required */) { + if f.Length(version) <= maxSize { + return nil, false + } + + n := f.MaxDataLen(maxSize) + if n == 0 { + return nil, true + } + + newLen := protocol.ByteCount(len(f.Data)) - n + + new := &CryptoFrame{} + new.Offset = f.Offset + new.Data = make([]byte, newLen) + + // swap the data slices + new.Data, f.Data = f.Data, new.Data + + copy(f.Data, new.Data[n:]) + new.Data = new.Data[:n] + f.Offset += n + + return new, true +} diff --git a/internal/wire/crypto_frame_test.go b/internal/wire/crypto_frame_test.go index b2b19cfa2..041ea0513 100644 --- a/internal/wire/crypto_frame_test.go +++ b/internal/wire/crypto_frame_test.go @@ -102,4 +102,46 @@ var _ = Describe("CRYPTO frame", func() { Expect(f.Length(versionIETFFrames)).To(Equal(1 + utils.VarIntLen(0x1337) + utils.VarIntLen(6) + 6)) }) }) + + Context("splitting", func() { + It("splits a frame", func() { + f := &CryptoFrame{ + Offset: 0x1337, + Data: []byte("foobar"), + } + hdrLen := f.Length(versionIETFFrames) - 6 + new, needsSplit := f.MaybeSplitOffFrame(hdrLen+3, versionIETFFrames) + Expect(needsSplit).To(BeTrue()) + Expect(new.Data).To(Equal([]byte("foo"))) + Expect(new.Offset).To(Equal(protocol.ByteCount(0x1337))) + Expect(f.Data).To(Equal([]byte("bar"))) + Expect(f.Offset).To(Equal(protocol.ByteCount(0x1337 + 3))) + }) + + It("doesn't split if there's enough space in the frame", func() { + f := &CryptoFrame{ + Offset: 0x1337, + Data: []byte("foobar"), + } + f, needsSplit := f.MaybeSplitOffFrame(f.Length(versionIETFFrames), versionIETFFrames) + Expect(needsSplit).To(BeFalse()) + Expect(f).To(BeNil()) + }) + + It("doesn't split if the size is too small", func() { + f := &CryptoFrame{ + Offset: 0x1337, + Data: []byte("foobar"), + } + length := f.Length(versionIETFFrames) - 6 + for i := protocol.ByteCount(0); i <= length; i++ { + f, needsSplit := f.MaybeSplitOffFrame(i, versionIETFFrames) + Expect(needsSplit).To(BeTrue()) + Expect(f).To(BeNil()) + } + f, needsSplit := f.MaybeSplitOffFrame(length+1, versionIETFFrames) + Expect(needsSplit).To(BeTrue()) + Expect(f).ToNot(BeNil()) + }) + }) })