Commit 3967b15
Add (API Gateway) WebSockets Support to Swift for AWS Lambda Events (#38)
Add APIGateway WebSockets Event Type
### Motivation:
What I propose is adding WebSockets support to AWS Lambda Events.
Let me begin by stating outright that I am not sure this is the correct
approach to take to bring WebSockets to AWS Lambda Events. Therefore, if
this pull request is outright rejected, it won't hurt my feelings in the
slightest.
API Gateway supports not only RESTful APIs, but also WebSockets. The way
that it works is that API Gateway manages WebSockets sessions with
clients. Whenever a client sends API Gateway some WebSockets data, API
Gateway bundles it up in as an APIGatewayV2 request (at least, according
to Amazon) and passes it along to a designated target…usually a Lambda
function. This is what a bundled request looks like:
```javascript
{
headers: {
Host: 'lqrlmblaa2.execute-api.us-east-1.amazonaws.com',
Origin: 'wss://lqrlmblaa2.execute-api.us-east-1.amazonaws.com',
'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits; server_max_window_bits=15',
'Sec-WebSocket-Key': 'am5ubWVpbHd3bmNyYXF0ag==',
'Sec-WebSocket-Version': '13',
'X-Amzn-Trace-Id': 'Root=1-64b83950-42de8e247b4c2b43091ef67c',
'X-Forwarded-For': '24.148.42.16',
'X-Forwarded-Port': '443',
'X-Forwarded-Proto': 'https'
},
multiValueHeaders: {
Host: [ 'lqrlmblaa2.execute-api.us-east-1.amazonaws.com' ],
Origin: [ 'wss://lqrlmblaa2.execute-api.us-east-1.amazonaws.com' ],
'Sec-WebSocket-Extensions': [
'permessage-deflate; client_max_window_bits; server_max_window_bits=15'
],
'Sec-WebSocket-Key': [ 'am5ubWVpbHd3bmNyYXF0ag==' ],
'Sec-WebSocket-Version': [ '13' ],
'X-Amzn-Trace-Id': [ 'Root=1-64b83950-42de8e247b4c2b43091ef67c' ],
'X-Forwarded-For': [ '24.148.42.16' ],
'X-Forwarded-Port': [ '443' ],
'X-Forwarded-Proto': [ 'https' ]
},
requestContext: {
routeKey: '$connect',
eventType: 'CONNECT',
extendedRequestId: 'IU3kkGyEoAMFwZQ=',
requestTime: '19/Jul/2023:19:28:16 +0000',
messageDirection: 'IN',
stage: 'dev',
connectedAt: 1689794896145,
requestTimeEpoch: 1689794896162,
identity: { sourceIp: '24.148.42.16' },
requestId: 'IU3kkGyEoAMFwZQ=',
domainName: 'lqrlmblaa2.execute-api.us-east-1.amazonaws.com',
connectionId: 'IU3kkeN4IAMCJwA=',
apiId: 'lqrlmblaa2'
},
isBase64Encoded: false
}
```
The problem, of course, is that the current `APIGatewayV2Request` type
cannot decode that JSON because it is is missing a number of
non-optional data values that `APIGatewayV2Request` expects to exist
(e.g., `version`, `rawPath`, etc.).
There are (at least as far as I can tell) two solutions to make this
work. The first is simply to alter the current `APIGatewayV2Request` so
that a number of its data values become optionals. I resisted suggesting
this because I suspected it could easily break production code (forcing
developers to `if-let` things). I thought a better solution might simply
be to create a new request/response type pair that could accommodate
WebSockets APIs.
### Modifications:
I suggest adding a new event source file to AWS Lambda Events:
`APIGateway+WebSockets.swift` containing two new types:
`APIGatewayWebSocketRequest` and `APIGatewayWebSocketResponse`.
`APIGatewayWebSocketResponse` would simply be a type alias (since
responses require that no changes be made to that type);
`APIGatewayWebSocketRequest` would be capable of decoding the JSON
listed above.
A typical Lambda handler supporting WebSockets would look like this:
```swift
func handle(
_ request: APIGatewayWebSocketRequest,
context: LambdaContext
) async throws -> APIGatewayWebSocketResponse {
let connectionID = request.context.connectionId
let routeKey = request.context.routeKey
// Route based on the type of WebSockets request
// The following are "default" request types
switch routeKey {
case "$connect": break
case "$disconnect": break
case "$default":
if let body = request.body {
// Responses are sent to clients via the
// ApiGatewayManagementApi. "post" is a method
// (not shown) which does that
try await post(
message: "{\"echo\": \"\(body)\"}",
toConnectionWithID: connectionID
)
}
default:
logger.log(level: .info, "Something weird happened");
}
// API Gateway requires that "some" status be returned
// "no matter what"
return APIGatewayWebSocketResponse(statusCode: .ok)
}
```
Note that responses to WebSockets clients (including, potentially,
errors) are made through Amazon's `ApiGatewayManagementApi`. However,
API Gateway itself always expects some kind of response…this can be a
simple as always sending a 200 "OK" back to API Gateway.
### Result:
The Swift for AWS Lambda Runtime would be able to support API Gateway
WebSockets applications.
---------
Co-authored-by: Sébastien Stormacq <[email protected]>1 parent 318868c commit 3967b15
File tree
2 files changed
+146
-0
lines changed- Sources/AWSLambdaEvents
- Tests/AWSLambdaEventsTests
2 files changed
+146
-0
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
Lines changed: 78 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
0 commit comments