diff --git a/Sources/ClientRuntime/Endpoints/ServiceEndpointMetadata.swift b/Sources/ClientRuntime/Endpoints/ServiceEndpointMetadata.swift index 923932a42..ce44a6fbb 100644 --- a/Sources/ClientRuntime/Endpoints/ServiceEndpointMetadata.swift +++ b/Sources/ClientRuntime/Endpoints/ServiceEndpointMetadata.swift @@ -4,7 +4,6 @@ // // SPDX-License-Identifier: Apache-2.0 // -import ClientRuntime public struct ServiceEndpointMetadata { public let defaultProtocol = ProtocolType.https.rawValue @@ -53,7 +52,7 @@ extension ServiceEndpointMetadata { return SmithyEndpoint(endpoint: Endpoint(host: hostname, path: "/", - protocolType: ProtocolType(rawValue: transportProtocol)), + protocolType: ProtocolType(rawValue: transportProtocol)!), signingName: signingName) } diff --git a/Sources/ClientRuntime/Endpoints/SmithyEndpoint.swift b/Sources/ClientRuntime/Endpoints/SmithyEndpoint.swift index a2a8aaf63..f915dea85 100644 --- a/Sources/ClientRuntime/Endpoints/SmithyEndpoint.swift +++ b/Sources/ClientRuntime/Endpoints/SmithyEndpoint.swift @@ -4,7 +4,6 @@ // // SPDX-License-Identifier: Apache-2.0 // -import ClientRuntime /** A structure used by the service client to determine the endpoint. diff --git a/Sources/ClientRuntime/Networking/Endpoint.swift b/Sources/ClientRuntime/Networking/Endpoint.swift index 2ff6eb157..9fd2f5176 100644 --- a/Sources/ClientRuntime/Networking/Endpoint.swift +++ b/Sources/ClientRuntime/Networking/Endpoint.swift @@ -7,18 +7,19 @@ import Foundation public struct Endpoint: Hashable { public let uri: URI - public let protocolType: ProtocolType? + public let headers: Headers + public var protocolType: ProtocolType? { uri.scheme } public var queryItems: [SDKURLQueryItem] { uri.query } public var path: String { uri.path } public var host: String { uri.host } public var port: Int16 { uri.port } - public var headers: Headers - public let properties: [String: AnyHashable] + public var url: URL? { uri.url } + private let properties: [String: AnyHashable] public init(urlString: String, headers: Headers = Headers(), properties: [String: AnyHashable] = [:]) throws { - guard let url = URL(string: urlString) else { + guard let url = URLComponents(string: urlString)?.url else { throw ClientError.unknownError("invalid url \(urlString)") } @@ -28,18 +29,22 @@ public struct Endpoint: Hashable { public init(url: URL, headers: Headers = Headers(), properties: [String: AnyHashable] = [:]) throws { + guard let host = url.host else { throw ClientError.unknownError("invalid host \(String(describing: url.host))") } let protocolType = ProtocolType(rawValue: url.scheme ?? "") ?? .https - let uri = URI(scheme: protocolType.rawValue, - path: url.path, - host: host, - port: Int16(url.port ?? protocolType.port), - query: url.toQueryItems() ?? []) + + let uri = URIBuilder() + .withScheme(protocolType) + .withPath(url.path) + .withHost(host) + .withPort(Int16(url.port ?? protocolType.port)) + .withQueryItems(url.toQueryItems() ?? []) + .build() + self.init(uri: uri, - protocolType: protocolType, headers: headers, properties: properties) } @@ -49,43 +54,29 @@ public struct Endpoint: Hashable { port: Int16 = 443, queryItems: [SDKURLQueryItem]? = nil, headers: Headers = Headers(), - protocolType: ProtocolType = .https) { - let uri = URI(scheme: protocolType.rawValue, path: path, host: host, port: port, query: queryItems ?? []) - self.init(uri: uri, protocolType: protocolType, headers: headers) + protocolType: ProtocolType? = .https) { + + let uri = URIBuilder() + .withScheme(protocolType) + .withPath(path) + .withHost(host) + .withPort(port) + .withQueryItems(queryItems ?? []) + .build() + + self.init(uri: uri, headers: headers) } public init(uri: URI, - protocolType: ProtocolType? = .https, headers: Headers = Headers(), properties: [String: AnyHashable] = [:]) { self.uri = uri - self.protocolType = protocolType self.headers = headers self.properties = properties } } extension Endpoint { - // We still have to keep 'url' as an optional, since we're - // dealing with dynamic components that could be invalid. - public var url: URL? { - var components = URLComponents() - components.scheme = protocolType?.rawValue - components.host = uri.host.isEmpty ? nil : uri.host // If host is empty, URL is invalid - components.percentEncodedPath = uri.path - components.percentEncodedQuery = query - return (components.host == nil || components.scheme == nil) ? nil : components.url - } - - var query: String? { - if (queryItems.isEmpty) { - return nil - } - return queryItems.map { queryItem in - return [queryItem.name, queryItem.value].compactMap { $0 }.joined(separator: "=") - }.joined(separator: "&") - } - /// Returns list of auth schemes /// This is an internal API and subject to change without notice /// - Returns: list of auth schemes if present diff --git a/Sources/ClientRuntime/Networking/Http/CRT/CRTClientEngine.swift b/Sources/ClientRuntime/Networking/Http/CRT/CRTClientEngine.swift index f4f97a1a7..9ce5c07c9 100644 --- a/Sources/ClientRuntime/Networking/Http/CRT/CRTClientEngine.swift +++ b/Sources/ClientRuntime/Networking/Http/CRT/CRTClientEngine.swift @@ -25,9 +25,9 @@ public class CRTClientEngine: HTTPClient { private let port: Int16 init(endpoint: Endpoint) { - self.protocolType = endpoint.protocolType - self.host = endpoint.host - self.port = endpoint.port + self.protocolType = endpoint.uri.scheme + self.host = endpoint.uri.host + self.port = endpoint.uri.port } } @@ -72,9 +72,9 @@ public class CRTClientEngine: HTTPClient { } private func createConnectionPool(endpoint: Endpoint) throws -> HTTPClientConnectionManager { - let tlsConnectionOptions = endpoint.protocolType == .https ? TLSConnectionOptions( + let tlsConnectionOptions = endpoint.uri.scheme == .https ? TLSConnectionOptions( context: self.crtTLSOptions?.resolveContext() ?? sharedDefaultIO.tlsContext, - serverName: endpoint.host + serverName: endpoint.uri.host ) : nil var socketOptions = SocketOptions(socketType: .stream) @@ -93,9 +93,9 @@ public class CRTClientEngine: HTTPClient { let options = HTTPClientConnectionOptions( clientBootstrap: sharedDefaultIO.clientBootstrap, - hostName: endpoint.host, + hostName: endpoint.uri.host, initialWindowSize: windowSize, - port: UInt32(endpoint.port), + port: UInt32(endpoint.uri.port), proxyOptions: nil, socketOptions: socketOptions, tlsOptions: tlsConnectionOptions, @@ -104,7 +104,7 @@ public class CRTClientEngine: HTTPClient { enableManualWindowManagement: false ) // not using backpressure yet logger.debug(""" - Creating connection pool for \(String(describing: endpoint.host)) \ + Creating connection pool for \(String(describing: endpoint.uri.host)) \ with max connections: \(maxConnectionsPerEndpoint) """) return try HTTPClientConnectionManager(options: options) @@ -122,20 +122,20 @@ public class CRTClientEngine: HTTPClient { let tlsConnectionOptions = TLSConnectionOptions( context: self.crtTLSOptions?.resolveContext() ?? sharedDefaultIO.tlsContext, alpnList: [ALPNProtocol.http2.rawValue], - serverName: endpoint.host + serverName: endpoint.uri.host ) let options = HTTP2StreamManagerOptions( clientBootstrap: sharedDefaultIO.clientBootstrap, - hostName: endpoint.host, - port: UInt32(endpoint.port), + hostName: endpoint.uri.host, + port: UInt32(endpoint.uri.port), maxConnections: maxConnectionsPerEndpoint, socketOptions: socketOptions, tlsOptions: tlsConnectionOptions, enableStreamManualWindowManagement: false ) logger.debug(""" - Creating connection pool for \(String(describing: endpoint.host)) \ + Creating connection pool for \(String(describing: endpoint.uri.host)) \ with max connections: \(maxConnectionsPerEndpoint) """) @@ -164,7 +164,7 @@ public class CRTClientEngine: HTTPClient { let connectionMgr = try await serialExecutor.getOrCreateConnectionPool(endpoint: request.endpoint) let connection = try await connectionMgr.acquireConnection() - self.logger.debug("Connection was acquired to: \(String(describing: request.endpoint.url?.absoluteString))") + self.logger.debug("Connection was acquired to: \(String(describing: request.destination.url?.absoluteString))") switch connection.httpVersion { case .version_1_1: self.logger.debug("Using HTTP/1.1 connection") diff --git a/Sources/ClientRuntime/Networking/Http/Headers.swift b/Sources/ClientRuntime/Networking/Http/Headers.swift index 315bbdd28..9e45afdf5 100644 --- a/Sources/ClientRuntime/Networking/Http/Headers.swift +++ b/Sources/ClientRuntime/Networking/Http/Headers.swift @@ -159,6 +159,10 @@ public struct Headers { return first + last } } + + public var isEmpty: Bool { + return self.headers.isEmpty + } } extension Headers: Equatable { diff --git a/Sources/ClientRuntime/Networking/Http/Middlewares/ContentTypeMiddleware.swift b/Sources/ClientRuntime/Networking/Http/Middlewares/ContentTypeMiddleware.swift index a0b97318a..34d8612aa 100644 --- a/Sources/ClientRuntime/Networking/Http/Middlewares/ContentTypeMiddleware.swift +++ b/Sources/ClientRuntime/Networking/Http/Middlewares/ContentTypeMiddleware.swift @@ -42,6 +42,6 @@ extension ContentTypeMiddleware: HttpInterceptor { ) async throws { let builder = context.getRequest().toBuilder() addHeaders(builder: builder) - context.updateRequest(updated: builder.build()) + context.updateRequest(updated: try builder.build()) } } diff --git a/Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsRequestMiddleware.swift b/Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsRequestMiddleware.swift index 929b5fe3e..ae9cb1791 100644 --- a/Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsRequestMiddleware.swift +++ b/Sources/ClientRuntime/Networking/Http/Middlewares/FlexibleChecksumsRequestMiddleware.swift @@ -109,6 +109,6 @@ extension FlexibleChecksumsRequestMiddleware: HttpInterceptor { ) async throws { let builder = context.getRequest().toBuilder() try await addHeaders(builder: builder, attributes: context.getAttributes()) - context.updateRequest(updated: builder.build()) + context.updateRequest(updated: try builder.build()) } } diff --git a/Sources/ClientRuntime/Networking/Http/ProtocolType.swift b/Sources/ClientRuntime/Networking/Http/ProtocolType.swift index a34be6f6a..190482acd 100644 --- a/Sources/ClientRuntime/Networking/Http/ProtocolType.swift +++ b/Sources/ClientRuntime/Networking/Http/ProtocolType.swift @@ -5,6 +5,8 @@ import Foundation +public typealias Scheme = ProtocolType + public enum ProtocolType: String, CaseIterable { case http case https diff --git a/Sources/ClientRuntime/Networking/Http/SdkHttpRequest.swift b/Sources/ClientRuntime/Networking/Http/SdkHttpRequest.swift index efab9c2ce..8ddc328bd 100644 --- a/Sources/ClientRuntime/Networking/Http/SdkHttpRequest.swift +++ b/Sources/ClientRuntime/Networking/Http/SdkHttpRequest.swift @@ -17,20 +17,30 @@ import struct Foundation.URLRequest // in the CRT engine so that is why it's a class public final class SdkHttpRequest: RequestMessage { public var body: ByteStream - public var endpoint: Endpoint + public var destination: URI + public var headers: Headers public let method: HttpMethodType - public var destination: URI { endpoint.uri } - public var headers: Headers { endpoint.headers } - public var path: String { endpoint.path } - public var host: String { endpoint.host } - public var queryItems: [SDKURLQueryItem]? { endpoint.queryItems } + public var host: String { destination.host } + public var path: String { destination.path } + public var queryItems: [SDKURLQueryItem]? { destination.query } public var trailingHeaders: Headers = Headers() + public var endpoint: Endpoint { + return Endpoint(uri: self.destination, headers: self.headers) + } + + public convenience init(method: HttpMethodType, + endpoint: Endpoint, + body: ByteStream = ByteStream.noStream) { + self.init(method: method, uri: endpoint.uri, headers: endpoint.headers, body: body) + } public init(method: HttpMethodType, - endpoint: Endpoint, + uri: URI, + headers: Headers, body: ByteStream = ByteStream.noStream) { self.method = method - self.endpoint = endpoint + self.destination = uri + self.headers = headers self.body = body } @@ -40,22 +50,20 @@ public final class SdkHttpRequest: RequestMessage { .withMethod(self.method) .withHeaders(self.headers) .withTrailers(self.trailingHeaders) - .withPath(self.path) - .withHost(self.host) - .withPort(self.endpoint.port) - .withProtocol(self.endpoint.protocolType ?? .https) - if let qItems = self.queryItems { - builder.withQueryItems(qItems) - } + .withPath(self.destination.path) + .withHost(self.destination.host) + .withPort(self.destination.port) + .withProtocol(self.destination.scheme) + .withQueryItems(self.destination.query) return builder } public func withHeader(name: String, value: String) { - self.endpoint.headers.add(name: name, value: value) + self.headers.add(name: name, value: value) } public func withoutHeader(name: String) { - self.endpoint.headers.remove(name: name) + self.headers.remove(name: name) } public func withBody(_ body: ByteStream) { @@ -88,7 +96,8 @@ extension SdkHttpRequest { public func toHttpRequest() throws -> HTTPRequest { let httpRequest = try HTTPRequest() httpRequest.method = method.rawValue - httpRequest.path = [endpoint.path, endpoint.query].compactMap { $0 }.joined(separator: "?") + httpRequest.path = [self.destination.path, self.destination.queryString] + .compactMap { $0 }.joined(separator: "?") httpRequest.addHeaders(headers: headers.toHttpHeaders()) httpRequest.body = isChunked ? nil : StreamableHttpBody(body: body) // body needs to be nil to use writeChunk() return httpRequest @@ -100,7 +109,8 @@ extension SdkHttpRequest { public func toHttp2Request() throws -> HTTPRequestBase { let httpRequest = try HTTPRequest() httpRequest.method = method.rawValue - httpRequest.path = [endpoint.path, endpoint.query].compactMap { $0 }.joined(separator: "?") + httpRequest.path = [self.destination.path, self.destination.queryString] + .compactMap { $0 }.joined(separator: "?") httpRequest.addHeaders(headers: headers.toHttpHeaders()) // Remove the "Transfer-Encoding" header if it exists since h2 does not support it @@ -116,7 +126,7 @@ extension SdkHttpRequest { public extension URLRequest { init(sdkRequest: SdkHttpRequest) async throws { // Set URL - guard let url = sdkRequest.endpoint.url else { + guard let url = sdkRequest.destination.url else { throw ClientError.dataNotFound("Failed to construct URLRequest due to missing URL.") } self.init(url: url) @@ -151,9 +161,11 @@ extension SdkHttpRequest: CustomDebugStringConvertible, CustomStringConvertible public var description: String { let method = method.rawValue.uppercased() - let protocolType = endpoint.protocolType ?? ProtocolType.https - let query = String(describing: queryItems) - return "\(method) \(protocolType):\(endpoint.port) \n Path: \(endpoint.path) \n \(headers) \n \(query)" + let protocolType = self.destination.scheme + let query = self.destination.queryString ?? "" + let port = self.destination.port + return "\(method) \(protocolType):\(port) \n " + + "Path: \(endpoint.uri.path) \n Headers: \(headers) \n Query: \(query)" } } @@ -285,9 +297,14 @@ public class SdkHttpRequestBuilder: RequestMessageBuilder { } public func build() -> SdkHttpRequest { - let uri = URI(scheme: protocolType.rawValue, path: path, host: host, port: port, query: queryItems) - let endpoint = Endpoint(uri: uri, protocolType: protocolType, headers: headers) - return SdkHttpRequest(method: methodType, endpoint: endpoint, body: body) + let uri = URIBuilder() + .withScheme(protocolType) + .withPath(path) + .withHost(host) + .withPort(port) + .withQueryItems(queryItems) + .build() + return SdkHttpRequest(method: methodType, uri: uri, headers: headers, body: body) } } diff --git a/Sources/ClientRuntime/Networking/Http/URI.swift b/Sources/ClientRuntime/Networking/Http/URI.swift index 69980bbfa..b8976387b 100644 --- a/Sources/ClientRuntime/Networking/Http/URI.swift +++ b/Sources/ClientRuntime/Networking/Http/URI.swift @@ -3,22 +3,36 @@ * SPDX-License-Identifier: Apache-2.0. */ +import Foundation + public struct URI: Hashable { - public let scheme: String + public let scheme: Scheme public let path: String public let host: String public let port: Int16 public let query: [SDKURLQueryItem] public let username: String? public let password: String? + public var url: URL? { + self.toBuilder().getUrl() + } + public var queryString: String? { + if self.query.isEmpty { + return nil + } + + return self.query.map { queryItem in + return [queryItem.name, queryItem.value].compactMap { $0 }.joined(separator: "=") + }.joined(separator: "&") + } - public init(scheme: String, - path: String, - host: String, - port: Int16, - query: [SDKURLQueryItem], - username: String? = nil, - password: String? = nil) { + fileprivate init(scheme: Scheme, + path: String, + host: String, + port: Int16, + query: [SDKURLQueryItem], + username: String? = nil, + password: String? = nil) { self.scheme = scheme self.path = path self.host = host @@ -27,4 +41,110 @@ public struct URI: Hashable { self.username = username self.password = password } + + public func toBuilder() -> URIBuilder { + return URIBuilder() + .withScheme(self.scheme) + .withPath(self.path) + .withHost(self.host) + .withPort(self.port) + .withQueryItems(self.query) + } +} + +public class URIBuilder { + var urlComponents: URLComponents + var scheme: Scheme = Scheme.https + var path: String = "/" + var host: String = "" + var port: Int16 = Int16(Scheme.https.port) + var query: [SDKURLQueryItem] = [] + var username: String? + var password: String? + + required public init() { + self.urlComponents = URLComponents() + self.urlComponents.scheme = self.scheme.rawValue + self.urlComponents.path = self.path + self.urlComponents.host = self.host +// self.urlComponents.port = Int(self.port) + } + + @discardableResult + public func withScheme(_ value: Scheme?) -> URIBuilder { + self.scheme = value ?? Scheme.https + self.urlComponents.scheme = self.scheme.rawValue + return self + } + + @discardableResult + public func withPath(_ value: String) -> URIBuilder { + self.path = value + if self.path.contains("%") { + self.urlComponents.percentEncodedPath = self.path + } else { + self.urlComponents.path = self.path + } + return self + } + + @discardableResult + public func withHost(_ value: String) -> URIBuilder { + self.host = value + self.urlComponents.host = self.host + return self + } + + @discardableResult + public func withPort(_ value: Int16) -> URIBuilder { + self.port = value +// self.urlComponents.port = Int(self.port) + return self + } + + @discardableResult + public func withQueryItems(_ value: [SDKURLQueryItem]) -> URIBuilder { + self.query.append(contentsOf: value) + if !self.query.isEmpty { + self.urlComponents.percentEncodedQuery = self.query.map { queryItem in + return [queryItem.name, queryItem.value].compactMap { $0 }.joined(separator: "=") + }.joined(separator: "&") + } + return self + } + + @discardableResult + public func withQueryItem(_ value: SDKURLQueryItem) -> URIBuilder { + withQueryItems([value]) + } + + @discardableResult + public func withUsername(_ value: String) -> URIBuilder { + self.username = value + self.urlComponents.user = self.username + return self + } + + @discardableResult + public func withPassword(_ value: String) -> URIBuilder { + self.password = value + self.urlComponents.password = self.password + return self + } + + public func build() -> URI { + return URI(scheme: self.scheme, + path: self.path, + host: self.host, + port: self.port, + query: self.query, + username: self.username, + password: self.password) + } + + // We still have to keep 'url' as an optional, since we're + // dealing with dynamic components that could be invalid. + fileprivate func getUrl() -> URL? { + return self.path.isEmpty && self.host.isEmpty ? nil : self.urlComponents.url + } } diff --git a/Sources/ClientRuntime/Networking/Http/URLSession/URLSessionHTTPClient.swift b/Sources/ClientRuntime/Networking/Http/URLSession/URLSessionHTTPClient.swift index 433409b50..b3762a3fc 100644 --- a/Sources/ClientRuntime/Networking/Http/URLSession/URLSessionHTTPClient.swift +++ b/Sources/ClientRuntime/Networking/Http/URLSession/URLSessionHTTPClient.swift @@ -462,10 +462,10 @@ public final class URLSessionHTTPClient: HTTPClient { /// - Returns: A `URLRequest` ready to be transmitted by `URLSession` for this operation. private func makeURLRequest(from request: SdkHttpRequest, httpBodyStream: InputStream?) throws -> URLRequest { var components = URLComponents() - components.scheme = config.protocolType?.rawValue ?? request.endpoint.protocolType?.rawValue ?? "https" - components.host = request.endpoint.host + components.scheme = config.protocolType?.rawValue ?? request.destination.scheme.rawValue ?? "https" + components.host = request.endpoint.uri.host components.port = port(for: request) - components.percentEncodedPath = request.path + components.percentEncodedPath = request.destination.path if let queryItems = request.queryItems, !queryItems.isEmpty { components.percentEncodedQueryItems = queryItems.map { Foundation.URLQueryItem(name: $0.name, value: $0.value) @@ -484,12 +484,12 @@ public final class URLSessionHTTPClient: HTTPClient { } private func port(for request: SdkHttpRequest) -> Int? { - switch (request.endpoint.protocolType, request.endpoint.port) { + switch (request.destination.scheme, request.destination.port) { case (.https, 443), (.http, 80): // Don't set port explicitly if it's the default port for the scheme return nil default: - return Int(request.endpoint.port) + return Int(request.destination.port) } } } diff --git a/Sources/SmithyTestUtil/RequestTestUtil/ExpectedSdkHttpRequest.swift b/Sources/SmithyTestUtil/RequestTestUtil/ExpectedSdkHttpRequest.swift index 2f23ad7c9..9d7758304 100644 --- a/Sources/SmithyTestUtil/RequestTestUtil/ExpectedSdkHttpRequest.swift +++ b/Sources/SmithyTestUtil/RequestTestUtil/ExpectedSdkHttpRequest.swift @@ -12,7 +12,7 @@ public struct ExpectedSdkHttpRequest { public var headers: Headers? public var forbiddenHeaders: [String]? public var requiredHeaders: [String]? - public var queryItems: [SDKURLQueryItem] { endpoint.queryItems } + public var queryItems: [SDKURLQueryItem] { endpoint.uri.query } public let forbiddenQueryItems: [SDKURLQueryItem]? public let requiredQueryItems: [SDKURLQueryItem]? public let endpoint: Endpoint @@ -137,8 +137,14 @@ public class ExpectedSdkHttpRequestBuilder { } public func build() -> ExpectedSdkHttpRequest { - let uri = URI(scheme: protocolType.rawValue, path: path, host: host, port: port, query: queryItems) - let endpoint = Endpoint(uri: uri, protocolType: protocolType) + let uri = URIBuilder() + .withScheme(protocolType) + .withPath(path) + .withHost(host) + .withPort(port) + .withQueryItems(queryItems) + .build() + let endpoint = Endpoint(uri: uri, headers: headers) let forbiddenQueryItems = !forbiddenQueryItems.isEmpty ? forbiddenQueryItems : nil let requiredQueryItems = !requiredQueryItems.isEmpty ? requiredQueryItems : nil diff --git a/Sources/SmithyTestUtil/RequestTestUtil/HttpRequestTestBase.swift b/Sources/SmithyTestUtil/RequestTestUtil/HttpRequestTestBase.swift index 32fb6ed7e..c46f0f729 100644 --- a/Sources/SmithyTestUtil/RequestTestUtil/HttpRequestTestBase.swift +++ b/Sources/SmithyTestUtil/RequestTestUtil/HttpRequestTestBase.swift @@ -214,8 +214,8 @@ open class HttpRequestTestBase: XCTestCase { assertQueryItems(expected.queryItems, actual.queryItems, file: file, line: line) - XCTAssertEqual(expected.endpoint.path, actual.path, file: file, line: line) - XCTAssertEqual(expected.endpoint.host, actual.host, file: file, line: line) + XCTAssertEqual(expected.endpoint.uri.path, actual.destination.path, file: file, line: line) + XCTAssertEqual(expected.endpoint.uri.host, actual.destination.host, file: file, line: line) XCTAssertEqual(expected.method, actual.method, file: file, line: line) assertForbiddenQueryItems(expected.forbiddenQueryItems, actual.queryItems, file: file, line: line) diff --git a/Sources/WeatherSDK/EndpointResolver.swift b/Sources/WeatherSDK/EndpointResolver.swift new file mode 100644 index 000000000..ee1f5de61 --- /dev/null +++ b/Sources/WeatherSDK/EndpointResolver.swift @@ -0,0 +1,146 @@ +// Code generated by smithy-swift-codegen. DO NOT EDIT! + +import ClientRuntime + +public struct EndpointParams { + /// docs + public let region: Swift.String + + public init( + region: Swift.String + ) + { + self.region = region + } +} + +public protocol EndpointResolver { + func resolve(params: EndpointParams) throws -> ClientRuntime.Endpoint +} + +public struct DefaultEndpointResolver: EndpointResolver { + + private let engine: ClientRuntime.EndpointsRuleEngine + private let ruleSet = "{\"version\":\"1.3\",\"parameters\":{\"Region\":{\"required\":true,\"documentation\":\"docs\",\"type\":\"String\"}},\"rules\":[{\"conditions\":[],\"documentation\":\"base rule\",\"endpoint\":{\"url\":\"https://{Region}.amazonaws.com\",\"properties\":{},\"headers\":{}},\"type\":\"endpoint\"}]}" + + public init() throws { + engine = try ClientRuntime.EndpointsRuleEngine(partitions: ClientRuntime.partitionJSON, ruleSet: ruleSet) + } + + public func resolve(params: EndpointParams) throws -> ClientRuntime.Endpoint { + let context = try ClientRuntime.EndpointsRequestContext() + try context.add(name: "Region", value: params.region) + + guard let crtResolvedEndpoint = try engine.resolve(context: context) else { + throw EndpointError.unresolved("Failed to resolved endpoint") + } + + if crtResolvedEndpoint.getType() == .error { + let error = crtResolvedEndpoint.getError() + throw EndpointError.unresolved(error) + } + + guard let url = crtResolvedEndpoint.getURL() else { + assertionFailure("This must be a bug in either CRT or the rule engine, if the endpoint is not an error, it must have a url") + throw EndpointError.unresolved("Failed to resolved endpoint") + } + + let headers = crtResolvedEndpoint.getHeaders() ?? [:] + let properties = crtResolvedEndpoint.getProperties() ?? [:] + return try Endpoint(urlString: url, headers: Headers(headers), properties: properties) + } +} + +public struct EndpointResolverMiddleware: ClientRuntime.Middleware { + public let id: Swift.String = "EndpointResolverMiddleware" + + let endpointResolver: EndpointResolver + + let endpointParams: EndpointParams + + let authSchemeResolver: ClientRuntime.EndpointsAuthSchemeResolver + + public init(endpointResolver: EndpointResolver, endpointParams: EndpointParams, authSchemeResolver: ClientRuntime.EndpointsAuthSchemeResolver = ClientRuntime.DefaultEndpointsAuthSchemeResolver()) { + self.endpointResolver = endpointResolver + self.endpointParams = endpointParams + self.authSchemeResolver = authSchemeResolver + } + + public func handle(context: Context, + input: ClientRuntime.SdkHttpRequestBuilder, + next: H) async throws -> ClientRuntime.OperationOutput + where H: Handler, + Self.MInput == H.Input, + Self.MOutput == H.Output, + Self.Context == H.Context + { + let selectedAuthScheme = context.getSelectedAuthScheme() + let request = input.build() + let updatedRequest = try await apply(request: request, selectedAuthScheme: selectedAuthScheme, attributes: context) + return try await next.handle(context: context, input: updatedRequest.toBuilder()) + } + + public typealias MInput = ClientRuntime.SdkHttpRequestBuilder + public typealias MOutput = ClientRuntime.OperationOutput + public typealias Context = ClientRuntime.HttpContext +} +extension EndpointResolverMiddleware: ApplyEndpoint { + public func apply( + request: SdkHttpRequest, + selectedAuthScheme: SelectedAuthScheme?, + attributes: HttpContext) async throws -> SdkHttpRequest + { + let builder = request.toBuilder() + + let endpoint = try endpointResolver.resolve(params: endpointParams) + + var signingName: String? = nil + var signingAlgorithm: String? = nil + if let authSchemes = endpoint.authSchemes() { + let schemes = try authSchemes.map { try EndpointsAuthScheme(from: $0) } + let authScheme = try authSchemeResolver.resolve(authSchemes: schemes) + signingAlgorithm = authScheme.name + switch authScheme { + case .sigV4(let param): + signingName = param.signingName + case .sigV4A(let param): + signingName = param.signingName + case .none: + break + } + } + + let smithyEndpoint = SmithyEndpoint(endpoint: endpoint, signingName: signingName) + + var host = "" + if let hostOverride = attributes.getHost() { + host = hostOverride + } else { + host = "\(attributes.getHostPrefix() ?? "")\(smithyEndpoint.endpoint.host)" + } + + if let protocolType = smithyEndpoint.endpoint.protocolType { + builder.withProtocol(protocolType) + } + + if let signingName = signingName { + attributes.set(key: AttributeKeys.signingName, value: signingName) + attributes.set(key: AttributeKeys.selectedAuthScheme, value: selectedAuthScheme?.getCopyWithUpdatedSigningProperty(key: AttributeKeys.signingName, value: signingName)) + } + + if let signingAlgorithm = signingAlgorithm { + attributes.set(key: AttributeKeys.signingAlgorithm, value: AWSSigningAlgorithm(rawValue: signingAlgorithm)) + } + + if !endpoint.headers.isEmpty { + builder.withHeaders(endpoint.headers) + } + + return builder.withMethod(attributes.getMethod()) + .withHost(host) + .withPort(smithyEndpoint.endpoint.port) + .withPath(smithyEndpoint.endpoint.path.appendingPathComponent(attributes.getPath())) + .withHeader(name: "Host", value: host) + .build() + } +} diff --git a/Tests/ClientRuntimeTests/InterceptorTests/InterceptorTests.swift b/Tests/ClientRuntimeTests/InterceptorTests/InterceptorTests.swift index e0d211b19..b435a6fd3 100644 --- a/Tests/ClientRuntimeTests/InterceptorTests/InterceptorTests.swift +++ b/Tests/ClientRuntimeTests/InterceptorTests/InterceptorTests.swift @@ -61,7 +61,7 @@ class InterceptorTests: XCTestCase { public func modifyBeforeTransmit(context: some MutableRequest) async throws { let builder = context.getRequest().toBuilder() builder.withHeader(name: headerName, value: headerValue) - context.updateRequest(updated: builder.build()) + context.updateRequest(updated: try builder.build()) } } @@ -84,7 +84,7 @@ class InterceptorTests: XCTestCase { let input: TestInput = try XCTUnwrap(context.getInput()) let builder = context.getRequest().toBuilder() builder.withHeader(name: "otherProperty", value: "\(input.otherProperty)") - context.updateRequest(updated: builder.build()) + context.updateRequest(updated: try builder.build()) } } @@ -106,7 +106,7 @@ class InterceptorTests: XCTestCase { for i in interceptors { try await i.modifyBeforeSerialization(context: interceptorContext) } - interceptorContext.updateRequest(updated: SdkHttpRequestBuilder().build()) + interceptorContext.updateRequest(updated: try SdkHttpRequestBuilder().build()) for i in interceptors { try await i.modifyBeforeTransmit(context: interceptorContext) } diff --git a/Tests/ClientRuntimeTests/NetworkingTests/EndpointTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/EndpointTests.swift index b86a286a8..b618087ec 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/EndpointTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/EndpointTests.swift @@ -19,7 +19,7 @@ class EndpointTests: XCTestCase { SDKURLQueryItem(name: "ghi", value: "jkl"), SDKURLQueryItem(name: "mno", value: "pqr") ] - XCTAssertEqual(endpoint.queryItems, expectedQueryItems) + XCTAssertEqual(endpoint.uri.query, expectedQueryItems) } func test_hashableAndEquatable_hashesMatchWhenURLQueryItemsAreEqual() throws { @@ -30,23 +30,23 @@ class EndpointTests: XCTestCase { } func test_path_percentEncodedInput() throws { - let endpoint = Endpoint( + let endpoint = try Endpoint( host: "xctest.amazonaws.com", path: "/abc%2Bdef", protocolType: .https ) - let foundationURL = try XCTUnwrap(endpoint.url) + let foundationURL = try XCTUnwrap(endpoint.uri.url) let absoluteString = foundationURL.absoluteString XCTAssertEqual(absoluteString, "https://xctest.amazonaws.com/abc%2Bdef") } func test_path_unencodedInput() throws { - let endpoint = Endpoint( + let endpoint = try Endpoint( host: "xctest.amazonaws.com", path: "/abc+def", protocolType: .https ) - let foundationURL = try XCTUnwrap(endpoint.url) + let foundationURL = try XCTUnwrap(endpoint.uri.url) let absoluteString = foundationURL.absoluteString XCTAssertEqual(absoluteString, "https://xctest.amazonaws.com/abc+def") } diff --git a/Tests/ClientRuntimeTests/NetworkingTests/Http/CRTClientEngineIntegrationTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/Http/CRTClientEngineIntegrationTests.swift index 7b379568a..4a5cc1543 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/Http/CRTClientEngineIntegrationTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/Http/CRTClientEngineIntegrationTests.swift @@ -32,7 +32,7 @@ class CRTClientEngineIntegrationTests: NetworkingTestUtils { var headers = Headers() headers.add(name: "Content-type", value: "application/json") headers.add(name: "Host", value: "httpbin.org") - let request = SdkHttpRequest(method: .get, endpoint: Endpoint(host: "httpbin.org", path: "/get", headers: headers)) + let request = SdkHttpRequest(method: .get, endpoint: try Endpoint(host: "httpbin.org", path: "/get", headers: headers)) let response = try await httpClient.send(request: request) XCTAssertNotNil(response) @@ -48,7 +48,7 @@ class CRTClientEngineIntegrationTests: NetworkingTestUtils { let encoder = JSONEncoder() let encodedData = try encoder.encode(body) let request = SdkHttpRequest(method: .post, - endpoint: Endpoint(host: "httpbin.org", path: "/post", headers: headers), + endpoint: try Endpoint(host: "httpbin.org", path: "/post", headers: headers), body: ByteStream.data(encodedData)) let response = try await httpClient.send(request: request) XCTAssertNotNil(response) @@ -61,7 +61,7 @@ class CRTClientEngineIntegrationTests: NetworkingTestUtils { headers.add(name: "Content-type", value: "application/json") headers.add(name: "Host", value: "httpbin.org") let request = SdkHttpRequest(method: .get, - endpoint: Endpoint(host: "httpbin.org", path: "/stream-bytes/1024", headers: headers), + endpoint: try Endpoint(host: "httpbin.org", path: "/stream-bytes/1024", headers: headers), body: ByteStream.stream(BufferedStream())) let response = try await httpClient.send(request: request) XCTAssertNotNil(response) @@ -75,7 +75,7 @@ class CRTClientEngineIntegrationTests: NetworkingTestUtils { headers.add(name: "Host", value: "httpbin.org") let request = SdkHttpRequest(method: .get, - endpoint: Endpoint(host: "httpbin.org", path: "/stream-bytes/1024", headers: headers), + endpoint: try Endpoint(host: "httpbin.org", path: "/stream-bytes/1024", headers: headers), body: ByteStream.stream(BufferedStream())) let response = try await httpClient.send(request: request) XCTAssertNotNil(response) @@ -95,7 +95,7 @@ class CRTClientEngineIntegrationTests: NetworkingTestUtils { headers.add(name: "Host", value: "httpbin.org") let request = SdkHttpRequest(method: .get, - endpoint: Endpoint(host: "httpbin.org", path: "/stream-bytes/1", headers: headers), + endpoint: try Endpoint(host: "httpbin.org", path: "/stream-bytes/1", headers: headers), body: ByteStream.stream(BufferedStream())) let response = try await httpClient.send(request: request) XCTAssertNotNil(response) @@ -115,7 +115,7 @@ class CRTClientEngineIntegrationTests: NetworkingTestUtils { headers.add(name: "Host", value: "httpbin.org") let request = SdkHttpRequest(method: .get, - endpoint: Endpoint(host: "httpbin.org", path: "/stream-bytes/3000", headers: headers), + endpoint: try Endpoint(host: "httpbin.org", path: "/stream-bytes/3000", headers: headers), body: ByteStream.stream(BufferedStream())) let response = try await httpClient.send(request: request) XCTAssertNotNil(response) @@ -138,7 +138,7 @@ class CRTClientEngineIntegrationTests: NetworkingTestUtils { let encodedData = try encoder.encode(body) let request = SdkHttpRequest(method: .post, - endpoint: Endpoint(host: "httpbin.org", path: "/post", headers: headers), + endpoint: try Endpoint(host: "httpbin.org", path: "/post", headers: headers), body: ByteStream.stream(BufferedStream(data: encodedData))) let response = try await httpClient.send(request: request) XCTAssertNotNil(response) @@ -156,7 +156,7 @@ class CRTClientEngineIntegrationTests: NetworkingTestUtils { let request = SdkHttpRequest( method: .put, - endpoint: Endpoint(host: "nghttp2.org", path: "/httpbin/put", headers: headers), + endpoint: try Endpoint(host: "nghttp2.org", path: "/httpbin/put", headers: headers), body: .data(encodedData) ) diff --git a/Tests/ClientRuntimeTests/NetworkingTests/Http/HttpRequestTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/Http/HttpRequestTests.swift index c91399658..f51c6dd6b 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/Http/HttpRequestTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/Http/HttpRequestTests.swift @@ -22,7 +22,7 @@ class HttpRequestTests: NetworkingTestUtils { func testSdkHttpRequestToHttpRequest() throws { let headers = Headers(["header-item-name": "header-item-value"]) - let endpoint = Endpoint(host: "host.com", path: "/", headers: headers) + let endpoint = try Endpoint(host: "host.com", path: "/", headers: headers) let httpBody = ByteStream.data(expectedMockRequestData) let mockHttpRequest = SdkHttpRequest(method: .get, endpoint: endpoint, body: httpBody) @@ -59,7 +59,7 @@ class HttpRequestTests: NetworkingTestUtils { func testSdkHttpRequestToURLRequest() async throws { let headers = Headers(["Testname-1": "testvalue-1", "Testname-2": "testvalue-2"]) - let endpoint = Endpoint(host: "host.com", path: "/", headers: headers) + let endpoint = try Endpoint(host: "host.com", path: "/", headers: headers) let httpBody = ByteStream.data(expectedMockRequestData) let mockHttpRequest = SdkHttpRequest(method: .get, endpoint: endpoint, body: httpBody) @@ -77,7 +77,7 @@ class HttpRequestTests: NetworkingTestUtils { XCTAssertTrue(headersFromRequest.contains { $0.key == "Testname-2" && $0.value == "testvalue-2" }) let expectedBody = try await httpBody.readData() XCTAssertEqual(urlRequest.httpBody, expectedBody) - XCTAssertEqual(urlRequest.url, endpoint.url) + XCTAssertEqual(urlRequest.url, endpoint.uri.url) XCTAssertEqual(urlRequest.httpMethod, mockHttpRequest.method.rawValue) } @@ -122,7 +122,7 @@ class HttpRequestTests: NetworkingTestUtils { let httpRequest = try builder.build().toHttpRequest() httpRequest.path = "/hello?foo=bar&quz=bar&signedthing=signed" - let updatedRequest = builder.update(from: httpRequest, originalRequest: builder.build()) + let updatedRequest = builder.update(from: httpRequest, originalRequest: try builder.build()) XCTAssert(updatedRequest.path == "/hello") XCTAssert(updatedRequest.queryItems.count == 3) diff --git a/Tests/ClientRuntimeTests/NetworkingTests/Http/SdkRequestBuilderTests.swift b/Tests/ClientRuntimeTests/NetworkingTests/Http/SdkRequestBuilderTests.swift index 60d7478c5..822515872 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/Http/SdkRequestBuilderTests.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/Http/SdkRequestBuilderTests.swift @@ -12,13 +12,13 @@ class SdkRequestBuilderTests: XCTestCase { let url = "https://stehlibstoragebucket144955-dev.s3.us-east-1.amazonaws.com/public/README.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAVIBY7SZR4C4MBGAW%2F20220225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220225T034419Z&X-Amz-Expires=17999&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEQaCXVzLWVhc3QtMSJHMEUCIEKUDdnCf1h3cZNdv10q6G24zLgn0r6th4m%2FXSS9TuR4AiEAwOwf2cG%2F1W%2FkAz1UMqFW9sZp7SY6j%2BmiicLy1dB8OXUqmgYInf%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARABGgwzNjA4OTY3NjM0OTEiDH6CiNUJiwL4sNy9KCruBbdJnGSx88A%2Be0SBpKEpSurxunasaDsJb2ZJPqVhC%2FcKPr87PYcp5BrnkGumcYhUAEknVbHACLLUx2%2Fnzfacp13PHmclSsLe52qGwjFFVMz0m41PV3HiCoHgIc7vVIHnwNySaX9ZJbVowhNvI4V9eixVKhjjx7Tqku1bsOzlq9dJP15qz8FVNjlKGjZFqrMGTzpTgmS9dNPqphwCcxx306RLUd35SEvXjxnWcidOdddYQs9j4wu47DOM8ftveag6cDptJQN71dDIRHgFTisVshD78Rm9pycKf3g0QvBAGtrzhcxUcJtNgIWv%2B10hsEBURsEYommcjI8vT1yX2K8pLVOxgL%2FRWXndbAeIzAu5vmLm6RqkfGwkHQPQl7uII6YzL2Gku%2FMDilVFw9TBKIg6KDP9l2GzzVRQsvLMpFIp%2Bx3a1s4OVduJRFpDYCwEsfKhIoVkb610gBbFayPKjQVcZfULdq1r5DOZzpHVDoijnKHAxHtFgaFPP6KtG%2BmdKeix8gccdbsdgMokWKtJhisFo%2BzLn02oSSX4ITkZKzZcriGxQO4E1YUlYyBhjlCg1b74faQfWstk24PrkCfNXYcQ5oxgglIA0tBOdOfwGn2Je3MBEj2T8Yz0GS%2BZib3DKVWRzU0Xk9pwDXH3iaBn9Uld%2BNyw9gxdOCBVKtTILtdfsjw9lJSVOJomlJn1h8gH96PToBg9lOc4ms6aA2Z%2FoN%2F3UV%2Beo5%2FpB9xfMkBIeOg6vAI0VtjkUhH052UouEKU%2BVGSGuCSuVzZmIBJLWHZaQUJZ3hJCdGqM%2FoM4Ud51Cidcnr%2Bni%2Bgp4RAfg0gvX6Eb2e%2FNMqNd0Eg7ftmnPXzQR3ZVC1yyNFu2kBt8icKdMnkLZT8YO1Racd1QgrZ5DZJlU%2FS2eisLGSTMb9Dmaq8AGbxgGK55acIvsQLzvJhavFWbh%2FyNudQBSLC0c5BZGxQk2J%2BJx%2FlR5wR%2B0gXOfTg5EImMrLzh7gOo56i2Rhj2xRFSDDDnuGQBjqHArU56DsGZNeKwkVK87lVJAfiAsWmKaDlBNXcuhKl084syaZMiVUQa7dPupgeg%2BvVPv4dAwNULjuE3B7V5lSea4RIDOfGwTtlj4Ekn3t4PrlxHpEyMeuRgekNhpdcfL5pgRcKH4AKgqI7fCrghhH1qDMTpUkORRv6EwPGqM0LPm93XPVOjrG3EJ5ZOYLJM05bbrcalzN0mbSbsRrcphme7Z8zpD5jNeSWdBhWA04DQ7RZuJpqFnn40yKq7G8kgtLmOJGRs7CGTWT9on7EOpH3RdKGPXL06%2BgLo7r9%2Fdkb%2BHGJF6spjqbH0SN%2FySjnvgNL1NrGAy%2FQgPwfDw6oJ6TPNdH8dfM8tmdd&X-Amz-Signature=90b9f3353ad4f2596fdd166c00da8fb86d5d255bf3c8b296c5e5a7db25fd41aa" let pathToMatch = "/public/README.md?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=ASIAVIBY7SZR4C4MBGAW%2F20220225%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220225T034419Z&X-Amz-Expires=17999&X-Amz-SignedHeaders=host&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEEQaCXVzLWVhc3QtMSJHMEUCIEKUDdnCf1h3cZNdv10q6G24zLgn0r6th4m%2FXSS9TuR4AiEAwOwf2cG%2F1W%2FkAz1UMqFW9sZp7SY6j%2BmiicLy1dB8OXUqmgYInf%2F%2F%2F%2F%2F%2F%2F%2F%2F%2FARABGgwzNjA4OTY3NjM0OTEiDH6CiNUJiwL4sNy9KCruBbdJnGSx88A%2Be0SBpKEpSurxunasaDsJb2ZJPqVhC%2FcKPr87PYcp5BrnkGumcYhUAEknVbHACLLUx2%2Fnzfacp13PHmclSsLe52qGwjFFVMz0m41PV3HiCoHgIc7vVIHnwNySaX9ZJbVowhNvI4V9eixVKhjjx7Tqku1bsOzlq9dJP15qz8FVNjlKGjZFqrMGTzpTgmS9dNPqphwCcxx306RLUd35SEvXjxnWcidOdddYQs9j4wu47DOM8ftveag6cDptJQN71dDIRHgFTisVshD78Rm9pycKf3g0QvBAGtrzhcxUcJtNgIWv%2B10hsEBURsEYommcjI8vT1yX2K8pLVOxgL%2FRWXndbAeIzAu5vmLm6RqkfGwkHQPQl7uII6YzL2Gku%2FMDilVFw9TBKIg6KDP9l2GzzVRQsvLMpFIp%2Bx3a1s4OVduJRFpDYCwEsfKhIoVkb610gBbFayPKjQVcZfULdq1r5DOZzpHVDoijnKHAxHtFgaFPP6KtG%2BmdKeix8gccdbsdgMokWKtJhisFo%2BzLn02oSSX4ITkZKzZcriGxQO4E1YUlYyBhjlCg1b74faQfWstk24PrkCfNXYcQ5oxgglIA0tBOdOfwGn2Je3MBEj2T8Yz0GS%2BZib3DKVWRzU0Xk9pwDXH3iaBn9Uld%2BNyw9gxdOCBVKtTILtdfsjw9lJSVOJomlJn1h8gH96PToBg9lOc4ms6aA2Z%2FoN%2F3UV%2Beo5%2FpB9xfMkBIeOg6vAI0VtjkUhH052UouEKU%2BVGSGuCSuVzZmIBJLWHZaQUJZ3hJCdGqM%2FoM4Ud51Cidcnr%2Bni%2Bgp4RAfg0gvX6Eb2e%2FNMqNd0Eg7ftmnPXzQR3ZVC1yyNFu2kBt8icKdMnkLZT8YO1Racd1QgrZ5DZJlU%2FS2eisLGSTMb9Dmaq8AGbxgGK55acIvsQLzvJhavFWbh%2FyNudQBSLC0c5BZGxQk2J%2BJx%2FlR5wR%2B0gXOfTg5EImMrLzh7gOo56i2Rhj2xRFSDDDnuGQBjqHArU56DsGZNeKwkVK87lVJAfiAsWmKaDlBNXcuhKl084syaZMiVUQa7dPupgeg%2BvVPv4dAwNULjuE3B7V5lSea4RIDOfGwTtlj4Ekn3t4PrlxHpEyMeuRgekNhpdcfL5pgRcKH4AKgqI7fCrghhH1qDMTpUkORRv6EwPGqM0LPm93XPVOjrG3EJ5ZOYLJM05bbrcalzN0mbSbsRrcphme7Z8zpD5jNeSWdBhWA04DQ7RZuJpqFnn40yKq7G8kgtLmOJGRs7CGTWT9on7EOpH3RdKGPXL06%2BgLo7r9%2Fdkb%2BHGJF6spjqbH0SN%2FySjnvgNL1NrGAy%2FQgPwfDw6oJ6TPNdH8dfM8tmdd&X-Amz-Signature=90b9f3353ad4f2596fdd166c00da8fb86d5d255bf3c8b296c5e5a7db25fd41aa" let queryItems = [SDKURLQueryItem(name: "Bucket", value: "stehlibstoragebucket144955-dev"), SDKURLQueryItem(name: "Key", value: "public%2FREADME.md")] - let originalRequest = SdkHttpRequest(method: .get, endpoint: Endpoint(host: "stehlibstoragebucket144955-dev.s3.us-east-1.amazonaws.com", path: "/", port: 80, queryItems: queryItems, protocolType: .https)) + let originalRequest = SdkHttpRequest(method: .get, endpoint: try Endpoint(host: "stehlibstoragebucket144955-dev.s3.us-east-1.amazonaws.com", path: "/", port: 80, queryItems: queryItems, protocolType: .https)) let crtRequest = try HTTPRequest() crtRequest.path = pathToMatch - let updatedRequest = SdkHttpRequestBuilder().update(from: crtRequest, originalRequest: originalRequest).build() - let updatedPath = [updatedRequest.endpoint.path, updatedRequest.endpoint.query].compactMap { $0 }.joined(separator: "?") + let updatedRequest = try SdkHttpRequestBuilder().update(from: crtRequest, originalRequest: originalRequest).build() + let updatedPath = [updatedRequest.destination.path, updatedRequest.destination.queryString].compactMap { $0 }.joined(separator: "?") XCTAssertEqual(pathToMatch, updatedPath) - XCTAssertEqual(url, updatedRequest.endpoint.url?.absoluteString) + XCTAssertEqual(url, updatedRequest.destination.url?.absoluteString) } } diff --git a/Tests/ClientRuntimeTests/NetworkingTests/NetworkingTestUtils.swift b/Tests/ClientRuntimeTests/NetworkingTests/NetworkingTestUtils.swift index 30fb4cf16..895a5c146 100644 --- a/Tests/ClientRuntimeTests/NetworkingTests/NetworkingTestUtils.swift +++ b/Tests/ClientRuntimeTests/NetworkingTests/NetworkingTestUtils.swift @@ -52,7 +52,7 @@ class NetworkingTestUtils: XCTestCase { let endpoint: Endpoint! queryItems.append(SDKURLQueryItem(name: "qualifier", value: "qualifier-value")) - endpoint = Endpoint(host: host, path: path, queryItems: queryItems, headers: headers) + endpoint = try? Endpoint(host: host, path: path, queryItems: queryItems, headers: headers) return endpoint } diff --git a/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift b/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift index a5d18d4af..4b010d3f8 100644 --- a/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift +++ b/Tests/ClientRuntimeTests/OrchestratorTests/OrchestratorTests.swift @@ -493,7 +493,7 @@ class OrchestratorTests: XCTestCase { let b = self.traceOrchestrator(trace: partitionIdTrace) // We can force a getPartitionId to fail by explicitly setting the host to '' b.interceptors.addModifyBeforeRetryLoop({ context in - context.updateRequest(updated: context.getRequest().toBuilder().withHost("").build()) + context.updateRequest(updated: try context.getRequest().toBuilder().withHost("").build()) }) return try await b.build().execute(input: TestInput(foo: "")) } diff --git a/Tests/SmithyTestUtilTests/RequestTestUtilTests/HttpRequestTestBaseTests.swift b/Tests/SmithyTestUtilTests/RequestTestUtilTests/HttpRequestTestBaseTests.swift index 18b593143..abc054a9f 100644 --- a/Tests/SmithyTestUtilTests/RequestTestUtilTests/HttpRequestTestBaseTests.swift +++ b/Tests/SmithyTestUtilTests/RequestTestUtilTests/HttpRequestTestBaseTests.swift @@ -164,7 +164,7 @@ class HttpRequestTestBaseTests: HttpRequestTestBase { // Mocks the code-generated unit test which includes testing for forbidden/required headers/queries func testSayHello() async throws { - let expected = buildExpectedHttpRequest(method: .post, + let expected = try buildExpectedHttpRequest(method: .post, path: "/", headers: ["Content-Type": "application/json", "RequiredHeader": "required header"], @@ -196,7 +196,7 @@ class HttpRequestTestBaseTests: HttpRequestTestBase { let forbiddenQueryParams = ["ForbiddenQuery"] for forbiddenQueryParam in forbiddenQueryParams { XCTAssertFalse( - self.queryItemExists(forbiddenQueryParam, in: actual.endpoint.queryItems), + self.queryItemExists(forbiddenQueryParam, in: actual.destination.query), "Forbidden Query:\(forbiddenQueryParam) exists in query items" ) } @@ -208,7 +208,7 @@ class HttpRequestTestBaseTests: HttpRequestTestBase { let requiredQueryParams = ["RequiredQuery"] for requiredQueryParam in requiredQueryParams { - XCTAssertTrue(self.queryItemExists(requiredQueryParam, in: actual.endpoint.queryItems), + XCTAssertTrue(self.queryItemExists(requiredQueryParam, in: actual.destination.query), "Required Query:\(requiredQueryParam) does not exist in query items") } diff --git a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/middleware/EndpointResolverMiddleware.kt b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/middleware/EndpointResolverMiddleware.kt index 7b8a754ef..c80bcef84 100644 --- a/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/middleware/EndpointResolverMiddleware.kt +++ b/smithy-swift-codegen/src/main/kotlin/software/amazon/smithy/swift/codegen/middleware/EndpointResolverMiddleware.kt @@ -103,8 +103,8 @@ open class EndpointResolverMiddleware( attributes.set(key: AttributeKeys.signingAlgorithm, value: AWSSigningAlgorithm(rawValue: signingAlgorithm)) } - if let headers = endpoint.headers { - builder.withHeaders(headers) + if !endpoint.headers.isEmpty { + builder.withHeaders(endpoint.headers) } return builder.withMethod(attributes.getMethod())