diff --git a/Sources/Modules/Core/HTTPRequest.swift b/Sources/Modules/Core/HTTPRequest.swift index c2fd27d..6fade7e 100644 --- a/Sources/Modules/Core/HTTPRequest.swift +++ b/Sources/Modules/Core/HTTPRequest.swift @@ -30,6 +30,7 @@ public struct HTTPRequest: Sendable { /// - method: The HTTP method. /// - scheme: URL scheme (e.g. "https"). /// - host: The host (e.g. "api.example.com"). + /// - port: The port of the server, if any (e.g. `8080`). /// - path: The URL path (e.g. "/v1/resource"). /// - headers: Initial headers. /// - body: The request body. @@ -37,6 +38,7 @@ public struct HTTPRequest: Sendable { method: Method, scheme: String = "https", host: String? = nil, + port: Int? = nil, path: String, headers: [String: String] = [:], body: HTTPBody = EmptyBody() @@ -47,6 +49,7 @@ public struct HTTPRequest: Sendable { urlComponents.scheme = scheme urlComponents.host = host + urlComponents.port = port urlComponents.path = path } @@ -93,6 +96,18 @@ extension HTTPRequest { set { urlComponents.host = newValue } } + /// The URL scheme for this request. + public var scheme: String? { + get { urlComponents.scheme } + set { urlComponents.scheme = newValue } + } + + /// The port component of the URL. + public var port: Int? { + get { urlComponents.port } + set { urlComponents.port = newValue } + } + /// The path component of the URL. public var path: String { get { urlComponents.path } diff --git a/Sources/Modules/Sauce/Handlers/ServerEnvironmentHandler.swift b/Sources/Modules/Sauce/Handlers/ServerEnvironmentHandler.swift index a5d7638..222d29c 100644 --- a/Sources/Modules/Sauce/Handlers/ServerEnvironmentHandler.swift +++ b/Sources/Modules/Sauce/Handlers/ServerEnvironmentHandler.swift @@ -37,6 +37,14 @@ public struct ServerEnvironmentHandler: HTTPHandler { requestCopy.host = environment.host } + if requestCopy.port == nil { + requestCopy.port = environment.port + } + + if let customScheme = environment.scheme { + requestCopy.scheme = customScheme + } + if requestCopy.path.hasPrefix("/") == false { let pathSeparator = environment.pathPrefix.hasSuffix("/") ? "" : "/" requestCopy.path = environment.pathPrefix + pathSeparator + requestCopy.path diff --git a/Sources/Modules/Sauce/Options/ServerEnvironment.swift b/Sources/Modules/Sauce/Options/ServerEnvironment.swift index aedbd42..24fa880 100644 --- a/Sources/Modules/Sauce/Options/ServerEnvironment.swift +++ b/Sources/Modules/Sauce/Options/ServerEnvironment.swift @@ -18,22 +18,35 @@ public struct ServerEnvironment: Sendable { /// Default headers to include on every request. public var headers: [String: String] + + /// Optional URL scheme to override on requests. + public var scheme: String? + + /// Optional port to use for the server. + public var port: Int? + /// Initializes a new environment. /// /// - Parameters: /// - host: The server host. /// - pathPrefix: A path segment to prepend. + /// - scheme: The URL scheme to override, if any. + /// - port: The port to use for the server, if any. /// - headers: Default headers. public init( host: String, pathPrefix: String = "/", + scheme: String? = nil, + port: Int? = nil, headers: [String: String] = [:] ) { let prefix = pathPrefix.hasPrefix("/") ? "" : "/" self.host = host self.pathPrefix = prefix + pathPrefix + self.scheme = scheme + self.port = port self.headers = headers } } diff --git a/Tests/SimpleHTTPTests/Sauce/Handlers/ServerEnvironmentHandlerTests.swift b/Tests/SimpleHTTPTests/Sauce/Handlers/ServerEnvironmentHandlerTests.swift index f804ed9..423600c 100644 --- a/Tests/SimpleHTTPTests/Sauce/Handlers/ServerEnvironmentHandlerTests.swift +++ b/Tests/SimpleHTTPTests/Sauce/Handlers/ServerEnvironmentHandlerTests.swift @@ -42,4 +42,41 @@ struct ServerEnvironmentHandlerTests { let url = try #require(sentRequest.url) #expect(url.absoluteString == "https://api.example.com/v1/users") } + + @Test("Injects localhost environment") + func testLocalhostEnvironmentInjection() async throws { + let environment = ServerEnvironment( + host: "localhost", + pathPrefix: "/v1", + scheme: "http", + port: 8080, + headers: ["Accept": "application/json"] + ) + + let responseHandler = MockHTTPHandler { request in + .mock(request: request) + } + + let environmentHandler = ServerEnvironmentHandler( + environment: environment, + nextHandler: responseHandler + ) + + let request = HTTPRequest( + method: .get, + path: "users" + ) + + let result = try await environmentHandler.handle(request: request) + let sentRequest = result.request + + #expect(sentRequest.host == environment.host) + #expect(sentRequest.path == "/v1/users") + #expect(sentRequest.headers["Accept"] == "application/json") + #expect(sentRequest.port == environment.port) + #expect(sentRequest.scheme == environment.scheme) + + let url = try #require(sentRequest.url) + #expect(url.absoluteString == "http://localhost:8080/v1/users") + } }