diff --git a/README.md b/README.md index bc64ba1b..fe1c5f18 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,7 @@ Examples converted to Hummingbird 2.0 - [hello](https://github.com/hummingbird-project/hummingbird-examples/tree/main/hello) - Basic application setup +- [http2](https://github.com/hummingbird-project/hummingbird-examples/tree/main/http2) - Basic application with HTTP2 upgrade added - [todos-dynamodb](https://github.com/hummingbird-project/hummingbird-examples/tree/main/todos-dynamodb) - Todos application, based off [TodoBackend](http://todobackend.com) spec, using DynamoDB Examples still working with Hummingbird 1.0 @@ -10,7 +11,6 @@ Examples still working with Hummingbird 1.0 - [auth-srp](https://github.com/hummingbird-project/hummingbird-examples/tree/main/auth-srp) - Secure Remote Password authentication. - [graphql-server](https://github.com/hummingbird-project/hummingbird-examples/tree/main/graphql-server) - GraphQL server using [Graphiti](https://github.com/GraphQLSwift/Graphiti) - [html-form](https://github.com/hummingbird-project/hummingbird-examples/tree/main/html-form) - Link HTML form to Hummingbird application -- [http2](https://github.com/hummingbird-project/hummingbird-examples/tree/main/http2) - Basic application with HTTP2 upgrade added - [ios-image-server](https://github.com/hummingbird-project/hummingbird-examples/tree/main/ios-image-server) - iOS web server that provides access to iPhone photo library. - [jobs](https://github.com/hummingbird-project/hummingbird-examples/tree/main/jobs) - Demonstrating offloading of jobs to another server. - [multipart-form](https://github.com/hummingbird-project/hummingbird-examples/tree/main/multipart-form) - HTML form using Multipart form data, using MultipartKit diff --git a/hello/Package.swift b/hello/Package.swift index 6c7a2957..53044228 100644 --- a/hello/Package.swift +++ b/hello/Package.swift @@ -29,6 +29,7 @@ let package = Package( name: "AppTests", dependencies: [ .byName(name: "App"), + .product(name: "Hummingbird", package: "hummingbird"), .product(name: "HummingbirdXCT", package: "hummingbird"), ] ), diff --git a/hello/Sources/App/Application+build.swift b/hello/Sources/App/Application+build.swift index dffcf62c..c6f53074 100644 --- a/hello/Sources/App/Application+build.swift +++ b/hello/Sources/App/Application+build.swift @@ -1,16 +1,13 @@ import Hummingbird -import HummingbirdCore -import HummingbirdFoundation -func buildApplication(configuration: HBApplicationConfiguration) -> HBApplication, HTTP1Channel> { - let router = HBRouterBuilder() - router.middlewares.add(HBFileMiddleware()) +func buildApplication(configuration: HBApplicationConfiguration) -> some HBApplicationProtocol { + let router = HBRouter() router.get("/") { _, _ in return "Hello" } let app = HBApplication( - responder: router.buildResponder(), + router: router, configuration: configuration ) return app diff --git a/hello/Tests/AppTests/AppTests.swift b/hello/Tests/AppTests/AppTests.swift index 4f81c487..b9f10ab5 100644 --- a/hello/Tests/AppTests/AppTests.swift +++ b/hello/Tests/AppTests/AppTests.swift @@ -1,19 +1,16 @@ -import App +@testable import App import Hummingbird import HummingbirdXCT import XCTest final class AppTests: XCTestCase { - func testApp() throws { - let app = HBApplication(testing: .live) - try app.configure() - - try app.XCTStart() - defer { app.XCTStop() } - - try app.XCTExecute(uri: "/", method: .GET) { response in - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.body.map { String(buffer: $0) }, "Hello") + func testApp() async throws { + let app = buildApplication(configuration: .init()) + try await app.test(.router) { client in + try await client.XCTExecute(uri: "/", method: .get) { response in + XCTAssertEqual(response.status, .ok) + XCTAssertEqual(String(buffer: response.body), "Hello") + } } } } diff --git a/http2/Package.swift b/http2/Package.swift index 52d9f42d..207d1170 100644 --- a/http2/Package.swift +++ b/http2/Package.swift @@ -1,19 +1,17 @@ -// swift-tools-version:5.7 +// swift-tools-version:5.9 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription let package = Package( name: "http2", - platforms: [.macOS(.v10_14)], + platforms: [.macOS(.v14)], products: [ .executable(name: "App", targets: ["App"]), ], dependencies: [ .package(url: "https://github.com/apple/swift-certificates.git", from: "1.0.0"), - .package(url: "https://github.com/hummingbird-project/hummingbird-core.git", from: "1.0.0"), - .package(url: "https://github.com/hummingbird-project/hummingbird.git", from: "1.0.0"), - .package(url: "https://github.com/hummingbird-project/hummingbird-xct-async-http-client.git", from: "0.1.0"), + .package(url: "https://github.com/hummingbird-project/hummingbird.git", branch: "2.x.x"), .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.0.0"), ], targets: [ @@ -21,7 +19,7 @@ let package = Package( name: "App", dependencies: [ .product(name: "Hummingbird", package: "hummingbird"), - .product(name: "HummingbirdHTTP2", package: "hummingbird-core"), + .product(name: "HummingbirdHTTP2", package: "hummingbird"), .product(name: "ArgumentParser", package: "swift-argument-parser"), ], swiftSettings: [ @@ -37,7 +35,6 @@ let package = Package( .byName(name: "App"), .product(name: "X509", package: "swift-certificates"), .product(name: "HummingbirdXCT", package: "hummingbird"), - .product(name: "HBXCTAsyncHTTPClient", package: "hummingbird-xct-async-http-client"), ] ), ] diff --git a/http2/Sources/App/Application+build.swift b/http2/Sources/App/Application+build.swift new file mode 100644 index 00000000..e8f76a03 --- /dev/null +++ b/http2/Sources/App/Application+build.swift @@ -0,0 +1,50 @@ +import Hummingbird +import HummingbirdHTTP2 +import Logging +import NIOCore +import NIOHTTPTypesHTTP2 + +public protocol AppArguments { + var tlsConfiguration: TLSConfiguration { get throws } +} + +struct ChannelRequestContext: HBRequestContext { + init(allocator: ByteBufferAllocator, logger: Logger) { + self.coreContext = .init(allocator: allocator, logger: logger) + self.channel = nil + } + + init(channel: Channel, logger: Logger) { + self.coreContext = .init(allocator: channel.allocator, logger: logger) + self.channel = channel + } + + var hasHTTP2Handler: Bool { + get async { + if let channel = self.channel { + return (try? await channel.pipeline.handler(type: HTTP2FramePayloadToHTTPServerCodec.self).get()) != nil + } + return false + } + } + + var coreContext: HBCoreRequestContext + let channel: Channel? +} + +import Hummingbird + +func buildApplication(arguments: some AppArguments, configuration: HBApplicationConfiguration) throws -> some HBApplicationProtocol { + let router = HBRouter(context: ChannelRequestContext.self) + router.get("/http") { _, context in + // return "Using http v\(request.head. == "h2" ? "2.0" : "1.1")" + return "Using http v\(await context.hasHTTP2Handler ? "2.0" : "1.1")" + } + + let app = try HBApplication( + router: router, + server: .http2(tlsConfiguration: arguments.tlsConfiguration), + configuration: configuration + ) + return app +} diff --git a/http2/Sources/App/app.swift b/http2/Sources/App/app.swift index e4f31ad9..55efe687 100644 --- a/http2/Sources/App/app.swift +++ b/http2/Sources/App/app.swift @@ -3,12 +3,12 @@ import Hummingbird import NIOSSL @main -struct HummingbirdArguments: ParsableCommand, AppArguments { +struct HummingbirdArguments: AsyncParsableCommand, AppArguments { @Option(name: .shortAndLong) var hostname: String = "127.0.0.1" @Option(name: .shortAndLong) - var port: Int = 8080 + var port: Int = 8081 @Option(name: .shortAndLong, help: "PEM file containing certificate chain") var certificateChain: String @@ -27,16 +27,14 @@ struct HummingbirdArguments: ParsableCommand, AppArguments { } } - func run() throws { - let app = HBApplication( + func run() async throws { + let app = try buildApplication( + arguments: self, configuration: .init( address: .hostname(self.hostname, port: self.port), - serverName: "Hummingbird", - idleTimeoutConfiguration: .init(readTimeout: .seconds(5), writeTimeout: .seconds(5)) + serverName: "Hummingbird" ) ) - try app.configure(self) - try app.start() - app.wait() + try await app.runService() } } diff --git a/http2/Sources/App/application+configure.swift b/http2/Sources/App/application+configure.swift deleted file mode 100644 index 812fcae6..00000000 --- a/http2/Sources/App/application+configure.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Hummingbird -import HummingbirdHTTP2 - -public protocol AppArguments { - var tlsConfiguration: TLSConfiguration { get throws } -} - -extension HBApplication { - /// configure your application - /// add middleware - /// setup the encoder/decoder - /// add your routes - public func configure(_ arguments: AppArguments) throws { - // Add HTTP2 TLS Upgrade option - try server.addHTTP2Upgrade( - tlsConfiguration: arguments.tlsConfiguration, - idleReadTimeout: .seconds(30) - ) - - router.get("/http") { request in - return "Using http v\(request.version.major).\(request.version.minor)" - } - } -} diff --git a/http2/Tests/AppTests/AppTests.swift b/http2/Tests/AppTests/AppTests.swift index 4dbf2390..bda9aae2 100644 --- a/http2/Tests/AppTests/AppTests.swift +++ b/http2/Tests/AppTests/AppTests.swift @@ -1,6 +1,5 @@ @testable import App import Crypto -import HBXCTAsyncHTTPClient import Hummingbird import HummingbirdXCT import NIOHTTP2 @@ -48,27 +47,19 @@ struct TestAppArguments: AppArguments { } final class AppTests: XCTestCase { - func testApp() throws { - var clientConfiguration = TLSConfiguration.makeClientConfiguration() - clientConfiguration.certificateVerification = .none - let app = HBApplication( - testing: .ahc(scheme: .https), - configuration: .init(idleTimeoutConfiguration: .init(readTimeout: .seconds(5), writeTimeout: .seconds(5))), - clientConfiguration: .init(tlsConfiguration: clientConfiguration) - ) - try app.configure(TestAppArguments()) + func testApp() async throws { + let app = try buildApplication(arguments: TestAppArguments(), configuration: .init()) - try app.XCTStart() - defer { app.XCTStop() } - - try app.XCTExecute(uri: "/http", method: .GET) { response in - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.body.map { String(buffer: $0) }, "Using http v2.0") - } + try await app.test(.ahc(.https)) { client in + try await client.XCTExecute(uri: "/http", method: .get) { response in + XCTAssertEqual(response.status, .ok) + XCTAssertEqual(String(buffer: response.body), "Using http v2.0") + } - try app.XCTExecute(uri: "/http", method: .GET) { response in - XCTAssertEqual(response.status, .ok) - XCTAssertEqual(response.body.map { String(buffer: $0) }, "Using http v2.0") + try await client.XCTExecute(uri: "/http", method: .get) { response in + XCTAssertEqual(response.status, .ok) + XCTAssertEqual(String(buffer: response.body), "Using http v2.0") + } } } }