From c4f3ecfffc90e2fe3ab3ca4e037934c724a03017 Mon Sep 17 00:00:00 2001 From: Daniel Firsht Date: Thu, 5 May 2016 17:17:02 -0500 Subject: [PATCH 01/14] IBM-Swift/Kitura#432 Migrated to 5/3 binaries --- Package.swift | 2 +- Sources/KituraNet/Http.swift | 2 +- Sources/KituraNet/HttpParser/UrlParser.swift | 8 ------- Sources/KituraNet/IncomingMessage.swift | 24 +++----------------- Sources/KituraNet/SpiUtils.swift | 3 +-- Tests/KituraNet/ParserTests.swift | 12 ++-------- 6 files changed, 8 insertions(+), 43 deletions(-) diff --git a/Package.swift b/Package.swift index d1c528d..9da4760 100644 --- a/Package.swift +++ b/Package.swift @@ -19,7 +19,7 @@ import PackageDescription let package = Package( name: "Kitura-net", dependencies: [ - .Package(url: "https://github.com/IBM-Swift/Kitura-sys.git", majorVersion: 0, minor: 11), + .Package(url: "https://github.com/IBM-Swift/Kitura-sys.git", majorVersion: 0, minor: 13), .Package(url: "https://github.com/IBM-Swift/BlueSocket.git", majorVersion: 0, minor: 5), .Package(url: "https://github.com/IBM-Swift/LoggerAPI.git", majorVersion: 0, minor: 5), .Package(url: "https://github.com/IBM-Swift/CCurl.git", majorVersion: 0, minor: 1), diff --git a/Sources/KituraNet/Http.swift b/Sources/KituraNet/Http.swift index 1560f8e..b1190e3 100644 --- a/Sources/KituraNet/Http.swift +++ b/Sources/KituraNet/Http.swift @@ -103,7 +103,7 @@ public class Http { /// A set of characters that are valid in requests /// #if os(Linux) - private static let allowedCharacterSet = NSCharacterSet(charactersInString:"\"#%/<>?@\\^`{|}").invertedSet + private static let allowedCharacterSet = NSCharacterSet(charactersIn:"\"#%/<>?@\\^`{|}").invertedSet #else private static let allowedCharacterSet = NSCharacterSet(charactersIn:"\"#%/<>?@\\^`{|}").inverted #endif diff --git a/Sources/KituraNet/HttpParser/UrlParser.swift b/Sources/KituraNet/HttpParser/UrlParser.swift index 79d4078..80c4c6a 100644 --- a/Sources/KituraNet/HttpParser/UrlParser.swift +++ b/Sources/KituraNet/HttpParser/UrlParser.swift @@ -131,18 +131,10 @@ public class UrlParser : CustomStringConvertible { if let query = query { - #if os(Linux) - let pairs = query.bridge().componentsSeparatedByString("&") - #else let pairs = query.components(separatedBy: "&") - #endif for pair in pairs { - #if os(Linux) - let pairArr = pair.bridge().componentsSeparatedByString("=") - #else let pairArr = pair.components(separatedBy: "=") - #endif if pairArr.count == 2 { queryParams[pairArr[0]] = pairArr[1] } diff --git a/Sources/KituraNet/IncomingMessage.swift b/Sources/KituraNet/IncomingMessage.swift index ac53cd3..87583f2 100644 --- a/Sources/KituraNet/IncomingMessage.swift +++ b/Sources/KituraNet/IncomingMessage.swift @@ -444,13 +444,11 @@ internal class HeaderStorage { internal var arrayHeaders = [String: [String]]() func addHeader(key headerKey: String, value headerValue: String) { - #if os(Linux) - let headerKeyLowerCase = headerKey.bridge().lowercaseString - #else + let headerKeyLowerCase = headerKey.lowercased() - #endif + // Determine how to handle the header (i.e. simple header array header,...) - switch(headerKeyLowerCase) { + switch(headerKey.lowercased()) { // Headers with an array value (can appear multiple times, but can't be merged) // @@ -498,11 +496,7 @@ public class SimpleHeaders { } public subscript(key: String) -> String? { - #if os(Linux) - let keyLowercase = key.bridge().lowercaseString - #else let keyLowercase = key.lowercased() - #endif var result = storage.simpleHeaders[keyLowercase] if result == nil { if let entry = storage.arrayHeaders[keyLowercase] { @@ -555,19 +549,11 @@ public class ArrayHeaders { } public subscript(key: String) -> [String]? { - #if os(Linux) - let keyLowercase = key.bridge().lowercaseString - #else let keyLowercase = key.lowercased() - #endif var result = storage.arrayHeaders[keyLowercase] if result == nil { if let entry = storage.simpleHeaders[keyLowercase] { - #if os(Linux) - result = entry.bridge().componentsSeparatedByString(", ") - #else result = entry.components(separatedBy: ", ") - #endif } } return result @@ -598,11 +584,7 @@ public struct ArrayHeadersIterator: IteratorProtocol { if result == nil { if let simpleElem = simpleIterator.next() { let (simpleKey, simpleValue) = simpleElem - #if os(Linux) - result = (simpleKey, simpleValue.bridge().componentsSeparatedByString(", ")) - #else result = (simpleKey, simpleValue.components(separatedBy: ", ")) - #endif } } return result diff --git a/Sources/KituraNet/SpiUtils.swift b/Sources/KituraNet/SpiUtils.swift index a3f21ad..734e0e3 100644 --- a/Sources/KituraNet/SpiUtils.swift +++ b/Sources/KituraNet/SpiUtils.swift @@ -75,11 +75,10 @@ public class SpiUtils { #endif calendar.timeZone = NSTimeZone(name: "UTC")! + let temp = calendar.components([.year, .month, .day, .hour, .minute, .second, .weekday], from: date) #if os(Linux) - let temp = calendar.components([.Year, .Month, .Day, .Hour, .Minute, .Second, .Weekday], fromDate: date) let components = temp! #else - let temp = calendar.components([.year, .month, .day, .hour, .minute, .second, .weekday], from: date) let components = temp #endif let wday = Int(components.weekday) diff --git a/Tests/KituraNet/ParserTests.swift b/Tests/KituraNet/ParserTests.swift index 2f2394e..dfd1a2c 100644 --- a/Tests/KituraNet/ParserTests.swift +++ b/Tests/KituraNet/ParserTests.swift @@ -28,11 +28,7 @@ class ParserTests: XCTestCase { } func testParseSimpleUrl() { -#if os(Linux) - let url = "https://example.org/absolute/URI/with/absolute/path/to/resource.txt".bridge().dataUsingEncoding(NSUTF8StringEncoding)! -#else - let url = "https://example.org/absolute/URI/with/absolute/path/to/resource.txt".data(using: NSUTF8StringEncoding)! -#endif + let url = "https://example.org/absolute/URI/with/absolute/path/to/resource.txt".data(using: NSUTF8StringEncoding)! let urlParser = UrlParser(url: url, isConnect: false) XCTAssertEqual(urlParser.schema!, "https", "Incorrect schema") XCTAssertEqual(urlParser.host!, "example.org", "Incorrect host") @@ -40,11 +36,7 @@ class ParserTests: XCTestCase { } func testParseComplexUrl() { -#if os(Linux) - let url = "abc://username:password@example.com:123/path/data?key=value&key1=value1#fragid1".bridge().dataUsingEncoding(NSUTF8StringEncoding)! -#else - let url = "abc://username:password@example.com:123/path/data?key=value&key1=value1#fragid1".data(using: NSUTF8StringEncoding)! -#endif + let url = "abc://username:password@example.com:123/path/data?key=value&key1=value1#fragid1".data(using: NSUTF8StringEncoding)! let urlParser = UrlParser(url: url, isConnect: false) XCTAssertEqual(urlParser.schema!, "abc", "Incorrect schema") XCTAssertEqual(urlParser.host!, "example.com", "Incorrect host") From f130e67763bd3fce722171e25905d98781e60e62 Mon Sep 17 00:00:00 2001 From: Daniel Firsht Date: Thu, 5 May 2016 17:30:42 -0500 Subject: [PATCH 02/14] fixed travis typo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index e5343e6..fe38700 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ branches: notifications: slack: - secure: "rbi1R2Icz5jsIo5YsFmRFyhjb+sJgAXHYQEdRE508oRjANhvOJ0JwIbDLLOT4w3lN1$ + secure: "rbi1R2Icz5jsIo5YsFmRFyhjb+sJgAXHYQEdRE508oRjANhvOJ0JwIbDLLOT4w3lN1$" matrix: include: From 59ca817ad21372fba09e75c170c127f6892b34d7 Mon Sep 17 00:00:00 2001 From: Shmuel Kallner Date: Sun, 8 May 2016 12:05:33 +0300 Subject: [PATCH 03/14] IBM-Swift/Kitura#439 Correctly support HTTP Continue response, by continueing to parse the final response for the request --- Sources/KituraNet/HttpParser/HttpParser.swift | 17 +++++++- Sources/KituraNet/IncomingMessage.swift | 42 +++++++++++++++---- 2 files changed, 49 insertions(+), 10 deletions(-) diff --git a/Sources/KituraNet/HttpParser/HttpParser.swift b/Sources/KituraNet/HttpParser/HttpParser.swift index f79226c..74f459d 100644 --- a/Sources/KituraNet/HttpParser/HttpParser.swift +++ b/Sources/KituraNet/HttpParser/HttpParser.swift @@ -32,6 +32,11 @@ class HttpParser { /// var settings: http_parser_settings + /// + /// Parsing a request? (or a response) + /// + var isRequest = true + /// /// Delegate used for the parsing /// @@ -61,6 +66,8 @@ class HttpParser { /// - Returns: an HttpParser instance /// init(isRequest: Bool) { + + self.isRequest = isRequest parser = http_parser() settings = http_parser_settings() @@ -130,8 +137,7 @@ class HttpParser { return 0 } - http_parser_init(&parser, isRequest ? HTTP_REQUEST : HTTP_RESPONSE) - + reset() } /// @@ -147,6 +153,13 @@ class HttpParser { let upgrade = get_upgrade_value(&parser) return (nparsed, upgrade) } + + /// + /// Reset the http_parser context structure. + /// + func reset() { + http_parser_init(&parser, isRequest ? HTTP_REQUEST : HTTP_RESPONSE) + } } /// diff --git a/Sources/KituraNet/IncomingMessage.swift b/Sources/KituraNet/IncomingMessage.swift index 87583f2..521611a 100644 --- a/Sources/KituraNet/IncomingMessage.swift +++ b/Sources/KituraNet/IncomingMessage.swift @@ -104,7 +104,7 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { private var status = Status.Initial /// - /// TODO: ??? + /// Chunk of body read in by the http_parser, filled by callbacks to onBody /// private var bodyChunk = BufferList() @@ -133,6 +133,7 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { case HeadersComplete case MessageComplete case Error + case Reset } @@ -187,20 +188,36 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { return } + var start = 0 + var length = 0 while status == .Initial { do { - ioBuffer!.length = 0 - let length = try helper!.readHelper(into: ioBuffer!) + if start == 0 { + ioBuffer!.length = 0 + length = try helper!.readHelper(into: ioBuffer!) + } if length > 0 { - let (nparsed, upgrade) = parser.execute(UnsafePointer(ioBuffer!.bytes), length: length) + let offset = start + start = 0 + let (nparsed, upgrade) = parser.execute(UnsafePointer(ioBuffer!.bytes)+offset, length: length) if upgrade == 1 { // TODO handle new protocol } else if (nparsed != length) { - /* Handle error. Usually just close the connection. */ - freeHttpParser() - status = .Error - callback(.ParsedLessThanRead) + + if status == .Reset { + // Apparently the short message was a Continue. Let's just keep on parsing + status = .Initial + start = nparsed + length -= nparsed + parser.reset() + } + else { + /* Handle error. Usually just close the connection. */ + freeHttpParser() + status = .Error + callback(.ParsedLessThanRead) + } } } else { @@ -425,6 +442,10 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { /// instructions for when reading is reset /// func reset() { + lastHeaderWasAValue = false + headerStorage.reset() + url.length = 0 + status = .Reset } } @@ -483,6 +504,11 @@ internal class HeaderStorage { break } } + + func reset() { + simpleHeaders.removeAll() + arrayHeaders.removeAll() + } } // From bac8f0138b65fd08e06e081f8849b3027c315e82 Mon Sep 17 00:00:00 2001 From: Karl Weinmeister Date: Mon, 9 May 2016 11:59:57 -0500 Subject: [PATCH 04/14] Updated capitalization to align with Swift standards (#22) Completes IBM-Swift/Kitura#132 * Updated capitalization to align with Swift standards * Also fixed capitalization in HTTPStatusCode enum per PR review feedback * Updated references in Kitura-net to use new HTTPStatusCode enum values --- Sources/KituraNet/ClientResponse.swift | 4 +- Sources/KituraNet/{Http.swift => HTTP.swift} | 40 +++++++-------- .../HTTPParser.swift} | 30 +++++------ .../URLParser.swift} | 6 +-- .../{HttpServer.swift => HTTPServer.swift} | 50 +++++++++---------- ...ttpServerSpi.swift => HTTPServerSPI.swift} | 12 ++--- Sources/KituraNet/IncomingMessage.swift | 50 +++++++++---------- .../{SpiUtils.swift => SPIUtils.swift} | 6 +-- Sources/KituraNet/ServerResponse.swift | 10 ++-- Tests/KituraNet/ClientTests.swift | 8 +-- Tests/KituraNet/ParserTests.swift | 4 +- 11 files changed, 110 insertions(+), 110 deletions(-) rename Sources/KituraNet/{Http.swift => HTTP.swift} (75%) rename Sources/KituraNet/{HttpParser/HttpParser.swift => HTTPParser/HTTPParser.swift} (86%) rename Sources/KituraNet/{HttpParser/UrlParser.swift => HTTPParser/URLParser.swift} (97%) rename Sources/KituraNet/{HttpServer.swift => HTTPServer.swift} (76%) rename Sources/KituraNet/{HttpServerSpi.swift => HTTPServerSPI.swift} (89%) rename Sources/KituraNet/{SpiUtils.swift => SPIUtils.swift} (96%) diff --git a/Sources/KituraNet/ClientResponse.swift b/Sources/KituraNet/ClientResponse.swift index 384df75..10c9cff 100644 --- a/Sources/KituraNet/ClientResponse.swift +++ b/Sources/KituraNet/ClientResponse.swift @@ -39,14 +39,14 @@ public class ClientResponse: IncomingMessage { public internal(set) var status = -1 { didSet { - statusCode = HttpStatusCode(rawValue: status)! + statusCode = HTTPStatusCode(rawValue: status)! } } /// /// HTTP Status code - public internal(set) var statusCode: HttpStatusCode = HttpStatusCode.UNKNOWN + public internal(set) var statusCode: HTTPStatusCode = HTTPStatusCode.Unknown /// /// BufferList instance for storing the response diff --git a/Sources/KituraNet/Http.swift b/Sources/KituraNet/HTTP.swift similarity index 75% rename from Sources/KituraNet/Http.swift rename to Sources/KituraNet/HTTP.swift index b1190e3..e8bbafa 100644 --- a/Sources/KituraNet/Http.swift +++ b/Sources/KituraNet/HTTP.swift @@ -16,9 +16,9 @@ import Foundation -// MARK: Http +// MARK: HTTP -public class Http { +public class HTTP { /// /// Mapping of integer status codes to the String description @@ -44,13 +44,13 @@ public class Http { ] /// - /// Creates a new Http server + /// Creates a new HTTP server /// - /// - Returns: an instance of HttpServer + /// - Returns: an instance of HTTPServer /// - public static func createServer() -> HttpServer { + public static func createServer() -> HTTPServer { - return HttpServer() + return HTTPServer() } @@ -134,20 +134,20 @@ public class Http { /// /// HTTP status codes and numbers /// -public enum HttpStatusCode: Int { +public enum HTTPStatusCode: Int { - case ACCEPTED = 202, BAD_GATEWAY = 502, BAD_REQUEST = 400, CONFLICT = 409, CONTINUE = 100, CREATED = 201 - case EXPECTATION_FAILED = 417, FAILED_DEPENDENCY = 424, FORBIDDEN = 403, GATEWAY_TIMEOUT = 504, GONE = 410 - case HTTP_VERSION_NOT_SUPPORTED = 505, INSUFFICIENT_SPACE_ON_RESOURCE = 419, INSUFFICIENT_STORAGE = 507 - case INTERNAL_SERVER_ERROR = 500, LENGTH_REQUIRED = 411, METHOD_FAILURE = 420, METHOD_NOT_ALLOWED = 405 - case MOVED_PERMANENTLY = 301, MOVED_TEMPORARILY = 302, MULTI_STATUS = 207, MULTIPLE_CHOICES = 300 - case NETWORK_AUTHENTICATION_REQUIRED = 511, NO_CONTENT = 204, NON_AUTHORITATIVE_INFORMATION = 203 - case NOT_ACCEPTABLE = 406, NOT_FOUND = 404, NOT_IMPLEMENTED = 501, NOT_MODIFIED = 304, OK = 200 - case PARTIAL_CONTENT = 206, PAYMENT_REQUIRED = 402, PRECONDITION_FAILED = 412, PRECONDITION_REQUIRED = 428 - case PROXY_AUTHENTICATION_REQUIRED = 407, PROCESSING = 102, REQUEST_HEADER_FIELDS_TOO_LARGE = 431 - case REQUEST_TIMEOUT = 408, REQUEST_TOO_LONG = 413, REQUEST_URI_TOO_LONG = 414, REQUESTED_RANGE_NOT_SATISFIABLE = 416 - case RESET_CONTENT = 205, SEE_OTHER = 303, SERVICE_UNAVAILABLE = 503, SWITCHING_PROTOCOLS = 101 - case TEMPORARY_REDIRECT = 307, TOO_MANY_REQUESTS = 429, UNAUTHORIZED = 401, UNPROCESSABLE_ENTITY = 422 - case UNSUPPORTED_MEDIA_TYPE = 415, USE_PROXY = 305, UNKNOWN = -1 + case Accepted = 202, BadGateway = 502, BadRequest = 400, Conflict = 409, Continue = 100, Created = 201 + case ExpectationFailed = 417, FailedDependency = 424, Forbidden = 403, GatewayTimeout = 504, Gone = 410 + case HTTPVersionNotSupported = 505, InsufficientSpaceOnResource = 419, InsufficientStorage = 507 + case InternalServerError = 500, LengthRequired = 411, MethodFailure = 420, MethodNotAllowed = 405 + case MovedPermanently = 301, MovedTemporarily = 302, MultiStatus = 207, MultipleChoices = 300 + case NetworkAuthenticationRequired = 511, NoContent = 204, NonAuthoritativeInformation = 203 + case NotAcceptable = 406, NotFound = 404, NotImplemented = 501, NotModified = 304, OK = 200 + case PartialContent = 206, PaymentRequired = 402, PreconditionFailed = 412, PreconditionRequired = 428 + case ProxyAuthenticationRequired = 407, Processing = 102, RequestHeaderFieldsTooLarge = 431 + case RequestTimeout = 408, RequestTooLong = 413, RequestURITooLong = 414, RequestedRangeNotSatisfiable = 416 + case ResetContent = 205, SeeOther = 303, ServiceUnavailable = 503, SwitchingProtocols = 101 + case TemporaryRedirect = 307, TooManyRequests = 429, Unauthorized = 401, UnprocessableEntity = 422 + case UnsupportedMediaType = 415, UseProxy = 305, Unknown = -1 } diff --git a/Sources/KituraNet/HttpParser/HttpParser.swift b/Sources/KituraNet/HTTPParser/HTTPParser.swift similarity index 86% rename from Sources/KituraNet/HttpParser/HttpParser.swift rename to Sources/KituraNet/HTTPParser/HTTPParser.swift index 74f459d..1887110 100644 --- a/Sources/KituraNet/HttpParser/HttpParser.swift +++ b/Sources/KituraNet/HTTPParser/HTTPParser.swift @@ -18,17 +18,17 @@ import KituraSys import CHttpParser import Foundation -// MARK: HttpParser +// MARK: HTTPParser -class HttpParser { +class HTTPParser { /// - /// A Handle to the HttpParser C-library + /// A Handle to the HTTPParser C-library /// var parser: http_parser /// - /// Settings used for HttpParser + /// Settings used for HTTPParser /// var settings: http_parser_settings @@ -40,7 +40,7 @@ class HttpParser { /// /// Delegate used for the parsing /// - var delegate: HttpParserDelegate? { + var delegate: HTTPParserDelegate? { didSet { if let _ = delegate { @@ -59,11 +59,11 @@ class HttpParser { var upgrade = 1 /// - /// Initializes a HttpParser instance + /// Initializes a HTTPParser instance /// /// - Parameter isRequest: whether or not this HTTP message is a request /// - /// - Returns: an HttpParser instance + /// - Returns: an HTTPParser instance /// init(isRequest: Bool) { @@ -73,7 +73,7 @@ class HttpParser { settings = http_parser_settings() settings.on_url = { (parser, chunk, length) -> Int32 in - let p = UnsafePointer(parser.pointee.data) + let p = UnsafePointer(parser.pointee.data) let data = NSData(bytes: chunk, length: length) p?.pointee?.onUrl(data) return 0 @@ -81,20 +81,20 @@ class HttpParser { settings.on_header_field = { (parser, chunk, length) -> Int32 in let data = NSData(bytes: chunk, length: length) - let p = UnsafePointer(parser.pointee.data) + let p = UnsafePointer(parser.pointee.data) p?.pointee?.onHeaderField(data) return 0 } settings.on_header_value = { (parser, chunk, length) -> Int32 in let data = NSData(bytes: chunk, length: length) - let p = UnsafePointer(parser.pointee.data) + let p = UnsafePointer(parser.pointee.data) p?.pointee?.onHeaderValue(data) return 0 } settings.on_body = { (parser, chunk, length) -> Int32 in - let p = UnsafePointer(parser.pointee.data) + let p = UnsafePointer(parser.pointee.data) let data = NSData(bytes: chunk, length: length) p?.pointee?.onBody(data) @@ -102,7 +102,7 @@ class HttpParser { } settings.on_headers_complete = { (parser) -> Int32 in - let p = UnsafePointer(parser.pointee.data) + let p = UnsafePointer(parser.pointee.data) // TODO: Clean and refactor //let method = String( get_method(parser)) let po = get_method(parser) @@ -119,14 +119,14 @@ class HttpParser { } settings.on_message_begin = { (parser) -> Int32 in - let p = UnsafePointer(parser.pointee.data) + let p = UnsafePointer(parser.pointee.data) p?.pointee?.onMessageBegin() return 0 } settings.on_message_complete = { (parser) -> Int32 in - let p = UnsafePointer(parser.pointee.data) + let p = UnsafePointer(parser.pointee.data) if get_status_code(parser) == 100 { p?.pointee?.reset() } @@ -165,7 +165,7 @@ class HttpParser { /// /// Delegate protocol for HTTP parsing stages /// -protocol HttpParserDelegate: class { +protocol HTTPParserDelegate: class { func onUrl(_ url:NSData) func onHeaderField(_ data: NSData) diff --git a/Sources/KituraNet/HttpParser/UrlParser.swift b/Sources/KituraNet/HTTPParser/URLParser.swift similarity index 97% rename from Sources/KituraNet/HttpParser/UrlParser.swift rename to Sources/KituraNet/HTTPParser/URLParser.swift index 80c4c6a..b0ee0e4 100644 --- a/Sources/KituraNet/HttpParser/UrlParser.swift +++ b/Sources/KituraNet/HTTPParser/URLParser.swift @@ -25,9 +25,9 @@ import Foundation import Darwin #endif -// MARK: UrlParser +// MARK: URLParser -public class UrlParser : CustomStringConvertible { +public class URLParser : CustomStringConvertible { /// /// Schema @@ -104,7 +104,7 @@ public class UrlParser : CustomStringConvertible { /// - /// Initializes a new UrlParser instance + /// Initializes a new URLParser instance /// /// - Parameter url: url to be parsed /// - Parameter isConnect: whether or not a connection has been established diff --git a/Sources/KituraNet/HttpServer.swift b/Sources/KituraNet/HTTPServer.swift similarity index 76% rename from Sources/KituraNet/HttpServer.swift rename to Sources/KituraNet/HTTPServer.swift index 920b962..0a7c147 100644 --- a/Sources/KituraNet/HttpServer.swift +++ b/Sources/KituraNet/HTTPServer.swift @@ -17,29 +17,29 @@ import KituraSys import Socket -// MARK: HttpServer +// MARK: HTTPServer -public class HttpServer { +public class HTTPServer { /// /// Queue for listening and establishing new connections /// - private static var listenerQueue = Queue(type: .PARALLEL, label: "HttpServer.listenerQueue") + private static var listenerQueue = Queue(type: .PARALLEL, label: "HTTPServer.listenerQueue") /// /// Queue for handling client requests /// - private static var clientHandlerQueue = Queue(type: .PARALLEL, label: "HttpServer.clientHandlerQueue") + private static var clientHandlerQueue = Queue(type: .PARALLEL, label: "HTTPServer.clientHandlerQueue") /// - /// HttpServerDelegate + /// HTTPServerDelegate /// - public weak var delegate: HttpServerDelegate? + public weak var delegate: HTTPServerDelegate? /// - /// Http service provider interface + /// HTTP service provider interface /// - private let spi: HttpServerSpi + private let spi: HTTPServerSPI /// /// Port number for listening for new connections @@ -55,13 +55,13 @@ public class HttpServer { private var listenSocket: Socket? /// - /// Initializes an HttpServer instance + /// Initializes an HTTPServer instance /// - /// - Returns: an HttpServer instance + /// - Returns: an HTTPServer instance /// public init() { - spi = HttpServerSpi() + spi = HTTPServerSPI() spi.delegate = self } @@ -91,10 +91,10 @@ public class HttpServer { } if notOnMainQueue { - HttpServer.listenerQueue.queueAsync(queuedBlock) + HTTPServer.listenerQueue.queueAsync(queuedBlock) } else { - Queue.queueIfFirstOnMain(queue: HttpServer.listenerQueue, block: queuedBlock) + Queue.queueIfFirstOnMain(queue: HTTPServer.listenerQueue, block: queuedBlock) } } @@ -112,17 +112,17 @@ public class HttpServer { } /// - /// Static method to create a new HttpServer and have it listen for conenctions + /// Static method to create a new HTTPServer and have it listen for conenctions /// /// - Parameter port: port number for accepting new connections - /// - Parameter delegate: the delegate handler for Http connections + /// - Parameter delegate: the delegate handler for HTTP connections /// - Parameter notOnMainQueue: whether to listen for new connections on the main Queue /// - /// - Returns: a new HttpServer instance + /// - Returns: a new HTTPServer instance /// - public static func listen(port: Int, delegate: HttpServerDelegate, notOnMainQueue: Bool=false) -> HttpServer { + public static func listen(port: Int, delegate: HTTPServerDelegate, notOnMainQueue: Bool=false) -> HTTPServer { - let server = Http.createServer() + let server = HTTP.createServer() server.delegate = delegate server.listen(port: port, notOnMainQueue: notOnMainQueue) return server @@ -131,11 +131,11 @@ public class HttpServer { } -// MARK: HttpServerSpiDelegate extension -extension HttpServer : HttpServerSpiDelegate { +// MARK: HTTPServerSPIDelegate extension +extension HTTPServer : HTTPServerSPIDelegate { /// - /// Handle a new client Http request + /// Handle a new client HTTP request /// /// - Parameter clientSocket: the socket used for connecting /// @@ -145,7 +145,7 @@ extension HttpServer : HttpServerSpiDelegate { return } - HttpServer.clientHandlerQueue.queueAsync() { + HTTPServer.clientHandlerQueue.queueAsync() { let response = ServerResponse(socket: clientSocket) let request = ServerRequest(socket: clientSocket) @@ -155,7 +155,7 @@ extension HttpServer : HttpServerSpiDelegate { delegate.handle(request: request, response: response) case .ParsedLessThanRead: print("ParsedLessThanRead") - response.statusCode = .BAD_REQUEST + response.statusCode = .BadRequest do { try response.end() } @@ -174,9 +174,9 @@ extension HttpServer : HttpServerSpiDelegate { } /// -/// Delegate protocol for an HttpServer +/// Delegate protocol for an HTTPServer /// -public protocol HttpServerDelegate: class { +public protocol HTTPServerDelegate: class { func handle(request: ServerRequest, response: ServerResponse) diff --git a/Sources/KituraNet/HttpServerSpi.swift b/Sources/KituraNet/HTTPServerSPI.swift similarity index 89% rename from Sources/KituraNet/HttpServerSpi.swift rename to Sources/KituraNet/HTTPServerSPI.swift index 6a8ad6e..f6f76fc 100644 --- a/Sources/KituraNet/HttpServerSpi.swift +++ b/Sources/KituraNet/HTTPServerSPI.swift @@ -17,17 +17,17 @@ import Socket import LoggerAPI -// MARK: HttpServerSpi +// MARK: HTTPServerSPI -class HttpServerSpi { +class HTTPServerSPI { /// - /// Delegate for handling the HttpServer connection + /// Delegate for handling the HTTPServer connection /// - weak var delegate: HttpServerSpiDelegate? + weak var delegate: HTTPServerSPIDelegate? /// - /// Whether the Http service provider handler has stopped runnign + /// Whether the HTTP service provider handler has stopped runnign /// var stopped = false @@ -72,7 +72,7 @@ class HttpServerSpi { /// /// Delegate for a service provider interface /// -protocol HttpServerSpiDelegate: class { +protocol HTTPServerSPIDelegate: class { /// /// Handle the client request diff --git a/Sources/KituraNet/IncomingMessage.swift b/Sources/KituraNet/IncomingMessage.swift index 521611a..3c8deb3 100644 --- a/Sources/KituraNet/IncomingMessage.swift +++ b/Sources/KituraNet/IncomingMessage.swift @@ -22,7 +22,7 @@ import Foundation // MARK: IncomingMessage -public class IncomingMessage : HttpParserDelegate, SocketReader { +public class IncomingMessage : HTTPParserDelegate, SocketReader { /// /// Default buffer size used for creating a BufferList @@ -96,7 +96,7 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { /// /// TODO: ??? /// - private var httpParser: HttpParser? + private var httpParser: HTTPParser? /// /// TODO: ??? @@ -139,9 +139,9 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { /// - /// Http parser error types + /// HTTP parser error types /// - public enum HttpParserErrorType { + public enum HTTPParserErrorType { case Success case ParsedLessThanRead @@ -158,7 +158,7 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { /// - Returns: an IncomingMessage instance /// init (isRequest: Bool) { - httpParser = HttpParser(isRequest: isRequest) + httpParser = HTTPParser(isRequest: isRequest) headers = SimpleHeaders(storage: headerStorage) headersAsArrays = ArrayHeaders(storage: headerStorage) @@ -179,11 +179,11 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { /// /// Parse the message /// - /// - Parameter callback: (HttpParserErrorType) -> Void closure + /// - Parameter callback: (HTTPParserErrorType) -> Void closure /// - func parse (_ callback: (HttpParserErrorType) -> Void) { + func parse (_ callback: (HTTPParserErrorType) -> Void) { guard let parser = httpParser where status == .Initial else { - freeHttpParser() + freeHTTPParser() callback(.InternalError) return } @@ -205,16 +205,16 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { } else if (nparsed != length) { - if status == .Reset { - // Apparently the short message was a Continue. Let's just keep on parsing - status = .Initial - start = nparsed - length -= nparsed - parser.reset() - } - else { + if status == .Reset { + // Apparently the short message was a Continue. Let's just keep on parsing + status = .Initial + start = nparsed + length -= nparsed + parser.reset() + } + else { /* Handle error. Usually just close the connection. */ - freeHttpParser() + freeHTTPParser() status = .Error callback(.ParsedLessThanRead) } @@ -222,14 +222,14 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { } else { /* Handle unexpected EOF. Usually just close the connection. */ - freeHttpParser() + freeHTTPParser() status = .Error callback(.UnexpectedEOF) } } catch { /* Handle error. Usually just close the connection. */ - freeHttpParser() + freeHTTPParser() status = .Error callback(.UnexpectedEOF) } @@ -260,7 +260,7 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { } else if (nparsed != count) { /* Handle error. Usually just close the connection. */ - freeHttpParser() + freeHTTPParser() status = .Error } else { @@ -269,12 +269,12 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { } else { status = .MessageComplete - freeHttpParser() + freeHTTPParser() } } catch let error { /* Handle error. Usually just close the connection. */ - freeHttpParser() + freeHTTPParser() status = .Error throw error } @@ -323,7 +323,7 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { /// /// Free the httpParser from the IncomingMessage /// - private func freeHttpParser () { + private func freeHTTPParser () { httpParser?.delegate = nil httpParser = nil @@ -434,7 +434,7 @@ public class IncomingMessage : HttpParserDelegate, SocketReader { func onMessageComplete() { status = .MessageComplete - freeHttpParser() + freeHTTPParser() } @@ -480,7 +480,7 @@ internal class HeaderStorage { break // Headers with a simple value that are not merged (i.e. duplicates dropped) - // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp + // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHTTPHeaderArray.cpp // case "content-type", "content-length", "user-agent", "referer", "host", "authorization", "proxy-authorization", "if-modified-since", diff --git a/Sources/KituraNet/SpiUtils.swift b/Sources/KituraNet/SPIUtils.swift similarity index 96% rename from Sources/KituraNet/SpiUtils.swift rename to Sources/KituraNet/SPIUtils.swift index 734e0e3..2a4fe9e 100644 --- a/Sources/KituraNet/SpiUtils.swift +++ b/Sources/KituraNet/SPIUtils.swift @@ -10,7 +10,7 @@ * 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 +* See the License for the specific language governing permissions and * limitations under the License. **/ @@ -23,9 +23,9 @@ import Foundation -// MARK: SpiUtils +// MARK: SPIUtils -public class SpiUtils { +public class SPIUtils { /// /// Abbreviations for month names diff --git a/Sources/KituraNet/ServerResponse.swift b/Sources/KituraNet/ServerResponse.swift index 4326f7e..ebef050 100644 --- a/Sources/KituraNet/ServerResponse.swift +++ b/Sources/KituraNet/ServerResponse.swift @@ -56,14 +56,14 @@ public class ServerResponse : SocketWriter { /// /// Status code /// - private var status = HttpStatusCode.OK.rawValue + private var status = HTTPStatusCode.OK.rawValue /// /// Status code /// - public var statusCode: HttpStatusCode? { + public var statusCode: HTTPStatusCode? { get { - return HttpStatusCode(rawValue: status) + return HTTPStatusCode(rawValue: status) } set (newValue) { if let newValue = newValue where !startFlushed { @@ -79,7 +79,7 @@ public class ServerResponse : SocketWriter { self.socket = socket buffer = NSMutableData(capacity: BUFFER_SIZE)! - setHeader("Date", value: SpiUtils.httpDate()) + setHeader("Date", value: SPIUtils.httpDate()) } @@ -264,7 +264,7 @@ public class ServerResponse : SocketWriter { try writeToSocketThroughBuffer(text: "HTTP/1.1 ") try writeToSocketThroughBuffer(text: String(status)) try writeToSocketThroughBuffer(text: " ") - var statusText = Http.statusCodes[status] + var statusText = HTTP.statusCodes[status] if statusText == nil { statusText = "" diff --git a/Tests/KituraNet/ClientTests.swift b/Tests/KituraNet/ClientTests.swift index 213d5a2..af8e83e 100644 --- a/Tests/KituraNet/ClientTests.swift +++ b/Tests/KituraNet/ClientTests.swift @@ -24,14 +24,14 @@ class ClientTests: XCTestCase { static var allTests : [(String, ClientTests -> () throws -> Void)] { return [ - ("testSimpleHttpClient", testSimpleHttpClient) + ("testSimpleHTTPClient", testSimpleHTTPClient) ] } - func testSimpleHttpClient() { - _ = Http.get("http://www.ibm.com") {response in + func testSimpleHTTPClient() { + _ = HTTP.get("http://www.ibm.com") {response in XCTAssertNotNil(response, "ERROR!!! ClientRequest response object was nil") - XCTAssertEqual(response!.statusCode, HttpStatusCode.OK, "HTTP Status code was \(response!.statusCode)") + XCTAssertEqual(response!.statusCode, HTTPStatusCode.OK, "HTTP Status code was \(response!.statusCode)") let contentType = response!.headers["Content-Type"] XCTAssertNotNil(contentType, "No ContentType header in response") XCTAssertEqual(contentType!, "text/html", "Content-Type header wasn't `text/html`") diff --git a/Tests/KituraNet/ParserTests.swift b/Tests/KituraNet/ParserTests.swift index dfd1a2c..04df6fa 100644 --- a/Tests/KituraNet/ParserTests.swift +++ b/Tests/KituraNet/ParserTests.swift @@ -29,7 +29,7 @@ class ParserTests: XCTestCase { func testParseSimpleUrl() { let url = "https://example.org/absolute/URI/with/absolute/path/to/resource.txt".data(using: NSUTF8StringEncoding)! - let urlParser = UrlParser(url: url, isConnect: false) + let urlParser = URLParser(url: url, isConnect: false) XCTAssertEqual(urlParser.schema!, "https", "Incorrect schema") XCTAssertEqual(urlParser.host!, "example.org", "Incorrect host") XCTAssertEqual(urlParser.path!, "/absolute/URI/with/absolute/path/to/resource.txt", "Incorrect path") @@ -37,7 +37,7 @@ class ParserTests: XCTestCase { func testParseComplexUrl() { let url = "abc://username:password@example.com:123/path/data?key=value&key1=value1#fragid1".data(using: NSUTF8StringEncoding)! - let urlParser = UrlParser(url: url, isConnect: false) + let urlParser = URLParser(url: url, isConnect: false) XCTAssertEqual(urlParser.schema!, "abc", "Incorrect schema") XCTAssertEqual(urlParser.host!, "example.com", "Incorrect host") XCTAssertEqual(urlParser.path!, "/path/data", "Incorrect path") From cee1013d47ce29cd20d72339f97313a77ef32cb4 Mon Sep 17 00:00:00 2001 From: Karl Weinmeister Date: Mon, 9 May 2016 21:27:54 -0500 Subject: [PATCH 05/14] Additional enum changes per commit comment on https://github.com/IBM-Swift/Kitura/issues/132 --- Sources/KituraNet/ClientRequest.swift | 24 +++---- Sources/KituraNet/ClientResponse.swift | 2 +- Sources/KituraNet/HTTP.swift | 26 ++++---- Sources/KituraNet/HTTPParser/URLParser.swift | 16 ++--- Sources/KituraNet/HTTPServer.swift | 10 +-- Sources/KituraNet/IncomingMessage.swift | 66 ++++++++++---------- Sources/KituraNet/ServerResponse.swift | 12 ++-- 7 files changed, 78 insertions(+), 78 deletions(-) diff --git a/Sources/KituraNet/ClientRequest.swift b/Sources/KituraNet/ClientRequest.swift index 883292d..8fb4078 100644 --- a/Sources/KituraNet/ClientRequest.swift +++ b/Sources/KituraNet/ClientRequest.swift @@ -117,25 +117,25 @@ public class ClientRequest: SocketWriter { for option in options { switch(option) { - case .Method(let method): + case .method(let method): self.method = method - case .Schema(let schema): + case .schema(let schema): theSchema = schema - case .Hostname(let host): + case .hostname(let host): hostName = host - case .Port(let thePort): + case .port(let thePort): port = thePort - case .Path(let thePath): + case .path(let thePath): path = thePath - case .Headers(let headers): + case .headers(let headers): for (key, value) in headers { self.headers[key] = value } - case .Username(let userName): + case .username(let userName): self.userName = userName - case .Password(let password): + case .password(let password): self.password = password - case .MaxRedirects(let maxRedirects): + case .maxRedirects(let maxRedirects): self.maxRedirects = maxRedirects } } @@ -248,7 +248,7 @@ public class ClientRequest: SocketWriter { if code == CURLE_OK { response.parse() {status in switch(status) { - case .Success: + case .success: self.callback(response: self.response) callCallback = false @@ -366,8 +366,8 @@ extension ClientRequest: CurlInvokerDelegate { /// public enum ClientRequestOptions { - case Method(String), Schema(String), Hostname(String), Port(Int16), Path(String), - Headers([String: String]), Username(String), Password(String), MaxRedirects(Int) + case method(String), schema(String), hostname(String), port(Int16), path(String), + headers([String: String]), username(String), password(String), maxRedirects(Int) } diff --git a/Sources/KituraNet/ClientResponse.swift b/Sources/KituraNet/ClientResponse.swift index 10c9cff..0002610 100644 --- a/Sources/KituraNet/ClientResponse.swift +++ b/Sources/KituraNet/ClientResponse.swift @@ -46,7 +46,7 @@ public class ClientResponse: IncomingMessage { /// /// HTTP Status code - public internal(set) var statusCode: HTTPStatusCode = HTTPStatusCode.Unknown + public internal(set) var statusCode: HTTPStatusCode = HTTPStatusCode.unknown /// /// BufferList instance for storing the response diff --git a/Sources/KituraNet/HTTP.swift b/Sources/KituraNet/HTTP.swift index e8bbafa..49df5c6 100644 --- a/Sources/KituraNet/HTTP.swift +++ b/Sources/KituraNet/HTTP.swift @@ -136,18 +136,18 @@ public class HTTP { /// public enum HTTPStatusCode: Int { - case Accepted = 202, BadGateway = 502, BadRequest = 400, Conflict = 409, Continue = 100, Created = 201 - case ExpectationFailed = 417, FailedDependency = 424, Forbidden = 403, GatewayTimeout = 504, Gone = 410 - case HTTPVersionNotSupported = 505, InsufficientSpaceOnResource = 419, InsufficientStorage = 507 - case InternalServerError = 500, LengthRequired = 411, MethodFailure = 420, MethodNotAllowed = 405 - case MovedPermanently = 301, MovedTemporarily = 302, MultiStatus = 207, MultipleChoices = 300 - case NetworkAuthenticationRequired = 511, NoContent = 204, NonAuthoritativeInformation = 203 - case NotAcceptable = 406, NotFound = 404, NotImplemented = 501, NotModified = 304, OK = 200 - case PartialContent = 206, PaymentRequired = 402, PreconditionFailed = 412, PreconditionRequired = 428 - case ProxyAuthenticationRequired = 407, Processing = 102, RequestHeaderFieldsTooLarge = 431 - case RequestTimeout = 408, RequestTooLong = 413, RequestURITooLong = 414, RequestedRangeNotSatisfiable = 416 - case ResetContent = 205, SeeOther = 303, ServiceUnavailable = 503, SwitchingProtocols = 101 - case TemporaryRedirect = 307, TooManyRequests = 429, Unauthorized = 401, UnprocessableEntity = 422 - case UnsupportedMediaType = 415, UseProxy = 305, Unknown = -1 + case accepted = 202, badGateway = 502, badRequest = 400, conflict = 409, `continue` = 100, created = 201 + case expectationFailed = 417, failedDependency = 424, forbidden = 403, gatewayTimeout = 504, gone = 410 + case httpVersionNotSupported = 505, insufficientSpaceOnResource = 419, insufficientStorage = 507 + case internalServerError = 500, lengthRequired = 411, methodFailure = 420, methodNotAllowed = 405 + case movedPermanently = 301, movedTemporarily = 302, multiStatus = 207, multipleChoices = 300 + case networkAuthenticationRequired = 511, noContent = 204, nonAuthoritativeInformation = 203 + case notAcceptable = 406, notFound = 404, notImplemented = 501, notModified = 304, OK = 200 + case partialContent = 206, paymentRequired = 402, preconditionFailed = 412, preconditionRequired = 428 + case proxyAuthenticationRequired = 407, processing = 102, requestHeaderFieldsTooLarge = 431 + case requestTimeout = 408, requestTooLong = 413, requestURITooLong = 414, requestedRangeNotSatisfiable = 416 + case resetContent = 205, seeOther = 303, serviceUnavailable = 503, switchingProtocols = 101 + case temporaryRedirect = 307, tooManyRequests = 429, unauthorized = 401, unprocessableEntity = 422 + case unsupportedMediaType = 415, useProxy = 305, unknown = -1 } diff --git a/Sources/KituraNet/HTTPParser/URLParser.swift b/Sources/KituraNet/HTTPParser/URLParser.swift index b0ee0e4..7a88026 100644 --- a/Sources/KituraNet/HTTPParser/URLParser.swift +++ b/Sources/KituraNet/HTTPParser/URLParser.swift @@ -117,13 +117,13 @@ public class URLParser : CustomStringConvertible { if http_parser_parse_url_url(UnsafePointer(url.bytes), url.length, isConnect ? 1 : 0 , &parsedUrl) == 0 { let (s, h, ps, p, q, f, u) = parsedUrl.field_data - schema = getValueFromUrl(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_SCHEMA.rawValue), fieldData: s) - host = getValueFromUrl(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_HOST.rawValue), fieldData: h) - let portString = getValueFromUrl(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_PORT.rawValue), fieldData: ps) - path = getValueFromUrl(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_PATH.rawValue), fieldData: p) - query = getValueFromUrl(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_QUERY.rawValue), fieldData: q) - fragment = getValueFromUrl(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_FRAGMENT.rawValue), fieldData: f) - userinfo = getValueFromUrl(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_USERINFO.rawValue), fieldData: u) + schema = getValueFromURL(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_SCHEMA.rawValue), fieldData: s) + host = getValueFromURL(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_HOST.rawValue), fieldData: h) + let portString = getValueFromURL(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_PORT.rawValue), fieldData: ps) + path = getValueFromURL(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_PATH.rawValue), fieldData: p) + query = getValueFromURL(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_QUERY.rawValue), fieldData: q) + fragment = getValueFromURL(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_FRAGMENT.rawValue), fieldData: f) + userinfo = getValueFromURL(url, fieldSet: parsedUrl.field_set, fieldIndex: UInt16(UF_USERINFO.rawValue), fieldData: u) if let _ = portString { port = parsedUrl.port @@ -149,7 +149,7 @@ public class URLParser : CustomStringConvertible { /// TODO: ??? /// /// - private func getValueFromUrl(_ url: NSData, fieldSet: UInt16, fieldIndex: UInt16, + private func getValueFromURL(_ url: NSData, fieldSet: UInt16, fieldIndex: UInt16, fieldData: http_parser_url_field_data) -> String? { if fieldSet & (1 << fieldIndex) != 0 { diff --git a/Sources/KituraNet/HTTPServer.swift b/Sources/KituraNet/HTTPServer.swift index 0a7c147..2a1958a 100644 --- a/Sources/KituraNet/HTTPServer.swift +++ b/Sources/KituraNet/HTTPServer.swift @@ -151,20 +151,20 @@ extension HTTPServer : HTTPServerSPIDelegate { let request = ServerRequest(socket: clientSocket) request.parse() { status in switch status { - case .Success: + case .success: delegate.handle(request: request, response: response) - case .ParsedLessThanRead: + case .parsedLessThanRead: print("ParsedLessThanRead") - response.statusCode = .BadRequest + response.statusCode = .badRequest do { try response.end() } catch { // handle error in connection } - case .UnexpectedEOF: + case .unexpectedEOF: print("UnexpectedEOF") - case .InternalError: + case .internalError: print("InternalError") } } diff --git a/Sources/KituraNet/IncomingMessage.swift b/Sources/KituraNet/IncomingMessage.swift index 3c8deb3..09e9315 100644 --- a/Sources/KituraNet/IncomingMessage.swift +++ b/Sources/KituraNet/IncomingMessage.swift @@ -27,7 +27,7 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// /// Default buffer size used for creating a BufferList /// - private static let BUFFER_SIZE = 2000 + private static let bufferSize = 2000 /// /// Major version for HTTP @@ -101,7 +101,7 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// /// TODO: ??? /// - private var status = Status.Initial + private var status = Status.initial /// /// Chunk of body read in by the http_parser, filled by callbacks to onBody @@ -116,12 +116,12 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// /// TODO: ??? /// - private var ioBuffer = NSMutableData(capacity: BUFFER_SIZE) + private var ioBuffer = NSMutableData(capacity: IncomingMessage.bufferSize) /// /// TODO: ??? /// - private var buffer = NSMutableData(capacity: BUFFER_SIZE) + private var buffer = NSMutableData(capacity: IncomingMessage.bufferSize) /// @@ -129,11 +129,11 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// private enum Status { - case Initial - case HeadersComplete - case MessageComplete - case Error - case Reset + case initial + case headersComplete + case messageComplete + case error + case reset } @@ -143,10 +143,10 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// public enum HTTPParserErrorType { - case Success - case ParsedLessThanRead - case UnexpectedEOF - case InternalError // TODO + case success + case parsedLessThanRead + case unexpectedEOF + case internalError // TODO } @@ -182,15 +182,15 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// - Parameter callback: (HTTPParserErrorType) -> Void closure /// func parse (_ callback: (HTTPParserErrorType) -> Void) { - guard let parser = httpParser where status == .Initial else { + guard let parser = httpParser where status == .initial else { freeHTTPParser() - callback(.InternalError) + callback(.internalError) return } var start = 0 var length = 0 - while status == .Initial { + while status == .initial { do { if start == 0 { ioBuffer!.length = 0 @@ -205,9 +205,9 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { } else if (nparsed != length) { - if status == .Reset { + if status == .reset { // Apparently the short message was a Continue. Let's just keep on parsing - status = .Initial + status = .initial start = nparsed length -= nparsed parser.reset() @@ -215,27 +215,27 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { else { /* Handle error. Usually just close the connection. */ freeHTTPParser() - status = .Error - callback(.ParsedLessThanRead) + status = .error + callback(.parsedLessThanRead) } } } else { /* Handle unexpected EOF. Usually just close the connection. */ freeHTTPParser() - status = .Error - callback(.UnexpectedEOF) + status = .error + callback(.unexpectedEOF) } } catch { /* Handle error. Usually just close the connection. */ freeHTTPParser() - status = .Error - callback(.UnexpectedEOF) + status = .error + callback(.unexpectedEOF) } } - if status != .Error { - callback(.Success) + if status != .error { + callback(.success) } } @@ -249,7 +249,7 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { public func read(into data: NSMutableData) throws -> Int { var count = bodyChunk.fill(data: data) if count == 0 { - if let parser = httpParser where status == .HeadersComplete { + if let parser = httpParser where status == .headersComplete { do { ioBuffer!.length = 0 count = try helper!.readHelper(into: ioBuffer!) @@ -261,21 +261,21 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { else if (nparsed != count) { /* Handle error. Usually just close the connection. */ freeHTTPParser() - status = .Error + status = .error } else { count = bodyChunk.fill(data: data) } } else { - status = .MessageComplete + status = .messageComplete freeHTTPParser() } } catch let error { /* Handle error. Usually just close the connection. */ freeHTTPParser() - status = .Error + status = .error throw error } } @@ -416,7 +416,7 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { addHeader() } - status = .HeadersComplete + status = .headersComplete } @@ -433,7 +433,7 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// func onMessageComplete() { - status = .MessageComplete + status = .messageComplete freeHTTPParser() } @@ -445,7 +445,7 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { lastHeaderWasAValue = false headerStorage.reset() url.length = 0 - status = .Reset + status = .reset } } diff --git a/Sources/KituraNet/ServerResponse.swift b/Sources/KituraNet/ServerResponse.swift index ebef050..49e1db2 100644 --- a/Sources/KituraNet/ServerResponse.swift +++ b/Sources/KituraNet/ServerResponse.swift @@ -31,7 +31,7 @@ public class ServerResponse : SocketWriter { /// /// Size of buffer /// - private let BUFFER_SIZE = 2000 + private static let bufferSize = 2000 /// /// Buffer for HTTP response line, headers, and short bodies @@ -78,7 +78,7 @@ public class ServerResponse : SocketWriter { init(socket: Socket) { self.socket = socket - buffer = NSMutableData(capacity: BUFFER_SIZE)! + buffer = NSMutableData(capacity: ServerResponse.bufferSize)! setHeader("Date", value: SPIUtils.httpDate()) } @@ -208,11 +208,11 @@ public class ServerResponse : SocketWriter { if let socket = socket { try flushStart() - if buffer.length + data.length > BUFFER_SIZE && buffer.length != 0 { + if buffer.length + data.length > ServerResponse.bufferSize && buffer.length != 0 { try socket.write(from: buffer) buffer.length = 0 } - if data.length > BUFFER_SIZE { + if data.length > ServerResponse.bufferSize { try socket.write(from: data) } else { @@ -302,11 +302,11 @@ public class ServerResponse : SocketWriter { return } - if buffer.length + utf8Data.length > BUFFER_SIZE && buffer.length != 0 { + if buffer.length + utf8Data.length > ServerResponse.bufferSize && buffer.length != 0 { try socket.write(from: buffer) buffer.length = 0 } - if utf8Data.length > BUFFER_SIZE { + if utf8Data.length > ServerResponse.bufferSize { try socket.write(from: utf8Data) } else { From 42b5082fe55d68cf4df719007816c595d9ed3edf Mon Sep 17 00:00:00 2001 From: Daniel Firsht Date: Tue, 10 May 2016 13:46:52 -0500 Subject: [PATCH 06/14] Updated Travis slack credentials --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index fe38700..8a32df4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,7 @@ branches: notifications: slack: - secure: "rbi1R2Icz5jsIo5YsFmRFyhjb+sJgAXHYQEdRE508oRjANhvOJ0JwIbDLLOT4w3lN1$" + secure: "P0r4xHJ1C6siPNtB0HM8kcRDGrD7PX0iY3UrDwRN41isiw1IpjNjNOvtzEzATyIkkpMlMXEq3QBdEsxZCwIKYUZHCy99CSkoY/brUdIJgaC92XZzP7BucQAhbweilVX4YcB8CmfKJ9b5I/+7o7bbnOlcVDUkd29QuMb0nUy2rXl6xZVLJ/9BmFSsffAquPjfdQKq/xh/hIbaBtHLDVxxQ4Q/DeDJ+FLkG16tFAvEj9EMDgeFcKZF9pOpFrCdpwj1Bg8NLB5vZqizxgr9xK4lBBr7LBrwhwfzvxs8DAjwWSUZkZEhPfoRzSBZ0tYeqH2V3dL8B9eEPVyfDawq8DXQcjMVIn5wpVQqNzzjuAXNlqhjT7PzjDccnU821uNs9nYDY5aug8pqC+ZaB1GSnt4Y0jH4NjSnEQIGeYwn3D4CiTwAGjO/OFJT5VV/DcKpdtzcuEbhCJyaPPfk4obgiUOOBEH6Ut932fqYfd70h7gupofFcctzsei3egAv9bff/PTIzhYsf5Vacj4i/hx+AaBK63BctKu8okcPne40OKms15dO/BB5AfFZYsN+sSOhzOpTzZpxS2tEqVJsVna0DcFX5cWzIMr0MonQYLr/uVB7mgE/VT8wETbVaEkSq/yqScOQRLM/ja/o273e3bv1EoRxYJPQSHQR/Du1le1E2dQS+Qc=" matrix: include: From ee4be90d00f635c7490023cf01a9c1c31ba89bd2 Mon Sep 17 00:00:00 2001 From: Shmuel Kallner Date: Wed, 11 May 2016 09:50:05 +0300 Subject: [PATCH 07/14] IBM-Swift/Kitura#466 Updates due to enum name changes --- Sources/KituraNet/HTTPServer.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/KituraNet/HTTPServer.swift b/Sources/KituraNet/HTTPServer.swift index 2a1958a..d15fba8 100644 --- a/Sources/KituraNet/HTTPServer.swift +++ b/Sources/KituraNet/HTTPServer.swift @@ -24,12 +24,12 @@ public class HTTPServer { /// /// Queue for listening and establishing new connections /// - private static var listenerQueue = Queue(type: .PARALLEL, label: "HTTPServer.listenerQueue") + private static var listenerQueue = Queue(type: .parallel, label: "HTTPServer.listenerQueue") /// /// Queue for handling client requests /// - private static var clientHandlerQueue = Queue(type: .PARALLEL, label: "HTTPServer.clientHandlerQueue") + private static var clientHandlerQueue = Queue(type: .parallel, label: "HTTPServer.clientHandlerQueue") /// /// HTTPServerDelegate From a42b5332e11b3a18f96c099f7c5d825f2ea76c82 Mon Sep 17 00:00:00 2001 From: Shmuel Kallner Date: Wed, 11 May 2016 11:41:10 +0300 Subject: [PATCH 08/14] IBM-Swift/Kitura#466 Updated to take latest LoggerAPI indirectly from Kitura-sys --- Package.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Package.swift b/Package.swift index 9da4760..761dced 100644 --- a/Package.swift +++ b/Package.swift @@ -21,7 +21,6 @@ let package = Package( dependencies: [ .Package(url: "https://github.com/IBM-Swift/Kitura-sys.git", majorVersion: 0, minor: 13), .Package(url: "https://github.com/IBM-Swift/BlueSocket.git", majorVersion: 0, minor: 5), - .Package(url: "https://github.com/IBM-Swift/LoggerAPI.git", majorVersion: 0, minor: 5), .Package(url: "https://github.com/IBM-Swift/CCurl.git", majorVersion: 0, minor: 1), .Package(url: "https://github.com/IBM-Swift/CHttpParser.git", majorVersion: 0, minor: 1), ] From 4a40361b95db9cb920186b576d7cedece8cbeab2 Mon Sep 17 00:00:00 2001 From: Daniel Firsht Date: Thu, 12 May 2016 14:32:41 -0500 Subject: [PATCH 09/14] IBM-Swift/Kitura-net#27 Added support for accepting curl options --- Sources/KituraNet/ClientRequest.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Sources/KituraNet/ClientRequest.swift b/Sources/KituraNet/ClientRequest.swift index 8fb4078..39be7c2 100644 --- a/Sources/KituraNet/ClientRequest.swift +++ b/Sources/KituraNet/ClientRequest.swift @@ -81,6 +81,8 @@ public class ClientRequest: SocketWriter { private var callback: ClientRequestCallback + + private var curlOptions: [(CURLoption, Int)]? /// /// Initializes a ClientRequest instance @@ -137,6 +139,8 @@ public class ClientRequest: SocketWriter { self.password = password case .maxRedirects(let maxRedirects): self.maxRedirects = maxRedirects + case .curlOptions(let curlOptions): + self.curlOptions = curlOptions } } @@ -282,6 +286,11 @@ public class ClientRequest: SocketWriter { // HTTP parser does the decoding curlHelperSetOptInt(handle!, CURLOPT_HTTP_TRANSFER_DECODING, 0) curlHelperSetOptString(handle!, CURLOPT_URL, UnsafeMutablePointer(urlBuf.bytes)) + if let curlOptions = curlOptions { + for (curlOption, value) in curlOptions { + curlHelperSetOptInt(handle!, curlOption, value) + } + } setMethod() let count = writeBuffers.count if count != 0 { @@ -367,7 +376,7 @@ extension ClientRequest: CurlInvokerDelegate { public enum ClientRequestOptions { case method(String), schema(String), hostname(String), port(Int16), path(String), - headers([String: String]), username(String), password(String), maxRedirects(Int) + headers([String: String]), username(String), password(String), maxRedirects(Int), curlOptions([(CURLoption, Int)]) } From 46304aae25f54398f82644e84e0380c9cf18cd54 Mon Sep 17 00:00:00 2001 From: Daniel Firsht Date: Mon, 16 May 2016 11:27:36 -0500 Subject: [PATCH 10/14] Header refactoring (#20) * IBM-Swift/Kitura#404 Refactored headers in ServerResponse * IBM-Swift/Kitura#404 moved Headers to seperate struct and implemented case-insensitive behavior * IBM-Swift/Kitura#404 Replaced IncomingMessage header storage with Headers struct * IBM-Swift#404 Header API cleanup * IBM-Swift/Kitura#404 added convience subscripting * IBM-Swift/Kitura#404 Set subscript to public * IBM-Swift/Kitura#404 Added back header merging differences * IBM-Swift/Kitura#404 Moved duplicate dropping to incoming only * IBM-Swift/Kitura#404 fixed typo * IBM-Swift/Kitura#404 Changed Headers struct to class * IBM-Swift/Kitura#404 hid methods that can be replaced by subscripting * IBM-Swift/Kitura#404 Removed unused method --- Sources/KituraNet/HeadersContainer.swift | 131 +++++++++++++ Sources/KituraNet/IncomingMessage.swift | 231 +++-------------------- Sources/KituraNet/ServerResponse.swift | 117 +----------- Tests/KituraNet/ClientTests.swift | 2 +- 4 files changed, 164 insertions(+), 317 deletions(-) create mode 100644 Sources/KituraNet/HeadersContainer.swift diff --git a/Sources/KituraNet/HeadersContainer.swift b/Sources/KituraNet/HeadersContainer.swift new file mode 100644 index 0000000..4f517ca --- /dev/null +++ b/Sources/KituraNet/HeadersContainer.swift @@ -0,0 +1,131 @@ +/** + * Copyright IBM Corporation 2015 + * + * 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 Foundation + +public class HeadersContainer { + + /// + /// The header storage + /// + internal var headers: [String: [String]] = [:] + + /// + /// The map of case insensitive header fields to their actual names + /// + private var caseInsensitiveMap: [String: String] = [:] + + public subscript(key: String) -> [String]? { + get { + return get(key) + } + + set(newValue) { + if let newValue = newValue { + set(key, value: newValue) + } + else { + remove(key) + } + } + } + + /// + /// Append values to the header + /// + /// - Parameter key: the key + /// - Parameter value: the value + /// + public func append(_ key: String, value: [String]) { + + // Determine how to handle the header (append or merge) + switch(key.lowercased()) { + + // Headers with an array value (can appear multiple times, but can't be merged) + // + case "set-cookie": + if let headerKey = caseInsensitiveMap[key.lowercased()] { + headers[headerKey]? += value + } else { + set(key, value: value) + } + + + // Headers with a simple value that can be merged + // + default: + guard let headerKey = caseInsensitiveMap[key.lowercased()], let oldValue = headers[headerKey]?.first else { + set(key, value: value) + return + } + let newValue = oldValue + ", " + value.joined(separator: ", ") + headers[headerKey]?[0] = newValue + } + } + + /// + /// Append values to the header + /// + /// - Parameter key: the key + /// - Parameter value: the value + /// + public func append(_ key: String, value: String) { + + append(key, value: [value]) + } + + /// + /// Gets the header (case insensitive) + /// + /// - Parameter key: the key + /// + /// - Returns: the value for the key + /// + private func get(_ key: String) -> [String]? { + if let headerKey = caseInsensitiveMap[key.lowercased()] { + return headers[headerKey] + } + + return nil + } + + /// + /// Set the header value + /// + /// - Parameter key: the key + /// - Parameter value: the value + /// + /// - Returns: the value for the key as a list + /// + private func set(_ key: String, value: [String]) { + + headers[key] = value + caseInsensitiveMap[key.lowercased()] = key + } + + /// + /// Remove the header by key (case insensitive) + /// + /// - Parameter key: the key + /// + private func remove(_ key: String) { + + if let headerKey = caseInsensitiveMap[key.lowercased()] { + headers[headerKey] = nil + } + caseInsensitiveMap[key.lowercased()] = nil + } +} \ No newline at end of file diff --git a/Sources/KituraNet/IncomingMessage.swift b/Sources/KituraNet/IncomingMessage.swift index 09e9315..4817871 100644 --- a/Sources/KituraNet/IncomingMessage.swift +++ b/Sources/KituraNet/IncomingMessage.swift @@ -39,25 +39,10 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// public private(set) var httpVersionMinor: UInt16? - // - // Storage for headers - // - private let headerStorage = HeaderStorage() - - /// - /// Set of headers who's value is a String - /// - public private(set) var headers: SimpleHeaders - /// - /// Set of headers who's value is an Array + /// Set of headers /// - public private(set) var headersAsArrays: ArrayHeaders - - /// - /// Raw headers before processing - /// - public private(set) var rawHeaders = [String]() + public var headers = HeadersContainer() /// /// HTTP Method @@ -160,9 +145,6 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { init (isRequest: Bool) { httpParser = HTTPParser(isRequest: isRequest) - headers = SimpleHeaders(storage: headerStorage) - headersAsArrays = ArrayHeaders(storage: headerStorage) - httpParser!.delegate = self } @@ -188,17 +170,17 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { return } - var start = 0 - var length = 0 + var start = 0 + var length = 0 while status == .initial { do { if start == 0 { ioBuffer!.length = 0 - length = try helper!.readHelper(into: ioBuffer!) - } + length = try helper!.readHelper(into: ioBuffer!) + } if length > 0 { - let offset = start - start = 0 + let offset = start + start = 0 let (nparsed, upgrade) = parser.execute(UnsafePointer(ioBuffer!.bytes)+offset, length: length) if upgrade == 1 { // TODO handle new protocol @@ -210,7 +192,7 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { status = .initial start = nparsed length -= nparsed - parser.reset() + parser.reset() } else { /* Handle error. Usually just close the connection. */ @@ -376,11 +358,22 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { let headerKey = StringUtils.fromUtf8String(lastHeaderField)! let headerValue = StringUtils.fromUtf8String(lastHeaderValue)! - - rawHeaders.append(headerKey) - rawHeaders.append(headerValue) - - headerStorage.addHeader(key: headerKey, value: headerValue) + + switch(headerKey.lowercased()) { + // Headers with a simple value that are not merged (i.e. duplicates dropped) + // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp + // + case "content-type", "content-length", "user-agent", "referer", "host", + "authorization", "proxy-authorization", "if-modified-since", + "if-unmodified-since", "from", "location", "max-forwards", + "retry-after", "etag", "last-modified", "server", "age", "expires": + if let _ = headers[headerKey] { + break + } + fallthrough + default: + headers.append(headerKey, value: headerValue) + } lastHeaderField.length = 0 lastHeaderValue.length = 0 @@ -442,179 +435,11 @@ public class IncomingMessage : HTTPParserDelegate, SocketReader { /// instructions for when reading is reset /// func reset() { - lastHeaderWasAValue = false - headerStorage.reset() - url.length = 0 - status = .reset - } - -} - -// -// Private class for Header storage -// -internal class HeaderStorage { - // - // Storage for headers who's value is a String - // - internal var simpleHeaders = [String:String]() - - // - // Storage for headers who's value is an Array of Strings - // - internal var arrayHeaders = [String: [String]]() - - func addHeader(key headerKey: String, value headerValue: String) { - - let headerKeyLowerCase = headerKey.lowercased() - - // Determine how to handle the header (i.e. simple header array header,...) - switch(headerKey.lowercased()) { - - // Headers with an array value (can appear multiple times, but can't be merged) - // - case "set-cookie": - var oldArray = arrayHeaders[headerKeyLowerCase] ?? [String]() - oldArray.append(headerValue) - arrayHeaders[headerKeyLowerCase] = oldArray - break - - // Headers with a simple value that are not merged (i.e. duplicates dropped) - // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHTTPHeaderArray.cpp - // - case "content-type", "content-length", "user-agent", "referer", "host", - "authorization", "proxy-authorization", "if-modified-since", - "if-unmodified-since", "from", "location", "max-forwards", - "retry-after", "etag", "last-modified", "server", "age", "expires": - if simpleHeaders[headerKeyLowerCase] == nil { - // ignore the header if we already had one with this key - simpleHeaders[headerKeyLowerCase] = headerValue - } - break - - // Headers with a simple value that can be merged - // - default: - if let oldValue = simpleHeaders[headerKeyLowerCase] { - simpleHeaders[headerKeyLowerCase] = oldValue + ", " + headerValue - } - else { - simpleHeaders[headerKeyLowerCase] = headerValue - } - break - } - } - - func reset() { - simpleHeaders.removeAll() - arrayHeaders.removeAll() - } -} - -// -// Class to "simulate" Dictionary access of headers with simple values -// -public class SimpleHeaders { - internal let storage: HeaderStorage - - private init(storage: HeaderStorage) { - self.storage = storage - } - - public subscript(key: String) -> String? { - let keyLowercase = key.lowercased() - var result = storage.simpleHeaders[keyLowercase] - if result == nil { - if let entry = storage.arrayHeaders[keyLowercase] { - result = entry[0] - } - } - return result - } -} - -extension SimpleHeaders: Sequence { - public typealias Iterator = SimpleHeadersIterator - - public func makeIterator() -> SimpleHeaders.Iterator { - return SimpleHeaders.Iterator(self) - } -} - -public struct SimpleHeadersIterator: IteratorProtocol { - public typealias Element = (String, String) - - private var simpleIterator: DictionaryIterator - private var arrayIterator: DictionaryIterator - - init(_ simpleHeaders: SimpleHeaders) { - simpleIterator = simpleHeaders.storage.simpleHeaders.makeIterator() - arrayIterator = simpleHeaders.storage.arrayHeaders.makeIterator() - } - - public mutating func next() -> SimpleHeadersIterator.Element? { - var result = simpleIterator.next() - if result == nil { - if let arrayElem = arrayIterator.next() { - let (arrayKey, arrayValue) = arrayElem - result = (arrayKey, arrayValue[0]) - } - } - return result - } -} - -// -// Class to "simulate" Dictionary access of headers with array values -// -public class ArrayHeaders { - private let storage: HeaderStorage - - private init(storage: HeaderStorage) { - self.storage = storage - } - - public subscript(key: String) -> [String]? { - let keyLowercase = key.lowercased() - var result = storage.arrayHeaders[keyLowercase] - if result == nil { - if let entry = storage.simpleHeaders[keyLowercase] { - result = entry.components(separatedBy: ", ") - } - } - return result - } -} - -extension ArrayHeaders: Sequence { - public typealias Iterator = ArrayHeadersIterator - - public func makeIterator() -> ArrayHeaders.Iterator { - return ArrayHeaders.Iterator(self) - } -} - -public struct ArrayHeadersIterator: IteratorProtocol { - public typealias Element = (String, [String]) - - private var arrayIterator: DictionaryIterator - private var simpleIterator: DictionaryIterator - - init(_ arrayHeaders: ArrayHeaders) { - arrayIterator = arrayHeaders.storage.arrayHeaders.makeIterator() - simpleIterator = arrayHeaders.storage.simpleHeaders.makeIterator() + lastHeaderWasAValue = false + url.length = 0 + status = .reset } - public mutating func next() -> ArrayHeadersIterator.Element? { - var result = arrayIterator.next() - if result == nil { - if let simpleElem = simpleIterator.next() { - let (simpleKey, simpleValue) = simpleElem - result = (simpleKey, simpleValue.components(separatedBy: ", ")) - } - } - return result - } } /// diff --git a/Sources/KituraNet/ServerResponse.swift b/Sources/KituraNet/ServerResponse.swift index 49e1db2..619700a 100644 --- a/Sources/KituraNet/ServerResponse.swift +++ b/Sources/KituraNet/ServerResponse.swift @@ -46,13 +46,8 @@ public class ServerResponse : SocketWriter { /// /// TODO: ??? /// - private var singleHeaders: [String: String] = [:] - - /// - /// TODO: ??? - /// - private var multiHeaders: [String: [String]] = [:] - + public var headers = HeadersContainer() + /// /// Status code /// @@ -79,104 +74,7 @@ public class ServerResponse : SocketWriter { self.socket = socket buffer = NSMutableData(capacity: ServerResponse.bufferSize)! - setHeader("Date", value: SPIUtils.httpDate()) - - } - - /// - /// Get a specific headers for the response by key - /// - /// - Parameter key: the header key - /// - public func getHeader(_ key: String) -> String? { - - return singleHeaders[key] - - } - - /// - /// Get all values on a specific key - /// - /// - Parameter key: the header key - /// - /// - Returns: a list of String values - /// - public func getHeaders(_ key: String) -> [String]? { - - return multiHeaders[key] - - } - - /// - /// Set the value for a header - /// - /// - Parameter key: key - /// - Parameter value: the value - /// - public func setHeader(_ key: String, value: String) { - singleHeaders[key] = value - multiHeaders.removeValue(forKey: key) - } - - /// - /// Set the value for a header (list) - /// - /// - Parameter key: key - /// - Parameter value: the value - /// - public func setHeader(_ key: String, value: [String]) { - multiHeaders[key] = value - singleHeaders.removeValue(forKey: key) - } - - /// - /// Append a value to the header - /// - /// - Parameter key: the header key - /// - Parameter value: string value - /// - public func append(key: String, value: String) { - - if let singleValue = singleHeaders[key] where multiHeaders.count == 0 { - multiHeaders[key] = [singleValue, value] - singleHeaders.removeValue(forKey: key) - } else if let _ = multiHeaders[key] { - multiHeaders[key]!.append(value) - } else { - setHeader(key, value: value) - } - } - - /// - /// Append values to the header - /// - /// - Parameter key: the header key - /// - Parameter value: array of string values - /// - public func append(key: String, value: [String]) { - - if let singleValue = singleHeaders[key] where multiHeaders.count == 0 { - multiHeaders[key] = [singleValue] + value - singleHeaders.removeValue(forKey: key) - } else if let _ = multiHeaders[key] { - multiHeaders[key]! = multiHeaders[key]! + value - } else { - if value.count == 1 { - setHeader(key, value: value.first!) - } else { - setHeader(key, value: value) - } - } - } - - /// - /// Remove a key from the header - /// - /// - Parameter key: key - /// - public func removeHeader(key: String) { - singleHeaders.removeValue(forKey: key) - multiHeaders.removeValue(forKey: key) + headers["Date"] = [SPIUtils.httpDate()] } /// @@ -273,14 +171,7 @@ public class ServerResponse : SocketWriter { try writeToSocketThroughBuffer(text: statusText!) try writeToSocketThroughBuffer(text: "\r\n") - for (key, value) in singleHeaders { - try writeToSocketThroughBuffer(text: key) - try writeToSocketThroughBuffer(text: ": ") - try writeToSocketThroughBuffer(text: value) - try writeToSocketThroughBuffer(text: "\r\n") - } - - for (key, valueSet) in multiHeaders { + for (key, valueSet) in headers.headers { for value in valueSet { try writeToSocketThroughBuffer(text: key) try writeToSocketThroughBuffer(text: ": ") diff --git a/Tests/KituraNet/ClientTests.swift b/Tests/KituraNet/ClientTests.swift index af8e83e..5c7a4f3 100644 --- a/Tests/KituraNet/ClientTests.swift +++ b/Tests/KituraNet/ClientTests.swift @@ -34,7 +34,7 @@ class ClientTests: XCTestCase { XCTAssertEqual(response!.statusCode, HTTPStatusCode.OK, "HTTP Status code was \(response!.statusCode)") let contentType = response!.headers["Content-Type"] XCTAssertNotNil(contentType, "No ContentType header in response") - XCTAssertEqual(contentType!, "text/html", "Content-Type header wasn't `text/html`") + XCTAssertEqual(contentType!, ["text/html"], "Content-Type header wasn't `text/html`") } } } \ No newline at end of file From 2b5db7235007471f0baaf60a5cc6ef1bfb741f99 Mon Sep 17 00:00:00 2001 From: Daniel Firsht Date: Mon, 16 May 2016 14:04:08 -0500 Subject: [PATCH 11/14] Expose makeIterator method in Headers --- Sources/KituraNet/HeadersContainer.swift | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Sources/KituraNet/HeadersContainer.swift b/Sources/KituraNet/HeadersContainer.swift index 4f517ca..43aeef9 100644 --- a/Sources/KituraNet/HeadersContainer.swift +++ b/Sources/KituraNet/HeadersContainer.swift @@ -54,8 +54,7 @@ public class HeadersContainer { // Determine how to handle the header (append or merge) switch(key.lowercased()) { - // Headers with an array value (can appear multiple times, but can't be merged) - // + // Headers with an array value (can appear multiple times, but can't be merged) case "set-cookie": if let headerKey = caseInsensitiveMap[key.lowercased()] { headers[headerKey]? += value @@ -64,8 +63,7 @@ public class HeadersContainer { } - // Headers with a simple value that can be merged - // + // Headers with a simple value that can be merged default: guard let headerKey = caseInsensitiveMap[key.lowercased()], let oldValue = headers[headerKey]?.first else { set(key, value: value) @@ -76,6 +74,13 @@ public class HeadersContainer { } } + /// + /// Creates an iterator of the underlying dictionary + /// + public func makeIterator() -> DictionaryIterator> { + return headers.makeIterator() + } + /// /// Append values to the header /// From ee863d73d68899075ddbd7c8267028c7dda7696d Mon Sep 17 00:00:00 2001 From: Daniel Firsht Date: Mon, 16 May 2016 14:29:06 -0500 Subject: [PATCH 12/14] IBM-Swift/Kitura-net#27 changed option to only allow SSL disabling --- Sources/KituraNet/ClientRequest.swift | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Sources/KituraNet/ClientRequest.swift b/Sources/KituraNet/ClientRequest.swift index 39be7c2..af73668 100644 --- a/Sources/KituraNet/ClientRequest.swift +++ b/Sources/KituraNet/ClientRequest.swift @@ -82,7 +82,7 @@ public class ClientRequest: SocketWriter { private var callback: ClientRequestCallback - private var curlOptions: [(CURLoption, Int)]? + private var disableSSLVerification = false /// /// Initializes a ClientRequest instance @@ -139,8 +139,8 @@ public class ClientRequest: SocketWriter { self.password = password case .maxRedirects(let maxRedirects): self.maxRedirects = maxRedirects - case .curlOptions(let curlOptions): - self.curlOptions = curlOptions + case .disableSSLVerification: + self.disableSSLVerification = true } } @@ -286,10 +286,9 @@ public class ClientRequest: SocketWriter { // HTTP parser does the decoding curlHelperSetOptInt(handle!, CURLOPT_HTTP_TRANSFER_DECODING, 0) curlHelperSetOptString(handle!, CURLOPT_URL, UnsafeMutablePointer(urlBuf.bytes)) - if let curlOptions = curlOptions { - for (curlOption, value) in curlOptions { - curlHelperSetOptInt(handle!, curlOption, value) - } + if disableSSLVerification { + curlHelperSetOptInt(handle!, CURLOPT_SSL_VERIFYHOST, 0) + curlHelperSetOptInt(handle!, CURLOPT_SSL_VERIFYPEER, 0) } setMethod() let count = writeBuffers.count @@ -376,7 +375,7 @@ extension ClientRequest: CurlInvokerDelegate { public enum ClientRequestOptions { case method(String), schema(String), hostname(String), port(Int16), path(String), - headers([String: String]), username(String), password(String), maxRedirects(Int), curlOptions([(CURLoption, Int)]) + headers([String: String]), username(String), password(String), maxRedirects(Int), disableSSLVerification } From f1fe6b52a482b6b678ca697842ab8b58bcb18385 Mon Sep 17 00:00:00 2001 From: Daniel Firsht Date: Tue, 17 May 2016 15:07:26 -0500 Subject: [PATCH 13/14] IBM-Swift/Kitura#478 Added methods for Collection protocol --- Sources/KituraNet/HeadersContainer.swift | 34 ++++++++++++++++++------ 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/Sources/KituraNet/HeadersContainer.swift b/Sources/KituraNet/HeadersContainer.swift index 43aeef9..ec6981b 100644 --- a/Sources/KituraNet/HeadersContainer.swift +++ b/Sources/KituraNet/HeadersContainer.swift @@ -74,13 +74,6 @@ public class HeadersContainer { } } - /// - /// Creates an iterator of the underlying dictionary - /// - public func makeIterator() -> DictionaryIterator> { - return headers.makeIterator() - } - /// /// Append values to the header /// @@ -92,6 +85,13 @@ public class HeadersContainer { append(key, value: [value]) } + /// + /// Creates an iterator of the underlying dictionary + /// + public func makeIterator() -> DictionaryIterator> { + return headers.makeIterator() + } + /// /// Gets the header (case insensitive) /// @@ -133,4 +133,22 @@ public class HeadersContainer { } caseInsensitiveMap[key.lowercased()] = nil } -} \ No newline at end of file +} + +/// Variables and methods to implement the Collection protocol +extension HeadersContainer { + + public var startIndex:DictionaryIndex { return headers.startIndex } + + public var endIndex:DictionaryIndex { return headers.endIndex } + + public subscript(position: DictionaryIndex) -> (key: String, value: [String]) { + get { + return headers[position] + } + } + + public func index(after i: DictionaryIndex) -> DictionaryIndex { + return headers.index(after: i) + } +} From 67207973965dfb1abe4b77354a329bee75369960 Mon Sep 17 00:00:00 2001 From: Shmuel Kallner Date: Wed, 18 May 2016 14:25:06 +0300 Subject: [PATCH 14/14] IBM-Swift/Kitura#481 Implemented the Sequence protocol in HeadersContainer --- Sources/KituraNet/HeadersContainer.swift | 31 ++++++++++++++---------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/Sources/KituraNet/HeadersContainer.swift b/Sources/KituraNet/HeadersContainer.swift index ec6981b..6d96c90 100644 --- a/Sources/KituraNet/HeadersContainer.swift +++ b/Sources/KituraNet/HeadersContainer.swift @@ -81,17 +81,10 @@ public class HeadersContainer { /// - Parameter value: the value /// public func append(_ key: String, value: String) { - + append(key, value: [value]) } - - /// - /// Creates an iterator of the underlying dictionary - /// - public func makeIterator() -> DictionaryIterator> { - return headers.makeIterator() - } - + /// /// Gets the header (case insensitive) /// @@ -137,18 +130,30 @@ public class HeadersContainer { /// Variables and methods to implement the Collection protocol extension HeadersContainer { - + public var startIndex:DictionaryIndex { return headers.startIndex } - + public var endIndex:DictionaryIndex { return headers.endIndex } - + public subscript(position: DictionaryIndex) -> (key: String, value: [String]) { get { return headers[position] } } - + public func index(after i: DictionaryIndex) -> DictionaryIndex { return headers.index(after: i) } } + +/// Implement the Sequence protocol +extension HeadersContainer: Sequence { + public typealias Iterator = DictionaryIterator> + + /// + /// Creates an iterator of the underlying dictionary + /// + public func makeIterator() -> HeadersContainer.Iterator { + return headers.makeIterator() + } +}