i suppose if server-side swift were easy, everyone would do it…
i’ve currently got an HTTP/1-only server whose ServerBootstrap
does something like:
guard let tls:NIOSSLContext = ...
else
{
// No HTTPS
return channel.pipeline.configureHTTPServerPipeline(withErrorHandling: true)
.flatMap
{
channel.pipeline.addHandler(endpoint)
}
}
return channel.pipeline.addHandler(NIOSSLServerHandler.init(context: tls))
.flatMap
{
// HTTPS
channel.pipeline.configureHTTPServerPipeline(withErrorHandling: true)
.flatMap
{
channel.pipeline.addHandler(endpoint)
}
}
the endpoint
is a channel handler with:
typealias InboundIn = HTTPServerRequestPart
typealias OutboundOut = HTTPServerResponsePart
this works fine.
after wandering aimlessly through the documentation for the two packages involved (both on the swift package index and my own index site), i deduced that Channel.configureCommonHTTPServerPipeline(h2ConnectionChannelConfigurator:_:)
is what i needed, and i changed the previous code to the following:
guard let tls:NIOSSLContext = authority.tls as? NIOSSLContext
else
{
return channel.configureCommonHTTPServerPipeline
{
$0.pipeline.addHandler(endpoint)
}
}
return channel.pipeline.addHandler(NIOSSLServerHandler.init(context: tls))
.flatMap
{
channel.configureCommonHTTPServerPipeline
{
$0.pipeline.addHandler(endpoint)
}
}
sadly, this doesn’t “just work”, the new channel doesn’t respond to anything anymore. what am i doing wrong?
it turns out that HTTP/2 requires (and is negotiated over) TLS, and you need to add HTTP/2 to the list of acceptable protocols in NIOSSLContext
.
posting here in case someone else stumbles upon this thread:
var configuration:TLSConfiguration = .makeServerConfiguration(
certificateChain: certificates.map(NIOSSLCertificateSource.certificate(_:)),
privateKey: .privateKey(privateKey))
configuration.applicationProtocols = ["h2", "http/1.1"]
let niossl:NIOSSLContext = try .init(configuration: configuration)
1 Like
FranzBusch
(Franz Busch)
September 22, 2023, 8:33am
3
Correct. If you want to setup a pipeline that supports both HTTP/1.1 and HTTP/2 then something has to do the protocol negotiation for you. This is mostly done via TLS as you discovered. RFC 9113 also explains how to start an HTTP/2 connection with prior knowledge.
If you are up for the latest async APIs that we are working on, this setups a connection that handles both HTTP/1.1 and HTTP/2 and provides you our new NIOAsyncChannel
based interfaces to handle incoming connections. (Omitted the sslContext creation)
let channel = try await ServerBootstrap(group: eventLoopGroup)
.bind(host: "127.0.0.1", port: 8080) { channel in
return channel.pipeline.addHandler(NIOSSLServerHandler(context: sslContext))
.flatMap {
channel.configureAsyncHTTPServerPipeline { http1ConnectionChannel in
http1ConnectionChannel.eventLoop.makeCompletedFuture {
try http1ConnectionChannel.pipeline.syncOperations.addHandler(HTTP1ToHTTPServerCodec(secure: false))
return try NIOAsyncChannel(
synchronouslyWrapping: http1ConnectionChannel,
configuration: .init(
inboundType: HTTPTypeRequestPart.self,
outboundType: HTTPTypeResponsePart.self
)
)
}
} http2ConnectionInitializer: { http2ConnectionChannel in
http2ConnectionChannel.eventLoop.makeCompletedFuture {
try NIOAsyncChannel(
synchronouslyWrapping: http2ConnectionChannel,
configuration: .init(
inboundType: HTTP2Frame.self,
outboundType: HTTP2Frame.self
)
)
}
} http2InboundStreamInitializer: { http2StreamChannel in
http2StreamChannel.eventLoop.makeCompletedFuture {
try http2StreamChannel.pipeline.syncOperations.addHandler(HTTP2FramePayloadToHTTPServerCodec())
return try NIOAsyncChannel(
synchronouslyWrapping: http2StreamChannel,
configuration: .init(
inboundType: HTTPTypeRequestPart.self,
outboundType: HTTPTypeResponsePart.self
)
)
}
}
}
}
1 Like
i’m sure i’m probably missing something obvious, but i can’t seem to find any of those APIs, e.g. configureAsyncHTTPServerPipeline
.
ironically, a google search for “swift-nio configureAsyncHTTPServerPipeline” directs me back to this thread, which has apparently already become the top search result in the 21 hours since i posted this
EDIT: found it, it looks like it is main-branch only (1.27.0 , main ). any chance we can get a release tagged, so i can use it in SPM?