Skip to content
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