From feaa75bfc9266f1a780b4e918ecc3324a4986e47 Mon Sep 17 00:00:00 2001 From: Adam Fowler Date: Tue, 21 Nov 2023 14:25:51 +0000 Subject: [PATCH] Add HBBaseRequestContext protocol (#287) * Add HBBaseRequestContext protocol That doesn't require an init using a NIO Channel * Use some * Use some HBBaseRequestContext in request handler tests --- Sources/Hummingbird/Application.swift | 41 ++++++--------- .../Codable/CodableProtocols.swift | 8 +-- .../Codable/RequestDecodable.swift | 2 +- .../Codable/ResponseEncodable.swift | 6 +-- .../Middleware/CORSMiddleware.swift | 2 +- .../Middleware/LogRequestMiddleware.swift | 2 +- .../Middleware/MetricsMiddleware.swift | 2 +- .../Hummingbird/Middleware/Middleware.swift | 4 +- .../Middleware/MiddlewareGroup.swift | 2 +- .../Middleware/TracingMiddleware.swift | 4 +- .../Router/EndpointResponder.swift | 2 +- .../Router/ResponseGenerator.swift | 16 +++--- Sources/Hummingbird/Router/RouteHandler.swift | 4 +- Sources/Hummingbird/Router/Router.swift | 2 +- .../Hummingbird/Router/RouterBuilder.swift | 6 +-- Sources/Hummingbird/Router/RouterGroup.swift | 2 +- .../Hummingbird/Router/RouterMethods.swift | 2 +- Sources/Hummingbird/Server/Request.swift | 2 +- .../Hummingbird/Server/RequestContext.swift | 46 ++++++----------- Sources/Hummingbird/Server/Responder.swift | 4 +- .../Codable/JSON/JSONCoding.swift | 4 +- .../URLEncodedForm+Request.swift | 4 +- .../HummingbirdFoundation/Files/FileIO.swift | 10 ++-- .../Files/FileMiddleware.swift | 2 +- Sources/HummingbirdXCT/Application+XCT.swift | 4 +- Sources/HummingbirdXCT/HBXCTLive.swift | 2 +- Sources/HummingbirdXCT/HBXCTRouter.swift | 50 ++++++++++++++----- Sources/PerformanceTest/main.swift | 19 +------ .../FilesTests.swift | 2 +- Tests/HummingbirdTests/ApplicationTests.swift | 8 +-- Tests/HummingbirdTests/HandlerTests.swift | 18 +++---- Tests/HummingbirdTests/MiddlewareTests.swift | 12 ++--- Tests/HummingbirdTests/RouterTests.swift | 22 ++++---- Tests/HummingbirdTests/TracingTests.swift | 2 +- 34 files changed, 154 insertions(+), 164 deletions(-) diff --git a/Sources/Hummingbird/Application.swift b/Sources/Hummingbird/Application.swift index c41e450c3..88c59b6c4 100644 --- a/Sources/Hummingbird/Application.swift +++ b/Sources/Hummingbird/Application.swift @@ -141,22 +141,14 @@ public struct HBApplication Logger { + let requestId = globalRequestID.loadThenWrappingIncrement(by: 1, ordering: .relaxed) + return logger.with(metadataKey: "hb_id", value: .stringConvertible(requestId)) } } /// Conform to `Service` from `ServiceLifecycle`. -extension HBApplication: Service { +extension HBApplication: Service where Responder.Context: HBRequestContext { public func run() async throws { let context = HBApplicationContext( threadPool: self.threadPool, @@ -173,7 +165,7 @@ extension HBApplication: Service { ) let context = Responder.Context( applicationContext: context, - source: ChannelContextSource(channel: channel), + channel: channel, logger: HBApplication.loggerWithRequestId(context.logger) ) // respond to request @@ -209,18 +201,17 @@ extension HBApplication: Service { } } - public static func loggerWithRequestId(_ logger: Logger) -> Logger { - let requestId = globalRequestID.loadThenWrappingIncrement(by: 1, ordering: .relaxed) - return logger.with(metadataKey: "hb_id", value: .stringConvertible(requestId)) - } - - /// Request Context Source from NIO Channel - public struct ChannelContextSource: RequestContextSource { - let channel: Channel - - public var eventLoop: EventLoop { self.channel.eventLoop } - public var allocator: ByteBufferAllocator { self.channel.allocator } - public var remoteAddress: SocketAddress? { self.channel.remoteAddress } + /// Helper function that runs application inside a ServiceGroup which will gracefully + /// shutdown on signals SIGINT, SIGTERM + public func runService(gracefulShutdownSignals: [UnixSignal] = [.sigterm, .sigint]) async throws { + let serviceGroup = ServiceGroup( + configuration: .init( + services: [self], + gracefulShutdownSignals: gracefulShutdownSignals, + logger: self.logger + ) + ) + try await serviceGroup.run() } } diff --git a/Sources/Hummingbird/Codable/CodableProtocols.swift b/Sources/Hummingbird/Codable/CodableProtocols.swift index 330bec715..2f4bc7354 100644 --- a/Sources/Hummingbird/Codable/CodableProtocols.swift +++ b/Sources/Hummingbird/Codable/CodableProtocols.swift @@ -19,7 +19,7 @@ public protocol HBResponseEncoder: Sendable { /// - Parameters: /// - value: value to encode /// - request: request that generated this value - func encode(_ value: T, from request: HBRequest, context: HBRequestContext) throws -> HBResponse + func encode(_ value: some Encodable, from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse } /// protocol for decoder deserializing from a Request body @@ -28,12 +28,12 @@ public protocol HBRequestDecoder: Sendable { /// - Parameters: /// - type: type to decode to /// - request: request - func decode(_ type: T.Type, from request: HBRequest, context: HBRequestContext) throws -> T + func decode(_ type: T.Type, from request: HBRequest, context: some HBBaseRequestContext) throws -> T } /// Default encoder. Outputs request with the swift string description of object struct NullEncoder: HBResponseEncoder { - func encode(_ value: T, from request: HBRequest, context: HBRequestContext) throws -> HBResponse { + func encode(_ value: some Encodable, from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse { return HBResponse( status: .ok, headers: ["content-type": "text/plain; charset=utf-8"], @@ -44,7 +44,7 @@ struct NullEncoder: HBResponseEncoder { /// Default decoder. there is no default decoder path so this generates an error struct NullDecoder: HBRequestDecoder { - func decode(_ type: T.Type, from request: HBRequest, context: HBRequestContext) throws -> T { + func decode(_ type: T.Type, from request: HBRequest, context: some HBBaseRequestContext) throws -> T { preconditionFailure("HBApplication.decoder has not been set") } } diff --git a/Sources/Hummingbird/Codable/RequestDecodable.swift b/Sources/Hummingbird/Codable/RequestDecodable.swift index bee2a5e5a..502a3824f 100644 --- a/Sources/Hummingbird/Codable/RequestDecodable.swift +++ b/Sources/Hummingbird/Codable/RequestDecodable.swift @@ -33,7 +33,7 @@ extension HBRequestDecodable { /// Create using `Codable` interfaces /// - Parameter request: request /// - Throws: HBHTTPError - public init(from request: HBRequest, context: HBRequestContext) throws { + public init(from request: HBRequest, context: some HBBaseRequestContext) throws { self = try request.decode(as: Self.self, using: context) } } diff --git a/Sources/Hummingbird/Codable/ResponseEncodable.swift b/Sources/Hummingbird/Codable/ResponseEncodable.swift index 2d49dc9f0..f91d81d3d 100644 --- a/Sources/Hummingbird/Codable/ResponseEncodable.swift +++ b/Sources/Hummingbird/Codable/ResponseEncodable.swift @@ -23,7 +23,7 @@ public protocol HBResponseCodable: HBResponseEncodable, Decodable {} /// Extend ResponseEncodable to conform to ResponseGenerator extension HBResponseEncodable { - public func response(from request: HBRequest, context: HBRequestContext) throws -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse { return try context.applicationContext.encoder.encode(self, from: request, context: context) } } @@ -33,7 +33,7 @@ extension Array: HBResponseGenerator where Element: Encodable {} /// Extend Array to conform to HBResponseEncodable extension Array: HBResponseEncodable where Element: Encodable { - public func response(from request: HBRequest, context: HBRequestContext) throws -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse { return try context.applicationContext.encoder.encode(self, from: request, context: context) } } @@ -43,7 +43,7 @@ extension Dictionary: HBResponseGenerator where Key: Encodable, Value: Encodable /// Extend Array to conform to HBResponseEncodable extension Dictionary: HBResponseEncodable where Key: Encodable, Value: Encodable { - public func response(from request: HBRequest, context: HBRequestContext) throws -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse { return try context.applicationContext.encoder.encode(self, from: request, context: context) } } diff --git a/Sources/Hummingbird/Middleware/CORSMiddleware.swift b/Sources/Hummingbird/Middleware/CORSMiddleware.swift index b501b5ec6..e17b591e8 100644 --- a/Sources/Hummingbird/Middleware/CORSMiddleware.swift +++ b/Sources/Hummingbird/Middleware/CORSMiddleware.swift @@ -20,7 +20,7 @@ import NIOCore /// then return an empty body with all the standard CORS headers otherwise send /// request onto the next handler and when you receive the response add a /// "access-control-allow-origin" header -public struct HBCORSMiddleware: HBMiddleware { +public struct HBCORSMiddleware: HBMiddleware { /// Defines what origins are allowed public enum AllowOrigin: Sendable { case none diff --git a/Sources/Hummingbird/Middleware/LogRequestMiddleware.swift b/Sources/Hummingbird/Middleware/LogRequestMiddleware.swift index 873c11293..21392d0c6 100644 --- a/Sources/Hummingbird/Middleware/LogRequestMiddleware.swift +++ b/Sources/Hummingbird/Middleware/LogRequestMiddleware.swift @@ -15,7 +15,7 @@ import Logging /// Middleware outputting to log for every call to server -public struct HBLogRequestsMiddleware: HBMiddleware { +public struct HBLogRequestsMiddleware: HBMiddleware { let logLevel: Logger.Level let includeHeaders: Bool diff --git a/Sources/Hummingbird/Middleware/MetricsMiddleware.swift b/Sources/Hummingbird/Middleware/MetricsMiddleware.swift index 720d8f83a..252ef14bc 100644 --- a/Sources/Hummingbird/Middleware/MetricsMiddleware.swift +++ b/Sources/Hummingbird/Middleware/MetricsMiddleware.swift @@ -19,7 +19,7 @@ import Metrics /// /// Records the number of requests, the request duration and how many errors were thrown. Each metric has additional /// dimensions URI and method. -public struct HBMetricsMiddleware: HBMiddleware { +public struct HBMetricsMiddleware: HBMiddleware { public init() {} public func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { diff --git a/Sources/Hummingbird/Middleware/Middleware.swift b/Sources/Hummingbird/Middleware/Middleware.swift index 550d0a9cf..f6e14ffbf 100644 --- a/Sources/Hummingbird/Middleware/Middleware.swift +++ b/Sources/Hummingbird/Middleware/Middleware.swift @@ -40,11 +40,11 @@ import NIOCore /// } /// ``` public protocol HBMiddleware: Sendable { - associatedtype Context: HBRequestContext + associatedtype Context func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse } -struct MiddlewareResponder: HBResponder { +struct MiddlewareResponder: HBResponder { let middleware: any HBMiddleware let next: any HBResponder diff --git a/Sources/Hummingbird/Middleware/MiddlewareGroup.swift b/Sources/Hummingbird/Middleware/MiddlewareGroup.swift index 06a8b0345..9bc5e8b3e 100644 --- a/Sources/Hummingbird/Middleware/MiddlewareGroup.swift +++ b/Sources/Hummingbird/Middleware/MiddlewareGroup.swift @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// /// Group of middleware that can be used to create a responder chain. Each middleware calls the next one -public final class HBMiddlewareGroup { +public final class HBMiddlewareGroup { var middlewares: [any HBMiddleware] /// Initialize `HBMiddlewareGroup` diff --git a/Sources/Hummingbird/Middleware/TracingMiddleware.swift b/Sources/Hummingbird/Middleware/TracingMiddleware.swift index 3616d2306..8d5262bf0 100644 --- a/Sources/Hummingbird/Middleware/TracingMiddleware.swift +++ b/Sources/Hummingbird/Middleware/TracingMiddleware.swift @@ -22,7 +22,7 @@ import Tracing /// You may opt in to recording a specific subset of HTTP request/response header values by passing /// a set of header names to ``init(recordingHeaders:)``. @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) -public struct HBTracingMiddleware: HBMiddleware { +public struct HBTracingMiddleware: HBMiddleware { private let headerNamesToRecord: Set /// Intialize a new HBTracingMiddleware. @@ -122,7 +122,7 @@ public struct HBTracingMiddleware: HBMiddleware { /// /// If you want the HBTracingMiddleware to record the remote address of requests /// then your request context will need to conform to this protocol -public protocol HBRemoteAddressRequestContext: HBRequestContext { +public protocol HBRemoteAddressRequestContext: HBBaseRequestContext { /// Connected host address var remoteAddress: SocketAddress? { get } } diff --git a/Sources/Hummingbird/Router/EndpointResponder.swift b/Sources/Hummingbird/Router/EndpointResponder.swift index d2085bc4d..2ce5c83c4 100644 --- a/Sources/Hummingbird/Router/EndpointResponder.swift +++ b/Sources/Hummingbird/Router/EndpointResponder.swift @@ -16,7 +16,7 @@ import NIOCore import NIOHTTP1 /// Stores endpoint responders for each HTTP method -struct HBEndpointResponders: Sendable { +struct HBEndpointResponders: Sendable { init(path: String) { self.path = path self.methods = [:] diff --git a/Sources/Hummingbird/Router/ResponseGenerator.swift b/Sources/Hummingbird/Router/ResponseGenerator.swift index 56b3adb71..7e9d5e68a 100644 --- a/Sources/Hummingbird/Router/ResponseGenerator.swift +++ b/Sources/Hummingbird/Router/ResponseGenerator.swift @@ -19,19 +19,19 @@ import NIOHTTP1 /// This is used by `Router` to convert handler return values into a `HBResponse`. public protocol HBResponseGenerator { /// Generate response based on the request this object came from - func response(from request: HBRequest, context: HBRequestContext) throws -> HBResponse + func response(from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse } /// Extend Response to conform to ResponseGenerator extension HBResponse: HBResponseGenerator { /// Return self as the response - public func response(from request: HBRequest, context: HBRequestContext) -> HBResponse { self } + public func response(from request: HBRequest, context: some HBBaseRequestContext) -> HBResponse { self } } /// Extend String to conform to ResponseGenerator extension String: HBResponseGenerator { /// Generate response holding string - public func response(from request: HBRequest, context: HBRequestContext) -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) -> HBResponse { let buffer = context.allocator.buffer(string: self) return HBResponse(status: .ok, headers: ["content-type": "text/plain; charset=utf-8"], body: .init(byteBuffer: buffer)) } @@ -40,7 +40,7 @@ extension String: HBResponseGenerator { /// Extend String to conform to ResponseGenerator extension Substring: HBResponseGenerator { /// Generate response holding string - public func response(from request: HBRequest, context: HBRequestContext) -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) -> HBResponse { let buffer = context.allocator.buffer(substring: self) return HBResponse(status: .ok, headers: ["content-type": "text/plain; charset=utf-8"], body: .init(byteBuffer: buffer)) } @@ -49,7 +49,7 @@ extension Substring: HBResponseGenerator { /// Extend ByteBuffer to conform to ResponseGenerator extension ByteBuffer: HBResponseGenerator { /// Generate response holding bytebuffer - public func response(from request: HBRequest, context: HBRequestContext) -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) -> HBResponse { HBResponse(status: .ok, headers: ["content-type": "application/octet-stream"], body: .init(byteBuffer: self)) } } @@ -57,14 +57,14 @@ extension ByteBuffer: HBResponseGenerator { /// Extend HTTPResponseStatus to conform to ResponseGenerator extension HTTPResponseStatus: HBResponseGenerator { /// Generate response with this response status code - public func response(from request: HBRequest, context: HBRequestContext) -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) -> HBResponse { HBResponse(status: self, headers: [:], body: .init()) } } /// Extend Optional to conform to HBResponseGenerator extension Optional: HBResponseGenerator where Wrapped: HBResponseGenerator { - public func response(from request: HBRequest, context: HBRequestContext) throws -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse { switch self { case .some(let wrapped): return try wrapped.response(from: request, context: context) @@ -89,7 +89,7 @@ public struct HBEditedResponse: HBResponseGenera self.responseGenerator = response } - public func response(from request: HBRequest, context: HBRequestContext) throws -> HBResponse { + public func response(from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse { var response = try responseGenerator.response(from: request, context: context) if let status = self.status { response.status = status diff --git a/Sources/Hummingbird/Router/RouteHandler.swift b/Sources/Hummingbird/Router/RouteHandler.swift index 913f2eb38..f575605fb 100644 --- a/Sources/Hummingbird/Router/RouteHandler.swift +++ b/Sources/Hummingbird/Router/RouteHandler.swift @@ -39,8 +39,8 @@ /// ``` public protocol HBRouteHandler { associatedtype _Output - init(from: HBRequest, context: HBRequestContext) throws - func handle(request: HBRequest, context: HBRequestContext) async throws -> _Output + init(from: HBRequest, context: some HBBaseRequestContext) throws + func handle(request: HBRequest, context: some HBBaseRequestContext) async throws -> _Output } extension HBRouterMethods { diff --git a/Sources/Hummingbird/Router/Router.swift b/Sources/Hummingbird/Router/Router.swift index 6a7dd7799..4d90d7fe5 100644 --- a/Sources/Hummingbird/Router/Router.swift +++ b/Sources/Hummingbird/Router/Router.swift @@ -17,7 +17,7 @@ /// Conforms to `HBResponder` so need to provide its own implementation of /// `func apply(to request: Request) -> EventLoopFuture`. /// -struct HBRouter: HBResponder { +struct HBRouter: HBResponder { let trie: RouterPathTrie> let notFoundResponder: any HBResponder diff --git a/Sources/Hummingbird/Router/RouterBuilder.swift b/Sources/Hummingbird/Router/RouterBuilder.swift index e9e9786f1..e267117f3 100644 --- a/Sources/Hummingbird/Router/RouterBuilder.swift +++ b/Sources/Hummingbird/Router/RouterBuilder.swift @@ -45,7 +45,7 @@ import NIOHTTP1 /// Both of these match routes which start with "/user" and the next path segment being anything. /// The second version extracts the path segment out and adds it to `HBRequest.parameters` with the /// key "id". -public final class HBRouterBuilder: HBRouterMethods { +public final class HBRouterBuilder: HBRouterMethods { var trie: RouterPathTrieBuilder> public let middlewares: HBMiddlewareGroup @@ -83,7 +83,7 @@ public final class HBRouterBuilder: HBRouterMethods { self.add(path, method: method, responder: responder) return self } - + /// return new `RouterGroup` /// - Parameter path: prefix to add to paths inside the group public func group(_ path: String = "") -> HBRouterGroup { @@ -92,7 +92,7 @@ public final class HBRouterBuilder: HBRouterMethods { } /// Responder that return a not found error -struct NotFoundResponder: HBResponder { +struct NotFoundResponder: HBResponder { func respond(to request: HBRequest, context: Context) throws -> HBResponse { throw HBHTTPError(.notFound) } diff --git a/Sources/Hummingbird/Router/RouterGroup.swift b/Sources/Hummingbird/Router/RouterGroup.swift index 079b49a05..fbc9453ff 100644 --- a/Sources/Hummingbird/Router/RouterGroup.swift +++ b/Sources/Hummingbird/Router/RouterGroup.swift @@ -30,7 +30,7 @@ import NIOHTTP1 /// .put(":id", use: todoController.update) /// .delete(":id", use: todoController.delete) /// ``` -public struct HBRouterGroup: HBRouterMethods { +public struct HBRouterGroup: HBRouterMethods { let path: String let router: HBRouterBuilder let middlewares: HBMiddlewareGroup diff --git a/Sources/Hummingbird/Router/RouterMethods.swift b/Sources/Hummingbird/Router/RouterMethods.swift index 66015010f..e17da530a 100644 --- a/Sources/Hummingbird/Router/RouterMethods.swift +++ b/Sources/Hummingbird/Router/RouterMethods.swift @@ -28,7 +28,7 @@ public struct HBRouterMethodOptions: OptionSet, Sendable { /// Conform to `HBRouterMethods` to add standard router verb (get, post ...) methods public protocol HBRouterMethods { - associatedtype Context: HBRequestContext + associatedtype Context: HBBaseRequestContext /// Add path for async closure @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) diff --git a/Sources/Hummingbird/Server/Request.swift b/Sources/Hummingbird/Server/Request.swift index f7e177441..86f50be0c 100644 --- a/Sources/Hummingbird/Server/Request.swift +++ b/Sources/Hummingbird/Server/Request.swift @@ -56,7 +56,7 @@ public struct HBRequest: Sendable { /// Decode request using decoder stored at `HBApplication.decoder`. /// - Parameter type: Type you want to decode to - public func decode(as type: Type.Type, using context: HBRequestContext) throws -> Type { + public func decode(as type: Type.Type, using context: some HBBaseRequestContext) throws -> Type { do { return try context.applicationContext.decoder.decode(type, from: self, context: context) } catch DecodingError.dataCorrupted(_) { diff --git a/Sources/Hummingbird/Server/RequestContext.swift b/Sources/Hummingbird/Server/RequestContext.swift index 8f8c9f3cc..630c0ebe9 100644 --- a/Sources/Hummingbird/Server/RequestContext.swift +++ b/Sources/Hummingbird/Server/RequestContext.swift @@ -34,15 +34,6 @@ public struct EndpointPath: Sendable { private let _value: NIOLockedValueBox } -/// Protocol for source of requests. -/// -/// This could be the NIO Channel that created the HTTP request of a Lambda event -public protocol RequestContextSource { - var eventLoop: EventLoop { get } - var allocator: ByteBufferAllocator { get } - var remoteAddress: SocketAddress? { get } -} - /// Request context values required by Hummingbird itself. public struct HBCoreRequestContext: Sendable { /// Application context @@ -68,8 +59,8 @@ public struct HBCoreRequestContext: Sendable { public init( applicationContext: HBApplicationContext, eventLoop: EventLoop, - logger: Logger, - allocator: ByteBufferAllocator = .init() + allocator: ByteBufferAllocator, + logger: Logger ) { self.applicationContext = applicationContext self.eventLoop = eventLoop @@ -78,31 +69,16 @@ public struct HBCoreRequestContext: Sendable { self.endpointPath = .init() self.parameters = .init() } - - @inlinable - public init( - applicationContext: HBApplicationContext, - source: some RequestContextSource, - logger: Logger - ) { - self.init(applicationContext: applicationContext, eventLoop: source.eventLoop, logger: logger, allocator: source.allocator) - } } /// Protocol that all request contexts should conform to. Holds data associated with /// a request. Provides context for request processing -public protocol HBRequestContext: Sendable { +public protocol HBBaseRequestContext: Sendable { /// Core context var coreContext: HBCoreRequestContext { get set } - /// initialize an `HBRequestContext` - /// - Parameters: - /// - applicationContext: Context coming from Application - /// - channel: Channel that created request and context - /// - logger: Logger to use with request - init(applicationContext: HBApplicationContext, source: some RequestContextSource, logger: Logger) } -extension HBRequestContext { +extension HBBaseRequestContext { /// Application context @inlinable public var applicationContext: HBApplicationContext { coreContext.applicationContext } @@ -136,6 +112,16 @@ extension HBRequestContext { public var id: String { self.logger[metadataKey: "hb_id"]!.description } } +/// Protocol for a request context that can be created from a NIO Channel +public protocol HBRequestContext: HBBaseRequestContext { + /// initialize an `HBRequestContext` + /// - Parameters: + /// - applicationContext: Context coming from Application + /// - channel: Channel that created request and context + /// - logger: Logger to use with request + init(applicationContext: HBApplicationContext, channel: Channel, logger: Logger) +} + /// Implementation of a basic request context that supports everything the Hummingbird library needs public struct HBBasicRequestContext: HBRequestContext { /// core context @@ -148,9 +134,9 @@ public struct HBBasicRequestContext: HBRequestContext { /// - logger: Logger public init( applicationContext: HBApplicationContext, - source: some RequestContextSource, + channel: Channel, logger: Logger ) { - self.coreContext = .init(applicationContext: applicationContext, source: source, logger: logger) + self.coreContext = .init(applicationContext: applicationContext, eventLoop: channel.eventLoop, allocator: channel.allocator, logger: logger) } } diff --git a/Sources/Hummingbird/Server/Responder.swift b/Sources/Hummingbird/Server/Responder.swift index 3a7b53c3e..b9676a148 100644 --- a/Sources/Hummingbird/Server/Responder.swift +++ b/Sources/Hummingbird/Server/Responder.swift @@ -19,13 +19,13 @@ import ServiceContextModule /// /// This is the core protocol for Hummingbird. It defines an object that can respond to a request. public protocol HBResponder: Sendable { - associatedtype Context: HBRequestContext + associatedtype Context /// Return EventLoopFuture that will be fulfilled with response to the request supplied func respond(to request: HBRequest, context: Context) async throws -> HBResponse } /// Responder that calls supplied closure -public struct HBCallbackResponder: HBResponder { +public struct HBCallbackResponder: HBResponder { let callback: @Sendable (HBRequest, Context) async throws -> HBResponse public init(callback: @escaping @Sendable (HBRequest, Context) async throws -> HBResponse) { diff --git a/Sources/HummingbirdFoundation/Codable/JSON/JSONCoding.swift b/Sources/HummingbirdFoundation/Codable/JSON/JSONCoding.swift index 33a722f03..731f2bc8b 100644 --- a/Sources/HummingbirdFoundation/Codable/JSON/JSONCoding.swift +++ b/Sources/HummingbirdFoundation/Codable/JSON/JSONCoding.swift @@ -23,7 +23,7 @@ extension JSONEncoder: HBResponseEncoder { /// - Parameters: /// - value: Value to encode /// - request: Request used to generate response - public func encode(_ value: T, from request: HBRequest, context: HBRequestContext) throws -> HBResponse { + public func encode(_ value: some Encodable, from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse { var buffer = context.allocator.buffer(capacity: 0) let data = try self.encode(value) buffer.writeBytes(data) @@ -40,7 +40,7 @@ extension JSONDecoder: HBRequestDecoder { /// - Parameters: /// - type: Type to decode /// - request: Request to decode from - public func decode(_ type: T.Type, from request: HBRequest, context: HBRequestContext) throws -> T { + public func decode(_ type: T.Type, from request: HBRequest, context: some HBBaseRequestContext) throws -> T { guard case .byteBuffer(var buffer) = request.body, let data = buffer.readData(length: buffer.readableBytes) else { diff --git a/Sources/HummingbirdFoundation/Codable/URLEncodedForm/URLEncodedForm+Request.swift b/Sources/HummingbirdFoundation/Codable/URLEncodedForm/URLEncodedForm+Request.swift index ab40e7bde..feeb24803 100644 --- a/Sources/HummingbirdFoundation/Codable/URLEncodedForm/URLEncodedForm+Request.swift +++ b/Sources/HummingbirdFoundation/Codable/URLEncodedForm/URLEncodedForm+Request.swift @@ -19,7 +19,7 @@ extension URLEncodedFormEncoder: HBResponseEncoder { /// - Parameters: /// - value: Value to encode /// - request: Request used to generate response - public func encode(_ value: T, from request: HBRequest, context: HBRequestContext) throws -> HBResponse { + public func encode(_ value: some Encodable, from request: HBRequest, context: some HBBaseRequestContext) throws -> HBResponse { var buffer = context.allocator.buffer(capacity: 0) let string = try self.encode(value) buffer.writeString(string) @@ -36,7 +36,7 @@ extension URLEncodedFormDecoder: HBRequestDecoder { /// - Parameters: /// - type: Type to decode /// - request: Request to decode from - public func decode(_ type: T.Type, from request: HBRequest, context: HBRequestContext) throws -> T { + public func decode(_ type: T.Type, from request: HBRequest, context: some HBBaseRequestContext) throws -> T { guard case .byteBuffer(var buffer) = request.body, let string = buffer.readString(length: buffer.readableBytes) else { diff --git a/Sources/HummingbirdFoundation/Files/FileIO.swift b/Sources/HummingbirdFoundation/Files/FileIO.swift index d697b6c7b..23d25d4c7 100644 --- a/Sources/HummingbirdFoundation/Files/FileIO.swift +++ b/Sources/HummingbirdFoundation/Files/FileIO.swift @@ -38,7 +38,7 @@ public struct HBFileIO: Sendable { /// - path: System file path /// - context: Context this request is being called in /// - Returns: Response body - public func loadFile(path: String, context: HBRequestContext, logger: Logger) async throws -> HBResponseBody { + public func loadFile(path: String, context: some HBBaseRequestContext, logger: Logger) async throws -> HBResponseBody { do { let (handle, region) = try await self.fileIO.openFile(path: path, eventLoop: context.eventLoop).get() logger.debug("[FileIO] GET", metadata: ["file": .string(path)]) @@ -66,7 +66,7 @@ public struct HBFileIO: Sendable { /// - range:Range defining how much of the file is to be loaded /// - context: Context this request is being called in /// - Returns: Response body plus file size - public func loadFile(path: String, range: ClosedRange, context: HBRequestContext, logger: Logger) async throws -> (HBResponseBody, Int) { + public func loadFile(path: String, range: ClosedRange, context: some HBBaseRequestContext, logger: Logger) async throws -> (HBResponseBody, Int) { do { let (handle, region) = try await self.fileIO.openFile(path: path, eventLoop: context.eventLoop).get() logger.debug("[FileIO] GET", metadata: ["file": .string(path)]) @@ -100,7 +100,7 @@ public struct HBFileIO: Sendable { /// - contents: Request body to write. /// - path: Path to write to /// - logger: Logger - public func writeFile(contents: HBRequestBody, path: String, context: HBRequestContext, logger: Logger) async throws { + public func writeFile(contents: HBRequestBody, path: String, context: some HBBaseRequestContext, logger: Logger) async throws { let handle = try await self.fileIO.openFile(path: path, mode: .write, flags: .allowFileCreation(), eventLoop: context.eventLoop).get() defer { try? handle.close() @@ -115,7 +115,7 @@ public struct HBFileIO: Sendable { } /// Load file as ByteBuffer - func loadFile(handle: NIOFileHandle, region: FileRegion, context: HBRequestContext) async throws -> HBResponseBody { + func loadFile(handle: NIOFileHandle, region: FileRegion, context: some HBBaseRequestContext) async throws -> HBResponseBody { let buffer = try await self.fileIO.read( fileHandle: handle, fromOffset: Int64(region.readerIndex), @@ -127,7 +127,7 @@ public struct HBFileIO: Sendable { } /// Return streamer that will load file - func streamFile(handle: NIOFileHandle, region: FileRegion, context: HBRequestContext) throws -> HBResponseBody { + func streamFile(handle: NIOFileHandle, region: FileRegion, context: some HBBaseRequestContext) throws -> HBResponseBody { let fileOffset = region.readerIndex let endOffset = region.endIndex return HBResponseBody(contentLength: region.readableBytes) { writer in diff --git a/Sources/HummingbirdFoundation/Files/FileMiddleware.swift b/Sources/HummingbirdFoundation/Files/FileMiddleware.swift index 54130cdcb..ad138e159 100644 --- a/Sources/HummingbirdFoundation/Files/FileMiddleware.swift +++ b/Sources/HummingbirdFoundation/Files/FileMiddleware.swift @@ -28,7 +28,7 @@ import NIOPosix /// "if-modified-since", "if-none-match", "if-range" and 'range" headers. It will output "content-length", /// "modified-date", "eTag", "content-type", "cache-control" and "content-range" headers where /// they are relevant. -public struct HBFileMiddleware: HBMiddleware { +public struct HBFileMiddleware: HBMiddleware { struct IsDirectoryError: Error {} let rootFolder: URL diff --git a/Sources/HummingbirdXCT/Application+XCT.swift b/Sources/HummingbirdXCT/Application+XCT.swift index 325ddfb02..6d09f87cc 100644 --- a/Sources/HummingbirdXCT/Application+XCT.swift +++ b/Sources/HummingbirdXCT/Application+XCT.swift @@ -50,7 +50,7 @@ public struct XCTRouterTestingSetup { /// } /// } /// ``` -extension HBApplication { +extension HBApplication where Responder.Context: HBRequestContext { // MARK: Initialization /// Creates a version of `HBApplication` that can be used for testing code @@ -68,7 +68,7 @@ extension HBApplication { } } -extension HBApplication where ChannelSetup == HTTP1Channel { +extension HBApplication where ChannelSetup == HTTP1Channel, Responder.Context: HBTestRequestContextProtocol { // MARK: Initialization /// Creates a version of `HBApplication` that can be used for testing code diff --git a/Sources/HummingbirdXCT/HBXCTLive.swift b/Sources/HummingbirdXCT/HBXCTLive.swift index e8586d0b6..2edc2b67a 100644 --- a/Sources/HummingbirdXCT/HBXCTLive.swift +++ b/Sources/HummingbirdXCT/HBXCTLive.swift @@ -23,7 +23,7 @@ import ServiceLifecycle import XCTest /// Test using a live server -final class HBXCTLive: HBXCTApplication { +final class HBXCTLive: HBXCTApplication where Responder.Context: HBRequestContext { struct Client: HBXCTClientProtocol { let client: HBXCTClient diff --git a/Sources/HummingbirdXCT/HBXCTRouter.swift b/Sources/HummingbirdXCT/HBXCTRouter.swift index 51761318f..e2667874f 100644 --- a/Sources/HummingbirdXCT/HBXCTRouter.swift +++ b/Sources/HummingbirdXCT/HBXCTRouter.swift @@ -21,23 +21,49 @@ import NIOCore import NIOPosix import Tracing -public struct HBTestRouterContext: HBRequestContext { - public init(applicationContext: HBApplicationContext, source: some RequestContextSource, logger: Logger) { - self.coreContext = .init(applicationContext: applicationContext, source: source, logger: logger) +public protocol HBTestRequestContextProtocol: HBBaseRequestContext { + init( + applicationContext: HBApplicationContext, + eventLoop: EventLoop, + allocator: ByteBufferAllocator, + logger: Logger + ) +} + +public struct HBTestRouterContext: HBTestRequestContextProtocol, HBRequestContext { + public init( + applicationContext: HBApplicationContext, + eventLoop: EventLoop, + allocator: ByteBufferAllocator, + logger: Logger + ) { + self.coreContext = .init( + applicationContext: applicationContext, + eventLoop: eventLoop, + allocator: allocator, + logger: logger + ) + } + + public init( + applicationContext: HBApplicationContext, + channel: Channel, + logger: Logger + ) { + self.coreContext = .init( + applicationContext: applicationContext, + eventLoop: channel.eventLoop, + allocator: channel.allocator, + logger: logger + ) } /// router context public var coreContext: HBCoreRequestContext } -struct TestContextSource: RequestContextSource { - let eventLoop: EventLoop - var allocator: ByteBufferAllocator { ByteBufferAllocator() } - var remoteAddress: SocketAddress? { nil } -} - /// Test sending values to requests to router. This does not setup a live server -struct HBXCTRouter: HBXCTApplication { +struct HBXCTRouter: HBXCTApplication where Responder.Context: HBTestRequestContextProtocol { let eventLoopGroup: EventLoopGroup let context: HBApplicationContext let responder: Responder @@ -77,10 +103,10 @@ struct HBXCTRouter: HBXCTApplication { head: .init(version: .http1_1, method: method, uri: uri, headers: headers), body: .stream(streamer) ) - let contextSource = TestContextSource(eventLoop: eventLoop) let context = Responder.Context( applicationContext: self.applicationContext, - source: contextSource, + eventLoop: eventLoop, + allocator: ByteBufferAllocator(), logger: HBApplication.loggerWithRequestId(self.applicationContext.logger) ) diff --git a/Sources/PerformanceTest/main.swift b/Sources/PerformanceTest/main.swift index 348a6f366..78f6c7069 100644 --- a/Sources/PerformanceTest/main.swift +++ b/Sources/PerformanceTest/main.swift @@ -18,23 +18,6 @@ import Logging import NIOCore import NIOPosix -struct MyRequestContext: HBRequestContext { - /// core context - public var coreContext: HBCoreRequestContext - - /// Initialize an `HBRequestContext` - /// - Parameters: - /// - applicationContext: Context from Application that instigated the request - /// - channelContext: Context providing source for EventLoop - public init( - applicationContext: HBApplicationContext, - source: some RequestContextSource, - logger: Logger - ) { - self.coreContext = .init(applicationContext: applicationContext, eventLoop: source.eventLoop, logger: logger, allocator: source.allocator) - } -} - // get environment let hostname = HBEnvironment.shared.get("SERVER_HOSTNAME") ?? "127.0.0.1" let port = HBEnvironment.shared.get("SERVER_PORT", as: Int.self) ?? 8080 @@ -42,7 +25,7 @@ let port = HBEnvironment.shared.get("SERVER_PORT", as: Int.self) ?? 8080 // create app let elg = MultiThreadedEventLoopGroup(numberOfThreads: 4) defer { try? elg.syncShutdownGracefully() } -var router = HBRouterBuilder(context: MyRequestContext.self) +var router = HBRouterBuilder() // number of raw requests // ./wrk -c 128 -d 15s -t 8 http://localhost:8080 router.get { _, _ in diff --git a/Tests/HummingbirdFoundationTests/FilesTests.swift b/Tests/HummingbirdFoundationTests/FilesTests.swift index a95b47ff6..b00771d0a 100644 --- a/Tests/HummingbirdFoundationTests/FilesTests.swift +++ b/Tests/HummingbirdFoundationTests/FilesTests.swift @@ -348,7 +348,7 @@ class HummingbirdFilesTests: XCTestCase { func testWriteLargeFile() async throws { let filename = "testWriteLargeFile.txt" - let router = HBRouterBuilder(context: HBTestRouterContext.self) + let router = HBRouterBuilder(context: HBBasicRequestContext.self) router.put("store", options: .streamBody) { request, context -> HTTPResponseStatus in let fileIO = HBFileIO(threadPool: context.threadPool) try await fileIO.writeFile(contents: request.body, path: filename, context: context, logger: context.logger) diff --git a/Tests/HummingbirdTests/ApplicationTests.swift b/Tests/HummingbirdTests/ApplicationTests.swift index 10972ab29..585093cb5 100644 --- a/Tests/HummingbirdTests/ApplicationTests.swift +++ b/Tests/HummingbirdTests/ApplicationTests.swift @@ -281,7 +281,7 @@ final class ApplicationTests: XCTestCase { } func testCollateBody() async throws { - struct CollateMiddleware: HBMiddleware { + struct CollateMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { var request = request request.body = try await request.body.collate(maxSize: context.applicationContext.configuration.maxUploadSize) @@ -432,11 +432,11 @@ final class ApplicationTests: XCTestCase { public init( applicationContext: HBApplicationContext, - source: some RequestContextSource, + channel: Channel, logger: Logger ) { - self.coreContext = .init(applicationContext: applicationContext, source: source, logger: logger) - self.remoteAddress = source.remoteAddress + self.coreContext = .init(applicationContext: applicationContext, eventLoop: channel.eventLoop, allocator: channel.allocator, logger: logger) + self.remoteAddress = channel.remoteAddress } } let router = HBRouterBuilder(context: HBSocketAddressRequestContext.self) diff --git a/Tests/HummingbirdTests/HandlerTests.swift b/Tests/HummingbirdTests/HandlerTests.swift index 79fb9723c..46b72c9e8 100644 --- a/Tests/HummingbirdTests/HandlerTests.swift +++ b/Tests/HummingbirdTests/HandlerTests.swift @@ -23,7 +23,7 @@ final class HandlerTests: XCTestCase { struct DecodeTest: HBRequestDecodable { let name: String - func handle(request: HBRequest, context: HBRequestContext) -> String { + func handle(request: HBRequest, context: some HBBaseRequestContext) -> String { return "Hello \(self.name)" } } @@ -53,7 +53,7 @@ final class HandlerTests: XCTestCase { struct DecodeTest: HBRequestDecodable { let value: Int - func handle(request: HBRequest, context: HBRequestContext) -> String { + func handle(request: HBRequest, context: some HBBaseRequestContext) -> String { return "Value: \(self.value)" } } @@ -83,7 +83,7 @@ final class HandlerTests: XCTestCase { struct DecodeTest: HBRequestDecodable { let name: String - func handle(request: HBRequest, context: HBRequestContext) -> String { + func handle(request: HBRequest, context: some HBBaseRequestContext) -> String { return "Hello \(self.name)" } } @@ -118,7 +118,7 @@ final class HandlerTests: XCTestCase { struct DecodeTest: HBRequestDecodable { let name: String - func handle(request: HBRequest, context: HBRequestContext) -> String { + func handle(request: HBRequest, context: some HBBaseRequestContext) -> String { return "Hello \(self.name)" } } @@ -147,7 +147,7 @@ final class HandlerTests: XCTestCase { func testDecode() async throws { struct DecodeTest: HBRequestDecodable { let name: String - func handle(request: HBRequest, context: HBRequestContext) -> String { + func handle(request: HBRequest, context: some HBBaseRequestContext) -> String { return "Hello \(self.name)" } } @@ -168,7 +168,7 @@ final class HandlerTests: XCTestCase { func testDecodeFutureResponse() async throws { struct DecodeTest: HBRequestDecodable { let name: String - func handle(request: HBRequest, context: HBRequestContext) async throws -> String { + func handle(request: HBRequest, context: some HBBaseRequestContext) -> String { "Hello \(self.name)" } } @@ -190,7 +190,7 @@ final class HandlerTests: XCTestCase { struct DecodeTest: HBRequestDecodable { let name: String - func handle(request: HBRequest, context: HBRequestContext) -> HTTPResponseStatus { + func handle(request: HBRequest, context: some HBBaseRequestContext) -> HTTPResponseStatus { return .ok } } @@ -209,11 +209,11 @@ final class HandlerTests: XCTestCase { func testEmptyRequest() async throws { struct ParameterTest: HBRouteHandler { let parameter: Int - init(from request: HBRequest, context: HBRequestContext) throws { + init(from request: HBRequest, context: some HBBaseRequestContext) throws { self.parameter = try context.parameters.require("test", as: Int.self) } - func handle(request: HBRequest, context: HBRequestContext) -> String { + func handle(request: HBRequest, context: some HBBaseRequestContext) -> String { return "\(self.parameter)" } } diff --git a/Tests/HummingbirdTests/MiddlewareTests.swift b/Tests/HummingbirdTests/MiddlewareTests.swift index ea08c3599..b571e34ec 100644 --- a/Tests/HummingbirdTests/MiddlewareTests.swift +++ b/Tests/HummingbirdTests/MiddlewareTests.swift @@ -24,7 +24,7 @@ final class MiddlewareTests: XCTestCase { } func testMiddleware() async throws { - struct TestMiddleware: HBMiddleware { + struct TestMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { var response = try await next.respond(to: request, context: context) response.headers.replaceOrAdd(name: "middleware", value: "TestMiddleware") @@ -45,7 +45,7 @@ final class MiddlewareTests: XCTestCase { } func testMiddlewareOrder() async throws { - struct TestMiddleware: HBMiddleware { + struct TestMiddleware: HBMiddleware { let string: String func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { var response = try await next.respond(to: request, context: context) @@ -70,7 +70,7 @@ final class MiddlewareTests: XCTestCase { } func testMiddlewareRunOnce() async throws { - struct TestMiddleware: HBMiddleware { + struct TestMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { var response = try await next.respond(to: request, context: context) XCTAssertNil(response.headers["alreadyRun"].first) @@ -91,7 +91,7 @@ final class MiddlewareTests: XCTestCase { } func testMiddlewareRunWhenNoRouteFound() async throws { - struct TestMiddleware: HBMiddleware { + struct TestMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { do { return try await next.respond(to: request, context: context) @@ -114,7 +114,7 @@ final class MiddlewareTests: XCTestCase { } func testEndpointPathInGroup() async throws { - struct TestMiddleware: HBMiddleware { + struct TestMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { XCTAssertNotNil(context.endpointPath) return try await next.respond(to: request, context: context) @@ -143,7 +143,7 @@ final class MiddlewareTests: XCTestCase { try await self.parentWriter.write(output) } } - struct TransformMiddleware: HBMiddleware { + struct TransformMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { let response = try await next.respond(to: request, context: context) var editedResponse = response diff --git a/Tests/HummingbirdTests/RouterTests.swift b/Tests/HummingbirdTests/RouterTests.swift index 8273f041f..41c10a53d 100644 --- a/Tests/HummingbirdTests/RouterTests.swift +++ b/Tests/HummingbirdTests/RouterTests.swift @@ -21,7 +21,7 @@ import Tracing import XCTest final class RouterTests: XCTestCase { - struct TestMiddleware: HBMiddleware { + struct TestMiddleware: HBMiddleware { let output: String init(_ output: String = "TestMiddleware") { @@ -37,7 +37,7 @@ final class RouterTests: XCTestCase { /// Test endpointPath is set func testEndpointPath() async throws { - struct TestEndpointMiddleware: HBMiddleware { + struct TestEndpointMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { guard let endpointPath = context.endpointPath else { return try await next.respond(to: request, context: context) } return .init(status: .ok, body: .init(byteBuffer: ByteBuffer(string: endpointPath))) @@ -59,7 +59,7 @@ final class RouterTests: XCTestCase { /// Test endpointPath is prefixed with a "/" func testEndpointPathPrefix() async throws { - struct TestEndpointMiddleware: HBMiddleware { + struct TestEndpointMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { guard let endpointPath = context.endpointPath else { return try await next.respond(to: request, context: context) } return .init(status: .ok, body: .init(byteBuffer: ByteBuffer(string: endpointPath))) @@ -97,7 +97,7 @@ final class RouterTests: XCTestCase { /// Test endpointPath doesn't have "/" at end func testEndpointPathSuffix() async throws { - struct TestEndpointMiddleware: HBMiddleware { + struct TestEndpointMiddleware: HBMiddleware { func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { guard let endpointPath = context.endpointPath else { return try await next.respond(to: request, context: context) } return .init(status: .ok, body: .init(byteBuffer: ByteBuffer(string: endpointPath))) @@ -356,7 +356,7 @@ final class RouterTests: XCTestCase { // Test redirect response func testRedirect() async throws { - let router = HBRouterBuilder() + let router = HBRouterBuilder(context: HBTestRouterContext.self) router.get("redirect") { _, _ in return HBResponse.redirect(to: "/other") } @@ -370,10 +370,14 @@ final class RouterTests: XCTestCase { } } -public struct HBTestRouterContext2: HBRequestContext { - public init(applicationContext: HBApplicationContext, source: some RequestContextSource, logger: Logger) { - self.coreContext = .init(applicationContext: applicationContext, source: source, logger: logger) - +public struct HBTestRouterContext2: HBTestRequestContextProtocol { + public init( + applicationContext: HBApplicationContext, + eventLoop: EventLoop, + allocator: ByteBufferAllocator, + logger: Logger + ) { + self.coreContext = .init(applicationContext: applicationContext, eventLoop: eventLoop, allocator: allocator, logger: logger) self.string = "" } diff --git a/Tests/HummingbirdTests/TracingTests.swift b/Tests/HummingbirdTests/TracingTests.swift index 91ad2c1a5..9050295bd 100644 --- a/Tests/HummingbirdTests/TracingTests.swift +++ b/Tests/HummingbirdTests/TracingTests.swift @@ -361,7 +361,7 @@ final class TracingTests: XCTestCase { let expectation = expectation(description: "Expected span to be ended.") expectation.expectedFulfillmentCount = 2 - struct SpanMiddleware: HBMiddleware { + struct SpanMiddleware: HBMiddleware { public func apply(to request: HBRequest, context: Context, next: any HBResponder) async throws -> HBResponse { var serviceContext = ServiceContext.current ?? ServiceContext.topLevel serviceContext.testID = "testMiddleware"