Просмотр исходного кода

Fix an issue in the HttpRouter causing the trail overlapping fails

* Remove the set of the empty string for not matched parameters, by default should be `nil`
* Add unit tests to verify the trail dynamic identifier as end of route and with more dynamic path after the end of the route and the overlapping during the middle of the route
* Update the `XCTestManifest` with the new tests for Linux
Victor Sigler 7 лет назад
Родитель
Сommit
d3806a10e0
3 измененных файлов с 114 добавлено и 46 удалено
  1. 16 19
      Sources/HttpRouter.swift
  2. 85 1
      XCode/Tests/SwifterTestsHttpRouter.swift
  3. 13 26
      XCode/Tests/XCTestManifests.swift

+ 16 - 19
Sources/HttpRouter.swift

@@ -93,7 +93,10 @@ open class HttpRouter {
     private func findHandler(_ node: inout Node, params: inout [String: String], generator: inout IndexingIterator<[String]>) -> ((HttpRequest) -> HttpResponse)? {
     private func findHandler(_ node: inout Node, params: inout [String: String], generator: inout IndexingIterator<[String]>) -> ((HttpRequest) -> HttpResponse)? {
         
         
         var matchedRoutes = [Node]()
         var matchedRoutes = [Node]()
-        findHandler(&node, params: &params, generator: &generator, matchedNodes: &matchedRoutes, index: 0, count: generator.reversed().count)
+        let pattern = generator.map { $0 }
+        let numberOfElements = pattern.count
+        
+        findHandler(&node, params: &params, pattern: pattern , matchedNodes: &matchedRoutes, index: 0, count: numberOfElements)
         return matchedRoutes.first?.handler
         return matchedRoutes.first?.handler
     }
     }
     
     
@@ -102,13 +105,13 @@ open class HttpRouter {
     /// - Parameters:
     /// - Parameters:
     ///   - node: The root node of the tree representing all the routes
     ///   - node: The root node of the tree representing all the routes
     ///   - params: The parameters of the match
     ///   - params: The parameters of the match
-    ///   - generator: The IndexingIterator to iterate through the pattern to match
+    ///   - pattern: The pattern or route to find in the routes tree
     ///   - matchedNodes: An array with the nodes matching the route
     ///   - matchedNodes: An array with the nodes matching the route
     ///   - index: The index of current position in the generator
     ///   - index: The index of current position in the generator
     ///   - count: The number of elements if the route to match
     ///   - count: The number of elements if the route to match
-    private func findHandler(_ node: inout Node, params: inout [String: String], generator: inout IndexingIterator<[String]>, matchedNodes: inout [Node], index: Int, count: Int) {
+    private func findHandler(_ node: inout Node, params: inout [String: String], pattern: [String], matchedNodes: inout [Node], index: Int, count: Int) {
     
     
-        if let pathToken = generator.next()?.removingPercentEncoding {
+        if index < count, let pathToken = pattern[index].removingPercentEncoding {
             
             
             var currentIndex = index + 1
             var currentIndex = index + 1
             let variableNodes = node.nodes.filter { $0.0.first == ":" }
             let variableNodes = node.nodes.filter { $0.0.first == ":" }
@@ -116,7 +119,7 @@ open class HttpRouter {
                 if currentIndex == count && variableNode.1.isEndOfRoute {
                 if currentIndex == count && variableNode.1.isEndOfRoute {
                     // if it's the last element of the pattern and it's a variable, stop the search and
                     // 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.
                     // append a tail as a value for the variable.
-                    let tail = generator.joined(separator: "/")
+                    let tail = pattern[currentIndex..<count].joined(separator: "/")
                     if tail.count > 0 {
                     if tail.count > 0 {
                         params[variableNode.0] = pathToken + "/" + tail
                         params[variableNode.0] = pathToken + "/" + tail
                     } else {
                     } else {
@@ -127,37 +130,31 @@ open class HttpRouter {
                     return
                     return
                 }
                 }
                 params[variableNode.0] = pathToken
                 params[variableNode.0] = pathToken
-                findHandler(&node.nodes[variableNode.0]!, params: &params, generator: &generator, matchedNodes: &matchedNodes, index: currentIndex, count: count)
+                findHandler(&node.nodes[variableNode.0]!, params: &params, pattern: pattern, matchedNodes: &matchedNodes, index: currentIndex, count: count)
             }
             }
             
             
             if var node = node.nodes[pathToken] {
             if var node = node.nodes[pathToken] {
-                findHandler(&node, params: &params, generator: &generator, matchedNodes: &matchedNodes, index: currentIndex, count: count)
+                findHandler(&node, params: &params, pattern: pattern, matchedNodes: &matchedNodes, index: currentIndex, count: count)
             }
             }
             
             
             if var node = node.nodes["*"] {
             if var node = node.nodes["*"] {
-                findHandler(&node, params: &params, generator: &generator, matchedNodes: &matchedNodes, index: currentIndex, count: count)
+                findHandler(&node, params: &params, pattern: pattern, matchedNodes: &matchedNodes, index: currentIndex, count: count)
             }
             }
             
             
             if let startStarNode = node.nodes["**"] {
             if let startStarNode = node.nodes["**"] {
                 let startStarNodeKeys = startStarNode.nodes.keys
                 let startStarNodeKeys = startStarNode.nodes.keys
-                while let pathToken = generator.next() {
+                currentIndex += 1
+                while currentIndex < count, let pathToken = pattern[currentIndex].removingPercentEncoding {
                     currentIndex += 1
                     currentIndex += 1
                     if startStarNodeKeys.contains(pathToken) {
                     if startStarNodeKeys.contains(pathToken) {
-                        findHandler(&startStarNode.nodes[pathToken]!, params: &params, generator: &generator, matchedNodes: &matchedNodes, index: currentIndex, count: count)
+                        findHandler(&startStarNode.nodes[pathToken]!, params: &params, pattern: pattern, matchedNodes: &matchedNodes, index: currentIndex, count: count)
                     }
                     }
                 }
                 }
             }
             }
-        } else if let variableNode = node.nodes.filter({ $0.0.first == ":" }).first {
-            // if it's the last element of the requested URL, check if there is a pattern with variable tail.
-            if variableNode.value.nodes.isEmpty {
-                params[variableNode.0] = ""
-                matchedNodes.append(variableNode.value)
-                return
-            }
         }
         }
         
         
-        // if it's the last element and the path to match is done then it's a pattern matching
-        if node.nodes.isEmpty && index == count {
+        if node.isEndOfRoute && index == count {
+            // if it's the last element and the path to match is done then it's a pattern matching
             matchedNodes.append(node)
             matchedNodes.append(node)
             return
             return
         }
         }

+ 85 - 1
XCode/Tests/SwifterTestsHttpRouter.swift

@@ -103,7 +103,7 @@ class SwifterTestsHttpRouter: XCTestCase {
         
         
         XCTAssertEqual(router.route(nil, path: "/a/b/value1")?.0[":var"], "value1")
         XCTAssertEqual(router.route(nil, path: "/a/b/value1")?.0[":var"], "value1")
         
         
-        XCTAssertEqual(router.route(nil, path: "/a/b/")?.0[":var"], "")
+        XCTAssertEqual(router.route(nil, path: "/a/b/")?.0[":var"], nil)
     }
     }
     
     
     func testHttpRouterPercentEncodedPathSegments() {
     func testHttpRouterPercentEncodedPathSegments() {
@@ -188,4 +188,88 @@ class SwifterTestsHttpRouter: XCTestCase {
         XCTAssertTrue(foundFirstVariableRoute)
         XCTAssertTrue(foundFirstVariableRoute)
         XCTAssertTrue(foundSecondVariableRoute)
         XCTAssertTrue(foundSecondVariableRoute)
     }
     }
+    
+    func testHttpRouterShouldHandleOverlappingRoutesInTrail() {
+        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") { _ in
+            foundSecondVariableRoute = true
+            secondVariableRouteExpectation.fulfill()
+            return HttpResponse.accepted
+        }
+        
+        let thirdVariableRouteExpectation = expectation(description: "Third Variable Route")
+        var foundThirdVariableRoute = false
+        router.register("GET", path: "/a/:id/b") { request in
+            foundThirdVariableRoute = true
+            thirdVariableRouteExpectation.fulfill()
+            return HttpResponse.accepted
+        }
+        
+        let firstRouteResult = router.route("GET", path: "/a")
+        let firstRouterHandler = firstRouteResult?.1
+        XCTAssertNotNil(firstRouteResult)
+        _ = firstRouterHandler?(request)
+        
+        let secondRouteResult = router.route("GET", path: "/a/b")
+        let secondRouterHandler = secondRouteResult?.1
+        XCTAssertNotNil(secondRouteResult)
+        _ = secondRouterHandler?(request)
+        
+        let thirdRouteResult = router.route("GET", path: "/a/b/b")
+        let thirdRouterHandler = thirdRouteResult?.1
+        XCTAssertNotNil(thirdRouteResult)
+        _ = thirdRouterHandler?(request)
+        
+        waitForExpectations(timeout: 10, handler: nil)
+        XCTAssertTrue(foundFirstVariableRoute)
+        XCTAssertTrue(foundSecondVariableRoute)
+        XCTAssertTrue(foundThirdVariableRoute)
+    }
+    
+    func testHttpRouterHandlesOverlappingPathsInDynamicRoutesInTheMiddle() {
+        let router = HttpRouter()
+        let request = HttpRequest()
+        
+        let firstVariableRouteExpectation = expectation(description: "First Variable Route")
+        var foundFirstVariableRoute = false
+        router.register("GET", path: "/a/b/c/d/e") { 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/f/g") { _ in
+            foundSecondVariableRoute = true
+            secondVariableRouteExpectation.fulfill()
+            return HttpResponse.accepted
+        }
+        
+        let firstRouteResult = router.route("GET", path: "/a/b/c/d/e")
+        let firstRouterHandler = firstRouteResult?.1
+        XCTAssertNotNil(firstRouteResult)
+        _ = firstRouterHandler?(request)
+        
+        let secondRouteResult = router.route("GET", path: "/a/b/f/g")
+        let secondRouterHandler = secondRouteResult?.1
+        XCTAssertNotNil(secondRouteResult)
+        _ = secondRouterHandler?(request)
+        
+        waitForExpectations(timeout: 10, handler: nil)
+        XCTAssertTrue(foundFirstVariableRoute)
+        XCTAssertTrue(foundSecondVariableRoute)
+    }
 }
 }

+ 13 - 26
XCode/Tests/XCTestManifests.swift

@@ -1,11 +1,7 @@
-#if !canImport(ObjectiveC)
 import XCTest
 import XCTest
 
 
 extension MimeTypeTests {
 extension MimeTypeTests {
-    // DO NOT MODIFY: This is autogenerated, use:
-    //   `swift test --generate-linuxmain`
-    // to regenerate.
-    static let __allTests__MimeTypeTests = [
+    static let __allTests = [
         ("testCaseInsensitivity", testCaseInsensitivity),
         ("testCaseInsensitivity", testCaseInsensitivity),
         ("testCorrectTypes", testCorrectTypes),
         ("testCorrectTypes", testCorrectTypes),
         ("testDefaultValue", testDefaultValue),
         ("testDefaultValue", testDefaultValue),
@@ -14,24 +10,20 @@ extension MimeTypeTests {
 }
 }
 
 
 extension SwifterTestsHttpParser {
 extension SwifterTestsHttpParser {
-    // DO NOT MODIFY: This is autogenerated, use:
-    //   `swift test --generate-linuxmain`
-    // to regenerate.
-    static let __allTests__SwifterTestsHttpParser = [
+    static let __allTests = [
         ("testParser", testParser),
         ("testParser", testParser),
     ]
     ]
 }
 }
 
 
 extension SwifterTestsHttpRouter {
 extension SwifterTestsHttpRouter {
-    // DO NOT MODIFY: This is autogenerated, use:
-    //   `swift test --generate-linuxmain`
-    // to regenerate.
-    static let __allTests__SwifterTestsHttpRouter = [
+    static let __allTests = [
         ("testHttpRouterEmptyTail", testHttpRouterEmptyTail),
         ("testHttpRouterEmptyTail", testHttpRouterEmptyTail),
         ("testHttpRouterHandlesOverlappingPaths", testHttpRouterHandlesOverlappingPaths),
         ("testHttpRouterHandlesOverlappingPaths", testHttpRouterHandlesOverlappingPaths),
         ("testHttpRouterHandlesOverlappingPathsInDynamicRoutes", testHttpRouterHandlesOverlappingPathsInDynamicRoutes),
         ("testHttpRouterHandlesOverlappingPathsInDynamicRoutes", testHttpRouterHandlesOverlappingPathsInDynamicRoutes),
+        ("testHttpRouterHandlesOverlappingPathsInDynamicRoutesInTheMiddle", testHttpRouterHandlesOverlappingPathsInDynamicRoutesInTheMiddle),
         ("testHttpRouterMultiplePathSegmentWildcards", testHttpRouterMultiplePathSegmentWildcards),
         ("testHttpRouterMultiplePathSegmentWildcards", testHttpRouterMultiplePathSegmentWildcards),
         ("testHttpRouterPercentEncodedPathSegments", testHttpRouterPercentEncodedPathSegments),
         ("testHttpRouterPercentEncodedPathSegments", testHttpRouterPercentEncodedPathSegments),
+        ("testHttpRouterShouldHandleOverlappingRoutesInTrail", testHttpRouterShouldHandleOverlappingRoutesInTrail),
         ("testHttpRouterSimplePathSegments", testHttpRouterSimplePathSegments),
         ("testHttpRouterSimplePathSegments", testHttpRouterSimplePathSegments),
         ("testHttpRouterSinglePathSegmentWildcard", testHttpRouterSinglePathSegmentWildcard),
         ("testHttpRouterSinglePathSegmentWildcard", testHttpRouterSinglePathSegmentWildcard),
         ("testHttpRouterSlashRoot", testHttpRouterSlashRoot),
         ("testHttpRouterSlashRoot", testHttpRouterSlashRoot),
@@ -40,10 +32,7 @@ extension SwifterTestsHttpRouter {
 }
 }
 
 
 extension SwifterTestsStringExtensions {
 extension SwifterTestsStringExtensions {
-    // DO NOT MODIFY: This is autogenerated, use:
-    //   `swift test --generate-linuxmain`
-    // to regenerate.
-    static let __allTests__SwifterTestsStringExtensions = [
+    static let __allTests = [
         ("testBASE64", testBASE64),
         ("testBASE64", testBASE64),
         ("testMiscRemovePercentEncoding", testMiscRemovePercentEncoding),
         ("testMiscRemovePercentEncoding", testMiscRemovePercentEncoding),
         ("testMiscReplace", testMiscReplace),
         ("testMiscReplace", testMiscReplace),
@@ -54,21 +43,19 @@ extension SwifterTestsStringExtensions {
 }
 }
 
 
 extension SwifterTestsWebSocketSession {
 extension SwifterTestsWebSocketSession {
-    // DO NOT MODIFY: This is autogenerated, use:
-    //   `swift test --generate-linuxmain`
-    // to regenerate.
-    static let __allTests__SwifterTestsWebSocketSession = [
+    static let __allTests = [
         ("testParser", testParser),
         ("testParser", testParser),
     ]
     ]
 }
 }
 
 
+#if !os(macOS)
 public func __allTests() -> [XCTestCaseEntry] {
 public func __allTests() -> [XCTestCaseEntry] {
     return [
     return [
-        testCase(MimeTypeTests.__allTests__MimeTypeTests),
-        testCase(SwifterTestsHttpParser.__allTests__SwifterTestsHttpParser),
-        testCase(SwifterTestsHttpRouter.__allTests__SwifterTestsHttpRouter),
-        testCase(SwifterTestsStringExtensions.__allTests__SwifterTestsStringExtensions),
-        testCase(SwifterTestsWebSocketSession.__allTests__SwifterTestsWebSocketSession),
+        testCase(MimeTypeTests.__allTests),
+        testCase(SwifterTestsHttpParser.__allTests),
+        testCase(SwifterTestsHttpRouter.__allTests),
+        testCase(SwifterTestsStringExtensions.__allTests),
+        testCase(SwifterTestsWebSocketSession.__allTests),
     ]
     ]
 }
 }
 #endif
 #endif