Skip to content

Commit ad5c428

Browse files
committed
add support for LOCAL_LAMBDA_PORT
1 parent ec28c96 commit ad5c428

File tree

4 files changed

+117
-2
lines changed

4 files changed

+117
-2
lines changed

Sources/AWSLambdaRuntime/Lambda+LocalServer.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,18 +42,21 @@ extension Lambda {
4242
///
4343
/// - parameters:
4444
/// - invocationEndpoint: The endpoint to post events to.
45+
/// - port: the TCP port to listen to
4546
/// - body: Code to run within the context of the mock server. Typically this would be a Lambda.run function call.
4647
///
4748
/// - note: This API is designed strictly for local testing and is behind a DEBUG flag
4849
@usableFromInline
4950
static func withLocalServer(
5051
invocationEndpoint: String? = nil,
52+
port: Int,
5153
logger: Logger,
5254
_ body: sending @escaping () async throws -> Void
5355
) async throws {
5456
do {
5557
try await LambdaHTTPServer.withLocalServer(
5658
invocationEndpoint: invocationEndpoint,
59+
port: port,
5760
logger: logger
5861
) {
5962
try await body()
@@ -112,7 +115,7 @@ internal struct LambdaHTTPServer {
112115
static func withLocalServer<Result: Sendable>(
113116
invocationEndpoint: String?,
114117
host: String = "127.0.0.1",
115-
port: Int = 7000,
118+
port: Int,
116119
eventLoopGroup: MultiThreadedEventLoopGroup = .singleton,
117120
logger: Logger,
118121
_ closure: sending @escaping () async throws -> Result

Sources/AWSLambdaRuntime/LambdaRuntime.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,20 @@ public final class LambdaRuntime<Handler>: Sendable where Handler: StreamingLamb
122122
} else {
123123

124124
#if LocalServerSupport
125+
125126
// we're not running on Lambda and we're compiled in DEBUG mode,
126127
// let's start a local server for testing
128+
129+
let port = Lambda.env("LOCAL_LAMBDA_PORT").flatMap(Int.init) ?? 7000
130+
127131
try await Lambda.withLocalServer(
128132
invocationEndpoint: Lambda.env("LOCAL_LAMBDA_SERVER_INVOCATION_ENDPOINT"),
133+
port: port,
129134
logger: self.logger
130135
) {
131136

132137
try await LambdaRuntimeClient.withRuntimeClient(
133-
configuration: .init(ip: "127.0.0.1", port: 7000),
138+
configuration: .init(ip: "127.0.0.1", port: port),
134139
eventLoop: self.eventLoop,
135140
logger: self.logger
136141
) { runtimeClient in
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the SwiftAWSLambdaRuntime open source project
4+
//
5+
// Copyright (c) 2024 Apple Inc. and the SwiftAWSLambdaRuntime project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import Logging
16+
import NIOCore
17+
import NIOPosix
18+
import Testing
19+
20+
@testable import AWSLambdaRuntime
21+
22+
@Suite("Lambda Local Server Tests")
23+
struct LambdaLocalServerTests {
24+
25+
@Test("Local server respects LOCAL_LAMBDA_PORT environment variable")
26+
func testLocalServerCustomPort() async throws {
27+
let customPort = 8080
28+
29+
// Set environment variable
30+
setenv("LOCAL_LAMBDA_PORT", "\(customPort)", 1)
31+
defer { unsetenv("LOCAL_LAMBDA_PORT") }
32+
33+
let result = try? await withThrowingTaskGroup(of: Bool.self) { group in
34+
35+
// start a local lambda + local server on custom port
36+
group.addTask {
37+
// Create a simple handler
38+
struct TestHandler: StreamingLambdaHandler {
39+
func handle(
40+
_ event: ByteBuffer,
41+
responseWriter: some LambdaResponseStreamWriter,
42+
context: LambdaContext
43+
) async throws {
44+
try await responseWriter.write(ByteBuffer(string: "test"))
45+
try await responseWriter.finish()
46+
}
47+
}
48+
49+
// create the Lambda Runtime
50+
let runtime = LambdaRuntime(
51+
handler: TestHandler(),
52+
logger: Logger(label: "test", factory: { _ in SwiftLogNoOpLogHandler() })
53+
)
54+
55+
// Start runtime
56+
try await runtime._run()
57+
58+
// we reach this line when the group is cancelled
59+
return false
60+
}
61+
62+
// start a client to check if tsomething responds on the custom port
63+
group.addTask {
64+
// Give server time to start
65+
try await Task.sleep(for: .milliseconds(100))
66+
67+
// Verify server is listening on custom port
68+
return try await isPortResponding(host: "127.0.0.1", port: customPort)
69+
}
70+
71+
let first = try await group.next()
72+
group.cancelAll()
73+
return first ?? false
74+
75+
}
76+
77+
#expect(result == true)
78+
}
79+
80+
private func isPortResponding(host: String, port: Int) async throws -> Bool {
81+
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
82+
83+
let bootstrap = ClientBootstrap(group: group)
84+
85+
do {
86+
let channel = try await bootstrap.connect(host: host, port: port).get()
87+
try await channel.close().get()
88+
try await group.shutdownGracefully()
89+
return true
90+
} catch {
91+
try await group.shutdownGracefully()
92+
return false
93+
}
94+
}
95+
}

readme.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,18 @@ Example:
476476
LOCAL_LAMBDA_SERVER_INVOCATION_ENDPOINT=/2015-03-31/functions/function/invocations swift run
477477
```
478478

479+
## Modifying the local port
480+
481+
By default, when using the local Lambda server, it listens on TCP port 7000.
482+
483+
On macOS, this port might be used by AirPlay under some configurations. You can use the `LOCAL_LAMBDA_PORT` environment variable to configure the runtime to listen on a different TCP port.
484+
485+
Example:
486+
487+
```sh
488+
LOCAL_LAMBDA_PORT=7777 swift run
489+
```
490+
479491
## Deploying your Swift Lambda functions
480492

481493
There is a full deployment guide available in [the documentation](https://swiftpackageindex.com/swift-server/swift-aws-lambda-runtime/main/documentation/awslambdaruntime/deployment).

0 commit comments

Comments
 (0)