Skip to content

Commit ce76c21

Browse files
authored
Miscellaneous documentation, tests, helper functions (#534)
* Documentation fixes * Remove unnecessary @preconcurrency * Add ResponseBody(contentsOf:) * Add test for ResponseBody(contentsOf:) * Add test for reading two file on one connection
1 parent 39362b4 commit ce76c21

File tree

6 files changed

+73
-21
lines changed

6 files changed

+73
-21
lines changed

Sources/Hummingbird/Codable/URLEncodedForm/URLEncodedForm.swift

-4
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,7 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
#if os(Linux)
16-
@preconcurrency import Foundation
17-
#else
1815
import Foundation
19-
#endif
2016

2117
internal enum URLEncodedForm {
2218
/// CodingKey used by URLEncodedFormEncoder and URLEncodedFormDecoder

Sources/Hummingbird/Configuration.swift

+7-7
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public struct ApplicationConfiguration: Sendable {
3333
public var backlog: Int
3434
/// Allows socket to be bound to an address that is already in use.
3535
public var reuseAddress: Bool
36-
/// Object deciding on when we should accept new connection. Use ``MaximumAvailableConnections``
36+
/// Object deciding on when we should accept new connection. Use ``HummingbirdCore/MaximumAvailableConnections``
3737
/// to set the maximum allowed connections.
3838
public var availableConnectionsDelegate: AvailableConnectionsDelegate?
3939
#if canImport(Network)
@@ -48,11 +48,11 @@ public struct ApplicationConfiguration: Sendable {
4848
/// - Parameters:
4949
/// - address: Bind address for server
5050
/// - serverName: Server name to return in "server" header
51-
/// - backlog: the maximum length for the queue of pending connections. If a connection request arrives with the queue full,
52-
/// the client may receive an error with an indication of ECONNREFUSE
51+
/// - backlog: the maximum length for the queue of pending connections. If a connection request
52+
/// arrives with the queue full, the client may receive an error with an indication of ECONNREFUSE
5353
/// - reuseAddress: Allows socket to be bound to an address that is already in use.
54-
/// - availableConnectionsDelegate: Object deciding on when we should accept new connection. Use ``MaximumAvailableConnections``
55-
/// to set the maximum allowed connections.
54+
/// - availableConnectionsDelegate: Object deciding on when we should accept new connection. Use
55+
/// ``HummingbirdCore/MaximumAvailableConnections`` to set the maximum allowed connections.
5656
public init(
5757
address: BindAddress = .hostname(),
5858
serverName: String? = nil,
@@ -77,8 +77,8 @@ public struct ApplicationConfiguration: Sendable {
7777
/// - address: Bind address for server
7878
/// - serverName: Server name to return in "server" header
7979
/// - reuseAddress: Allows socket to be bound to an address that is already in use.
80-
/// - availableConnectionsDelegate: Object deciding on when we should accept new connection. Use ``MaximumAvailableConnections``
81-
/// to set the maximum allowed connections.
80+
/// - availableConnectionsDelegate: Object deciding on when we should accept new connection. Use
81+
/// ``HummingbirdCore/MaximumAvailableConnections`` to set the maximum allowed connections.
8282
/// - tlsOptions: TLS options for when you are using NIOTransportServices
8383
public init(
8484
address: BindAddress = .hostname(),

Sources/HummingbirdCore/Response/ResponseBody.swift

+15-7
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,14 @@ public struct ResponseBody: Sendable {
2323

2424
/// Initialise ResponseBody with closure writing body contents.
2525
///
26-
/// When you have finished writing the response body you need to indicate you
27-
/// have finished by calling ``ResponseBodyWriter.finish``. At this point you can also
28-
/// send trailing headers by including them as a parameter in the finsh() call.
26+
/// When you have finished writing the response body you need to indicate you have
27+
/// finished by calling ``ResponseBodyWriter/finish(_:)``. At this
28+
/// point you can also send trailing headers by including them as a parameter in
29+
/// the finsh() call.
2930
/// ```
3031
/// let responseBody = ResponseBody(contentLength: contentLength) { writer in
3132
/// try await writer.write(buffer)
32-
/// try await writer.finish()
33+
/// try await writer.finish(nil)
3334
/// }
3435
/// ```
3536
/// - Parameters:
@@ -58,13 +59,20 @@ public struct ResponseBody: Sendable {
5859
}
5960
}
6061

62+
/// Initialise ResponseBody that contains a sequence of ByteBuffers
63+
/// - Parameter byteBuffers: Sequence of ByteBuffers to write
64+
public init<BufferSequence: Sequence & Sendable>(contentsOf byteBuffers: BufferSequence) where BufferSequence.Element == ByteBuffer {
65+
self.init(contentLength: byteBuffers.map { $0.readableBytes }.reduce(0, +)) { writer in
66+
try await writer.write(contentsOf: byteBuffers)
67+
try await writer.finish(nil)
68+
}
69+
}
70+
6171
/// Initialise ResponseBody with an AsyncSequence of ByteBuffers
6272
/// - Parameter asyncSequence: ByteBuffer AsyncSequence
6373
public init<BufferSequence: AsyncSequence & Sendable>(asyncSequence: BufferSequence) where BufferSequence.Element == ByteBuffer {
6474
self.init { writer in
65-
for try await buffer in asyncSequence {
66-
try await writer.write(buffer)
67-
}
75+
try await writer.write(asyncSequence)
6876
try await writer.finish(nil)
6977
}
7078
}

Tests/HummingbirdTests/ApplicationTests.swift

+23
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,29 @@ final class ApplicationTests: XCTestCase {
249249
}
250250
}
251251

252+
func testResponseBodySequence() async throws {
253+
let router = Router()
254+
router
255+
.group("/echo-body")
256+
.post { request, _ -> Response in
257+
var buffers: [ByteBuffer] = []
258+
for try await buffer in request.body {
259+
buffers.append(buffer)
260+
}
261+
return .init(status: .ok, headers: [:], body: .init(contentsOf: buffers))
262+
}
263+
let app = Application(responder: router.buildResponder())
264+
try await app.test(.router) { client in
265+
266+
let buffer = Self.randomBuffer(size: 400_000)
267+
try await client.execute(uri: "/echo-body", method: .post, body: buffer) { response in
268+
XCTAssertEqual(response.status, .ok)
269+
XCTAssertEqual(response.headers[.contentLength], "400000")
270+
XCTAssertEqual(response.body, buffer)
271+
}
272+
}
273+
}
274+
252275
/// Test streaming of requests and streaming of responses by streaming the request body into a response streamer
253276
func testStreaming() async throws {
254277
let router = Router()

Tests/HummingbirdTests/FileIOTests.swift

+27-2
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ class FileIOTests: XCTestCase {
2727
let router = Router()
2828
router.get("test.jpg") { _, context -> Response in
2929
let fileIO = FileIO(threadPool: .singleton)
30-
let body = try await fileIO.loadFile(path: "test.jpg", context: context)
30+
let body = try await fileIO.loadFile(path: "testReadFileIO.jpg", context: context)
3131
return .init(status: .ok, headers: [:], body: body)
3232
}
3333
let buffer = Self.randomBuffer(size: 320_003)
3434
let data = Data(buffer: buffer)
35-
let fileURL = URL(fileURLWithPath: "test.jpg")
35+
let fileURL = URL(fileURLWithPath: "testReadFileIO.jpg")
3636
XCTAssertNoThrow(try data.write(to: fileURL))
3737
defer { XCTAssertNoThrow(try FileManager.default.removeItem(at: fileURL)) }
3838

@@ -45,6 +45,31 @@ class FileIOTests: XCTestCase {
4545
}
4646
}
4747

48+
func testReadMultipleFilesOnSameConnection() async throws {
49+
let router = Router()
50+
router.get("test.jpg") { _, context -> Response in
51+
let fileIO = FileIO(threadPool: .singleton)
52+
let body = try await fileIO.loadFile(path: "testReadMultipleFilesOnSameConnection.jpg", context: context)
53+
return .init(status: .ok, headers: [:], body: body)
54+
}
55+
let buffer = Self.randomBuffer(size: 54003)
56+
let data = Data(buffer: buffer)
57+
let fileURL = URL(fileURLWithPath: "testReadMultipleFilesOnSameConnection.jpg")
58+
XCTAssertNoThrow(try data.write(to: fileURL))
59+
defer { XCTAssertNoThrow(try FileManager.default.removeItem(at: fileURL)) }
60+
61+
let app = Application(responder: router.buildResponder())
62+
63+
try await app.test(.live) { client in
64+
try await client.execute(uri: "/test.jpg", method: .get) { response in
65+
XCTAssertEqual(response.body, buffer)
66+
}
67+
try await client.execute(uri: "/test.jpg", method: .get) { response in
68+
XCTAssertEqual(response.body, buffer)
69+
}
70+
}
71+
}
72+
4873
func testWrite() async throws {
4974
let filename = "testWrite.txt"
5075
let router = Router()

Tests/HummingbirdTests/MetricsTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
import Hummingbird
1616
import HummingbirdTesting
17-
@preconcurrency import Metrics
17+
import Metrics
1818
import NIOConcurrencyHelpers
1919
import XCTest
2020

0 commit comments

Comments
 (0)