Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 42 additions & 52 deletions Tests/HTTPServerTests/HTTPRequestConcludingAsyncReaderTests.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import AsyncStreaming
@testable import HTTPServer
import HTTPTypes
import NIOCore
Expand Down Expand Up @@ -26,7 +27,7 @@ struct HTTPRequestConcludingAsyncReaderTests {

_ = try await requestReader.consumeAndConclude { bodyReader in
var bodyReader = bodyReader
try await bodyReader.read { element in () }
try await bodyReader.read(maximumCount: nil) { element in () }
}
}
}
Expand Down Expand Up @@ -54,20 +55,16 @@ struct HTTPRequestConcludingAsyncReaderTests {
var bodyReader = bodyReader

var buffer = ByteBuffer()
// Read just once: we only sent one body chunk
try await bodyReader.read { element in
if let element {
buffer.writeBytes(element.bytes)
} else {
Issue.record("Unexpectedly failed to read the client's request body")
}
// Read the body chunk
try await bodyReader.read(maximumCount: nil) { element in
buffer.writeBytes(element.bytes)
return
}

// Attempting to read again should result in a `nil` element (we only sent one body chunk)
try await bodyReader.read { element in
if element != nil {
Issue.record("Received a non-nil value after the request body was completely read")
}
// Now read the trailer. We should get back an empty element here, but the trailer should be available in
// the tuple returned by `consumeAndConclude`
try await bodyReader.read(maximumCount: nil) { element in
#expect(element.count == 0)
}

return buffer
Expand All @@ -80,7 +77,6 @@ struct HTTPRequestConcludingAsyncReaderTests {
@Test(
"Streamed request with concluding element",
arguments: [
(0..<10).map { i in ByteBuffer() }, // 10 empty ByteBuffers
(0..<100).map { i in ByteBuffer(bytes: [i]) } // 100 single-byte ByteBuffers
],
[
Expand All @@ -107,25 +103,15 @@ struct HTTPRequestConcludingAsyncReaderTests {
iterator: stream.makeAsyncIterator(),
readerState: .init()
)
let finalElement = try await requestReader.consumeAndConclude { bodyReader in
var bodyReader = bodyReader

for chunk in bodyChunks {
try await bodyReader.read { element in
if let element {
var buffer = ByteBuffer()
buffer.writeBytes(element.bytes)
#expect(chunk == buffer)
} else {
Issue.record("Received a nil element before the request body was completely read")
}
}
}

try await bodyReader.read { element in
if element != nil {
Issue.record("Received a non-nil element after the request body was completely read")
}
let (_, finalElement) = try await requestReader.consumeAndConclude { bodyReader in
// Read all body chunks
var chunksProcessed = 0
try await bodyReader.forEach { element in
var buffer = ByteBuffer()
buffer.writeBytes(element.bytes)
#expect(bodyChunks[chunksProcessed] == buffer)

chunksProcessed += 1
}
}

Expand All @@ -146,18 +132,22 @@ struct HTTPRequestConcludingAsyncReaderTests {
source.yield(.end([.cookie: "test"]))
source.finish()

// Check that the read error is propagated
try await #require(throws: TestError.errorWhileReading) {
let requestReader = HTTPRequestConcludingAsyncReader(
iterator: stream.makeAsyncIterator(),
readerState: .init()
)
let requestReader = HTTPRequestConcludingAsyncReader(
iterator: stream.makeAsyncIterator(),
readerState: .init()
)

_ = try await requestReader.consumeAndConclude { bodyReader in
var bodyReader = bodyReader
_ = await requestReader.consumeAndConclude { bodyReader in
var bodyReader = bodyReader

try await bodyReader.read { element in
throw TestError.errorWhileReading
// Check that the read error is propagated
await #expect(throws: TestError.errorWhileReading) {
do {
try await bodyReader.read(maximumCount: nil) { (element) throws(TestError) in
throw TestError.errorWhileReading
}
} catch let eitherError as EitherError<Error, TestError> {
try eitherError.unwrap()
}
}
}
Expand All @@ -166,19 +156,19 @@ struct HTTPRequestConcludingAsyncReaderTests {
@available(macOS 26.0, iOS 26.0, watchOS 26.0, tvOS 26.0, visionOS 26.0, *)
@Test("More bytes available than consumption limit")
func testCollectMoreBytesThanAvailable() async throws {
let (stream, source) = NIOAsyncChannelInboundStream<HTTPRequestPart>.makeTestingStream()
await #expect(processExitsWith: .failure) {
let (stream, source) = NIOAsyncChannelInboundStream<HTTPRequestPart>.makeTestingStream()

// Write 10 bytes
source.yield(.body(.init(repeating: 5, count: 10)))
source.finish()
// Write 10 bytes
source.yield(.body(.init(repeating: 5, count: 10)))
source.finish()

let requestReader = HTTPRequestConcludingAsyncReader(iterator: stream.makeAsyncIterator(), readerState: .init())
let requestReader = HTTPRequestConcludingAsyncReader(iterator: stream.makeAsyncIterator(), readerState: .init())

_ = try await requestReader.consumeAndConclude { requestBodyReader in
var requestBodyReader = requestBodyReader
_ = try await requestReader.consumeAndConclude { requestBodyReader in
var requestBodyReader = requestBodyReader

// Attempting to collect a maximum of 9 bytes should result in a LimitExceeded error.
await #expect(throws: LimitExceeded.self) {
// Since there are more bytes than requested, this should fail.
try await requestBodyReader.collect(upTo: 9) { element in
()
}
Expand Down
30 changes: 16 additions & 14 deletions Tests/HTTPServerTests/HTTPResponseConcludingAsyncWriterTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import Testing

@Suite
struct HTTPResponseConcludingAsyncWriterTests {
let bodySampleOne: [UInt8] = [1, 2]
let bodySampleTwo: [UInt8] = [3, 4]
let bodySampleOne: UInt8 = 1
let bodySampleTwo: UInt8 = 2

let trailerSampleOne: HTTPFields = [.serverTiming: "test"]
let trailerSampleTwo: HTTPFields = [.serverTiming: "test", .cookie: "cookie"]

Expand All @@ -17,13 +18,14 @@ struct HTTPResponseConcludingAsyncWriterTests {
let (writer, sink) = NIOAsyncChannelOutboundWriter<HTTPResponsePart>.makeTestingWriter()
let responseWriter = HTTPResponseConcludingAsyncWriter(writer: writer, writerState: .init())

try await responseWriter.writeAndConclude(element: self.bodySampleOne.span, finalElement: self.trailerSampleOne)
try await responseWriter.writeAndConclude(self.bodySampleOne, finalElement: self.trailerSampleOne)

// Now read the response
var responseIterator = sink.makeAsyncIterator()

let element = try #require(await responseIterator.next())
#expect(element == .body(.init(bytes: self.bodySampleOne)))
#expect(element == .body(.init(bytes: [self.bodySampleOne])))

let trailer = try #require(await responseIterator.next())
#expect(trailer == .end(self.trailerSampleOne))
}
Expand All @@ -38,8 +40,8 @@ struct HTTPResponseConcludingAsyncWriterTests {
var bodyWriter = bodyWriter

// Write multiple elements
try await bodyWriter.write(self.bodySampleOne.span)
try await bodyWriter.write(self.bodySampleTwo.span)
try await bodyWriter.write(self.bodySampleOne)
try await bodyWriter.write(self.bodySampleTwo)

return self.trailerSampleOne
}
Expand All @@ -48,8 +50,8 @@ struct HTTPResponseConcludingAsyncWriterTests {

let firstElement = try #require(await responseIterator.next())
let secondElement = try #require(await responseIterator.next())
#expect(firstElement == .body(.init(bytes: self.bodySampleOne)))
#expect(secondElement == .body(.init(bytes: self.bodySampleTwo)))
#expect(firstElement == .body(.init(bytes: [self.bodySampleOne])))
#expect(secondElement == .body(.init(bytes: [self.bodySampleTwo])))

let trailer = try #require(await responseIterator.next())
#expect(trailer == .end(self.trailerSampleOne))
Expand All @@ -67,7 +69,7 @@ struct HTTPResponseConcludingAsyncWriterTests {
var bodyWriter = bodyWriter

// Write an element
try await bodyWriter.write(self.bodySampleOne.span)
try await bodyWriter.write(self.bodySampleOne)
// Then throw
throw TestError.errorWhileWriting
}
Expand All @@ -76,7 +78,7 @@ struct HTTPResponseConcludingAsyncWriterTests {
var responseIterator = sink.makeAsyncIterator()

let firstElement = try #require(await responseIterator.next())
#expect(firstElement == .body(.init(bytes: self.bodySampleOne)))
#expect(firstElement == .body(.init(bytes: [self.bodySampleOne])))
}

@Test("Write multiple elements and multiple trailers")
Expand All @@ -89,8 +91,8 @@ struct HTTPResponseConcludingAsyncWriterTests {
var bodyWriter = bodyWriter

// Write multiple elements
try await bodyWriter.write(self.bodySampleOne.span)
try await bodyWriter.write(self.bodySampleTwo.span)
try await bodyWriter.write(self.bodySampleOne)
try await bodyWriter.write(self.bodySampleTwo)

return self.trailerSampleTwo
}
Expand All @@ -99,8 +101,8 @@ struct HTTPResponseConcludingAsyncWriterTests {

let firstElement = try #require(await responseIterator.next())
let secondElement = try #require(await responseIterator.next())
#expect(firstElement == .body(.init(bytes: self.bodySampleOne)))
#expect(secondElement == .body(.init(bytes: self.bodySampleTwo)))
#expect(firstElement == .body(.init(bytes: [self.bodySampleOne])))
#expect(secondElement == .body(.init(bytes: [self.bodySampleTwo])))

let trailer = try #require(await responseIterator.next())
#expect(trailer == .end(self.trailerSampleTwo))
Expand Down
2 changes: 1 addition & 1 deletion Tests/HTTPServerTests/NIOHTTPServerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ struct NIOHTTPServerTests {
#expect(request.path == "/")

var buffer = ByteBuffer()
let finalElement = try await reader.consumeAndConclude { bodyReader in
let (_, finalElement) = try await reader.consumeAndConclude { bodyReader in
var bodyReader = bodyReader
return try await bodyReader.collect(upTo: Self.bodyData.readableBytes + 1) { body in
buffer.writeBytes(body.bytes)
Expand Down
Loading