diff --git a/.swift-version b/.swift-version index f2c6cb6a..819e07a2 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -4.2.3 +5.0 diff --git a/.travis.yml b/.travis.yml index 65f9321a..24a14f72 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,43 +15,25 @@ matrix: dist: xenial sudo: required services: docker - env: DOCKER_IMAGE=swift:4.0.3 SWIFT_SNAPSHOT=4.0.3 CUSTOM_TEST_SCRIPT=.kitura-test.sh DOCKER_PRIVILEGED=true + env: DOCKER_IMAGE=swift:5.0-xenial DOCKER_PRIVILEGED=true SWIFT_TEST_ARGS="--parallel" - os: linux dist: xenial sudo: required services: docker - env: DOCKER_IMAGE=swift:4.1.3 SWIFT_SNAPSHOT=4.1.3 CUSTOM_TEST_SCRIPT=.kitura-test.sh DOCKER_PRIVILEGED=true + env: DOCKER_IMAGE=swift:5.0 DOCKER_PRIVILEGED=true SWIFT_TEST_ARGS="--parallel" - os: linux dist: xenial sudo: required services: docker - env: DOCKER_IMAGE=swift:4.2.3 SWIFT_TEST_ARGS="--parallel" DOCKER_PRIVILEGED=true - - os: linux - dist: xenial - sudo: required - services: docker - env: DOCKER_IMAGE=swift:4.2.3 SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT DOCKER_PRIVILEGED=true - - os: linux - dist: xenial - sudo: required - services: docker - env: DOCKER_IMAGE=ubuntu:18.04 DOCKER_PRIVILEGED=true - - os: osx - osx_image: xcode9.2 - sudo: required - env: SWIFT_SNAPSHOT=4.0.3 BREW_INSTALL_PACKAGES="libressl" CUSTOM_TEST_SCRIPT=.kitura-test.sh - - os: osx - osx_image: xcode9.4 - sudo: required - env: SWIFT_SNAPSHOT=4.1.2 JAZZY_ELIGIBLE=true CODECOV_ELIGIBLE=true BREW_INSTALL_PACKAGES="libressl" + env: DOCKER_IMAGE=swift:5.0 SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT DOCKER_PRIVILEGED=true SWIFT_TEST_ARGS="--parallel" - os: osx - osx_image: xcode10.1 + osx_image: xcode10.2 sudo: required - env: SWIFT_TEST_ARGS="--parallel" BREW_INSTALL_PACKAGES="libressl" + env: JAZZY_ELIGIBLE=true SWIFT_TEST_ARGS="--parallel" - os: osx - osx_image: xcode10.1 + osx_image: xcode10.2 sudo: required - env: SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT BREW_INSTALL_PACKAGES="libressl" CUSTOM_TEST_SCRIPT=.kitura-test.sh + env: SWIFT_SNAPSHOT=$SWIFT_DEVELOPMENT_SNAPSHOT SWIFT_TEST_ARGS="--parallel" before_install: - git clone https://github.com/IBM-Swift/Package-Builder.git diff --git a/Package.swift b/Package.swift index eaae9a15..b13a89af 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:4.0 +// swift-tools-version:5.0 // The swift-tools-version declares the minimum version of Swift required to build this package. /* * Copyright IBM Corporation 2016, 2017, 2018 @@ -28,8 +28,8 @@ let package = Package( ], dependencies: [ // Dependencies declare other packages that this package depends on. - .package(url: "https://github.com/apple/swift-nio.git", from: "1.0.0"), - .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "1.0.1"), + .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), + .package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.0.0"), .package(url: "https://github.com/IBM-Swift/BlueSSLService.git", from: "1.0.0"), .package(url: "https://github.com/IBM-Swift/LoggerAPI.git", from: "1.7.3") ], @@ -41,7 +41,7 @@ let package = Package( dependencies: []), .target( name: "KituraNet", - dependencies: ["NIO", "NIOFoundationCompat", "NIOHTTP1", "NIOOpenSSL", "SSLService", "LoggerAPI", "NIOWebSocket", "CLinuxHelpers"]), + dependencies: ["NIO", "NIOFoundationCompat", "NIOHTTP1", "NIOSSL", "SSLService", "LoggerAPI", "NIOWebSocket", "CLinuxHelpers"]), .testTarget( name: "KituraNetTests", dependencies: ["KituraNet"]) diff --git a/Sources/KituraNet/BufferList.swift b/Sources/KituraNet/BufferList.swift index c770e708..409334d0 100644 --- a/Sources/KituraNet/BufferList.swift +++ b/Sources/KituraNet/BufferList.swift @@ -115,7 +115,7 @@ public class BufferList { */ public func append(bytes: UnsafePointer, length: Int) { - byteBuffer.write(bytes: UnsafeBufferPointer(start: bytes, count: length)) + byteBuffer.writeBytes(UnsafeBufferPointer(start: bytes, count: length)) } /** @@ -130,7 +130,7 @@ public class BufferList { */ public func append(data: Data) { - byteBuffer.write(bytes: data) + byteBuffer.writeBytes(data) } /** diff --git a/Sources/KituraNet/ClientRequest.swift b/Sources/KituraNet/ClientRequest.swift index 456b7ec9..5b0f8ce4 100644 --- a/Sources/KituraNet/ClientRequest.swift +++ b/Sources/KituraNet/ClientRequest.swift @@ -17,7 +17,7 @@ import NIO import NIOHTTP1 import Foundation -import NIOOpenSSL +import NIOSSL import LoggerAPI import Dispatch @@ -159,7 +159,7 @@ public class ClientRequest { /// The current redirection count internal var redirectCount: Int = 0 - private var sslContext: NIOOpenSSL.SSLContext? + private var sslContext: NIOSSLContext? /// Should HTTP/2 protocol be used private var useHTTP2 = false @@ -608,7 +608,7 @@ public class ClientRequest { private func initializeClientBootstrapWithSSL(eventLoopGroup: EventLoopGroup) { if let sslConfig = self.sslConfig { do { - sslContext = try SSLContext(configuration: sslConfig) + sslContext = try NIOSSLContext(configuration: sslConfig) } catch let error { Log.error("Failed to create SSLContext. Error: \(error)") } @@ -617,9 +617,9 @@ public class ClientRequest { bootstrap = ClientBootstrap(group: eventLoopGroup) .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .channelInitializer { channel in - channel.pipeline.add(handler: try! OpenSSLClientHandler(context: self.sslContext!)).then { - channel.pipeline.addHTTPClientHandlers().then { - channel.pipeline.add(handler: HTTPClientHandler(request: self)) + channel.pipeline.addHandler(try! NIOSSLClientHandler(context: self.sslContext!, serverHostname: nil)).flatMap { + channel.pipeline.addHTTPClientHandlers().flatMap { + channel.pipeline.addHandler(HTTPClientHandler(request: self)) } } } @@ -629,8 +629,8 @@ public class ClientRequest { bootstrap = ClientBootstrap(group: eventLoopGroup) .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .channelInitializer { channel in - channel.pipeline.addHTTPClientHandlers().then { - channel.pipeline.add(handler: HTTPClientHandler(request: self)) + channel.pipeline.addHTTPClientHandlers().flatMap { + channel.pipeline.addHandler(HTTPClientHandler(request: self)) } } } @@ -745,19 +745,19 @@ class HTTPClientHandler: ChannelInboundHandler { typealias InboundIn = HTTPClientResponsePart /// Read the header, body and trailer. Redirection is handled in the trailer case. - func channelRead(ctx: ChannelHandlerContext, data: NIOAny) { + func channelRead(context: ChannelHandlerContext, data: NIOAny) { let response = self.unwrapInboundIn(data) switch response { case .head(let header): clientResponse.httpHeaders = header.headers - clientResponse.httpVersionMajor = header.version.major - clientResponse.httpVersionMinor = header.version.minor + clientResponse.httpVersionMajor = UInt16(header.version.major) + clientResponse.httpVersionMinor = UInt16(header.version.minor) clientResponse.statusCode = HTTPStatusCode(rawValue: Int(header.status.code))! case .body(var buffer): if clientResponse.buffer == nil { clientResponse.buffer = BufferList(with: buffer) } else { - clientResponse.buffer!.byteBuffer.write(buffer: &buffer) + clientResponse.buffer!.byteBuffer.writeBuffer(&buffer) } case .end: // Handle redirection diff --git a/Sources/KituraNet/HTTP/HTTPRequestHandler.swift b/Sources/KituraNet/HTTP/HTTPRequestHandler.swift index 43e3c159..a10f08c7 100644 --- a/Sources/KituraNet/HTTP/HTTPRequestHandler.swift +++ b/Sources/KituraNet/HTTP/HTTPRequestHandler.swift @@ -21,7 +21,7 @@ import LoggerAPI import Foundation import Dispatch -internal class HTTPRequestHandler: ChannelInboundHandler { +internal class HTTPRequestHandler: ChannelInboundHandler, RemovableChannelHandler { /// The HTTPServer instance on which this handler is installed var server: HTTPServer @@ -70,7 +70,7 @@ internal class HTTPRequestHandler: ChannelInboundHandler { public typealias InboundIn = HTTPServerRequestPart public typealias OutboundOut = HTTPServerResponsePart - public func channelRead(ctx: ChannelHandlerContext, data: NIOAny) { + public func channelRead(context: ChannelHandlerContext, data: NIOAny) { let request = self.unwrapInboundIn(data) // If an error response was already sent, we'd want to spare running through this for now. // If an upgrade to WebSocket fails, both `errorCaught` and `channelRead` are triggered. @@ -79,7 +79,7 @@ internal class HTTPRequestHandler: ChannelInboundHandler { switch request { case .head(let header): - serverRequest = HTTPServerRequest(ctx: ctx, requestHead: header, enableSSL: enableSSLVerification) + serverRequest = HTTPServerRequest(ctx: context, requestHead: header, enableSSL: enableSSLVerification) self.clientRequestedKeepAlive = header.isKeepAlive case .body(var buffer): guard let serverRequest = serverRequest else { @@ -89,10 +89,10 @@ internal class HTTPRequestHandler: ChannelInboundHandler { if serverRequest.buffer == nil { serverRequest.buffer = BufferList(with: buffer) } else { - serverRequest.buffer!.byteBuffer.write(buffer: &buffer) + serverRequest.buffer!.byteBuffer.writeBuffer(&buffer) } case .end: - serverResponse = HTTPServerResponse(channel: ctx.channel, handler: self) + serverResponse = HTTPServerResponse(channel: context.channel, handler: self) //Make sure we use the latest delegate registered with the server DispatchQueue.global().async { guard let serverRequest = self.serverRequest, let serverResponse = self.serverResponse else { return } @@ -104,17 +104,17 @@ internal class HTTPRequestHandler: ChannelInboundHandler { } //IdleStateEvents are received on this method - public func userInboundEventTriggered(ctx: ChannelHandlerContext, event: Any) { + public func userInboundEventTriggered(context: ChannelHandlerContext, event: Any) { if event is IdleStateHandler.IdleStateEvent { - _ = ctx.close() + _ = context.close() } } - public func channelReadComplete(ctx: ChannelHandlerContext) { - ctx.flush() + public func channelReadComplete(context: ChannelHandlerContext) { + context.flush() } - public func errorCaught(ctx: ChannelHandlerContext, error: Error) { + public func errorCaught(context: ChannelHandlerContext, error: Error) { guard !errorResponseSent else { return } var message: String? switch error { @@ -128,18 +128,20 @@ internal class HTTPRequestHandler: ChannelInboundHandler { break case KituraWebSocketUpgradeError.invalidVersionHeader(_): message = "Only WebSocket protocol version 13 is supported" - case NIOWebSocketUpgradeError.unsupportedWebSocketTarget: - let target = server.latestWebSocketURI ?? "/" - message = "No service has been registered for the path \(target)" default: - // Don't handle any other errors, including `HTTPParserError`s. - // We could log an error message here. - ctx.close(promise: nil) + // Handle only NIOWebSocketUpgradeError here, nothing else + if let upgradeError = error as? NIOWebSocketUpgradeError, upgradeError == .unsupportedWebSocketTarget { + let target = server.latestWebSocketURI ?? "/" + message = "No service has been registered for the path \(target)" + return + } + // TODO: Do we log an error message here? + context.close(promise: nil) return } do { - serverResponse = HTTPServerResponse(channel: ctx.channel, handler: self) + serverResponse = HTTPServerResponse(channel: context.channel, handler: self) errorResponseSent = true try serverResponse?.end(with: .badRequest, message: message) } catch { diff --git a/Sources/KituraNet/HTTP/HTTPServer.swift b/Sources/KituraNet/HTTP/HTTPServer.swift index 3b301b8e..9ebb8cd8 100644 --- a/Sources/KituraNet/HTTP/HTTPServer.swift +++ b/Sources/KituraNet/HTTP/HTTPServer.swift @@ -17,7 +17,7 @@ import NIO import NIOHTTP1 import Dispatch -import NIOOpenSSL +import NIOSSL import SSLService import LoggerAPI import NIOWebSocket @@ -158,19 +158,19 @@ public class HTTPServer: Server { } } - /// NIOOpenSSL.TLSConfiguration used with the ServerBootstrap + /// NIOSSL.TLSConfiguration used with the ServerBootstrap private var tlsConfig: TLSConfiguration? /// The SSLContext built using the TLSConfiguration - private var sslContext: NIOOpenSSL.SSLContext? + private var sslContext: NIOSSLContext? /// URI for which the latest WebSocket upgrade was requested by a client var latestWebSocketURI: String? /// Determines if the request should be upgraded and adds additional upgrade headers to the request - private func shouldUpgradeToWebSocket(webSocketHandlerFactory: ProtocolHandlerFactory, head: HTTPRequestHead) -> HTTPHeaders? { + private func shouldUpgradeToWebSocket(channel: Channel, webSocketHandlerFactory: ProtocolHandlerFactory, head: HTTPRequestHead) -> EventLoopFuture { self.latestWebSocketURI = head.uri - guard webSocketHandlerFactory.isServiceRegistered(at: head.uri) else { return nil } + guard webSocketHandlerFactory.isServiceRegistered(at: head.uri) else { return channel.eventLoop.makeSucceededFuture(nil) } var headers = HTTPHeaders() if let wsProtocol = head.headers["Sec-WebSocket-Protocol"].first { headers.add(name: "Sec-WebSocket-Protocol", value: wsProtocol) @@ -181,7 +181,7 @@ public class HTTPServer: Server { if let _extension = head.headers["Sec-WebSocket-Extensions"].first { headers.add(name: "Sec-WebSocket-Extensions", value: webSocketHandlerFactory.negotiate(header: _extension)) } - return headers + return channel.eventLoop.makeSucceededFuture(headers) } /// Creates upgrade request and adds WebSocket handler to pipeline @@ -190,23 +190,23 @@ public class HTTPServer: Server { ///TODO: Handle secure upgrade request ("wss://") let serverRequest = HTTPServerRequest(ctx: ctx, requestHead: request, enableSSL: false) let websocketConnectionHandler = webSocketHandlerFactory.handler(for: serverRequest) - let future = ctx.channel.pipeline.add(handler: websocketConnectionHandler) + let future = ctx.channel.pipeline.addHandler(websocketConnectionHandler) if let _extensions = request.headers["Sec-WebSocket-Extensions"].first { for handler in webSocketHandlerFactory.extensionHandlers(header: _extensions) { - _ = future.then { - ctx.channel.pipeline.add(handler: handler, before: websocketConnectionHandler) + _ = future.flatMap { + ctx.channel.pipeline.addHandler(handler, position: .before(websocketConnectionHandler)) } } } return future } - private typealias ShouldUpgradeFunction = (HTTPRequestHead) -> HTTPHeaders? + private typealias ShouldUpgradeFunction = (Channel, HTTPRequestHead) -> EventLoopFuture private typealias UpgradePipelineHandlerFunction = (Channel, HTTPRequestHead) -> EventLoopFuture private func generateShouldUpgrade(_ webSocketHandlerFactory: ProtocolHandlerFactory) -> ShouldUpgradeFunction { - return { (head: HTTPRequestHead) in - return self.shouldUpgradeToWebSocket(webSocketHandlerFactory: webSocketHandlerFactory, head: head) + return { (channel: Channel, head: HTTPRequestHead) in + return self.shouldUpgradeToWebSocket(channel: channel, webSocketHandlerFactory: webSocketHandlerFactory, head: head) } } @@ -216,12 +216,12 @@ public class HTTPServer: Server { } } - private func createOpenSSLServerHandler() -> OpenSSLServerHandler? { + private func createNIOSSLServerHandler() -> NIOSSLServerHandler? { if let sslContext = self.sslContext { do { - return try OpenSSLServerHandler(context: sslContext) + return try NIOSSLServerHandler(context: sslContext) } catch let error { - Log.error("Failed to create OpenSSLServerHandler. Error: \(error)") + Log.error("Failed to create NIOSSLServerHandler. Error: \(error)") } } return nil @@ -269,13 +269,13 @@ public class HTTPServer: Server { if let tlsConfig = tlsConfig { do { - self.sslContext = try SSLContext(configuration: tlsConfig) + self.sslContext = try NIOSSLContext(configuration: tlsConfig) } catch let error { Log.error("Failed to create SSLContext. Error: \(error)") } } - var upgraders: [HTTPProtocolUpgrader] = [] + var upgraders: [HTTPServerProtocolUpgrader] = [] if let webSocketHandlerFactory = ConnectionUpgrader.getProtocolHandlerFactory(for: "websocket") { ///TODO: Should `maxFrameSize` be configurable? let upgrader = KituraWebSocketUpgrader(maxFrameSize: 1 << 24, @@ -286,20 +286,20 @@ public class HTTPServer: Server { } let bootstrap = ServerBootstrap(group: eventLoopGroup) - .serverChannelOption(ChannelOptions.backlog, value: BacklogOption.OptionType(self.maxPendingConnections)) + .serverChannelOption(ChannelOptions.backlog, value: BacklogOption.Value(self.maxPendingConnections)) .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEPORT), value: allowPortReuse ? 1 : 0) .childChannelInitializer { channel in let httpHandler = HTTPRequestHandler(for: self) let config: HTTPUpgradeConfiguration = (upgraders: upgraders, completionHandler: { ctx in self.ctx = ctx - _ = channel.pipeline.remove(handler: httpHandler) + _ = channel.pipeline.removeHandler(httpHandler) }) - return channel.pipeline.configureHTTPServerPipeline(withServerUpgrade: config, withErrorHandling: true).then { - if let openSSLServerHandler = self.createOpenSSLServerHandler() { - _ = channel.pipeline.add(handler: openSSLServerHandler, first: true) + return channel.pipeline.configureHTTPServerPipeline(withServerUpgrade: config, withErrorHandling: true).flatMap { + if let nioSSLServerHandler = self.createNIOSSLServerHandler() { + _ = channel.pipeline.addHandler(nioSSLServerHandler, position: .first) } - return channel.pipeline.add(handler: httpHandler) + return channel.pipeline.addHandler(httpHandler) } } .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) @@ -568,16 +568,17 @@ class HTTPDummyServerDelegate: ServerDelegate { // This work-around will have to be removed once the limitation is removed from swift-nio, possibly in version 2.0 // TODO: Re-evaluate the need for this class when swift-nio 2.0 is released. -final class KituraWebSocketUpgrader: HTTPProtocolUpgrader { +final class KituraWebSocketUpgrader: HTTPServerProtocolUpgrader { private let _wrappedUpgrader: WebSocketUpgrader - public init(maxFrameSize: Int, automaticErrorHandling: Bool = true, shouldUpgrade: @escaping (HTTPRequestHead) -> HTTPHeaders?, + public init(maxFrameSize: Int, automaticErrorHandling: Bool = true, + shouldUpgrade: @escaping (Channel, HTTPRequestHead) -> EventLoopFuture, upgradePipelineHandler: @escaping (Channel, HTTPRequestHead) -> EventLoopFuture) { _wrappedUpgrader = WebSocketUpgrader(maxFrameSize: maxFrameSize, automaticErrorHandling: automaticErrorHandling, shouldUpgrade: shouldUpgrade, upgradePipelineHandler: upgradePipelineHandler) } - public convenience init(automaticErrorHandling: Bool = true, shouldUpgrade: @escaping (HTTPRequestHead) -> HTTPHeaders?, + public convenience init(automaticErrorHandling: Bool = true, shouldUpgrade: @escaping (Channel, HTTPRequestHead) -> EventLoopFuture, upgradePipelineHandler: @escaping (Channel, HTTPRequestHead) -> EventLoopFuture) { self.init(maxFrameSize: 1 << 14, automaticErrorHandling: automaticErrorHandling, shouldUpgrade: shouldUpgrade, upgradePipelineHandler: upgradePipelineHandler) @@ -591,35 +592,38 @@ final class KituraWebSocketUpgrader: HTTPProtocolUpgrader { return _wrappedUpgrader.requiredUpgradeHeaders } - public func buildUpgradeResponse(upgradeRequest: HTTPRequestHead, initialResponseHeaders: HTTPHeaders) throws -> HTTPHeaders { - do { - return try _wrappedUpgrader.buildUpgradeResponse(upgradeRequest: upgradeRequest, initialResponseHeaders: initialResponseHeaders) - } catch { - if case NIOWebSocketUpgradeError.invalidUpgradeHeader = error { - let keyHeader = upgradeRequest.headers[canonicalForm: "Sec-WebSocket-Key"] - let versionHeader = upgradeRequest.headers[canonicalForm: "Sec-WebSocket-Version"] - - if keyHeader.count == 0 { - throw KituraWebSocketUpgradeError.noWebSocketKeyHeader - } else if keyHeader.count > 1 { - throw KituraWebSocketUpgradeError.invalidKeyHeaderCount(keyHeader.count) - } else if versionHeader.count == 0 { - throw KituraWebSocketUpgradeError.noWebSocketVersionHeader - } else if versionHeader.count > 1 { - throw KituraWebSocketUpgradeError.invalidVersionHeaderCount(versionHeader.count) - } else if versionHeader.first! != "13" { - throw KituraWebSocketUpgradeError.invalidVersionHeader(versionHeader.first!) - } else { - throw error - } + public func buildUpgradeResponse(channel: Channel, upgradeRequest: HTTPRequestHead, initialResponseHeaders: HTTPHeaders) -> EventLoopFuture { + let future = _wrappedUpgrader.buildUpgradeResponse(channel: channel, upgradeRequest: upgradeRequest, initialResponseHeaders: initialResponseHeaders) + return future.flatMapError { error in + guard let upgradeError = error as? NIOWebSocketUpgradeError, upgradeError == NIOWebSocketUpgradeError.invalidUpgradeHeader else { + return channel.eventLoop.makeFailedFuture(error) + } + + let keyHeader = upgradeRequest.headers[canonicalForm: "Sec-WebSocket-Key"] + let versionHeader = upgradeRequest.headers[canonicalForm: "Sec-WebSocket-Version"] + + var error: KituraWebSocketUpgradeError + if keyHeader.count == 0 { + error = KituraWebSocketUpgradeError.noWebSocketKeyHeader + } else if keyHeader.count > 1 { + error = KituraWebSocketUpgradeError.invalidKeyHeaderCount(keyHeader.count) + } else if versionHeader.count == 0 { + error = KituraWebSocketUpgradeError.noWebSocketVersionHeader + } else if versionHeader.count > 1 { + error = KituraWebSocketUpgradeError.invalidVersionHeaderCount(versionHeader.count) + } else if versionHeader.first! != "13" { + error = KituraWebSocketUpgradeError.invalidVersionHeader(String(versionHeader.first!)) } else { - throw error + error = KituraWebSocketUpgradeError.unknownUpgradeError } + return channel.eventLoop.makeFailedFuture(error) + }.flatMap { value in + return channel.eventLoop.makeSucceededFuture(value) } } - public func upgrade(ctx: ChannelHandlerContext, upgradeRequest: HTTPRequestHead) -> EventLoopFuture { - return _wrappedUpgrader.upgrade(ctx: ctx, upgradeRequest: upgradeRequest) + public func upgrade(context: ChannelHandlerContext, upgradeRequest: HTTPRequestHead) -> EventLoopFuture { + return _wrappedUpgrader.upgrade(context: context, upgradeRequest: upgradeRequest) } } @@ -640,4 +644,7 @@ enum KituraWebSocketUpgradeError: Error { // The Sec-WebSocket-Version is not 13 case invalidVersionHeader(String) + + // Unknown upgrade error + case unknownUpgradeError } diff --git a/Sources/KituraNet/HTTP/HTTPServerRequest.swift b/Sources/KituraNet/HTTP/HTTPServerRequest.swift index 66295e60..7beeadda 100644 --- a/Sources/KituraNet/HTTP/HTTPServerRequest.swift +++ b/Sources/KituraNet/HTTP/HTTPServerRequest.swift @@ -118,7 +118,7 @@ public class HTTPServerRequest: ServerRequest { _urlComponents?.scheme = self.enableSSL ? "https" : "http" var localAddress = "" - var localAddressPort = UInt16(0) + var localAddressPort = 0 do { try ctx.eventLoop.runAndWait { @@ -246,8 +246,8 @@ public class HTTPServerRequest: ServerRequest { self.ctx = ctx self.headers = HeadersContainer(with: requestHead.headers) self.method = requestHead.method.string() - self.httpVersionMajor = requestHead.version.major - self.httpVersionMinor = requestHead.version.minor + self.httpVersionMajor = UInt16(requestHead.version.major) + self.httpVersionMinor = UInt16(requestHead.version.minor) self.rawURLString = requestHead.uri self.enableSSL = enableSSL } @@ -388,6 +388,8 @@ extension HTTPMethod { return "MKACTIVITY" case .UNSUBSCRIBE: return "UNSUBSCRIBE" + case .SOURCE: + return "SOURCE" case .RAW(let value): return value } diff --git a/Sources/KituraNet/HTTP/HTTPServerResponse.swift b/Sources/KituraNet/HTTP/HTTPServerResponse.swift index 8eb49bd6..910ec3b5 100644 --- a/Sources/KituraNet/HTTP/HTTPServerResponse.swift +++ b/Sources/KituraNet/HTTP/HTTPServerResponse.swift @@ -96,8 +96,8 @@ public class HTTPServerResponse: ServerResponse { self.channel = channel self.handler = handler self.buffer = channel.allocator.buffer(capacity: HTTPServerResponse.bufferSize) - let httpVersionMajor = handler.serverRequest?.httpVersionMajor ?? 1 - let httpVersionMinor = handler.serverRequest?.httpVersionMinor ?? 1 + let httpVersionMajor = Int(handler.serverRequest?.httpVersionMajor ?? 1) + let httpVersionMinor = Int(handler.serverRequest?.httpVersionMinor ?? 1) self.httpVersion = HTTPVersion(major: httpVersionMajor, minor: httpVersionMinor) headers["Date"] = [SPIUtils.httpDate()] } @@ -121,7 +121,7 @@ public class HTTPServerResponse: ServerResponse { } channel.eventLoop.run { - self.buffer.write(string: string) + self.buffer.writeString(string) } } @@ -144,7 +144,7 @@ public class HTTPServerResponse: ServerResponse { } channel.eventLoop.run { - self.buffer.write(bytes: data) + self.buffer.writeBytes(data) } } @@ -228,7 +228,7 @@ public class HTTPServerResponse: ServerResponse { headers["Connection"] = ["Close"] // We want to close this channel after the error response is sent - let responseSentPromise = channel.eventLoop.newPromise(of: Void.self) + let responseSentPromise = channel.eventLoop.makePromise(of: Void.self) channel.eventLoop.run { do { try self.sendResponse(channel: channel, handler: handler, status: status, withBody: withBody, promise: responseSentPromise) @@ -236,7 +236,7 @@ public class HTTPServerResponse: ServerResponse { Log.error("Error sending response: \(error)") //TODO: We must be rethrowing/throwing from here, for which we'd need to add a new Error type to the API } - responseSentPromise.futureResult.whenComplete { + responseSentPromise.futureResult.whenComplete { _ in channel.close(promise: nil) } } diff --git a/Sources/KituraNet/HTTP/SSLConfiguration.swift b/Sources/KituraNet/HTTP/SSLConfiguration.swift index e1170940..a91a2649 100644 --- a/Sources/KituraNet/HTTP/SSLConfiguration.swift +++ b/Sources/KituraNet/HTTP/SSLConfiguration.swift @@ -14,11 +14,11 @@ * limitations under the License. */ -import NIOOpenSSL +import NIOSSL import SSLService import LoggerAPI -/// A helper class to bridge between SSLService.Configuration (used by Kitura) and TLSConfiguration required by NIOOpenSSL +/// A helper class to bridge between SSLService.Configuration (used by Kitura) and TLSConfiguration required by NIOSSL internal class SSLConfiguration { private var certificateFilePath: String? @@ -39,7 +39,7 @@ internal class SSLConfiguration { self.password = sslConfig.password } - /// Convert SSLService.Configuration to NIOOpenSSL.TLSConfiguration + /// Convert SSLService.Configuration to NIOSSL.TLSConfiguration func tlsServerConfig() -> TLSConfiguration? { // TODO: Consider other configuration options if let certificateFilePath = certificateFilePath, let keyFilePath = keyFilePath { @@ -48,8 +48,8 @@ internal class SSLConfiguration { /// TLSConfiguration for PKCS#12 formatted certificate guard let certificateChainFilePath = certificateChainFilePath, let password = password else { return nil } do { - let pkcs12Bundle = try OpenSSLPKCS12Bundle(file: certificateChainFilePath, passphrase: password.utf8) - var sslCertificateSource: [OpenSSLCertificateSource] = [] + let pkcs12Bundle = try NIOSSLPKCS12Bundle(file: certificateChainFilePath, passphrase: password.utf8) + var sslCertificateSource: [NIOSSLCertificateSource] = [] pkcs12Bundle.certificateChain.forEach { sslCertificateSource.append(.certificate($0)) } diff --git a/Tests/KituraNetTests/PipeliningTests.swift b/Tests/KituraNetTests/PipeliningTests.swift index d2df2794..1fedd1c3 100644 --- a/Tests/KituraNetTests/PipeliningTests.swift +++ b/Tests/KituraNetTests/PipeliningTests.swift @@ -57,8 +57,8 @@ class PipeliningTests: KituraNetTest { do { let clientChannel = try ClientBootstrap(group: group) .channelInitializer { channel in - channel.pipeline.addHTTPClientHandlers().then { - channel.pipeline.add(handler: PipelinedRequestsHandler(with: expectation)) + channel.pipeline.addHTTPClientHandlers().flatMap { + channel.pipeline.addHandler(PipelinedRequestsHandler(with: expectation)) } } .channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) @@ -95,8 +95,8 @@ class PipeliningTests: KituraNetTest { do { let clientChannel = try ClientBootstrap(group: group) .channelInitializer { channel in - channel.pipeline.addHTTPClientHandlers().then { - channel.pipeline.add(handler: PipelinedRequestsHandler(with: expectation)) + channel.pipeline.addHTTPClientHandlers().flatMap { + channel.pipeline.addHandler(PipelinedRequestsHandler(with: expectation)) } } .channelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: 1) @@ -160,7 +160,7 @@ private class PipelinedRequestsHandler: ChannelInboundHandler { private var responses: [String] = [] - public func channelRead(ctx: ChannelHandlerContext, data: NIOAny) { + public func channelRead(context: ChannelHandlerContext, data: NIOAny) { let request = self.unwrapInboundIn(data) switch request { case .head: diff --git a/Tests/KituraNetTests/RegressionTests.swift b/Tests/KituraNetTests/RegressionTests.swift index d628cad6..1cd7e842 100644 --- a/Tests/KituraNetTests/RegressionTests.swift +++ b/Tests/KituraNetTests/RegressionTests.swift @@ -20,7 +20,7 @@ import XCTest import Foundation import NIO import NIOHTTP1 -import NIOOpenSSL +import NIOSSL import LoggerAPI class RegressionTests: KituraNetTest { @@ -219,7 +219,7 @@ class RegressionTests: KituraNetTest { } } - /// A simple client based on OpenSSL, which connects to a port and performs + /// A simple client based on NIOSSL, which connects to a port and performs /// an SSL handshake struct GoodClient { let clientBootstrap: ClientBootstrap @@ -231,13 +231,13 @@ class RegressionTests: KituraNetTest { } init() throws { - var openSSLClientHandler: OpenSSLHandler? { + var nioSSLClientHandler: NIOSSLHandler? { let sslConfig = TLSConfiguration.forClient(certificateVerification: .none) do { - let sslContext = try SSLContext(configuration: sslConfig) - return try OpenSSLClientHandler(context: sslContext) + let sslContext = try NIOSSLContext(configuration: sslConfig) + return try NIOSSLClientHandler(context: sslContext, serverHostname: nil) } catch let error { - Log.error("Failed to create OpenSSLClientHandler. Error: \(error)") + Log.error("Failed to create NIOSSLClientHandler. Error: \(error)") return nil } } @@ -245,8 +245,8 @@ class RegressionTests: KituraNetTest { clientBootstrap = ClientBootstrap(group: MultiThreadedEventLoopGroup(numberOfThreads: 1)) .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .channelInitializer { channel in - if let openSSLClientHandler = openSSLClientHandler { - _ = channel.pipeline.add(handler: openSSLClientHandler) + if let nioSSLClientHandler = nioSSLClientHandler { + _ = channel.pipeline.addHandler(nioSSLClientHandler) } return channel.pipeline.addHTTPClientHandlers() } @@ -256,8 +256,8 @@ class RegressionTests: KituraNetTest { clientBootstrap = ClientBootstrap(group: MultiThreadedEventLoopGroup(numberOfThreads: 1)) .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) .channelInitializer { channel in - channel.pipeline.addHTTPClientHandlers().then { - channel.pipeline.add(handler: httpClient) + channel.pipeline.addHTTPClientHandlers().flatMap { + channel.pipeline.addHandler(httpClient) } } } @@ -315,7 +315,7 @@ class HTTPClient: ChannelInboundHandler { private var responses: [String] = [] - public func channelRead(ctx: ChannelHandlerContext, data: NIOAny) { + public func channelRead(context: ChannelHandlerContext, data: NIOAny) { let request = self.unwrapInboundIn(data) switch request { case .head(let header): diff --git a/Tests/KituraNetTests/SSLConfig/SSLConfig.swift b/Tests/KituraNetTests/SSLConfig/SSLConfig.swift index 8dc83c6f..69aa86eb 100644 --- a/Tests/KituraNetTests/SSLConfig/SSLConfig.swift +++ b/Tests/KituraNetTests/SSLConfig/SSLConfig.swift @@ -42,7 +42,7 @@ public struct SSLConfig { /// Initialize an `SSLService.Configuration` instance using a CA certificate directory. /// - /// *Note:* `caCertificateDirPath` - all certificates in the specified directory **must** be hashed using the OpenSSL Certificate Tool. + /// *Note:* `caCertificateDirPath` - all certificates in the specified directory **must** be hashed using the NIOSSL Certificate Tool. /// /// - Parameter caCertificateDirPath: Path to a directory containing CA certificates. *(see note above)* /// - Parameter certificateFilePath: Path to the PEM formatted certificate file. If nil, `certificateFilePath` will be used.