Skip to content

Commit

Permalink
Implement an HTTP2 channel setup
Browse files Browse the repository at this point in the history
# Conflicts:
#	Sources/HummingbirdCore/Server/HTTP/HTTP1Channel.swift
#	Sources/HummingbirdCore/Server/HTTP/HTTPChannelHandler.swift
#	Sources/HummingbirdCore/Server/Server.swift
#	Sources/HummingbirdTLS/TLSChannelSetup.swift
#	Sources/PerformanceTest/SimpleHTTP1Channel.swift
  • Loading branch information
Joannis committed Nov 17, 2023
1 parent f48ea56 commit 08a6e60
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 93 deletions.
14 changes: 8 additions & 6 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ let package = Package(
platforms: [.macOS(.v14), .iOS(.v17), .tvOS(.v17)],
products: [
.library(name: "Hummingbird", targets: ["Hummingbird"]),
.library(name: "HummingbirdHTTP2", targets: ["HummingbirdHTTP2"]),
.library(name: "HummingbirdCore", targets: ["HummingbirdCore"]),
.library(name: "HummingbirdFoundation", targets: ["HummingbirdFoundation"]),
.library(name: "HummingbirdJobs", targets: ["HummingbirdJobs"]),
Expand Down Expand Up @@ -74,19 +75,20 @@ let package = Package(
.product(name: "NIOPosix", package: "swift-nio"),
.product(name: "NIOSSL", package: "swift-nio-ssl"),
]),
/* .target(name: "HummingbirdHTTP2", dependencies: [
.byName(name: "HummingbirdCore"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOHTTP2", package: "swift-nio-http2"),
.product(name: "NIOSSL", package: "swift-nio-ssl"),
]),*/
.target(name: "HummingbirdHTTP2", dependencies: [
.byName(name: "HummingbirdCore"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOHTTP2", package: "swift-nio-http2"),
.product(name: "NIOSSL", package: "swift-nio-ssl"),
]),
.target(name: "HummingbirdTLS", dependencies: [
.byName(name: "HummingbirdCore"),
.product(name: "NIOCore", package: "swift-nio"),
.product(name: "NIOSSL", package: "swift-nio-ssl"),
]),
.executableTarget(name: "PerformanceTest", dependencies: [
.byName(name: "Hummingbird"),
.byName(name: "HummingbirdHTTP2"),
.byName(name: "HummingbirdFoundation"),
.product(name: "NIOPosix", package: "swift-nio"),
]),
Expand Down
87 changes: 0 additions & 87 deletions Sources/HummingbirdHTTP2/ChannelInitializer.swift

This file was deleted.

97 changes: 97 additions & 0 deletions Sources/HummingbirdHTTP2/HTTP2Channel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Hummingbird server framework project
//
// Copyright (c) 2023 the Hummingbird authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See hummingbird/CONTRIBUTORS.txt for the list of Hummingbird authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import Logging
import NIOCore
import NIOPosix
import NIOHTTP1
import NIOHTTP2
import Hummingbird
import HummingbirdCore
import NIOSSL

public struct HTTP2Channel: HTTPChannelHandler {
public typealias Value = EventLoopFuture<NIONegotiatedHTTPVersion<HTTP1Channel.Value, (NIOAsyncChannel<HTTP2Frame, HTTP2Frame>, NIOHTTP2Handler.AsyncStreamMultiplexer<HTTP1Channel.Value>)>>

private var tlsConfiguration: TLSConfiguration
private var http1: HTTP1Channel
public var responder: @Sendable (HBHTTPRequest, Channel) async throws -> HBHTTPResponse {
get { http1.responder }
set { http1.responder = newValue }
}

public init(
tlsConfiguration: TLSConfiguration,
http1: HTTP1Channel = HTTP1Channel(),
responder: @escaping @Sendable (HBHTTPRequest, Channel) async throws -> HBHTTPResponse = { _, _ in throw HBHTTPError(.notImplemented) }
) {
self.tlsConfiguration = tlsConfiguration
self.http1 = http1
// self.additionalChannelHandlers = additionalChannelHandlers
self.responder = responder
}

public func initialize(channel: Channel, configuration: HBServerConfiguration, logger: Logger) -> EventLoopFuture<Value> {
channel.eventLoop.flatSubmit {
do {
let sslContext = try NIOSSLContext(configuration: tlsConfiguration)
try channel.pipeline.syncOperations.addHandler(NIOSSLServerHandler(context: sslContext))
} catch {
return channel.eventLoop.makeFailedFuture(error)
}

return channel.configureAsyncHTTPServerPipeline { http1Channel -> EventLoopFuture<HTTP1Channel.Value> in
http1Channel
.pipeline
.addHandler(HBHTTPSendableResponseChannelHandler())
.flatMapThrowing {
try HTTP1Channel.Value(synchronouslyWrapping: http1Channel)
}
} http2ConnectionInitializer: { http2Channel -> EventLoopFuture<NIOAsyncChannel<HTTP2Frame, HTTP2Frame>> in
http2Channel.eventLoop.makeCompletedFuture {
try NIOAsyncChannel<HTTP2Frame, HTTP2Frame>(synchronouslyWrapping: http2Channel)
}
} http2StreamInitializer: { http2ChildChannel -> EventLoopFuture<HTTP1Channel.Value> in
http2ChildChannel
.pipeline
.addHandlers(HTTP2FramePayloadToHTTP1ServerCodec(), HBHTTPSendableResponseChannelHandler())
.flatMapThrowing {
try HTTP1Channel.Value(synchronouslyWrapping: http2ChildChannel)
}
}
}
}

public func handle(value: Value, logger: Logger) async {
do {
let channel = try await value.get()
switch channel {
case .http1_1(let http1):
await handleHTTP(asyncChannel: http1, logger: logger)
case .http2((let http2, let multiplexer)):
try await withThrowingDiscardingTaskGroup { group in
for try await client in multiplexer.inbound {
group.addTask {
await handleHTTP(asyncChannel: client, logger: logger)
}
}
}

try await http2.channel.close()
}
} catch {
logger.error("Error handling inbound connection for HTTP2 handler: \(error)")
}
}
}
10 changes: 10 additions & 0 deletions Sources/PerformanceTest/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@

import Hummingbird
import HummingbirdFoundation
import HummingbirdHTTP2
import NIOTLS
import NIOPosix

// get environment
Expand Down Expand Up @@ -44,6 +46,14 @@ router.get("json") { _, _ in

var app = HBApplication(
responder: router.buildResponder(),
channelSetup: try HTTP2Channel(
tlsConfiguration: .makeServerConfiguration(
certificateChain: [
.certificate(.init(file: "/Users/joannisorlandos/git/hummingbird/hummingbird/cert.pem", format: .pem))
],
privateKey: .file("/Users/joannisorlandos/git/hummingbird/hummingbird/key.pem")
)
),
configuration: .init(
address: .hostname(hostname, port: port),
serverName: "Hummingbird"
Expand Down

0 comments on commit 08a6e60

Please sign in to comment.