Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide documentation and context information for NIOTooManyBytesError #2831

Merged
merged 9 commits into from
Aug 28, 2024
41 changes: 36 additions & 5 deletions Sources/NIOCore/AsyncAwaitSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,39 @@ extension ChannelPipeline {
}
}

public struct NIOTooManyBytesError: Error, Hashable {
public init() {}
/// An error that is thrown when the number of bytes in an AsyncSequence exceeds the limit.
///
/// When collecting the bytes from an AsyncSequence, there is a limit up to where the content
/// exceeds a certain threshold beyond which the content isn't matching an expected reasonable
/// size to be processed. This error is generally thrown when it is discovered that there are more
/// more bytes in a sequence than what was specified as the maximum. It could be that this upTo
/// limit should be increased, or that the sequence has unexpected content in it.
public struct NIOTooManyBytesError: Error {
/// Current limit on the maximum number of bytes in the sequence
public var maxBytes: Int?

@available(*, deprecated, message: "Construct the NIOTooManyBytesError with the maxBytes limit that triggered this error")
public init() {
Lukasa marked this conversation as resolved.
Show resolved Hide resolved
self.maxBytes = nil
}

public init(maxBytes: Int) {
self.maxBytes = maxBytes
}
}

extension NIOTooManyBytesError: Equatable {
public static func == (lhs: NIOTooManyBytesError, rhs: NIOTooManyBytesError) -> Bool {
// Equality of the maxBytes isn't of consequence
return true
}
}

extension NIOTooManyBytesError: Hashable {
public func hash(into hasher: inout Hasher) {
// All errors of this type hash to the same value since maxBytes isn't of consequence
hasher.combine(7)
}
}

@available(macOS 10.15, iOS 13, tvOS 13, watchOS 6, *)
Expand All @@ -262,7 +293,7 @@ extension AsyncSequence where Element: RandomAccessCollection, Element.Element =
for try await fragment in self {
bytesRead += fragment.count
guard bytesRead <= maxBytes else {
throw NIOTooManyBytesError()
throw NIOTooManyBytesError(maxBytes: maxBytes)
}
accumulationBuffer.writeBytes(fragment)
}
Expand Down Expand Up @@ -305,7 +336,7 @@ extension AsyncSequence where Element == ByteBuffer {
for try await fragment in self {
bytesRead += fragment.readableBytes
guard bytesRead <= maxBytes else {
throw NIOTooManyBytesError()
throw NIOTooManyBytesError(maxBytes: maxBytes)
}
accumulationBuffer.writeImmutableBuffer(fragment)
}
Expand All @@ -328,7 +359,7 @@ extension AsyncSequence where Element == ByteBuffer {
return ByteBuffer()
}
guard head.readableBytes <= maxBytes else {
throw NIOTooManyBytesError()
throw NIOTooManyBytesError(maxBytes: maxBytes)
}

let tail = AsyncSequenceFromIterator(iterator)
Expand Down
35 changes: 33 additions & 2 deletions Tests/NIOCoreTests/AsyncSequenceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,11 @@ final class AsyncSequenceCollectTests: XCTestCase {
}

// test for the generic version
let maxBytes = max(expectedBytes.count - 1, 0)
await XCTAssertThrowsError(
try await testCase.buffers
.asAsyncSequence()
.collect(upTo: max(expectedBytes.count - 1, 0), using: .init()),
.collect(upTo: maxBytes, using: .init()),
file: testCase.file,
line: testCase.line
) { error in
Expand All @@ -138,14 +139,29 @@ final class AsyncSequenceCollectTests: XCTestCase {
file: testCase.file,
line: testCase.line
)
guard let tooManyBytesErr = error as? NIOTooManyBytesError else {
XCTFail(
"Error was not an NIOTooManyBytesError",
file: testCase.file,
line: testCase.line
)
return
}

XCTAssertEqual(
maxBytes,
tooManyBytesErr.maxBytes,
file: testCase.file,
line: testCase.line
)
}

// test for the `ByteBuffer` optimised version
await XCTAssertThrowsError(
try await testCase.buffers
.map(ByteBuffer.init(bytes:))
.asAsyncSequence()
.collect(upTo: max(expectedBytes.count - 1, 0)),
.collect(upTo: maxBytes),
file: testCase.file,
line: testCase.line
) { error in
Expand All @@ -154,6 +170,21 @@ final class AsyncSequenceCollectTests: XCTestCase {
file: testCase.file,
line: testCase.line
)
guard let tooManyBytesErr = error as? NIOTooManyBytesError else {
XCTFail(
"Error was not an NIOTooManyBytesError",
file: testCase.file,
line: testCase.line
)
return
}

// Sometimes the max bytes is subtracted from the header size
XCTAssertTrue(
tooManyBytesErr.maxBytes != nil && tooManyBytesErr.maxBytes! <= maxBytes,
file: testCase.file,
line: testCase.line
)
}
}
}
Expand Down
Loading