Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add signer protocol. #598

Merged
merged 9 commits into from
Oct 9, 2023
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
16 changes: 16 additions & 0 deletions Sources/ClientRuntime/Auth/HTTPAuthAPI/Signer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

public protocol Signer {
func sign<IdentityT: Identity>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generic in the method signature versus an associated type on the Signer protocol provides enough flexibility here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe so, yes.

requestBuilder: SdkHttpRequestBuilder,
identity: IdentityT,
signingProperties: Attributes
) async throws -> SdkHttpRequestBuilder
}
150 changes: 78 additions & 72 deletions Sources/ClientRuntime/Networking/Http/HttpContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,61 +9,65 @@ public class HttpContext: MiddlewareContext {
public init(attributes: Attributes) {
self.attributes = attributes
}
// FIXME: Move all defined keys to separate file as constants to be used elsewhere
public func getPath() -> String {
return attributes.get(key: AttributeKey<String>(name: "Path"))!
}

public func getMethod() -> HttpMethodType {
return attributes.get(key: AttributeKey<HttpMethodType>(name: "Method"))!
public func getDecoder() -> ResponseDecoder {
return attributes.get(key: AttributeKeys.decoder)!
}

public func getEncoder() -> RequestEncoder {
return attributes.get(key: AttributeKey<RequestEncoder>(name: "Encoder"))!
}

public func getDecoder() -> ResponseDecoder {
return attributes.get(key: AttributeKey<ResponseDecoder>(name: "Decoder"))!
return attributes.get(key: AttributeKeys.encoder)!
}

public func getHost() -> String? {
return attributes.get(key: AttributeKey<String>(name: "Host"))
return attributes.get(key: AttributeKeys.host)
}

public func getServiceName() -> String {
return attributes.get(key: AttributeKey<String>(name: "ServiceName"))!
public func getHostPrefix() -> String? {
return attributes.get(key: AttributeKeys.hostPrefix)
}

public func getIdempotencyTokenGenerator() -> IdempotencyTokenGenerator {
return attributes.get(key: AttributeKey<IdempotencyTokenGenerator>(name: "IdempotencyTokenGenerator"))!
return attributes.get(key: AttributeKeys.idempotencyTokenGenerator)!
}

public func getHostPrefix() -> String? {
return attributes.get(key: AttributeKey<String>(name: "HostPrefix"))
public func getIdentityResolvers() -> Attributes {
return attributes.get(key: AttributeKeys.identityResolvers)!
}

public func getLogger() -> LogAgent? {
return attributes.get(key: AttributeKey<LogAgent>(name: "Logger"))
return attributes.get(key: AttributeKeys.logger)
}

public func getMessageEncoder() -> MessageEncoder? {
return attributes.get(key: AttributeKeys.messageEncoder)
}

public func getMessageSigner() -> MessageSigner? {
return attributes.get(key: AttributeKeys.messageSigner)
}

public func getMethod() -> HttpMethodType {
return attributes.get(key: AttributeKeys.method)!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please avoid use of forced unwrapping

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code existed previously, it shows up as diff because I changed the order of methods in the file to be alphabetical. Avoiding use of forced unwrapping may be refactor task for later time. From what I've gathered, the code force-unwraps because by the time this method is called, this attribute HAS to have been set.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Issue created: awslabs/aws-sdk-swift#1168

Will pick up after finishing SRA Identity & Auth

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "right" way to unwrap is probably to throw an exception back to the caller if the expected attribute is not set.

David's correct though, !-ing an optional is virtually never the best solution in production code. (It's typically more okay in tests & other support code.)

}

/// The partition ID to be used for this context.
///
/// Requests made with the same partition ID will be grouped together for retry throttling purposes.
/// If no partition ID is provided, requests will be partitioned based on the hostname.
public func getPartitionID() -> String? {
return attributes.get(key: AttributeKey<String>(name: "PartitionID"))
return attributes.get(key: AttributeKeys.partitionId)
}

public func getMessageEncoder() -> MessageEncoder? {
return attributes.get(key: HttpContext.messageEncoder)
public func getPath() -> String {
return attributes.get(key: AttributeKeys.path)!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please avoid use of forced unwrapping

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above

}

public func getMessageSigner() -> MessageSigner? {
return attributes.get(key: HttpContext.messageSigner)
public func getServiceName() -> String {
return attributes.get(key: AttributeKeys.serviceName)!
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please avoid use of forced unwrapping

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comment above

}

public func isBidirectionalStreamingEnabled() -> Bool {
return attributes.get(key: HttpContext.bidirectionalStreaming) ?? false
return attributes.get(key: AttributeKeys.bidirectionalStreaming) ?? false
}

/// Returns `true` if the request should use `http2` and only `http2` without falling back to `http1`
Expand All @@ -72,29 +76,11 @@ public class HttpContext: MiddlewareContext {
}
}

extension HttpContext {
public static let messageEncoder = AttributeKey<MessageEncoder>(name: "MessageEncoder")
public static let messageSigner = AttributeKey<MessageSigner>(name: "MessageSigner")
public static let bidirectionalStreaming = AttributeKey<Bool>(name: "BidirectionalStreaming")
}

public class HttpContextBuilder {

public init() {}

public var attributes: Attributes = Attributes()
let encoder = AttributeKey<RequestEncoder>(name: "Encoder")
let method = AttributeKey<HttpMethodType>(name: "Method")
let path = AttributeKey<String>(name: "Path")
let operation = AttributeKey<String>(name: "Operation")
let host = AttributeKey<String>(name: "Host")
let serviceName = AttributeKey<String>(name: "ServiceName")
var response: HttpResponse = HttpResponse()
let decoder = AttributeKey<ResponseDecoder>(name: "Decoder")
let idempotencyTokenGenerator = AttributeKey<IdempotencyTokenGenerator>(name: "IdempotencyTokenGenerator")
let hostPrefix = AttributeKey<String>(name: "HostPrefix")
let logger = AttributeKey<LogAgent>(name: "Logger")
let partitionID = AttributeKey<String>(name: "PartitionID")

// We follow the convention of returning the builder object
// itself from any configuration methods, and by adding the
Expand All @@ -108,84 +94,104 @@ public class HttpContextBuilder {
}

@discardableResult
public func withEncoder(value: RequestEncoder) -> HttpContextBuilder {
self.attributes.set(key: encoder, value: value)
public func withDecoder(value: ResponseDecoder) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.decoder, value: value)
return self
}

@discardableResult
public func withMethod(value: HttpMethodType) -> HttpContextBuilder {
self.attributes.set(key: method, value: value)
public func withEncoder(value: RequestEncoder) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.encoder, value: value)
return self
}

@discardableResult
public func withPath(value: String) -> HttpContextBuilder {
self.attributes.set(key: path, value: value)
public func withHost(value: String) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.host, value: value)
return self
}

@discardableResult
public func withHost(value: String) -> HttpContextBuilder {
self.attributes.set(key: host, value: value)
public func withHostPrefix(value: String) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.hostPrefix, value: value)
return self
}

@discardableResult
public func withHostPrefix(value: String) -> HttpContextBuilder {
self.attributes.set(key: hostPrefix, value: value)
public func withIdempotencyTokenGenerator(value: IdempotencyTokenGenerator) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.idempotencyTokenGenerator, value: value)
return self
}

@discardableResult
public func withOperation(value: String) -> HttpContextBuilder {
self.attributes.set(key: operation, value: value)
public func withLogger(value: LogAgent) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.logger, value: value)
return self
}

@discardableResult
public func withServiceName(value: String) -> HttpContextBuilder {
self.attributes.set(key: serviceName, value: value)
public func withMethod(value: HttpMethodType) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.method, value: value)
return self
}

@discardableResult
public func withDecoder(value: ResponseDecoder) -> HttpContextBuilder {
self.attributes.set(key: decoder, value: value)
public func withOperation(value: String) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.operation, value: value)
return self
}

/// Sets the partition ID on the context builder.
///
/// Requests made with the same partition ID will be grouped together for retry throttling purposes.
/// If no partition ID is provided, requests will be partitioned based on the hostname.
/// - Parameter value: The partition ID to be set on this builder, or `nil`.
/// - Returns: `self`, after the partition ID is set as specified.
@discardableResult
public func withResponse(value: HttpResponse) -> HttpContextBuilder {
self.response = value
public func withPartitionID(value: String?) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.partitionId, value: value)
return self
}

@discardableResult
public func withIdempotencyTokenGenerator(value: IdempotencyTokenGenerator) -> HttpContextBuilder {
self.attributes.set(key: idempotencyTokenGenerator, value: value)
public func withPath(value: String) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.path, value: value)
return self
}

@discardableResult
public func withLogger(value: LogAgent) -> HttpContextBuilder {
self.attributes.set(key: logger, value: value)
public func withResponse(value: HttpResponse) -> HttpContextBuilder {
self.response = value
return self
}

/// Sets the partition ID on the context builder.
///
/// Requests made with the same partition ID will be grouped together for retry throttling purposes.
/// If no partition ID is provided, requests will be partitioned based on the hostname.
/// - Parameter value: The partition ID to be set on this builder, or `nil`.
/// - Returns: `self`, after the partition ID is set as specified.
@discardableResult
public func withPartitionID(value: String?) -> HttpContextBuilder {
self.attributes.set(key: partitionID, value: value)
public func withServiceName(value: String) -> HttpContextBuilder {
self.attributes.set(key: AttributeKeys.serviceName, value: value)
return self
}

public func build() -> HttpContext {
return HttpContext(attributes: attributes)
}
}

public enum AttributeKeys {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as on the SDK

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Responded to comment on the SDK side.

public static let bidirectionalStreaming = AttributeKey<Bool>(name: "BidirectionalStreaming")
public static let decoder = AttributeKey<ResponseDecoder>(name: "Decoder")
public static let encoder = AttributeKey<RequestEncoder>(name: "Encoder")
public static let host = AttributeKey<String>(name: "Host")
public static let hostPrefix = AttributeKey<String>(name: "HostPrefix")
public static let idempotencyTokenGenerator = AttributeKey<IdempotencyTokenGenerator>(
name: "IdempotencyTokenGenerator"
)
public static let identityResolvers = AttributeKey<Attributes>(name: "IdentityResolvers")
public static let logger = AttributeKey<LogAgent>(name: "Logger")
public static let messageEncoder = AttributeKey<MessageEncoder>(name: "MessageEncoder")
public static let messageSigner = AttributeKey<MessageSigner>(name: "MessageSigner")
public static let method = AttributeKey<HttpMethodType>(name: "Method")
public static let operation = AttributeKey<String>(name: "Operation")
public static let partitionId = AttributeKey<String>(name: "PartitionID")
public static let path = AttributeKey<String>(name: "Path")
public static let serviceName = AttributeKey<String>(name: "ServiceName")
}
Loading