From 8959fdc0fad966e8e6aae50eb8f5404a887b50e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 22 Oct 2024 11:07:44 +0200 Subject: [PATCH 01/10] add streaming example and doc --- Examples/APIGateway/template.yaml | 2 +- Examples/Streaming/.gitignore | 8 + Examples/Streaming/Package.swift | 60 +++++++ Examples/Streaming/README.md | 217 ++++++++++++++++++++++++++ Examples/Streaming/Sources/main.swift | 22 +++ Examples/Streaming/format.sh | 45 ++++++ Examples/Streaming/template.yaml | 25 +++ readme.md | 35 ++++- 8 files changed, 412 insertions(+), 2 deletions(-) create mode 100644 Examples/Streaming/.gitignore create mode 100644 Examples/Streaming/Package.swift create mode 100644 Examples/Streaming/README.md create mode 100644 Examples/Streaming/Sources/main.swift create mode 100755 Examples/Streaming/format.sh create mode 100644 Examples/Streaming/template.yaml diff --git a/Examples/APIGateway/template.yaml b/Examples/APIGateway/template.yaml index 0d7a6af4..939f09f8 100644 --- a/Examples/APIGateway/template.yaml +++ b/Examples/APIGateway/template.yaml @@ -1,6 +1,6 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 -Description: SAM Template for QuoteService +Description: SAM Template for APIGateway Lambda Example Resources: # Lambda function diff --git a/Examples/Streaming/.gitignore b/Examples/Streaming/.gitignore new file mode 100644 index 00000000..0023a534 --- /dev/null +++ b/Examples/Streaming/.gitignore @@ -0,0 +1,8 @@ +.DS_Store +/.build +/Packages +xcuserdata/ +DerivedData/ +.swiftpm/configuration/registries.json +.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata +.netrc diff --git a/Examples/Streaming/Package.swift b/Examples/Streaming/Package.swift new file mode 100644 index 00000000..85e1d093 --- /dev/null +++ b/Examples/Streaming/Package.swift @@ -0,0 +1,60 @@ +// swift-tools-version:6.0 + +import PackageDescription + +// needed for CI to test the local version of the library +import struct Foundation.URL + +#if os(macOS) +let platforms: [PackageDescription.SupportedPlatform]? = [.macOS(.v15)] +#else +let platforms: [PackageDescription.SupportedPlatform]? = nil +#endif + +let package = Package( + name: "swift-aws-lambda-runtime-example", + platforms: platforms, + products: [ + .executable(name: "StreamingNumbers", targets: ["StreamingNumbers"]) + ], + dependencies: [ + // during CI, the dependency on local version of swift-aws-lambda-runtime is added dynamically below + .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", branch: "main") + ], + targets: [ + .executableTarget( + name: "StreamingNumbers", + dependencies: [ + .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime") + ], + path: "." + ) + ] +) + +if let localDepsPath = Context.environment["LAMBDA_USE_LOCAL_DEPS"], + localDepsPath != "", + let v = try? URL(fileURLWithPath: localDepsPath).resourceValues(forKeys: [.isDirectoryKey]), + v.isDirectory == true +{ + // when we use the local runtime as deps, let's remove the dependency added above + let indexToRemove = package.dependencies.firstIndex { dependency in + if case .sourceControl( + name: _, + location: "https://github.com/swift-server/swift-aws-lambda-runtime.git", + requirement: _ + ) = dependency.kind { + return true + } + return false + } + if let indexToRemove { + package.dependencies.remove(at: indexToRemove) + } + + // then we add the dependency on LAMBDA_USE_LOCAL_DEPS' path (typically ../..) + print("[INFO] Compiling against swift-aws-lambda-runtime located at \(localDepsPath)") + package.dependencies += [ + .package(name: "swift-aws-lambda-runtime", path: localDepsPath) + ] +} diff --git a/Examples/Streaming/README.md b/Examples/Streaming/README.md new file mode 100644 index 00000000..a74c640e --- /dev/null +++ b/Examples/Streaming/README.md @@ -0,0 +1,217 @@ +# Streaming Lambda function + +You can configure your Lambda function to stream response payloads back to clients. Response streaming can benefit latency sensitive applications by improving time to first byte (TTFB) performance. This is because you can send partial responses back to the client as they become available. Additionally, you can use response streaming to build functions that return larger payloads. Response stream payloads have a soft limit of 20 MB as compared to the 6 MB limit for buffered responses. Streaming a response also means that your function doesn’t need to fit the entire response in memory. For very large responses, this can reduce the amount of memory you need to configure for your function. + +Streaming responses incurs a cost. For more information, see [AWS Lambda Pricing](https://aws.amazon.com/lambda/pricing/). + +You can stream responses through [Lambda function URLs](https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html), the AWS SDK, or using the Lambda [InvokeWithResponseStream](https://docs.aws.amazon.com/lambda/latest/dg/API_InvokeWithResponseStream.html) API. In this example, we create an authenticated Lambda function URL. + + +## Code + +The sample code creates a `SendNumbersWithPause` struct that conforms to the `StreamingLambdaHandler` protocol provided by the Swift AWS Lambda Runtime. + +The `handle(...)` method of this protocol receives incoming events as a Swift NIO `ByteBuffer` and returns the output as a `ByteBuffer`. + +The response is streamed through the `LambdaResponseStreamWriter`, which is passed as an argument in the `handle` function. The code calls the `write(_:)` function of the `LambdaResponseStreamWriter` with partial data repeatedly written before +finally closing the response stream by calling `finish()`. Developers can also choose to return the entire output and not +stream the response by calling `writeAndFinish(_:)`. + +An error is thrown if `finish()` is called multiple times or if it is called after having called `writeAndFinish(_:)`. + +The `handle(...)` method is marked as `mutating` to allow handlers to be implemented with a `struct`. + +Once the struct is created and the `handle(...)` method is defined, the sample code creates a `LambdaRuntime` struct and initializes it with the handler just created. Then, the code calls `run()` to start the interaction with the AWS Lambda control plane. + +## Build & Package + +To build & archive the package, type the following commands. + +```bash +swift package archive --allow-network-connections docker +``` + +If there is no error, there is a ZIP file ready to deploy. +The ZIP file is located at `.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/StreamingNumbers/StreamingNumbers.zip` + +## Deploy with the AWS CLI + +Here is how to deploy using the `aws` command line. + +### Step 1: Create the function +```bash +AWS_ACCOUNT_ID=012345678901 +aws lambda create-function \ +--function-name StreamingNumbers \ +--zip-file fileb://.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/StreamingNumbers/StreamingNumbers.zip \ +--runtime provided.al2 \ +--handler provided \ +--architectures arm64 \ +--role arn:aws:iam::$AWS_ACCOUNT_ID:role/lambda_basic_execution \ +--timeout 15 +``` + +> [!IMPORTANT] +> The timeout value must be bigger than the time it takes for your function to stream its output. Otherwise, the Lambda control plane will terminate the execution environment before your code has a chance to finish writing the stream. Here, teh sample function stream responses during 10 seconds and we set the timeout for 15 seconds. + +The `--architectures` flag is only required when you build the binary on an Apple Silicon machine (Apple M1 or more recent). It defaults to `x64`. + +Be sure to set `AWS_ACCOUNT_ID` with your actual AWS account ID (for example: 012345678901). + +### Step2: Give permission to invoke that function through an URL + +Anyone with a valid signature from your AWS account will have permission to invoke the function through its URL. + +```bash +aws lambda add-permission \ + --function-name StreamingNumbers \ + --action lambda:InvokeFunctionUrl \ + --principal $AWS_ACCOUNT_ID \ + --function-url-auth-type AWS_IAM \ + --statement-id allowURL +``` + +Be sure to replace with your actual AWS account ID (for example: 012345678901). + +### Step3: Create the URL + +This creates [an URL with IAM authentication](https://docs.aws.amazon.com/lambda/latest/dg/urls-auth.html). Only calls with a valid signature will be authorized. + +```bash +aws lambda create-function-url-config \ + --function-name StreamingNumbers \ + --auth-type AWS_IAM \ + --invoke-mode RESPONSE_STREAM +``` +This calls return various information, including the URL to invoke your function. + +```json +{ + "FunctionUrl": "https://ul3nf4dogmgyr7ffl5r5rs22640fwocc.lambda-url.us-east-1.on.aws/", + "FunctionArn": "arn:aws:lambda:us-east-1:012345678901:function:StreamingNumbers", + "AuthType": "AWS_IAM", + "CreationTime": "2024-10-22T07:57:23.112599Z", + "InvokeMode": "RESPONSE_STREAM" +} +``` + +### Invoke your Lambda function + +To invoke the Lambda function, use `curl` with the AWS Sigv4 option to generate the signature. +```bash +URL=https://ul3nf4dogmgyr7ffl5r5rs22640fwocc.lambda-url.us-east-1.on.aws/ +REGION=us-east-1 +ACCESS_KEY=AK... +SECRET_KEY=... +AWS_SESSION_TOKEN=... + +curl "$URL" \ + --user "$ACCESS_KEY":"$SECRET_KEY" \ + --aws-sigv4 "aws:amz:$REGION:lambda" \ + -H "x-amz-security-token: $AWS_SESSION_TOKEN" \ + --no-buffer +``` + +Note that there is no payload required for this example. + +This should output the following result, with a one-second delay between each numbers. + +``` +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 +``` + +### Undeploy + +When done testing, you can delete the Lambda function with this command. + +```bash +aws lambda delete-function --function-name StreamingNumbers +``` + +## Deploy with AWS SAM + +Alternatively, you can use [AWS SAM](https://aws.amazon.com/serverless/sam/) to deploy the Lambda function. + +**Prerequisites** : Install the [SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) + +### SAM Template + +The template file is provided as part of the example in the `template.yaml` file. It defines a Lambda function based on the binary ZIP file. It creates the function url with IAM authentication and sets the function timeout to 15 seconds. + +```yaml +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: SAM Template for StreamingLambda Example + +Resources: + # Lambda function + StreamingNumbers: + Type: AWS::Serverless::Function + Properties: + CodeUri: .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/StreamingNumbers/StreamingNumbers.zip + Timeout: 15 + Handler: swift.bootstrap # ignored by the Swift runtime + Runtime: provided.al2 + MemorySize: 128 + Architectures: + - arm64 + FunctionUrlConfig: + AuthType: AWS_IAM + InvokeMode: RESPONSE_STREAM + +Outputs: + # print Lambda function URL + LambdaURL: + Description: Lambda URL + Value: !GetAtt StreamingNumbersUrl.FunctionUrl +``` + +### Deploy with SAM + +```bash +sam deploy \ +--resolve-s3 \ +--template-file template.yaml \ +--stack-name StreamingNumbers \ +--capabilities CAPABILITY_IAM +``` + +The URL of the function is provided as part of the output. + +``` +CloudFormation outputs from deployed stack +----------------------------------------------------------------------------------------------------------------------------- +Outputs +----------------------------------------------------------------------------------------------------------------------------- +Key LambdaURL +Description Lambda URL +Value https://gaudpin2zjqizfujfnqxstnv6u0czrfu.lambda-url.us-east-1.on.aws/ +----------------------------------------------------------------------------------------------------------------------------- +``` + +Once the function is deployed, you can invoke it with `curl`, similarly to what you did when deploying with the AWS CLI. + +```bash +curl "$URL" \ + --user "$ACCESS_KEY":"$SECRET_KEY" \ + --aws-sigv4 "aws:amz:$REGION:lambda" \ + -H "x-amz-security-token: $AWS_SESSION_TOKEN" \ + --no-buffer +``` + +### Undeploy with SAM + +When done testing, you can delete the infrastructure with this command. + +```bash +sam delete +``` \ No newline at end of file diff --git a/Examples/Streaming/Sources/main.swift b/Examples/Streaming/Sources/main.swift new file mode 100644 index 00000000..51a1091a --- /dev/null +++ b/Examples/Streaming/Sources/main.swift @@ -0,0 +1,22 @@ +import AWSLambdaRuntime +import NIOCore + +struct SendNumbersWithPause: StreamingLambdaHandler { + func handle( + _ event: ByteBuffer, + responseWriter: some LambdaResponseStreamWriter, + context: LambdaContext + ) async throws { + for i in 1...10 { + // Send partial data + try await responseWriter.write(ByteBuffer(string: "\(i)\n")) + // Perform some long asynchronous work + try await Task.sleep(for: .milliseconds(1000)) + } + // All data has been sent. Close off the response stream. + try await responseWriter.finish() + } +} + +let runtime = LambdaRuntime.init(handler: SendNumbersWithPause()) +try await runtime.run() diff --git a/Examples/Streaming/format.sh b/Examples/Streaming/format.sh new file mode 100755 index 00000000..706669c3 --- /dev/null +++ b/Examples/Streaming/format.sh @@ -0,0 +1,45 @@ +#!/bin/bash +## ===----------------------------------------------------------------------===## +## +## This source file is part of the Swift.org open source project +## +## Copyright (c) 2024 Apple Inc. and the Swift project authors +## Licensed under Apache License v2.0 with Runtime Library Exception +## +## See https://swift.org/LICENSE.txt for license information +## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +## +## ===----------------------------------------------------------------------===## + +set -euo pipefail + +log() { printf -- "** %s\n" "$*" >&2; } +error() { printf -- "** ERROR: %s\n" "$*" >&2; } +fatal() { error "$@"; exit 1; } + + +if [[ -f .swiftformatignore ]]; then + log "Found swiftformatignore file..." + + log "Running swift format format..." + tr '\n' '\0' < .swiftformatignore| xargs -0 -I% printf '":(exclude)%" '| xargs git ls-files -z '*.swift' | xargs -0 swift format format --parallel --in-place + + log "Running swift format lint..." + + tr '\n' '\0' < .swiftformatignore | xargs -0 -I% printf '":(exclude)%" '| xargs git ls-files -z '*.swift' | xargs -0 swift format lint --strict --parallel +else + log "Running swift format format..." + git ls-files -z '*.swift' | xargs -0 swift format format --parallel --in-place + + log "Running swift format lint..." + + git ls-files -z '*.swift' | xargs -0 swift format lint --strict --parallel +fi + + + +log "Checking for modified files..." + +GIT_PAGER='' git diff --exit-code '*.swift' + +log "✅ Found no formatting issues." diff --git a/Examples/Streaming/template.yaml b/Examples/Streaming/template.yaml new file mode 100644 index 00000000..282ca625 --- /dev/null +++ b/Examples/Streaming/template.yaml @@ -0,0 +1,25 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: SAM Template for QuoteService + +Resources: + # Lambda function + StreamingNumbers: + Type: AWS::Serverless::Function + Properties: + CodeUri: .build/plugins/AWSLambdaPackager/outputs/AWSLambdaPackager/StreamingNumbers/StreamingNumbers.zip + Timeout: 15 + Handler: swift.bootstrap # ignored by the Swift runtime + Runtime: provided.al2 + MemorySize: 128 + Architectures: + - arm64 + FunctionUrlConfig: + AuthType: AWS_IAM + InvokeMode: RESPONSE_STREAM + +Outputs: + # print Lambda function URL + LambdaURL: + Description: Lambda URL + Value: !GetAtt StreamingNumbersUrl.FunctionUrl \ No newline at end of file diff --git a/readme.md b/readme.md index ca8097e8..b5677063 100644 --- a/readme.md +++ b/readme.md @@ -154,7 +154,40 @@ tbd ### Lambda Streaming Response -tbd + link to docc +You can configure your Lambda function to stream response payloads back to clients. Response streaming can benefit latency sensitive applications by improving time to first byte (TTFB) performance. This is because you can send partial responses back to the client as they become available. Additionally, you can use response streaming to build functions that return larger payloads. Response stream payloads have a soft limit of 20 MB as compared to the 6 MB limit for buffered responses. Streaming a response also means that your function doesn’t need to fit the entire response in memory. For very large responses, this can reduce the amount of memory you need to configure for your function. + +Streaming responses incurs a cost. For more information, see [AWS Lambda Pricing](https://aws.amazon.com/lambda/pricing/). + +You can stream responses through [Lambda function URLs](https://docs.aws.amazon.com/lambda/latest/dg/urls-configuration.html), the AWS SDK, or using the Lambda [InvokeWithResponseStream](https://docs.aws.amazon.com/lambda/latest/dg/API_InvokeWithResponseStream.html) API. In this example, we create an authenticated Lambda function URL. + +Here is an example of a minimal function that streams 10 numbers with an interval of one second for each number. + +```swift +import AWSLambdaRuntime +import NIOCore + +struct SendNumbersWithPause: StreamingLambdaHandler { + func handle( + _ event: ByteBuffer, + responseWriter: some LambdaResponseStreamWriter, + context: LambdaContext + ) async throws { + for i in 1...10 { + // Send partial data + try await responseWriter.write(ByteBuffer(string: "\(i)\n")) + // Perform some long asynchronous work + try await Task.sleep(for: .milliseconds(1000)) + } + // All data has been sent. Close off the response stream. + try await responseWriter.finish() + } +} + +let runtime = LambdaRuntime.init(handler: SendNumbersWithPause()) +try await runtime.run() +``` + +You can learn how to deploy and invoke this function in [the example README file](Examples/Streaming/README.md). ### Integration with Swift Service LifeCycle From 6e0975d343d3bdab0019ee16ca925cde23bcdcd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 22 Oct 2024 11:09:20 +0200 Subject: [PATCH 02/10] add streaming example to CI --- .github/workflows/pull_request.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 5e9a8fc5..7e5aabf3 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -35,7 +35,7 @@ jobs: # We pass the list of examples here, but we can't pass an array as argument # Instead, we pass a String with a valid JSON array. # The workaround is mentioned here https://github.com/orgs/community/discussions/11692 - examples: "[ 'HelloWorld', 'APIGateway','S3_AWSSDK', 'S3_Soto' ]" + examples: "[ 'HelloWorld', 'APIGateway','S3_AWSSDK', 'S3_Soto', 'Streaming' ]" archive_plugin_enabled: true From 0bd77ea8bd1b063b20577763402b26018f7bf1ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 22 Oct 2024 11:13:18 +0200 Subject: [PATCH 03/10] fix soundness --- Examples/Streaming/Sources/main.swift | 14 ++++++++++++++ Examples/Streaming/template.yaml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Examples/Streaming/Sources/main.swift b/Examples/Streaming/Sources/main.swift index 51a1091a..ce92560c 100644 --- a/Examples/Streaming/Sources/main.swift +++ b/Examples/Streaming/Sources/main.swift @@ -1,3 +1,17 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the SwiftAWSLambdaRuntime open source project +// +// Copyright (c) 2024 Apple Inc. and the SwiftAWSLambdaRuntime project authors +// Licensed under Apache License v2.0 +// +// See LICENSE.txt for license information +// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors +// +// SPDX-License-Identifier: Apache-2.0 +// +//===----------------------------------------------------------------------===// + import AWSLambdaRuntime import NIOCore diff --git a/Examples/Streaming/template.yaml b/Examples/Streaming/template.yaml index 282ca625..0621a031 100644 --- a/Examples/Streaming/template.yaml +++ b/Examples/Streaming/template.yaml @@ -22,4 +22,4 @@ Outputs: # print Lambda function URL LambdaURL: Description: Lambda URL - Value: !GetAtt StreamingNumbersUrl.FunctionUrl \ No newline at end of file + Value: !GetAtt StreamingNumbersUrl.FunctionUrl From 327595255b3dc90d19d78942de79d5ab55204873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Tue, 22 Oct 2024 11:17:16 +0200 Subject: [PATCH 04/10] remove unused files --- Examples/Streaming/format.sh | 45 ------------------------------------ 1 file changed, 45 deletions(-) delete mode 100755 Examples/Streaming/format.sh diff --git a/Examples/Streaming/format.sh b/Examples/Streaming/format.sh deleted file mode 100755 index 706669c3..00000000 --- a/Examples/Streaming/format.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/bin/bash -## ===----------------------------------------------------------------------===## -## -## This source file is part of the Swift.org open source project -## -## Copyright (c) 2024 Apple Inc. and the Swift project authors -## Licensed under Apache License v2.0 with Runtime Library Exception -## -## See https://swift.org/LICENSE.txt for license information -## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors -## -## ===----------------------------------------------------------------------===## - -set -euo pipefail - -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { error "$@"; exit 1; } - - -if [[ -f .swiftformatignore ]]; then - log "Found swiftformatignore file..." - - log "Running swift format format..." - tr '\n' '\0' < .swiftformatignore| xargs -0 -I% printf '":(exclude)%" '| xargs git ls-files -z '*.swift' | xargs -0 swift format format --parallel --in-place - - log "Running swift format lint..." - - tr '\n' '\0' < .swiftformatignore | xargs -0 -I% printf '":(exclude)%" '| xargs git ls-files -z '*.swift' | xargs -0 swift format lint --strict --parallel -else - log "Running swift format format..." - git ls-files -z '*.swift' | xargs -0 swift format format --parallel --in-place - - log "Running swift format lint..." - - git ls-files -z '*.swift' | xargs -0 swift format lint --strict --parallel -fi - - - -log "Checking for modified files..." - -GIT_PAGER='' git diff --exit-code '*.swift' - -log "✅ Found no formatting issues." From ea3bcf619488f11ed50b492f72d03494d3426714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Sat, 26 Oct 2024 13:35:43 +0200 Subject: [PATCH 05/10] correct usage of zsh env var --- Examples/Streaming/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Streaming/README.md b/Examples/Streaming/README.md index a74c640e..4877df8b 100644 --- a/Examples/Streaming/README.md +++ b/Examples/Streaming/README.md @@ -47,7 +47,7 @@ aws lambda create-function \ --runtime provided.al2 \ --handler provided \ --architectures arm64 \ ---role arn:aws:iam::$AWS_ACCOUNT_ID:role/lambda_basic_execution \ +--role arn:aws:iam::${AWS_ACCOUNT_ID}:role/lambda_basic_execution \ --timeout 15 ``` From 2bff0a95741484849febc4f2df4752333ec407e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Thu, 7 Nov 2024 11:35:48 +0100 Subject: [PATCH 06/10] typo Co-authored-by: Tim Condon <0xTim@users.noreply.github.com> --- Examples/Streaming/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Streaming/template.yaml b/Examples/Streaming/template.yaml index 0621a031..2cc72839 100644 --- a/Examples/Streaming/template.yaml +++ b/Examples/Streaming/template.yaml @@ -1,6 +1,6 @@ AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 -Description: SAM Template for QuoteService +Description: SAM Template for Streaming Example Resources: # Lambda function From b7865a8d1c0091cbd14d27661258220978f56b22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Thu, 7 Nov 2024 11:36:29 +0100 Subject: [PATCH 07/10] typo Co-authored-by: Tim Condon <0xTim@users.noreply.github.com> --- Examples/Streaming/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Streaming/README.md b/Examples/Streaming/README.md index 4877df8b..2cfa82cb 100644 --- a/Examples/Streaming/README.md +++ b/Examples/Streaming/README.md @@ -52,7 +52,7 @@ aws lambda create-function \ ``` > [!IMPORTANT] -> The timeout value must be bigger than the time it takes for your function to stream its output. Otherwise, the Lambda control plane will terminate the execution environment before your code has a chance to finish writing the stream. Here, teh sample function stream responses during 10 seconds and we set the timeout for 15 seconds. +> The timeout value must be bigger than the time it takes for your function to stream its output. Otherwise, the Lambda control plane will terminate the execution environment before your code has a chance to finish writing the stream. Here, the sample function stream responses during 10 seconds and we set the timeout for 15 seconds. The `--architectures` flag is only required when you build the binary on an Apple Silicon machine (Apple M1 or more recent). It defaults to `x64`. From 7128b1c1fdc7bf9b5cff559e8a8a9aea8d68ff04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Thu, 7 Nov 2024 11:36:42 +0100 Subject: [PATCH 08/10] add comments Co-authored-by: Tim Condon <0xTim@users.noreply.github.com> --- Examples/Streaming/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Examples/Streaming/README.md b/Examples/Streaming/README.md index 2cfa82cb..efbdbb8d 100644 --- a/Examples/Streaming/README.md +++ b/Examples/Streaming/README.md @@ -39,7 +39,9 @@ The ZIP file is located at `.build/plugins/AWSLambdaPackager/outputs/AWSLambdaPa Here is how to deploy using the `aws` command line. ### Step 1: Create the function + ```bash +# Replace with your AWS Account ID AWS_ACCOUNT_ID=012345678901 aws lambda create-function \ --function-name StreamingNumbers \ From d3890ae963d8c92fe116ffe9572bf475e5902978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Thu, 7 Nov 2024 11:37:08 +0100 Subject: [PATCH 09/10] typo Co-authored-by: Tim Condon <0xTim@users.noreply.github.com> --- Examples/Streaming/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Examples/Streaming/README.md b/Examples/Streaming/README.md index efbdbb8d..8390e0f7 100644 --- a/Examples/Streaming/README.md +++ b/Examples/Streaming/README.md @@ -77,7 +77,7 @@ Be sure to replace with your actual AWS account ID (for exampl ### Step3: Create the URL -This creates [an URL with IAM authentication](https://docs.aws.amazon.com/lambda/latest/dg/urls-auth.html). Only calls with a valid signature will be authorized. +This creates [a URL with IAM authentication](https://docs.aws.amazon.com/lambda/latest/dg/urls-auth.html). Only calls with a valid signature will be authorized. ```bash aws lambda create-function-url-config \ From c95f63599fac9941c01b372b5529003927cb6f90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Stormacq?= Date: Thu, 7 Nov 2024 14:03:57 +0100 Subject: [PATCH 10/10] add an example README with common sections + link to each example --- Examples/README.md | 57 ++++++++++++++++++++++++++++++++++++ Examples/Streaming/README.md | 19 +++++++----- 2 files changed, 68 insertions(+), 8 deletions(-) create mode 100644 Examples/README.md diff --git a/Examples/README.md b/Examples/README.md new file mode 100644 index 00000000..4e597305 --- /dev/null +++ b/Examples/README.md @@ -0,0 +1,57 @@ +This directory contains example code for Lambda functions. + +## Pre-requisites + +- Ensure you have the Swift 6.x toolchain installed. You can [install Swift toolchains](https://www.swift.org/install/macos/) from Swift.org + +- When developing on macOS, be sure you use macOS 15 (Sequoia) or a more recent macOS version. + +- To build and archive your Lambda functions, you need to [install docker](https://docs.docker.com/desktop/install/mac-install/). + +- To deploy your Lambda functions and invoke them, you must have [an AWS account](https://docs.aws.amazon.com/accounts/latest/reference/manage-acct-creating.html) and [install and configure the `aws` command line](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). + +- Some examples are using [AWS SAM](https://aws.amazon.com/serverless/sam/). Install the [SAM CLI](https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/install-sam-cli.html) before deploying these examples. + +## Examples + +- **[API Gateway](APIGateway/README.md)**: an HTTPS REST API with [Amazon API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/welcome.html) and a Lambda function as backend (requires [AWS SAM](https://aws.amazon.com/serverless/sam/)). + +- **[BackgroundTasks](BackgroundTasks/README.md)**: a Lambda function that continues to run background tasks after having sent the response (requires [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)). + +- **[HelloWorld](HelloWorld/README.md)**: a simple Lambda function (requires [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html)). + +- **[S3_AWSSDK](S3_AWSSDK/README.md)**: a Lambda function that uses the [AWS SDK for Swift](https://docs.aws.amazon.com/sdk-for-swift/latest/developer-guide/getting-started.html) to invoke an [Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) API (requires [AWS SAM](https://aws.amazon.com/serverless/sam/)). + +- **[S3_Soto](S3_Soto/README.md)**: a Lambda function that uses [Soto](https://github.com/soto-project/soto) to invoke an [Amazon S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/Welcome.html) API (requires [AWS SAM](https://aws.amazon.com/serverless/sam/)). + +- **[Streaming]**: create a Lambda function exposed as an URL. The Lambda function streams its response over time. (requires [AWS SAM](https://aws.amazon.com/serverless/sam/)). + +## AWS Credentials and Signature + +This section is a short tutorial on the AWS Signature protocol and the AWS credentials. + +**What is AWS SigV4?** + +AWS SigV4, short for "Signature Version 4," is a protocol AWS uses to authenticate and secure requests. When you, as a developer, send a request to an AWS service, AWS SigV4 makes sure the request is verified and hasn’t been tampered with. This is done through a digital signature, which is created by combining your request details with your secret AWS credentials. This signature tells AWS that the request is genuine and is coming from a user who has the right permissions. + +**How to Obtain AWS Access Keys and Session Tokens** + +To start making authenticated requests with AWS SigV4, you’ll need three main pieces of information: + +1. **Access Key ID**: This is a unique identifier for your AWS account, IAM (Identity and Access Management) user, or federated user. + +2. **Secret Access Key**: This is a secret code that only you and AWS know. It works together with your access key ID to sign requests. + +3. **Session Token (Optional)**: If you're using temporary security credentials, AWS will also provide a session token. This is usually required if you're using temporary access (e.g., through AWS STS, which provides short-lived, temporary credentials, or for federated users). + +To obtain these keys, you need an AWS account: + +1. **Sign up or Log in to AWS Console**: Go to the [AWS Management Console](https://aws.amazon.com/console/), log in, or create an AWS account if you don’t have one. + +2. **Create IAM User**: In the console, go to IAM (Identity and Access Management) and create a new user. Ensure you set permissions that match what the user will need for your application (e.g., permissions to access specific AWS services, such as AWS Lambda). + +3. **Generate Access Key and Secret Access Key**: In the IAM user settings, find the option to generate an "Access Key" and "Secret Access Key." Save these securely! You’ll need them to authenticate your requests. + +4. **(Optional) Generate Temporary Security Credentials**: If you’re using temporary credentials (which are more secure for short-term access), use AWS Security Token Service (STS). You can call the `GetSessionToken` or `AssumeRole` API to generate temporary credentials, including a session token. + +With these in hand, you can use AWS SigV4 to securely sign your requests and interact with AWS services from your Swift app. \ No newline at end of file diff --git a/Examples/Streaming/README.md b/Examples/Streaming/README.md index 8390e0f7..86a42754 100644 --- a/Examples/Streaming/README.md +++ b/Examples/Streaming/README.md @@ -68,13 +68,11 @@ Anyone with a valid signature from your AWS account will have permission to invo aws lambda add-permission \ --function-name StreamingNumbers \ --action lambda:InvokeFunctionUrl \ - --principal $AWS_ACCOUNT_ID \ + --principal ${AWS_ACCOUNT_ID} \ --function-url-auth-type AWS_IAM \ --statement-id allowURL ``` -Be sure to replace with your actual AWS account ID (for example: 012345678901). - ### Step3: Create the URL This creates [a URL with IAM authentication](https://docs.aws.amazon.com/lambda/latest/dg/urls-auth.html). Only calls with a valid signature will be authorized. @@ -100,6 +98,11 @@ This calls return various information, including the URL to invoke your function ### Invoke your Lambda function To invoke the Lambda function, use `curl` with the AWS Sigv4 option to generate the signature. + +Read the [AWS Credentials and Signature](../README.md/#AWS-Credentials-and-Signature) section for more details about the AWS Sigv4 protocol and how to obtain AWS credentials. + +When you have the `aws` command line installed and configured, you will find the credentials in the `~/.aws/credentials` file. + ```bash URL=https://ul3nf4dogmgyr7ffl5r5rs22640fwocc.lambda-url.us-east-1.on.aws/ REGION=us-east-1 @@ -108,9 +111,9 @@ SECRET_KEY=... AWS_SESSION_TOKEN=... curl "$URL" \ - --user "$ACCESS_KEY":"$SECRET_KEY" \ - --aws-sigv4 "aws:amz:$REGION:lambda" \ - -H "x-amz-security-token: $AWS_SESSION_TOKEN" \ + --user "${ACCESS_KEY}":"${SECRET_KEY}" \ + --aws-sigv4 "aws:amz:${REGION}:lambda" \ + -H "x-amz-security-token: ${AWS_SESSION_TOKEN}" \ --no-buffer ``` @@ -205,7 +208,7 @@ Once the function is deployed, you can invoke it with `curl`, similarly to what ```bash curl "$URL" \ --user "$ACCESS_KEY":"$SECRET_KEY" \ - --aws-sigv4 "aws:amz:$REGION:lambda" \ + --aws-sigv4 "aws:amz:${REGION}:lambda" \ -H "x-amz-security-token: $AWS_SESSION_TOKEN" \ --no-buffer ``` @@ -216,4 +219,4 @@ When done testing, you can delete the infrastructure with this command. ```bash sam delete -``` \ No newline at end of file +```