diff --git a/Package.swift b/Package.swift index 85ca08e03..02320805f 100644 --- a/Package.swift +++ b/Package.swift @@ -17,7 +17,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-log.git", from: "1.4.0"), .package(url: "https://github.com/apple/swift-metrics.git", "1.0.0"..<"3.0.0"), .package(url: "https://github.com/apple/swift-distributed-tracing.git", from: "1.0.1"), - .package(url: "https://github.com/apple/swift-nio.git", from: "2.56.0"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.58.0"), .package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "1.0.0-alpha.9"), .package(url: "https://github.com/hummingbird-project/hummingbird-core.git", from: "1.2.1"), ], diff --git a/Sources/Hummingbird/Application.swift b/Sources/Hummingbird/Application.swift index 1b90c6d49..379a0021a 100644 --- a/Sources/Hummingbird/Application.swift +++ b/Sources/Hummingbird/Application.swift @@ -36,6 +36,25 @@ import NIOTransportServices /// ``` /// Editing the application setup after calling `start` will produce undefined behaviour. public final class HBApplication: HBExtensible { + /// Indicates where we should get our EventLoopGroup from + public struct EventLoopGroupProvider { + enum Internal { + case createNew + case shared(EventLoopGroup) + case singleton + } + + let value: Internal + init(_ value: Internal) { + self.value = value + } + + /// Use EventLoopGroup provided + public static func shared(_ eventLoopGroup: EventLoopGroup) -> Self { .init(.shared(eventLoopGroup)) } + /// Use singleton EventLoopGroup + public static var singleton: Self { .init(.singleton) } + } + // MARK: Member variables /// server lifecycle, controls initialization and shutdown of application @@ -60,14 +79,14 @@ public final class HBApplication: HBExtensible { public var decoder: HBRequestDecoder /// who provided the eventLoopGroup - let eventLoopGroupProvider: NIOEventLoopGroupProvider + let eventLoopGroupProvider: HBApplication.EventLoopGroupProvider // MARK: Initialization /// Initialize new Application public init( configuration: HBApplication.Configuration = HBApplication.Configuration(), - eventLoopGroupProvider: NIOEventLoopGroupProvider = .createNew, + eventLoopGroupProvider: EventLoopGroupProvider = .singleton, serviceLifecycleProvider: ServiceLifecycleProvider = .createNew ) { var logger = Logger(label: configuration.serverName ?? "HummingBird") @@ -82,13 +101,19 @@ public final class HBApplication: HBExtensible { // create eventLoopGroup self.eventLoopGroupProvider = eventLoopGroupProvider - switch eventLoopGroupProvider { + switch self.eventLoopGroupProvider.value { case .createNew: #if os(iOS) self.eventLoopGroup = NIOTSEventLoopGroup() #else self.eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) #endif + case .singleton: + #if os(iOS) + self.eventLoopGroup = NIOTSEventLoopGroup.singleton + #else + self.eventLoopGroup = MultiThreadedEventLoopGroup.singleton + #endif case .shared(let elg): self.eventLoopGroup = elg } @@ -132,6 +157,21 @@ public final class HBApplication: HBExtensible { } } + @available(*, deprecated, message: "Calling HBApplication.init(eventLoopGroupProvider: .createNew) has been deprecated. Use .singleton instead.") + @_disfavoredOverload + public convenience init( + configuration: HBApplication.Configuration = HBApplication.Configuration(), + eventLoopGroupProvider: NIOEventLoopGroupProvider = .createNew, + serviceLifecycleProvider: ServiceLifecycleProvider = .createNew + ) { + switch eventLoopGroupProvider { + case .createNew: + self.init(configuration: configuration, eventLoopGroupProvider: .init(.createNew), serviceLifecycleProvider: serviceLifecycleProvider) + case .shared(let elg): + self.init(configuration: configuration, eventLoopGroupProvider: .init(.shared(elg)), serviceLifecycleProvider: serviceLifecycleProvider) + } + } + // MARK: Methods /// Run application @@ -174,7 +214,7 @@ public final class HBApplication: HBExtensible { public func shutdownApplication() throws { try self.extensions.shutdown() try self.threadPool.syncShutdownGracefully() - if case .createNew = self.eventLoopGroupProvider { + if case .createNew = self.eventLoopGroupProvider.value { try self.eventLoopGroup.syncShutdownGracefully() } } diff --git a/Tests/HummingbirdTests/ApplicationTests.swift b/Tests/HummingbirdTests/ApplicationTests.swift index 839fdd3fd..a8a315439 100644 --- a/Tests/HummingbirdTests/ApplicationTests.swift +++ b/Tests/HummingbirdTests/ApplicationTests.swift @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// import Hummingbird +import HummingbirdCoreXCT import HummingbirdXCT import NIOHTTP1 import XCTest @@ -473,4 +474,28 @@ final class ApplicationTests: XCTestCase { XCTAssert(address == "127.0.0.1" || address == "::1") } } + + func testSingleEventLoopGroup() throws { + let app = HBApplication(eventLoopGroupProvider: .singleton) + app.router.get("/hello") { request -> EventLoopFuture in + let buffer = request.allocator.buffer(string: "GET: Hello") + return request.eventLoop.makeSucceededFuture(buffer) + } + try app.start() + defer { app.stop() } + + let client = HBXCTClient( + host: "localhost", + port: app.server.port!, + configuration: .init(timeout: .seconds(15)), + eventLoopGroupProvider: .createNew + ) + defer { try? client.syncShutdown() } + client.connect() + let response = try client.get("/hello").wait() + var body = try XCTUnwrap(response.body) + let string = body.readString(length: body.readableBytes) + XCTAssertEqual(response.status, .ok) + XCTAssertEqual(string, "GET: Hello") + } }