Skip to content
Open
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
34 changes: 34 additions & 0 deletions Sources/NIOHTTPServer/NIOHTTPServerResponseSender.swift
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,40 @@ extension NIOHTTPServer.ResponseSender {
try await self.writer.write(.body(ByteBuffer(draining: &buffer)))
}

/// Writes a chunk of the response body from borrowed memory.
///
/// - Parameter bytes: The body bytes to write. Only borrowed for the duration of the call.
public mutating func write(_ bytes: RawSpan) async throws(WriteFailure) {
var byteBuffer = ByteBuffer()
byteBuffer.reserveCapacity(bytes.byteCount)
byteBuffer.writeBytes(bytes)
Comment on lines +127 to +129

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The byte buffer allocation here makes this path really sad.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It needs to be copied into a ByteBuffer at some point because that's what the channel handler takes. The HTTP server needs to own the data because of the difference of when NIO returns and when the data actually hits the socket (IIRC this is a documented part of the channel handler). This APIs here at least stops you going from Data/Array/ByteBuffer/whatever type you want, into a unique array back into ByteBuffer in the channel handler

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only reason we need this API is because ByteBuffer does not conform to RangeReplacableContainer nor do the span types.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'd still have a copy somewhere, unless it was ByteBuffer through the whole stack right? (Assuming RangeReplaceableContainer). Because that's what NIO talks in the channel handler/socket so it needs a copy to convert it in. I don't think ByteBuffer has any mechanism to borrow yet


try await self.writer.write(.body(byteBuffer))
}

/// Writes a final chunk of the response body from borrowed memory and completes the response.
///
/// The borrowing counterpart to ``finish(buffer:finalElement:)``
///
/// - Parameters:
/// - bytes: The final body bytes. Only borrowed for the duration of the call. May be empty,
/// in which case no body part is written.
/// - finalElement: Trailing header fields to send with the end of the response, if any.
public consuming func finish(
bytes: RawSpan,
finalElement: consuming HTTPFields?
) async throws(WriteFailure) {
if bytes.byteCount > 0 {
var byteBuffer = ByteBuffer()
byteBuffer.reserveCapacity(bytes.byteCount)
byteBuffer.writeBytes(bytes)

try await self.writer.write(.body(byteBuffer))
}
try await self.writer.write(.end(finalElement))
self.writerState.wrapped.withLock { $0.finishedWriting = true }
}

public consuming func finish(
buffer: inout some RangeReplaceableContainer<UInt8> & ~Copyable,
finalElement: consuming HTTPFields?
Expand Down
Loading