@@ -19,6 +19,12 @@ import Testing
1919
2020@testable import AWSLambdaRuntime
2121
22+ #if canImport(FoundationEssentials)
23+ import FoundationEssentials
24+ #else
25+ import Foundation
26+ #endif
27+
2228extension LambdaRuntimeTests {
2329
2430 @Test ( " Local server respects LOCAL_LAMBDA_PORT environment variable " )
@@ -77,6 +83,88 @@ extension LambdaRuntimeTests {
7783 #expect( result == true )
7884 }
7985
86+ @Test ( " Local server handles rapid concurrent requests without HTTP 400 errors " )
87+ @available ( LambdaSwift 2 . 0 , * )
88+ func testRapidConcurrentRequests( ) async throws {
89+ let customPort = 8081
90+
91+ // Set environment variable
92+ setenv ( " LOCAL_LAMBDA_PORT " , " \( customPort) " , 1 )
93+ defer { unsetenv ( " LOCAL_LAMBDA_PORT " ) }
94+
95+ let results = try await withThrowingTaskGroup ( of: [ Int ] . self) { group in
96+
97+ // Start the Lambda runtime with local server
98+ group. addTask {
99+ let runtime = LambdaRuntime { ( event: String , context: LambdaContext ) in
100+ try await Task . sleep ( for: . milliseconds( 100 ) )
101+ return " Hello \( event) "
102+ }
103+
104+ // Start runtime (this will block until cancelled)
105+ try await runtime. _run ( )
106+ return [ ]
107+ }
108+
109+ // Start HTTP client to make rapid requests
110+ group. addTask {
111+ // Give server time to start
112+ try await Task . sleep ( for: . milliseconds( 200 ) )
113+
114+ // Make 10 rapid concurrent POST requests to /invoke
115+ return try await withThrowingTaskGroup ( of: Int . self) { clientGroup in
116+ var statuses : [ Int ] = [ ]
117+
118+ for i in 0 ..< 10 {
119+ try await Task . sleep ( for: . milliseconds( 0 ) )
120+ clientGroup. addTask {
121+ let ( _, response) = try await self . makeInvokeRequest (
122+ host: " 127.0.0.1 " ,
123+ port: customPort,
124+ payload: " \" World \( i) \" "
125+ )
126+ return response. statusCode
127+ }
128+ }
129+
130+ for try await status in clientGroup {
131+ statuses. append ( status)
132+ }
133+
134+ return statuses
135+ }
136+ }
137+
138+ // Get the first result (HTTP statuses) and cancel the runtime
139+ let first = try await group. next ( )
140+ group. cancelAll ( )
141+ return first ?? [ ]
142+ }
143+
144+ // Verify all requests returned 200 OK (no HTTP 400 errors)
145+ #expect( results. count == 10 , " Expected 10 responses " )
146+ for (index, status) in results. enumerated ( ) {
147+ #expect( status == 202 , " Request \( index) returned \( status) , expected 202 OK " )
148+ }
149+ }
150+
151+ private func makeInvokeRequest( host: String , port: Int , payload: String ) async throws -> ( Data , HTTPURLResponse ) {
152+ let url = URL ( string: " http:// \( host) : \( port) /invoke " ) !
153+ var request = URLRequest ( url: url)
154+ request. httpMethod = " POST "
155+ request. setValue ( " application/json " , forHTTPHeaderField: " Content-Type " )
156+ request. httpBody = payload. data ( using: . utf8)
157+ request. timeoutInterval = 10.0
158+
159+ let ( data, response) = try await URLSession . shared. data ( for: request)
160+
161+ guard let httpResponse = response as? HTTPURLResponse else {
162+ throw URLError ( . badServerResponse)
163+ }
164+
165+ return ( data, httpResponse)
166+ }
167+
80168 private func isPortResponding( host: String , port: Int ) async throws -> Bool {
81169 let group = MultiThreadedEventLoopGroup ( numberOfThreads: 1 )
82170
0 commit comments