Skip to content

Commit

Permalink
ResponseBodyWriter.write is no longer mutating
Browse files Browse the repository at this point in the history
Also use sending in swift 6 to pass ResponseBodyWriter into function so it can be used in a sending closure
  • Loading branch information
adam-fowler committed Dec 5, 2024
1 parent a25af21 commit 863996b
Show file tree
Hide file tree
Showing 7 changed files with 45 additions and 28 deletions.
24 changes: 18 additions & 6 deletions Sources/HummingbirdCore/Response/ResponseBody.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,14 @@ import NIOCore

/// Response body
public struct ResponseBody: Sendable {
#if compiler(>=6.0)
public typealias WriteFunction = @Sendable (sending any ResponseBodyWriter) async throws -> Void
#else
public typealias WriteFunction = @Sendable (any ResponseBodyWriter) async throws -> Void
#endif

@usableFromInline
let _write: @Sendable (inout any ResponseBodyWriter) async throws -> Void
let _write: WriteFunction
public let contentLength: Int?

/// Initialise ResponseBody with closure writing body contents.
Expand All @@ -36,9 +42,9 @@ public struct ResponseBody: Sendable {
/// - Parameters:
/// - contentLength: Optional length of body
/// - write: closure provided with `writer` type that can be used to write to response body
public init(contentLength: Int? = nil, _ write: @Sendable @escaping (inout any ResponseBodyWriter) async throws -> Void) {
public init(contentLength: Int? = nil, _ write: @escaping WriteFunction) {
self._write = { writer in
try await write(&writer)
try await write(writer)
}
self.contentLength = contentLength
}
Expand Down Expand Up @@ -77,11 +83,17 @@ public struct ResponseBody: Sendable {
}
}

#if compiler(>=6.0)
@inlinable
public consuming func write(_ writer: consuming any ResponseBodyWriter) async throws {
try await self._write(&writer)
public consuming func write(_ writer: sending any ResponseBodyWriter) async throws {
try await self._write(writer)
}

#else
@inlinable
public consuming func write(_ writer: any ResponseBodyWriter) async throws {
try await self._write(writer)
}
#endif
/// Returns a ResponseBody containing the results of mapping the given closure over the sequence of
/// ByteBuffers written.
/// - Parameter transform: A mapping closure applied to every ByteBuffer in ResponseBody
Expand Down
14 changes: 7 additions & 7 deletions Sources/HummingbirdCore/Response/ResponseBodyWriter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ import NIOCore
public protocol ResponseBodyWriter {
/// Write a single ByteBuffer
/// - Parameter buffer: single buffer to write
mutating func write(_ buffer: ByteBuffer) async throws
func write(_ buffer: ByteBuffer) async throws
/// Write a sequence of ByteBuffers
/// - Parameter buffers: Sequence of buffers
mutating func write(contentsOf buffers: some Sequence<ByteBuffer>) async throws
func write(contentsOf buffers: some Sequence<ByteBuffer>) async throws
/// Finish writing body
/// - Parameter trailingHeaders: Any trailing headers you want to include at end
consuming func finish(_ trailingHeaders: HTTPFields?) async throws
Expand All @@ -31,7 +31,7 @@ public protocol ResponseBodyWriter {
extension ResponseBodyWriter {
/// Default implementation of writing a sequence of ByteBuffers
@inlinable
public mutating func write(contentsOf buffers: some Sequence<ByteBuffer>) async throws {
public func write(contentsOf buffers: some Sequence<ByteBuffer>) async throws {
for part in buffers {
try await self.write(part)
}
Expand All @@ -40,26 +40,26 @@ extension ResponseBodyWriter {
/// Write AsyncSequence of ByteBuffers
/// - Parameter buffers: ByteBuffer AsyncSequence
@inlinable
public mutating func write<BufferSequence: AsyncSequence>(_ buffers: BufferSequence) async throws where BufferSequence.Element == ByteBuffer {
public func write<BufferSequence: AsyncSequence>(_ buffers: BufferSequence) async throws where BufferSequence.Element == ByteBuffer {
for try await buffer in buffers {
try await self.write(buffer)
}
}
}

struct MappedResponseBodyWriter<ParentWriter: ResponseBodyWriter>: ResponseBodyWriter {
fileprivate var parentWriter: ParentWriter
fileprivate let parentWriter: ParentWriter
fileprivate let transform: @Sendable (ByteBuffer) async throws -> ByteBuffer

/// Write a single ByteBuffer
/// - Parameter buffer: single buffer to write
mutating func write(_ buffer: ByteBuffer) async throws {
func write(_ buffer: ByteBuffer) async throws {
try await self.parentWriter.write(self.transform(buffer))
}

/// Write a sequence of ByteBuffers
/// - Parameter buffers: Sequence of buffers
mutating func write(contentsOf buffers: some Sequence<ByteBuffer>) async throws {
func write(contentsOf buffers: some Sequence<ByteBuffer>) async throws {
for part in buffers {
try await self.parentWriter.write(self.transform(part))
}
Expand Down
14 changes: 7 additions & 7 deletions Tests/HummingbirdCoreTests/CoreTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ final class HummingBirdCoreTests: XCTestCase {
@Sendable func helloResponder(to request: Request, responseWriter: consuming ResponseWriter, channel: Channel) async throws {
try? await Task.sleep(for: .milliseconds(10))
let responseBody = channel.allocator.buffer(string: "Hello")
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(responseBody)
try await bodyWriter.finish(nil)
}
Expand Down Expand Up @@ -171,7 +171,7 @@ final class HummingBirdCoreTests: XCTestCase {
try await responseWriter.writeResponse(.init(status: .contentTooLarge))
return
}
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(buffer)
try await bodyWriter.finish(nil)
},
Expand All @@ -191,7 +191,7 @@ final class HummingBirdCoreTests: XCTestCase {
try await testServer(
responder: { (_, responseWriter: consuming ResponseWriter, _) in
let buffer = Self.randomBuffer(size: 1_140_000)
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(buffer)
try await bodyWriter.finish(nil)
},
Expand All @@ -209,7 +209,7 @@ final class HummingBirdCoreTests: XCTestCase {
func testStreamBody() async throws {
try await testServer(
responder: { (request, responseWriter: consuming ResponseWriter, _) in
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(request.body)
try await bodyWriter.finish(nil)
},
Expand All @@ -228,7 +228,7 @@ final class HummingBirdCoreTests: XCTestCase {
func testStreamBodyWriteSlow() async throws {
try await testServer(
responder: { (request, responseWriter: consuming ResponseWriter, _) in
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(request.body.delayed())
try await bodyWriter.finish(nil)
},
Expand Down Expand Up @@ -259,7 +259,7 @@ final class HummingBirdCoreTests: XCTestCase {
}
try await testServer(
responder: { (request, responseWriter: consuming ResponseWriter, _) in
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(request.body.delayed())
try await bodyWriter.finish(nil)
},
Expand Down Expand Up @@ -515,7 +515,7 @@ final class HummingBirdCoreTests: XCTestCase {
) { (request, responseWriter: consuming ResponseWriter, _) in
await handlerPromise.complete(())
try? await Task.sleep(for: .milliseconds(500))
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(request.body.delayed())
try await bodyWriter.finish(nil)
} onServerRunning: {
Expand Down
2 changes: 1 addition & 1 deletion Tests/HummingbirdCoreTests/TestUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public enum TestErrors: Error {
/// Basic responder that just returns "Hello" in body
@Sendable public func helloResponder(to request: Request, responseWriter: consuming ResponseWriter, channel: Channel) async throws {
let responseBody = channel.allocator.buffer(string: "Hello")
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(responseBody)
try await bodyWriter.finish(nil)
}
Expand Down
2 changes: 1 addition & 1 deletion Tests/HummingbirdHTTP2Tests/TestUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public enum TestErrors: Error {
/// Basic responder that just returns "Hello" in body
@Sendable public func helloResponder(to request: Request, responseWriter: consuming ResponseWriter, channel: Channel) async throws {
let responseBody = channel.allocator.buffer(string: "Hello")
var bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
let bodyWriter = try await responseWriter.writeHead(.init(status: .ok))
try await bodyWriter.write(responseBody)
try await bodyWriter.finish(nil)
}
Expand Down
9 changes: 7 additions & 2 deletions Tests/HummingbirdRouterTests/MiddlewareTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,15 @@ final class MiddlewareTests: XCTestCase {
}

func testMiddlewareResponseBodyWriter() async throws {
struct TransformWriter: ResponseBodyWriter {
// Test non-sendable ResponseBodyWriter
final class TransformWriter: ResponseBodyWriter {
var parentWriter: any ResponseBodyWriter

mutating func write(_ buffer: ByteBuffer) async throws {
init(parentWriter: any ResponseBodyWriter) {
self.parentWriter = parentWriter
}

func write(_ buffer: ByteBuffer) async throws {
let output = ByteBuffer(bytes: buffer.readableBytesView.map { $0 ^ 255 })
try await self.parentWriter.write(output)
}
Expand Down
8 changes: 4 additions & 4 deletions Tests/HummingbirdTests/MiddlewareTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,9 @@ final class MiddlewareTests: XCTestCase {

func testMiddlewareResponseBodyWriter() async throws {
struct TransformWriter: ResponseBodyWriter {
var parentWriter: any ResponseBodyWriter
let parentWriter: any ResponseBodyWriter

mutating func write(_ buffer: ByteBuffer) async throws {
func write(_ buffer: ByteBuffer) async throws {
let output = ByteBuffer(bytes: buffer.readableBytesView.map { $0 ^ 255 })
try await self.parentWriter.write(output)
}
Expand Down Expand Up @@ -187,9 +187,9 @@ final class MiddlewareTests: XCTestCase {

func testMappedResponseBodyWriter() async throws {
struct TransformWriter: ResponseBodyWriter {
var parentWriter: any ResponseBodyWriter
let parentWriter: any ResponseBodyWriter

mutating func write(_ buffer: ByteBuffer) async throws {
func write(_ buffer: ByteBuffer) async throws {
let output = ByteBuffer(bytes: buffer.readableBytesView.map { $0 ^ 255 })
try await self.parentWriter.write(output)
}
Expand Down

0 comments on commit 863996b

Please sign in to comment.