From 69cf88ae66ad6940a9c8c4962f30b37c5d87a6f0 Mon Sep 17 00:00:00 2001 From: tom doron Date: Mon, 1 Jun 2020 13:06:14 -0700 Subject: [PATCH] handle lambda error corrctly in mock server 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 --- .../Lambda+LocalServer.swift | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift b/Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift index 148dcd5a..ffc9ff52 100644 --- a/Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift +++ b/Sources/AWSLambdaRuntimeCore/Lambda+LocalServer.swift @@ -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 @@ -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()) @@ -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)")