Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 2 additions & 1 deletion Sources/Errno.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}
32 changes: 21 additions & 11 deletions Sources/HttpRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand Down Expand Up @@ -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)? {
Expand All @@ -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: "/")
Expand Down
1 change: 1 addition & 0 deletions XCode/LinuxMain.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import XCTest

import SwifterTests

var tests = [XCTestCaseEntry]()
Expand Down
40 changes: 22 additions & 18 deletions XCode/Swifter.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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;
};
Expand Down Expand Up @@ -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;
};
Expand All @@ -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;
};
Expand Down Expand Up @@ -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;
};
Expand All @@ -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";
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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 = "";
};
Expand All @@ -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 = "";
};
Expand Down Expand Up @@ -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 = "";
};
Expand Down Expand Up @@ -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 = "";
};
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
};
Expand All @@ -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;
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1000"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1010"
LastUpgradeVersion = "1020"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
35 changes: 35 additions & 0 deletions XCode/Tests/SwifterTestsHttpRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Loading