Skip to content
Merged
Show file tree
Hide file tree
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
40 changes: 5 additions & 35 deletions aws-serverless-java-container-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,42 +65,12 @@
</dependency>
</dependencies>

<reporting>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>3.0.5</version>
<configuration>
<!--
Enables analysis which takes more memory but finds more bugs.
If you run out of memory, changes the value of the effort element
to 'low'.
-->
<effort>Max</effort>
<!-- Reports all bugs (other values are medium and max) -->
<threshold>Low</threshold>
<!-- Produces XML report -->
<xmlOutput>true</xmlOutput>

<plugins>
<plugin>
<groupId>com.h3xstream.findsecbugs</groupId>
<artifactId>findsecbugs-plugin</artifactId>
<version>1.7.1</version>
</plugin>
</plugins>
</configuration>
</plugin>
</plugins>
</reporting>

<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>findbugs-maven-plugin</artifactId>
<version>3.0.5</version>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-maven-plugin</artifactId>
<version>3.1.1</version>
<configuration>
<!--
Enables analysis which takes more memory but finds more bugs.
Expand All @@ -113,7 +83,7 @@
<!-- Produces XML report -->
<xmlOutput>true</xmlOutput>
<!-- Configures the directory in which the XML report is created -->
<findbugsXmlOutputDirectory>${project.build.directory}/findbugs</findbugsXmlOutputDirectory>
<spotbugsXmlOutputDirectory>${project.build.directory}/spotbugs</spotbugsXmlOutputDirectory>

<plugins>
<plugin>
Expand All @@ -125,7 +95,7 @@
</configuration>
<executions>
<!--
Ensures that FindBugs inspects source code when project is compiled.
Ensures that SpotBug inspects source code when project is compiled.
-->
<execution>
<id>analyze-compile</id>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.amazonaws.serverless.proxy;


import javax.ws.rs.core.SecurityContext;


/**
* Implementations of the log formatter interface are used by {@link com.amazonaws.serverless.proxy.internal.LambdaContainerHandler} class to log each request
* processed in the container. You can set the log formatter using the {@link com.amazonaws.serverless.proxy.internal.LambdaContainerHandler#setLogFormatter(LogFormatter)}
* method. The servlet implementation of the container ({@link com.amazonaws.serverless.proxy.internal.servlet.AwsLambdaServletContainerHandler} includes a
* default log formatter that produces Apache combined logs. {@link com.amazonaws.serverless.proxy.internal.servlet.ApacheCombinedServletLogFormatter}.
* @param <ContainerRequestType> The request type used by the underlying framework
* @param <ContainerResponseType> The response type produced by the underlying framework
*/
public interface LogFormatter<ContainerRequestType, ContainerResponseType> {
/**
* The format method is called by the container handler to produce the log line that should be written to the logs.
* @param req The incoming request
* @param res The completed response
* @param ctx The security context produced based on the request
* @return The log line
*/
String format(ContainerRequestType req, ContainerResponseType res, SecurityContext ctx);
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
package com.amazonaws.serverless.proxy.internal;


import com.amazonaws.serverless.proxy.LogFormatter;
import com.amazonaws.serverless.proxy.internal.servlet.ApacheCombinedServletLogFormatter;
import com.amazonaws.serverless.proxy.model.ContainerConfig;
import com.amazonaws.serverless.proxy.ExceptionHandler;
import com.amazonaws.serverless.proxy.RequestReader;
Expand Down Expand Up @@ -56,10 +58,12 @@ public abstract class LambdaContainerHandler<RequestType, ResponseType, Containe
private ExceptionHandler<ResponseType> exceptionHandler;

protected Context lambdaContext;
protected LogFormatter<ContainerRequestType, ContainerResponseType> logFormatter;

private Logger log = LoggerFactory.getLogger(LambdaContainerHandler.class);



//-------------------------------------------------------------
// Variables - Private - Static
//-------------------------------------------------------------
Expand Down Expand Up @@ -119,6 +123,15 @@ public void stripBasePath(String basePath) {
config.setServiceBasePath(basePath);
}

/**
* Sets the formatter used to log request data in CloudWatch. By default this is set to use an Apache
* combined log format based on the servlet request and response object {@link ApacheCombinedServletLogFormatter}.
* @param formatter The log formatter object
*/
public void setLogFormatter(LogFormatter<ContainerRequestType, ContainerResponseType> formatter) {
this.logFormatter = formatter;
}


/**
* Proxies requests to the underlying container given the incoming Lambda request. This method returns a populated
Expand All @@ -140,6 +153,10 @@ public ResponseType proxy(RequestType request, Context context) {

latch.await();

if (logFormatter != null) {
log.info(SecurityUtils.crlf(logFormatter.format(containerRequest, containerResponse, securityContext)));
}

return responseWriter.writeResponse(containerResponse, context);
} catch (Exception e) {
log.error("Error while handling request", e);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package com.amazonaws.serverless.proxy.internal.servlet;


import com.amazonaws.serverless.proxy.LogFormatter;
import com.amazonaws.serverless.proxy.model.ApiGatewayRequestContext;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.SecurityContext;

import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;

import static com.amazonaws.serverless.proxy.RequestReader.API_GATEWAY_CONTEXT_PROPERTY;


/**
* Default implementation of the log formatter. Based on an <code>HttpServletRequest</code> and <code>HttpServletResponse</code> implementations produced
* a log line in the Apache combined log format: https://httpd.apache.org/docs/2.4/logs.html
* @param <ContainerRequestType> An implementation of <code>HttpServletRequest</code>
* @param <ContainerResponseType> An implementation of <code>HttpServletResponse</code>
*/
public class ApacheCombinedServletLogFormatter<ContainerRequestType extends HttpServletRequest, ContainerResponseType extends HttpServletResponse>
implements LogFormatter<ContainerRequestType, ContainerResponseType> {
SimpleDateFormat dateFormat;

public ApacheCombinedServletLogFormatter() {
dateFormat = new SimpleDateFormat("[dd/MM/yyyy:hh:mm:ss Z]");
}

@Override
@SuppressFBWarnings({ "SERVLET_HEADER_REFERER", "SERVLET_HEADER_USER_AGENT" })
public String format(ContainerRequestType servletRequest, ContainerResponseType servletResponse, SecurityContext ctx) {
//LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" combined
StringBuilder logLineBuilder = new StringBuilder();
ApiGatewayRequestContext gatewayContext = (ApiGatewayRequestContext)servletRequest.getAttribute(API_GATEWAY_CONTEXT_PROPERTY);

// %h
logLineBuilder.append(servletRequest.getRemoteAddr());
logLineBuilder.append(" ");

// %l
if (gatewayContext != null) {
if (gatewayContext.getIdentity().getUserArn() != null) {
logLineBuilder.append(gatewayContext.getIdentity().getUserArn());
} else {
logLineBuilder.append("-");
}
} else {
logLineBuilder.append("-");
}
logLineBuilder.append(" ");

// %u
if (ctx != null && ctx.getUserPrincipal().getName() != null) {
logLineBuilder.append(ctx.getUserPrincipal().getName());
logLineBuilder.append(" ");
}


// %t
if (gatewayContext != null) {
logLineBuilder.append(dateFormat.format(Date.from(Instant.ofEpochMilli(gatewayContext.getRequestTimeEpoch()))));
} else {
logLineBuilder.append(dateFormat.format(Calendar.getInstance().getTime()));
}
logLineBuilder.append(" ");

// %r
logLineBuilder.append("\"");
logLineBuilder.append(servletRequest.getMethod().toUpperCase(Locale.ENGLISH));
logLineBuilder.append(" ");
logLineBuilder.append(servletRequest.getPathInfo());
logLineBuilder.append(" ");
logLineBuilder.append(servletRequest.getProtocol());
logLineBuilder.append("\" ");

// %>s
logLineBuilder.append(servletResponse.getStatus());
logLineBuilder.append(" ");

// %b
if (servletResponse instanceof AwsHttpServletResponse) {
AwsHttpServletResponse awsResponse = (AwsHttpServletResponse)servletResponse;
if (awsResponse.getAwsResponseBodyBytes().length > 0) {
logLineBuilder.append(awsResponse.getAwsResponseBodyBytes().length);
} else {
logLineBuilder.append("-");
}
} else {
logLineBuilder.append("-");
}
logLineBuilder.append(" ");

// \"%{Referer}i\"
logLineBuilder.append("\"");
if (servletRequest.getHeader("referer") != null) {
logLineBuilder.append(servletRequest.getHeader("referer"));
} else {
logLineBuilder.append("-");
}
logLineBuilder.append("\" ");

// \"%{User-agent}i\"
logLineBuilder.append("\"");
if (servletRequest.getHeader("user-agent") != null) {
logLineBuilder.append(servletRequest.getHeader("user-agent"));
} else {
logLineBuilder.append("-");
}
logLineBuilder.append("\" ");

logLineBuilder.append("combined");


return logLineBuilder.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,13 @@ public abstract class AwsHttpServletRequest implements HttpServletRequest {

@Override
public String getRequestedSessionId() {
throw new UnsupportedOperationException();
return null;
}


@Override
public HttpSession getSession(boolean b) {
log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session");
log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session");
if (b && null == this.session) {
ApiGatewayRequestContext requestContext = (ApiGatewayRequestContext) getAttribute(RequestReader.API_GATEWAY_CONTEXT_PROPERTY);
this.session = new AwsHttpSession(requestContext.getRequestId());
Expand All @@ -118,43 +118,43 @@ public HttpSession getSession(boolean b) {

@Override
public HttpSession getSession() {
log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session");
log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session");
return this.session;
}


@Override
public String changeSessionId() {
log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session");
log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session");
return null;
}


@Override
public boolean isRequestedSessionIdValid() {
log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session");
log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session");
return false;
}


@Override
public boolean isRequestedSessionIdFromCookie() {
log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session");
log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session");
return false;
}


@Override
public boolean isRequestedSessionIdFromURL() {
log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session");
log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session");
return false;
}


@Override
@Deprecated
public boolean isRequestedSessionIdFromUrl() {
log.warn("Trying to access session. Lambda functions are stateless and should not rely on the session");
log.debug("Trying to access session. Lambda functions are stateless and should not rely on the session");
return false;
}

Expand Down Expand Up @@ -275,7 +275,7 @@ protected Cookie[] parseCookieHeaderValue(String headerValue) {

return parsedHeaders.stream()
.filter(e -> e.getKey() != null)
.map(e -> new Cookie(e.getKey(), e.getValue()))
.map(e -> new Cookie(SecurityUtils.crlf(e.getKey()), SecurityUtils.crlf(e.getValue())))
.toArray(Cookie[]::new);
}

Expand Down Expand Up @@ -304,7 +304,7 @@ protected String generateQueryString(Map<String, String> parameters) {
newValue = URLEncoder.encode(newValue, StandardCharsets.UTF_8.name());
}
} catch (UnsupportedEncodingException e) {
log.error("Could not URLEncode: " + newKey, e);
log.error(SecurityUtils.crlf("Could not URLEncode: " + newKey), e);

}
return newKey + "=" + newValue;
Expand Down
Loading