From 1c7c4ca601653c8cff71b4c25e1b0523a15549d4 Mon Sep 17 00:00:00 2001 From: Pete Cornish Date: Tue, 1 Oct 2024 15:05:05 +0100 Subject: [PATCH] fix(awslambda): attributes should persist between exchanges for the same request/response pair. --- .../imposter/awslambda/impl/LambdaServer.kt | 11 +++++++---- .../awslambda/impl/model/LambdaHttpExchange.kt | 14 +++++++------- .../imposter/service/HandlerServiceImpl.kt | 12 ++++-------- 3 files changed, 18 insertions(+), 19 deletions(-) diff --git a/adapter/awslambda/src/main/java/io/gatehill/imposter/awslambda/impl/LambdaServer.kt b/adapter/awslambda/src/main/java/io/gatehill/imposter/awslambda/impl/LambdaServer.kt index 74e0696ec..03baf83f7 100644 --- a/adapter/awslambda/src/main/java/io/gatehill/imposter/awslambda/impl/LambdaServer.kt +++ b/adapter/awslambda/src/main/java/io/gatehill/imposter/awslambda/impl/LambdaServer.kt @@ -76,6 +76,8 @@ abstract class LambdaServer( fun dispatch(event: Request): Response { val response = LambdaHttpResponse() + val attributes = mutableMapOf() + var failureCause: Throwable? = null try { val matched = matchRoutes(event) @@ -87,7 +89,7 @@ abstract class LambdaServer( } else { matched.forEach { route -> val request = buildRequest(event, route) - val exchange = LambdaHttpExchange(router, route, request, response) + val exchange = LambdaHttpExchange(router, route, request, response, attributes) val handler = route.handler ?: throw IllegalStateException("No route handler set for: $route") try { handler(exchange).get() @@ -111,12 +113,12 @@ abstract class LambdaServer( when (val statusCode = response.statusCode) { in 400..499 -> { errorHandlers[statusCode]?.let { errorHandler -> - failExchange(event, response, statusCode, null, errorHandler) + failExchange(event, response, attributes, statusCode, null, errorHandler) } ?: logger.warn("Unhandled client error for: ${describeRequestShort(event)} [status code: $statusCode]") } in 500..599 -> { errorHandlers[statusCode]?.let { errorHandler -> - failExchange(event, response, statusCode, failureCause, errorHandler) + failExchange(event, response, attributes, statusCode, failureCause, errorHandler) } ?: logger.error("Unhandled server error for: ${describeRequestShort(event)} [status code: $statusCode]", failureCause) } } @@ -127,12 +129,13 @@ abstract class LambdaServer( private fun failExchange( event: Request, response: LambdaHttpResponse, + attributes: MutableMap, statusCode: Int, failureCause: Throwable?, errorHandler: (HttpExchange) -> Unit, ) { val request = buildRequest(event, null) - val exchange = LambdaHttpExchange(router, null, request, response) + val exchange = LambdaHttpExchange(router, null, request, response, attributes) exchange.fail(statusCode, failureCause) errorHandler(exchange) } diff --git a/adapter/awslambda/src/main/java/io/gatehill/imposter/awslambda/impl/model/LambdaHttpExchange.kt b/adapter/awslambda/src/main/java/io/gatehill/imposter/awslambda/impl/model/LambdaHttpExchange.kt index a0be2f890..70d531a01 100644 --- a/adapter/awslambda/src/main/java/io/gatehill/imposter/awslambda/impl/model/LambdaHttpExchange.kt +++ b/adapter/awslambda/src/main/java/io/gatehill/imposter/awslambda/impl/model/LambdaHttpExchange.kt @@ -44,12 +44,7 @@ package io.gatehill.imposter.awslambda.impl.model import com.google.common.base.Strings -import io.gatehill.imposter.http.ExchangePhase -import io.gatehill.imposter.http.HttpExchange -import io.gatehill.imposter.http.HttpRequest -import io.gatehill.imposter.http.HttpResponse -import io.gatehill.imposter.http.HttpRoute -import io.gatehill.imposter.http.HttpRouter +import io.gatehill.imposter.http.* import io.gatehill.imposter.util.HttpUtil import io.vertx.core.buffer.Buffer @@ -61,9 +56,14 @@ class LambdaHttpExchange( override val currentRoute: HttpRoute?, override val request: HttpRequest, response: HttpResponse, + + /** + * Externally manage the lifecycle of attributes so they persist between exchange + * instances for the same request/response pair. + */ + private val attributes : MutableMap, ) : HttpExchange { override var phase = ExchangePhase.REQUEST_RECEIVED - private val attributes = mutableMapOf() override var failureCause: Throwable? = null private set diff --git a/core/engine/src/main/java/io/gatehill/imposter/service/HandlerServiceImpl.kt b/core/engine/src/main/java/io/gatehill/imposter/service/HandlerServiceImpl.kt index bf79bbe6e..ae4daa2de 100644 --- a/core/engine/src/main/java/io/gatehill/imposter/service/HandlerServiceImpl.kt +++ b/core/engine/src/main/java/io/gatehill/imposter/service/HandlerServiceImpl.kt @@ -46,13 +46,7 @@ import com.google.common.collect.Lists import io.gatehill.imposter.ImposterConfig import io.gatehill.imposter.config.ResolvedResourceConfig import io.gatehill.imposter.config.util.EnvVars -import io.gatehill.imposter.http.ExchangePhase -import io.gatehill.imposter.http.HttpExchange -import io.gatehill.imposter.http.HttpExchangeFutureHandler -import io.gatehill.imposter.http.HttpExchangeHandler -import io.gatehill.imposter.http.HttpMethod -import io.gatehill.imposter.http.HttpRouter -import io.gatehill.imposter.http.ResourceMatcher +import io.gatehill.imposter.http.* import io.gatehill.imposter.lifecycle.SecurityLifecycleHooks import io.gatehill.imposter.lifecycle.SecurityLifecycleListener import io.gatehill.imposter.plugin.config.InterceptorsHolder @@ -73,7 +67,7 @@ import kotlinx.coroutines.future.future import org.apache.logging.log4j.Level import org.apache.logging.log4j.LogManager import java.io.File -import java.util.UUID +import java.util.* import java.util.concurrent.CompletableFuture import java.util.regex.Pattern import javax.inject.Inject @@ -155,6 +149,8 @@ class HandlerServiceImpl @Inject constructor( httpExchange.get(ResourceUtil.RC_SEND_NOT_FOUND_RESPONSE) == true ) { // only override response processing if the 404 did not originate from the mock engine + // otherwise this will attempt to send a duplicate response to an already completed + // exchange, resulting in an IllegalStateException logAppropriatelyForPath(httpExchange, "File not found") responseService.sendNotFoundResponse(httpExchange) }