diff --git a/Sources/Branches/Branch+Paths.swift b/Sources/Branches/Branch+Paths.swift index 9373f288..69acd03a 100644 --- a/Sources/Branches/Branch+Paths.swift +++ b/Sources/Branches/Branch+Paths.swift @@ -20,6 +20,12 @@ extension Branch { } } +extension Branch { + public var allSubBranches: [Branch] { + return subBranches.allBranches + } +} + extension Branch { // The individual route leading to the calling branch internal var route: String { diff --git a/Sources/Routing/RouteBuilder.swift b/Sources/Routing/RouteBuilder.swift index 4e696b33..f1e347b0 100644 --- a/Sources/Routing/RouteBuilder.swift +++ b/Sources/Routing/RouteBuilder.swift @@ -24,6 +24,7 @@ extension RouteBuilder { register(host: nil, method: method, path: path, responder: responder) } + // FIXME: This function feels like it might not fit public func add( _ method: HTTP.Method, _ path: String ..., diff --git a/Sources/Routing/Router+Responder.swift b/Sources/Routing/Router+Responder.swift index 0ffe5a23..4f5059f0 100644 --- a/Sources/Routing/Router+Responder.swift +++ b/Sources/Routing/Router+Responder.swift @@ -1,11 +1,55 @@ import HTTP +import Branches import Debugging +public var supportOptionsRequests = true + extension Router: Responder { public func respond(to request: Request) throws -> Response { - guard let responder = route(request) else { throw RouterError.missingRoute(for: request) } + guard let responder = route(request) else { return try fallbackResponse(for: request) } return try responder.respond(to: request) } + + private func fallbackResponse(for request: Request) throws -> Response { + guard supportOptionsRequests, request.method == .options else { throw RouterError.missingRoute(for: request) } + return options(for: request) + } + + private func options(for request: Request) -> Response { + let opts = supportedMethods(for: request) + .map { $0.description } + .joined(separator: ", ") + return Response(status: .ok, headers: ["Allow": opts]) + } + + private func supportedMethods(for request: Request) -> [Method] { + let request = request.copy() + let host = self.host(for: request.uri.host) + let allOptions = host.allSubBranches + let allPossibleMethods = allOptions.map { Method($0.name) } + return allPossibleMethods.filter { method in + request.method = method + return route(request) != nil + } + } + + private func host(for host: String) -> Branch { + // FIXME: RM ! + return base.fetch([host])! + } +} + +extension Request { + public func copy() -> Request { + return Request( + method: method, + uri: uri, + version: version, + headers: headers, + body: body, + peerAddress: peerAddress + ) + } } public enum RouterError: Debuggable { diff --git a/Tests/RoutingTests/RouteTests.swift b/Tests/RoutingTests/RouteTests.swift index 58358316..2563d945 100644 --- a/Tests/RoutingTests/RouteTests.swift +++ b/Tests/RoutingTests/RouteTests.swift @@ -46,7 +46,7 @@ class RouteTests: XCTestCase { query: nil, fragment: nil ) - let request = try Request(method: .get, uri: uri) + let request = Request(method: .get, uri: uri) let response = try router.respond(to: request) XCTAssertEqual(response.body.bytes?.string, path.prefix(3).joined(separator: ":")) } diff --git a/Tests/RoutingTests/RouterTests.swift b/Tests/RoutingTests/RouterTests.swift index c9e576da..31560e2c 100644 --- a/Tests/RoutingTests/RouterTests.swift +++ b/Tests/RoutingTests/RouterTests.swift @@ -141,7 +141,7 @@ class RouterTests: XCTestCase { let empties: [String] = ["", "/"] try empties.forEach { emptypath in let uri = URI(scheme: "http", host: "0.0.0.0", path: emptypath) - let request = try Request(method: .get, uri: uri) + let request = Request(method: .get, uri: uri) let response = try router.respond(to: request) XCTAssertEqual(response.body.bytes?.string, "Hello, Empty!") } @@ -157,7 +157,7 @@ class RouterTests: XCTestCase { scheme: "", host: "" ) - let request = try Request(method: .get, uri: uri) + let request = Request(method: .get, uri: uri) let response = try router.respond(to: request) XCTAssertEqual(response.body.bytes?.string, "Hello, World!") } diff --git a/Tests/RoutingTests/Utilities.swift b/Tests/RoutingTests/Utilities.swift index 8748d0c0..f61db71f 100644 --- a/Tests/RoutingTests/Utilities.swift +++ b/Tests/RoutingTests/Utilities.swift @@ -6,7 +6,7 @@ import URI extension Request { convenience init(method: HTTP.Method, path: String, host: String = "0.0.0.0") { let uri = URI(host: host, path: path) - try! self.init(method: method, uri: uri) + self.init(method: method, uri: uri) } enum BytesError: Error {