Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge HBApplication and HBApplicationBuilder #254

Merged
merged 4 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
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
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v3
- name: SPM tests
run: swift test --enable-code-coverage
run: swift test
- name: Convert coverage files
run: |
xcrun llvm-cov export -format "lcov" \
Expand Down
162 changes: 124 additions & 38 deletions Sources/Hummingbird/Application.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,33 @@
import NIOTransportServices
import ServiceLifecycle

/// Where should the application get its EventLoopGroup from
public enum EventLoopGroupProvider {
/// Use this EventLoopGroup
case shared(EventLoopGroup)
/// Use one of the singleton EventLoopGroups
case singleton

public var eventLoopGroup: EventLoopGroup {
switch self {
case .singleton:
#if os(iOS)
return NIOTSEventLoopGroup.singleton
#else
return MultiThreadedEventLoopGroup.singleton
#endif
case .shared(let elg):
return elg

Check warning on line 40 in Sources/Hummingbird/Application.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Hummingbird/Application.swift#L40

Added line #L40 was not covered by tests
}
}
}

public final class HBApplicationContext: Sendable {
/// thread pool used by application
public let threadPool: NIOThreadPool
/// Configuration
public let configuration: HBApplicationConfiguration
/// Logger. Required to be a var by hummingbird-lambda
/// Logger
public let logger: Logger
/// Encoder used by router
public let encoder: HBResponseEncoder
Expand All @@ -48,60 +69,125 @@
}
}

public struct HBApplication<RequestContext: HBRequestContext>: Sendable {
/// Application builder class. Brings together all the components of Hummingbird together
///
/// Setup an HBApplicationBuilder, setup your application middleware, encoders, routes etc and then either
/// add call `build` to create an `HBApplication` which you add to your ServiceLifecycle `ServiceGroup` or
/// run separately with `buildAndRun`.
/// ```
/// let app = HBApplicationBuilder()
/// app.middleware.add(MyMiddleware())
/// app.router.get("hello") { _ in
/// return "hello"
/// }
/// try await app.buildAndRun()
/// ```
/// Editing the application builder setup after calling `build` will produce undefined behaviour.
public struct HBApplication<Responder: HBResponder> {
// MARK: Member variables

/// event loop group used by application
public let context: HBApplicationContext
// eventLoopGroup
public let eventLoopGroup: EventLoopGroup
// server
public let server: HBHTTPServer
// date cache service
internal let dateCache: HBDateCache
/// thread pool used by application
public let threadPool: NIOThreadPool
/// routes requests to requestResponders based on URI
public let responder: Responder
/// Configuration
public var configuration: HBApplicationConfiguration
/// Logger
public var logger: Logger
/// Encoder used by router
public var encoder: HBResponseEncoder
/// decoder used by router
public var decoder: HBRequestDecoder
/// on server running
public var onServerRunning: @Sendable (Channel) async -> Void
/// additional channel handlers
var additionalChannelHandlers: [@Sendable () -> any RemovableChannelHandler]

init(builder: HBApplicationBuilder<RequestContext>) {
self.eventLoopGroup = builder.eventLoopGroup
self.context = .init(
threadPool: builder.threadPool,
configuration: builder.configuration,
logger: builder.logger,
encoder: builder.encoder,
decoder: builder.decoder
)
self.dateCache = .init()
let responder = Responder(
responder: builder.constructResponder(),
applicationContext: self.context,
dateCache: self.dateCache
)
self.server = HBHTTPServer(
group: builder.eventLoopGroup,
configuration: builder.configuration.httpServer,
responder: responder,
additionalChannelHandlers: builder.additionalChannelHandlers.map { $0() },
onServerRunning: builder.onServerRunning,
logger: builder.logger
)
// MARK: Initialization

/// Initialize new Application
public init(
responder: Responder,
configuration: HBApplicationConfiguration = HBApplicationConfiguration(),
threadPool: NIOThreadPool = .singleton,
eventLoopGroupProvider: EventLoopGroupProvider = .singleton
) {
var logger = Logger(label: configuration.serverName ?? "HummingBird")
logger.logLevel = configuration.logLevel
self.logger = logger

self.responder = responder
self.configuration = configuration
self.encoder = NullEncoder()
self.decoder = NullDecoder()
self.onServerRunning = { _ in }
// add idle read, write handlers
if let idleTimeoutConfiguration = configuration.idleTimeoutConfiguration {
self.additionalChannelHandlers = [{
IdleStateHandler(
readTimeout: idleTimeoutConfiguration.readTimeout,
writeTimeout: idleTimeoutConfiguration.writeTimeout
)
}]

Check warning on line 133 in Sources/Hummingbird/Application.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Hummingbird/Application.swift#L128-L133

Added lines #L128 - L133 were not covered by tests
} else {
self.additionalChannelHandlers = []
}

self.eventLoopGroup = eventLoopGroupProvider.eventLoopGroup
self.threadPool = threadPool
}

/// shutdown eventloop, threadpool and any extensions attached to the Application
public func shutdownApplication() throws {
try self.context.threadPool.syncShutdownGracefully()
// MARK: Methods

/// Helper function that runs application inside a ServiceGroup which will gracefully
/// shutdown on signals SIGINT, SIGTERM
public func runService() async throws {
let serviceGroup = ServiceGroup(
configuration: .init(
services: [self],
gracefulShutdownSignals: [.sigterm, .sigint],
logger: self.logger
)
)
try await serviceGroup.run()

Check warning on line 154 in Sources/Hummingbird/Application.swift

View check run for this annotation

Codecov / codecov/patch

Sources/Hummingbird/Application.swift#L146-L154

Added lines #L146 - L154 were not covered by tests
}
}

/// Conform to `Service` from `ServiceLifecycle`.
extension HBApplication: Service {
public func run() async throws {
let context = HBApplicationContext(
threadPool: self.threadPool,
configuration: self.configuration,
logger: self.logger,
encoder: self.encoder,
decoder: self.decoder
)
let dateCache = HBDateCache()
let responder = HTTPResponder(
responder: self.responder,
applicationContext: context,
dateCache: dateCache
)
let server = HBHTTPServer(
group: self.eventLoopGroup,
configuration: self.configuration.httpServer,
responder: responder,
additionalChannelHandlers: self.additionalChannelHandlers.map { $0() },
onServerRunning: self.onServerRunning,
logger: self.logger
)
try await withGracefulShutdownHandler {
let services: [any Service] = [self.server, self.dateCache]
let services: [any Service] = [server, dateCache]
let serviceGroup = ServiceGroup(
configuration: .init(services: services, logger: self.context.logger)
configuration: .init(services: services, logger: self.logger)
)
try await serviceGroup.run()
try self.shutdownApplication()
} onGracefulShutdown: {
Task {
try await self.server.shutdownGracefully()
try await server.shutdownGracefully()
}
}
}
Expand Down
143 changes: 0 additions & 143 deletions Sources/Hummingbird/ApplicationBuilder.swift

This file was deleted.

6 changes: 3 additions & 3 deletions Sources/Hummingbird/Server/Application+HTTPResponder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ import NIOCore
import NIOHTTP1

extension HBApplication {
struct Responder: HBHTTPResponder {
let responder: any HBResponder<RequestContext>
struct HTTPResponder: HBHTTPResponder {
let responder: Responder
let applicationContext: HBApplicationContext
let dateCache: HBDateCache

Expand All @@ -34,7 +34,7 @@ extension HBApplication {
head: request.head,
body: request.body
)
let context = RequestContext(
let context = Responder.Context(
applicationContext: self.applicationContext,
channel: context.channel,
logger: loggerWithRequestId(self.applicationContext.logger)
Expand Down
3 changes: 2 additions & 1 deletion Sources/Hummingbird/Storage/MemoryPersistDriver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ import NIOCore

/// In memory driver for persist system for storing persistent cross request key/value pairs
public final class HBMemoryPersistDriver: HBPersistDriver {
public init(eventLoopGroup: EventLoopGroup) {
public init(eventLoopGroupProvider: EventLoopGroupProvider = .singleton) {
let eventLoopGroup = eventLoopGroupProvider.eventLoopGroup
self.eventLoop = eventLoopGroup.next()
self.values = [:]
self.task = self.eventLoop.scheduleRepeatedTask(initialDelay: .hours(1), delay: .hours(1)) { _ in
Expand Down
2 changes: 1 addition & 1 deletion Sources/HummingbirdFoundation/Files/FileMiddleware.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public struct HBFileMiddleware<Context: HBRequestContext>: HBMiddleware {
cacheControl: HBCacheControl = .init([]),
searchForIndexHtml: Bool = false,
threadPool: NIOThreadPool = NIOThreadPool.singleton,
logger: Logger
logger: Logger = Logger(label: "HBFileMiddleware")
) {
self.rootFolder = URL(fileURLWithPath: rootFolder)
self.threadPool = threadPool
Expand Down
Loading
Loading