wip
This commit is contained in:
parent
f86552cde7
commit
324ef421e2
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRouting"
|
||||
BuildableName = "VaporRouting"
|
||||
BlueprintName = "VaporRouting"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRouting"
|
||||
BuildableName = "VaporRouting"
|
||||
BlueprintName = "VaporRouting"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,114 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRouting"
|
||||
BuildableName = "VaporRouting"
|
||||
BlueprintName = "VaporRouting"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "vapor-routing-benchmark"
|
||||
BuildableName = "vapor-routing-benchmark"
|
||||
BlueprintName = "vapor-routing-benchmark"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRoutingTests"
|
||||
BuildableName = "VaporRoutingTests"
|
||||
BlueprintName = "VaporRoutingTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRoutingTests"
|
||||
BuildableName = "VaporRoutingTests"
|
||||
BlueprintName = "VaporRoutingTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "vapor-routing-benchmark"
|
||||
BuildableName = "vapor-routing-benchmark"
|
||||
BlueprintName = "vapor-routing-benchmark"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "vapor-routing-benchmark"
|
||||
BuildableName = "vapor-routing-benchmark"
|
||||
BlueprintName = "vapor-routing-benchmark"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,109 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "vapor-routing-benchmark"
|
||||
BuildableName = "vapor-routing-benchmark"
|
||||
BlueprintName = "vapor-routing-benchmark"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRoutingTests"
|
||||
BuildableName = "VaporRoutingTests"
|
||||
BlueprintName = "VaporRoutingTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRoutingTests"
|
||||
BuildableName = "VaporRoutingTests"
|
||||
BlueprintName = "VaporRoutingTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "YES"
|
||||
customWorkingDirectory = "/tmp"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "vapor-routing-benchmark"
|
||||
BuildableName = "vapor-routing-benchmark"
|
||||
BlueprintName = "vapor-routing-benchmark"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<CommandLineArguments>
|
||||
<CommandLineArgument
|
||||
argument = "--allow-debug-build"
|
||||
isEnabled = "YES">
|
||||
</CommandLineArgument>
|
||||
</CommandLineArguments>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "vapor-routing-benchmark"
|
||||
BuildableName = "vapor-routing-benchmark"
|
||||
BlueprintName = "vapor-routing-benchmark"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRouting"
|
||||
BuildableName = "VaporRouting"
|
||||
BlueprintName = "VaporRouting"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRoutingTests"
|
||||
BuildableName = "VaporRoutingTests"
|
||||
BlueprintName = "VaporRoutingTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRoutingTests"
|
||||
BuildableName = "VaporRoutingTests"
|
||||
BlueprintName = "VaporRoutingTests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "VaporRouting"
|
||||
BuildableName = "VaporRouting"
|
||||
BlueprintName = "VaporRouting"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
|
@ -46,6 +46,15 @@
|
|||
"version": "4.3.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-argument-parser",
|
||||
"repositoryURL": "https://github.com/apple/swift-argument-parser",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "6b2aa2748a7881eebb9f84fb10c01293e15b52ca",
|
||||
"version": "0.5.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-backtrace",
|
||||
"repositoryURL": "https://github.com/swift-server/swift-backtrace.git",
|
||||
|
@ -55,6 +64,15 @@
|
|||
"version": "1.3.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "Benchmark",
|
||||
"repositoryURL": "https://github.com/google/swift-benchmark",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "a0564bf88df5f94eec81348a2f089494c6b28d80",
|
||||
"version": "0.1.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"package": "swift-case-paths",
|
||||
"repositoryURL": "https://github.com/pointfreeco/swift-case-paths",
|
||||
|
@ -149,9 +167,9 @@
|
|||
"package": "URLRouting",
|
||||
"repositoryURL": "https://github.com/pointfreeco/swift-url-routing",
|
||||
"state": {
|
||||
"branch": null,
|
||||
"revision": "53f56e552b3932d5c11252cb6669a059e9e9e69a",
|
||||
"version": "0.1.0"
|
||||
"branch": "main",
|
||||
"revision": "97b2826fb5f1300b7b618c300569ed0725ce831a",
|
||||
"version": null
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -15,7 +15,8 @@ let package = Package(
|
|||
],
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-url-routing", from: "0.1.0"),
|
||||
.package(url: "https://github.com/pointfreeco/swift-url-routing", branch: "main"), // from: "0.1.0"),
|
||||
.package(name: "Benchmark", url: "https://github.com/google/swift-benchmark", from: "0.1.1"),
|
||||
],
|
||||
targets: [
|
||||
.target(
|
||||
|
@ -32,5 +33,13 @@ let package = Package(
|
|||
.product(name: "XCTVapor", package: "vapor"),
|
||||
]
|
||||
),
|
||||
.executableTarget(
|
||||
name: "vapor-routing-benchmark",
|
||||
dependencies: [
|
||||
"VaporRouting",
|
||||
.product(name: "Benchmark", package: "Benchmark"),
|
||||
.product(name: "Vapor", package: "vapor"),
|
||||
]
|
||||
)
|
||||
]
|
||||
)
|
||||
|
|
|
@ -16,9 +16,9 @@ extension URLRequestData {
|
|||
let components = URLComponents(url: url, resolvingAgainstBaseURL: false)
|
||||
else { return nil }
|
||||
|
||||
let body: [UInt8]?
|
||||
let body: Data?
|
||||
if var buffer = request.body.data,
|
||||
let bytes = buffer.readBytes(length: buffer.readableBytes)
|
||||
let bytes = buffer.readData(length: buffer.readableBytes)
|
||||
{
|
||||
body = bytes
|
||||
} else {
|
||||
|
@ -27,12 +27,12 @@ extension URLRequestData {
|
|||
|
||||
self.init(
|
||||
method: request.method.string,
|
||||
scheme: request.url.scheme,
|
||||
scheme: components.scheme,
|
||||
user: request.headers.basicAuthorization?.username,
|
||||
password: request.headers.basicAuthorization?.password,
|
||||
host: request.url.host,
|
||||
port: request.url.port,
|
||||
path: request.url.path,
|
||||
host: components.host,
|
||||
port: components.port,
|
||||
path: components.path,
|
||||
query: components.queryItems?.reduce(into: [:]) { query, item in
|
||||
query[item.name, default: []].append(item.value)
|
||||
} ?? [:],
|
||||
|
@ -45,7 +45,7 @@ extension URLRequestData {
|
|||
},
|
||||
uniquingKeysWith: { $0 + $1 }
|
||||
),
|
||||
body: body.map { Data($0) }
|
||||
body: body
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,12 +11,54 @@ extension Application {
|
|||
public func mount<R: Parser>(
|
||||
_ router: R,
|
||||
use closure: @escaping (Request, R.Output) async throws -> AsyncResponseEncodable
|
||||
) where R.Input == URLRequestData {
|
||||
self.middleware.use(AsyncRoutingMiddleware(router: router, respond: closure))
|
||||
}
|
||||
|
||||
@_disfavoredOverload
|
||||
public func _mount<R: Parser>(
|
||||
_ router: R,
|
||||
use closure: @escaping (Request, R.Output) -> EventLoopFuture<ResponseEncodable>
|
||||
) where R.Input == URLRequestData {
|
||||
self.middleware.use(RoutingMiddleware(router: router, respond: closure))
|
||||
}
|
||||
}
|
||||
|
||||
private struct RoutingMiddleware<Router: Parser>: AsyncMiddleware
|
||||
private struct RoutingMiddleware<Router: Parser>: Middleware
|
||||
where Router.Input == URLRequestData {
|
||||
let router: Router
|
||||
let respond: (Request, Router.Output) -> EventLoopFuture<ResponseEncodable>
|
||||
|
||||
public func respond(
|
||||
to request: Request,
|
||||
chainingTo next: Responder
|
||||
) -> EventLoopFuture<Response> {
|
||||
|
||||
guard let requestData = URLRequestData(request: request)
|
||||
else { return next.respond(to: request) }
|
||||
|
||||
let route: Router.Output
|
||||
do {
|
||||
route = try self.router.parse(requestData)
|
||||
return self.respond(request, route)
|
||||
.flatMap { $0.encodeResponse(for: request) }
|
||||
} catch let routingError {
|
||||
return next.respond(to: request)
|
||||
.flatMapError { error in
|
||||
request.logger.info("\(routingError)")
|
||||
|
||||
guard request.application.environment == .development
|
||||
else { return request.eventLoop.makeFailedFuture(error) }
|
||||
|
||||
return request.eventLoop.makeSucceededFuture(
|
||||
Response(status: .notFound, body: .init(string: "Routing \(routingError)"))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct AsyncRoutingMiddleware<Router: Parser>: AsyncMiddleware
|
||||
where Router.Input == URLRequestData {
|
||||
let router: Router
|
||||
let respond: (Request, Router.Output) async throws -> AsyncResponseEncodable
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
import Benchmark
|
||||
|
||||
extension BenchmarkSuite {
|
||||
func benchmark(
|
||||
_ name: String,
|
||||
run: @escaping () throws -> Void,
|
||||
setUp: @escaping () -> Void = {},
|
||||
tearDown: @escaping () -> Void = {}
|
||||
) {
|
||||
self.register(
|
||||
benchmark: Benchmarking(name: name, run: run, setUp: setUp, tearDown: tearDown)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct Benchmarking: AnyBenchmark {
|
||||
let name: String
|
||||
let settings: [BenchmarkSetting] = []
|
||||
private let _run: () throws -> Void
|
||||
private let _setUp: () -> Void
|
||||
private let _tearDown: () -> Void
|
||||
|
||||
init(
|
||||
name: String,
|
||||
run: @escaping () throws -> Void,
|
||||
setUp: @escaping () -> Void = {},
|
||||
tearDown: @escaping () -> Void = {}
|
||||
) {
|
||||
self.name = name
|
||||
self._run = run
|
||||
self._setUp = setUp
|
||||
self._tearDown = tearDown
|
||||
}
|
||||
|
||||
func setUp() {
|
||||
self._setUp()
|
||||
}
|
||||
|
||||
func run(_ state: inout BenchmarkState) throws {
|
||||
try self._run()
|
||||
}
|
||||
|
||||
func tearDown() {
|
||||
self._tearDown()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,319 @@
|
|||
import Benchmark
|
||||
import URLRouting
|
||||
import Vapor
|
||||
import VaporRouting
|
||||
|
||||
let routingSuite = BenchmarkSuite(name: "Routing", settings: MaxIterations(1_000)) { suite in
|
||||
LoggingSystem.bootstrap { _ in SwiftLogNoOpLogHandler() }
|
||||
|
||||
let eventLoop = EmbeddedEventLoop()
|
||||
var app: Application!
|
||||
var req: Request!
|
||||
var res: Response!
|
||||
|
||||
struct NoopMiddleware: Middleware {
|
||||
func respond(to request: Request, chainingTo next: Responder) -> EventLoopFuture<Response> {
|
||||
request.eventLoop.makeSucceededFuture(.init())
|
||||
}
|
||||
}
|
||||
|
||||
suite.benchmark("Vapor.baseline") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
app.middleware.use(NoopMiddleware())
|
||||
req = .init(application: app, method: .GET, url: .init(string: "/"), on: eventLoop)
|
||||
} tearDown: {
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("Vapor.home (no-op)") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
try! routes(app)
|
||||
req = .init(application: app, method: .GET, url: .init(string: "/"), on: eventLoop)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("VaporRouting.home") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
app._mount(siteRouter, use: siteHandler)
|
||||
req = .init(application: app, method: .GET, url: .init(string: "/"), on: eventLoop)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("Vapor.createUser") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
try! routes(app)
|
||||
req = .init(
|
||||
application: app,
|
||||
method: .POST,
|
||||
url: .init(string: "/users"),
|
||||
headers: ["content-type": "application/json"],
|
||||
collectedBody: .init(string: #"{"name":"Blob","bio":"Blobbed around the world!"}"#),
|
||||
on: eventLoop
|
||||
)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("VaporRouting.createUser") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
app._mount(siteRouter, use: siteHandler)
|
||||
req = .init(
|
||||
application: app,
|
||||
method: .POST,
|
||||
url: .init(string: "/users"),
|
||||
collectedBody: .init(string: #"{"name":"Blob","bio":"Blobbed around the world!"}"#),
|
||||
on: eventLoop
|
||||
)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("Vapor.fetchUser") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
try! routes(app)
|
||||
req = .init(application: app, method: .GET, url: .init(string: "/users/42"), on: eventLoop)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("VaporRouting.fetchUser") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
app._mount(siteRouter, use: siteHandler)
|
||||
req = .init(application: app, method: .GET, url: .init(string: "/users/42"), on: eventLoop)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("Vapor.bookSearch") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
try! routes(app)
|
||||
req = .init(
|
||||
application: app,
|
||||
method: .GET,
|
||||
url: .init(string: "/users/42/books/search"),
|
||||
on: eventLoop
|
||||
)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("VaporRouting.bookSearch") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
app._mount(siteRouter, use: siteHandler)
|
||||
req = .init(
|
||||
application: app,
|
||||
method: .GET,
|
||||
url: .init(string: "/users/42/books/search"),
|
||||
on: eventLoop
|
||||
)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("Vapor.bookSearchQuery") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
try! routes(app)
|
||||
req = .init(
|
||||
application: app,
|
||||
method: .GET,
|
||||
url: .init(string: "/users/42/books/search?direction=desc&sort=category&count=100"),
|
||||
on: eventLoop
|
||||
)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("VaporRouting.bookSearchQuery") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
app._mount(siteRouter, use: siteHandler)
|
||||
req = .init(
|
||||
application: app,
|
||||
method: .GET,
|
||||
url: .init(string: "/users/42/books/search?direction=desc&sort=category&count=100"),
|
||||
on: eventLoop
|
||||
)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
|
||||
suite.benchmark("Vapor.fetchUserBook") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
try! routes(app)
|
||||
req = .init(
|
||||
application: app,
|
||||
method: .GET,
|
||||
url: .init(string: "/users/42/books/deadbeef-dead-beef-dead-beefdeadbeef"),
|
||||
on: eventLoop
|
||||
)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
|
||||
suite.benchmark("VaporRouting.fetchUserBook") {
|
||||
res = try app.responder.respond(to: req).wait()
|
||||
} setUp: {
|
||||
app = .init()
|
||||
app._mount(siteRouter, use: siteHandler)
|
||||
req = .init(
|
||||
application: app,
|
||||
method: .GET,
|
||||
url: .init(string: "/users/42/books/deadbeef-dead-beef-dead-beefdeadbeef"),
|
||||
on: eventLoop
|
||||
)
|
||||
} tearDown: {
|
||||
precondition(res.status == .ok)
|
||||
app.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateUser: Codable {
|
||||
let bio: String
|
||||
let name: String
|
||||
}
|
||||
struct SearchOptions: Decodable {
|
||||
var sort: Sort = .title
|
||||
var direction: Direction = .asc
|
||||
var count = 10
|
||||
|
||||
enum Direction: String, CaseIterable, Decodable {
|
||||
case asc, desc
|
||||
}
|
||||
enum Sort: String, CaseIterable, Decodable {
|
||||
case title, category
|
||||
}
|
||||
}
|
||||
|
||||
@inline(never)
|
||||
public func blackHole<T>(_ x: T) {}
|
||||
|
||||
func routes(_ app: Application) throws {
|
||||
app.get { req -> Response in
|
||||
return .init()
|
||||
}
|
||||
app.post("users") { req -> Response in
|
||||
blackHole(try req.content.decode(CreateUser.self))
|
||||
return .init()
|
||||
}
|
||||
app.get("users", ":userId") { req -> Response in
|
||||
blackHole(try req.parameters.require("userId", as: Int.self))
|
||||
return .init()
|
||||
}
|
||||
app.get("users", ":userId", "books", "search") { req -> Response in
|
||||
blackHole(try req.parameters.require("userId", as: Int.self))
|
||||
blackHole((try? req.query.get(SearchOptions.Sort.self, at: "sort")) ?? .title)
|
||||
blackHole((try? req.query.get(SearchOptions.Direction.self, at: "sort")) ?? .asc)
|
||||
blackHole((try? req.query.get(Int.self, at: "count")) ?? 10)
|
||||
return .init()
|
||||
}
|
||||
app.get("users", ":userId", "books", ":bookId") { req -> Response in
|
||||
blackHole(try req.parameters.require("userId", as: Int.self))
|
||||
blackHole(try req.parameters.require("bookId", as: UUID.self))
|
||||
return .init()
|
||||
}
|
||||
}
|
||||
|
||||
enum BookRoute {
|
||||
case fetch
|
||||
}
|
||||
enum BooksRoute {
|
||||
case book(UUID, BookRoute = .fetch)
|
||||
case search(SearchOptions = .init())
|
||||
}
|
||||
enum UserRoute {
|
||||
case books(BooksRoute = .search())
|
||||
case fetch
|
||||
}
|
||||
enum UsersRoute {
|
||||
case create(CreateUser)
|
||||
case user(Int, UserRoute = .fetch)
|
||||
}
|
||||
enum SiteRoute {
|
||||
case home
|
||||
case users(UsersRoute)
|
||||
}
|
||||
|
||||
let bookRouter = OneOf {
|
||||
Route(.case(BookRoute.fetch))
|
||||
}
|
||||
let booksRouter = OneOf {
|
||||
Route(.case(BooksRoute.search)) {
|
||||
Path { From(.utf8) { "search".utf8 } }
|
||||
Parse(.memberwise(SearchOptions.init)) {
|
||||
Query {
|
||||
Field("sort", .string.representing(SearchOptions.Sort.self), default: .title)
|
||||
Field("direction", .string.representing(SearchOptions.Direction.self), default: .asc)
|
||||
Field("count", default: 10) { Digits() }
|
||||
}
|
||||
}
|
||||
}
|
||||
Route(.case(BooksRoute.book)) {
|
||||
Path { UUID.parser() }
|
||||
bookRouter
|
||||
}
|
||||
}
|
||||
let userRouter = OneOf {
|
||||
Route(.case(UserRoute.fetch))
|
||||
Route(.case(UserRoute.books)) {
|
||||
Path { From(.utf8) { "books".utf8 } }
|
||||
booksRouter
|
||||
}
|
||||
}
|
||||
let usersRouter = OneOf {
|
||||
Route(.case(UsersRoute.create)) {
|
||||
Method.post
|
||||
Body(.json(CreateUser.self))
|
||||
}
|
||||
Route(.case(UsersRoute.user)) {
|
||||
Path { Digits() }
|
||||
userRouter
|
||||
}
|
||||
}
|
||||
let siteRouter = OneOf {
|
||||
Route(.case(SiteRoute.home))
|
||||
Route(.case(SiteRoute.users)) {
|
||||
Path { From(.utf8) { "users".utf8 } }
|
||||
usersRouter
|
||||
}
|
||||
}
|
||||
|
||||
func siteHandler(request: Request, route: SiteRoute) -> EventLoopFuture<ResponseEncodable> {
|
||||
request.eventLoop.makeSucceededFuture(Response())
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
import Benchmark
|
||||
|
||||
Benchmark.main(
|
||||
[
|
||||
defaultBenchmarkSuite,
|
||||
routingSuite,
|
||||
]
|
||||
)
|
Loading…
Reference in New Issue