Procházet zdrojové kódy

Merge pull request #379 from Vkt0r/route-overlapping

Route overlapping issue
Victor Sigler před 7 roky
rodič
revize
bdfd5f5d9e

+ 1 - 1
.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: 

+ 2 - 1
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))
     }
 }

+ 21 - 11
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: "/")

+ 1 - 0
XCode/LinuxMain.swift

@@ -1,4 +1,5 @@
 import XCTest
+
 import SwifterTests
 
 var tests = [XCTestCaseEntry]()

+ 22 - 18
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;
 		};

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifterMac.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1020"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifteriOS.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1020"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwifteriOSTests.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1010"
+   LastUpgradeVersion = "1020"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwiftermacOSTests.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1010"
+   LastUpgradeVersion = "1020"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwiftertvOS.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1000"
+   LastUpgradeVersion = "1020"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 1 - 1
XCode/Swifter.xcodeproj/xcshareddata/xcschemes/SwiftertvOSTests.xcscheme

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1010"
+   LastUpgradeVersion = "1020"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"

+ 35 - 0
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)
+    }
 }

+ 27 - 11
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