Skip to content

Commit

Permalink
Provide documentation and context information for NIOTooManyBytesError (
Browse files Browse the repository at this point in the history
#2831)

### Motivation:

The NIOTooManyBytesError doesn't have any documentation for someone that
encounters this error. Also, they have no idea the magnitude of the
limit that was set to decide if the payload is an unreasonable size.

### Modifications:

Provide documentation that explains the situation when the error occurs,
which is the upTo limit of an AsyncSequence is exceeded. Describe one
potential action, which is to increase this limit.

Provide the maxBytes in the error so that the user can gauge whether the
upTo limit is already reasonable and the payload size is excessive, or
the limit needs to be increased to suit more situations.

### Result:

There will be documentation for the NIOTooManyBytesError so that if
someone looks it up they will have a better understanding of the
situation, and possible actions. The limit will be included in the error
to give them an idea of the scale of the payload limit that is in place,
or potentially the configuration value that they can change.

---------

Co-authored-by: Cory Benfield <[email protected]>
  • Loading branch information
cmcgee1024 and Lukasa authored Aug 28, 2024
1 parent 0851091 commit e1b2a99
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 7 deletions.
45 changes: 40 additions & 5 deletions Sources/NIOCore/AsyncAwaitSupport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -240,8 +240,43 @@ 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() {
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
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 +297,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 +340,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 +363,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

0 comments on commit e1b2a99

Please sign in to comment.