Ver Fonte

Custom split/replace/trim/fromUInt8 String methods replaced by Swift's build in methods.

Damian Kołakowski há 9 anos atrás
pai
commit
7b3b96e820

+ 12 - 9
Sources/HttpParser.swift

@@ -17,7 +17,7 @@ public class HttpParser {
     
     public func readHttpRequest(_ socket: Socket) throws -> HttpRequest {
         let statusLine = try socket.readLine()
-        let statusLineTokens = statusLine.split(" ")
+        let statusLineTokens = statusLine.components(separatedBy: " ")
         if statusLineTokens.count < 3 {
             throw HttpParserError.InvalidStatusLine(statusLine)
         }
@@ -33,13 +33,16 @@ public class HttpParser {
     }
     
     private func extractQueryParams(_ url: String) -> [(String, String)] {
-        guard let query = url.split("?").last else {
+        let tokens = url.components(separatedBy: "?")
+        guard let query = tokens.last, tokens.count >= 2 else {
             return []
         }
-        return query.split("&").reduce([(String, String)]()) { (c, s) -> [(String, String)] in
-            let tokens = s.split(1, separator: "=")
-            if let name = tokens.first?.removingPercentEncoding, let value = tokens.last?.removingPercentEncoding {
-                return c + [(name, value)]
+        return query.components(separatedBy: "&").reduce([(String, String)]()) { (c, s) -> [(String, String)] in
+            let tokens = s.components(separatedBy: "=")
+            let name = tokens.first?.removingPercentEncoding
+            let value = tokens.count > 1 ? (tokens.last?.removingPercentEncoding ?? "") : ""
+            if let nameFound = name {
+                return c + [(nameFound, value)]
             }
             return c
         }
@@ -54,9 +57,9 @@ public class HttpParser {
     private func readHeaders(_ socket: Socket) throws -> [String: String] {
         var headers = [String: String]()
         while case let headerLine = try socket.readLine() , !headerLine.isEmpty {
-            let headerTokens = headerLine.split(1, separator: ":")
+            let headerTokens = headerLine.components(separatedBy: ":")
             if let name = headerTokens.first, let value = headerTokens.last {
-                headers[name.lowercased()] = value.trim()
+                headers[name.lowercased()] = value.trimmingCharacters(in: .whitespaces)
             }
         }
         return headers
@@ -64,7 +67,7 @@ public class HttpParser {
     
     func supportsKeepAlive(_ headers: [String: String]) -> Bool {
         if let value = headers["connection"] {
-            return "keep-alive" == value.trim()
+            return "keep-alive" == value.trimmingCharacters(in: .whitespaces)
         }
         return false
     }

+ 20 - 16
Sources/HttpRequest.swift

@@ -21,22 +21,26 @@ public class HttpRequest {
         guard let headerValue = headers[headerName] else {
             return false
         }
-        return headerValue.split(",").filter({ $0.trim().lowercased() == token }).count > 0
+        return headerValue.components(separatedBy: ",").filter({ $0.trimmingCharacters(in: .whitespaces).lowercased() == token }).count > 0
     }
     
     public func parseUrlencodedForm() -> [(String, String)] {
         guard let contentTypeHeader = headers["content-type"] else {
             return []
         }
-        let contentTypeHeaderTokens = contentTypeHeader.split(";").map { $0.trim() }
+        let contentTypeHeaderTokens = contentTypeHeader.components(separatedBy: ";").map { $0.trimmingCharacters(in: .whitespaces) }
         guard let contentType = contentTypeHeaderTokens.first, contentType == "application/x-www-form-urlencoded" else {
             return []
         }
-        return String.fromUInt8(body).split("&").map { param -> (String, String) in
-            let tokens = param.split("=")
+        guard let utf8String = String(bytes: body, encoding: .utf8) else {
+            // Consider to throw an exception here (examine the encoding from headers).
+            return []
+        }
+        return utf8String.components(separatedBy: "&").map { param -> (String, String) in
+            let tokens = param.components(separatedBy: "=")
             if let name = tokens.first?.removingPercentEncoding, let value = tokens.last?.removingPercentEncoding, tokens.count == 2 {
-                return (name.replace(old: "+", " "),
-                        value.replace(old: "+", " "))
+                return (name.replacingOccurrences(of: "+", with: " "),
+                        value.replacingOccurrences(of: "+", with: " "))
             }
             return ("","")
         }
@@ -60,9 +64,9 @@ public class HttpRequest {
                 guard header.key == headerName else {
                     return combined
                 }
-                let headerValueParams = header.value.split(";").map { $0.trim() }
+                let headerValueParams = header.value.components(separatedBy: ";").map { $0.trimmingCharacters(in: .whitespaces) }
                 return headerValueParams.reduce(combined, { (results, token) -> [String] in
-                    let parameterTokens = token.split(1, separator: "=")
+                    let parameterTokens = token.components(separatedBy: "=")
                     if parameterTokens.first == parameter, let value = parameterTokens.last {
                         return results + [value]
                     }
@@ -76,13 +80,13 @@ public class HttpRequest {
         guard let contentTypeHeader = headers["content-type"] else {
             return []
         }
-        let contentTypeHeaderTokens = contentTypeHeader.split(";").map { $0.trim() }
+        let contentTypeHeaderTokens = contentTypeHeader.components(separatedBy: ";").map { $0.trimmingCharacters(in: .whitespaces) }
         guard let contentType = contentTypeHeaderTokens.first, contentType == "multipart/form-data" else {
             return []
         }
         var boundary: String? = nil
         contentTypeHeaderTokens.forEach({
-            let tokens = $0.split("=")
+            let tokens = $0.components(separatedBy: "=")
             if let key = tokens.first, key == "boundary" && tokens.count == 2 {
                 boundary = tokens.last
             }
@@ -104,17 +108,17 @@ public class HttpRequest {
     
     private func nextMultiPart(_ generator: inout IndexingIterator<[UInt8]>, boundary: String, isFirst: Bool) -> MultiPart? {
         if isFirst {
-            guard nextMultiPartLine(&generator) == boundary else {
+            guard nextUTF8MultiPartLine(&generator) == boundary else {
                 return nil
             }
         } else {
-            let /* ignore */ _ = nextMultiPartLine(&generator)
+            let /* ignore */ _ = nextUTF8MultiPartLine(&generator)
         }
         var headers = [String: String]()
-        while let line = nextMultiPartLine(&generator), !line.isEmpty {
-            let tokens = line.split(":")
+        while let line = nextUTF8MultiPartLine(&generator), !line.isEmpty {
+            let tokens = line.components(separatedBy: ":")
             if let name = tokens.first, let value = tokens.last, tokens.count == 2 {
-                headers[name.lowercased()] = value.trim()
+                headers[name.lowercased()] = value.trimmingCharacters(in: .whitespaces)
             }
         }
         guard let body = nextMultiPartBody(&generator, boundary: boundary) else {
@@ -123,7 +127,7 @@ public class HttpRequest {
         return MultiPart(headers: headers, body: body)
     }
     
-    private func nextMultiPartLine(_ generator: inout IndexingIterator<[UInt8]>) -> String? {
+    private func nextUTF8MultiPartLine(_ generator: inout IndexingIterator<[UInt8]>) -> String? {
         var temp = [UInt8]()
         while let value = generator.next() {
             if value > HttpRequest.CR {

+ 10 - 1
Sources/HttpRouter.swift

@@ -7,6 +7,7 @@
 
 import Foundation
 
+
 open class HttpRouter {
     
     public init() {
@@ -124,9 +125,17 @@ open class HttpRouter {
     }
     
     private func stripQuery(_ path: String) -> String {
-        if let path = path.split("?").first {
+        if let path = path.components(separatedBy: "?").first {
             return path
         }
         return path
     }
 }
+
+extension String {
+    
+    public func split(_ separator: Character) -> [String] {
+        return self.characters.split { $0 == separator }.map(String.init)
+    }
+    
+}

+ 2 - 2
Sources/String+BASE64.swift

@@ -12,7 +12,7 @@ extension String {
     
     private static let CODES = [UInt8]("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=".utf8)
     
-    public static func toBase64(_ data: [UInt8]) -> String {
+    public static func toBase64(_ data: [UInt8]) -> String? {
         
         // Based on: https://en.wikipedia.org/wiki/Base64#Sample_Implementation_in_Java
         
@@ -41,6 +41,6 @@ extension String {
                 result.append(contentsOf: [UInt8]("==".utf8));
             }
         }
-        return String.fromUInt8(result)
+        return String(bytes: result, encoding: .utf8)
     }
 }

+ 0 - 27
Sources/String+Misc.swift

@@ -9,20 +9,6 @@ import Foundation
 
 
 extension String {
-
-    public func split(_ separator: Character) -> [String] {
-        return self.characters.split { $0 == separator }.map(String.init)
-    }
-    
-    public func split(_ maxSplit: Int = Int.max, separator: Character) -> [String] {
-        return self.characters.split(maxSplits: maxSplit, omittingEmptySubsequences: true) { $0 == separator }.map(String.init)
-    }
-    
-    public func replace(old: Character, _ new: Character) -> String {
-        var buffer = [Character]()
-        self.characters.forEach { buffer.append($0 == old ? new : $0) }
-        return String(buffer)
-    }
     
     public func unquote() -> String {
         var scalars = self.unicodeScalars;
@@ -33,19 +19,6 @@ extension String {
         }
         return self
     }
-    
-    public func trim() -> String {
-        var scalars = self.unicodeScalars
-        while let _ = scalars.first?.asWhitespace() { scalars.removeFirst() }
-        while let _ = scalars.last?.asWhitespace() { scalars.removeLast() }
-        return String(scalars)
-    }
-    
-    public static func fromUInt8(_ array: [UInt8]) -> String {
-        // Apple changes the definition of String(data: .... ) every release so let's stay with 'fromUInt8(...)' wrapper.
-        return array.reduce("", { $0.0 + String(UnicodeScalar($0.1)) })
-    }
-    
 }
 
 extension UnicodeScalar {

+ 4 - 2
Sources/WebSockets.swift

@@ -119,8 +119,10 @@ public func websocket(
                 session.writeCloseFrame()
             }
         }
-        let secWebSocketAccept = String.toBase64((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").sha1())
-        let headers = [ "Upgrade": "WebSocket", "Connection": "Upgrade", "Sec-WebSocket-Accept": secWebSocketAccept]
+        guard let secWebSocketAccept = String.toBase64((secWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").sha1()) else {
+            return HttpResponse.internalServerError
+        }
+        let headers = ["Upgrade": "WebSocket", "Connection": "Upgrade", "Sec-WebSocket-Accept": secWebSocketAccept]
         return HttpResponse.switchProtocols(headers, protocolSessionClosure)
     }
 }

+ 29 - 32
XCode/SwifterTestsCommon/SwifterTestsStringExtensions.swift

@@ -68,43 +68,40 @@ class SwifterTestsStringExtensions: XCTestCase {
     }
     
     func testMiscTrim() {
-        XCTAssertEqual("".trim(), "")
-        XCTAssertEqual("\n".trim(), "")
-        XCTAssertEqual("\t".trim(), "")
-        XCTAssertEqual("\r".trim(), "")
-        XCTAssertEqual(" ".trim(), "")
-        XCTAssertEqual("      ".trim(), "")
-        XCTAssertEqual("1 test     ".trim(), "1 test")
-        XCTAssertEqual("      test          ".trim(), "test")
-        XCTAssertEqual("   \t\n\rtest          ".trim(), "test")
-        XCTAssertEqual("   \t\n\rtest  n   \n\t asd    ".trim(), "test  n   \n\t asd")
+        XCTAssertEqual("".trimmingCharacters(in: .whitespacesAndNewlines), "")
+        XCTAssertEqual(" ".trimmingCharacters(in: .whitespacesAndNewlines), "")
+        XCTAssertEqual("      ".trimmingCharacters(in: .whitespacesAndNewlines), "")
+        XCTAssertEqual("1 test     ".trimmingCharacters(in: .whitespacesAndNewlines), "1 test")
+        XCTAssertEqual("      test          ".trimmingCharacters(in: .whitespacesAndNewlines), "test")
+        XCTAssertEqual("   \t\n\rtest          ".trimmingCharacters(in: .whitespacesAndNewlines), "test")
+        XCTAssertEqual("   \t\n\rtest  n   \n\t asd    ".trimmingCharacters(in: .whitespacesAndNewlines), "test  n   \n\t asd")
     }
 
     func testMiscReplace() {
-        XCTAssertEqual("".replace(old: "+", "-"), "")
-        XCTAssertEqual("test".replace(old: "+", "-"), "test")
-        XCTAssertEqual("+++".replace(old: "+", "-"), "---")
-        XCTAssertEqual("t&e&s&t12%3%".replace(old: "&", "+").replace(old: "%", "+"), "t+e+s+t12+3+")
-        XCTAssertEqual("test 1234 #$%^&*( test   ".replace(old: " ", "_"), "test_1234_#$%^&*(_test___")
+        XCTAssertEqual("".replacingOccurrences(of: "+", with: "-"), "")
+        XCTAssertEqual("test".replacingOccurrences(of: "+", with: "-"), "test")
+        XCTAssertEqual("+++".replacingOccurrences(of: "+", with: "-"), "---")
+        XCTAssertEqual("t&e&s&t12%3%".replacingOccurrences(of: "&", with: "+").replacingOccurrences(of: "%", with: "+"), "t+e+s+t12+3+")
+        XCTAssertEqual("test 1234 #$%^&*( test   ".replacingOccurrences(of: " ", with: "_"), "test_1234_#$%^&*(_test___")
     }
     
     func testMiscRemovePercentEncoding() {
-        XCTAssertEqual("".removePercentEncoding(), "")
-        XCTAssertEqual("%20".removePercentEncoding(), " ")
-        XCTAssertEqual("%22".removePercentEncoding(), "\"")
-        XCTAssertEqual("%25".removePercentEncoding(), "%")
-        XCTAssertEqual("%2d".removePercentEncoding(), "-")
-        XCTAssertEqual("%2e".removePercentEncoding(), ".")
-        XCTAssertEqual("%3C".removePercentEncoding(), "<")
-        XCTAssertEqual("%3E".removePercentEncoding(), ">")
-        XCTAssertEqual("%5C".removePercentEncoding(), "\\")
-        XCTAssertEqual("%5E".removePercentEncoding(), "^")
-        XCTAssertEqual("%5F".removePercentEncoding(), "_")
-        XCTAssertEqual("%60".removePercentEncoding(), "`")
-        XCTAssertEqual("%7B".removePercentEncoding(), "{")
-        XCTAssertEqual("%7C".removePercentEncoding(), "|")
-        XCTAssertEqual("%7D".removePercentEncoding(), "}")
-        XCTAssertEqual("%7E".removePercentEncoding(), "~")
-        XCTAssertEqual("%7e".removePercentEncoding(), "~")
+        XCTAssertEqual("".removingPercentEncoding!, "")
+        XCTAssertEqual("%20".removingPercentEncoding!, " ")
+        XCTAssertEqual("%22".removingPercentEncoding!, "\"")
+        XCTAssertEqual("%25".removingPercentEncoding!, "%")
+        XCTAssertEqual("%2d".removingPercentEncoding!, "-")
+        XCTAssertEqual("%2e".removingPercentEncoding!, ".")
+        XCTAssertEqual("%3C".removingPercentEncoding!, "<")
+        XCTAssertEqual("%3E".removingPercentEncoding!, ">")
+        XCTAssertEqual("%5C".removingPercentEncoding!, "\\")
+        XCTAssertEqual("%5E".removingPercentEncoding!, "^")
+        XCTAssertEqual("%5F".removingPercentEncoding!, "_")
+        XCTAssertEqual("%60".removingPercentEncoding!, "`")
+        XCTAssertEqual("%7B".removingPercentEncoding!, "{")
+        XCTAssertEqual("%7C".removingPercentEncoding!, "|")
+        XCTAssertEqual("%7D".removingPercentEncoding!, "}")
+        XCTAssertEqual("%7E".removingPercentEncoding!, "~")
+        XCTAssertEqual("%7e".removingPercentEncoding!, "~")
     }
 }