Commit Graph

457 Commits

Author SHA1 Message Date
Sebastien Stormacq 3cbc9f9310 Merge branch 'main' into sebsto/new-plugins 2026-02-17 17:09:06 +01:00
Sebastien Stormacq f3fbfb4891 Merge branch 'main' into sebsto/new-plugins 2026-02-17 17:08:25 +01:00
Sebastien Stormacq 37a30a4a1b Update Plugin Spec doc to add Apple container 2026-02-17 17:07:20 +01:00
Sébastien Stormacq 11ef057299 Fix more crashes in archive (#642)
## Issue #
Related to intermittent CI crashes with Signal 11 (SIGSEGV) during
`swift package archive` on Linux x86_64 (Ubuntu 24.04)

## Description of the changes
Replaces `FileHandle.readabilityHandler` with a manual blocking read
loop using `availableData` on a serial `DispatchQueue` in the
`Utils.execute` method of the AWSLambdaPackager plugin.

On Linux, `FileHandle.readabilityHandler`'s setter internally calls
`_bridgeAnythingToObjectiveC`, which triggers `swift_dynamicCast` /
`swift_conformsToProtocol`. When the Swift runtime is concurrently
resolving type metadata on other threads, this causes a bad pointer
dereference crash in `_dispatch_event_loop_drain`. The new approach uses
a `while` loop calling `availableData` (blocks until data arrives,
returns empty `Data` at EOF), completely avoiding the problematic
Foundation/ObjC bridging code paths.

## New/existing dependencies impact assessment, if applicable
No new dependencies were added to this change.

## Conventional Commits
`fix: replace FileHandle.readabilityHandler with manual read loop in
AWSLambdaPackager plugin`

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2026-02-17 08:52:43 +01:00
Sébastien Stormacq 4a917a74de Fix crash on Linux when testing the archive plugin (#641)
Fix a rare crash in CI on the archive plugin.

The crash is a race condition in `PluginUtils.swift`'s execute method.
The code set a `terminationHandler` on the `Process` that called
`readToEnd()` on the pipe, while simultaneously the `readabilityHandler`
could still be firing on the same pipe's file handle. On Linux (x86_64,
Ubuntu 24.04 in CI), this race corrupts memory during Swift runtime
metadata resolution (`swift_conformsToProtocol,
_swift_getGenericMetadata`), which manifests as the `SIGSEGV` we're
seeing in `_dispatch_event_loop_drain`.

The fix:

I removed the `terminationHandler` entirely. Since `waitUntilExit()` is
already called synchronously, we know the process is done.
After `waitUntilExit()`, we set `readabilityHandler = nil` to stop the
async reads, then do one final `readToEnd()` on the output queue to
drain any remaining data.
This eliminates the race between the readability handler and the
termination handler competing over the same file handle.

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2.6.2
2026-02-16 12:18:11 +01:00
Sébastien Stormacq fef8c0d130 Fix for swift test : _Concurrency/Executor.swift:357: Fatal error (#639)
Fix the issue described at
https://github.com/awslabs/swift-aws-lambda-runtime/issues/640

Here is the proposed fix:

I added a new function `assumeIsolatedOnEventLoop` — a nonisolated
method that:
1. Calls `self.eventLoop.preconditionInEventLoop()` to verify we're on
the correct event loop (NIO's own thread-identity check, which always
works)
2. Uses `unsafeBitCast` to strip the isolated annotation, the same
pattern NIO uses internally and that I found on the Swift Forums.

See:
https://github.com/swiftlang/swift/blob/main/stdlib/public/Concurrency/ExecutorAssertions.swift#L348

See:
https://forums.swift.org/t/actor-assumeisolated-erroneously-crashes-when-using-a-dispatch-queue-as-the-underlying-executor/72434/3

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-02-14 13:05:33 +01:00
Sébastien Stormacq a8a54d9d98 Add docc for Lambda Managed Instances (#637)
Add a docc page to cover Lambda Managed instanced added at version 2.6.0

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2.6.1
2026-02-12 15:32:17 +01:00
Sébastien Stormacq 190eb81876 Add support for Lambda Managed Instances without changing the public API [Convenience + Example] (#623)
This PR builds on
https://github.com/awslabs/swift-aws-lambda-runtime/pull/629 to add
convenience structs (Handlers and Adapters) that are `Sendable`

**Changes**

- **Added Sendable adapter types**: Implemented `ClosureHandlerSendable`
- a thread-safe version of existing closure handler that enforces
`Sendable` conformance for concurrent execution environments - and added
conditional conformance to `Sendable` for other Adapters when the
Handler is `Sendable`

- **Enhanced handler protocols for concurrency**: Extended handler
protocols to support `Sendable` constraints and concurrent response
writing through `LambdaResponseStreamWriter & Sendable`, enabling safe
multi-threaded invocation processing

- **Created comprehensive Lambda Managed Instances examples**: Built
three demonstration functions showcasing concurrent execution
capabilities, streaming responses, and background processing patterns
specific to the new managed instances deployment model

**Context**
Lambda Managed Instances support multi-concurrent invocations where
multiple invocations execute simultaneously within the same execution
environment. The runtime now detects the configured concurrency level
and launches the appropriate number of RICs to handle concurrent
requests efficiently.

When `AWS_LAMBDA_MAX_CONCURRENCY` is 1 or unset, the runtime maintains
the existing single-threaded behaviour for optimal performance on
traditional Lambda deployments.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2.6.0
2026-02-12 00:32:31 +01:00
Sébastien Stormacq d456396581 Add support for Lambda Managed Instances without changing the public API [CORE] (#629)
AWS launched [Lambda Managed
Instances](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances.html),
i.e Lambda functions running on EC2 instances.

This comes with [a major change in the programming
model](https://docs.aws.amazon.com/lambda/latest/dg/lambda-managed-instances.html#lambda-managed-instances-concurrency-model)
as function handlers are now allowed to run concurrently on the same
machine (multiple in flight events being processed in parallel in the
same execution environment). The maximum concurrency per runtime
environment is controlled by the user.

This PR adds support for running multiple Runtime Interface Clients
(RICs) concurrently when deployed on Lambda Managed Instances, enabling
the runtime to handle multiple invocations simultaneously within a
single execution environment.

This PR is a followup to
https://github.com/awslabs/swift-aws-lambda-runtime/pull/617 which used
another approach to support Lambda Managed Instances by changing the
public API and requiring that all handlers must conform to `Sendable`.
The original PR was closed as we agreed that only a fraction of the
Lambda functions will be deployed on EC2 and it was not worth adding a
`Sendable` requirement for all.

**Changes**

- **Introduced thread-safe LambdaManagedRuntime**: Created new
Sendable-conforming runtime class that supports concurrent handler
execution with atomic guards to prevent multiple runtime instances and
thread-safe handler requirements (`Handler: StreamingLambdaHandler &
Sendable`)

- **Implemented ServiceLifecycle integration**: Added managed runtime
support for structured concurrency lifecycle management, allowing proper
startup/shutdown coordination in multi-concurrent environments

This PR contains only changes to the core runtime, convenience
functions, handlers, adapters, and a comprehensive example will be added
in a follow up PR.

**Context**
Lambda Managed Instances support multi-concurrent invocations where
multiple invocations execute simultaneously within the same execution
environment. The runtime now detects the configured concurrency level
and launches the appropriate number of RICs to handle concurrent
requests efficiently.

When `AWS_LAMBDA_MAX_CONCURRENCY` is 1 or unset, the runtime maintains
the existing single-threaded behaviour for optimal performance on
traditional Lambda deployments.

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2026-02-12 00:17:04 +01:00
Sebastien Stormacq ba48cf70b0 Add source terminal image in tutorial 2026-02-08 06:30:56 +01:00
Sébastien Stormacq 24d4022a24 Merge branch 'main' into sebsto/new-plugins 2026-01-27 10:12:14 +01:00
Sébastien Stormacq 4815273dc3 Fix race condition in Lambda+LocalServer causing NIOAsyncWriter fatal error (Bug #635) (#636)
On fast machines, the local Lambda server crashes with:
```
Fatal error: Deinited NIOAsyncWriter without calling finish()
```

This occurs in `NIOAsyncChannelHandler.channelActive()` when child
connection channels are created.

## Root Cause

This is a known issue with NIO's async server channel API (see
[swift-nio#2637](https://github.com/apple/swift-nio/issues/2637)).

**The fundamental problem:**

1. The async `bind()` API creates `NIOAsyncChannel` instances for
incoming connections
2. These channels are yielded through an async stream to the server loop
3. When the serving task is cancelled (or completes), the async stream
iteration stops
4. Any channels that were accepted but not yet read from the stream are
dropped
5. These unread channels never have `executeThenClose()` called on them
6. Their `NIOAsyncWriter` is deallocated without `finish()` being called
→ fatal error

**Why graceful shutdown doesn't help:**

Even closing the server channel gracefully doesn't eliminate the race -
there's a timing window where:
- A connection is accepted and queued in the async stream
- The server task is cancelled or completes
- The queued channel is never read and gets dropped

IMHO, this is an inherent limitation of the `async bind()` API when
combined with task cancellation.

## Solution

I stopped using the `async bind()` API entirely. Instead, I use the
traditional callback-based `childChannelInitializer`:

1. Create `NIOAsyncChannel` directly in `childChannelInitializer`
(synchronous context)
2. Immediately spawn a `Task.detached` to handle the connection
3. Each connection is handled independently, not through a cancellable
async stream
4. Detached tasks are not affected by task group cancellation
5. Every channel has `executeThenClose()` called immediately, preventing
the writer from being dropped

This approach avoids the async stream entirely, eliminating the race
condition.

## Changes

- Replaced `async bind()` with traditional `childChannelInitializer`
- Each connection spawns a `Task.detached` that immediately calls
`executeThenClose()`
- Removed the connection iteration loop (no longer needed)
- Server task now simply waits for the channel to close
- Simplified shutdown logic since there's no async stream to drain

## Trade-offs

- Uses `Task.detached` (unstructured concurrency) to bridge NIO's
event-loop world with Swift concurrency
- This is necessary until NIO provides a new bootstrap API that properly
handles cancellation
- Each connection is handled independently rather than through
structured concurrency

## Testing

Tested on fast machines where the race condition was reliably
reproducible. The crash no longer occurs.

## References

- [swift-nio#2637](https://github.com/apple/swift-nio/issues/2637) -
Known issue with async server channels and cancellation
- [Comment from NIO
maintainer](https://github.com/apple/swift-nio/issues/2637#issuecomment-1921317577)
- Recommends avoiding cancellation or using callback-based API

Fixes #635

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2.5.3
2026-01-27 09:10:58 +00:00
Sébastien Stormacq 102f92aafb Fix race condition crash in LambdaRuntimeClient channel lifecycle (Bug #624) (#632)
This PR fixes a race condition in `LambdaRuntimeClient` that causes a
fatal crash when an old channel's `closeFuture` callback fires after a
new connection has been established. The fix adds proper channel
lifecycle tracking and replaces the fatal error with graceful handling.

## Problem

**Crash Location**: `LambdaRuntimeClient.swift:270` in `channelClosed()`

**Error Message**:
```
Fatal error: Invalid state: connected(SocketChannel { ... }), closed
```

**Root Cause**: Race condition where:
1. An old channel's `closeFuture` callback fires
2. AFTER a new connection has been established (`connectionState =
.connected`)
3. BUT `closingState` is `.closed` from a previous close operation
4. The code asserted this was impossible and crashed with `fatalError`

This can occur when:
- Network conditions cause delayed channel cleanup
- Connection is recycled quickly (old channel still closing while new
one connects)
- Timing issues between channel close callbacks and new connection
establishment

## Solution

### Key Changes

1. **Added channel identity tracking**:
   ```swift
   private var channelsBeingClosed: Set<ObjectIdentifier> = []
   ```
Tracks which channels are in the process of closing to distinguish old
channels from the current one.

2. **Enhanced `connectionWillClose()`**:
   - Marks channels as "being closed" using `ObjectIdentifier`
   - Adds logging when old channels close while new connection is active

3. **Rewrote `channelClosed()` with defensive logic**:
- **Early return for tracked old channels**: Handles them gracefully
without affecting current connection
- **Replaced `fatalError` with warning log**: The `(_, .closed)` case
now logs a warning instead of crashing
- **Channel identity checks**: Only transitions state if the closing
channel is the CURRENT channel
- **Removed unconditional state change**: Previously set
`connectionState = .disconnected` for ANY channel close, now only for
the current channel

### Why This Fixes the Bug

The fix addresses the race condition by:
- Distinguishing between "current channel closing" vs "old channel
closing"
- Handling old channel closes gracefully without crashing or corrupting
state
- Not overwriting connection state when old channels close
- Providing visibility through logging when the race condition occurs

## Changes

### Modified Files

- **Sources/AWSLambdaRuntime/HTTPClient/LambdaRuntimeClient.swift**
  - Added `channelsBeingClosed: Set<ObjectIdentifier>` property
  - Enhanced `connectionWillClose()` with channel tracking
  - Rewrote `channelClosed()` with defensive logic and identity checks
  - Replaced `fatalError` with warning log for unexpected states
  - Removed unconditional state change in `closeFuture` callback

**Lines Changed**: ~150 lines modified/added

**Backward Compatibility**:  Fully compatible, no API changes

## Testing

###  All Existing Tests Pass

```bash
swift test
# Result: 91 tests passed in 14 suites
```

All original functionality is preserved with no regressions.

### ⚠️ Note on Test Coverage

While we cannot reproduce the exact race condition from bug #624 in a
deterministic test (it requires specific network timing), the fix:
- Is logically sound for the described race condition
- Improves defensive programming around channel lifecycle
- Replaces a fatal crash with graceful handling + logging
- Should prevent the crash by properly tracking channel identity

## Related Issues

Fixes #624

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2026-01-27 08:53:31 +00:00
Sébastien Stormacq 34e89b4027 Fix Test hangs in Lambda+LocalServer (#630) (#631)
# Fix test hangs caused by Pool cancellation race conditions

## Summary

This PR fixes two related race conditions in
`Lambda+LocalServer+Pool.swift` that were causing the test suite to hang
approximately 10% of the time.

## Problem

The test suite exhibited intermittent hangs (~10% frequency) due to two
bugs in the Pool implementation:

1. **Individual task cancellation bug**: When one task waiting for a
specific `requestId` was cancelled, the cancellation handler would
incorrectly cancel ALL waiting tasks instead of just the cancelled one.

2. **Server shutdown hang**: When the server shut down, waiting
continuations in the pools were never cancelled, causing handlers to
wait indefinitely for responses that would never arrive.

## Root Causes

### Root Cause #1: Cancellation Handler Removes ALL Continuations

The `onCancel` handler in `Pool._next()` was removing all continuations
from the `waitingForSpecific` dictionary when any single task was
cancelled:

```swift
onCancel: {
    // BUG: Removes ALL continuations, not just the cancelled task's
    for continuation in state.waitingForSpecific.values {
        toCancel.append(continuation)
    }
    state.waitingForSpecific.removeAll()
}
```

This caused unrelated concurrent invocations to fail with
`CancellationError` when one client cancelled their request.

### Root Cause #2: No Pool Cleanup During Server Shutdown

When the server shut down (e.g., test completes), the task group was
cancelled but the pools' waiting continuations were never notified. The
`/invoke` endpoint handlers would continue waiting for responses that
would never arrive because the Lambda function had stopped.

## Solution

### Fix #1: Only Remove Specific Continuation on Cancellation

Modified the cancellation handler to only remove the continuation for
the specific cancelled task:

```swift
onCancel: {
    // Only remove THIS task's continuation
    let continuationToCancel = self.lock.withLock { state -> CheckedContinuation<T, any Error>? in
        if let requestId = requestId {
            return state.waitingForSpecific.removeValue(forKey: requestId)
        } else {
            let cont = state.waitingForAny
            state.waitingForAny = nil
            return cont
        }
    }
    
    continuationToCancel?.resume(throwing: CancellationError())
}
```

### Fix #2: Add Pool Cleanup During Server Shutdown

Added `cancelAll()` method to the Pool class and call it during server
shutdown:

```swift
func cancelAll() {
    let continuationsToCancel = self.lock.withLock { state -> [CheckedContinuation<T, any Error>] in
        var toCancel: [CheckedContinuation<T, any Error>] = []
        
        if let continuation = state.waitingForAny {
            toCancel.append(continuation)
            state.waitingForAny = nil
        }
        
        for continuation in state.waitingForSpecific.values {
            toCancel.append(continuation)
        }
        state.waitingForSpecific.removeAll()
        
        return toCancel
    }
    
    for continuation in continuationsToCancel {
        continuation.resume(throwing: CancellationError())
    }
}
```

Called during server shutdown:

```swift
let serverOrHandlerResult1 = await group.next()!
group.cancelAll()

// Cancel all waiting continuations in the pools to prevent hangs
server.invocationPool.cancelAll()
server.responsePool.cancelAll()
```

## Changes

### Modified Files

- **Sources/AWSLambdaRuntime/HTTPServer/Lambda+LocalServer+Pool.swift**
- Fixed cancellation handler in `_next()` to only remove specific
continuation
  - Added `cancelAll()` method for server shutdown cleanup

- **Sources/AWSLambdaRuntime/HTTPServer/Lambda+LocalServer.swift**
  - Call `cancelAll()` on both pools during server shutdown

### New Files

- **Tests/AWSLambdaRuntimeTests/LocalServerPoolCancellationTests.swift**
  - Added comprehensive test suite with 3 tests
- `testCancellationOnlyAffectsOwnTask`: Verifies only the cancelled task
receives CancellationError
- `testConcurrentInvocationsWithCancellation`: Tests real-world scenario
with 5 concurrent invocations
- `testFIFOModeCancellation`: Ensures FIFO mode cancellation works
correctly

## Testing

### Before Fix
- Test suite hung ~10% of the time
- When 1 task was cancelled, all 5 concurrent tasks received
`CancellationError`
- Streaming tests would occasionally hang during shutdown

### After Fix
- All 91 tests pass consistently without hangs
- When 1 task is cancelled, only that specific task receives
`CancellationError`
- Other tasks continue waiting normally
- Server shutdown properly cleans up all waiting continuations
- Multiple consecutive test runs confirm stability

### Test Coverage

The new test suite reproduces both bugs and verifies the fixes:

1. **testCancellationOnlyAffectsOwnTask**: Creates 3 tasks waiting for
different requestIds, cancels only one, and verifies the others are not
affected
2. **testConcurrentInvocationsWithCancellation**: Simulates 5 concurrent
invocations with one cancellation
3. **testFIFOModeCancellation**: Tests FIFO mode to ensure it still
works correctly

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2.5.2
2026-01-15 20:58:03 +01:00
Sébastien Stormacq feb6d2cd49 Cleanup script and test example (#628)
Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2026-01-13 11:46:00 +01:00
Sébastien Stormacq 61f392df7a Refactor LambdaRuntime (#627)
refcator to isolate the code in `startRuntimeInterfaceClient` and
`startLocalServer` functions
Also change the name of the global variable `_isRunning` to
`_isLambdaRuntimeRunning`

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2026-01-13 11:31:03 +01:00
Sébastien Stormacq 6d2c5bf91d Rename Lambda+JSON file (#626)
Rename Lambda+JSON file to LambdaRuntime+JSON, to prepare for furture
support of Lambda Managed Runtime

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2026-01-13 11:11:21 +01:00
Sébastien Stormacq 634c36e5d3 Lift up the trace limit on payload to make it actually useful when debugging (#625)
When using `trace` log level the runtime diplays 1Kb of the input
payload to help debugging cases where the JSON decoding fails. Most of
the type, this 1 Kb trace is not enough to understand what part of the
incoming JSON fails to decode.

This PR raises the limit to 6Mb to be actually useful.
2.5.1
2026-01-11 15:58:13 +01:00
Sébastien Stormacq 4b4054d1c4 Remove unnecessary Sendable constraint (#622)
This PR removes `Sendable` requirement on `StreamingClosureHandler `.

While analyzing code for the upcoming support of Lambda Managed Runtime,
I found out that `StreamingClosureHandler` has `Sendable` requirement,
which is not necessary.

Removing requirement doesn't require a major speed bump as the change is
compatible with existing code.
2.5.0
2026-01-01 11:33:12 -05:00
Sébastien Stormacq e0f064a93e Refactor project directories (#621)
This PR refactors the project's directories.
As the number of source files grows, I created subdirectories to
separate the runtime itself, from its HTTP Client (`RuntimeClient`) and
local HTTP Server (`Lambda+LocalServer`).

The new layout looks like this:

```text
Sources
├── AWSLambdaRuntime
│   ├── FoundationSupport
│   │   ├── Context+Foundation.swift
│   │   ├── Lambda+JSON.swift
│   │   └── Vendored
│   │       ├── ByteBuffer-foundation.swift
│   │       └── JSON+ByteBuffer.swift
│   ├── HTTPClient
│   │   ├── ControlPlaneRequest.swift
│   │   ├── ControlPlaneRequestEncoder.swift
│   │   ├── LambdaRuntimeClient+ChannelHandler.swift
│   │   ├── LambdaRuntimeClient.swift
│   │   └── LambdaRuntimeClientProtocol.swift
│   ├── HTTPServer
│   │   ├── Lambda+LocalServer+Pool.swift
│   │   └── Lambda+LocalServer.swift
│   ├── Lambda.swift
│   ├── LambdaClock.swift
│   ├── LambdaContext.swift
│   ├── LambdaRequestID.swift
│   ├── LambdaResponseStreamWriter+Headers.swift
│   ├── LambdaRuntimeError.swift
│   ├── Runtime
│   │   ├── LambdaHandlers.swift
│   │   ├── LambdaRuntime+Codable.swift
│   │   ├── LambdaRuntime+Handler.swift
│   │   ├── LambdaRuntime+ServiceLifecycle.swift
│   │   └── LambdaRuntime.swift
│   ├── SendableMetatype.swift
│   ├── Utils.swift
│   └── Version.swift
└── MockServer
    └── MockHTTPServer.swift
```
2026-01-01 11:19:13 -05:00
Adam Fowler 1f0888ee7b Fix after release of swift-log 1.8.0 (#619)
<!--- Provide a general summary of your changes in the Title above -->
Fix after release of swift-log 1.8.0
## Issue \#
<!--- If it fixes an issue, please link to the issue here -->

## Description of changes
<!--- Why is this change required? What problem does it solve? -->

## New/existing dependencies impact assessment, if applicable
<!--- No new dependencies were added to this change. -->
<!--- If any dependency was added / modified / removed,
THIRD-PARTY-LICENSES must be updated accordingly. -->

## Conventional Commits
<!--- Please use conventional commits to let us know what kind of change
this is.-->
<!--- More info can be found here:
https://www.conventionalcommits.org/en/v1.0.0/-->

By submitting this pull request, I confirm that my contribution is made
under the terms of the Apache 2.0 license.
2.4.1
2025-12-14 09:49:47 +01:00
Sébastien Stormacq 4494361efb Fix dependency on HB example (#618)
The Hummingbird-Lambda exports the Swift AWS Lambda Runtime. Therefore,
the sample project doesn't need to declare the runtime as a dependency.
2025-12-10 07:58:11 +01:00
Sébastien Stormacq 72865e405a Update commented deps in examples to use runtime v2 (#616)
Fix example dependencies

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-12-05 16:48:19 -08:00
Sébastien Stormacq 3ddd64087d Add Streaming Lambda Examples with API Gateway and Function URL (#615)
## Overview

This PR reorganizes and enhances the streaming Lambda examples by
splitting them into two distinct examples that demonstrate different
invocation methods:

1. **Streaming+FunctionUrl** - Streaming responses via Lambda Function
URLs
2. **Streaming+APIGateway** - Streaming responses via API Gateway REST
API

## Changes

### 🔄 Restructured Examples

- **Renamed**: `Examples/Streaming/` → `Examples/Streaming+FunctionUrl/`
  - Maintains the original streaming example using Lambda Function URLs
  - Updated documentation to clarify Function URL-specific configuration
  - Improved AWS credentials handling in curl examples

- **New**: `Examples/Streaming+APIGateway/`
- Comprehensive example demonstrating API Gateway REST API with response
streaming
- Complete SAM template with proper IAM roles and streaming
configuration
  - Detailed documentation covering API Gateway-specific setup

### 📚 Documentation Improvements

#### Streaming+FunctionUrl
- Clarified that this example uses Lambda Function URLs
- Updated curl examples to use `eval $(aws configure export-credentials
--format env)` for cleaner credential handling
- Maintained all existing functionality and deployment instructions

#### Streaming+APIGateway (New)
- **316-line comprehensive README** covering:
  - Response streaming concepts and benefits
  - HTTP status code and header configuration
  - Streaming response body patterns
  - Local testing instructions
  - Complete SAM deployment guide with detailed template explanation
  - API Gateway-specific invocation with AWS Sigv4 authentication
  - Payload format documentation with example JSON
  - Security and reliability best practices
  - How API Gateway streaming works under the hood

### 🛠️ Technical Details

#### API Gateway Streaming Configuration
The new example demonstrates:
- Special Lambda URI: `/response-streaming-invocations` endpoint
- `responseTransferMode: STREAM` configuration
- IAM role with both `lambda:InvokeFunction` and
`lambda:InvokeWithResponseStream` permissions
- Proper timeout configuration (60s) to accommodate streaming duration

#### SAM Template Features
```yaml
- Lambda function with streaming support (arm64, provided.al2)
- API Gateway REST API with OpenAPI 3.0 definition
- IAM execution role for API Gateway to invoke Lambda with streaming
- Complete outputs for easy testing (API URL and Lambda ARN)
```

### 🔐 Security Enhancements

Both examples now include comprehensive security best practices:
- API Gateway access logging
- Throttling configuration
- AWS WAF integration recommendations
- Lambda concurrent execution limits
- Environment variable encryption
- Dead Letter Queue (DLQ) configuration
- VPC configuration guidance

### 🧪 Testing

Both examples support:
- **Local testing**: `swift run` with curl invocation on port 7000
- **AWS deployment**: Complete SAM templates with deployment
instructions
- **Authenticated invocation**: AWS Sigv4 examples with proper
credential handling

## Benefits

1. **Clearer separation**: Developers can now easily choose between
Function URLs and API Gateway based on their use case
2. **Better documentation**: Each example has tailored documentation for
its specific invocation method
3. **Production-ready**: Includes security best practices and proper IAM
configuration
4. **Easier testing**: Improved credential handling in curl examples

## Breaking Changes

None - this is purely additive. The original streaming example is
preserved as `Streaming+FunctionUrl`.

## Testing Checklist

- [x] Local testing works for both examples
- [x] SAM deployment templates are valid
- [x] Documentation is comprehensive and accurate
- [x] Security best practices are documented
- [x] Curl examples work with proper authentication

## Related Documentation

- [AWS Lambda Response
Streaming](https://docs.aws.amazon.com/lambda/latest/dg/configuration-response-streaming.html)
- [API Gateway Lambda Proxy Integration with
Streaming](https://docs.aws.amazon.com/apigateway/latest/developerguide/response-streaming-lambda-configure.html)
- [Lambda Function
URLs](https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html)
EOF

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-12-05 16:38:50 -08:00
Sébastien Stormacq a590d08818 Use Swiftlang's github action for unit tests (#614)
Remove a dependency on Swift NIO's GitHub workflows and use Swiftlang's
github action for unit tests instead

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-11-30 19:52:23 +01:00
Sébastien Stormacq 9f9434bfa4 Use Swiftlang's GitHUb workflow for testing with Static SDK (#613)
Use Swiftlang's GitHub workflow script to test with the static SDK

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-11-30 19:17:31 +01:00
Sébastien Stormacq 023b8fe526 Simplify local dependency injection for examples (#612)
See issue #536 

All the examples are now depending on the runtime library located at
`../..`. The `Package.swift` files contain a commented line with the
`.package` to use when user wants to fetch the runtime from GitHub.

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-11-30 18:48:06 +01:00
Sébastien Stormacq fb31389f88 Fix 565 : HelloWorld and Resource examples now uses Swift 6.2 (#611)
See issues #565 
HelloWorld and Resources examples project now use Swift 6.2

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-11-30 12:12:34 +01:00
Sébastien Stormacq 437835530a fix #609 : [plugin] Update the README and the doc to mention potential credentials error when using the plugin. (#610)
## Description

Adds documentation for Docker credential store errors that users may
encounter when running `swift package archive
--allow-network-connections docker`.

## Changes

- Added a note in `readme.md` explaining how to resolve Docker
credential store errors
- Added a note in `Sources/AWSLambdaRuntime/Docs.docc/Deployment.md`
with the same guidance

## Problem

Users may encounter authentication errors when the Swift package plugin
attempts to pull Docker images, particularly when Docker is configured
with a credential store (e.g., `docker-credential-desktop`). This issue
is documented in #609.

## Solution

The documentation now provides two workarounds:
1. Remove the `credsStore` entry from `~/.docker/config.json`
2. Use the `--disable-sandbox` flag with the archive command

Both notes link to issue #609 for additional context and discussion.

## Related Issue

Fixes #609
2025-11-28 08:25:16 +01:00
Sébastien Stormacq 2abe7eb7de Add support for Lambda Tenants (#608)
Address https://github.com/awslabs/swift-aws-lambda-runtime/issues/605

NEW Lambda Tenant isolation capability: 
https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation.html 


# Add Support for Lambda Tenant Isolation Mode

## Summary

This PR adds support for AWS Lambda's tenant isolation mode to the Swift
AWS Lambda Runtime, enabling developers to build multi-tenant
applications with strict execution environment isolation per tenant.

## Changes

### Runtime Support
- Added `tenantID` property to `LambdaContext` to expose the tenant
identifier
- Extended `InvocationMetadata` to capture the
`Lambda-Runtime-Aws-Tenant-Id` header
- Added `AmazonHeaders.tenantID` constant for the tenant ID header
- Added trace logging for invocation headers to aid debugging

### New Example: MultiTenant
A complete working example demonstrating tenant isolation mode:
- **Request tracking system** that maintains separate counters and
histories per tenant
- **Actor-based storage** (`TenantDataStore`) for thread-safe tenant
data management
- **Immutable data structures** (`TenantData`) following Swift best
practices
- **API Gateway integration** with tenant ID passed via query parameter
- **SAM template** configured with `TenancyConfig.TenantIsolationMode:
PER_TENANT`
- **Comprehensive documentation** covering architecture, deployment,
testing, and best practices

### Testing
- Added unit test for tenant ID extraction from invocation headers
- Integrated MultiTenant example into CI/CD pipeline

### Documentation
The example includes detailed documentation on:
- When to use tenant isolation (user code execution, sensitive data
processing)
- How tenant isolation works (dedicated environments, no cross-tenant
reuse)
- Concurrency limits and scaling considerations
- Pricing implications
- Security best practices
- CloudWatch monitoring with tenant dimensions

## Files Changed
- `Sources/AWSLambdaRuntime/LambdaContext.swift` - Added tenantID
property
- `Sources/AWSLambdaRuntime/ControlPlaneRequest.swift` - Capture tenant
ID from headers
- `Sources/AWSLambdaRuntime/Utils.swift` - Added tenantID header
constant
- `Sources/AWSLambdaRuntime/Lambda.swift` - Pass tenant ID to context
- `Sources/AWSLambdaRuntime/LambdaRuntimeClient+ChannelHandler.swift` -
Added trace logging
- `Tests/AWSLambdaRuntimeTests/InvocationTests.swift` - Added tenant ID
test
- `Examples/MultiTenant/*` - New complete example with SAM template
- `.github/workflows/pull_request.yml` - Added MultiTenant to CI
pipeline

## Testing Instructions

1. Build and deploy the example:
   bash
  cd Examples/MultiTenant
  swift package archive --allow-network-connections docker
  sam deploy --guided
  

2. Test with different tenants:
   bash
curl
"https://<api-id>.execute-api.<region>.amazonaws.com/Prod?tenant-id=
alice"
curl
"https://<api-id>.execute-api.<region>.amazonaws.com/Prod?tenant-id=
bob"
  


3. Verify isolation by checking that each tenant maintains separate
request counts

## Related Documentation
- [AWS Lambda Tenant
Isolation](https://docs.aws.amazon.com/lambda/latest/dg/tenant-isolation.html)
- [AWS Blog: Streamlined Multi-Tenant Application
Development](https://aws.amazon.com/blogs/aws/streamlined-multi-tenant-application-development-with-tenant-isolation-mode-in-aws-lambda/)

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
Co-authored-by: Tim Condon <0xTim@users.noreply.github.com>
2.4.0
2025-11-21 21:14:15 +01:00
Sébastien Stormacq 0305cb31b8 Add scripts to measure cold start performance (#604)
I added two shell scripts under `scripts/lambda-performance` to help
measure Lambda cold and warm start time.

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-11-19 20:36:51 +01:00
Sébastien Stormacq 85bd29e4de Makes performance script run on macOS (#603)
By default, the script started the MockServer on port 7000, which is
used by AirPlay on macOS.
This caused conflicts.

I also made a minor change on the logging to avoir reporting an error
when the server closes the connection.
2025-11-16 21:02:15 +01:00
Sebastien Stormacq 19b8ff166e Merge branch 'main' into sebsto/new-plugins 2025-11-03 12:19:19 +01:00
Sébastien Stormacq 1b3c81c51f Add Swift ServiceLifecycle section to the readme (#601)
Add Swift ServiceLifecycle section to the readme

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-11-03 12:16:36 +01:00
Sébastien Stormacq 76c272edba Fix broken links (#600)
This PR fixes broken links in the README and add a link to the project
incubation process of the SSWG and to the repo GitHub issues.

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-11-03 11:52:52 +01:00
Sebastien Stormacq 728150cd6f fix 6.0 language mode 2025-11-02 23:03:45 +01:00
Sebastien Stormacq 030a6c0142 fix plugin package 2025-11-02 22:50:21 +01:00
Sebastien Stormacq 71c6a89698 fix unit test errors on nightly-main 2025-11-02 22:35:04 +01:00
Sebastien Stormacq bf73943759 more license header fix 2025-11-02 22:31:41 +01:00
Sebastien Stormacq da47411f65 mor elicense fixes 2025-11-02 22:17:45 +01:00
Sebastien Stormacq 77d29af7a6 more fixes fro shell checks 2025-11-02 22:11:25 +01:00
Sebastien Stormacq 6502638f4a more license header fixes 2025-11-02 22:10:14 +01:00
Sebastien Stormacq 90ff596a56 fix shell check 2025-11-02 22:07:47 +01:00
Sebastien Stormacq 174c967142 fix license headers 2025-11-02 22:05:02 +01:00
Sebastien Stormacq 088ac9bf3a Merge branch 'main' into sebsto/new-plugins 2025-11-02 21:58:48 +01:00
Sébastien Stormacq 97583a78c2 Add Multi-Source API Example (#598)
This PR adds a new example demonstrating how to build a Lambda function
that handles requests from multiple sources (Application Load Balancer
and API Gateway V2) using a single handler.

### What's New

**New Example: `Examples/MultiSourceAPI`**

A Lambda function that:
- Implements `StreamingLambdaHandler` to accept raw `ByteBuffer` events
- Dynamically decodes events as either `ALBTargetGroupRequest` or
`APIGatewayV2Request`
- Returns appropriate responses based on the detected event source
- Demonstrates handling multiple AWS service integrations with a single
function

### Key Features

- **Type-safe event detection**: Uses Swift's `Decodable` to identify
the event source at runtime
- **Streaming response**: Implements `StreamingLambdaHandler` for
efficient response handling
- **Complete deployment**: Includes SAM template with both ALB and API
Gateway V2 infrastructure
- **Production-ready**: Full VPC setup with subnets, security groups,
and load balancer configuration

### Files Added

- `Examples/MultiSourceAPI/Sources/main.swift` - Lambda handler
implementation
- `Examples/MultiSourceAPI/Package.swift` - Swift package configuration
- `Examples/MultiSourceAPI/template.yaml` - SAM deployment template with
ALB and API Gateway V2
- `Examples/MultiSourceAPI/README.md` - Documentation with build,
deploy, and test instructions
- Updated CI to include the new example

### Use Case

This pattern is useful when you want to:
- Expose the same Lambda function through multiple AWS services
- Reduce code duplication by handling similar requests from different
sources
- Maintain a single codebase for multi-channel APIs

---------

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
Co-authored-by: Josh Elkins <jbelkins@users.noreply.github.com>
2025-11-02 21:58:02 +01:00
Sébastien Stormacq 5c7a55500c Fix typo in readme (#599)
Fix typo in TL;DR instructions to setup a new Lambda project.

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-11-02 21:35:56 +01:00
Sebastien Stormacq 781bf3bf9f Merge branch 'main' into sebsto/new-plugins 2025-11-02 21:29:46 +01:00
Sébastien Stormacq f5e534cae2 fix more links (#597)
Fix addition links and replace /swift-server to /awslabs

Co-authored-by: Sebastien Stormacq <stormacq@amazon.lu>
2025-10-30 11:24:19 +01:00
Sebastien Stormacq bf83300f5f Merge branch 'main' into sebsto/new-plugins 2025-10-22 14:56:12 +02:00