From fc6e2178bba46b3209709c2e331b3ad4c402e780 Mon Sep 17 00:00:00 2001 From: Aaron Liberatore Date: Fri, 17 Nov 2017 09:13:25 -0600 Subject: [PATCH] Issue.swift402 (#233) * Migrate to Swift 4.0.2 * Removes compilation blocks * Removes Swift 3.1.1 Compilation Blocks * Adds docker-compose --- .gitignore | 1 + .swift-version | 2 +- .travis.yml | 12 +- Package.pins | 5 - Package.swift | 66 ++++-- Package@swift-4.0.swift | 74 ------- Sources/KituraNet/ClientRequest.swift | 2 +- Sources/KituraNet/ConnectionUpgrader.swift | 2 +- .../FastCGI/FastCGIRecordParser.swift | 2 +- .../FastCGI/FastCGIServerRequest.swift | 208 ++++++++---------- Tests/KituraNetTests/ClientE2ETests.swift | 60 ++--- .../KituraNetTests/FastCGIRequestTests.swift | 2 +- Tests/KituraNetTests/LargePayloadTests.swift | 6 +- Tests/KituraNetTests/PrintLogger.swift | 6 +- Tests/LinuxMain.swift | 39 +--- docker-compose.yml | 6 + 16 files changed, 206 insertions(+), 287 deletions(-) delete mode 100644 Package.pins delete mode 100644 Package@swift-4.0.swift create mode 100644 docker-compose.yml diff --git a/.gitignore b/.gitignore index 6521fb3..b737a86 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .DS_Store /build /.build +/.build-ubuntu /Packages /*.xcodeproj Package.resolved diff --git a/.swift-version b/.swift-version index 94ff29c..4d54dad 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.1.1 +4.0.2 diff --git a/.travis.yml b/.travis.yml index 67de2ff..5172d44 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,22 +13,14 @@ matrix: - os: linux dist: trusty sudo: required - - os: linux - dist: trusty - sudo: required - env: SWIFT_SNAPSHOT=4.0 - os: linux dist: trusty sudo: required services: docker - env: DOCKER_IMAGE=ubuntu:16.04 SWIFT_SNAPSHOT=4.0 - - os: osx - osx_image: xcode8.3 - sudo: required + env: DOCKER_IMAGE=ubuntu:16.04 - os: osx - osx_image: xcode9 + osx_image: xcode9.1 sudo: required - env: SWIFT_SNAPSHOT=4.0 script: - ./build.sh diff --git a/Package.pins b/Package.pins deleted file mode 100644 index e0570d6..0000000 --- a/Package.pins +++ /dev/null @@ -1,5 +0,0 @@ -{ - "autoPin": false, - "pins": [], - "version": 1 -} diff --git a/Package.swift b/Package.swift index 66bfbb6..6962b61 100644 --- a/Package.swift +++ b/Package.swift @@ -1,3 +1,6 @@ +// swift-tools-version:4.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + /** * Copyright IBM Corporation 2016, 2017 * @@ -16,23 +19,56 @@ import PackageDescription +var dependencies: [Package.Dependency] = [ + .package(url: "https://github.com/IBM-Swift/LoggerAPI.git", .upToNextMinor(from: "1.7.0")), + .package(url: "https://github.com/IBM-Swift/BlueSocket.git", .upToNextMinor(from: "0.12.0")), + .package(url: "https://github.com/IBM-Swift/CCurl.git", .upToNextMinor(from: "0.4.0")), + .package(url: "https://github.com/IBM-Swift/BlueSSLService.git", .upToNextMinor(from: "0.12.0")) +] + +var kituraNetDependencies: [Target.Dependency] = [ + .byNameItem(name: "CHTTPParser"), + .byNameItem(name: "LoggerAPI"), + .byNameItem(name: "Socket"), + .byNameItem(name: "CCurl"), + .byNameItem(name: "SSLService") +] + +#if os(Linux) +dependencies.append(contentsOf: [ + .package(url: "https://github.com/IBM-Swift/CEpoll.git", .upToNextMinor(from: "0.1.0")), + .package(url: "https://github.com/IBM-Swift/BlueSignals.git", .upToNextMinor(from: "0.9.0")) + ]) + +kituraNetDependencies.append(contentsOf: [ + .byNameItem(name: "CEpoll"), + .byNameItem(name: "Signals") + ]) +#endif + let package = Package( name: "Kitura-net", - targets: [ - Target(name: "KituraNet", dependencies: ["CHTTPParser"]), - Target(name: "CHTTPParser", dependencies: []) + products: [ + // Products define the executables and libraries produced by a package, and make them visible to other packages. + .library( + name: "KituraNet", + targets: ["KituraNet"] + ) ], - dependencies: [ - .Package(url: "https://github.com/IBM-Swift/LoggerAPI.git", majorVersion: 1, minor: 7), - .Package(url: "https://github.com/IBM-Swift/BlueSocket.git", majorVersion: 0, minor: 12), - .Package(url: "https://github.com/IBM-Swift/CCurl.git", majorVersion: 0, minor: 4), - .Package(url: "https://github.com/IBM-Swift/BlueSSLService.git", majorVersion: 0, minor: 12) + dependencies: dependencies, + targets: [ + // Targets are the basic building blocks of a package. A target can define a module or a test suite. + // Targets can depend on other targets in this package, and on products in packages which this package depends on. + .target( + name: "CHTTPParser" + ), + .target( + name: "KituraNet", + dependencies: kituraNetDependencies + ), + .testTarget( + name: "KituraNetTests", + dependencies: ["KituraNet"] + ) ] ) - -#if os(Linux) - package.dependencies.append( - .Package(url: "https://github.com/IBM-Swift/CEpoll.git", majorVersion: 0, minor: 1)) - package.dependencies.append( - .Package(url: "https://github.com/IBM-Swift/BlueSignals.git", majorVersion: 0, minor: 9)) -#endif diff --git a/Package@swift-4.0.swift b/Package@swift-4.0.swift deleted file mode 100644 index 6962b61..0000000 --- a/Package@swift-4.0.swift +++ /dev/null @@ -1,74 +0,0 @@ -// swift-tools-version:4.0 -// The swift-tools-version declares the minimum version of Swift required to build this package. - -/** - * Copyright IBM Corporation 2016, 2017 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -import PackageDescription - -var dependencies: [Package.Dependency] = [ - .package(url: "https://github.com/IBM-Swift/LoggerAPI.git", .upToNextMinor(from: "1.7.0")), - .package(url: "https://github.com/IBM-Swift/BlueSocket.git", .upToNextMinor(from: "0.12.0")), - .package(url: "https://github.com/IBM-Swift/CCurl.git", .upToNextMinor(from: "0.4.0")), - .package(url: "https://github.com/IBM-Swift/BlueSSLService.git", .upToNextMinor(from: "0.12.0")) -] - -var kituraNetDependencies: [Target.Dependency] = [ - .byNameItem(name: "CHTTPParser"), - .byNameItem(name: "LoggerAPI"), - .byNameItem(name: "Socket"), - .byNameItem(name: "CCurl"), - .byNameItem(name: "SSLService") -] - -#if os(Linux) -dependencies.append(contentsOf: [ - .package(url: "https://github.com/IBM-Swift/CEpoll.git", .upToNextMinor(from: "0.1.0")), - .package(url: "https://github.com/IBM-Swift/BlueSignals.git", .upToNextMinor(from: "0.9.0")) - ]) - -kituraNetDependencies.append(contentsOf: [ - .byNameItem(name: "CEpoll"), - .byNameItem(name: "Signals") - ]) -#endif - -let package = Package( - name: "Kitura-net", - products: [ - // Products define the executables and libraries produced by a package, and make them visible to other packages. - .library( - name: "KituraNet", - targets: ["KituraNet"] - ) - ], - dependencies: dependencies, - targets: [ - // Targets are the basic building blocks of a package. A target can define a module or a test suite. - // Targets can depend on other targets in this package, and on products in packages which this package depends on. - .target( - name: "CHTTPParser" - ), - .target( - name: "KituraNet", - dependencies: kituraNetDependencies - ), - .testTarget( - name: "KituraNetTests", - dependencies: ["KituraNet"] - ) - ] -) diff --git a/Sources/KituraNet/ClientRequest.swift b/Sources/KituraNet/ClientRequest.swift index 64b6baf..876c2c0 100644 --- a/Sources/KituraNet/ClientRequest.swift +++ b/Sources/KituraNet/ClientRequest.swift @@ -166,7 +166,7 @@ public class ClientRequest { case .port(let thePort): port = ":\(thePort)" case .path(var thePath): - if thePath.characters.first != "/" { + if thePath.first != "/" { thePath = "/" + thePath } path = thePath diff --git a/Sources/KituraNet/ConnectionUpgrader.swift b/Sources/KituraNet/ConnectionUpgrader.swift index 3364a75..8d1033f 100644 --- a/Sources/KituraNet/ConnectionUpgrader.swift +++ b/Sources/KituraNet/ConnectionUpgrader.swift @@ -73,7 +73,7 @@ public struct ConnectionUpgrader { var protocolName: String? for eachProtocol in protocolList { let theProtocol = eachProtocol.first?.trimmingCharacters(in: CharacterSet.whitespaces) ?? "" - if theProtocol.characters.count != 0, let factory = registry[theProtocol.lowercased()] { + if theProtocol.count != 0, let factory = registry[theProtocol.lowercased()] { (processor, responseBody, responseBodyMimeType) = factory.upgrade(handler: handler, request: request, response: response) protocolName = theProtocol notFound = false diff --git a/Sources/KituraNet/FastCGI/FastCGIRecordParser.swift b/Sources/KituraNet/FastCGI/FastCGIRecordParser.swift index 14ce092..6e866ff 100644 --- a/Sources/KituraNet/FastCGI/FastCGIRecordParser.swift +++ b/Sources/KituraNet/FastCGI/FastCGIRecordParser.swift @@ -311,7 +311,7 @@ class FastCGIRecordParser { throw FastCGI.RecordErrors.emptyParameters } - guard nameString.characters.count > 0 else { + guard nameString.count > 0 else { // The data received form the web server existed and transcoded, // but someone resulted in a string of zero length. // Strange, but an error none the less. diff --git a/Sources/KituraNet/FastCGI/FastCGIServerRequest.swift b/Sources/KituraNet/FastCGI/FastCGIServerRequest.swift index 8cfe678..49d32c5 100644 --- a/Sources/KituraNet/FastCGI/FastCGIServerRequest.swift +++ b/Sources/KituraNet/FastCGI/FastCGIServerRequest.swift @@ -22,25 +22,25 @@ import Socket /// The FastCGIServerRequest class implements the `ServerRequest` protocol /// for incoming HTTP requests that come in over a FastCGI connection. public class FastCGIServerRequest : ServerRequest { - + /// Socket for the request private let socket: Socket - + /// The IP address of the client public private(set) var remoteAddress: String = "" /// Major version of HTTP of the request public private(set) var httpVersionMajor: UInt16? = 0 - + /// Minor version of HTTP of the request public private(set) var httpVersionMinor: UInt16? = 9 - + /// The set of HTTP headers received with the incoming request public var headers = HeadersContainer() /// The set of non-HTTP headers received with the incoming request public var fastCGIHeaders = HeadersContainer() - + /// The HTTP Method specified in the request public private(set) var method: String = "" @@ -74,18 +74,18 @@ public class FastCGIServerRequest : ServerRequest { /// State of incoming message handling private var status = Status.initial - + /// The request ID established by the FastCGI client. public private(set) var requestId : UInt16 = 0 - + /// An array of request ID's that are not our primary one. /// When the main request is done, the FastCGIServer can reject the /// extra requests as being unusable. public private(set) var extraRequestIds : [UInt16] = [] - + /// Some defaults private static let defaultMethod : String = "GET" - + /// List of status states private enum Status { case initial @@ -93,7 +93,7 @@ public class FastCGIServerRequest : ServerRequest { case headersComplete case requestComplete } - + /// HTTP parser error types public enum FastCGIParserErrorType { case success @@ -103,14 +103,14 @@ public class FastCGIServerRequest : ServerRequest { case unsupportedRole case internalError } - + /// Initialize a `FastCGIServerRequest` instance /// /// - Parameter socket: The socket to read the request from. required public init (socket: Socket) { self.socket = socket } - + /// Read data from the body of the request /// /// - Parameter data: A Data struct to hold the data read in. @@ -120,7 +120,7 @@ public class FastCGIServerRequest : ServerRequest { public func read(into data: inout Data) throws -> Int { return bodyChunk.fill(data: &data) } - + /// Read all of the data in the body of the request /// /// - Parameter data: A Data struct to hold the data read in. @@ -130,7 +130,7 @@ public class FastCGIServerRequest : ServerRequest { public func readAllData(into data: inout Data) throws -> Int { return bodyChunk.fill(data: &data) } - + /// Read a string from the body of the request. /// /// - Throws: Socket.error if an error occurred while reading from the socket. @@ -138,7 +138,7 @@ public class FastCGIServerRequest : ServerRequest { public func readString() throws -> String? { var data = Data() let bytes : Int = bodyChunk.fill(data: &data) - + if bytes > 0 { return String(data: data, encoding: .utf8) } else { @@ -163,7 +163,7 @@ public class FastCGIServerRequest : ServerRequest { Log.error("Host header not received") } - if let requestUri = requestUri, requestUri.characters.count > 0 { + if let requestUri = requestUri, requestUri.count > 0 { url.append(requestUri) } else { Log.error("REQUEST_URI header value not received") @@ -180,91 +180,75 @@ public class FastCGIServerRequest : ServerRequest { /// so lets massage these into place and make sure, at worst, sane /// defaults are in place. private func postProcessParameters() { - + // make sure our method is set - if method.characters.count == 0 { + if method.count == 0 { method = FastCGIServerRequest.defaultMethod } - + // make sure our remoteAddress is set - if remoteAddress.characters.count == 0 { + if remoteAddress.count == 0 { remoteAddress = socket.remoteHostname } - + // assign our URL postProcessUrlParameter() - + } - + /// FastCGI delivers headers that were originally sent by the browser/client /// with "HTTP_" prefixed. We want to normalize these out to remove HTTP_ /// and correct the capitilization (first letter of each word capitilized). private func processHttpHeader(_ name: String, value: String, remove: String) { - #if swift(>=3.2) - var processedName = String(name[name.index(name.startIndex, offsetBy: remove.count)...]) - #else - var processedName = name.substring(from: name.index(name.startIndex, offsetBy: remove.characters.count)) - #endif - + var processedName = String(name[name.index(name.startIndex, offsetBy: remove.count)...]) + processedName = processedName.replacingOccurrences(of: "_", with: "-") processedName = processedName.capitalized - + headers.append(processedName, value: value) } - + /// Parse the server protocol into a major and minor version private func processServerProtocol(_ protocolString: String) { - - guard protocolString.characters.count > 0 else { + + guard protocolString.count > 0 else { return } - + guard protocolString.lowercased().hasPrefix("http/") && - protocolString.characters.count > "http/".characters.count else { + protocolString.count > "http/".count else { return } - #if swift(>=3.2) - let versionPortion = String(protocolString[protocolString.index(protocolString.startIndex, offsetBy: "http/".count)...]) - #else - let versionPortion = protocolString.substring(from: protocolString.index(protocolString.startIndex, offsetBy: "http/".characters.count)) - #endif + let versionPortion = String(protocolString[protocolString.index(protocolString.startIndex, offsetBy: "http/".count)...]) var decimalPosition : Int = 0 - - for i in versionPortion.characters { + + for i in versionPortion { if i == "." { break } else { decimalPosition += 1 } } - + var majorVersion : UInt16? = nil var minorVersion : UInt16? = nil - + // get major version if decimalPosition > 0 { - #if swift(>=3.2) - let majorPortion = String(versionPortion[.. decimalPosition { - #if swift(>=3.2) - let minorPortion = String(versionPortion[versionPortion.index(versionPortion.startIndex, offsetBy: decimalPosition + 1)...]) - #else - let minorPortion = versionPortion.substring(from:versionPortion.index(versionPortion.startIndex, offsetBy: decimalPosition + 1)) - #endif + if protocolString.count > decimalPosition { + let minorPortion = String(versionPortion[versionPortion.index(versionPortion.startIndex, offsetBy: decimalPosition + 1)...]) minorVersion = UInt16(minorPortion) } - + // assign our values if applicable if majorVersion != nil && minorVersion != nil { httpVersionMajor = majorVersion! @@ -272,40 +256,40 @@ public class FastCGIServerRequest : ServerRequest { } } - - + + /// Process our headers. /// /// a) there are some special case headers we want to deal with directly. /// b) we want to add HTTP_ headers to the header table after noralizing /// c) everything else just discard private func processHeader (_ name : String, value: String) { - + if name.caseInsensitiveCompare("REQUEST_METHOD") == .orderedSame { - + // The request method (GET/POST/etc) method = value - + } else if name.caseInsensitiveCompare("REQUEST_URI") == .orderedSame { - + // The URI as submitted to the web server requestUri = value - + } else if name.caseInsensitiveCompare("REMOTE_ADDR") == .orderedSame { - + // The actual IP address of the client remoteAddress = value - + } else if name.caseInsensitiveCompare("SERVER_PROTOCOL") == .orderedSame { - + // The HTTP protocol used by the client to speak with the // web server (HTTP/0.9, HTTP/1.0, HTTP/1.1, HTTP/2.0, etc) // processServerProtocol(value) - + } else if name.hasPrefix("HTTP_") { - + // Any other headers starting with "HTTP_", which are all // original headers sent from the browser. // @@ -315,18 +299,18 @@ public class FastCGIServerRequest : ServerRequest { // processHttpHeader(name, value: value, remove: "HTTP_") return - + } if !name.hasPrefix("HTTP_") { fastCGIHeaders.append(name, value: value) } } - + /// process a record parsed from the connection. /// this has already been parsed and is just waiting for us to make a decision. private func processRecord (_ record : FastCGIRecordParser) throws { - + // is this record for a request that is an extra // request that we've already seen? if so, ignore it. // @@ -336,22 +320,22 @@ public class FastCGIServerRequest : ServerRequest { if status == Status.initial && record.type == FastCGI.Constants.FCGI_BEGIN_REQUEST { - + // this is a request begin record and we haven't seen any requests // in this FastCGIServerRequest object. We're safe to begin parsing. // requestId = record.requestId status = Status.requestStarted - + } else if record.type == FastCGI.Constants.FCGI_BEGIN_REQUEST { - + // this is another request begin record and we've already received // one before now. this is a request to multiplex the connection or // is this a protocol error? // // if this is an extra begin request, we need to throw an error - // and have the request + // and have the request // if record.requestId == requestId { // a second begin request is insanity. @@ -363,24 +347,24 @@ public class FastCGIServerRequest : ServerRequest { // extraRequestIds.append(record.requestId) } - + } else if status == Status.requestStarted && record.type == FastCGI.Constants.FCGI_PARAMS { - + // this is a parameter record - + // this request and the request in the record have to match - // if not, the web server is still sending headers related to a + // if not, the web server is still sending headers related to a // multiplexing attempt. // - // we want to keep processing the real request though, so we just + // we want to keep processing the real request though, so we just // ignore this for now and we can reject the attempt later. // guard record.requestId == requestId else { return } - + if record.headers.count > 0 { for pair in record.headers { // parse the header we've received @@ -390,7 +374,7 @@ public class FastCGIServerRequest : ServerRequest { // no params were received in this parameter record. // which means parameters are either completed (a blank param // record is sent to terminate parameter delivery) or the web - // server is badly misconfigured. either way, attempt to + // server is badly misconfigured. either way, attempt to // process and we can reject this as an error state later // as necessary. // @@ -398,13 +382,13 @@ public class FastCGIServerRequest : ServerRequest { status = Status.headersComplete Log.verbose("FastCGI request forwarded for=\(fastCGIHeaders["REMOTE_ADDR"]?[0] ?? "N.A."); proto=\(fastCGIHeaders["REQUEST_SCHEME"]?[0] ?? "N.A."); by=\(socket.remoteHostname);") } - + } else if status == Status.headersComplete && record.type == FastCGI.Constants.FCGI_STDIN { - + // Headers are complete and we're received STDIN records. - + // this request and the request in the record have to match // if not, the web server is still sending headers related to a // multiplexing attempt. @@ -415,7 +399,7 @@ public class FastCGIServerRequest : ServerRequest { guard record.requestId == requestId else { return } - + if let data = record.data, data.count > 0 { // we've received some request body data as part of the STDIN // @@ -426,53 +410,53 @@ public class FastCGIServerRequest : ServerRequest { // status = Status.requestComplete } - + } - + } - + /// Parse the request from FastCGI. func parse (_ callback: (FastCGIParserErrorType) -> Void) { - - + + var networkBuffer = Data() - + // we want to repeat this until we're done // in case the intake data isn't sufficient to // parse completed records. // repeat { - + do { var socketBuffer = Data() let bytesRead = try socket.read(into: &socketBuffer) - + guard bytesRead > 0 else { // did our client disconnect? strange. callback(.clientDisconnect) return } - + // add the read data to our main buffer networkBuffer.append(socketBuffer) - + // we want to parse records out one at a time. repeat { // make a parser let parser = FastCGIRecordParser(networkBuffer) - - // replace our network buffer of read data with the - // data sent back from the parser (data left over once the + + // replace our network buffer of read data with the + // data sent back from the parser (data left over once the // record at the head of the array was parsed - // - // Note that if we get an error indicating that the buffer - // suffered an exhaustion, we want to read more data from + // + // Note that if we get an error indicating that the buffer + // suffered an exhaustion, we want to read more data from // the socket as it's likely that we don't have sufficient // data yet. // do { let remainingData = try parser.parse() - + if remainingData == nil { networkBuffer.count = 0 } else { @@ -481,7 +465,7 @@ public class FastCGIServerRequest : ServerRequest { } } catch FastCGI.RecordErrors.bufferExhausted { - // break out of this repeat, which will start the + // break out of this repeat, which will start the // outer repeat loop again (read more data from the // socket). // @@ -490,19 +474,19 @@ public class FastCGIServerRequest : ServerRequest { // break } - + // if we got here, we parsed a record. try processRecord(parser) - + // if we're ready to send back a response, do so if status == Status.requestComplete { callback(.success) return } - + } while true - - + + } catch FastCGI.RecordErrors.invalidVersion { callback(.protocolError) return @@ -522,8 +506,8 @@ public class FastCGIServerRequest : ServerRequest { callback(.internalError) return } - + } while true - + } } diff --git a/Tests/KituraNetTests/ClientE2ETests.swift b/Tests/KituraNetTests/ClientE2ETests.swift index 77e6207..5f459eb 100644 --- a/Tests/KituraNetTests/ClientE2ETests.swift +++ b/Tests/KituraNetTests/ClientE2ETests.swift @@ -47,11 +47,11 @@ class ClientE2ETests: KituraNetTest { override func tearDown() { doTearDown() } - + static let urlPath = "/urltest" - + let delegate = TestServerDelegate() - + func testHeadRequests() { performServerTest(delegate) { expectation in self.performRequest("head", path: "/headtest", callback: {response in @@ -70,7 +70,7 @@ class ClientE2ETests: KituraNetTest { }) } } - + func testKeepAlive() { performServerTest(delegate, asyncTasks: { expectation in self.performRequest("get", path: "/posttest", callback: {response in @@ -93,7 +93,7 @@ class ClientE2ETests: KituraNetTest { }) }) } - + /// Tests that the server responds appropriately to pipelined requests. /// Three POST requests are sent to a test server in a single socket write. The /// server is expected to process them sequentially, sending three separate @@ -108,7 +108,7 @@ class ClientE2ETests: KituraNetTest { try socket.write(from: pipelinedRequests) } } - + /// Tests that the server responds appropriately to pipelined requests. /// Three POST requests are sent to a test server in a pipelined fashion, but /// spanning several packets. It is necessary to sleep between writes to allow @@ -131,7 +131,7 @@ class ClientE2ETests: KituraNetTest { try socket.write(from: thirdBuffer) } } - + private func doPipelineTest(expecting expectedResponse: String, totalRequests: Int, writer: (Socket) throws -> Void) { do { let server: HTTPServer @@ -140,7 +140,7 @@ class ClientE2ETests: KituraNetTest { defer { server.stop() } - + // Send pipelined requests to the server let clientSocket = try Socket.create() try clientSocket.connect(to: "localhost", port: Int32(serverPort)) @@ -149,7 +149,7 @@ class ClientE2ETests: KituraNetTest { } try writer(clientSocket) //try clientSocket.write(from: pipelinedRequests) - + // Queue a recovery task to close our socket so that the test cannot wait forever // waiting for responses from the server let recoveryTask = DispatchWorkItem { @@ -158,7 +158,7 @@ class ClientE2ETests: KituraNetTest { } let timeout = DispatchTime.now() + .seconds(1) DispatchQueue.global().asyncAfter(deadline: timeout, execute: recoveryTask) - + // Read responses from the server let buffer = NSMutableData(capacity: 2000)! var read = 0 @@ -191,12 +191,12 @@ class ClientE2ETests: KituraNetTest { // We completed reading the responses, cancel the recovery task recoveryTask.cancel() XCTAssert(bufferPosition == buffer.length, "Unparsed bytes remaining after final response") - + } catch { XCTFail("Error: \(error)") } } - + func testEphemeralListeningPort() { do { let server = try HTTPServer.listen(on: 0, delegate: delegate) @@ -221,7 +221,7 @@ class ClientE2ETests: KituraNetTest { } } } - + func testPostRequests() { performServerTest(delegate, asyncTasks: { expectation in self.performRequest("post", path: "/posttest", callback: {response in @@ -229,7 +229,7 @@ class ClientE2ETests: KituraNetTest { do { let postValue = try response?.readString() XCTAssertNotNil(postValue, "The body of the response was empty") - XCTAssertEqual(postValue?.characters.count, 12, "Result should have been 12 bytes, was \(String(describing: postValue?.characters.count)) bytes") + XCTAssertEqual(postValue?.count, 12, "Result should have been 12 bytes, was \(String(describing: postValue?.count)) bytes") if let postValue = postValue { XCTAssertEqual(postValue, "Read 0 bytes") } @@ -314,7 +314,7 @@ class ClientE2ETests: KituraNetTest { request.write(from: "A few characters") } }) - } + } func testPatchRequests() { performServerTest(delegate, asyncTasks: { expectation in @@ -362,7 +362,7 @@ class ClientE2ETests: KituraNetTest { } }) } - + func testErrorRequests() { performServerTest(delegate, asyncTasks: { expectation in self.performRequest("plover", path: "/xzzy", callback: {response in @@ -371,7 +371,7 @@ class ClientE2ETests: KituraNetTest { }) }) } - + func testUrlURL() { performServerTest(TestURLDelegate()) { expectation in self.performRequest("post", path: ClientE2ETests.urlPath, callback: {response in @@ -380,27 +380,27 @@ class ClientE2ETests: KituraNetTest { }) } } - + class TestServerDelegate: ServerDelegate { - + func handle(request: ServerRequest, response: ServerResponse) { XCTAssertEqual(request.remoteAddress, "127.0.0.1", "Remote address wasn't 127.0.0.1, it was \(request.remoteAddress)") - + let result: String switch request.method.lowercased() { case "head": result = "This a really simple head request result" - + case "put": do { - var body = try request.readString() - result = "Read \(body?.characters.count ?? 0) bytes" + let body = try request.readString() + result = "Read \(body?.count ?? 0) bytes" } catch { print("Error reading body") result = "Read -1 bytes" } - + default: var body = Data() do { @@ -412,13 +412,13 @@ class ClientE2ETests: KituraNetTest { result = "Read -1 bytes" } } - + do { response.statusCode = .OK XCTAssertEqual(response.statusCode, .OK, "Set response status code wasn't .OK, it was \(String(describing: response.statusCode))") response.headers["Content-Type"] = ["text/plain"] - response.headers["Content-Length"] = ["\(result.characters.count)"] - + response.headers["Content-Length"] = ["\(result.count)"] + try response.end(text: result) } catch { @@ -426,9 +426,9 @@ class ClientE2ETests: KituraNetTest { } } } - + class TestURLDelegate: ServerDelegate { - + func handle(request: ServerRequest, response: ServerResponse) { XCTAssertEqual(request.httpVersionMajor, 1, "HTTP Major code from KituraNet should be 1, was \(String(describing: request.httpVersionMajor))") XCTAssertEqual(request.httpVersionMinor, 1, "HTTP Minor code from KituraNet should be 1, was \(String(describing: request.httpVersionMinor))") @@ -441,7 +441,7 @@ class ClientE2ETests: KituraNetTest { response.headers["Content-Type"] = ["text/plain"] let resultData = result.data(using: .utf8)! response.headers["Content-Length"] = ["\(resultData.count)"] - + try response.write(from: resultData) try response.end() } diff --git a/Tests/KituraNetTests/FastCGIRequestTests.swift b/Tests/KituraNetTests/FastCGIRequestTests.swift index ce349dc..def327d 100644 --- a/Tests/KituraNetTests/FastCGIRequestTests.swift +++ b/Tests/KituraNetTests/FastCGIRequestTests.swift @@ -319,7 +319,7 @@ class FastCGIRequestTests: KituraNetTest { response.statusCode = .OK let result = "OK" response.headers["Content-Type"] = ["text/plain"] - response.headers["Content-Length"] = ["\(result.characters.count)"] + response.headers["Content-Length"] = ["\(result.count)"] try response.end(text: result) } diff --git a/Tests/KituraNetTests/LargePayloadTests.swift b/Tests/KituraNetTests/LargePayloadTests.swift index f425786..19e91c4 100644 --- a/Tests/KituraNetTests/LargePayloadTests.swift +++ b/Tests/KituraNetTests/LargePayloadTests.swift @@ -46,10 +46,10 @@ class LargePayloadTests: KituraNetTest { self.performRequest("post", path: "/largepost", callback: {response in XCTAssertEqual(response?.statusCode, HTTPStatusCode.OK, "Status code wasn't .Ok was \(String(describing: response?.statusCode))") do { - let expectedResult = "Read \(payload.characters.count) bytes" + let expectedResult = "Read \(payload.count) bytes" var data = Data() let count = try response?.readAllData(into: &data) - XCTAssertEqual(count, expectedResult.characters.count, "Result should have been \(expectedResult.characters.count) bytes, was \(String(describing: count)) bytes") + XCTAssertEqual(count, expectedResult.count, "Result should have been \(expectedResult.count) bytes, was \(String(describing: count)) bytes") let postValue = String(data: data, encoding: .utf8) if let postValue = postValue { XCTAssertEqual(postValue, expectedResult) @@ -113,7 +113,7 @@ class LargePayloadTests: KituraNetTest { let length = try request.readAllData(into: &body) let result = "Read \(length) bytes" response.headers["Content-Type"] = ["text/plain"] - response.headers["Content-Length"] = ["\(result.characters.count)"] + response.headers["Content-Length"] = ["\(result.count)"] try response.end(text: result) } diff --git a/Tests/KituraNetTests/PrintLogger.swift b/Tests/KituraNetTests/PrintLogger.swift index b65a7fa..107b16f 100644 --- a/Tests/KituraNetTests/PrintLogger.swift +++ b/Tests/KituraNetTests/PrintLogger.swift @@ -75,10 +75,6 @@ public class PrintLogger: Logger { return path } - #if swift(>=3.2) - return String(path[range.upperBound...]) - #else - return path.substring(from: range.upperBound) - #endif + return String(path[range.upperBound...]) } } diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index a8d3840..bf62633 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -19,37 +19,20 @@ import Glibc @testable import KituraNetTests // http://stackoverflow.com/questions/24026510/how-do-i-shuffle-an-array-in-swift -#if swift(>=3.2) - extension MutableCollection { - mutating func shuffle() { - let c = count - guard c > 1 else { return } +extension MutableCollection { + mutating func shuffle() { + let c = count + guard c > 1 else { return } - srand(UInt32(time(nil))) - for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) { - let d: IndexDistance = numericCast(random() % numericCast(unshuffledCount)) - guard d != 0 else { continue } - let i = index(firstUnshuffled, offsetBy: d) - swapAt(firstUnshuffled, i) - } + srand(UInt32(time(nil))) + for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) { + let d: IndexDistance = numericCast(random() % numericCast(unshuffledCount)) + guard d != 0 else { continue } + let i = index(firstUnshuffled, offsetBy: d) + swapAt(firstUnshuffled, i) } } -#else - extension MutableCollection where Indices.Iterator.Element == Index { - mutating func shuffle() { - let c = count - guard c > 1 else { return } - - srand(UInt32(time(nil))) - for (firstUnshuffled , unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) { - let d: IndexDistance = numericCast(random() % numericCast(unshuffledCount)) - guard d != 0 else { continue } - let i = index(firstUnshuffled, offsetBy: d) - swap(&self[firstUnshuffled], &self[i]) - } - } - } -#endif +} extension Sequence { func shuffled() -> [Iterator.Element] { diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..3522158 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,6 @@ +# docker-compose up +app: + image: ibmcom/swift-ubuntu:4.0.2 + volumes: + - .:/Kitura-Net + command: bash -c "cd /Kitura-Net && swift package --build-path .build-ubuntu clean && swift build --build-path .build-ubuntu && swift test --build-path .build-ubuntu"