Skip to content

Commit 3b29513

Browse files
authored
handle lambda errors correctly in mock server (#104)
motivation: better mock server changes: * add handler for :requestID/error * return .accepted to lambda * return .internalServerError to client + error json * small refactoring of response code in mock server to make it DRYer
1 parent e2ac820 commit 3b29513

File tree

1 file changed

+35
-6
lines changed

1 file changed

+35
-6
lines changed

Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ private enum LocalLambda {
149149
case .waitingForLambdaRequest, .waitingForLambdaResponse:
150150
Self.invocations.append(invocation)
151151
}
152+
152153
// /next endpoint is called by the lambda polling for work
153154
case (.GET, let url) where url.hasSuffix(Consts.getNextInvocationURLSuffix):
154155
// check if our server is in the correct state
@@ -168,7 +169,7 @@ private enum LocalLambda {
168169
switch result {
169170
case .failure(let error):
170171
self.logger.error("invocation error: \(error)")
171-
self.writeResponse(context: context, response: .init(status: .internalServerError))
172+
self.writeResponse(context: context, status: .internalServerError)
172173
case .success(let invocation):
173174
Self.invocationState = .waitingForLambdaResponse(invocation)
174175
self.writeResponse(context: context, response: invocation.makeResponse())
@@ -180,33 +181,61 @@ private enum LocalLambda {
180181
Self.invocationState = .waitingForLambdaResponse(invocation)
181182
self.writeResponse(context: context, response: invocation.makeResponse())
182183
}
184+
183185
// :requestID/response endpoint is called by the lambda posting the response
184186
case (.POST, let url) where url.hasSuffix(Consts.postResponseURLSuffix):
185187
let parts = request.head.uri.split(separator: "/")
186188
guard let requestID = parts.count > 2 ? String(parts[parts.count - 2]) : nil else {
187189
// the request is malformed, since we were expecting a requestId in the path
188-
return self.writeResponse(context: context, response: .init(status: .badRequest))
190+
return self.writeResponse(context: context, status: .badRequest)
189191
}
190192
guard case .waitingForLambdaResponse(let invocation) = Self.invocationState else {
191193
// a response was send, but we did not expect to receive one
192194
self.logger.error("invalid invocation state \(Self.invocationState)")
193-
return self.writeResponse(context: context, response: .init(status: .unprocessableEntity))
195+
return self.writeResponse(context: context, status: .unprocessableEntity)
194196
}
195197
guard requestID == invocation.requestID else {
196198
// the request's requestId is not matching the one we are expecting
197199
self.logger.error("invalid invocation state request ID \(requestID) does not match expected \(invocation.requestID)")
198-
return self.writeResponse(context: context, response: .init(status: .badRequest))
200+
return self.writeResponse(context: context, status: .badRequest)
199201
}
200202

201203
invocation.responsePromise.succeed(.init(status: .ok, body: request.body))
202-
self.writeResponse(context: context, response: .init(status: .accepted))
204+
self.writeResponse(context: context, status: .accepted)
205+
Self.invocationState = .waitingForLambdaRequest
206+
207+
// :requestID/error endpoint is called by the lambda posting an error response
208+
case (.POST, let url) where url.hasSuffix(Consts.postErrorURLSuffix):
209+
let parts = request.head.uri.split(separator: "/")
210+
guard let requestID = parts.count > 2 ? String(parts[parts.count - 2]) : nil else {
211+
// the request is malformed, since we were expecting a requestId in the path
212+
return self.writeResponse(context: context, status: .badRequest)
213+
}
214+
guard case .waitingForLambdaResponse(let invocation) = Self.invocationState else {
215+
// a response was send, but we did not expect to receive one
216+
self.logger.error("invalid invocation state \(Self.invocationState)")
217+
return self.writeResponse(context: context, status: .unprocessableEntity)
218+
}
219+
guard requestID == invocation.requestID else {
220+
// the request's requestId is not matching the one we are expecting
221+
self.logger.error("invalid invocation state request ID \(requestID) does not match expected \(invocation.requestID)")
222+
return self.writeResponse(context: context, status: .badRequest)
223+
}
224+
225+
invocation.responsePromise.succeed(.init(status: .internalServerError, body: request.body))
226+
self.writeResponse(context: context, status: .accepted)
203227
Self.invocationState = .waitingForLambdaRequest
228+
204229
// unknown call
205230
default:
206-
self.writeResponse(context: context, response: .init(status: .notFound))
231+
self.writeResponse(context: context, status: .notFound)
207232
}
208233
}
209234

235+
func writeResponse(context: ChannelHandlerContext, status: HTTPResponseStatus) {
236+
self.writeResponse(context: context, response: .init(status: status))
237+
}
238+
210239
func writeResponse(context: ChannelHandlerContext, response: Response) {
211240
var headers = HTTPHeaders(response.headers ?? [])
212241
headers.add(name: "content-length", value: "\(response.body?.readableBytes ?? 0)")

0 commit comments

Comments
 (0)