@@ -393,35 +393,50 @@ internal struct LambdaHTTPServer {
393393 self . invocationPool. push ( LocalServerInvocation ( requestId: requestId, request: body) )
394394
395395 // wait for the lambda function to process the request
396- for try await response in self . responsePool {
397- logger [ metadataKey: " response requestId " ] = " \( response. requestId ?? " nil " ) "
398- logger. trace ( " Received response to return to client " )
399- if response. requestId == requestId {
400- logger. trace ( " /invoke requestId is valid, sending the response " )
401- // send the response to the client
402- // if the response is final, we can send it and return
403- // if the response is not final, we can send it and wait for the next response
404- try await self . sendResponse ( response, outbound: outbound, logger: logger)
405- if response. final == true {
406- logger. trace ( " /invoke returning " )
407- return // if the response is final, we can return and close the connection
396+ // when POST /invoke is called multiple times before a response is processed,
397+ // the `for try await ... in` loop will throw an error and we will return a 400 error to the client
398+ do {
399+ for try await response in self . responsePool {
400+ logger [ metadataKey: " response requestId " ] = " \( response. requestId ?? " nil " ) "
401+ logger. trace ( " Received response to return to client " )
402+ if response. requestId == requestId {
403+ logger. trace ( " /invoke requestId is valid, sending the response " )
404+ // send the response to the client
405+ // if the response is final, we can send it and return
406+ // if the response is not final, we can send it and wait for the next response
407+ try await self . sendResponse ( response, outbound: outbound, logger: logger)
408+ if response. final == true {
409+ logger. trace ( " /invoke returning " )
410+ return // if the response is final, we can return and close the connection
411+ }
412+ } else {
413+ logger. error (
414+ " Received response for a different requestId " ,
415+ metadata: [ " response requestId " : " \( response. requestId ?? " " ) " ]
416+ )
417+ let response = LocalServerResponse (
418+ id: requestId,
419+ status: . badRequest,
420+ body: ByteBuffer ( string: " The responseId is not equal to the requestId. " )
421+ )
422+ try await self . sendResponse ( response, outbound: outbound, logger: logger)
408423 }
409- } else {
410- logger. error (
411- " Received response for a different requestId " ,
412- metadata: [ " response requestId " : " \( response. requestId ?? " " ) " ]
413- )
414- let response = LocalServerResponse (
415- id: requestId,
416- status: . badRequest,
417- body: ByteBuffer ( string: " The responseId is not equal to the requestId. " )
418- )
419- try await self . sendResponse ( response, outbound: outbound, logger: logger)
420424 }
425+ // What todo when there is no more responses to process?
426+ // This should not happen as the async iterator blocks until there is a response to process
427+ fatalError ( " No more responses to process - the async for loop should not return " )
428+ } catch is LambdaHTTPServer . Pool < LambdaHTTPServer . LocalServerResponse > . PoolError {
429+ // detect concurrent invocations of POST and gently decline the requests while we're processing one.
430+ let response = LocalServerResponse (
431+ id: requestId,
432+ status: . badRequest,
433+ body: ByteBuffer (
434+ string:
435+ " It is not allowed to invoke multiple Lambda function executions in parallel. (The Lambda runtime environment on AWS will never do that) "
436+ )
437+ )
438+ try await self . sendResponse ( response, outbound: outbound, logger: logger)
421439 }
422- // What todo when there is no more responses to process?
423- // This should not happen as the async iterator blocks until there is a response to process
424- fatalError ( " No more responses to process - the async for loop should not return " )
425440
426441 // client uses incorrect HTTP method
427442 case ( _, let url) where url. hasSuffix ( self . invocationEndpoint) :
@@ -606,19 +621,18 @@ internal struct LambdaHTTPServer {
606621 if let nextAction = state. actionQueue. popFirst ( ) {
607622 return ( nextAction, continuation)
608623 } else {
609- // there is no continuation and no action waiting,
610- // enqueue the continuation for later usage
611- state. continuationQueue. append ( continuation)
612- return ( nil , continuation)
624+ state = . continuation( continuation)
625+ return nil
613626 }
627+
628+ case . continuation ( _) :
629+ fatalError ( " \( self . poolName) : Concurrent invocations to next(). This is not allowed. " )
614630 }
615631 }
616632
617- // there is no next action, ignore
618633 guard let nextAction else { return }
619634
620- // we have a next action and a next continuation, resume it
621- nextContinuation. resume ( returning: nextAction)
635+ continuation. resume ( returning: nextAction)
622636 }
623637 } onCancel: {
624638 self . lock. withLock { state in
0 commit comments