Skip to content

handle lambda error correctly in mock server #104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 1, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 35 additions & 6 deletions Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ private enum LocalLambda {
case .waitingForLambdaRequest, .waitingForLambdaResponse:
Self.invocations.append(invocation)
}

// /next endpoint is called by the lambda polling for work
case (.GET, let url) where url.hasSuffix(Consts.getNextInvocationURLSuffix):
// check if our server is in the correct state
Expand All @@ -168,7 +169,7 @@ private enum LocalLambda {
switch result {
case .failure(let error):
self.logger.error("invocation error: \(error)")
self.writeResponse(context: context, response: .init(status: .internalServerError))
self.writeResponse(context: context, status: .internalServerError)
case .success(let invocation):
Self.invocationState = .waitingForLambdaResponse(invocation)
self.writeResponse(context: context, response: invocation.makeResponse())
Expand All @@ -180,33 +181,61 @@ private enum LocalLambda {
Self.invocationState = .waitingForLambdaResponse(invocation)
self.writeResponse(context: context, response: invocation.makeResponse())
}

// :requestID/response endpoint is called by the lambda posting the response
case (.POST, let url) where url.hasSuffix(Consts.postResponseURLSuffix):
let parts = request.head.uri.split(separator: "/")
guard let requestID = parts.count > 2 ? String(parts[parts.count - 2]) : nil else {
// the request is malformed, since we were expecting a requestId in the path
return self.writeResponse(context: context, response: .init(status: .badRequest))
return self.writeResponse(context: context, status: .badRequest)
}
guard case .waitingForLambdaResponse(let invocation) = Self.invocationState else {
// a response was send, but we did not expect to receive one
self.logger.error("invalid invocation state \(Self.invocationState)")
return self.writeResponse(context: context, response: .init(status: .unprocessableEntity))
return self.writeResponse(context: context, status: .unprocessableEntity)
}
guard requestID == invocation.requestID else {
// the request's requestId is not matching the one we are expecting
self.logger.error("invalid invocation state request ID \(requestID) does not match expected \(invocation.requestID)")
return self.writeResponse(context: context, response: .init(status: .badRequest))
return self.writeResponse(context: context, status: .badRequest)
}

invocation.responsePromise.succeed(.init(status: .ok, body: request.body))
self.writeResponse(context: context, response: .init(status: .accepted))
self.writeResponse(context: context, status: .accepted)
Self.invocationState = .waitingForLambdaRequest

// :requestID/error endpoint is called by the lambda posting an error response
case (.POST, let url) where url.hasSuffix(Consts.postErrorURLSuffix):
let parts = request.head.uri.split(separator: "/")
guard let requestID = parts.count > 2 ? String(parts[parts.count - 2]) : nil else {
// the request is malformed, since we were expecting a requestId in the path
return self.writeResponse(context: context, status: .badRequest)
}
guard case .waitingForLambdaResponse(let invocation) = Self.invocationState else {
// a response was send, but we did not expect to receive one
self.logger.error("invalid invocation state \(Self.invocationState)")
return self.writeResponse(context: context, status: .unprocessableEntity)
}
guard requestID == invocation.requestID else {
// the request's requestId is not matching the one we are expecting
self.logger.error("invalid invocation state request ID \(requestID) does not match expected \(invocation.requestID)")
return self.writeResponse(context: context, status: .badRequest)
}

invocation.responsePromise.succeed(.init(status: .internalServerError, body: request.body))
self.writeResponse(context: context, status: .accepted)
Self.invocationState = .waitingForLambdaRequest

// unknown call
default:
self.writeResponse(context: context, response: .init(status: .notFound))
self.writeResponse(context: context, status: .notFound)
}
}

func writeResponse(context: ChannelHandlerContext, status: HTTPResponseStatus) {
self.writeResponse(context: context, response: .init(status: status))
}

func writeResponse(context: ChannelHandlerContext, response: Response) {
var headers = HTTPHeaders(response.headers ?? [])
headers.add(name: "content-length", value: "\(response.body?.readableBytes ?? 0)")
Expand Down