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.add(handler: HTTPHandler(htdocsPath: htdocs))
})
})
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,10 +341,8 @@ 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.add(handler: httpHandler)
}
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
})).bind(to: "127.0.0.1", on: 0).wait()
@ -354,10 +352,8 @@ 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.add(handler: accumulation)
}
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}))
.connect(to: serverChannel.localAddress!)
@ -403,10 +399,8 @@ 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.add(handler: httpHandler)
}
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
})).bind(to: "127.0.0.1", on: 0).wait()
@ -416,10 +410,8 @@ 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.add(handler: accumulation)
}
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}))
.connect(to: serverChannel.localAddress!)
@ -465,10 +457,8 @@ 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.add(handler: httpHandler)
}
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
})).bind(to: "127.0.0.1", on: 0).wait()
@ -478,10 +468,8 @@ 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.add(handler: accumulation)
}
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}))
.connect(to: serverChannel.localAddress!)
@ -529,10 +517,8 @@ 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.add(handler: httpHandler)
}
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
})).bind(to: "127.0.0.1", on: 0).wait()
@ -573,10 +559,8 @@ 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.add(handler: httpHandler)
}
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
})).bind(to: "127.0.0.1", on: 0).wait()
defer {
@ -585,10 +569,8 @@ 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.add(handler: accumulation)
}
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}))
.connect(to: serverChannel.localAddress!)
@ -622,10 +604,8 @@ 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.add(handler: httpHandler)
}
channel.pipeline.addHTTPServerHandlers().then {
channel.pipeline.add(handler: httpHandler)
}
})).bind(to: "127.0.0.1", on: 0).wait()
defer {
@ -634,10 +614,8 @@ 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.add(handler: accumulation)
}
channel.pipeline.addHTTPClientHandlers().then {
channel.pipeline.add(handler: accumulation)
}
}))
.connect(to: serverChannel.localAddress!)

View File

@ -31,11 +31,9 @@ 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 {
let futureResults = handlers.map { channel.pipeline.add(handler: $0) }
return EventLoopFuture<Void>.andAll(futureResults, eventLoop: channel.eventLoop)
}
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()
}