All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.networknt.aws.lambda.LambdaFunctionHandler Maven / Gradle / Ivy

package com.networknt.aws.lambda;

import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import com.networknt.body.BodyHandler;
import com.networknt.config.Config;
import com.networknt.config.JsonMapper;
import com.networknt.handler.Handler;
import com.networknt.handler.LightHttpHandler;
import com.networknt.httpstring.AttachmentConstants;
import com.networknt.metrics.AbstractMetricsHandler;
import com.networknt.metrics.MetricsConfig;
import com.networknt.utility.Constants;
import com.networknt.utility.ModuleRegistry;
import com.networknt.utility.StringUtils;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.util.HeaderMap;
import io.undertow.util.HttpString;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import software.amazon.awssdk.core.SdkBytes;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.lambda.LambdaClient;
import software.amazon.awssdk.services.lambda.LambdaClientBuilder;
import software.amazon.awssdk.services.lambda.model.InvokeRequest;
import software.amazon.awssdk.services.lambda.model.InvokeResponse;
import software.amazon.awssdk.services.lambda.model.LambdaException;

import java.net.URI;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

public class LambdaFunctionHandler implements LightHttpHandler {
    private static final Logger logger = LoggerFactory.getLogger(LambdaFunctionHandler.class);
    private static LambdaInvokerConfig config;
    private static final String MISSING_ENDPOINT_FUNCTION = "ERR10063";
    private static final String EMPTY_LAMBDA_RESPONSE = "ERR10064";
    private static AbstractMetricsHandler metricsHandler;

    private static LambdaClient client;

    public LambdaFunctionHandler() {
        config = LambdaInvokerConfig.load();
        LambdaClientBuilder builder = LambdaClient.builder().region(Region.of(config.getRegion()));
        if(!StringUtils.isEmpty(config.getEndpointOverride())) {
            builder.endpointOverride(URI.create(config.getEndpointOverride()));
        }
        client = builder.build();
        if(config.isMetricsInjection()) {
            // get the metrics handler from the handler chain for metrics registration. If we cannot get the
            // metrics handler, then an error message will be logged.
            Map handlers = Handler.getHandlers();
            metricsHandler = (AbstractMetricsHandler) handlers.get(MetricsConfig.CONFIG_NAME);
            if(metricsHandler == null) {
                logger.error("An instance of MetricsHandler is not configured in the handler.yml.");
            }
        }
        ModuleRegistry.registerModule(LambdaFunctionHandler.class.getName(), config.getMappedConfig(), null);
        if(logger.isInfoEnabled()) logger.info("LambdaFunctionHandler is loaded.");
    }

    @Override
    public void handleRequest(HttpServerExchange exchange) throws Exception {
        long startTime = System.nanoTime();
        String httpMethod = exchange.getRequestMethod().toString();
        String requestPath = exchange.getRequestPath();
        Map headers = convertHeaders(exchange.getRequestHeaders());
        Map queryStringParameters = convertQueryParameters(exchange.getQueryParameters());
        Map pathParameters = convertPathParameters(exchange.getPathParameters());
        String body = exchange.getAttachment(BodyHandler.REQUEST_BODY_STRING);
        if(logger.isTraceEnabled()) {
            logger.trace("requestPath = " + requestPath + " httpMethod = " + httpMethod + " body = " + body);
            logger.trace("headers = " + JsonMapper.toJson(headers));
            logger.trace("queryParameters = " + JsonMapper.toJson(queryStringParameters));
            logger.trace("pathParameters = " + JsonMapper.toJson(pathParameters));
        }
        String endpoint = (String)exchange.getAttachment(AttachmentConstants.AUDIT_INFO).get(Constants.ENDPOINT_STRING);
        String functionName = config.getFunctions().get(endpoint);
        if(functionName == null) {
            setExchangeStatus(exchange, MISSING_ENDPOINT_FUNCTION, endpoint);
            if(config.isMetricsInjection() && metricsHandler != null) metricsHandler.injectMetrics(exchange, startTime, config.getMetricsName());
            return;
        }
        APIGatewayProxyRequestEvent requestEvent = new APIGatewayProxyRequestEvent();
        requestEvent.setHttpMethod(httpMethod);
        requestEvent.setPath(requestPath);
        requestEvent.setHeaders(headers);
        requestEvent.setPathParameters(pathParameters);
        requestEvent.setQueryStringParameters(queryStringParameters);
        requestEvent.setBody(body == null ? null : body);
        String requestBody = JsonMapper.objectMapper.writeValueAsString(requestEvent);
        if(logger.isTraceEnabled()) logger.trace("requestBody = " + requestBody);
        String res = invokeFunction(client, functionName, requestBody);
        if(logger.isDebugEnabled()) logger.debug("response = " + res);
        if(res == null) {
            setExchangeStatus(exchange, EMPTY_LAMBDA_RESPONSE, functionName);
            if(config.isMetricsInjection() && metricsHandler != null) metricsHandler.injectMetrics(exchange, startTime, config.getMetricsName());
            return;
        }
        APIGatewayProxyResponseEvent responseEvent = JsonMapper.fromJson(res, APIGatewayProxyResponseEvent.class);
        setResponseHeaders(exchange, responseEvent.getHeaders());
        exchange.setStatusCode(responseEvent.getStatusCode());
        exchange.getResponseSender().send(responseEvent.getBody());
        if(config.isMetricsInjection() && metricsHandler != null) metricsHandler.injectMetrics(exchange, startTime, config.getMetricsName());
    }

    private String invokeFunction(LambdaClient awsLambda, String functionName, String requestBody)  {
        String response = null;
        try {
            //Need a SdkBytes instance for the payload
            SdkBytes payload = SdkBytes.fromUtf8String(requestBody) ;

            //Setup an InvokeRequest
            InvokeRequest request = InvokeRequest.builder()
                    .functionName(functionName)
                    .logType(config.getLogType())
                    .payload(payload)
                    .build();

            //Invoke the Lambda function
            InvokeResponse res = awsLambda.invoke(request);
            if(logger.isDebugEnabled()) {
                logger.debug("lambda call function error:" + res.functionError());
                logger.debug("lambda logger result:" + res.logResult());
                logger.debug("lambda call status:" + res.statusCode());
            }

            response = res.payload().asUtf8String() ;
        } catch(LambdaException e) {
            logger.error("LambdaException", e);
        }
        return response;
    }

    private Map convertHeaders(HeaderMap headerMap) {
        Map headers = new HashMap<>();
        if(headerMap != null) {
            for(HttpString headerName : headerMap.getHeaderNames()) {
                headers.put(headerName.toString(), headerMap.get(headerName).getFirst());
            }
        }
        return headers;
    }

    private Map convertQueryParameters(Map> params) {
        Map queryParameters = new HashMap<>();
        if(params != null) {
            for(String key : params.keySet()) {
                queryParameters.put(key, params.get(key).getFirst());
            }
        }
        return queryParameters;

    }

    private Map convertPathParameters(Map> params) {
        Map pathParameters = new HashMap<>();
        if(params != null) {
            for(String key : params.keySet()) {
                pathParameters.put(key, params.get(key).getFirst());
            }
        }
        return pathParameters;

    }

    private void setResponseHeaders(HttpServerExchange exchange, Map headers) {
        if(headers != null) {
            for(String key : headers.keySet()) {
                exchange.getResponseHeaders().put(new HttpString(key), headers.get(key));
            }
        }
    }

    public static void reload() {
        config.reload();
        LambdaClientBuilder builder = LambdaClient.builder().region(Region.of(config.getRegion()));
        if(!StringUtils.isEmpty(config.getEndpointOverride())) {
            builder.endpointOverride(URI.create(config.getEndpointOverride()));
        }
        client = builder.build();
        if(config.isMetricsInjection()) {
            // get the metrics handler from the handler chain for metrics registration. If we cannot get the
            // metrics handler, then an error message will be logged.
            Map handlers = Handler.getHandlers();
            metricsHandler = (AbstractMetricsHandler) handlers.get(MetricsConfig.CONFIG_NAME);
            if(metricsHandler == null) {
                logger.error("An instance of MetricsHandler is not configured in the handler.yml.");
            }
        }
        ModuleRegistry.registerModule(LambdaFunctionHandler.class.getName(), config.getMappedConfig(), null);
        if(logger.isInfoEnabled()) logger.info("LambdaFunctionHandler is loaded.");
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy