diff options
| author | 2026-06-08 09:58:26 +0000 | |
|---|---|---|
| committer | 2026-06-08 09:58:26 +0000 | |
| commit | 71f44dca0b6210fa501e9d8450ee3d7bf5f73347 (patch) | |
| tree | af6af234e9f12d8aa71e59ffb77ae06635df289b /internal/iolimit/expect_length_reader.go | |
| parent | REFATOR: object and object/fetch are done (diff) | |
| signature | No signature | |
internal/iolimit: Add
Might move this to lgo sometime
Diffstat (limited to 'internal/iolimit/expect_length_reader.go')
| -rw-r--r-- | internal/iolimit/expect_length_reader.go | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/internal/iolimit/expect_length_reader.go b/internal/iolimit/expect_length_reader.go new file mode 100644 index 00000000..317f1b16 --- /dev/null +++ b/internal/iolimit/expect_length_reader.go @@ -0,0 +1,78 @@ +package iolimit + +import ( + "errors" + "io" +) + +// ErrExpectedLengthExceeded reports that a stream +// produced bytes beyond the expected length. +var ErrExpectedLengthExceeded = errors.New("iolimit: stream exceeded expected length") + +// ExpectLengthReader wraps src and enforces an expected byte length. +// +// It returns io.ErrUnexpectedEOF +// if src ends before expected bytes are read. +// It returns ErrExpectedLengthExceeded +// if reads continue beyond the expected boundary +// and src still produces bytes. +// +// This reader does not drain src on close or at the expected boundary. +// As a result, +// overlength streams are detected only +// when a caller reads at or past the boundary. +func ExpectLengthReader(src io.Reader, expected uint64) io.Reader { + return &expectLengthReader{ + src: src, + remaining: expected, + } +} + +type expectLengthReader struct { + src io.Reader + remaining uint64 +} + +func (reader *expectLengthReader) Read(dst []byte) (int, error) { + if len(dst) == 0 { + return 0, nil + } + + if reader.remaining == 0 { + var probe [1]byte + + n, err := reader.src.Read(probe[:]) + if n > 0 { + return 0, ErrExpectedLengthExceeded + } + + if err == nil { + return 0, nil + } + + return 0, err //nolint:wrapcheck + } + + if uint64(len(dst)) > reader.remaining { + dst = dst[:int(reader.remaining)] + } + + n, err := reader.src.Read(dst) + if n > 0 { + reader.remaining -= uint64(n) + } + + if errors.Is(err, io.EOF) { + if reader.remaining > 0 { + return n, io.ErrUnexpectedEOF + } + + if n > 0 { + return n, nil + } + + return 0, io.EOF + } + + return n, err //nolint:wrapcheck +} |
