Provide a nicer setup helper for HTTP connections

This commit is contained in:
Cory Benfield 2017-12-13 15:38:40 +00:00
parent 4154a3bcbc
commit 3614d09a4b
4 changed files with 74 additions and 54 deletions

View File

@ -110,8 +110,48 @@ private protocol AnyHTTPDecoder: class {
func popRequestMethod() -> HTTPMethod?
}
public extension ChannelPipeline {
/// Configure a `ChannelPipeline` for use as a HTTP server.
///
/// - parameters:
/// - first: Whether to add the HTTP server at the head of the channel pipeline,
/// or at the tail.
/// - returns: An `EventLoopFuture` that will fire when the pipeline is configured.
public func addHTTPServerHandlers(first: Bool = false) -> EventLoopFuture<Void> {
return addHandlers(HTTPResponseEncoder(), HTTPRequestDecoder(), first: first)
}
/// Configure a `ChannelPipeline` for use as a HTTP client.
///
/// - parameters:
/// - first: Whether to add the HTTP client at the head of the channel pipeline,
/// or at the tail.
/// - returns: An `EventLoopFuture` that will fire when the pipeline is configured.
public func addHTTPClientHandlers(first: Bool = false) -> EventLoopFuture<Void> {
return addHandlers(HTTPRequestEncoder(), HTTPResponseDecoder(), first: first)
}
/// Adds the provided channel handlers to the pipeline in the order given, taking account
/// of the behaviour of `ChannelHandler.add(first:)`.
private func addHandlers(_ handlers: ChannelHandler..., first: Bool) -> EventLoopFuture<Void> {
var handlers = handlers
if first {
handlers = handlers.reversed()
}
return EventLoopFuture<Void>.andAll(handlers.map { add(handler: $0) }, eventLoop: eventLoop)
}
}
/// A `ChannelInboundHandler` used to decode HTTP requests. See the documentation
/// on `HTTPDecoder` for more.
///
/// While the `HTTPRequestDecoder` does not currently have a specific ordering requirement in the
/// `ChannelPipeline` (unlike `HTTPResponseDecoder`), it is possible that it will develop one. For
/// that reason, applications should try to ensure that the `HTTPRequestDecoder` *later* in the
/// `ChannelPipeline` than the `HTTPResponseEncoder`.
///
/// Rather than set this up manually, consider using `ChannelPipeline.addHTTPServerHandlers`.
public final class HTTPRequestDecoder: HTTPDecoder<HTTPServerRequestPart> {
public convenience init() {
self.init(type: HTTPServerRequestPart.self)
@ -120,6 +160,12 @@ public final class HTTPRequestDecoder: HTTPDecoder<HTTPServerRequestPart> {
/// A `ChannelInboundHandler` used to decode HTTP responses. See the documentation
/// on `HTTPDecoder` for more.
///
/// The `HTTPResponseDecoder` must be placed later in the channel pipeline than the `HTTPRequestEncoder`,
/// as it needs to see the outbound messages in order to keep track of what the HTTP request methods
/// were for accurate decoding.
///
/// Rather than set this up manually, consider using `ChannelPipeline.addHTTPClientHandlers`.
public final class HTTPResponseDecoder: HTTPDecoder<HTTPClientResponsePart>, ChannelOutboundHandler {
public typealias OutboundIn = HTTPClientRequestPart
public typealias OutboundOut = HTTPClientRequestPart

View File

@ -331,11 +331,9 @@ let bootstrap = ServerBootstrap(group: group)
// Set the handlers that are applied to the accepted Channels
.handler(childHandler: ChannelInitializer(initChannel: { channel in
return channel.pipeline.add(handler: HTTPResponseEncoder()).then(callback: { v2 in
return channel.pipeline.add(handler: HTTPRequestDecoder()).then(callback: { v2 in
return channel.pipeline.addHTTPServerHandlers().then {
return channel.pipeline.add(handler: HTTPHandler(htdocsPath: htdocs))
})
})
}
}))
// Enable TCP_NODELAY and SO_REUSEADDR for the accepted Channels

View File

@ -341,11 +341,9 @@ class HTTPServerClientTest : XCTestCase {
// Set the handlers that are appled to the accepted Channels
.handler(childHandler: ChannelInitializer(initChannel: { channel in
// Ensure we not read faster then we can write by adding the BackPressureHandler into the pipeline.
channel.pipeline.add(handler: HTTPRequestDecoder()).then {
channel.pipeline.add(handler: HTTPResponseEncoder()).then {
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
}
})).bind(to: "127.0.0.1", on: 0).wait()
defer {
@ -354,11 +352,9 @@ class HTTPServerClientTest : XCTestCase {
let clientChannel = try ClientBootstrap(group: group)
.handler(handler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestEncoder()).then {
channel.pipeline.add(handler: HTTPResponseDecoder()).then {
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}
}))
.connect(to: serverChannel.localAddress!)
.wait()
@ -403,11 +399,9 @@ class HTTPServerClientTest : XCTestCase {
// Set the handlers that are appled to the accepted Channels
.handler(childHandler: ChannelInitializer(initChannel: { channel in
// Ensure we not read faster then we can write by adding the BackPressureHandler into the pipeline.
channel.pipeline.add(handler: HTTPRequestDecoder()).then {
channel.pipeline.add(handler: HTTPResponseEncoder()).then {
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
}
})).bind(to: "127.0.0.1", on: 0).wait()
defer {
@ -416,11 +410,9 @@ class HTTPServerClientTest : XCTestCase {
let clientChannel = try ClientBootstrap(group: group)
.handler(handler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestEncoder()).then {
channel.pipeline.add(handler: HTTPResponseDecoder()).then {
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}
}))
.connect(to: serverChannel.localAddress!)
.wait()
@ -465,11 +457,9 @@ class HTTPServerClientTest : XCTestCase {
let serverChannel = try ServerBootstrap(group: group)
.option(option: ChannelOptions.Socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.handler(childHandler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestDecoder()).then {
channel.pipeline.add(handler: HTTPResponseEncoder()).then {
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
}
})).bind(to: "127.0.0.1", on: 0).wait()
defer {
@ -478,11 +468,9 @@ class HTTPServerClientTest : XCTestCase {
let clientChannel = try ClientBootstrap(group: group)
.handler(handler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestEncoder()).then {
channel.pipeline.add(handler: HTTPResponseDecoder()).then {
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}
}))
.connect(to: serverChannel.localAddress!)
.wait()
@ -529,11 +517,9 @@ class HTTPServerClientTest : XCTestCase {
// Set the handlers that are appled to the accepted Channels
.handler(childHandler: ChannelInitializer(initChannel: { channel in
// Ensure we not read faster then we can write by adding the BackPressureHandler into the pipeline.
channel.pipeline.add(handler: HTTPRequestDecoder()).then {
channel.pipeline.add(handler: HTTPResponseEncoder()).then {
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
}
})).bind(to: "127.0.0.1", on: 0).wait()
defer {
@ -573,11 +559,9 @@ class HTTPServerClientTest : XCTestCase {
let serverChannel = try ServerBootstrap(group: group)
.option(option: ChannelOptions.Socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.handler(childHandler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestDecoder()).then {
channel.pipeline.add(handler: HTTPResponseEncoder()).then {
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
}
})).bind(to: "127.0.0.1", on: 0).wait()
defer {
_ = serverChannel.close()
@ -585,11 +569,9 @@ class HTTPServerClientTest : XCTestCase {
let clientChannel = try ClientBootstrap(group: group)
.handler(handler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestEncoder()).then {
channel.pipeline.add(handler: HTTPResponseDecoder()).then {
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}
}))
.connect(to: serverChannel.localAddress!)
.wait()
@ -622,11 +604,9 @@ class HTTPServerClientTest : XCTestCase {
let serverChannel = try ServerBootstrap(group: group)
.option(option: ChannelOptions.Socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.handler(childHandler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestDecoder()).then {
channel.pipeline.add(handler: HTTPResponseEncoder()).then {
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
}
})).bind(to: "127.0.0.1", on: 0).wait()
defer {
_ = serverChannel.close()
@ -634,11 +614,9 @@ class HTTPServerClientTest : XCTestCase {
let clientChannel = try ClientBootstrap(group: group)
.handler(handler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestEncoder()).then {
channel.pipeline.add(handler: HTTPResponseDecoder()).then {
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}
}))
.connect(to: serverChannel.localAddress!)
.wait()

View File

@ -31,12 +31,10 @@ private func serverHTTPChannel(group: EventLoopGroup, handlers: [ChannelHandler]
return try! ServerBootstrap(group: group)
.option(option: ChannelOptions.Socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1)
.handler(childHandler: ChannelInitializer(initChannel: { channel in
channel.pipeline.add(handler: HTTPRequestDecoder()).then {
channel.pipeline.add(handler: HTTPResponseEncoder()).then {
channel.pipeline.addHTTPServerHandlers().then {
let futureResults = handlers.map { channel.pipeline.add(handler: $0) }
return EventLoopFuture<Void>.andAll(futureResults, eventLoop: channel.eventLoop)
}
}
})).bind(to: "127.0.0.1", on: 0).wait()
}