From a9551c9bb3e3b804863a7a4621d401f8e3d4f42a Mon Sep 17 00:00:00 2001 From: Victor Sigler Date: Sat, 30 Mar 2019 15:20:28 -0400 Subject: [PATCH 1/5] Fix an issue in the HttpRouter * Fix an issue causing the HttpRouter was not resolving correclty the overlapping of routes. * Add the new property `isEndOfRoute` to the `Node` class to know when a node is the end of route or not. --- Sources/HttpRouter.swift | 32 ++++++++++++++-------- XCode/Tests/SwifterTestsHttpRouter.swift | 35 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/Sources/HttpRouter.swift b/Sources/HttpRouter.swift index c27a1fcb..3ec27aaa 100644 --- a/Sources/HttpRouter.swift +++ b/Sources/HttpRouter.swift @@ -7,14 +7,19 @@ import Foundation - open class HttpRouter { - public init() { - } + public init() {} private class Node { + + /// The children nodes that form the route var nodes = [String: Node]() + + /// Define whether or not this node is the end of a route + var isEndOfRoute: Bool = false + + /// The closure to handle the route var handler: ((HttpRequest) -> HttpResponse)? = nil } @@ -69,15 +74,20 @@ open class HttpRouter { } private func inflate(_ node: inout Node, generator: inout IndexingIterator<[String]>) -> Node { - if let pathSegment = generator.next() { - if let _ = node.nodes[pathSegment] { - return inflate(&node.nodes[pathSegment]!, generator: &generator) + + var currentNode = node + + while let pathSegment = generator.next() { + if let nextNode = currentNode.nodes[pathSegment] { + currentNode = nextNode + } else { + currentNode.nodes[pathSegment] = Node() + currentNode = currentNode.nodes[pathSegment]! } - var nextNode = Node() - node.nodes[pathSegment] = nextNode - return inflate(&nextNode, generator: &generator) } - return node + + currentNode.isEndOfRoute = true + return currentNode } private func findHandler(_ node: inout Node, params: inout [String: String], generator: inout IndexingIterator<[String]>) -> ((HttpRequest) -> HttpResponse)? { @@ -103,7 +113,7 @@ open class HttpRouter { var currentIndex = index + 1 let variableNodes = node.nodes.filter { $0.0.first == ":" } if let variableNode = variableNodes.first { - if variableNode.1.nodes.count == 0 { + if currentIndex == count && variableNode.1.isEndOfRoute { // if it's the last element of the pattern and it's a variable, stop the search and // append a tail as a value for the variable. let tail = generator.joined(separator: "/") diff --git a/XCode/Tests/SwifterTestsHttpRouter.swift b/XCode/Tests/SwifterTestsHttpRouter.swift index 91cc7588..45186256 100644 --- a/XCode/Tests/SwifterTestsHttpRouter.swift +++ b/XCode/Tests/SwifterTestsHttpRouter.swift @@ -153,4 +153,39 @@ class SwifterTestsHttpRouter: XCTestCase { XCTAssertTrue(foundStaticRoute) XCTAssertTrue(foundVariableRoute) } + + func testHttpRouterHandlesOverlappingPathsInDynamicRoutes() { + let router = HttpRouter() + let request = HttpRequest() + + let firstVariableRouteExpectation = expectation(description: "First Variable Route") + var foundFirstVariableRoute = false + router.register("GET", path: "a/:id") { request in + foundFirstVariableRoute = true + firstVariableRouteExpectation.fulfill() + return HttpResponse.accepted + } + + let secondVariableRouteExpectation = expectation(description: "Second Variable Route") + var foundSecondVariableRoute = false + router.register("GET", path: "a/:id/c") { _ in + foundSecondVariableRoute = true + secondVariableRouteExpectation.fulfill() + return HttpResponse.accepted + } + + let firstRouteResult = router.route("GET", path: "a/b") + let firstRouterHandler = firstRouteResult?.1 + XCTAssertNotNil(firstRouteResult) + _ = firstRouterHandler?(request) + + let secondRouteResult = router.route("GET", path: "a/b/c") + let secondRouterHandler = secondRouteResult?.1 + XCTAssertNotNil(secondRouteResult) + _ = secondRouterHandler?(request) + + waitForExpectations(timeout: 10, handler: nil) + XCTAssertTrue(foundFirstVariableRoute) + XCTAssertTrue(foundSecondVariableRoute) + } } From fd69c884f4f187e15af8880d0e2231b33944e96f Mon Sep 17 00:00:00 2001 From: Victor Sigler Date: Sat, 30 Mar 2019 15:21:19 -0400 Subject: [PATCH 2/5] Update the project to Xcode 10.2 Update the full project to Xcode 10.2 and the recommended settings to remove the warnings --- XCode/Swifter.xcodeproj/project.pbxproj | 40 ++++++++++--------- .../xcschemes/SwifterMac.xcscheme | 2 +- .../xcschemes/SwifteriOS.xcscheme | 2 +- .../xcschemes/SwifteriOSTests.xcscheme | 2 +- .../xcschemes/SwiftermacOSTests.xcscheme | 2 +- .../xcschemes/SwiftertvOS.xcscheme | 2 +- .../xcschemes/SwiftertvOSTests.xcscheme | 2 +- 7 files changed, 28 insertions(+), 24 deletions(-) diff --git a/XCode/Swifter.xcodeproj/project.pbxproj b/XCode/Swifter.xcodeproj/project.pbxproj index 5b1897d4..5d59f94e 100644 --- a/XCode/Swifter.xcodeproj/project.pbxproj +++ b/XCode/Swifter.xcodeproj/project.pbxproj @@ -617,27 +617,29 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 1010; - LastUpgradeCheck = 1000; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "Damian KoĊ‚akowski"; TargetAttributes = { 043660C121FED34100497989 = { CreatedOnToolsVersion = 10.1; + LastSwiftMigration = 1020; ProvisioningStyle = Manual; }; 043660D921FED3A300497989 = { CreatedOnToolsVersion = 10.1; + LastSwiftMigration = 1020; ProvisioningStyle = Manual; }; 269B47861D3AAAE20042D137 = { - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; }; 7AE893E61C05127900A29F63 = { CreatedOnToolsVersion = 7.1; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; }; 7AE893FA1C0512C400A29F63 = { CreatedOnToolsVersion = 7.1; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; }; 7C839B6D19422CFF003A6950 = { CreatedOnToolsVersion = 6.0; @@ -649,13 +651,13 @@ }; 7CCD875B1C66099B0068099B = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 1000; + LastSwiftMigration = 1020; }; }; }; buildConfigurationList = 7C839B6919422CFF003A6950 /* Build configuration list for PBXProject "Swifter" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -946,7 +948,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -974,7 +976,7 @@ PRODUCT_BUNDLE_IDENTIFIER = oss.SwiftermacOSTests; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -1000,7 +1002,7 @@ PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = appletvos; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 12.1; }; @@ -1028,7 +1030,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = appletvos; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 12.1; }; @@ -1054,7 +1056,7 @@ PRODUCT_NAME = Swifter; SDKROOT = appletvos; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -1083,7 +1085,7 @@ PRODUCT_NAME = Swifter; SDKROOT = appletvos; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; @@ -1111,7 +1113,7 @@ PRODUCT_NAME = Swifter; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1138,7 +1140,7 @@ PRODUCT_NAME = Swifter; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1166,7 +1168,7 @@ PRODUCT_NAME = Swifter; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1195,7 +1197,7 @@ PRODUCT_NAME = Swifter; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -1207,6 +1209,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -1268,6 +1271,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ALWAYS_SEARCH_USER_PATHS = NO; APPLICATION_EXTENSION_API_ONLY = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; @@ -1406,7 +1410,7 @@ PRODUCT_BUNDLE_IDENTIFIER = pl.kolakowski.SwifteriOSTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -1424,7 +1428,7 @@ PRODUCT_BUNDLE_IDENTIFIER = pl.kolakowski.SwifteriOSTests; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifterMac.xcscheme b/XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifterMac.xcscheme index ba3effcc..b1472c40 100644 --- a/XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifterMac.xcscheme +++ b/XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifterMac.xcscheme @@ -1,6 +1,6 @@ Date: Sat, 30 Mar 2019 19:28:26 -0400 Subject: [PATCH 3/5] Update the Xcode version in the CircleCI config file --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d07af302..adae20e3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,7 +6,7 @@ jobs: TEST_REPORTS: /tmp/test-results LANG: en_US.UTF-8 macos: - xcode: 10.1.0 + xcode: 10.2.0 steps: - checkout - run: From 82549f17c5a0b1d54f59d62b6ff037f100662922 Mon Sep 17 00:00:00 2001 From: Victor Sigler Date: Sat, 30 Mar 2019 19:30:57 -0400 Subject: [PATCH 4/5] Update the Linux manifest tests --- XCode/LinuxMain.swift | 1 + XCode/Tests/XCTestManifests.swift | 38 ++++++++++++++++++++++--------- 2 files changed, 28 insertions(+), 11 deletions(-) diff --git a/XCode/LinuxMain.swift b/XCode/LinuxMain.swift index 581f29f5..42b4a191 100644 --- a/XCode/LinuxMain.swift +++ b/XCode/LinuxMain.swift @@ -1,4 +1,5 @@ import XCTest + import SwifterTests var tests = [XCTestCaseEntry]() diff --git a/XCode/Tests/XCTestManifests.swift b/XCode/Tests/XCTestManifests.swift index f0df36aa..126d69ed 100644 --- a/XCode/Tests/XCTestManifests.swift +++ b/XCode/Tests/XCTestManifests.swift @@ -1,7 +1,11 @@ +#if !canImport(ObjectiveC) import XCTest extension MimeTypeTests { - static let __allTests = [ + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__MimeTypeTests = [ ("testCaseInsensitivity", testCaseInsensitivity), ("testCorrectTypes", testCorrectTypes), ("testDefaultValue", testDefaultValue), @@ -10,15 +14,22 @@ extension MimeTypeTests { } extension SwifterTestsHttpParser { - static let __allTests = [ + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__SwifterTestsHttpParser = [ ("testParser", testParser), ] } extension SwifterTestsHttpRouter { - static let __allTests = [ + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__SwifterTestsHttpRouter = [ ("testHttpRouterEmptyTail", testHttpRouterEmptyTail), ("testHttpRouterHandlesOverlappingPaths", testHttpRouterHandlesOverlappingPaths), + ("testHttpRouterHandlesOverlappingPathsInDynamicRoutes", testHttpRouterHandlesOverlappingPathsInDynamicRoutes), ("testHttpRouterMultiplePathSegmentWildcards", testHttpRouterMultiplePathSegmentWildcards), ("testHttpRouterPercentEncodedPathSegments", testHttpRouterPercentEncodedPathSegments), ("testHttpRouterSimplePathSegments", testHttpRouterSimplePathSegments), @@ -29,7 +40,10 @@ extension SwifterTestsHttpRouter { } extension SwifterTestsStringExtensions { - static let __allTests = [ + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__SwifterTestsStringExtensions = [ ("testBASE64", testBASE64), ("testMiscRemovePercentEncoding", testMiscRemovePercentEncoding), ("testMiscReplace", testMiscReplace), @@ -40,19 +54,21 @@ extension SwifterTestsStringExtensions { } extension SwifterTestsWebSocketSession { - static let __allTests = [ + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__SwifterTestsWebSocketSession = [ ("testParser", testParser), ] } -#if !os(macOS) public func __allTests() -> [XCTestCaseEntry] { return [ - testCase(MimeTypeTests.__allTests), - testCase(SwifterTestsHttpParser.__allTests), - testCase(SwifterTestsHttpRouter.__allTests), - testCase(SwifterTestsStringExtensions.__allTests), - testCase(SwifterTestsWebSocketSession.__allTests), + testCase(MimeTypeTests.__allTests__MimeTypeTests), + testCase(SwifterTestsHttpParser.__allTests__SwifterTestsHttpParser), + testCase(SwifterTestsHttpRouter.__allTests__SwifterTestsHttpRouter), + testCase(SwifterTestsStringExtensions.__allTests__SwifterTestsStringExtensions), + testCase(SwifterTestsWebSocketSession.__allTests__SwifterTestsWebSocketSession), ] } #endif From 16f3d4272de4e2fdcf3fd29ab4e6c54b2e9b028f Mon Sep 17 00:00:00 2001 From: Victor Sigler Date: Sat, 30 Mar 2019 19:55:32 -0400 Subject: [PATCH 5/5] Remove the construction of the UnsafePointer Remove the unnecessary construction of the UnsafePointer as the `sterror` returns a `UnsafeMutablePointer!` --- Sources/Errno.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Errno.swift b/Sources/Errno.swift index acef4235..7d4375b7 100644 --- a/Sources/Errno.swift +++ b/Sources/Errno.swift @@ -10,6 +10,7 @@ import Foundation public class Errno { public class func description() -> String { - return String(cString: UnsafePointer(strerror(errno))) + // https://forums.developer.apple.com/thread/113919 + return String(cString: strerror(errno)) } }