Skip to content
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
4 changes: 3 additions & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
workflow_dispatch:

env:
BUILDER_VERSION: v0.8.19
BUILDER_VERSION: v0.9.11
BUILDER_SOURCE: releases
# host owned by CRT team to host aws-crt-builder releases. Contact their on-call with any issues
BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net
Expand All @@ -22,6 +22,8 @@ env:
jobs:
downstream:
runs-on: macos-11
env:
DEVELOPER_DIR: /Applications/Xcode_13.2.1.app
steps:
- name: Checkout sources
uses: actions/checkout@v2
Expand Down
12 changes: 6 additions & 6 deletions Packages/ClientRuntime/Sources/Middleware/AnyHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@
// SPDX-License-Identifier: Apache-2.0.

/// Type erased Handler
public struct AnyHandler<MInput, MOutput, Context: MiddlewareContext, MError: Error>: Handler {
private let _handle: (Context, MInput) -> Result<MOutput, MError>
public struct AnyHandler<MInput, MOutput, Context: MiddlewareContext>: Handler {
private let _handle: (Context, MInput) async throws -> MOutput

public init<H: Handler> (_ realHandler: H)
where H.Input == MInput, H.Output == MOutput, H.Context == Context, H.MiddlewareError == MError {
if let alreadyErased = realHandler as? AnyHandler<MInput, MOutput, Context, MError> {
where H.Input == MInput, H.Output == MOutput, H.Context == Context {
if let alreadyErased = realHandler as? AnyHandler<MInput, MOutput, Context> {
self = alreadyErased
return
}
self._handle = realHandler.handle
}

public func handle(context: Context, input: MInput) -> Result<MOutput, MError> {
return _handle(context, input)
public func handle(context: Context, input: MInput) async throws -> MOutput {
return try await _handle(context, input)
}
}
20 changes: 9 additions & 11 deletions Packages/ClientRuntime/Sources/Middleware/AnyMiddleware.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
// SPDX-License-Identifier: Apache-2.0.

/// type erase the Middleware protocol
public struct AnyMiddleware<MInput, MOutput, Context: MiddlewareContext, MError: Error>: Middleware {
public struct AnyMiddleware<MInput, MOutput, Context: MiddlewareContext>: Middleware {

private let _handle: (Context, MInput, AnyHandler<MInput, MOutput, Context, MError>) -> Result<MOutput, MError>
private let _handle: (Context, MInput, AnyHandler<MInput, MOutput, Context>) async throws -> MOutput

public var id: String

public init<M: Middleware>(_ realMiddleware: M)
where M.MInput == MInput, M.MOutput == MOutput, M.Context == Context, M.MError == MError {
if let alreadyErased = realMiddleware as? AnyMiddleware<MInput, MOutput, Context, MError> {
where M.MInput == MInput, M.MOutput == MOutput, M.Context == Context {
if let alreadyErased = realMiddleware as? AnyMiddleware<MInput, MOutput, Context> {
self = alreadyErased
return
}
Expand All @@ -21,20 +21,18 @@ public struct AnyMiddleware<MInput, MOutput, Context: MiddlewareContext, MError:

public init<H: Handler>(handler: H, id: String) where H.Input == MInput,
H.Output == MOutput,
H.Context == Context,
H.MiddlewareError == MError {
H.Context == Context {

self._handle = { context, input, handler in
handler.handle(context: context, input: input)
try await handler.handle(context: context, input: input)
}
self.id = id
}

public func handle<H: Handler>(context: Context, input: MInput, next: H) -> Result<MOutput, MError>
public func handle<H: Handler>(context: Context, input: MInput, next: H) async throws -> MOutput
where H.Input == MInput,
H.Output == MOutput,
H.Context == Context,
H.MiddlewareError == MError {
return _handle(context, input, next.eraseToAnyHandler())
H.Context == Context {
return try await _handle(context, input, next.eraseToAnyHandler())
}
}
14 changes: 6 additions & 8 deletions Packages/ClientRuntime/Sources/Middleware/ComposedHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,28 @@
// SPDX-License-Identifier: Apache-2.0.

// handler chain, used to decorate a handler with middleware
public struct ComposedHandler<MInput, MOutput, Context: MiddlewareContext, MError: Error> {
public struct ComposedHandler<MInput, MOutput, Context: MiddlewareContext> {
// the next handler to call
let next: AnyHandler<MInput, MOutput, Context, MError>
let next: AnyHandler<MInput, MOutput, Context>

// the middleware decorating 'next'
let with: AnyMiddleware<MInput, MOutput, Context, MError>
let with: AnyMiddleware<MInput, MOutput, Context>

public init<H: Handler, M: Middleware> (_ realNext: H, _ realWith: M)
where H.Input == MInput,
H.Output == MOutput,
M.MInput == MInput,
M.MOutput == MOutput,
M.Context == Context,
H.Context == Context,
H.MiddlewareError == MError,
M.MError == MError {
H.Context == Context {

self.next = AnyHandler(realNext)
self.with = AnyMiddleware(realWith)
}
}

extension ComposedHandler: Handler {
public func handle(context: Context, input: MInput) -> Result<MOutput, MError> {
return with.handle(context: context, input: input, next: next)
public func handle(context: Context, input: MInput) async throws -> MOutput {
return try await with.handle(context: context, input: input, next: next)
}
}
5 changes: 2 additions & 3 deletions Packages/ClientRuntime/Sources/Middleware/Handler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ public protocol Handler {
associatedtype Input
associatedtype Output
associatedtype Context: MiddlewareContext
associatedtype MiddlewareError: Error

func handle(context: Context, input: Input) -> Result<Output, MiddlewareError>
func handle(context: Context, input: Input) async throws -> Output
}

extension Handler {
public func eraseToAnyHandler() -> AnyHandler<Input, Output, Context, MiddlewareError> {
public func eraseToAnyHandler() -> AnyHandler<Input, Output, Context> {
return AnyHandler(self)
}
}
7 changes: 3 additions & 4 deletions Packages/ClientRuntime/Sources/Middleware/Middleware.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,18 @@ public protocol Middleware {
associatedtype MInput
associatedtype MOutput
associatedtype Context: MiddlewareContext
associatedtype MError: Error

/// The middleware ID
var id: String { get }

func handle<H: Handler>(context: Context,
input: MInput,
next: H) -> Result<MOutput, MError>
where H.Input == MInput, H.Output == MOutput, H.Context == Context, H.MiddlewareError == MError
next: H) async throws -> MOutput
where H.Input == MInput, H.Output == MOutput, H.Context == Context
}

extension Middleware {
public func eraseToAnyMiddleware() -> AnyMiddleware<MInput, MOutput, Context, MError> {
public func eraseToAnyMiddleware() -> AnyMiddleware<MInput, MOutput, Context> {
return AnyMiddleware(self)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@

public typealias MiddlewareFunction<MInput,
MOutput,
Context: MiddlewareContext,
MError: Error> = (Context,
Context: MiddlewareContext> = (Context,
MInput,
AnyHandler<MInput,
MOutput,
Context,
MError>) -> Result<MOutput, MError>
Context>) async throws -> MOutput
21 changes: 9 additions & 12 deletions Packages/ClientRuntime/Sources/Middleware/MiddlewareStep.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,36 @@
/// An instance of MiddlewareStep will be contained in the operation stack, and recognized as a single
/// step (initialize, build, etc..) that contains an ordered list of middlewares. This class is
/// responsible for ordering these middlewares so that they are executed in the correct order.
public struct MiddlewareStep<StepContext: MiddlewareContext, Input, Output, MError: Error>: Middleware {
public struct MiddlewareStep<StepContext: MiddlewareContext, Input, Output>: Middleware {
public typealias Context = StepContext
public typealias MInput = Input
public typealias MOutput = Output
public typealias MError = MError

var orderedMiddleware: OrderedGroup<MInput,
MOutput,
Context,
MError> = OrderedGroup<MInput, MOutput, Context, MError>()
Context> = OrderedGroup<MInput, MOutput, Context>()

public let id: String

public init(id: String) {
self.id = id
}

func get(id: String) -> AnyMiddleware<MInput, MOutput, Context, MError>? {
func get(id: String) -> AnyMiddleware<MInput, MOutput, Context>? {
return orderedMiddleware.get(id: id)
}

/// This execute will execute the stack and use your next as the last closure in the chain
public func handle<H: Handler>(context: Context,
input: MInput,
next: H) -> Result<MOutput, MError>
where H.Input == MInput, H.Output == MOutput, H.Context == Context, H.MiddlewareError == MError {
next: H) async throws -> MOutput
where H.Input == MInput, H.Output == MOutput, H.Context == Context {

var handler = next.eraseToAnyHandler()
let order = orderedMiddleware.orderedItems

guard !order.isEmpty else {
return handler.handle(context: context, input: input)
return try await handler.handle(context: context, input: input)
}
let numberOfMiddlewares = order.count
let reversedCollection = (0...(numberOfMiddlewares-1)).reversed()
Expand All @@ -44,12 +42,11 @@ public struct MiddlewareStep<StepContext: MiddlewareContext, Input, Output, MErr
handler = composedHandler.eraseToAnyHandler()
}

let result = handler.handle(context: context, input: input)
return result
return try await handler.handle(context: context, input: input)
}

public mutating func intercept<M: Middleware>(position: Position, middleware: M)
where M.MInput == MInput, M.MOutput == MOutput, M.Context == Context, M.MError == MError {
where M.MInput == MInput, M.MOutput == MOutput, M.Context == Context {
orderedMiddleware.add(middleware: middleware.eraseToAnyMiddleware(), position: position)
}

Expand All @@ -61,7 +58,7 @@ public struct MiddlewareStep<StepContext: MiddlewareContext, Input, Output, MErr
///
public mutating func intercept(position: Position,
id: String,
middleware: @escaping MiddlewareFunction<MInput, MOutput, Context, MError>) {
middleware: @escaping MiddlewareFunction<MInput, MOutput, Context>) {
let middleware = WrappedMiddleware(middleware, id: id)
orderedMiddleware.add(middleware: middleware.eraseToAnyMiddleware(), position: position)
}
Expand Down
11 changes: 5 additions & 6 deletions Packages/ClientRuntime/Sources/Middleware/NoopHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@
// SPDX-License-Identifier: Apache-2.0
//

public struct NoopHandler<Output: HttpResponseBinding, OutputError: HttpResponseBinding>: Handler {
public init() {
}
public func handle(context: HttpContext, input: SdkHttpRequest) -> Result<OperationOutput<Output>, SdkError<OutputError>> {
let output = OperationOutput<Output>(httpResponse: HttpResponse())
return .success(output)
public struct NoopHandler<Output: HttpResponseBinding>: Handler {
public init() {}

public func handle(context: HttpContext, input: SdkHttpRequest) async throws -> OperationOutput<Output> {
return OperationOutput<Output>(httpResponse: HttpResponse())
}
}
70 changes: 31 additions & 39 deletions Packages/ClientRuntime/Sources/Middleware/OperationStack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,76 +7,68 @@ public struct OperationStack<OperationStackInput,

/// returns the unique id for the operation stack as middleware
public var id: String
public var initializeStep: InitializeStep<OperationStackInput, OperationStackOutput, OperationStackError>
public var serializeStep: SerializeStep<OperationStackInput, OperationStackOutput, OperationStackError>
public var buildStep: BuildStep<OperationStackOutput, OperationStackError>
public var finalizeStep: FinalizeStep<OperationStackOutput, OperationStackError>
public var deserializeStep: DeserializeStep<OperationStackOutput, OperationStackError>
public var initializeStep: InitializeStep<OperationStackInput, OperationStackOutput>
public var serializeStep: SerializeStep<OperationStackInput, OperationStackOutput>
public var buildStep: BuildStep<OperationStackOutput>
public var finalizeStep: FinalizeStep<OperationStackOutput>
public var deserializeStep: DeserializeStep<OperationStackOutput>

public init(id: String) {
self.id = id
self.initializeStep = InitializeStep<OperationStackInput,
OperationStackOutput,
OperationStackError>(id: InitializeStepId)
OperationStackOutput>(id: InitializeStepId)
self.serializeStep = SerializeStep<OperationStackInput,
OperationStackOutput,
OperationStackError>(id: SerializeStepId)
self.buildStep = BuildStep<OperationStackOutput,
OperationStackError>(id: BuildStepId)
self.finalizeStep = FinalizeStep<OperationStackOutput,
OperationStackError>(id: FinalizeStepId)
self.deserializeStep = DeserializeStep<OperationStackOutput,
OperationStackError>(id: DeserializeStepId)
OperationStackOutput>(id: SerializeStepId)
self.buildStep = BuildStep<OperationStackOutput>(id: BuildStepId)
self.finalizeStep = FinalizeStep<OperationStackOutput>(id: FinalizeStepId)
self.deserializeStep = DeserializeStep<OperationStackOutput>(id: DeserializeStepId)

}

/// This execute will execute the stack and use your next as the last closure in the chain
public func handleMiddleware<H: Handler>(context: HttpContext,
input: OperationStackInput,
next: H) -> SdkResult<OperationStackOutput, OperationStackError>
next: H) async throws -> OperationStackOutput
where H.Input == SdkHttpRequest,
H.Output == OperationOutput<OperationStackOutput>,
H.Context == HttpContext,
H.MiddlewareError == SdkError<OperationStackError> {

let deserialize = compose(next: DeserializeStepHandler(handler: next), with: deserializeStep)
let finalize = compose(next: FinalizeStepHandler(handler: deserialize), with: finalizeStep)
let build = compose(next: BuildStepHandler(handler: finalize), with: buildStep)
let serialize = compose(next: SerializeStepHandler(handler: build), with: serializeStep)
let initialize = compose(next: InitializeStepHandler(handler: serialize), with: initializeStep)

let result = initialize.handle(context: context, input: input)
return result.flatMap { operationOutput in
return .success(operationOutput.output!)
}
}
H.Context == HttpContext {
let deserialize = compose(next: DeserializeStepHandler(handler: next), with: deserializeStep)
let finalize = compose(next: FinalizeStepHandler(handler: deserialize), with: finalizeStep)
let build = compose(next: BuildStepHandler(handler: finalize), with: buildStep)
let serialize = compose(next: SerializeStepHandler(handler: build), with: serializeStep)
let initialize = compose(next: InitializeStepHandler(handler: serialize), with: initializeStep)
let result = try await initialize.handle(context: context, input: input)
guard let output = result.output else {
throw ClientError.unknownError("Something went terribly wrong where the output was not set on the response. Please open a ticket with us at https://github.com/awslabs/aws-sdk-swift")
}
return output
}

mutating public func presignedRequest<H: Handler>(context: HttpContext,
input: OperationStackInput,
next: H) -> SdkHttpRequestBuilder?
next: H) async throws -> SdkHttpRequestBuilder?
where H.Input == SdkHttpRequest,
H.Output == OperationOutput<OperationStackOutput>,
H.Context == HttpContext,
H.MiddlewareError == SdkError<OperationStackError> {
H.Context == HttpContext {
var builder: SdkHttpRequestBuilder?
self.finalizeStep.intercept(position: .after,
middleware: PresignerShim(handler: { buildInMiddleware in
middleware: PresignerShim<OperationStackOutput, OperationStackError>(handler: { buildInMiddleware in
builder = buildInMiddleware
}))
_ = handleMiddleware(context: context, input: input, next: next)
_ = try await handleMiddleware(context: context, input: input, next: next)
return builder
}

/// Compose (wrap) the handler with the given middleware or essentially build out the linked list of middleware
private func compose<H: Handler, M: Middleware>(next handler: H,
with middlewares: M...) -> AnyHandler<H.Input,
H.Output,
H.Context,
H.MiddlewareError>
H.Context>
where M.MOutput == H.Output,
M.MInput == H.Input,
H.Context == M.Context,
H.MiddlewareError == M.MError {
H.Context == M.Context {
guard !middlewares.isEmpty,
let lastMiddleware = middlewares.last else {
return handler.eraseToAnyHandler()
Expand Down
Loading