diff --git a/.github/workflows/maven-build-all.yml b/.github/workflows/maven-build-all.yml
index 67c4a611..afe75d98 100644
--- a/.github/workflows/maven-build-all.yml
+++ b/.github/workflows/maven-build-all.yml
@@ -31,3 +31,7 @@ jobs:
run: mvn -B package --file aws-lambda-java-events-sdk-transformer/pom.xml
- name: Build log4j2 with Maven
run: mvn -B package --file aws-lambda-java-log4j2/pom.xml
+ - name: Build serialization with Maven
+ run: mvn -B package --file aws-lambda-java-serialization/pom.xml
+ - name: Build runtime-interface-client with Maven
+ run: mvn -B package --file aws-lambda-java-runtime-interface-client/pom.xml
diff --git a/.gitignore b/.gitignore
index e7133935..fbdc4bbe 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,12 @@ hs_err_pid*
# Maven build
target/
+dependency-reduced-pom.xml
# IDEA internal
*.iml
.idea
+.gradle
+.settings
+.classpath
+.project
\ No newline at end of file
diff --git a/README.md b/README.md
index 57e03ea1..ba4cf48e 100644
--- a/README.md
+++ b/README.md
@@ -19,6 +19,8 @@ Check out the per-module release notes:
- [aws-lambda-java-events](aws-lambda-java-events/RELEASE.CHANGELOG.md)
- [aws-lambda-java-events-sdk-transformer](aws-lambda-java-events-sdk-transformer/RELEASE.CHANGELOG.md)
- [aws-lambda-java-log4j2](aws-lambda-java-log4j2/RELEASE.CHANGELOG.md)
+- [aws-lambda-java-runtime-interface-client](aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md)
+- [aws-lambda-java-serialization](aws-lambda-java-serialization/RELEASE.CHANGELOG.md)
# Where to get packages
___
@@ -34,45 +36,53 @@ ___
com.amazonaws
aws-lambda-java-events
- 3.4.0
+ 3.6.0
com.amazonaws
aws-lambda-java-events-sdk-transformer
- 2.0.6
+ 2.0.8
com.amazonaws
aws-lambda-java-log4j2
1.2.0
+
+ com.amazonaws
+ aws-lambda-java-runtime-interface-client
+ 1.0.0
+
```
[Gradle](https://gradle.org)
```groovy
'com.amazonaws:aws-lambda-java-core:1.2.1'
-'com.amazonaws:aws-lambda-java-events:3.4.0'
-'com.amazonaws:aws-lambda-java-events-sdk-transformer:2.0.6'
+'com.amazonaws:aws-lambda-java-events:3.6.0'
+'com.amazonaws:aws-lambda-java-events-sdk-transformer:2.0.8'
'com.amazonaws:aws-lambda-java-log4j2:1.2.0'
+'com.amazonaws:aws-lambda-java-runtime-interface-client:1.0.0'
```
[Leiningen](http://leiningen.org) and [Boot](http://boot-clj.com)
```clojure
[com.amazonaws/aws-lambda-java-core "1.2.1"]
-[com.amazonaws/aws-lambda-java-events "3.4.0"]
-[com.amazonaws/aws-lambda-java-events-sdk-transformer "2.0.6"]
+[com.amazonaws/aws-lambda-java-events "3.6.0"]
+[com.amazonaws/aws-lambda-java-events-sdk-transformer "2.0.8"]
[com.amazonaws/aws-lambda-java-log4j2 "1.2.0"]
+[com.amazonaws/aws-lambda-java-runtime-interface-client "1.0.0"]
```
[sbt](http://www.scala-sbt.org)
```scala
"com.amazonaws" % "aws-lambda-java-core" % "1.2.1"
-"com.amazonaws" % "aws-lambda-java-events" % "3.4.0"
-"com.amazonaws" % "aws-lambda-java-events-sdk-transformer" % "2.0.6"
+"com.amazonaws" % "aws-lambda-java-events" % "3.6.0"
+"com.amazonaws" % "aws-lambda-java-events-sdk-transformer" % "2.0.8"
"com.amazonaws" % "aws-lambda-java-log4j2" % "1.2.0"
+"com.amazonaws" % "aws-lambda-java-runtime-interface-client" % "1.0.0"
```
# Using aws-lambda-java-core
@@ -95,3 +105,12 @@ See the [documentation](aws-lambda-java-events-sdk-transformer/README.md) for mo
This package defines the Lambda adapter to use with log4j version 2.
See the [README](aws-lambda-java-log4j2/README.md) or the [official documentation](http://docs.aws.amazon.com/lambda/latest/dg/java-logging.html#java-wt-logging-using-log4j) for information on how to use the adapter.
+
+# Using aws-lambda-java-runtime-interface-client
+
+This package defines the Lambda Java Runtime Interface Client package, a Lambda Runtime component that starts the runtime and interacts with the Runtime API - i.e., it calls the API for invocation events, starts the function code, calls the API to return the response.
+The purpose of this package is to allow developers to deploy their applications in Lambda under the form of Container Images. See the [README](aws-lambda-java-runtime-interface-client/README.md) for information on how to use the library.
+
+# Using aws-lambda-java-serialization
+
+This package defines the Lambda serialization logic using in the aws-lambda-java-runtime-client library. It has no current standalone usage.
diff --git a/aws-lambda-java-runtime-interface-client/.gitignore b/aws-lambda-java-runtime-interface-client/.gitignore
new file mode 100644
index 00000000..73a7f328
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/.gitignore
@@ -0,0 +1 @@
+compile-flags.txt
diff --git a/aws-lambda-java-runtime-interface-client/Makefile b/aws-lambda-java-runtime-interface-client/Makefile
new file mode 100644
index 00000000..a88954e9
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/Makefile
@@ -0,0 +1,44 @@
+.PHONY: target
+target:
+ $(info ${HELP_MESSAGE})
+ @exit 0
+
+.PHONY: test
+test:
+ mvn test
+
+.PHONY: setup-codebuild-agent
+setup-codebuild-agent:
+ docker build -t codebuild-agent - < test/integration/codebuild-local/Dockerfile.agent
+
+.PHONY: test-smoke
+test-smoke: setup-codebuild-agent
+ CODEBUILD_IMAGE_TAG=codebuild-agent test/integration/codebuild-local/test_one.sh test/integration/codebuild/buildspec.os.alpine.yml alpine 3.12 corretto11
+
+.PHONY: test-integ
+test-integ: setup-codebuild-agent
+ CODEBUILD_IMAGE_TAG=codebuild-agent test/integration/codebuild-local/test_all.sh test/integration/codebuild
+
+# Command to run everytime you make changes to verify everything works
+.PHONY: dev
+dev: test
+
+# Verifications to run before sending a pull request
+.PHONY: pr
+pr: test test-smoke
+
+.PHONY: build
+build:
+ mvn clean install
+
+define HELP_MESSAGE
+
+Usage: $ make [TARGETS]
+
+TARGETS
+ build Builds the package.
+ dev Run all development tests after a change.
+ pr Perform all checks before submitting a Pull Request.
+ test Run the Unit tests.
+
+endef
diff --git a/aws-lambda-java-runtime-interface-client/README.md b/aws-lambda-java-runtime-interface-client/README.md
new file mode 100644
index 00000000..04bdb9e9
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/README.md
@@ -0,0 +1,131 @@
+## AWS Lambda Java Runtime Interface Client
+
+We have open-sourced a set of software packages, Runtime Interface Clients (RIC), that implement the Lambda
+ [Runtime API](https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html), allowing you to seamlessly extend your preferred
+ base images to be Lambda compatible.
+The Lambda Runtime Interface Client is a lightweight interface that allows your runtime to receive requests from and send requests to the Lambda service.
+
+You can include this package in your preferred base image to make that base image Lambda compatible.
+
+## Usage
+
+### Creating a Docker Image for Lambda with the Runtime Interface Client
+
+Choose a preferred base image. The Runtime Interface Client is tested on Amazon Linux, Alpine, Ubuntu, Debian, and CentOS. The requirements are that the image is:
+
+* built for x86_64
+* contains Java >= 8
+* contains glibc >= 2.12 or musl
+
+### Example
+
+The Runtime Interface Client library can be installed into the image separate from the function code, but the simplest approach to keeping the Dockerfile simple is to include the library as a part of the function's dependencies!
+
+Dockerfile
+```dockerfile
+# we'll use Amazon Linux 2 + Corretto 11 as our base
+FROM amazoncorretto:11 as base
+
+# configure the build environment
+FROM base as build
+RUN yum install -y maven
+WORKDIR /src
+
+# cache and copy dependencies
+ADD pom.xml .
+RUN mvn dependency:go-offline dependency:copy-dependencies
+
+# compile the function
+ADD . .
+RUN mvn package
+
+# copy the function artifact and dependencies onto a clean base
+FROM base
+WORKDIR /function
+
+COPY --from=build /src/target/dependency/*.jar ./
+COPY --from=build /src/target/*.jar ./
+
+# configure the runtime startup as main
+ENTRYPOINT [ "java", "-cp", "./*", "com.amazonaws.services.lambda.runtime.api.client.AWSLambda" ]
+# pass the name of the function handler as an argument to the runtime
+CMD [ "example.App::sayHello" ]
+```
+pom.xml
+```xml
+
+ 4.0.0
+ example
+ hello-lambda
+ jar
+ 1.0-SNAPSHOT
+ hello-lambda
+ http://maven.apache.org
+
+ 1.8
+ 1.8
+
+
+
+ com.amazonaws
+ aws-lambda-java-runtime-interface-client
+ 1.0.0
+
+
+
+```
+src/main/java/example/App.java
+```java
+package example;
+
+public class App {
+ public static String sayHello() {
+ return "Hello λ!";
+ }
+}
+```
+
+### Local Testing
+
+To make it easy to locally test Lambda functions packaged as container images we open-sourced a lightweight web-server, Lambda Runtime Interface Emulator (RIE), which allows your function packaged as a container image to accept HTTP requests. You can install the [AWS Lambda Runtime Interface Emulator](https://github.com/aws/aws-lambda-runtime-interface-emulator) on your local machine to test your function. Then when you run the image function, you set the entrypoint to be the emulator.
+
+*To install the emulator and test your Lambda function*
+
+1) From your project directory, run the following command to download the RIE from GitHub and install it on your local machine.
+
+```shell script
+mkdir -p ~/.aws-lambda-rie && \
+ curl -Lo ~/.aws-lambda-rie/aws-lambda-rie https://github.com/aws/aws-lambda-runtime-interface-emulator/releases/latest/download/aws-lambda-rie && \
+ chmod +x ~/.aws-lambda-rie/aws-lambda-rie
+```
+2) Run your Lambda image function using the docker run command.
+
+```shell script
+docker run -d -v ~/.aws-lambda-rie:/aws-lambda -p 9000:8080 \
+ --entrypoint /aws-lambda/aws-lambda-rie \
+ myfunction:latest \
+ java -cp ./* com.amazonaws.services.lambda.runtime.api.client.AWSLambda example.App::sayHello
+```
+
+This runs the image as a container and starts up an endpoint locally at `http://localhost:9000/2015-03-31/functions/function/invocations`.
+
+3) Post an event to the following endpoint using a curl command:
+
+```shell script
+curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d '{}'.
+```
+
+This command invokes the function running in the container image and returns a response.
+
+*Alternately, you can also include RIE as a part of your base image. See the AWS documentation on how to [Build RIE into your base image](https://docs.aws.amazon.com/lambda/latest/dg/images-test.html#images-test-alternative).*
+
+
+## Security
+
+If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
+
+## License
+
+This project is licensed under the Apache-2.0 License.
+
diff --git a/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md b/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md
new file mode 100644
index 00000000..e9e0c6c0
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md
@@ -0,0 +1,3 @@
+### December 01, 2020
+`1.0.0`:
+- Initial release of AWS Lambda Java Runtime Interface Client
\ No newline at end of file
diff --git a/aws-lambda-java-runtime-interface-client/pom.xml b/aws-lambda-java-runtime-interface-client/pom.xml
new file mode 100644
index 00000000..54d2c06d
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/pom.xml
@@ -0,0 +1,254 @@
+
+ 4.0.0
+
+ com.amazonaws
+ aws-lambda-java-runtime-interface-client
+ 1.0.0
+ jar
+
+ AWS Lambda Java Runtime Interface Client
+
+ The AWS Lambda Java Runtime Interface Client implements the Lambda programming model for Java
+
+ https://aws.amazon.com/lambda/
+
+
+ Apache License, Version 2.0
+ https://aws.amazon.com/apache2.0
+ repo
+
+
+
+ https://github.com/aws/aws-lambda-java-libs.git
+
+
+
+ AWS Lambda team
+ Amazon Web Services
+ https://aws.amazon.com/
+
+
+
+
+ 1.8
+ 1.8
+
+
+
+
+ com.amazonaws
+ aws-lambda-java-core
+ 1.2.0
+
+
+ com.amazonaws
+ aws-lambda-java-serialization
+ 1.0.0
+
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ 5.7.0
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.7.0
+ test
+
+
+
+
+
+
+ com.allogy.maven.wagon
+ maven-s3-wagon
+ 1.2.0
+
+
+
+
+ maven-surefire-plugin
+ 2.22.2
+
+
+ maven-failsafe-plugin
+ 2.22.2
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ 1.7
+
+
+ build-jni-lib
+ prepare-package
+
+ run
+
+
+
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.3.2
+
+ UTF-8
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ com.amazonaws.services.lambda.runtime.api.client.AWSLambda
+ true
+ true
+
+
+
+
+
+ org.jacoco
+ jacoco-maven-plugin
+ 0.8.6
+
+
+ default-prepare-agent
+
+ prepare-agent
+
+
+
+ default-report
+ test
+
+ report
+
+
+
+ default-check
+ test
+
+ check
+
+
+
+
+ PACKAGE
+
+
+ LINE
+ COVEREDRATIO
+ 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ dev
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.9.1
+
+ -Xdoclint:none
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+
+
+
+ release
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 2.9.1
+
+ -Xdoclint:none
+
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.3
+ true
+
+ sonatype-nexus-staging
+ https://aws.oss.sonatype.org/
+ false
+
+
+
+
+
+
+
diff --git a/aws-lambda-java-runtime-interface-client/src/.gitkeep b/aws-lambda-java-runtime-interface-client/src/.gitkeep
new file mode 100644
index 00000000..74a6ce44
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/.gitkeep
@@ -0,0 +1 @@
+Feel free to delete this file as soon as actual Java code is added to this directory.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java
new file mode 100644
index 00000000..cc8abe28
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambda.java
@@ -0,0 +1,309 @@
+//
+// AWSLambda.java
+//
+// Copyright (c) 2013 Amazon. All rights reserved.
+//
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import com.amazonaws.services.lambda.runtime.LambdaLogger;
+import com.amazonaws.services.lambda.runtime.api.client.logging.LambdaContextLogger;
+import com.amazonaws.services.lambda.runtime.api.client.logging.StdOutLogSink;
+import com.amazonaws.services.lambda.runtime.api.client.util.EnvReader;
+import com.amazonaws.services.lambda.runtime.api.client.util.EnvWriter;
+import com.amazonaws.services.lambda.runtime.api.client.util.LambdaOutputStream;
+import com.amazonaws.services.lambda.runtime.api.client.util.UnsafeUtil;
+import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
+import com.amazonaws.services.lambda.runtime.serialization.factories.GsonFactory;
+import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory;
+import com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil;
+import com.amazonaws.services.lambda.runtime.api.client.LambdaRequestHandler.UserFaultHandler;
+import com.amazonaws.services.lambda.runtime.api.client.logging.FramedTelemetryLogSink;
+import com.amazonaws.services.lambda.runtime.api.client.logging.LogSink;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.LambdaRuntimeClient;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.net.URLClassLoader;
+import java.nio.file.Paths;
+import java.security.Security;
+import java.util.Properties;
+
+import static com.amazonaws.services.lambda.runtime.api.client.UserFault.makeUserFault;
+
+/**
+ * The entrypoint of this class is {@link AWSLambda#startRuntime}. It performs two main tasks:
+ *
+ *
+ * 1. loads the user's handler.
+ *
+ * 2. enters the Lambda runtime loop which handles function invocations as defined in the Lambda Custom Runtime API.
+ *
+ *
+ * Once initialized, {@link AWSLambda#startRuntime} will halt only if an irrecoverable error occurs.
+ */
+public class AWSLambda {
+
+ private static final Runnable doNothing = () -> {
+ };
+
+ private static final String TRUST_STORE_PROPERTY = "javax.net.ssl.trustStore";
+
+ private static final String JAVA_SECURITY_PROPERTIES = "java.security.properties";
+
+ private static final String NETWORKADDRESS_CACHE_NEGATIVE_TTL_ENV_VAR = "AWS_LAMBDA_JAVA_NETWORKADDRESS_CACHE_NEGATIVE_TTL";
+
+ private static final String NETWORKADDRESS_CACHE_NEGATIVE_TTL_PROPERTY = "networkaddress.cache.negative.ttl";
+
+ private static final String DEFAULT_NEGATIVE_CACHE_TTL = "1";
+
+ static {
+ // Override the disabledAlgorithms setting to match configuration for openjdk8-u181.
+ // This is to keep DES ciphers around while we deploying security updates.
+ Security.setProperty(
+ "jdk.tls.disabledAlgorithms",
+ "SSLv3, RC4, MD5withRSA, DH keySize < 1024, EC keySize < 224, DES40_CBC, RC4_40, 3DES_EDE_CBC"
+ );
+ // Override the location of the trusted certificate authorities to be provided by the system.
+ // The ca-certificates package provides /etc/pki/java/cacerts which becomes the symlink destination
+ // of $java_home/lib/security/cacerts when java is installed in the chroot. Given that java is provided
+ // in /var/lang as opposed to installed in the chroot, this brings it closer.
+ if(System.getProperty(TRUST_STORE_PROPERTY) == null) {
+ final File systemCacerts = new File("/etc/pki/java/cacerts");
+ if(systemCacerts.exists() && systemCacerts.isFile()) {
+ System.setProperty(TRUST_STORE_PROPERTY, systemCacerts.getPath());
+ }
+ }
+
+ if (isNegativeCacheOverridable()) {
+ String ttlFromEnv = System.getenv(NETWORKADDRESS_CACHE_NEGATIVE_TTL_ENV_VAR);
+ String negativeCacheTtl = ttlFromEnv == null ? DEFAULT_NEGATIVE_CACHE_TTL : ttlFromEnv;
+ Security.setProperty(NETWORKADDRESS_CACHE_NEGATIVE_TTL_PROPERTY, negativeCacheTtl);
+ }
+ }
+
+ private static boolean isNegativeCacheOverridable() {
+ String securityPropertiesPath = System.getProperty(JAVA_SECURITY_PROPERTIES);
+ if (securityPropertiesPath == null) {
+ return true;
+ }
+ try (FileInputStream inputStream = new FileInputStream(securityPropertiesPath)) {
+ Properties secProps = new Properties();
+ secProps.load(inputStream);
+ return !secProps.containsKey(NETWORKADDRESS_CACHE_NEGATIVE_TTL_PROPERTY);
+ } catch (IOException e) {
+ return true;
+ }
+ }
+
+ private static UserMethods findUserMethods(final String handlerString, ClassLoader customerClassLoader) {
+ final HandlerInfo handlerInfo;
+ try {
+ handlerInfo = HandlerInfo.fromString(handlerString, customerClassLoader);
+ } catch (HandlerInfo.InvalidHandlerException e) {
+ UserFault userFault = UserFault.makeUserFault("Invalid handler: `" + handlerString + "'");
+ return new UserMethods(
+ doNothing,
+ new UserFaultHandler(userFault)
+ );
+ } catch (ClassNotFoundException e) {
+ return new UserMethods(doNothing, LambdaRequestHandler.classNotFound(e, HandlerInfo.className(handlerString)));
+ } catch (NoClassDefFoundError e) {
+ return new UserMethods(doNothing, LambdaRequestHandler.initErrorHandler(e, HandlerInfo.className(handlerString)));
+ } catch (Throwable t) {
+ throw UserFault.makeInitErrorUserFault(t, HandlerInfo.className(handlerString));
+ }
+
+ final LambdaRequestHandler requestHandler = EventHandlerLoader.loadEventHandler(handlerInfo);
+ // if loading the handler failed and the failure is fatal (for e.g. the constructor threw an exception)
+ // we want to report this as an init error rather than deferring to the first invoke.
+ if(requestHandler instanceof UserFaultHandler) {
+ UserFault userFault =((UserFaultHandler) requestHandler).fault;
+ if(userFault.fatal) {
+ throw userFault;
+ }
+ }
+
+ Runnable initHandler = doNothing;
+ try {
+ initHandler = wrapInitCall(handlerInfo.clazz.getMethod("init"));
+ } catch (NoSuchMethodException | NoClassDefFoundError e) {
+ }
+
+ return new UserMethods(initHandler, requestHandler);
+ }
+
+ private static Runnable wrapInitCall(final Method method) {
+ return () -> {
+ try {
+ method.invoke(null);
+ } catch (Throwable t) {
+ throw UserFault.makeUserFault(t);
+ }
+ };
+ }
+
+ public static void setupRuntimeLogger(LambdaLogger lambdaLogger)
+ throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
+ ReflectUtil.setStaticField(
+ Class.forName("com.amazonaws.services.lambda.runtime.LambdaRuntime"),
+ "logger",
+ true,
+ lambdaLogger
+ );
+ }
+
+ public static String getEnvOrExit(String envVariableName) {
+ String value = System.getenv(envVariableName);
+ if(value == null) {
+ System.err.println("Could not get environment variable " + envVariableName);
+ System.exit(-1);
+ }
+ return value;
+ }
+
+ protected static URLClassLoader customerClassLoader;
+
+ private static LogSink createLogSink() {
+ final String fd = System.getenv("_LAMBDA_TELEMETRY_LOG_FD");
+ if(fd == null) {
+ return new StdOutLogSink();
+ }
+
+ try {
+ File pipeFdFile = Paths.get("/proc", "self", "fd", fd).toFile();
+ return new FramedTelemetryLogSink(pipeFdFile);
+ } catch (IOException e) {
+ return new StdOutLogSink();
+ }
+ }
+
+ public static void main(String[] args) {
+ // TODO validate arguments, show usage
+ startRuntime(args[0]);
+ }
+
+ private static void startRuntime(String handler) {
+ try (LogSink logSink = createLogSink()) {
+ startRuntime(handler, new LambdaContextLogger(logSink));
+ } catch (Throwable t) {
+ throw new Error(t);
+ }
+ }
+
+ private static void startRuntime(String handler, LambdaLogger lambdaLogger) throws Throwable {
+ UnsafeUtil.disableIllegalAccessWarning();
+
+ System.setOut(new PrintStream(new LambdaOutputStream(System.out), false, "UTF-8"));
+ System.setErr(new PrintStream(new LambdaOutputStream(System.err), false, "UTF-8"));
+ setupRuntimeLogger(lambdaLogger);
+
+ String runtimeApi = getEnvOrExit("AWS_LAMBDA_RUNTIME_API");
+ LambdaRuntimeClient runtimeClient = new LambdaRuntimeClient(runtimeApi);
+
+ EnvReader envReader = new EnvReader();
+ try (EnvWriter envWriter = new EnvWriter(envReader)) {
+ envWriter.unsetLambdaInternalEnv();
+ envWriter.setupEnvironmentCredentials();
+ envWriter.setupAwsExecutionEnv();
+ }
+
+ String taskRoot = System.getProperty("user.dir");
+ String libRoot = "/opt/java";
+ // Make system classloader the customer classloader's parent to ensure any aws-lambda-java-core classes
+ // are loaded from the system classloader.
+ customerClassLoader = new CustomerClassLoader(taskRoot, libRoot, ClassLoader.getSystemClassLoader());
+ Thread.currentThread().setContextClassLoader(customerClassLoader);
+
+ // Load the user's handler
+ UserMethods methods;
+ try {
+ methods = findUserMethods(handler, customerClassLoader);
+ } catch (UserFault userFault) {
+ lambdaLogger.log(userFault.reportableError());
+ ByteArrayOutputStream payload = new ByteArrayOutputStream(1024);
+ Failure failure = new Failure(userFault);
+ GsonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload);
+ runtimeClient.postInitError(payload.toByteArray(), failure.getErrorType());
+ System.exit(1);
+ return;
+ }
+
+ // Call the user's init handler(a function called 'init'), if provided in the same module as the request handler.
+ // This is an undocumented feature, and still exists to keep backward compatibility. Continue if this call fails.
+ try {
+ methods.initHandler.run();
+ } catch (UserFault f) {
+ lambdaLogger.log(f.reportableError());
+ }
+
+ try (EnvWriter envWriter = new EnvWriter(envReader)) {
+ boolean shouldExit = false;
+ while (!shouldExit) {
+ UserFault userFault = null;
+ InvocationRequest request = runtimeClient.waitForNextInvocation();
+ if (request.getXrayTraceId() != null) {
+ envWriter.modifyEnv(m -> m.put("_X_AMZN_TRACE_ID", request.getXrayTraceId()));
+ } else {
+ envWriter.modifyEnv(m -> m.remove("_X_AMZN_TRACE_ID"));
+ }
+
+ ByteArrayOutputStream payload;
+ try {
+ payload = methods.requestHandler.call(request);
+ // TODO calling payload.toByteArray() creates a new copy of the underlying buffer
+ runtimeClient.postInvocationResponse(request.getId(), payload.toByteArray());
+ } catch (UserFault f) {
+ userFault = f;
+ UserFault.filterStackTrace(f);
+ payload = new ByteArrayOutputStream(1024);
+ Failure failure = new Failure(f);
+ GsonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload);
+ shouldExit = f.fatal;
+ runtimeClient.postInvocationError(request.getId(), payload.toByteArray(), failure.getErrorType());
+ } catch (Throwable t) {
+ UserFault.filterStackTrace(t);
+ userFault = UserFault.makeUserFault(t);
+ payload = new ByteArrayOutputStream(1024);
+ Failure failure = new Failure(t);
+ GsonFactory.getInstance().getSerializer(Failure.class).toJson(failure, payload);
+ // These two categories of errors are considered fatal.
+ shouldExit = Failure.isInvokeFailureFatal(t);
+ runtimeClient.postInvocationError(request.getId(), payload.toByteArray(), failure.getErrorType(),
+ serializeAsXRayJson(t));
+ } finally {
+ if (userFault != null) {
+ lambdaLogger.log(userFault.reportableError());
+ }
+ }
+ }
+ }
+ }
+
+ private static PojoSerializer xRayErrorCauseSerializer;
+
+ /**
+ *
+ * @param throwable throwable to convert
+ * @return json as string expected by XRay's web console. On conversion failure, returns null.
+ */
+ private static String serializeAsXRayJson(Throwable throwable) {
+ try {
+ final OutputStream outputStream = new ByteArrayOutputStream();
+ final XRayErrorCause cause = new XRayErrorCause(throwable);
+ if(xRayErrorCauseSerializer == null) {
+ xRayErrorCauseSerializer = JacksonFactory.getInstance().getSerializer(XRayErrorCause.class);
+ }
+ xRayErrorCauseSerializer.toJson(cause, outputStream);
+ return outputStream.toString();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoader.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoader.java
new file mode 100644
index 00000000..69845cbe
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/ClasspathLoader.java
@@ -0,0 +1,91 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * This class loads all of the classes that are in jars on the classpath.
+ *
+ * It is used to generate a class list and Application CDS archive that includes all the possible classes that could be
+ * loaded by the runtime. This simplifies the process of generating the Application CDS archive.
+ */
+public class ClasspathLoader {
+
+ private static final Set BLACKLIST = new HashSet<>();
+ private static final ClassLoader SYSTEM_CLASS_LOADER = ClassLoader.getSystemClassLoader();
+ private static final int CLASS_SUFFIX_LEN = ".class".length();
+
+ static {
+ // NativeClient loads a native library and crashes if loaded here so just exclude it
+ BLACKLIST.add("lambdainternal.runtimeapi.NativeClient");
+ }
+
+ private static String pathToClassName(final String path) {
+ return path.substring(0, path.length() - CLASS_SUFFIX_LEN).replaceAll("/|\\\\", "\\.");
+ }
+
+ private static void loadClass(String name) {
+ try {
+ Class.forName(name, true, SYSTEM_CLASS_LOADER);
+ } catch (ClassNotFoundException e) {
+ System.err.println("[WARN] Failed to load " + name + ": " + e.getMessage());
+ }
+ }
+
+ private static void loadClassesInJar(File file) throws IOException {
+ JarFile jar = new JarFile(file);
+ Enumeration en = jar.entries();
+ while (en.hasMoreElements()) {
+ JarEntry entry = en.nextElement();
+
+ if(!entry.getName().endsWith(".class")) {
+ continue;
+ }
+
+ String name = pathToClassName(entry.getName());
+
+ if(BLACKLIST.contains(name)) {
+ continue;
+ }
+
+ loadClass(name);
+ }
+ }
+
+ private static void loadClassesInClasspathEntry(String entry) throws IOException {
+ File file = new File(entry);
+
+ if(!file.exists()) {
+ throw new FileNotFoundException("Classpath entry does not exist: " + file.getPath());
+ }
+
+ if(file.isDirectory() || !file.getPath().endsWith(".jar")) {
+ System.err.println("[WARN] Only jar classpath entries are supported. Skipping " + file.getPath());
+ return;
+ }
+
+ loadClassesInJar(file);
+ }
+
+ private static void loadAllClasses() throws IOException {
+ final String classPath = System.getProperty("java.class.path");
+ if(classPath == null) {
+ return;
+ }
+ for(String classPathEntry : classPath.split(File.pathSeparator)) {
+ loadClassesInClasspathEntry(classPathEntry);
+ }
+ }
+
+ public static void main(String[] args) throws IOException {
+ loadAllClasses();
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/CustomerClassLoader.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/CustomerClassLoader.java
new file mode 100644
index 00000000..3932547d
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/CustomerClassLoader.java
@@ -0,0 +1,54 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.List;
+
+class CustomerClassLoader extends URLClassLoader {
+ /**
+ * This Comparator is used to ensure that jars added to this classloader are added in a deterministic order which
+ * does not depend on the underlying filesystem.
+ */
+ private final static Comparator LEXICAL_SORT_ORDER = Comparator.comparing(String::toString);
+
+ CustomerClassLoader(String taskRoot, String optRoot, ClassLoader parent) throws IOException {
+ super(getUrls(taskRoot, optRoot), parent);
+ }
+
+ private static URL[] getUrls(String taskRoot, String optRoot) throws MalformedURLException {
+ File taskDir = new File(taskRoot + "/");
+ List res = new ArrayList<>();
+ res.add(newURL(taskDir, ""));
+ appendJars(new File(taskRoot + "/lib"), res);
+ appendJars(new File(optRoot + "/lib"), res);
+ return res.toArray(new URL[res.size()]);
+ }
+
+ private static void appendJars(File dir, List result) throws MalformedURLException {
+ if (!dir.isDirectory()) {
+ return;
+ }
+ String[] names = dir.list();
+ if (names == null) {
+ return;
+ }
+ Arrays.sort(names, CustomerClassLoader.LEXICAL_SORT_ORDER);
+ for(String path : names) {
+ if(path.endsWith(".jar")) {
+ result.add(newURL(dir, path));
+ }
+ }
+ }
+
+ private static URL newURL(File parent, String path) throws MalformedURLException {
+ return new URL("file", null, -1, parent.getPath() + "/" + path);
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java
new file mode 100644
index 00000000..fc16d43f
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/EventHandlerLoader.java
@@ -0,0 +1,904 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import com.amazonaws.services.lambda.runtime.ClientContext;
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.RequestHandler;
+import com.amazonaws.services.lambda.runtime.RequestStreamHandler;
+import com.amazonaws.services.lambda.runtime.LambdaRuntimeInternal;
+
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaClientContext;
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaCognitoIdentity;
+import com.amazonaws.services.lambda.runtime.api.client.api.LambdaContext;
+import com.amazonaws.services.lambda.runtime.api.client.util.UnsafeUtil;
+import com.amazonaws.services.lambda.runtime.serialization.PojoSerializer;
+import com.amazonaws.services.lambda.runtime.serialization.events.LambdaEventSerializers;
+import com.amazonaws.services.lambda.runtime.serialization.factories.GsonFactory;
+import com.amazonaws.services.lambda.runtime.serialization.factories.JacksonFactory;
+import com.amazonaws.services.lambda.runtime.serialization.util.Functions;
+import com.amazonaws.services.lambda.runtime.serialization.util.ReflectUtil;
+import com.amazonaws.services.lambda.runtime.api.client.LambdaRequestHandler.UserFaultHandler;
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.EnumMap;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Optional;
+
+import static com.amazonaws.services.lambda.runtime.api.client.UserFault.filterStackTrace;
+import static com.amazonaws.services.lambda.runtime.api.client.UserFault.makeUserFault;
+import static com.amazonaws.services.lambda.runtime.api.client.UserFault.trace;
+
+public final class EventHandlerLoader {
+ private static final byte[] _JsonNull = new byte[]{'n', 'u', 'l', 'l'};
+
+ private enum Platform {
+ ANDROID,
+ IOS,
+ UNKNOWN
+ }
+
+ private static final EnumMap>> typeCache = new EnumMap<>(Platform.class);
+
+ private EventHandlerLoader() { }
+
+ /**
+ * returns the appropriate serializer for the class based on platform and whether the class is a supported event
+ * @param platform enum platform
+ * @param type Type of object used
+ * @return PojoSerializer
+ * @see Platform for which platforms are used
+ * @see LambdaEventSerializers for how mixins and modules are added to the serializer
+ */
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ private static PojoSerializer getSerializer(Platform platform, Type type) {
+ // if serializing a Class that is a Lambda supported event, use Jackson with customizations
+ if (type instanceof Class) {
+ Class clazz = ((Class)type);
+ if (LambdaEventSerializers.isLambdaSupportedEvent(clazz.getName())) {
+ return LambdaEventSerializers.serializerFor(clazz, AWSLambda.customerClassLoader);
+ }
+ }
+ // else platform dependent (Android uses GSON but all other platforms use Jackson)
+ switch (platform) {
+ case ANDROID:
+ return GsonFactory.getInstance().getSerializer(type);
+ default:
+ return JacksonFactory.getInstance().getSerializer(type);
+ }
+ }
+
+ private static PojoSerializer getSerializerCached(Platform platform, Type type) {
+ Map> cache = typeCache.get(platform);
+ if (cache == null) {
+ cache = new HashMap<>();
+ typeCache.put(platform, cache);
+ }
+
+ PojoSerializer serializer = cache.get(type);
+ if (serializer == null) {
+ serializer = getSerializer(platform, type);
+ cache.put(type, serializer);
+ }
+
+ return serializer;
+ }
+
+ private static volatile PojoSerializer contextSerializer;
+ private static volatile PojoSerializer cognitoSerializer;
+
+ private static PojoSerializer getContextSerializer() {
+ if (contextSerializer == null) {
+ contextSerializer = GsonFactory.getInstance().getSerializer(LambdaClientContext.class);
+ }
+ return contextSerializer;
+ }
+
+ private static PojoSerializer getCognitoSerializer() {
+ if (cognitoSerializer == null) {
+ cognitoSerializer = GsonFactory.getInstance().getSerializer(LambdaCognitoIdentity.class);
+ }
+ return cognitoSerializer;
+ }
+
+
+ private static Platform getPlatform(Context context) {
+ ClientContext cc = context.getClientContext();
+ if (cc == null) {
+ return Platform.UNKNOWN;
+ }
+
+ Map env = cc.getEnvironment();
+ if (env == null) {
+ return Platform.UNKNOWN;
+ }
+
+ String platform = env.get("platform");
+ if (platform == null) {
+ return Platform.UNKNOWN;
+ }
+
+ if ("Android".equalsIgnoreCase(platform)) {
+ return Platform.ANDROID;
+ } else if ("iPhoneOS".equalsIgnoreCase(platform)) {
+ return Platform.IOS;
+ } else {
+ return Platform.UNKNOWN;
+ }
+ }
+
+ private static boolean isVoid(Type type) {
+ return Void.TYPE.equals(type) || (type instanceof Class) && Void.class.isAssignableFrom((Class>)type);
+ }
+
+ /**
+ * Wraps a RequestHandler as a lower level stream handler using supplied types.
+ * Optional types mean that the input and/or output should be ignored respectiveley
+ */
+ @SuppressWarnings("rawtypes")
+ private static final class PojoHandlerAsStreamHandler implements RequestStreamHandler {
+
+ public RequestHandler innerHandler;
+ public final Optional inputType;
+ public final Optional outputType;
+
+ public PojoHandlerAsStreamHandler(
+ RequestHandler innerHandler,
+ Optional inputType,
+ Optional outputType
+ ) {
+ this.innerHandler = innerHandler;
+ this.inputType = inputType;
+ this.outputType = outputType;
+
+
+ if (inputType.isPresent()) {
+ getSerializerCached(Platform.UNKNOWN, inputType.get());
+ }
+
+ if (outputType.isPresent()) {
+ getSerializerCached(Platform.UNKNOWN, outputType.get());
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
+ throws IOException {
+ final Object input;
+ final Platform platform = getPlatform(context);
+ try {
+ if (inputType.isPresent()) {
+ input = getSerializerCached(platform, inputType.get()).fromJson(inputStream);
+ } else {
+ input = null;
+ }
+ } catch (Throwable t) {
+ throw new RuntimeException("An error occurred during JSON parsing", filterStackTrace(t));
+ }
+
+ final Object output;
+ try {
+ output = innerHandler.handleRequest(input, context);
+ } catch (Throwable t) {
+ throw UnsafeUtil.throwException(filterStackTrace(t));
+ }
+
+ try {
+ if (outputType.isPresent()) {
+ PojoSerializer serializer = getSerializerCached(platform, outputType.get());
+ serializer.toJson(output, outputStream);
+ } else {
+ outputStream.write(_JsonNull);
+ }
+ } catch (Throwable t) {
+ throw new RuntimeException("An error occurred during JSON serialization of response", t);
+ }
+ }
+ }
+
+ /**
+ * Wraps a java.lang.reflect.Method as a POJO RequestHandler
+ */
+ private static final class PojoMethodRequestHandler implements RequestHandler {
+ public final Method m;
+ public final Type pType;
+ public final Object instance;
+ public final boolean needsContext;
+ public final int argSize;
+
+ public PojoMethodRequestHandler(Method m, Type pType, Type rType, Object instance, boolean needsContext) {
+ this.m = m;
+ this.pType = pType;
+ this.instance = instance;
+ this.needsContext = needsContext;
+ this.argSize = (needsContext ? 1 : 0) + (pType != null ? 1 : 0);
+ }
+
+ public static PojoMethodRequestHandler fromMethod(
+ Class> clazz,
+ Method m,
+ Type pType,
+ Type rType,
+ boolean needsContext
+ ) throws Exception {
+ final Object instance;
+ if (Modifier.isStatic(m.getModifiers())) {
+ instance = null;
+ } else {
+ instance = newInstance(getConstructor(clazz));
+ }
+
+ return new PojoMethodRequestHandler(m, pType, rType, instance, needsContext);
+ }
+
+ public static LambdaRequestHandler makeRequestHandler(
+ Class> clazz,
+ Method m,
+ Type pType,
+ Type rType,
+ boolean needsContext
+ ) {
+ try {
+ return wrapPojoHandler(fromMethod(clazz, m, pType, rType, needsContext), pType, rType);
+ } catch (UserFault f) {
+ return new UserFaultHandler(f);
+ } catch (Throwable t) {
+ return new UserFaultHandler(makeUserFault(t));
+ }
+ }
+
+ @Override
+ public Object handleRequest(Object input, Context context) {
+ final Object[] args = new Object[argSize];
+ int idx = 0;
+
+ if (pType != null) {
+ args[idx++] = input;
+ }
+
+ if (this.needsContext) {
+ args[idx++] = context;
+ }
+
+ try {
+ return m.invoke(this.instance, args);
+ } catch (InvocationTargetException e) {
+ if (e.getCause() != null) {
+ throw UnsafeUtil.throwException(filterStackTrace(e.getCause()));
+ } else {
+ throw UnsafeUtil.throwException(filterStackTrace(e));
+ }
+ } catch (Throwable t) {
+ throw UnsafeUtil.throwException(filterStackTrace(t));
+ }
+ }
+ }
+
+ /**
+ * Wraps a java.lang.reflect.Method object as a RequestStreamHandler
+ */
+ private static final class StreamMethodRequestHandler implements RequestStreamHandler {
+ public final Method m;
+ public final Object instance;
+ public final boolean needsInput;
+ public final boolean needsOutput;
+ public final boolean needsContext;
+ public final int argSize;
+
+ public StreamMethodRequestHandler(
+ Method m,
+ Object instance,
+ boolean needsInput,
+ boolean needsOutput,
+ boolean needsContext
+ ) {
+ this.m = m;
+ this.instance = instance;
+ this.needsInput = needsInput;
+ this.needsOutput = needsOutput;
+ this.needsContext = needsContext;
+ this.argSize = (needsInput ? 1 : 0) + (needsOutput ? 1 : 0) + (needsContext ? 1 : 0);
+ }
+
+ public static StreamMethodRequestHandler fromMethod(
+ Class> clazz,
+ Method m,
+ boolean needsInput,
+ boolean needsOutput,
+ boolean needsContext
+ ) throws Exception {
+ if (!isVoid(m.getReturnType())) {
+ System.err.println("Will ignore return type " + m.getReturnType() + " on byte stream handler");
+ }
+ final Object instance = Modifier.isStatic(m.getModifiers())
+ ? null
+ : newInstance(getConstructor(clazz));
+
+ return new StreamMethodRequestHandler(m, instance, needsInput, needsOutput, needsContext);
+ }
+
+ public static LambdaRequestHandler makeRequestHandler(
+ Class> clazz,
+ Method m,
+ boolean needsInput,
+ boolean needsOutput,
+ boolean needsContext
+ ) {
+ try {
+ return wrapRequestStreamHandler(fromMethod(clazz, m, needsInput, needsOutput, needsContext));
+ } catch (UserFault f) {
+ return new UserFaultHandler(f);
+ } catch (Throwable t) {
+ return new UserFaultHandler(makeUserFault(t));
+ }
+ }
+
+ @Override
+ public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context)
+ throws IOException {
+ final Object[] args = new Object[argSize];
+ int idx = 0;
+
+ if (needsInput) {
+ args[idx++] = inputStream;
+ } else {
+ inputStream.close();
+ }
+
+ if (needsOutput) {
+ args[idx++] = outputStream;
+ }
+
+ if (needsContext) {
+ args[idx++] = context;
+ }
+
+ try {
+ m.invoke(this.instance, args);
+ if (!needsOutput) {
+ outputStream.write(_JsonNull);
+ }
+ } catch (InvocationTargetException e) {
+ if (e.getCause() != null) {
+ throw UnsafeUtil.throwException(filterStackTrace(e.getCause()));
+ } else {
+ throw UnsafeUtil.throwException(filterStackTrace(e));
+ }
+ } catch (Throwable t) {
+ throw UnsafeUtil.throwException(filterStackTrace(t));
+ }
+ }
+ }
+
+ public static Constructor getConstructor(Class clazz) throws Exception {
+ final Constructor constructor;
+ try {
+ constructor = clazz.getConstructor();
+ } catch (NoSuchMethodException e) {
+ if (clazz.getEnclosingClass() != null && !Modifier.isStatic(clazz.getModifiers())) {
+ throw new Exception("Class "
+ + clazz.getName()
+ + " cannot be instantiated because it is a non-static inner class");
+ } else {
+ throw new Exception("Class " + clazz.getName() + " has no public zero-argument constructor", e);
+ }
+ }
+ return constructor;
+ }
+
+ public static T newInstance(Constructor extends T> constructor) {
+ try {
+ return constructor.newInstance();
+ } catch (UserFault e) {
+ throw e;
+ } catch (InvocationTargetException e) {
+ throw makeUserFault(e.getCause() == null ? e : e.getCause(), true);
+ } catch (InstantiationException e) {
+ throw UnsafeUtil.throwException(e.getCause() == null ? e : e.getCause());
+ } catch (IllegalAccessException e) {
+ throw UnsafeUtil.throwException(e);
+ }
+ }
+
+ private static final class ClassContext {
+ public final Class> clazz;
+ public final Type[] actualTypeArguments;
+
+ @SuppressWarnings({"rawtypes"})
+ private TypeVariable[] typeParameters;
+
+ public ClassContext(Class> clazz, Type[] actualTypeArguments) {
+ this.clazz = clazz;
+ this.actualTypeArguments = actualTypeArguments;
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ public ClassContext(Class> clazz, ClassContext curContext) {
+ this.typeParameters = clazz.getTypeParameters();
+ if (typeParameters.length == 0 || curContext.actualTypeArguments == null) {
+ this.clazz = clazz;
+ this.actualTypeArguments = null;
+ } else {
+ Type[] types = new Type[typeParameters.length];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = curContext.resolveTypeVariable(typeParameters[i]);
+ }
+
+ this.clazz = clazz;
+ this.actualTypeArguments = types;
+ }
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ public ClassContext(ParameterizedType type, ClassContext curContext) {
+ Type[] types = type.getActualTypeArguments();
+ for (int i = 0; i < types.length; i++) {
+ Type t = types[i];
+ if (t instanceof TypeVariable) {
+ types[i] = curContext.resolveTypeVariable((TypeVariable)t);
+ }
+ }
+
+ Type t = type.getRawType();
+ if (t instanceof Class) {
+ this.clazz = (Class)t;
+ } else if (t instanceof TypeVariable) {
+ this.clazz = (Class)((TypeVariable)t).getGenericDeclaration();
+ } else {
+ throw new RuntimeException("Type " + t + " is of unexpected type " + t.getClass());
+ }
+ this.actualTypeArguments = types;
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ public Type resolveTypeVariable(TypeVariable t) {
+ TypeVariable[] variables = getTypeParameters();
+ for (int i = 0; i < variables.length; i++) {
+ if (t.getName().equals(variables[i].getName())) {
+ return actualTypeArguments == null ? variables[i] : actualTypeArguments[i];
+ }
+ }
+
+ return t;
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ private TypeVariable[] getTypeParameters() {
+ if (typeParameters == null) {
+ typeParameters = clazz.getTypeParameters();
+ }
+ return typeParameters;
+ }
+ }
+
+ /**
+ * perform a breadth-first search for the first parameterized type for iface
+ *
+ * @return null of no type found. Otherwise the type found.
+ */
+ public static Type[] findInterfaceParameters(Class> clazz, Class> iface) {
+ LinkedList clazzes = new LinkedList<>();
+ clazzes.addFirst(new ClassContext(clazz, (Type[])null));
+ while (!clazzes.isEmpty()) {
+ final ClassContext curContext = clazzes.removeLast();
+ Type[] interfaces = curContext.clazz.getGenericInterfaces();
+
+ for (Type type : interfaces) {
+ if (type instanceof ParameterizedType) {
+ ParameterizedType candidate = (ParameterizedType)type;
+ Type rawType = candidate.getRawType();
+ if (!(rawType instanceof Class)) {
+ //should be impossible
+ System.err.println("raw type is not a class: " + rawType);
+ continue;
+ }
+ Class> rawClass = (Class>)rawType;
+ if (iface.isAssignableFrom(rawClass)) {
+ return new ClassContext(candidate, curContext).actualTypeArguments;
+ } else {
+ clazzes.addFirst(new ClassContext(candidate, curContext));
+ }
+ } else if (type instanceof Class) {
+ clazzes.addFirst(new ClassContext((Class>)type, curContext));
+ } else {
+ //should never happen?
+ System.err.println("Unexpected type class " + type.getClass().getName());
+ }
+ }
+
+ final Type superClass = curContext.clazz.getGenericSuperclass();
+ if (superClass instanceof ParameterizedType) {
+ clazzes.addFirst(new ClassContext((ParameterizedType)superClass, curContext));
+ } else if (superClass != null) {
+ clazzes.addFirst(new ClassContext((Class>)superClass, curContext));
+ }
+ }
+ return null;
+ }
+
+
+ @SuppressWarnings({"rawtypes"})
+ public static LambdaRequestHandler wrapRequestHandlerClass(final Class extends RequestHandler> clazz) {
+ Type[] ptypes = findInterfaceParameters(clazz, RequestHandler.class);
+ if (ptypes == null) {
+ return new UserFaultHandler(makeUserFault("Class "
+ + clazz.getName()
+ + " does not implement RequestHandler with concrete type parameters"));
+ }
+ if (ptypes.length != 2) {
+ return new UserFaultHandler(makeUserFault(
+ "Invalid class signature for RequestHandler. Expected two generic types, got " + ptypes.length));
+ }
+
+ for (Type t : ptypes) {
+ if (t instanceof TypeVariable) {
+ Type[] bounds = ((TypeVariable)t).getBounds();
+ boolean foundBound = false;
+ if (bounds != null) {
+ for (Type bound : bounds) {
+ if (!Object.class.equals(bound)) {
+ foundBound = true;
+ break;
+ }
+ }
+ }
+
+ if (!foundBound) {
+ return new UserFaultHandler(makeUserFault("Class " + clazz.getName()
+ + " does not implement RequestHandler with concrete type parameters: parameter "
+ + t + " has no upper bound."));
+ }
+ }
+ }
+
+ final Type pType = ptypes[0];
+ final Type rType = ptypes[1];
+
+ final Constructor extends RequestHandler> constructor;
+ try {
+ constructor = getConstructor(clazz);
+ return wrapPojoHandler(newInstance(constructor), pType, rType);
+ } catch (UserFault f) {
+ return new UserFaultHandler(f);
+ } catch (Throwable e) {
+ return new UserFaultHandler(makeUserFault(e));
+ }
+ }
+
+ public static LambdaRequestHandler wrapRequestStreamHandlerClass(final Class extends RequestStreamHandler> clazz) {
+ final Constructor extends RequestStreamHandler> constructor;
+ try {
+ constructor = getConstructor(clazz);
+ return wrapRequestStreamHandler(newInstance(constructor));
+ } catch (UserFault f) {
+ return new UserFaultHandler(f);
+ } catch (Throwable e) {
+ return new UserFaultHandler(makeUserFault(e));
+ }
+ }
+
+ public static LambdaRequestHandler loadStreamingRequestHandler(Class> clazz) {
+ if (RequestStreamHandler.class.isAssignableFrom(clazz)) {
+ return wrapRequestStreamHandlerClass(clazz.asSubclass(RequestStreamHandler.class));
+ } else if (RequestHandler.class.isAssignableFrom(clazz)) {
+ return wrapRequestHandlerClass(clazz.asSubclass(RequestHandler.class));
+ } else {
+ return new UserFaultHandler(makeUserFault("Class does not implement an appropriate handler interface: "
+ + clazz.getName()));
+ }
+ }
+
+ public static LambdaRequestHandler loadEventHandler(HandlerInfo handlerInfo) {
+ if (handlerInfo.methodName == null) {
+ return loadStreamingRequestHandler(handlerInfo.clazz);
+ } else {
+ return loadEventPojoHandler(handlerInfo);
+ }
+ }
+
+ private static Optional getOneLengthHandler(
+ Class> clazz,
+ Method m,
+ Type pType,
+ Type rType
+ ) {
+ if (InputStream.class.equals(pType)) {
+ return Optional.of(StreamMethodRequestHandler.makeRequestHandler(clazz, m, true, false, false));
+ } else if (OutputStream.class.equals(pType)) {
+ return Optional.of(StreamMethodRequestHandler.makeRequestHandler(clazz, m, false, true, false));
+ } else if (isContext(pType)) {
+ return Optional.of(PojoMethodRequestHandler.makeRequestHandler(clazz, m, null, rType, true));
+ } else {
+ return Optional.of(PojoMethodRequestHandler.makeRequestHandler(clazz, m, pType, rType, false));
+ }
+ }
+
+ private static Optional getTwoLengthHandler(
+ Class> clazz,
+ Method m,
+ Type pType1,
+ Type pType2,
+ Type rType
+ ) {
+ if (OutputStream.class.equals(pType1)) {
+ if (isContext(pType2)) {
+ return Optional.of(StreamMethodRequestHandler.makeRequestHandler(clazz, m, false, true, true));
+ } else {
+ System.err.println(
+ "Ignoring two-argument overload because first argument type is OutputStream and second argument type is not Context");
+ return Optional.empty();
+ }
+ } else if (isContext(pType1)) {
+ System.err.println("Ignoring two-argument overload because first argument type is Context");
+ return Optional.empty();
+ } else if (InputStream.class.equals(pType1)) {
+ if (OutputStream.class.equals(pType2)) {
+ return Optional.of(StreamMethodRequestHandler.makeRequestHandler(clazz, m, true, true, false));
+ } else if (isContext(pType2)) {
+ return Optional.of(StreamMethodRequestHandler.makeRequestHandler(clazz, m, true, false, true));
+ } else {
+ System.err.println("Ignoring two-argument overload because second parameter type, "
+ + ReflectUtil.getRawClass(pType2).getName()
+ + ", is not OutputStream.");
+ return Optional.empty();
+ }
+ } else if (isContext(pType2)) {
+ return Optional.of(PojoMethodRequestHandler.makeRequestHandler(clazz, m, pType1, rType, true));
+ } else {
+ System.err.println("Ignoring two-argument overload because second parameter type is not Context");
+ return Optional.empty();
+ }
+ }
+
+ private static Optional getThreeLengthHandler(
+ Class> clazz,
+ Method m,
+ Type pType1,
+ Type pType2,
+ Type pType3,
+ Type rType
+ ) {
+ if (InputStream.class.equals(pType1) && OutputStream.class.equals(pType2) && isContext(pType3)) {
+ return Optional.of(StreamMethodRequestHandler.makeRequestHandler(clazz, m, true, true, true));
+ } else {
+ System.err.println(
+ "Ignoring three-argument overload because argument signature is not (InputStream, OutputStream, Context");
+ return Optional.empty();
+ }
+ }
+
+ private static Optional getHandlerFromOverload(Class> clazz, Method m) {
+ final Type rType = m.getGenericReturnType();
+ final Type[] pTypes = m.getGenericParameterTypes();
+
+ if (pTypes.length == 0) {
+ return Optional.of(PojoMethodRequestHandler.makeRequestHandler(clazz, m, null, rType, false));
+ } else if (pTypes.length == 1) {
+ return getOneLengthHandler(clazz, m, pTypes[0], rType);
+ } else if (pTypes.length == 2) {
+ return getTwoLengthHandler(clazz, m, pTypes[0], pTypes[1], rType);
+ } else if (pTypes.length == 3) {
+ return getThreeLengthHandler(clazz, m, pTypes[0], pTypes[1], pTypes[2], rType);
+ } else {
+ System.err.println("Ignoring an overload of method "
+ + m.getName()
+ + " because it has too many parameters: Expected at most 3, got "
+ + pTypes.length);
+ return Optional.empty();
+ }
+ }
+
+ private static final boolean isContext(Type t) {
+ return Context.class.equals(t);
+ }
+
+ /**
+ * Returns true if the last type in params is a lambda context object interface (Context).
+ */
+ private static final boolean lastParameterIsContext(Class>[] params) {
+ return params.length != 0 && isContext(params[params.length - 1]);
+ }
+
+ /**
+ * Implement a comparator for Methods. We sort overloaded handler methods using this comparator, and then pick the
+ * lowest sorted method.
+ */
+ private static final Comparator methodPriority = new Comparator() {
+ public int compare(Method lhs, Method rhs) {
+
+ //1. Non bridge methods are preferred over bridge methods.
+ if (! lhs.isBridge() && rhs.isBridge()) {
+ return -1;
+ }
+ else if (!rhs.isBridge() && lhs.isBridge()) {
+ return 1;
+ }
+
+ //2. We prefer longer signatures to shorter signatures. Except we count a method whose last argument is
+ //Context as having 1 more argument than it really does. This is a stupid thing to do, but we
+ //need to keep it for back compat reasons.
+ Class>[] lParams = lhs.getParameterTypes();
+ Class>[] rParams = rhs.getParameterTypes();
+
+ int lParamCompareLength = lParams.length;
+ int rParamCompareLength = rParams.length;
+
+ if (lastParameterIsContext(lParams)) {
+ ++lParamCompareLength;
+ }
+
+ if (lastParameterIsContext(rParams)) {
+ ++rParamCompareLength;
+ }
+
+ return -Integer.compare(lParamCompareLength, rParamCompareLength);
+ }
+ };
+
+ private static LambdaRequestHandler loadEventPojoHandler(HandlerInfo handlerInfo) {
+ Method[] methods;
+ try {
+ methods = handlerInfo.clazz.getMethods();
+ } catch (NoClassDefFoundError e) {
+ return new LambdaRequestHandler.UserFaultHandler(new UserFault(
+ "Error loading method " + handlerInfo.methodName + " on class " + handlerInfo.clazz.getName(),
+ e.getClass().getName(),
+ trace(e)
+ ));
+ }
+ if (methods.length == 0) {
+ final String msg = "Class "
+ + handlerInfo.getClass().getName()
+ + " has no public method named "
+ + handlerInfo.methodName;
+ return new UserFaultHandler(makeUserFault(msg));
+ }
+
+ /*
+ * We support the following signatures
+ * Anything (InputStream, OutputStream, Context)
+ * Anything (InputStream, OutputStream)
+ * Anything (OutputStream, Context)
+ * Anything (InputStream, Context)
+ * Anything (InputStream)
+ * Anything (OutputStream)
+ * Anything (Context)
+ * Anything (AlmostAnything, Context)
+ * Anything (AlmostAnything)
+ * Anything ()
+ *
+ * where AlmostAnything is any type except InputStream, OutputStream, Context
+ * Anything represents any type (primitive, void, or Object)
+ *
+ * prefer methods with longer signatures, add extra weight to those ending with a Context object
+ *
+ */
+
+ int slide = 0;
+
+ for (int i = 0; i < methods.length; i++) {
+ Method m = methods[i];
+ methods[i - slide] = m;
+ if (!m.getName().equals(handlerInfo.methodName)) {
+ slide++;
+ continue;
+ }
+ }
+
+ final int end = methods.length - slide;
+ Arrays.sort(methods, 0, end, methodPriority);
+
+ for (int i = 0; i < end; i++) {
+ Method m = methods[i];
+ Optional result = getHandlerFromOverload(handlerInfo.clazz, m);
+ if (result.isPresent()) {
+ return result.get();
+ } else {
+ continue;
+ }
+ }
+
+ return new UserFaultHandler(makeUserFault("No public method named "
+ + handlerInfo.methodName
+ + " with appropriate method signature found on class "
+ + handlerInfo.clazz.getName()));
+ }
+
+ @SuppressWarnings({"rawtypes"})
+ public static LambdaRequestHandler wrapPojoHandler(RequestHandler instance, Type pType, Type rType) {
+ return wrapRequestStreamHandler(new PojoHandlerAsStreamHandler(instance, Optional.ofNullable(pType),
+ isVoid(rType) ? Optional.empty() : Optional.of(rType)
+ ));
+ }
+
+ public static String exceptionToString(Throwable t) {
+ StringWriter writer = new StringWriter(65536);
+ try (PrintWriter wrapped = new PrintWriter(writer)) {
+ t.printStackTrace(wrapped);
+ }
+ StringBuffer buffer = writer.getBuffer();
+ if (buffer.length() > 262144) {
+ final String extra = " Truncated by Lambda";
+ buffer.delete(262144, buffer.length());
+ buffer.append(extra);
+ }
+
+ return buffer.toString();
+ }
+
+ public static LambdaRequestHandler wrapRequestStreamHandler(final RequestStreamHandler handler) {
+ return new LambdaRequestHandler() {
+ private final ByteArrayOutputStream output = new ByteArrayOutputStream(1024);
+ private Functions.V2 log4jContextPutMethod = null;
+
+ private void safeAddRequestIdToLog4j(String log4jContextClassName,
+ InvocationRequest request, Class contextMapValueClass) {
+ try {
+ Class> log4jContextClass = ReflectUtil.loadClass(AWSLambda.customerClassLoader, log4jContextClassName);
+ log4jContextPutMethod = ReflectUtil.loadStaticV2(log4jContextClass, "put", false, String.class, contextMapValueClass);
+ log4jContextPutMethod.call("AWSRequestId", request.getId());
+ } catch (Exception e) {}
+ }
+
+ public ByteArrayOutputStream call(InvocationRequest request) throws Error, Exception {
+ output.reset();
+
+ LambdaCognitoIdentity cognitoIdentity = null;
+ if(request.getCognitoIdentity() != null && !request.getCognitoIdentity().isEmpty()) {
+ cognitoIdentity = getCognitoSerializer().fromJson(request.getCognitoIdentity());
+ }
+
+ LambdaClientContext clientContext = null;
+ if (request.getClientContext() != null && !request.getClientContext().isEmpty()) {
+ //Use GSON here because it handles immutable types without requiring annotations
+ clientContext = getContextSerializer().fromJson(request.getClientContext());
+ }
+
+ LambdaContext context = new LambdaContext(
+ LambdaEnvironment.MEMORY_LIMIT,
+ request.getDeadlineTimeInMs(),
+ request.getId(),
+ LambdaEnvironment.LOG_GROUP_NAME,
+ LambdaEnvironment.LOG_STREAM_NAME,
+ LambdaEnvironment.FUNCTION_NAME,
+ cognitoIdentity,
+ LambdaEnvironment.FUNCTION_VERSION,
+ request.getInvokedFunctionArn(),
+ clientContext
+ );
+
+ if (LambdaRuntimeInternal.getUseLog4jAppender()) {
+ safeAddRequestIdToLog4j("org.apache.log4j.MDC", request, Object.class);
+ safeAddRequestIdToLog4j("org.apache.logging.log4j.ThreadContext", request, String.class);
+ // if put method not assigned in either call to safeAddRequestIdtoLog4j then log4jContextPutMethod = null
+ if (log4jContextPutMethod == null) {
+ System.err.println("Customer using log4j appender but unable to load either " +
+ "org.apache.log4j.MDC or org.apache.logging.log4j.ThreadContext. " +
+ "Customer cannot see RequestId in log4j log lines.");
+ }
+ }
+
+ handler.handleRequest(request.getContentAsStream(), output, context);
+ return output;
+ }
+ };
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/Failure.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/Failure.java
new file mode 100644
index 00000000..d568d973
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/Failure.java
@@ -0,0 +1,84 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import java.io.IOError;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class Failure {
+
+ private static final Class[] reportableExceptionsArray = {
+ Error.class,
+ ClassNotFoundException.class,
+ IOError.class,
+ Throwable.class,
+ VirtualMachineError.class,
+ LinkageError.class,
+ ExceptionInInitializerError.class,
+ NoClassDefFoundError.class,
+ HandlerInfo.InvalidHandlerException.class
+ };
+
+ private static final List sortedExceptions = Collections.unmodifiableList(
+ Arrays.stream(reportableExceptionsArray).sorted(new ClassHierarchyComparator()).collect(Collectors.toList()));
+
+ private final String errorMessage;
+ private final String errorType;
+ private final String[] stackTrace;
+ private final Failure cause;
+
+ public Failure(Throwable t) {
+ this.errorMessage = t.getLocalizedMessage() == null ? t.getClass().getName() : t.getLocalizedMessage();
+ this.errorType = t.getClass().getName();
+ StackTraceElement[] trace = t.getStackTrace();
+ this.stackTrace = new String[trace.length];
+ for( int i = 0; i < trace.length; i++) {
+ this.stackTrace[i] = trace[i].toString();
+ }
+ Throwable cause = t.getCause();
+ this.cause = (cause == null) ? null : new Failure(cause);
+ }
+
+ public Failure(UserFault userFault) {
+ this.errorMessage = userFault.msg;
+ this.errorType = userFault.exception;
+ // Not setting stacktrace for compatibility with legacy/native runtime
+ this.stackTrace = null;
+ this.cause = null;
+ }
+
+ public static Class getReportableExceptionClass(Throwable customerException) {
+ return sortedExceptions
+ .stream()
+ .filter(e -> e.isAssignableFrom(customerException.getClass()))
+ .findFirst()
+ .orElse(Throwable.class);
+ }
+
+ public static String getReportableExceptionClassName(Throwable f) {
+ return getReportableExceptionClass(f).getName();
+ }
+
+ public static boolean isInvokeFailureFatal(Throwable t) {
+ return t instanceof VirtualMachineError || t instanceof IOError;
+ }
+
+ private static class ClassHierarchyComparator implements Comparator {
+ @Override
+ public int compare(Class o1, Class o2) {
+ if (o1.isAssignableFrom(o2)) {
+ return 1;
+ } else {
+ return -1;
+ }
+ }
+ }
+
+ public String getErrorType() {
+ return errorType;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/HandlerInfo.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/HandlerInfo.java
new file mode 100644
index 00000000..eaca0fd0
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/HandlerInfo.java
@@ -0,0 +1,40 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+public final class HandlerInfo {
+ public static class InvalidHandlerException extends RuntimeException {
+ public static final long serialVersionUID = -1;
+ }
+
+ public final Class> clazz;
+ public final String methodName;
+
+ public HandlerInfo (Class> clazz, String methodName) {
+ this.clazz = clazz;
+ this.methodName = methodName;
+ }
+
+ public static HandlerInfo fromString(String handler, ClassLoader cl) throws ClassNotFoundException, NoClassDefFoundError, InvalidHandlerException {
+ final int colonLoc = handler.lastIndexOf("::");
+ final String className;
+ final String methodName;
+ if(colonLoc < 0) {
+ className = handler;
+ methodName = null;
+ } else {
+ className = handler.substring(0, colonLoc);
+ methodName = handler.substring(colonLoc + 2);
+ }
+
+ if(className.isEmpty() || (methodName != null && methodName.isEmpty())) {
+ throw new InvalidHandlerException();
+ }
+ return new HandlerInfo(Class.forName(className, true, cl), methodName);
+ }
+
+ public static String className(String handler) {
+ final int colonLoc = handler.lastIndexOf("::");
+ return (colonLoc < 0) ? handler : handler.substring(0, colonLoc);
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaEnvironment.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaEnvironment.java
new file mode 100644
index 00000000..8821a113
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaEnvironment.java
@@ -0,0 +1,16 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import com.amazonaws.services.lambda.runtime.api.client.util.EnvReader;
+
+import static java.lang.Integer.parseInt;
+
+public class LambdaEnvironment {
+ public static final EnvReader ENV_READER = new EnvReader();
+ public static final int MEMORY_LIMIT = parseInt(ENV_READER.getEnvOrDefault("AWS_LAMBDA_FUNCTION_MEMORY_SIZE", "128"));
+ public static final String LOG_GROUP_NAME = ENV_READER.getEnv("AWS_LAMBDA_LOG_GROUP_NAME");
+ public static final String LOG_STREAM_NAME = ENV_READER.getEnv("AWS_LAMBDA_LOG_STREAM_NAME");
+ public static final String FUNCTION_NAME = ENV_READER.getEnv("AWS_LAMBDA_FUNCTION_NAME");
+ public static final String FUNCTION_VERSION = ENV_READER.getEnv("AWS_LAMBDA_FUNCTION_VERSION");
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaRequestHandler.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaRequestHandler.java
new file mode 100644
index 00000000..d22d6394
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/LambdaRequestHandler.java
@@ -0,0 +1,31 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import com.amazonaws.services.lambda.runtime.api.client.runtimeapi.InvocationRequest;
+
+import java.io.ByteArrayOutputStream;
+
+public interface LambdaRequestHandler {
+ ByteArrayOutputStream call(InvocationRequest request) throws Error, Exception;
+
+ class UserFaultHandler implements LambdaRequestHandler {
+ public final UserFault fault;
+
+ public UserFaultHandler(UserFault fault) {
+ this.fault = fault;
+ }
+
+ public ByteArrayOutputStream call(InvocationRequest request) {
+ throw fault;
+ }
+ }
+
+ static LambdaRequestHandler initErrorHandler(final Throwable e, String className) {
+ return new UserFaultHandler(UserFault.makeInitErrorUserFault(e, className));
+ }
+
+ static LambdaRequestHandler classNotFound(final Throwable e, String className) {
+ return new UserFaultHandler(UserFault.makeClassNotFoundUserFault(e, className));
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/UserFault.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/UserFault.java
new file mode 100644
index 00000000..73066ad5
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/UserFault.java
@@ -0,0 +1,113 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+public final class UserFault extends RuntimeException {
+ private static final long serialVersionUID = 0;
+
+ public final String msg;
+ public final String exception;
+ public final String trace;
+ public final Boolean fatal;
+
+ private static final String packagePrefix = AWSLambda.class.getPackage().getName();
+
+ public UserFault(String msg, String exception, String trace) {
+ this.msg = msg;
+ this.exception = exception;
+ this.trace = trace;
+ this.fatal = false;
+ }
+
+ public UserFault(String msg, String exception, String trace, Boolean fatal) {
+ this.msg = msg;
+ this.exception = exception;
+ this.trace = trace;
+ this.fatal = fatal;
+ }
+
+ /**
+ * Convenience function to report a fault given an exception. The constructed fault is marked non-fatal.
+ * No more user code should run after a fault.
+ */
+ public static UserFault makeUserFault(Throwable t) {
+ return makeUserFault(t, false);
+ }
+
+ public static UserFault makeUserFault(Throwable t, boolean fatal) {
+ final String msg = t.getLocalizedMessage() == null ? t.getClass().getName() : t.getLocalizedMessage();
+ return new UserFault(msg, t.getClass().getName(), trace(t), fatal);
+ }
+
+ /**
+ * Convenience function to report a fault given a message.
+ * No more user code should run after a fault.
+ */
+ public static UserFault makeUserFault(String msg) {
+ return new UserFault(msg, null, null);
+ }
+
+ /**
+ * Convert a throwable's stack trace to a String
+ */
+ public static String trace(Throwable t) {
+ filterStackTrace(t);
+ StringWriter sw = new StringWriter();
+ t.printStackTrace(new PrintWriter(sw));
+ return sw.toString();
+ }
+
+ /**
+ * remove our runtime code from the stack trace recursively. Returns
+ * the same object for convenience.
+ */
+ public static T filterStackTrace(T t) {
+ StackTraceElement[] trace = t.getStackTrace();
+ for(int i = 0; i < trace.length; i++) {
+ if(trace[i].getClassName().startsWith(packagePrefix)) {
+ StackTraceElement[] newTrace = new StackTraceElement[i];
+ System.arraycopy(trace, 0, newTrace, 0, i);
+ t.setStackTrace(newTrace);
+ break;
+ }
+ }
+
+
+ Throwable cause = t.getCause();
+ if(cause != null) {
+ filterStackTrace(cause);
+ }
+ return t;
+ }
+
+ static UserFault makeInitErrorUserFault(Throwable e, String className) {
+ return new UserFault(
+ "Error loading class " + className + (e.getMessage() == null ? "" : ": " + e.getMessage()),
+ e.getClass().getName(),
+ trace(e),
+ true
+ );
+ }
+
+ static UserFault makeClassNotFoundUserFault(Throwable e, String className) {
+ return new UserFault(
+ "Class not found: " + className,
+ e.getClass().getName(),
+ trace(e),
+ false
+ );
+ }
+
+ public String reportableError() {
+ if(this.exception != null || this.trace != null) {
+ return String.format("%s: %s\n%s\n",
+ this.msg,
+ this.exception == null ? "" : this.exception,
+ this.trace == null ? "" : this.trace);
+ }
+ return String.format("%s\n", this.msg);
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/UserMethods.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/UserMethods.java
new file mode 100644
index 00000000..4012b381
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/UserMethods.java
@@ -0,0 +1,13 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+public final class UserMethods {
+ public final Runnable initHandler;
+ public final LambdaRequestHandler requestHandler;
+
+ public UserMethods(Runnable initHandler, LambdaRequestHandler requestHandler) {
+ this.initHandler = initHandler;
+ this.requestHandler = requestHandler;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCause.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCause.java
new file mode 100644
index 00000000..5a05810e
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/XRayErrorCause.java
@@ -0,0 +1,114 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * helper class for serializing an exception in the format expected by XRay's web console.
+ */
+public class XRayErrorCause {
+ private final String working_directory;
+ private final Collection exceptions;
+ private final Collection paths;
+
+ public XRayErrorCause(Throwable throwable) {
+ working_directory = System.getProperty("user.dir");
+ exceptions = Collections.unmodifiableCollection(Collections.singletonList(new XRayException(throwable)));
+ paths = Collections.unmodifiableCollection(
+ Arrays.stream(throwable.getStackTrace())
+ .map(XRayErrorCause::determineFileName)
+ .collect(Collectors.toSet()));
+ }
+
+ public String getWorking_directory() {
+ return working_directory;
+ }
+
+ public Collection getExceptions() {
+ return exceptions;
+ }
+
+ public Collection getPaths() {
+ return paths;
+ }
+
+ /**
+ * This method provides compatibility between Java 8 and Java 11 in determining the fileName of the class in the
+ * StackTraceElement.
+ *
+ * If the fileName property of the StackTraceElement is null (as it can be for native methods in Java 11), it
+ * constructs it using the className by stripping out the package and appending ".java".
+ */
+ private static String determineFileName(StackTraceElement e) {
+ String fileName = null;
+ if(e.getFileName() != null) {
+ fileName = e.getFileName();
+ }
+ if(fileName == null) {
+ String className = e.getClassName();
+ fileName = className == null ? null : className.substring(className.lastIndexOf('.') + 1) + ".java";
+ }
+ return fileName;
+ }
+
+ public static class XRayException {
+ private final String message;
+ private final String type;
+ private final List stack;
+
+ public XRayException(Throwable throwable) {
+ this.message = throwable.getMessage();
+ this.type = throwable.getClass().getName();
+ this.stack = Arrays.stream(throwable.getStackTrace()).map(this::toStackElement).collect(Collectors.toList());
+ }
+
+ private StackElement toStackElement(StackTraceElement e) {
+ return new StackElement(
+ e.getMethodName(),
+ determineFileName(e),
+ e.getLineNumber());
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public List getStack() {
+ return stack;
+ }
+
+ public static class StackElement {
+ private final String label;
+ private final String path;
+ private final int line;
+
+ private StackElement(String label, String path, int line) {
+ this.label = label;
+ this.path = path;
+ this.line = line;
+ }
+
+ public String getLabel() {
+ return label;
+ }
+
+ public String getPath() {
+ return path;
+ }
+
+ public int getLine() {
+ return line;
+ }
+ }
+ }
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContext.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContext.java
new file mode 100644
index 00000000..8887dfb2
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContext.java
@@ -0,0 +1,27 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.api;
+
+import java.util.Map;
+
+import com.amazonaws.services.lambda.runtime.ClientContext;
+import com.amazonaws.services.lambda.runtime.Client;
+
+public class LambdaClientContext implements ClientContext {
+
+ private LambdaClientContextClient client;
+ private Map custom;
+ private Map env;
+
+ public Client getClient() {
+ return client;
+ }
+
+ public Map getCustom() {
+ return custom;
+ }
+
+ public Map getEnvironment() {
+ return env;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContextClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContextClient.java
new file mode 100644
index 00000000..66e86d3f
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaClientContextClient.java
@@ -0,0 +1,38 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.api;
+
+import com.amazonaws.services.lambda.runtime.Client;
+
+public class LambdaClientContextClient implements Client {
+
+ private String installation_id;
+
+ private String app_title;
+
+ private String app_version_name;
+
+ private String app_version_code;
+
+ private String app_package_name;
+
+ public String getInstallationId() {
+ return installation_id;
+ }
+
+ public String getAppTitle() {
+ return app_title;
+ }
+
+ public String getAppVersionName() {
+ return app_version_name;
+ }
+
+ public String getAppVersionCode() {
+ return app_version_code;
+ }
+
+ public String getAppPackageName() {
+ return app_package_name;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaCognitoIdentity.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaCognitoIdentity.java
new file mode 100644
index 00000000..5b9df4f2
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaCognitoIdentity.java
@@ -0,0 +1,24 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.api;
+
+import com.amazonaws.services.lambda.runtime.CognitoIdentity;
+
+public class LambdaCognitoIdentity implements CognitoIdentity {
+
+ private final String cognitoIdentityId;
+ private final String cognitoIdentityPoolId;
+
+ public LambdaCognitoIdentity(String identityid, String poolid) {
+ this.cognitoIdentityId = identityid;
+ this.cognitoIdentityPoolId = poolid;
+ }
+
+ public String getIdentityId() {
+ return this.cognitoIdentityId;
+ }
+
+ public String getIdentityPoolId() {
+ return this.cognitoIdentityPoolId;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaContext.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaContext.java
new file mode 100644
index 00000000..689c9110
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/api/LambdaContext.java
@@ -0,0 +1,94 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.api;
+
+import com.amazonaws.services.lambda.runtime.ClientContext;
+import com.amazonaws.services.lambda.runtime.CognitoIdentity;
+import com.amazonaws.services.lambda.runtime.Context;
+import com.amazonaws.services.lambda.runtime.LambdaLogger;
+
+public class LambdaContext implements Context {
+
+ private int memoryLimit;
+ private final String awsRequestId;
+ private final String logGroupName;
+ private final String logStreamName;
+ private final String functionName;
+ private final String functionVersion;
+ private final String invokedFunctionArn;
+ private final long deadlineTimeInMs;
+ private final CognitoIdentity cognitoIdentity;
+ private final ClientContext clientContext;
+ private final LambdaLogger logger;
+
+ public LambdaContext(
+ int memoryLimit,
+ long deadlineTimeInMs,
+ String requestId,
+ String logGroupName,
+ String logStreamName,
+ String functionName,
+ CognitoIdentity identity,
+ String functionVersion,
+ String invokedFunctionArn,
+ ClientContext clientContext
+ ) {
+ this.memoryLimit = memoryLimit;
+ this.deadlineTimeInMs = deadlineTimeInMs;
+ this.awsRequestId = requestId;
+ this.logGroupName = logGroupName;
+ this.logStreamName = logStreamName;
+ this.functionName = functionName;
+ this.cognitoIdentity = identity;
+ this.clientContext = clientContext;
+ this.functionVersion = functionVersion;
+ this.invokedFunctionArn = invokedFunctionArn;
+ this.logger = com.amazonaws.services.lambda.runtime.LambdaRuntime.getLogger();
+ }
+
+ public int getMemoryLimitInMB() {
+ return memoryLimit;
+ }
+
+ public String getAwsRequestId() {
+ return awsRequestId;
+ }
+
+ public String getLogGroupName() {
+ return logGroupName;
+ }
+
+ public String getLogStreamName() {
+ return logStreamName;
+ }
+
+ public String getFunctionName() {
+ return functionName;
+ }
+
+ public String getFunctionVersion() {
+ return functionVersion;
+ }
+
+ public String getInvokedFunctionArn() {
+ return invokedFunctionArn;
+ }
+
+ public CognitoIdentity getIdentity() {
+ return cognitoIdentity;
+ }
+
+ public ClientContext getClientContext() {
+ return clientContext;
+ }
+
+ public int getRemainingTimeInMillis() {
+ long now = System.currentTimeMillis();
+ int delta = (int) (this.deadlineTimeInMs - now);
+ return delta > 0 ? delta : 0;
+ }
+
+ public LambdaLogger getLogger() {
+ return logger;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameType.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameType.java
new file mode 100644
index 00000000..1284bc64
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FrameType.java
@@ -0,0 +1,18 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+public enum FrameType {
+
+ LOG(0xa55a0001);
+
+ private final int val;
+
+ FrameType(int val) {
+ this.val = val;
+ }
+
+ public int getValue() {
+ return this.val;
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSink.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSink.java
new file mode 100644
index 00000000..60f1d18f
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/FramedTelemetryLogSink.java
@@ -0,0 +1,69 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * FramedTelemetryLogSink implements the logging contract between runtimes and the platform. It implements a simple
+ * framing protocol so message boundaries can be determined. Each frame can be visualized as follows:
+ *
+ *
+ * {@code
+ * +----------------------+------------------------+-----------------------+
+ * | Frame Type - 4 bytes | Length (len) - 4 bytes | Message - 'len' bytes |
+ * +----------------------+------------------------+-----------------------+
+ * }
+ *
+ *
+ * The first 4 bytes indicate the type of the frame - log frames have a type defined as the hex value 0xa55a0001. The
+ * second 4 bytes should indicate the message's length. The next 'len' bytes contain the message. The byte order is
+ * big-endian.
+ */
+public class FramedTelemetryLogSink implements LogSink {
+
+ private static final int HEADER_LENGTH = 8;
+
+ private final FileOutputStream logOutputStream;
+ private final ByteBuffer headerBuf;
+
+ public FramedTelemetryLogSink(File file) throws IOException {
+ this.logOutputStream = new FileOutputStream(file);
+ this.headerBuf = ByteBuffer.allocate(HEADER_LENGTH).order(ByteOrder.BIG_ENDIAN);
+ }
+
+ @Override
+ public synchronized void log(byte[] message) {
+ try {
+ writeFrame(message);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private void writeFrame(byte[] message) throws IOException {
+ updateHeader(message.length);
+ this.logOutputStream.write(this.headerBuf.array());
+ this.logOutputStream.write(message);
+ }
+
+ /**
+ * Updates the header ByteBuffer with the provided length. The header comprises the frame type and message length.
+ */
+ private void updateHeader(int length) {
+ this.headerBuf.clear();
+ this.headerBuf.putInt(FrameType.LOG.getValue());
+ this.headerBuf.putInt(length);
+ this.headerBuf.flip();
+ }
+
+ @Override
+ public void close() throws IOException {
+ this.logOutputStream.close();
+ }
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java
new file mode 100644
index 00000000..eaa2e3ea
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LambdaContextLogger.java
@@ -0,0 +1,34 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import com.amazonaws.services.lambda.runtime.LambdaLogger;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+public class LambdaContextLogger implements LambdaLogger {
+ // If a null string is passed in, replace it with "null",
+ // replicating the behavior of System.out.println(null);
+ private static final byte[] NULL_BYTES_VALUE = "null".getBytes(UTF_8);
+
+ private final transient LogSink sink;
+
+ public LambdaContextLogger(LogSink sink) {
+ this.sink = sink;
+ }
+
+ public void log(byte[] message) {
+ if (message == null) {
+ message = NULL_BYTES_VALUE;
+ }
+ sink.log(message);
+ }
+
+ public void log(String message) {
+ if (message == null) {
+ this.log(NULL_BYTES_VALUE);
+ } else {
+ this.log(message.getBytes(UTF_8));
+ }
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogSink.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogSink.java
new file mode 100644
index 00000000..11c3f92c
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/LogSink.java
@@ -0,0 +1,11 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import java.io.Closeable;
+
+public interface LogSink extends Closeable {
+
+ void log(byte[] message);
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSink.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSink.java
new file mode 100644
index 00000000..5487c5dd
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/logging/StdOutLogSink.java
@@ -0,0 +1,19 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.logging;
+
+import java.io.IOException;
+
+public class StdOutLogSink implements LogSink {
+ @Override
+ public void log(byte[] message) {
+ try {
+ System.out.write(message);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void close() {}
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest.java
new file mode 100644
index 00000000..c8df04af
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest.java
@@ -0,0 +1,79 @@
+package com.amazonaws.services.lambda.runtime.api.client.runtimeapi;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+/**
+ * An invocation request represents the response of the runtime API's next invocation API.
+ *
+ * Copyright (c) 2019 Amazon. All rights reserved.
+ */
+public class InvocationRequest {
+
+ /**
+ * The Lambda request ID associated with the request.
+ */
+ private String id;
+
+ /**
+ * The X-Ray tracing ID.
+ */
+ private String xrayTraceId;
+
+ /**
+ * The ARN of the Lambda function being invoked.
+ */
+ private String invokedFunctionArn;
+
+ /**
+ * Function execution deadline counted in milliseconds since the Unix epoch.
+ */
+ private long deadlineTimeInMs;
+
+ /**
+ * The client context header. This field is populated when the function is invoked from a mobile client.
+ */
+ private String clientContext;
+
+ /**
+ * The Cognito Identity context for the invocation. This field is populated when the function is invoked with AWS
+ * credentials obtained from Cognito Identity.
+ */
+ private String cognitoIdentity;
+
+ /**
+ * An input stream of the invocation's request body.
+ */
+ private InputStream stream;
+
+ private byte[] content;
+
+ public String getId() {
+ return id;
+ }
+
+ public String getXrayTraceId() {
+ return xrayTraceId;
+ }
+
+ public String getInvokedFunctionArn() {
+ return invokedFunctionArn;
+ }
+
+ public long getDeadlineTimeInMs() {
+ return deadlineTimeInMs;
+ }
+
+ public String getClientContext() {
+ return clientContext;
+ }
+
+ public String getCognitoIdentity() {
+ return cognitoIdentity;
+ }
+
+ public InputStream getContentAsStream() {
+ return new ByteArrayInputStream(content);
+ }
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java
new file mode 100644
index 00000000..05aa50a1
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClient.java
@@ -0,0 +1,116 @@
+package com.amazonaws.services.lambda.runtime.api.client.runtimeapi;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Objects;
+
+import static java.net.HttpURLConnection.HTTP_ACCEPTED;
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+/**
+ * LambdaRuntimeClient is a client of the AWS Lambda Runtime HTTP API for custom runtimes.
+ *
+ * API definition can be found at https://docs.aws.amazon.com/lambda/latest/dg/runtimes-api.html
+ *
+ * Copyright (c) 2019 Amazon. All rights reserved.
+ */
+public class LambdaRuntimeClient {
+
+ private final String hostname;
+ private final int port;
+ private final String invocationEndpoint;
+
+ private static final String DEFAULT_CONTENT_TYPE = "application/json";
+ private static final String XRAY_ERROR_CAUSE_HEADER = "Lambda-Runtime-Function-XRay-Error-Cause";
+ private static final String ERROR_TYPE_HEADER = "Lambda-Runtime-Function-Error-Type";
+ private static final int XRAY_ERROR_CAUSE_MAX_HEADER_SIZE = 1024 * 1024; // 1MiB
+
+ public LambdaRuntimeClient(String hostnamePort) {
+ Objects.requireNonNull(hostnamePort, "hostnamePort cannot be null");
+ String[] parts = hostnamePort.split(":");
+ this.hostname = parts[0];
+ this.port = Integer.parseInt(parts[1]);
+ this.invocationEndpoint = invocationEndpoint();
+ }
+
+ public InvocationRequest waitForNextInvocation() {
+ return NativeClient.next();
+ }
+
+ public void postInvocationResponse(String requestId, byte[] response) {
+ NativeClient.postInvocationResponse(requestId.getBytes(UTF_8), response);
+ }
+
+ public void postInvocationError(String requestId, byte[] errorResponse, String errorType) throws IOException {
+ postInvocationError(requestId, errorResponse, errorType, null);
+ }
+
+ public void postInvocationError(String requestId, byte[] errorResponse, String errorType, String errorCause)
+ throws IOException {
+ String endpoint = invocationErrorEndpoint(requestId);
+ post(endpoint, errorResponse, errorType, errorCause);
+ }
+
+ public void postInitError(byte[] errorResponse, String errorType) throws IOException {
+ String endpoint = initErrorEndpoint();
+ post(endpoint, errorResponse, errorType, null);
+ }
+
+ private void post(String endpoint, byte[] errorResponse, String errorType, String errorCause) throws IOException {
+ URL url = createUrl(endpoint);
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("POST");
+ conn.setRequestProperty("Content-Type", DEFAULT_CONTENT_TYPE);
+ if(errorType != null && !errorType.isEmpty()) {
+ conn.setRequestProperty(ERROR_TYPE_HEADER, errorType);
+ }
+ if(errorCause != null && errorCause.getBytes().length < XRAY_ERROR_CAUSE_MAX_HEADER_SIZE) {
+ conn.setRequestProperty(XRAY_ERROR_CAUSE_HEADER, errorCause);
+ }
+ conn.setFixedLengthStreamingMode(errorResponse.length);
+ conn.setDoOutput(true);
+ try (OutputStream outputStream = conn.getOutputStream()) {
+ outputStream.write(errorResponse);
+ }
+
+ int responseCode = conn.getResponseCode();
+ if (responseCode != HTTP_ACCEPTED) {
+ throw new LambdaRuntimeClientException(endpoint, responseCode);
+ }
+
+ // don't need to read the response, close stream to ensure connection re-use
+ closeQuietly(conn.getInputStream());
+ }
+
+ private String invocationEndpoint() {
+ return "http://" + hostname + ":" + port + "/2018-06-01/runtime/invocation/";
+ }
+
+ private String invocationErrorEndpoint(String requestId) {
+ return invocationEndpoint + requestId + "/error";
+ }
+
+ private String initErrorEndpoint() {
+ return "http://" + hostname + ":" + port + "/2018-06-01/runtime/init/error";
+ }
+
+ private URL createUrl(String endpoint) {
+ try {
+ return new URL(endpoint);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void closeQuietly(InputStream inputStream) {
+ if (inputStream == null) return;
+ try {
+ inputStream.close();
+ } catch (IOException e) {
+ }
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClientException.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClientException.java
new file mode 100644
index 00000000..1fc52d2f
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClientException.java
@@ -0,0 +1,11 @@
+package com.amazonaws.services.lambda.runtime.api.client.runtimeapi;
+
+/**
+ * Copyright (c) 2019 Amazon. All rights reserved.
+ */
+public class LambdaRuntimeClientException extends RuntimeException {
+ public LambdaRuntimeClientException(String message, int responseCode) {
+ super(message + "Response code: '" + responseCode + "'.");
+ }
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/NativeClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/NativeClient.java
new file mode 100644
index 00000000..f3cda8bc
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/NativeClient.java
@@ -0,0 +1,53 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.runtimeapi;
+
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+
+/**
+ * This module defines the native Runtime Interface Client which is responsible for all HTTP
+ * interactions with the Runtime API.
+ */
+class NativeClient {
+ private static final String nativeLibPath = "/tmp/.aws-lambda-runtime-interface-client";
+ private static final String[] libsToTry = {
+ "/aws-lambda-runtime-interface-client.glibc.so",
+ "/aws-lambda-runtime-interface-client.musl.so",
+ };
+ private static final Throwable[] exceptions = new Throwable[libsToTry.length];
+ static {
+ boolean loaded = false;
+ for (int i = 0; !loaded && i < libsToTry.length; ++i) {
+ try (InputStream lib = NativeClient.class.getResourceAsStream(libsToTry[i])) {
+ Files.copy(lib, Paths.get(nativeLibPath), StandardCopyOption.REPLACE_EXISTING);
+ System.load(nativeLibPath);
+ loaded = true;
+ } catch (UnsatisfiedLinkError e) {
+ exceptions[i] = e;
+ } catch (Exception e) {
+ exceptions[i] = e;
+ }
+ }
+ if (!loaded) {
+ for (int i = 0; i < libsToTry.length; ++i) {
+ System.err.printf("Failed to load the native runtime interface client library %s. Exception: %s\n", libsToTry[i], exceptions[i].getMessage());
+ }
+ System.exit(-1);
+ }
+ String userAgent = String.format(
+ "aws-lambda-java/%s-%s" ,
+ System.getProperty("java.vendor.version"),
+ NativeClient.class.getPackage().getImplementationVersion());
+ initializeClient(userAgent.getBytes());
+ }
+
+ static native void initializeClient(byte[] userAgent);
+
+ static native InvocationRequest next();
+
+ static native void postInvocationResponse(byte[] requestId, byte[] response);
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/EnvReader.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/EnvReader.java
new file mode 100644
index 00000000..968119ac
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/EnvReader.java
@@ -0,0 +1,22 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.util;
+
+import java.util.Map;
+
+public class EnvReader {
+
+ public Map getEnv() {
+ return System.getenv();
+ }
+
+ public String getEnv(String envVariableName) {
+ return System.getenv(envVariableName);
+ }
+
+ public String getEnvOrDefault(String envVariableName, String defaultVal) {
+ String val = getEnv(envVariableName);
+ return val == null ? defaultVal : val;
+ }
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/EnvWriter.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/EnvWriter.java
new file mode 100644
index 00000000..839dddc9
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/EnvWriter.java
@@ -0,0 +1,76 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.util;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+import java.util.function.Consumer;
+
+public class EnvWriter implements AutoCloseable {
+
+ private Map envMap;
+ private final Field field;
+
+ public EnvWriter(EnvReader envReader) {
+ Map env = envReader.getEnv();
+ try {
+ field = env.getClass().getDeclaredField("m");
+ field.setAccessible(true);
+ @SuppressWarnings("unchecked")
+ Map map = (Map) field.get(env);
+ envMap = map;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() {
+ if (field != null) {
+ field.setAccessible(false);
+ }
+ }
+
+ public void modifyEnv(Consumer> modifier) {
+ modifier.accept(envMap);
+ }
+
+ public void unsetLambdaInternalEnv() {
+ modifyEnv(env -> env.remove("_LAMBDA_TELEMETRY_LOG_FD"));
+ }
+
+ public void setupEnvironmentCredentials() {
+ modifyEnv((env) -> {
+ // AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN are set by the runtime API daemon when
+ // executing the runtime's bootstrap. Ensure these are not empty values.
+ removeIfEmpty(env, "AWS_ACCESS_KEY_ID");
+ removeIfEmpty(env, "AWS_SECRET_ACCESS_KEY");
+ removeIfEmpty(env, "AWS_SESSION_TOKEN");
+
+ // The AWS Java SDK supports two alternate keys for the aws access and secret keys for compatibility.
+ // These are not set by the runtime API daemon when executing a runtime's bootstrap so set them here.
+ addIfNotNull(env, "AWS_ACCESS_KEY", env.get("AWS_ACCESS_KEY_ID"));
+ addIfNotNull(env, "AWS_SECRET_KEY", env.get("AWS_SECRET_ACCESS_KEY"));
+ });
+ }
+
+ public void setupAwsExecutionEnv() {
+ String version = System.getProperty("java.version");
+ if (version.startsWith("1.8")) {
+ modifyEnv(env -> env.put("AWS_EXECUTION_ENV", "AWS_Lambda_java8"));
+ } else if (version.startsWith("11")) {
+ modifyEnv(env -> env.put("AWS_EXECUTION_ENV", "AWS_Lambda_java11"));
+ }
+ }
+
+ private void addIfNotNull(Map env, String key, String value) {
+ if (value != null && !value.isEmpty()) {
+ env.put(key, value);
+ }
+ }
+
+ private void removeIfEmpty(Map env, String key) {
+ env.computeIfPresent(key, (k, v) -> v.isEmpty() ? null : v);
+ }
+
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/LambdaOutputStream.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/LambdaOutputStream.java
new file mode 100644
index 00000000..83ffcb9a
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/LambdaOutputStream.java
@@ -0,0 +1,30 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.util;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+public class LambdaOutputStream extends OutputStream {
+ private final OutputStream inner;
+
+ public LambdaOutputStream(OutputStream inner) {
+ this.inner = inner;
+ }
+
+ @Override
+ public void write(int b) throws IOException {
+ write(new byte[] {(byte)b});
+ }
+
+ @Override
+ public void write(byte[] bytes) throws IOException {
+ write(bytes, 0, bytes.length);
+
+ }
+
+ @Override
+ public void write(byte[] bytes, int offset, int length) throws IOException {
+ inner.write(bytes, offset, length);
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/UnsafeUtil.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/UnsafeUtil.java
new file mode 100644
index 00000000..f9351c94
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/util/UnsafeUtil.java
@@ -0,0 +1,40 @@
+/* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. */
+
+package com.amazonaws.services.lambda.runtime.api.client.util;
+
+import sun.misc.Unsafe;
+
+import java.lang.reflect.Field;
+
+/**
+ * Utilities for easy access to sun.misc.Unsafe
+ */
+public final class UnsafeUtil {
+ public static final Unsafe TheUnsafe;
+
+ static {
+ try {
+ final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ TheUnsafe = (Unsafe) theUnsafe.get(null);
+ } catch (Exception e) {
+ throw new Error("failed to load Unsafe", e);
+ }
+ }
+
+ private UnsafeUtil() {
+ }
+
+ public static void disableIllegalAccessWarning() {
+ try {
+ Class illegalAccessLoggerClass = Class.forName("jdk.internal.module.IllegalAccessLogger");
+ Field loggerField = illegalAccessLoggerClass.getDeclaredField("logger");
+ TheUnsafe.putObjectVolatile(illegalAccessLoggerClass, TheUnsafe.staticFieldOffset(loggerField), null);
+ } catch (Throwable t) { /* ignore */ }
+ }
+
+ public static RuntimeException throwException(Throwable t) {
+ TheUnsafe.throwException(t);
+ throw new Error("should never get here");
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.glibc b/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.glibc
new file mode 100644
index 00000000..658d8548
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.glibc
@@ -0,0 +1,79 @@
+# we use centos 6 to build against glibc 2.12
+FROM centos:6
+
+# aws-lambda-cpp requires cmake3, it's available in EPEL
+RUN yum install -y epel-release
+RUN yum install -y \
+ cmake3 \
+ gcc \
+ gcc-c++ \
+ glibc-devel \
+ gmp-devel \
+ libmpc-devel \
+ libtool \
+ mpfr-devel \
+ wget
+
+# aws-lambda-cpp also needs a newer compiler than the default gcc 4.4
+ARG GCC_VERSION=4.8.5
+RUN wget -qO- https://mirrors.kernel.org/gnu/gcc/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz | tar xzf -
+WORKDIR gcc-$GCC_VERSION
+RUN ./configure \
+ --disable-multilib \
+ --enable-languages=c,c++ && \
+ make -j$(nproc) && \
+ make install
+RUN yum remove -y gcc gcc-c++
+RUN rm -rf /usr/bin/gcc && \
+ rm -rf /usr/bin/c++ && \
+ rm -rf /usr/bin/cc && \
+ ln -s /usr/local/bin/x86_64-unknown-linux-gnu-gcc-$GCC_VERSION /usr/bin/gcc && \
+ ln -s /usr/local/bin/x86_64-unknown-linux-gnu-c++ /usr/bin/c++ && \
+ ln -s /usr/local/bin/x86_64-unknown-linux-gnu-gcc /usr/bin/cc
+
+RUN rpm --import https://yum.corretto.aws/corretto.key
+RUN curl -L -o /etc/yum.repos.d/corretto.repo https://yum.corretto.aws/corretto.repo
+RUN yum install -y java-1.8.0-amazon-corretto-devel
+
+ADD ./deps/curl-* /src/deps/curl
+WORKDIR /src/deps/curl
+RUN ./configure \
+ --prefix $(pwd)/../artifacts \
+ --disable-shared \
+ --without-ssl \
+ --without-zlib && \
+ make && \
+ make install
+
+ADD ./deps/aws-lambda-cpp-* /src/deps/aws-lambda-cpp
+RUN sed -i.bak 's/VERSION 3.9/VERSION 3.6/' /src/deps/aws-lambda-cpp/CMakeLists.txt
+RUN mkdir -p /src/deps/aws-lambda-cpp/build
+WORKDIR /src/deps/aws-lambda-cpp/build
+RUN cmake3 .. \
+ -DENABLE_LTO=OFF \
+ -DCMAKE_CXX_FLAGS="-fPIC -DBACKWARD_SYSTEM_UNKNOWN" \
+ -DCMAKE_CXX_STANDARD=11 \
+ -DCMAKE_INSTALL_PREFIX=$(pwd)/../../artifacts\
+ -DCMAKE_MODULE_PATH=$(pwd)/../../artifacts/lib/pkgconfig && \
+ make && \
+ make install
+
+ADD *.cpp *.h /src/
+WORKDIR /src
+ENV JAVA_HOME=/usr/lib/jvm/java-1.8.0-amazon-corretto
+RUN /usr/bin/c++ -c \
+ -std=gnu++11 \
+ -fPIC \
+ -I${JAVA_HOME}/include \
+ -I${JAVA_HOME}/include/linux \
+ -I ./deps/artifacts/include \
+ com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp -o com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.o && \
+ /usr/bin/c++ -shared \
+ -std=gnu++11 \
+ -o aws-lambda-runtime-interface-client.so com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.o \
+ -L ./deps/artifacts/lib64/ \
+ -L ./deps/artifacts/lib/ \
+ -laws-lambda-runtime \
+ -lcurl \
+ -static-libstdc++ \
+ -lrt
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.musl b/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.musl
new file mode 100644
index 00000000..9de8e617
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/Dockerfile.musl
@@ -0,0 +1,54 @@
+FROM alpine:3
+RUN wget -O /etc/apk/keys/amazoncorretto.rsa.pub https://apk.corretto.aws/amazoncorretto.rsa.pub
+RUN echo "https://apk.corretto.aws/" >> /etc/apk/repositories
+RUN apk update
+RUN apk add \
+ amazon-corretto-8 \
+ cmake \
+ file \
+ g++ \
+ gcc \
+ make \
+ libexecinfo-dev \
+ perl
+
+ADD ./deps/curl-* /src/deps/curl
+WORKDIR /src/deps/curl
+RUN ./configure \
+ --prefix $(pwd)/../artifacts \
+ --disable-shared \
+ --without-ssl \
+ --without-zlib && \
+ make && \
+ make install
+
+ADD ./deps/aws-lambda-cpp-* /src/deps/aws-lambda-cpp
+RUN mkdir -p /src/deps/aws-lambda-cpp/build
+WORKDIR /src/deps/aws-lambda-cpp/build
+RUN cmake .. \
+ -DCMAKE_CXX_FLAGS="-fPIC -DBACKWARD_SYSTEM_UNKNOWN" \
+ -DCMAKE_CXX_STANDARD=11 \
+ -DCMAKE_INSTALL_PREFIX=$(pwd)/../../artifacts\
+ -DCMAKE_MODULE_PATH=$(pwd)/../../artifacts/lib/pkgconfig && \
+ make && \
+ make install
+
+ADD *.cpp *.h /src/
+WORKDIR /src
+ENV JAVA_HOME=/usr/lib/jvm/java-8-amazon-corretto
+RUN /usr/bin/c++ -c \
+ -std=gnu++11 \
+ -fPIC \
+ -I${JAVA_HOME}/include \
+ -I${JAVA_HOME}/include/linux \
+ -I ./deps/artifacts/include \
+ com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp -o com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.o && \
+ /usr/bin/c++ -shared \
+ -std=gnu++11 \
+ -o aws-lambda-runtime-interface-client.so com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.o \
+ -L ./deps/artifacts/lib64/ \
+ -L ./deps/artifacts/lib/ \
+ -laws-lambda-runtime \
+ -lcurl \
+ -static-libstdc++ \
+ -static-libgcc
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh b/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh
new file mode 100755
index 00000000..78d8baff
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/build-jni-lib.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+
+set -euo pipefail
+
+SRC_DIR=$(dirname "$0")
+DST_DIR=${1}
+
+# compile the native library
+docker build -f "${SRC_DIR}/Dockerfile.glibc" -t lambda-java-jni-lib-glibc "${SRC_DIR}"
+docker run --rm --entrypoint /bin/cat lambda-java-jni-lib-glibc /src/aws-lambda-runtime-interface-client.so > "${DST_DIR}"/classes/aws-lambda-runtime-interface-client.glibc.so
+
+docker build -f "${SRC_DIR}/Dockerfile.musl" -t lambda-java-jni-lib-musl "${SRC_DIR}"
+docker run --rm --entrypoint /bin/cat lambda-java-jni-lib-musl /src/aws-lambda-runtime-interface-client.so > "${DST_DIR}"/classes/aws-lambda-runtime-interface-client.musl.so
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp b/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp
new file mode 100644
index 00000000..c6a1dabc
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.cpp
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2019-present Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+#include
+#include "com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.h"
+#include "aws/lambda-runtime/runtime.h"
+#include "aws/lambda-runtime/version.h"
+
+#define CHECK_EXCEPTION(env, expr) \
+ expr; \
+ if ((env)->ExceptionOccurred()) \
+ goto ERROR;
+
+static aws::lambda_runtime::runtime * CLIENT = nullptr;
+
+static void throwLambdaRuntimeClientException(JNIEnv *env, std::string message, aws::http::response_code responseCode){
+ jclass lambdaRuntimeExceptionClass = env->FindClass("com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeClientException");
+ jstring jMessage = env->NewStringUTF(message.c_str());
+ jmethodID exInit = env->GetMethodID(lambdaRuntimeExceptionClass, "", "(Ljava/lang/String;I)V");
+ jthrowable lambdaRuntimeException = (jthrowable) env->NewObject(lambdaRuntimeExceptionClass, exInit, jMessage, static_cast(responseCode));
+ env->Throw(lambdaRuntimeException);
+}
+
+static std::string toNativeString(JNIEnv *env, jbyteArray jArray) {
+ int length = env->GetArrayLength(jArray);
+ jbyte* bytes = env->GetByteArrayElements(jArray, NULL);
+ std::string nativeString = std::string((char *)bytes, length);
+ env->ReleaseByteArrayElements(jArray, bytes, JNI_ABORT);
+ env->DeleteLocalRef(jArray);
+ return nativeString;
+}
+
+JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_initializeClient(JNIEnv *env, jobject thisObject, jbyteArray userAgent) {
+ std::string user_agent = toNativeString(env, userAgent);
+ std::string endpoint(getenv("AWS_LAMBDA_RUNTIME_API"));
+ CLIENT = new aws::lambda_runtime::runtime(endpoint, user_agent);
+}
+
+JNIEXPORT jobject JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_next
+ (JNIEnv *env, jobject thisObject){
+ auto outcome = CLIENT->get_next();
+ if (!outcome.is_success()) {
+ std::string errorMessage("Failed to get next.");
+ throwLambdaRuntimeClientException(env, errorMessage, outcome.get_failure());
+ return NULL;
+ }
+
+ jclass invocationRequestClass;
+ jfieldID invokedFunctionArnField;
+ jfieldID deadlineTimeInMsField;
+ jfieldID xrayTraceIdField;
+ jfieldID idField;
+ jobject invocationRequest;
+ jfieldID clientContextField;
+ jfieldID cognitoIdentityField;
+ jbyteArray jArray;
+ const jbyte* bytes;
+ jfieldID contentField;
+ auto response = outcome.get_result();
+
+ CHECK_EXCEPTION(env, invocationRequestClass = env->FindClass("com/amazonaws/services/lambda/runtime/api/client/runtimeapi/InvocationRequest"));
+ CHECK_EXCEPTION(env, invocationRequest = env->AllocObject(invocationRequestClass));
+
+ CHECK_EXCEPTION(env, idField = env->GetFieldID(invocationRequestClass , "id", "Ljava/lang/String;"));
+ CHECK_EXCEPTION(env, env->SetObjectField(invocationRequest, idField, env->NewStringUTF(response.request_id.c_str())));
+
+ CHECK_EXCEPTION(env, invokedFunctionArnField = env->GetFieldID(invocationRequestClass , "invokedFunctionArn", "Ljava/lang/String;"));
+ CHECK_EXCEPTION(env, env->SetObjectField(invocationRequest, invokedFunctionArnField, env->NewStringUTF(response.function_arn.c_str())));
+
+ CHECK_EXCEPTION(env, deadlineTimeInMsField = env->GetFieldID(invocationRequestClass , "deadlineTimeInMs", "J"));
+ CHECK_EXCEPTION(env, env->SetLongField(invocationRequest, deadlineTimeInMsField, std::chrono::duration_cast(response.deadline.time_since_epoch()).count()));
+
+ if(response.xray_trace_id != ""){
+ CHECK_EXCEPTION(env, xrayTraceIdField = env->GetFieldID(invocationRequestClass , "xrayTraceId", "Ljava/lang/String;"));
+ CHECK_EXCEPTION(env, env->SetObjectField(invocationRequest, xrayTraceIdField, env->NewStringUTF(response.xray_trace_id.c_str())));
+ }
+
+ if(response.client_context != ""){
+ CHECK_EXCEPTION(env, clientContextField = env->GetFieldID(invocationRequestClass , "clientContext", "Ljava/lang/String;"));
+ CHECK_EXCEPTION(env, env->SetObjectField(invocationRequest, clientContextField, env->NewStringUTF(response.client_context.c_str())));
+ }
+
+ if(response.cognito_identity != ""){
+ CHECK_EXCEPTION(env, cognitoIdentityField = env->GetFieldID(invocationRequestClass , "cognitoIdentity", "Ljava/lang/String;"));
+ CHECK_EXCEPTION(env, env->SetObjectField(invocationRequest, cognitoIdentityField, env->NewStringUTF(response.cognito_identity.c_str())));
+ }
+
+ bytes = reinterpret_cast(response.payload.c_str());
+ CHECK_EXCEPTION(env, jArray = env->NewByteArray(response.payload.length()));
+ CHECK_EXCEPTION(env, env->SetByteArrayRegion(jArray, 0, response.payload.length(), bytes));
+ CHECK_EXCEPTION(env, contentField = env->GetFieldID(invocationRequestClass , "content", "[B"));
+ CHECK_EXCEPTION(env, env->SetObjectField(invocationRequest, contentField, jArray));
+
+ return invocationRequest;
+
+ ERROR:
+ return NULL;
+}
+
+JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_postInvocationResponse
+ (JNIEnv *env, jobject thisObject, jbyteArray jrequestId, jbyteArray jresponseArray) {
+ std::string payload = toNativeString(env, jresponseArray);
+ if ((env)->ExceptionOccurred()){
+ return;
+ }
+ std::string requestId = toNativeString(env, jrequestId);
+ if ((env)->ExceptionOccurred()){
+ return;
+ }
+
+ auto response = aws::lambda_runtime::invocation_response::success(payload, "application/json");
+ auto outcome = CLIENT->post_success(requestId, response);
+ if (!outcome.is_success()) {
+ std::string errorMessage("Failed to post invocation response.");
+ throwLambdaRuntimeClientException(env, errorMessage, outcome.get_failure());
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.h b/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.h
new file mode 100644
index 00000000..28a6f444
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient.h
@@ -0,0 +1,21 @@
+#include
+
+#ifndef _Included_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient
+#define _Included_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_initializeClient
+ (JNIEnv *, jobject, jbyteArray);
+
+JNIEXPORT jobject JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_next
+ (JNIEnv *, jobject);
+
+JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_postInvocationResponse
+ (JNIEnv *, jobject, jbyteArray, jbyteArray);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.clang-format b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.clang-format
new file mode 100644
index 00000000..ec8bb67d
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.clang-format
@@ -0,0 +1,61 @@
+---
+Language: Cpp
+# BasedOnStyle: Mozilla
+AlignAfterOpenBracket: AlwaysBreak
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: false
+BinPackParameters: false
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: true
+BreakStringLiterals: true
+ColumnLimit: 120
+ContinuationIndentWidth: 4
+DerivePointerAlignment: false
+IncludeBlocks: Preserve
+IndentCaseLabels: true
+IndentPPDirectives: AfterHash
+IndentWidth: 4
+IndentWrappedFunctionNames: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+PenaltyBreakComment: 10
+PenaltyBreakAssignment: 20
+PenaltyBreakString: 30
+PenaltyBreakBeforeFirstCallParameter: 35
+PenaltyBreakFirstLessLess: 40
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 100000
+PointerAlignment: Left
+ReflowComments: true
+SortIncludes: false
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+TabWidth: 4
+UseTab: Never
+NamespaceIndentation: None
+BreakBeforeBraces: Stroustrup
+AccessModifierOffset: -4
+...
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.clang-tidy b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.clang-tidy
new file mode 100644
index 00000000..7d343ead
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.clang-tidy
@@ -0,0 +1,41 @@
+---
+Checks:
+'clang-diagnostic-*,clang-analyzer-*,performance-*,readability-*,modernize-*,bugprone-*,misc-*,-modernize-use-trailing-return-type'
+WarningsAsErrors: '*'
+HeaderFilterRegex: 'include/aws/.*\.h$'
+FormatStyle: 'none'
+CheckOptions:
+ - key: modernize-pass-by-value.ValuesOnly
+ value: '1'
+ - key: readability-implicit-bool-conversion.AllowPointerConditions
+ value: '1'
+ - key: readability-implicit-bool-conversion.AllowIntegerConditions
+ value: '1'
+ - key: misc-non-private-member-variables-in-classes.IgnoreClassesWithAllMemberVariablesBeingPublic
+ value: '1'
+ - key: readability-identifier-naming.ClassCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.StructCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.StructCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.ParameterCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.PrivateMemberCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.LocalVariableCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.TypeAliasCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.UnionCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.FunctionCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.NamespaceCase
+ value: 'lower_case'
+ - key: readability-identifier-naming.GlobalConstantCase
+ value: 'UPPER_CASE'
+ - key: readability-identifier-naming.PrivateMemberPrefix
+ value: 'm_'
+
+...
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.github/PULL_REQUEST_TEMPLATE.md b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.github/PULL_REQUEST_TEMPLATE.md
new file mode 100644
index 00000000..ab40d21d
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.github/PULL_REQUEST_TEMPLATE.md
@@ -0,0 +1,6 @@
+*Issue #, if available:*
+
+*Description of changes:*
+
+
+By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.gitignore b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.gitignore
new file mode 100644
index 00000000..647f4493
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/.gitignore
@@ -0,0 +1,5 @@
+build
+tags
+TODO
+compile_commands.json
+.clangd
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CMakeLists.txt b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CMakeLists.txt
new file mode 100644
index 00000000..1765caf0
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CMakeLists.txt
@@ -0,0 +1,132 @@
+cmake_minimum_required(VERSION 3.9)
+set(CMAKE_CXX_STANDARD 11)
+project(aws-lambda-runtime
+ VERSION 0.2.7
+ LANGUAGES CXX)
+
+option(ENABLE_LTO "Enables link-time optimization, requires compiler support." ON)
+option(ENABLE_TESTS "Enables building the test project, requires AWS C++ SDK." OFF)
+
+add_library(${PROJECT_NAME}
+ "src/logging.cpp"
+ "src/runtime.cpp"
+ "src/backward.cpp"
+ "${CMAKE_CURRENT_BINARY_DIR}/version.cpp"
+ )
+
+set_target_properties(${PROJECT_NAME} PROPERTIES
+ SOVERSION 0
+ VERSION ${PROJECT_VERSION})
+
+target_include_directories(${PROJECT_NAME} PUBLIC
+ $
+ $)
+
+if (ENABLE_LTO)
+ include(CheckIPOSupported)
+ check_ipo_supported(RESULT has_lto OUTPUT lto_check_output)
+ if(has_lto)
+ set_property(TARGET ${PROJECT_NAME} PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
+ else()
+ message(WARNING "Link-time optimization (LTO) is not supported: ${lto_check_output}")
+ endif()
+endif()
+
+find_package(CURL REQUIRED)
+if (CMAKE_VERSION VERSION_LESS 3.12)
+ target_link_libraries(${PROJECT_NAME} PRIVATE ${CURL_LIBRARIES})
+else()
+ target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl)
+endif()
+
+target_include_directories(${PROJECT_NAME} PRIVATE ${CURL_INCLUDE_DIRS})
+
+target_compile_options(${PROJECT_NAME} PRIVATE
+ "-fno-exceptions"
+ "-fno-rtti"
+ "-fvisibility=hidden"
+ "-fvisibility-inlines-hidden"
+ "-Wall"
+ "-Wextra"
+ "-Werror"
+ "-Wconversion"
+ "-Wno-sign-conversion")
+
+find_library(DW_LIB NAMES dw)
+if (NOT DW_LIB STREQUAL DW_LIB-NOTFOUND)
+ message("-- Enhanced stack-traces are enabled via libdw: ${DW_LIB}")
+ target_compile_definitions(${PROJECT_NAME} PRIVATE "BACKWARD_HAS_DW=1")
+ target_link_libraries(${PROJECT_NAME} PUBLIC "${DW_LIB}")
+else()
+ find_library(BFD_LIB NAMES bfd)
+ if (NOT BFD_LIB STREQUAL BFD_LIB-NOTFOUND)
+ message("-- Enhanced stack-traces are enabled via libbfd: ${BFD_LIB}")
+ target_compile_definitions(${PROJECT_NAME} PRIVATE "BACKWARD_HAS_BFD=1")
+ target_link_libraries(${PROJECT_NAME} PRIVATE "${BFD_LIB}")
+ endif()
+endif()
+
+if (LOG_VERBOSITY)
+ target_compile_definitions(${PROJECT_NAME} PRIVATE "AWS_LAMBDA_LOG=${LOG_VERBOSITY}")
+elseif(CMAKE_BUILD_TYPE STREQUAL Debug)
+ target_compile_definitions(${PROJECT_NAME} PRIVATE "AWS_LAMBDA_LOG=3")
+else ()
+ target_compile_definitions(${PROJECT_NAME} PRIVATE "AWS_LAMBDA_LOG=0")
+endif()
+
+#tests
+if (ENABLE_TESTS)
+ enable_testing()
+ add_subdirectory(tests)
+endif()
+
+#versioning
+configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/src/version.cpp.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/version.cpp"
+ NEWLINE_STYLE LF)
+
+include (CMakePackageConfigHelpers)
+
+write_basic_package_version_file("${PROJECT_NAME}-config-version.cmake"
+ VERSION ${PROJECT_VERSION}
+ COMPATIBILITY SameMajorVersion)
+
+# installation
+install(FILES "include/aws/http/response.h"
+ DESTINATION "include/aws/http")
+
+install(FILES
+ "include/aws/lambda-runtime/runtime.h"
+ "include/aws/lambda-runtime/version.h"
+ "include/aws/lambda-runtime/outcome.h"
+ DESTINATION "include/aws/lambda-runtime")
+
+install(FILES "include/aws/logging/logging.h"
+ DESTINATION "include/aws/logging")
+
+include(GNUInstallDirs)
+install(TARGETS ${PROJECT_NAME}
+ EXPORT ${PROJECT_NAME}-targets
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ )
+
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PROJECT_NAME}-config.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
+ @ONLY)
+
+export(EXPORT "${PROJECT_NAME}-targets" NAMESPACE AWS::)
+
+install(EXPORT "${PROJECT_NAME}-targets"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/"
+ NAMESPACE AWS::)
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config.cmake"
+ "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-config-version.cmake"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/")
+
+install(PROGRAMS "${CMAKE_CURRENT_SOURCE_DIR}/packaging/packager"
+ DESTINATION "${CMAKE_INSTALL_LIBDIR}/${PROJECT_NAME}/cmake/")
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CODE_OF_CONDUCT.md b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CODE_OF_CONDUCT.md
new file mode 100644
index 00000000..3b644668
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CODE_OF_CONDUCT.md
@@ -0,0 +1,4 @@
+## Code of Conduct
+This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
+For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
+opensource-codeofconduct@amazon.com with any additional questions or comments.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CONTRIBUTING.md b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CONTRIBUTING.md
new file mode 100644
index 00000000..e8c3aa58
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/CONTRIBUTING.md
@@ -0,0 +1,61 @@
+# Contributing Guidelines
+
+Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
+documentation, we greatly value feedback and contributions from our community.
+
+Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
+information to effectively respond to your bug report or contribution.
+
+
+## Reporting Bugs/Feature Requests
+
+We welcome you to use the GitHub issue tracker to report bugs or suggest features.
+
+When filing an issue, please check [existing open](https://github.com/awslabs/aws-lambda-cpp-runtime/issues), or [recently closed](https://github.com/awslabs/aws-lambda-cpp-runtime/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already
+reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
+
+* A reproducible test case or series of steps
+* The version of our code being used
+* Any modifications you've made relevant to the bug
+* Anything unusual about your environment or deployment
+
+
+## Contributing via Pull Requests
+Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
+
+1. You are working against the latest source on the *master* branch.
+2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
+3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
+
+To send us a pull request, please:
+
+1. Fork the repository.
+2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
+3. Ensure local tests pass.
+4. Commit to your fork using clear commit messages.
+5. Send us a pull request, answering any default questions in the pull request interface.
+6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
+
+GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
+[creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
+
+
+## Finding contributions to work on
+Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/aws-lambda-cpp-runtime/labels/help%20wanted) issues is a great place to start.
+
+
+## Code of Conduct
+This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
+For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
+opensource-codeofconduct@amazon.com with any additional questions or comments.
+
+
+## Security issue notifications
+If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
+
+
+## Licensing
+
+See the [LICENSE](https://github.com/awslabs/aws-lambda-cpp-runtime/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
+
+We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/LICENSE b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/LICENSE
new file mode 100644
index 00000000..d6456956
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/LICENSE
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/NOTICE b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/NOTICE
new file mode 100644
index 00000000..34e186a0
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/NOTICE
@@ -0,0 +1,2 @@
+AWS Lambda Cpp Runtime
+Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/README.md b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/README.md
new file mode 100644
index 00000000..0812476a
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/README.md
@@ -0,0 +1,220 @@
+[](https://github.com/awslabs/aws-lambda-cpp/blob/master/LICENSE)
+
+[](https://lgtm.com/projects/g/awslabs/aws-lambda-cpp/context:cpp)
+## AWS Lambda C++ Runtime
+
+C++ implementation of the lambda runtime API
+
+## Design Goals
+1. Negligible cold-start overhead (single digit millisecond).
+2. Freedom of choice in compilers, build platforms and C standard library versions.
+
+## Building and Installing the Runtime
+Since AWS Lambda runs on GNU/Linux, you should build this runtime library and your logic on GNU/Linux as well.
+
+### Prerequisites
+Make sure you have the following packages installed first:
+1. CMake (version 3.9 or later)
+1. git
+1. Make or Ninja
+1. zip
+1. libcurl-devel (on Debian-basded distros it's libcurl4-openssl-dev)
+
+In a terminal, run the following commands:
+```bash
+$ git clone https://github.com/awslabs/aws-lambda-cpp.git
+$ cd aws-lambda-cpp
+$ mkdir build
+$ cd build
+$ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=~/lambda-install
+$ make && make install
+```
+
+To consume this library in a project that is also using CMake, you would do:
+
+```cmake
+cmake_minimum_required(VERSION 3.9)
+set(CMAKE_CXX_STANDARD 11)
+project(demo LANGUAGES CXX)
+find_package(aws-lambda-runtime)
+add_executable(${PROJECT_NAME} "main.cpp")
+target_link_libraries(${PROJECT_NAME} PRIVATE AWS::aws-lambda-runtime)
+target_compile_features(${PROJECT_NAME} PRIVATE "cxx_std_11")
+target_compile_options(${PROJECT_NAME} PRIVATE "-Wall" "-Wextra")
+
+# this line creates a target that packages your binary and zips it up
+aws_lambda_package_target(${PROJECT_NAME})
+```
+
+And here is how a sample `main.cpp` would look like:
+```cpp
+#include
+
+using namespace aws::lambda_runtime;
+
+static invocation_response my_handler(invocation_request const& req)
+{
+ if (req.payload.length() > 42) {
+ return invocation_response::failure("error message here"/*error_message*/,
+ "error type here" /*error_type*/);
+ }
+
+ return invocation_response::success("json payload here" /*payload*/,
+ "application/json" /*MIME type*/);
+}
+
+int main()
+{
+ run_handler(my_handler);
+ return 0;
+}
+```
+
+And finally, here's how you would package it all. Run the following commands from your application's root directory:
+
+```bash
+$ mkdir build
+$ cd build
+$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=~/lambda-install
+$ make
+$ make aws-lambda-package-demo
+```
+The last command above `make aws-lambda-package-demo` will create a zip file called `demo.zip` in the current directory.
+
+Now, create an IAM role and the Lambda function via the AWS CLI.
+
+First create the following trust policy JSON file
+
+```
+$ cat trust-policy.json
+{
+ "Version": "2012-10-17",
+ "Statement": [
+ {
+ "Effect": "Allow",
+ "Principal": {
+ "Service": ["lambda.amazonaws.com"]
+ },
+ "Action": "sts:AssumeRole"
+ }
+ ]
+}
+
+```
+Then create the IAM role:
+
+```bash
+$ aws iam create-role --role-name lambda-demo --assume-role-policy-document file://trust-policy.json
+```
+
+Note down the role Arn returned to you after running that command. We'll need it in the next steps:
+
+Attach the following policy to allow Lambda to write logs in CloudWatch:
+```bash
+$ aws iam attach-role-policy --role-name lambda-demo --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
+```
+
+Make sure you attach the appropriate policies and/or permissions for any other AWS services that you plan on using.
+
+And finally, create the Lambda function:
+
+```
+$ aws lambda create-function --function-name demo \
+--role \
+--runtime provided --timeout 15 --memory-size 128 \
+--handler demo --zip-file fileb://demo.zip
+```
+
+And to invoke the function:
+```bash
+$ aws lambda invoke --function-name demo --payload '{"answer":42}' output.txt
+```
+
+## Using the C++ SDK for AWS with this runtime
+This library is completely independent from the AWS C++ SDK. You should treat the AWS C++ SDK as just another dependency in your application.
+See [the examples section](https://github.com/awslabs/aws-lambda-cpp/tree/master/examples/) for a demo utilizing the AWS C++ SDK with this Lambda runtime.
+
+## Supported Compilers
+Any *fully* compliant C++11 compiler targeting GNU/Linux x86-64 should work. Please avoid compiler versions that provide half-baked C++11 support.
+
+- Use GCC v5.x or above
+- Use Clang v3.3 or above
+
+## Packaging, ABI, GNU C Library, Oh My!
+Lambda runs your code on some version of Amazon Linux. It would be a less than ideal customer experience if you are forced to build your application on that platform and that platform only.
+
+However, the freedom to build on any linux distro brings a challenge. The GNU C Library ABI. There is no guarantee the platform used to build the Lambda function has the same GLIBC version as the one used by AWS Lambda. In fact, you might not even be using GNU's implementation. For example you could build a C++ Lambda function using musl libc.
+
+To ensure that your application will run correctly on Lambda, we must package the entire C runtime library with your function.
+If you choose to build on the same [Amazon Linux version used by lambda](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html), you can avoid packaging the C runtime in your zip file.
+This can be done by passing the `NO_LIBC` flag in CMake as follows:
+
+```cmake
+aws_lambda_package_target(${PROJECT_NAME} NO_LIBC)
+```
+### Common Pitfalls with Packaging
+
+* Any library dependency your Lambda function has that is dynamically loaded via `dlopen` will NOT be automatically packaged. You **must** add those dependencies manually to the zip file.
+This applies to any configuration or resource files that your code depends on.
+
+* If you are making HTTP calls over TLS (https), keep in mind that the CA bundle location is different between distros.
+For example, if you are using the AWS C++ SDK, it's best to set the following configuration options:
+
+```cpp
+Aws::Client::ClientConfiguration config;
+config.caFile = "/etc/pki/tls/certs/ca-bundle.crt";
+```
+If you are not using the AWS C++ SDK, but happen to be using libcurl directly, you can set the CA bundle location by doing:
+```c
+curl_easy_setopt(curl_handle, CURLOPT_CAINFO, "/etc/pki/tls/certs/ca-bundle.crt");
+```
+
+## FAQ & Troubleshooting
+1. **Why is the zip file so large? what are all those files?**
+ Typically, the zip file is large because we have to package the entire C standard library.
+ You can reduce the size by doing some or all of the following:
+ - Ensure you're building in release mode `-DCMAKE_BUILD_TYPE=Release`
+ - If possible, build your function using musl libc, it's tiny. The easiest way to do this, assuming your code is portable, is to build on Alpine linux, which uses musl libc by default.
+1. **How to upload a zip file that's bigger than 50MB via the CLI?**
+ Upload your zip file to S3 first:
+ ```bash
+ $ aws s3 cp demo.zip s3://mys3bucket/demo.zip
+ ```
+ NOTE: you must use the same region for your S3 bucket as the lambda.
+
+ Then you can create the Lambda function this way:
+
+ ```bash
+ $ aws lambda create-function --function-name demo \
+ --role \
+ --runtime provided --timeout 15 --memory-size 128 \
+ --handler demo
+ --code "S3Bucket=mys3bucket,S3Key=demo.zip"
+ ```
+1. **My code is crashing, how can I debug it?**
+
+ - Starting with [v0.2.0](https://github.com/awslabs/aws-lambda-cpp/releases/tag/v0.2.0) you should see a stack-trace of the crash site in the logs (which are typically stored in CloudWatch).
+ - To get a more detailed stack-trace with source-code information such as line numbers, file names, etc. you need to install one of the following packages:
+ - On Debian-based systems - `sudo apt install libdw-dev` or `sudo apt install binutils-dev`
+ - On RHEL based systems - `sudo yum install elfutils-devel` or `sudo yum install binutils-devel`
+ If you have either of those packages installed, CMake will detect them and automatically link to them. No other
+ steps are required.
+ - Turn up the logging verbosity to the maximum.
+ - Build the runtime in Debug mode. `-DCMAKE_BUILD_TYPE=Debug`. Verbose logs are enabled by default in Debug builds.
+ - To enable verbose logs in Release builds, build the runtime with the following CMake flag `-DLOG_VERBOSITY=3`
+ - If you are using the AWS C++ SDK, see [this FAQ](https://github.com/aws/aws-sdk-cpp/wiki#how-do-i-turn-on-logging) on how to adjust its logging verbosity
+ - Run your code locally on an Amazon Linux AMI or Docker container to reproduce the problem
+ - If you go the AMI route, [use the official one](https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html) recommended by AWS Lambda
+ - If you go the Docker route, use the following command to launch a container running AL2017.03
+ `$ docker run -v /tmp:/tmp -it --security-opt seccomp=unconfined amazonlinux:2017.03`
+ The `security-opt` argument is necessary to run `gdb`, `strace`, etc.
+1. **CURL problem with the SSL CA cert**
+ - Make sure you are using a `libcurl` version built with OpenSSL, or one of its flavors (BoringSSL, LibreSSL)
+ - Make sure you tell `libcurl` where to find the CA bundle file.
+ - You can try hitting the non-TLS version of the endpoint if available. (Not Recommended).
+1. **No known conversion between `std::string` and `Aws::String`**
+ - Either turn off custom memory management in the AWS C++ SDK or build it as a static library (`-DBUILD_SHARED_LIBS=OFF`)
+
+## License
+
+This library is licensed under the Apache 2.0 License.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/amazonlinux-2017.03.yml b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/amazonlinux-2017.03.yml
new file mode 100644
index 00000000..eab1bafb
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/amazonlinux-2017.03.yml
@@ -0,0 +1,18 @@
+version: 0.1
+# This uses the docker image specified in ci/docker/amazon-linux-2017.03
+phases:
+ pre_build:
+ commands:
+ - alias cmake=cmake3
+ - pip install awscli
+ - ci/codebuild/build-cpp-sdk.sh
+ build:
+ commands:
+ - echo Build started on `date`
+ - ci/codebuild/build.sh -DENABLE_TESTS=ON -DTEST_RESOURCE_PREFIX=amzn201703
+ - ci/codebuild/run-tests.sh aws-lambda-package-lambda-test-fun amzn201703
+ - ci/codebuild/run-tests.sh aws-lambda-package-lambda-test-fun-no-glibc amzn201703
+ post_build:
+ commands:
+ - echo Build completed on `date`
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/build-cpp-sdk.sh b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/build-cpp-sdk.sh
new file mode 100755
index 00000000..93ae7ebe
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/build-cpp-sdk.sh
@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -euo pipefail
+
+# build the AWS C++ SDK
+cd /aws-sdk-cpp
+git pull
+mkdir build
+cd build
+cmake .. -GNinja -DBUILD_ONLY="lambda" \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DENABLE_UNITY_BUILD=ON \
+ -DBUILD_SHARED_LIBS=ON \
+ -DENABLE_TESTING=OFF \
+ -DCMAKE_INSTALL_PREFIX=/install $@
+ninja
+ninja install
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/build.sh b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/build.sh
new file mode 100755
index 00000000..53a9544e
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/build.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+set -euo pipefail
+
+# build the lambda-runtime
+cd $CODEBUILD_SRC_DIR
+mkdir build
+cd build
+cmake .. -GNinja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=/install $@
+ninja
+ninja install
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/format-check.sh b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/format-check.sh
new file mode 100755
index 00000000..3afb8023
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/ci/codebuild/format-check.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+set -euo pipefail
+
+CLANG_FORMAT=clang-format
+
+if NOT type $CLANG_FORMAT > /dev/null 2>&1; then
+ echo "No appropriate clang-format found."
+ exit 1
+fi
+
+FAIL=0
+SOURCE_FILES=$(find src include tests -type f -name "*.h" -o -name "*.cpp")
+for i in $SOURCE_FILES
+do
+ if [ $($CLANG_FORMAT -output-replacements-xml $i | grep -c "
+ DEPENDS ${target})
+endfunction()
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/Dockerfile b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/Dockerfile
new file mode 100644
index 00000000..aabb4dd4
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/Dockerfile
@@ -0,0 +1,3 @@
+FROM alpine:latest
+
+RUN apk update && apk add cmake make git g++ bash curl-dev zlib-dev
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/CMakeLists.txt b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/CMakeLists.txt
new file mode 100644
index 00000000..02da6ccf
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.5)
+set(CMAKE_CXX_STANDARD 11)
+
+project(api LANGUAGES CXX)
+
+find_package(aws-lambda-runtime REQUIRED)
+find_package(AWSSDK COMPONENTS core)
+
+add_executable(${PROJECT_NAME} "main.cpp")
+target_link_libraries(${PROJECT_NAME} PUBLIC AWS::aws-lambda-runtime ${AWSSDK_LINK_LIBRARIES})
+
+aws_lambda_package_target(${PROJECT_NAME})
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/README.md b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/README.md
new file mode 100644
index 00000000..d184165b
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/README.md
@@ -0,0 +1,81 @@
+# Example using the AWS C++ Lambda runtime and Amazon API Gateway
+
+In this example, we'll build a simple "Hello, World" lambda function that can be invoked using an api endpoint created using Amazon API gateway. This example can be viewed as the C++ counterpart to the NodeJS "Hello, World" API example as viewed [here](https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-create-api-as-simple-proxy-for-lambda.html). At the end of this example, you should be able to invoke your lambda via an api endpoint and receive a raw JSON response. This example employs the use of the AWS C++ SDK to parse the request and write the necessary response.
+
+## Build the AWS C++ SDK
+Start by building the SDK from source.
+
+```bash
+$ mkdir ~/install
+$ git clone https://github.com/aws/aws-sdk-cpp.git
+$ cd aws-sdk-cpp
+$ mkdir build
+$ cd build
+$ cmake .. -DBUILD_ONLY="core" \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DENABLE_UNITY_BUILD=ON \
+ -DCUSTOM_MEMORY_MANAGEMENT=OFF \
+ -DCMAKE_INSTALL_PREFIX=~/install \
+ -DENABLE_UNITY_BUILD=ON
+$ make
+$ make install
+```
+
+## Build the Runtime
+We need to build the C++ Lambda runtime as outlined in the other examples.
+
+```bash
+$ git clone https://github.com/awslabs/aws-lambda-cpp-runtime.git
+$ cd aws-lambda-cpp-runtime
+$ mkdir build
+$ cd build
+$ cmake .. -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DCMAKE_INSTALL_PREFIX=~/install \
+$ make
+$ make install
+```
+
+## Build the application
+The next step is to build the Lambda function in `main.cpp` and run the packaging command as follows:
+
+```bash
+$ mkdir build
+$ cd build
+$ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=~/install
+$ make
+$ make aws-lambda-package-api
+```
+
+You should now have a zip file called `api.zip`. Follow the instructions in the main README to upload it and return here once complete.
+
+## Using Amazon API Gateway
+For the rest of this example, we will use the AWS Management Console to create the API endpoint using Amazon API Gateway.
+
+1. Navigate to AWS Lambda within the console [here](https://console.aws.amazon.com/lambda/home)
+1. Select the newly created function. Within the specific function, the "Designer" window should appear.
+1. Simply click "Add trigger" -> "API Gateway" -> "Create an API". Please view the settings below.
+ * API Type: HTTP API
+ * Security: Open
+ * API name: Hello-World-API (or desired name)
+ * Deployment stage: default
+1. Once you have added the API gateway, locate the newly created endpoint. View how to test the endpoint below.
+
+## Test the endpoint
+Feel free to test the endpoint any way you desire. Below is a way to test using cURL:
+
+```
+curl -v -X POST \
+ '?name=Bradley&city=Chicago' \
+ -H 'content-type: application/json' \
+ -H 'day: Sunday' \
+ -d '{ "time": "evening" }'
+```
+
+With the expected response being:
+```
+{
+ "message": "Good evening, Bradley of Chicago. Happy Sunday!"
+}
+```
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/main.cpp b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/main.cpp
new file mode 100644
index 00000000..90f10355
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/api-gateway/main.cpp
@@ -0,0 +1,61 @@
+#include
+#include
+#include
+
+using namespace aws::lambda_runtime;
+
+invocation_response my_handler(invocation_request const& request)
+{
+
+ using namespace Aws::Utils::Json;
+
+ JsonValue json(request.payload);
+ if (!json.WasParseSuccessful()) {
+ return invocation_response::failure("Failed to parse input JSON", "InvalidJSON");
+ }
+
+ auto v = json.View();
+ Aws::SimpleStringStream ss;
+ ss << "Good ";
+
+ if (v.ValueExists("body") && v.GetObject("body").IsString()) {
+ auto body = v.GetString("body");
+ JsonValue body_json(body);
+
+ if (body_json.WasParseSuccessful()) {
+ auto body_v = body_json.View();
+ ss << (body_v.ValueExists("time") && body_v.GetObject("time").IsString() ? body_v.GetString("time") : "");
+ }
+ }
+ ss << ", ";
+
+ if (v.ValueExists("queryStringParameters")) {
+ auto query_params = v.GetObject("queryStringParameters");
+ ss << (query_params.ValueExists("name") && query_params.GetObject("name").IsString()
+ ? query_params.GetString("name")
+ : "")
+ << " of ";
+ ss << (query_params.ValueExists("city") && query_params.GetObject("city").IsString()
+ ? query_params.GetString("city")
+ : "")
+ << ". ";
+ }
+
+ if (v.ValueExists("headers")) {
+ auto headers = v.GetObject("headers");
+ ss << "Happy "
+ << (headers.ValueExists("day") && headers.GetObject("day").IsString() ? headers.GetString("day") : "")
+ << "!";
+ }
+
+ JsonValue resp;
+ resp.WithString("message", ss.str());
+
+ return invocation_response::success(resp.View().WriteCompact(), "application/json");
+}
+
+int main()
+{
+ run_handler(my_handler);
+ return 0;
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/CMakeLists.txt b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/CMakeLists.txt
new file mode 100644
index 00000000..8447e019
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/CMakeLists.txt
@@ -0,0 +1,24 @@
+cmake_minimum_required(VERSION 3.5)
+set(CMAKE_CXX_STANDARD 11)
+project(ddb-demo LANGUAGES CXX)
+
+find_package(aws-lambda-runtime)
+find_package(AWSSDK COMPONENTS dynamodb)
+
+add_executable(${PROJECT_NAME} "main.cpp")
+
+target_link_libraries(${PROJECT_NAME} PUBLIC AWS::aws-lambda-runtime ${AWSSDK_LINK_LIBRARIES})
+
+target_compile_options(${PROJECT_NAME} PRIVATE
+ "-fno-exceptions"
+ "-fno-rtti"
+ "-Wall"
+ "-Wextra"
+ "-Werror"
+ "-Wconversion"
+ "-Wno-sign-conversion")
+
+target_compile_features(${PROJECT_NAME} PRIVATE "cxx_std_11")
+
+aws_lambda_package_target(${PROJECT_NAME})
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/README.md b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/README.md
new file mode 100644
index 00000000..db84fd87
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/README.md
@@ -0,0 +1,52 @@
+# Example using the AWS C++ SDK with Lambda & DynamoDB
+
+We'll build a Lambda function that can be used as an API Gateway proxy to fetch records from a DynamoDB table.
+To also show case how this can be done on a Linux distro other than Amazon Linux, you can use the Dockerfile in this directory to create an Alpine Linux environment in which you can run the following instructions.
+
+That being said, the instructions below should work on any Linux distribution.
+
+## Build the AWS C++ SDK
+Start by building the SDK from source.
+```bash
+$ mkdir ~/install
+$ git clone https://github.com/aws/aws-sdk-cpp.git
+$ cd aws-sdk-cpp
+$ mkdir build
+$ cd build
+$ cmake .. -DBUILD_ONLY="dynamodb" \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DENABLE_UNITY_BUILD=ON \
+ -DCUSTOM_MEMORY_MANAGEMENT=OFF \
+ -DCMAKE_INSTALL_PREFIX=~/install \
+ -DENABLE_UNITY_BUILD=ON
+
+$ make -j 4
+$ make install
+```
+
+## Build the Runtime
+Now let's build the C++ Lambda runtime, so in a separate directory clone this repository and follow these steps:
+
+```bash
+$ git clone https://github.com/awslabs/aws-lambda-cpp-runtime.git
+$ cd aws-lambda-cpp-runtime
+$ mkdir build
+$ cd build
+$ cmake .. -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DCMAKE_INSTALL_PREFIX=~/install \
+$ make
+$ make install
+```
+
+## Build the application
+The last step is to build the Lambda function in `main.cpp` and run the packaging command as follows:
+
+```bash
+$ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=~/install
+$ make
+$ make aws-lambda-package-ddb-demo
+```
+
+You should now have a zip file called `ddb-demo.zip`. Follow the instructions in the main README to upload it and invoke the lambda.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/main.cpp b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/main.cpp
new file mode 100644
index 00000000..a8b86621
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/dynamodb/main.cpp
@@ -0,0 +1,229 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+// API Gateway Input format
+// {
+// "resource": "Resource path",
+// "path": "Path parameter",
+// "httpMethod": "Incoming request's method name"
+// "headers": {String containing incoming request headers}
+// "multiValueHeaders": {List of strings containing incoming request headers}
+// "queryStringParameters": {query string parameters }
+// "multiValueQueryStringParameters": {List of query string parameters}
+// "pathParameters": {path parameters}
+// "stageVariables": {Applicable stage variables}
+// "requestContext": {Request context, including authorizer-returned key-value pairs}
+// "body": "A JSON string of the request payload."
+// "isBase64Encoded": "A boolean flag to indicate if the applicable request payload is Base64-encode"
+// }
+
+static char const TAG[] = "lambda";
+
+struct criteria {
+ criteria(Aws::Utils::Json::JsonView data) : error_msg(nullptr)
+ {
+ using namespace Aws::Utils;
+ auto path_params = data.GetObject("pathParameters");
+ if (!path_params.ValueExists("productId")) {
+ error_msg = "Missing URL parameter {productId}.";
+ return;
+ }
+
+ product_id = path_params.GetString("productId");
+ auto qs = data.GetObject("queryStringParameters");
+
+ if (!qs.ValueExists("startDate")) {
+ error_msg = "Missing query string parameter 'startDate'.";
+ return;
+ }
+ start_date = DateTime(qs.GetString("startDate"), DateFormat::ISO_8601);
+ if (!start_date.WasParseSuccessful()) {
+ error_msg = "Invalid input format. startDate must be in ISO 8601 format.";
+ return;
+ }
+
+ if (!qs.ValueExists("endDate")) {
+ error_msg = "Missing query string parameter 'endDate'.";
+ return;
+ }
+ end_date = DateTime(qs.GetString("endDate"), DateFormat::ISO_8601);
+ if (!end_date.WasParseSuccessful()) {
+ error_msg = "Invalid input format. endDate must be in ISO 8601 format.";
+ return;
+ }
+ }
+
+ std::string product_id;
+ Aws::Utils::DateTime start_date;
+ Aws::Utils::DateTime end_date;
+ char const* error_msg;
+};
+
+Aws::Utils::Json::JsonValue query(criteria const cr, Aws::DynamoDB::DynamoDBClient const& client)
+{
+ using namespace Aws::DynamoDB;
+ using namespace Aws::DynamoDB::Model;
+ using namespace Aws::Utils::Json;
+
+ AWS_LOGSTREAM_DEBUG(
+ TAG,
+ "criteria is: product_id: " << cr.product_id << " start_date epoch: " << cr.start_date.Millis()
+ << " end_date epoch: " << cr.end_date.Millis());
+
+ QueryRequest query;
+
+ auto const& table_name = Aws::Environment::GetEnv("TABLE_NAME");
+ query.SetTableName(table_name);
+ query.SetKeyConditionExpression("#H = :h AND #R BETWEEN :s AND :e");
+ query.AddExpressionAttributeNames("#H", "product_id");
+ query.AddExpressionAttributeNames("#R", "date_time");
+
+ query.AddExpressionAttributeValues(":h", AttributeValue(cr.product_id));
+ AttributeValue date;
+ date.SetN(std::to_string(cr.start_date.Millis() / 1000));
+ query.AddExpressionAttributeValues(":s", date);
+
+ date.SetN(std::to_string(cr.end_date.Millis() / 1000));
+ query.AddExpressionAttributeValues(":e", date);
+
+ auto outcome = client.Query(query);
+ if (outcome.IsSuccess()) {
+ auto const& maps = outcome.GetResult().GetItems(); // returns vector of map
+ if (maps.empty()) {
+ AWS_LOGSTREAM_DEBUG(TAG, "No data returned from query");
+ return {};
+ }
+
+ // Schema
+ // string_attr :product_id, hash_key: true
+ // epoch_time_attr :date_time, range_key: true
+ // string_attr :product_title
+ // string_attr :marketplace
+ // string_attr :product_category
+ // date_attr :review_date
+ // integer_attr :star_rating
+ // float_attr :postive
+ // float_attr :mixed
+ // float_attr :neutral
+ // float_attr :negative
+
+ JsonValue output;
+ output.WithString("product", maps[0].find("product_title")->second.GetS());
+ output.WithString("category", maps[0].find("product_category")->second.GetS());
+ Aws::Utils::Array sentiments(maps.size());
+ for (size_t i = 0; i < maps.size(); i++) {
+ JsonValue review;
+ auto&& m = maps[i];
+
+ auto it = m.find("review_date");
+ if (it != m.end()) {
+ review.WithString("date", it->second.GetS());
+ }
+
+ it = m.find("positive");
+ if (it != m.end()) {
+ review.WithString("positive", it->second.GetN());
+ }
+
+ it = m.find("negative");
+ if (it != m.end()) {
+ review.WithString("negative", it->second.GetN());
+ }
+
+ it = m.find("mixed");
+ if (it != m.end()) {
+ review.WithString("mixed", it->second.GetN());
+ }
+
+ it = m.find("neutral");
+ if (it != m.end()) {
+ review.WithString("neutral", it->second.GetN());
+ }
+
+ sentiments[i] = std::move(review);
+ }
+ output.WithArray("sentiment", sentiments);
+ return output;
+ }
+
+ AWS_LOGSTREAM_ERROR(TAG, "database query failed: " << outcome.GetError());
+ return {};
+}
+
+
+aws::lambda_runtime::invocation_response my_handler(
+ aws::lambda_runtime::invocation_request const& req,
+ Aws::DynamoDB::DynamoDBClient const& client)
+{
+ using namespace Aws::Utils::Json;
+ AWS_LOGSTREAM_DEBUG(TAG, "received payload: " << req.payload);
+ JsonValue eventJson(req.payload);
+ assert(eventJson.WasParseSuccessful());
+ const criteria cr(eventJson);
+ if (cr.error_msg) {
+ JsonValue response;
+ response.WithString("body", cr.error_msg).WithInteger("statusCode", 400);
+ auto const apig_response = response.View().WriteCompact();
+ AWS_LOGSTREAM_ERROR(TAG, "Validation failed. " << apig_response);
+ return aws::lambda_runtime::invocation_response::success(apig_response, "application/json");
+ }
+
+ auto result = query(cr, client);
+ auto const query_response = result.View().WriteCompact();
+ AWS_LOGSTREAM_DEBUG(TAG, "query response: " << query_response);
+
+ JsonValue response;
+ if (result.View().ValueExists("product")) {
+ response.WithString("body", query_response).WithInteger("statusCode", 200);
+ }
+ else {
+ response.WithString("body", "No data found for this product.").WithInteger("statusCode", 400);
+ }
+
+ auto const apig_response = response.View().WriteCompact();
+ AWS_LOGSTREAM_DEBUG(TAG, "api gateway response: " << apig_response);
+
+ return aws::lambda_runtime::invocation_response::success(apig_response, "application/json");
+}
+
+std::function()> GetConsoleLoggerFactory()
+{
+ return [] {
+ return Aws::MakeShared(
+ "console_logger", Aws::Utils::Logging::LogLevel::Trace);
+ };
+}
+
+int main()
+{
+ using namespace Aws;
+ SDKOptions options;
+ options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
+ options.loggingOptions.logger_create_fn = GetConsoleLoggerFactory();
+ InitAPI(options);
+ {
+ Aws::Client::ClientConfiguration config;
+ config.region = Aws::Environment::GetEnv("AWS_REGION");
+ config.caFile = "/etc/pki/tls/certs/ca-bundle.crt";
+ config.disableExpectHeader = true;
+
+ auto credentialsProvider = Aws::MakeShared(TAG);
+ Aws::DynamoDB::DynamoDBClient client(credentialsProvider, config);
+ auto handler_fn = [&client](aws::lambda_runtime::invocation_request const& req) {
+ return my_handler(req, client);
+ };
+ aws::lambda_runtime::run_handler(handler_fn);
+ }
+ ShutdownAPI(options);
+ return 0;
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/CMakeLists.txt b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/CMakeLists.txt
new file mode 100644
index 00000000..b398a4e2
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/CMakeLists.txt
@@ -0,0 +1,22 @@
+cmake_minimum_required(VERSION 3.5)
+set(CMAKE_CXX_STANDARD 11)
+project(encoder LANGUAGES CXX)
+
+find_package(aws-lambda-runtime)
+find_package(AWSSDK COMPONENTS s3)
+
+add_executable(${PROJECT_NAME} "main.cpp")
+
+target_link_libraries(${PROJECT_NAME} PRIVATE AWS::aws-lambda-runtime ${AWSSDK_LINK_LIBRARIES})
+
+target_compile_options(${PROJECT_NAME} PRIVATE
+ "-Wall"
+ "-Wextra"
+ "-Wconversion"
+ "-Wshadow"
+ "-Wno-sign-conversion")
+
+target_compile_features(${PROJECT_NAME} PRIVATE "cxx_std_11")
+
+aws_lambda_package_target(${PROJECT_NAME})
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/README.md b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/README.md
new file mode 100644
index 00000000..8bc3255a
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/README.md
@@ -0,0 +1,51 @@
+# Example using the AWS C++ SDK with Lambda
+
+We'll build a lambda that downloads an image file from S3 and sends it back in the response as Base64 encoded that can be displayed in a web page for example.
+To also show case how this can be done on a Linux distro other than Amazon Linux, you can use the Dockerfile in this directory to create an Alpine Linux environment in which you can run the following instructions.
+
+That being said, the instructions below should work on any Linux distribution.
+
+## Build the AWS C++ SDK
+Start by building the SDK from source.
+```bash
+$ mkdir ~/install
+$ git clone https://github.com/aws/aws-sdk-cpp.git
+$ cd aws-sdk-cpp
+$ mkdir build
+$ cd build
+$ cmake .. -DBUILD_ONLY="s3" \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DCUSTOM_MEMORY_MANAGEMENT=OFF \
+ -DCMAKE_INSTALL_PREFIX=~/install \
+ -DENABLE_UNITY_BUILD=ON
+
+$ make
+$ make install
+```
+
+## Build the Runtime
+Now let's build the C++ Lambda runtime, so in a separate directory clone this repository and follow these steps:
+
+```bash
+$ git clone https://github.com/awslabs/aws-lambda-cpp-runtime.git
+$ cd aws-lambda-cpp-runtime
+$ mkdir build
+$ cd build
+$ cmake .. -DCMAKE_BUILD_TYPE=Release \
+ -DBUILD_SHARED_LIBS=OFF \
+ -DCMAKE_INSTALL_PREFIX=~/install \
+$ make
+$ make install
+```
+
+## Build the application
+The last step is to build the Lambda function in `main.cpp` and run the packaging command as follows:
+
+```bash
+$ cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_PREFIX_PATH=~/install
+$ make
+$ make aws-lambda-package-encoder
+```
+
+You should now have a zip file called `encoder.zip`. Follow the instructions in the main README to upload it and invoke the lambda.
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/main.cpp b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/main.cpp
new file mode 100644
index 00000000..45b935bf
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/examples/s3/main.cpp
@@ -0,0 +1,128 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+using namespace aws::lambda_runtime;
+
+std::string download_and_encode_file(
+ Aws::S3::S3Client const& client,
+ Aws::String const& bucket,
+ Aws::String const& key,
+ Aws::String& encoded_output);
+
+std::string encode(Aws::String const& filename, Aws::String& output);
+char const TAG[] = "LAMBDA_ALLOC";
+
+static invocation_response my_handler(invocation_request const& req, Aws::S3::S3Client const& client)
+{
+ using namespace Aws::Utils::Json;
+ JsonValue json(req.payload);
+ if (!json.WasParseSuccessful()) {
+ return invocation_response::failure("Failed to parse input JSON", "InvalidJSON");
+ }
+
+ auto v = json.View();
+
+ if (!v.ValueExists("s3bucket") || !v.ValueExists("s3key") || !v.GetObject("s3bucket").IsString() ||
+ !v.GetObject("s3key").IsString()) {
+ return invocation_response::failure("Missing input value s3bucket or s3key", "InvalidJSON");
+ }
+
+ auto bucket = v.GetString("s3bucket");
+ auto key = v.GetString("s3key");
+
+ AWS_LOGSTREAM_INFO(TAG, "Attempting to download file from s3://" << bucket << "/" << key);
+
+ Aws::String base64_encoded_file;
+ auto err = download_and_encode_file(client, bucket, key, base64_encoded_file);
+ if (!err.empty()) {
+ return invocation_response::failure(err, "DownloadFailure");
+ }
+
+ return invocation_response::success(base64_encoded_file, "application/base64");
+}
+
+std::function()> GetConsoleLoggerFactory()
+{
+ return [] {
+ return Aws::MakeShared(
+ "console_logger", Aws::Utils::Logging::LogLevel::Trace);
+ };
+}
+
+int main()
+{
+ using namespace Aws;
+ SDKOptions options;
+ options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace;
+ options.loggingOptions.logger_create_fn = GetConsoleLoggerFactory();
+ InitAPI(options);
+ {
+ Client::ClientConfiguration config;
+ config.region = Aws::Environment::GetEnv("AWS_REGION");
+ config.caFile = "/etc/pki/tls/certs/ca-bundle.crt";
+
+ auto credentialsProvider = Aws::MakeShared(TAG);
+ S3::S3Client client(credentialsProvider, config);
+ auto handler_fn = [&client](aws::lambda_runtime::invocation_request const& req) {
+ return my_handler(req, client);
+ };
+ run_handler(handler_fn);
+ }
+ ShutdownAPI(options);
+ return 0;
+}
+
+std::string encode(Aws::IOStream& stream, Aws::String& output)
+{
+ Aws::Vector bits;
+ bits.reserve(stream.tellp());
+ stream.seekg(0, stream.beg);
+
+ char streamBuffer[1024 * 4];
+ while (stream.good()) {
+ stream.read(streamBuffer, sizeof(streamBuffer));
+ auto bytesRead = stream.gcount();
+
+ if (bytesRead > 0) {
+ bits.insert(bits.end(), (unsigned char*)streamBuffer, (unsigned char*)streamBuffer + bytesRead);
+ }
+ }
+ Aws::Utils::ByteBuffer bb(bits.data(), bits.size());
+ output = Aws::Utils::HashingUtils::Base64Encode(bb);
+ return {};
+}
+
+std::string download_and_encode_file(
+ Aws::S3::S3Client const& client,
+ Aws::String const& bucket,
+ Aws::String const& key,
+ Aws::String& encoded_output)
+{
+ using namespace Aws;
+
+ S3::Model::GetObjectRequest request;
+ request.WithBucket(bucket).WithKey(key);
+
+ auto outcome = client.GetObject(request);
+ if (outcome.IsSuccess()) {
+ AWS_LOGSTREAM_INFO(TAG, "Download completed!");
+ auto& s = outcome.GetResult().GetBody();
+ return encode(s, encoded_output);
+ }
+ else {
+ AWS_LOGSTREAM_ERROR(TAG, "Failed with error: " << outcome.GetError());
+ return outcome.GetError().GetMessage();
+ }
+}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/http/response.h b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/http/response.h
new file mode 100644
index 00000000..9b8cbda1
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/http/response.h
@@ -0,0 +1,174 @@
+#pragma once
+/*
+ * Copyright 2018-present Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#include
+#include
+#include
+#include // tolower
+#include
+
+namespace aws {
+namespace http {
+enum class response_code;
+class response {
+public:
+ /**
+ * lower-case the name but store the value as is
+ */
+ inline void add_header(std::string name, std::string const& value);
+ inline void append_body(const char* p, size_t sz);
+ inline bool has_header(char const* header) const;
+ inline std::string const& get_header(char const* header) const;
+ inline response_code get_response_code() const { return m_response_code; }
+ inline void set_response_code(aws::http::response_code c);
+ inline void set_content_type(char const* ct);
+ inline std::string const& get_body() const;
+
+private:
+ response_code m_response_code;
+ using key_value_collection = std::vector>;
+ key_value_collection m_headers;
+ std::string m_body;
+ std::string m_content_type;
+};
+
+enum class response_code {
+ REQUEST_NOT_MADE = -1,
+ CONTINUE = 100,
+ SWITCHING_PROTOCOLS = 101,
+ PROCESSING = 102,
+ OK = 200,
+ CREATED = 201,
+ ACCEPTED = 202,
+ NON_AUTHORITATIVE_INFORMATION = 203,
+ NO_CONTENT = 204,
+ RESET_CONTENT = 205,
+ PARTIAL_CONTENT = 206,
+ MULTI_STATUS = 207,
+ ALREADY_REPORTED = 208,
+ IM_USED = 226,
+ MULTIPLE_CHOICES = 300,
+ MOVED_PERMANENTLY = 301,
+ FOUND = 302,
+ SEE_OTHER = 303,
+ NOT_MODIFIED = 304,
+ USE_PROXY = 305,
+ SWITCH_PROXY = 306,
+ TEMPORARY_REDIRECT = 307,
+ PERMANENT_REDIRECT = 308,
+ BAD_REQUEST = 400,
+ UNAUTHORIZED = 401,
+ PAYMENT_REQUIRED = 402,
+ FORBIDDEN = 403,
+ NOT_FOUND = 404,
+ METHOD_NOT_ALLOWED = 405,
+ NOT_ACCEPTABLE = 406,
+ PROXY_AUTHENTICATION_REQUIRED = 407,
+ REQUEST_TIMEOUT = 408,
+ CONFLICT = 409,
+ GONE = 410,
+ LENGTH_REQUIRED = 411,
+ PRECONDITION_FAILED = 412,
+ REQUEST_ENTITY_TOO_LARGE = 413,
+ REQUEST_URI_TOO_LONG = 414,
+ UNSUPPORTED_MEDIA_TYPE = 415,
+ REQUESTED_RANGE_NOT_SATISFIABLE = 416,
+ EXPECTATION_FAILED = 417,
+ IM_A_TEAPOT = 418,
+ AUTHENTICATION_TIMEOUT = 419,
+ METHOD_FAILURE = 420,
+ UNPROC_ENTITY = 422,
+ LOCKED = 423,
+ FAILED_DEPENDENCY = 424,
+ UPGRADE_REQUIRED = 426,
+ PRECONDITION_REQUIRED = 427,
+ TOO_MANY_REQUESTS = 429,
+ REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
+ LOGIN_TIMEOUT = 440,
+ NO_RESPONSE = 444,
+ RETRY_WITH = 449,
+ BLOCKED = 450,
+ REDIRECT = 451,
+ REQUEST_HEADER_TOO_LARGE = 494,
+ CERT_ERROR = 495,
+ NO_CERT = 496,
+ HTTP_TO_HTTPS = 497,
+ CLIENT_CLOSED_TO_REQUEST = 499,
+ INTERNAL_SERVER_ERROR = 500,
+ NOT_IMPLEMENTED = 501,
+ BAD_GATEWAY = 502,
+ SERVICE_UNAVAILABLE = 503,
+ GATEWAY_TIMEOUT = 504,
+ HTTP_VERSION_NOT_SUPPORTED = 505,
+ VARIANT_ALSO_NEGOTIATES = 506,
+ INSUFFICIENT_STORAGE = 506,
+ LOOP_DETECTED = 508,
+ BANDWIDTH_LIMIT_EXCEEDED = 509,
+ NOT_EXTENDED = 510,
+ NETWORK_AUTHENTICATION_REQUIRED = 511,
+ NETWORK_READ_TIMEOUT = 598,
+ NETWORK_CONNECT_TIMEOUT = 599
+};
+
+inline void response::set_response_code(http::response_code c)
+{
+ m_response_code = c;
+}
+
+inline void response::set_content_type(char const* ct)
+{
+ m_content_type = ct;
+}
+
+inline std::string const& response::get_body() const
+{
+ return m_body;
+}
+inline void response::add_header(std::string name, std::string const& value)
+{
+ std::transform(name.begin(), name.end(), name.begin(), ::tolower);
+ m_headers.emplace_back(name, value);
+}
+
+inline void response::append_body(const char* p, size_t sz)
+{
+ // simple and generates significantly less code than std::stringstream
+ constexpr size_t min_capacity = 512;
+ if (m_body.capacity() < min_capacity) {
+ m_body.reserve(min_capacity);
+ }
+
+ m_body.append(p, sz);
+}
+
+inline bool response::has_header(char const* header) const
+{
+ return std::any_of(m_headers.begin(), m_headers.end(), [header](std::pair const& p) {
+ return p.first == header;
+ });
+}
+
+inline std::string const& response::get_header(char const* header) const
+{
+ auto it = std::find_if(m_headers.begin(), m_headers.end(), [header](std::pair const& p) {
+ return p.first == header;
+ });
+ assert(it != m_headers.end());
+ return it->second;
+}
+
+} // namespace http
+} // namespace aws
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/outcome.h b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/outcome.h
new file mode 100644
index 00000000..b5d0b8b0
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/outcome.h
@@ -0,0 +1,96 @@
+#pragma once
+/*
+ * Copyright 2018-present Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#include
+#include
+
+namespace aws {
+namespace lambda_runtime {
+
+template
+class outcome {
+public:
+ outcome(TResult const& s) : m_s(s), m_success(true) {}
+ outcome(TResult&& s) : m_s(std::move(s)), m_success(true) {}
+
+ outcome(TFailure const& f) : m_f(f), m_success(false) {}
+ outcome(TFailure&& f) : m_f(std::move(f)), m_success(false) {}
+
+ outcome(outcome const& other) : m_success(other.m_success)
+ {
+ if (m_success) {
+ new (&m_s) TResult(other.m_s);
+ }
+ else {
+ new (&m_f) TFailure(other.m_f);
+ }
+ }
+
+ outcome(outcome&& other) noexcept : m_success(other.m_success)
+ {
+ if (m_success) {
+ new (&m_s) TResult(std::move(other.m_s));
+ }
+ else {
+ new (&m_f) TFailure(std::move(other.m_f));
+ }
+ }
+
+ ~outcome()
+ {
+ if (m_success) {
+ m_s.~TResult();
+ }
+ else {
+ m_f.~TFailure();
+ }
+ }
+
+ TResult const& get_result() const&
+ {
+ assert(m_success);
+ return m_s;
+ }
+
+ TResult&& get_result() &&
+ {
+ assert(m_success);
+ return std::move(m_s);
+ }
+
+ TFailure const& get_failure() const&
+ {
+ assert(!m_success);
+ return m_f;
+ }
+
+ TFailure&& get_failure() &&
+ {
+ assert(!m_success);
+ return std::move(m_f);
+ }
+
+ bool is_success() const { return m_success; }
+
+private:
+ union {
+ TResult m_s;
+ TFailure m_f;
+ };
+ bool m_success;
+};
+} // namespace lambda_runtime
+} // namespace aws
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/runtime.h b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/runtime.h
new file mode 100644
index 00000000..94e1e22c
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/runtime.h
@@ -0,0 +1,183 @@
+#pragma once
+/*
+ * Copyright 2018-present Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include "aws/lambda-runtime/outcome.h"
+#include "aws/http/response.h"
+
+namespace aws {
+namespace lambda_runtime {
+
+struct invocation_request {
+ /**
+ * The user's payload represented as a UTF-8 string.
+ */
+ std::string payload;
+
+ /**
+ * An identifier unique to the current invocation.
+ */
+ std::string request_id;
+
+ /**
+ * X-Ray tracing ID of the current invocation.
+ */
+ std::string xray_trace_id;
+
+ /**
+ * Information about the client application and device when invoked through the AWS Mobile SDK.
+ */
+ std::string client_context;
+
+ /**
+ * Information about the Amazon Cognito identity provider when invoked through the AWS Mobile SDK.
+ */
+ std::string cognito_identity;
+
+ /**
+ * The ARN requested. This can be different in each invoke that executes the same version.
+ */
+ std::string function_arn;
+
+ /**
+ * Function execution deadline counted in milliseconds since the Unix epoch.
+ */
+ std::chrono::time_point deadline;
+
+ /**
+ * The number of milliseconds left before lambda terminates the current execution.
+ */
+ inline std::chrono::milliseconds get_time_remaining() const;
+};
+
+class invocation_response {
+private:
+ /**
+ * The output of the function which is sent to the lambda caller.
+ */
+ std::string m_payload;
+
+ /**
+ * The MIME type of the payload.
+ * This is always set to 'application/json' in unsuccessful invocations.
+ */
+ std::string m_content_type;
+
+ /**
+ * Flag to distinguish if the contents are for successful or unsuccessful invocations.
+ */
+ bool m_success;
+
+ /**
+ * Instantiate an empty response. Used by the static functions 'success' and 'failure' to create a populated
+ * invocation_response
+ */
+ invocation_response() = default;
+
+public:
+ // Create a success or failure response. Typically, you should use the static functions invocation_response::success
+ // and invocation_response::failure, however, invocation_response::failure doesn't allow for arbitrary payloads.
+ // To support clients that need to control the entire error response body (e.g. adding a stack trace), this
+ // constructor should be used instead.
+ // Note: adding an overload to invocation_response::failure is not feasible since the parameter types are the same.
+ invocation_response(std::string const& payload, std::string const& content_type, bool success)
+ : m_payload(payload), m_content_type(content_type), m_success(success)
+ {
+ }
+
+ /**
+ * Create a successful invocation response with the given payload and content-type.
+ */
+ static invocation_response success(std::string const& payload, std::string const& content_type);
+
+ /**
+ * Create a failure response with the given error message and error type.
+ * The content-type is always set to application/json in this case.
+ */
+ static invocation_response failure(std::string const& error_message, std::string const& error_type);
+
+ /**
+ * Get the MIME type of the payload.
+ */
+ std::string const& get_content_type() const { return m_content_type; }
+
+ /**
+ * Get the payload string. The string is assumed to be UTF-8 encoded.
+ */
+ std::string const& get_payload() const { return m_payload; }
+
+ /**
+ * Returns true if the payload and content-type are set. Returns false if the error message and error types are set.
+ */
+ bool is_success() const { return m_success; }
+};
+
+struct no_result {
+};
+
+class runtime {
+public:
+ using next_outcome = aws::lambda_runtime::outcome;
+ using post_outcome = aws::lambda_runtime::outcome;
+
+ runtime(std::string const& endpoint, std::string const& user_agent);
+ runtime(std::string const& endpoint);
+ ~runtime();
+
+ /**
+ * Ask lambda for an invocation.
+ */
+ next_outcome get_next();
+
+ /**
+ * Tells lambda that the function has succeeded.
+ */
+ post_outcome post_success(std::string const& request_id, invocation_response const& handler_response);
+
+ /**
+ * Tells lambda that the function has failed.
+ */
+ post_outcome post_failure(std::string const& request_id, invocation_response const& handler_response);
+
+private:
+ void set_curl_next_options();
+ void set_curl_post_result_options();
+ post_outcome do_post(
+ std::string const& url,
+ std::string const& request_id,
+ invocation_response const& handler_response);
+
+private:
+ std::string const m_user_agent_header;
+ std::array const m_endpoints;
+ CURL* const m_curl_handle;
+};
+
+inline std::chrono::milliseconds invocation_request::get_time_remaining() const
+{
+ using namespace std::chrono;
+ return duration_cast(deadline - system_clock::now());
+}
+
+// Entry method
+void run_handler(std::function const& handler);
+
+} // namespace lambda_runtime
+} // namespace aws
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/version.h b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/version.h
new file mode 100644
index 00000000..eafcbde7
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/lambda-runtime/version.h
@@ -0,0 +1,41 @@
+#pragma once
+/*
+ * Copyright 2018-present Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+namespace aws {
+namespace lambda_runtime {
+
+/**
+ * Returns the major component of the library version.
+ */
+unsigned get_version_major();
+
+/**
+ * Returns the minor component of the library version.
+ */
+unsigned get_version_minor();
+
+/**
+ * Returns the patch component of the library version.
+ */
+unsigned get_version_patch();
+
+/**
+ * Returns the semantic version of the library in the form Major.Minor.Patch
+ */
+char const* get_version();
+
+} // namespace lambda_runtime
+} // namespace aws
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/logging/logging.h b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/logging/logging.h
new file mode 100644
index 00000000..0b5d0ef9
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/include/aws/logging/logging.h
@@ -0,0 +1,67 @@
+#pragma once
+/*
+ * Copyright 2018-present Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+#include
+
+namespace aws {
+namespace logging {
+
+enum class verbosity {
+ error,
+ info,
+ debug,
+};
+
+void log(verbosity v, char const* tag, char const* msg, va_list args);
+
+[[gnu::format(printf, 2, 3)]] inline void log_error(char const* tag, char const* msg, ...)
+{
+ va_list args;
+ va_start(args, msg);
+ log(verbosity::error, tag, msg, args);
+ va_end(args);
+ (void)tag;
+ (void)msg;
+}
+
+[[gnu::format(printf, 2, 3)]] inline void log_info(char const* tag, char const* msg, ...)
+{
+#if AWS_LAMBDA_LOG >= 1
+ va_list args;
+ va_start(args, msg);
+ log(verbosity::info, tag, msg, args);
+ va_end(args);
+#else
+ (void)tag;
+ (void)msg;
+#endif
+}
+
+[[gnu::format(printf, 2, 3)]] inline void log_debug(char const* tag, char const* msg, ...)
+{
+#if AWS_LAMBDA_LOG >= 2
+ va_list args;
+ va_start(args, msg);
+ log(verbosity::debug, tag, msg, args);
+ va_end(args);
+#else
+ (void)tag;
+ (void)msg;
+#endif
+}
+
+} // namespace logging
+} // namespace aws
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/packaging/packager b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/packaging/packager
new file mode 100755
index 00000000..d3338916
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/packaging/packager
@@ -0,0 +1,180 @@
+#!/bin/bash
+# Copyright 2018-present Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License").
+# You may not use this file except in compliance with the License.
+# A copy of the License is located at
+#
+# http://aws.amazon.com/apache2.0
+#
+# or in the "license" file accompanying this file. This file is distributed
+# on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+# express or implied. See the License for the specific language governing
+# permissions and limitations under the License.
+
+set -euo pipefail
+
+print_help() {
+ echo -e "Usage: packager [OPTIONS] \n"
+ echo -e "OPTIONS\n"
+ echo -e "\t-d,--default-libc\t Use the target host libc libraries. This will not package the C library files.\n"
+}
+
+if [ $# -lt 1 ]; then
+ echo -e "Error: missing arguments\n"
+ print_help
+ exit 1
+fi
+
+POSITIONAL=()
+INCLUDE_LIBC=true
+while [[ $# -gt 0 ]]
+do
+ key="$1"
+ case $key in
+ -d|--default-libc)
+ INCLUDE_LIBC=false
+ shift # past argument
+ ;;
+ *) # unknown option
+ POSITIONAL+=("$1") # save it in an array for later
+ shift # past argument
+ ;;
+ esac
+done
+set -- "${POSITIONAL[@]}" # restore positional parameters
+
+PKG_BIN_PATH=$1
+
+if [ ! -f "$PKG_BIN_PATH" ]; then
+ echo "$PKG_BIN_PATH" - No such file.;
+ exit 1;
+fi
+
+if ! type zip > /dev/null 2>&1; then
+ echo "zip utility is not found. Please install it and re-run this script"
+ exit 1
+fi
+function package_libc_via_pacman {
+ if grep --extended-regexp "Arch Linux|Manjaro Linux" < /etc/os-release > /dev/null 2>&1; then
+ if type pacman > /dev/null 2>&1; then
+ pacman --query --list --quiet glibc | sed -E '/\.so$|\.so\.[0-9]+$/!d'
+ fi
+ fi
+}
+
+function package_libc_via_dpkg() {
+ if type dpkg-query > /dev/null 2>&1; then
+ if [[ $(dpkg-query --listfiles libc6 | wc -l) -gt 0 ]]; then
+ dpkg-query --listfiles libc6 | sed -E '/\.so$|\.so\.[0-9]+$/!d'
+ fi
+ fi
+}
+
+function package_libc_via_rpm() {
+ if type rpm > /dev/null 2>&1; then
+ if [[ $(rpm --query --list glibc.x86_64 | wc -l) -gt 1 ]]; then
+ rpm --query --list glibc.x86_64 | sed -E '/\.so$|\.so\.[0-9]+$/!d'
+ fi
+ fi
+}
+
+# hasElement expects an element and an array parameter
+# it's equivalent to array.contains(element)
+# e.g. hasElement "needle" ${haystack[@]}
+function hasElement() {
+ local el key=$1
+ shift
+ for el in "$@"
+ do
+ [[ "$el" == "$key" ]] && return 0
+ done
+ return 1
+}
+
+PKG_BIN_FILENAME=$(basename "$PKG_BIN_PATH")
+PKG_DIR=tmp
+PKG_LD=""
+
+list=$(ldd "$PKG_BIN_PATH" | awk '{print $(NF-1)}')
+libc_libs=()
+libc_libs+=($(package_libc_via_dpkg))
+libc_libs+=($(package_libc_via_rpm))
+libc_libs+=($(package_libc_via_pacman))
+
+mkdir -p "$PKG_DIR/bin" "$PKG_DIR/lib"
+
+for i in $list
+do
+ if [[ ! -f $i ]]; then # ignore linux-vdso.so.1
+ continue
+ fi
+
+ # Do not copy libc files which are directly linked unless it's the dynamic loader
+ if hasElement "$i" "${libc_libs[@]}"; then
+ filename=$(basename "$i")
+ if [[ -z "${filename##ld-*}" ]]; then
+ PKG_LD=$filename # Use this file as the loader
+ cp "$i" "$PKG_DIR/lib"
+ fi
+ continue
+ fi
+
+ cp "$i" $PKG_DIR/lib
+done
+
+if [[ $INCLUDE_LIBC == true ]]; then
+ for i in "${libc_libs[@]}"
+ do
+ filename=$(basename "$i")
+ if [[ -z "${filename##ld-*}" ]]; then
+ # if the loader is empty, then the binary is probably linked to a symlink of the loader. The symlink will
+ # not show up when quering the package manager for libc files. So, in this case, we want to copy the loader
+ if [[ -z "$PKG_LD" ]]; then
+ PKG_LD=$filename
+ cp "$i" "$PKG_DIR/lib" # we want to follow the symlink (default behavior)
+ fi
+ continue # We don't want the dynamic loader's symlink because its target is an absolute path (/lib/ld-*).
+ fi
+ cp --no-dereference "$i" "$PKG_DIR/lib"
+ done
+fi
+
+if [[ -z "$PKG_LD" ]]; then
+ echo "Failed to identify, locate or package the loader. Please file an issue on Github!" 1>&2
+ exit 1
+fi
+
+bootstrap_script=$(cat < "$PKG_DIR/bootstrap"
+else
+ echo -e "$bootstrap_script_no_libc" > "$PKG_DIR/bootstrap"
+fi
+chmod +x "$PKG_DIR/bootstrap"
+# some shenanigans to create the right layout in the zip file without extraneous directories
+pushd "$PKG_DIR" > /dev/null
+zip --symlinks --recurse-paths "$PKG_BIN_FILENAME".zip -- *
+ORIGIN_DIR=$(dirs -l +1)
+mv "$PKG_BIN_FILENAME".zip "$ORIGIN_DIR"
+popd > /dev/null
+rm -r "$PKG_DIR"
+echo Created "$ORIGIN_DIR/$PKG_BIN_FILENAME".zip
+
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/backward.cpp b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/backward.cpp
new file mode 100644
index 00000000..cc64abdb
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/backward.cpp
@@ -0,0 +1,32 @@
+// Pick your poison.
+//
+// On GNU/Linux, you have few choices to get the most out of your stack trace.
+//
+// By default you get:
+// - object filename
+// - function name
+//
+// In order to add:
+// - source filename
+// - line and column numbers
+// - source code snippet (assuming the file is accessible)
+
+// Install one of the following library then uncomment one of the macro (or
+// better, add the detection of the lib and the macro definition in your build
+// system)
+
+// - apt-get install libdw-dev ...
+// - g++/clang++ -ldw ...
+// #define BACKWARD_HAS_DW 1
+
+// - apt-get install binutils-dev ...
+// - g++/clang++ -lbfd ...
+// #define BACKWARD_HAS_BFD 1
+
+#include "backward.h"
+
+namespace backward {
+
+backward::SignalHandling sh;
+
+} // namespace backward
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/backward.h b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/backward.h
new file mode 100644
index 00000000..e9e56c79
--- /dev/null
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/backward.h
@@ -0,0 +1,4291 @@
+// clang-format off
+/*
+ * backward.hpp
+ * Copyright 2013 Google Inc. All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#ifndef H_6B9572DA_A64B_49E6_B234_051480991C89
+#define H_6B9572DA_A64B_49E6_B234_051480991C89
+
+#ifndef __cplusplus
+# error "It's not going to compile without a C++ compiler..."
+#endif
+
+#if defined(BACKWARD_CXX11)
+#elif defined(BACKWARD_CXX98)
+#else
+# if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+# define BACKWARD_CXX11
+# define BACKWARD_ATLEAST_CXX11
+# define BACKWARD_ATLEAST_CXX98
+# else
+# define BACKWARD_CXX98
+# define BACKWARD_ATLEAST_CXX98
+# endif
+#endif
+
+// You can define one of the following (or leave it to the auto-detection):
+//
+// #define BACKWARD_SYSTEM_LINUX
+// - specialization for linux
+//
+// #define BACKWARD_SYSTEM_DARWIN
+// - specialization for Mac OS X 10.5 and later.
+//
+// #define BACKWARD_SYSTEM_UNKNOWN
+// - placebo implementation, does nothing.
+//
+#if defined(BACKWARD_SYSTEM_LINUX)
+#elif defined(BACKWARD_SYSTEM_DARWIN)
+#elif defined(BACKWARD_SYSTEM_UNKNOWN)
+#elif defined(BACKWARD_SYSTEM_WINDOWS)
+#else
+# if defined(__linux) || defined(__linux__)
+# define BACKWARD_SYSTEM_LINUX
+# elif defined(__APPLE__)
+# define BACKWARD_SYSTEM_DARWIN
+# elif defined(_WIN32)
+# define BACKWARD_SYSTEM_WINDOWS
+# else
+# define BACKWARD_SYSTEM_UNKNOWN
+# endif
+#endif
+
+#define NOINLINE __attribute__((noinline))
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if defined(BACKWARD_SYSTEM_LINUX)
+
+// On linux, backtrace can back-trace or "walk" the stack using the following
+// libraries:
+//
+// #define BACKWARD_HAS_UNWIND 1
+// - unwind comes from libgcc, but I saw an equivalent inside clang itself.
+// - with unwind, the stacktrace is as accurate as it can possibly be, since
+// this is used by the C++ runtine in gcc/clang for stack unwinding on
+// exception.
+// - normally libgcc is already linked to your program by default.
+//
+// #define BACKWARD_HAS_BACKTRACE == 1
+// - backtrace seems to be a little bit more portable than libunwind, but on
+// linux, it uses unwind anyway, but abstract away a tiny information that is
+// sadly really important in order to get perfectly accurate stack traces.
+// - backtrace is part of the (e)glib library.
+//
+// The default is:
+// #define BACKWARD_HAS_UNWIND == 1
+//
+// Note that only one of the define should be set to 1 at a time.
+//
+# if BACKWARD_HAS_UNWIND == 1
+# elif BACKWARD_HAS_BACKTRACE == 1
+# else
+# undef BACKWARD_HAS_UNWIND
+# define BACKWARD_HAS_UNWIND 1
+# undef BACKWARD_HAS_BACKTRACE
+# define BACKWARD_HAS_BACKTRACE 0
+# endif
+
+// On linux, backward can extract detailed information about a stack trace
+// using one of the following libraries:
+//
+// #define BACKWARD_HAS_DW 1
+// - libdw gives you the most juicy details out of your stack traces:
+// - object filename
+// - function name
+// - source filename
+// - line and column numbers
+// - source code snippet (assuming the file is accessible)
+// - variables name and values (if not optimized out)
+// - You need to link with the lib "dw":
+// - apt-get install libdw-dev
+// - g++/clang++ -ldw ...
+//
+// #define BACKWARD_HAS_BFD 1
+// - With libbfd, you get a fair amount of details:
+// - object filename
+// - function name
+// - source filename
+// - line numbers
+// - source code snippet (assuming the file is accessible)
+// - You need to link with the lib "bfd":
+// - apt-get install binutils-dev
+// - g++/clang++ -lbfd ...
+//
+// #define BACKWARD_HAS_DWARF 1
+// - libdwarf gives you the most juicy details out of your stack traces:
+// - object filename
+// - function name
+// - source filename
+// - line and column numbers
+// - source code snippet (assuming the file is accessible)
+// - variables name and values (if not optimized out)
+// - You need to link with the lib "dwarf":
+// - apt-get install libdwarf-dev
+// - g++/clang++ -ldwarf ...
+//
+// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1
+// - backtrace provides minimal details for a stack trace:
+// - object filename
+// - function name
+// - backtrace is part of the (e)glib library.
+//
+// The default is:
+// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1
+//
+// Note that only one of the define should be set to 1 at a time.
+//
+# if BACKWARD_HAS_DW == 1
+# elif BACKWARD_HAS_BFD == 1
+# elif BACKWARD_HAS_DWARF == 1
+# elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1
+# else
+# undef BACKWARD_HAS_DW
+# define BACKWARD_HAS_DW 0
+# undef BACKWARD_HAS_BFD
+# define BACKWARD_HAS_BFD 0
+# undef BACKWARD_HAS_DWARF
+# define BACKWARD_HAS_DWARF 0
+# undef BACKWARD_HAS_BACKTRACE_SYMBOL
+# define BACKWARD_HAS_BACKTRACE_SYMBOL 1
+# endif
+
+# include
+# include
+# ifdef __ANDROID__
+// Old Android API levels define _Unwind_Ptr in both link.h and
+// unwind.h Rename the one in link.h as we are not going to be using
+// it
+# define _Unwind_Ptr _Unwind_Ptr_Custom
+# include
+# undef _Unwind_Ptr
+# else
+# include
+# endif
+# include
+# include
+# include
+# include
+
+# if BACKWARD_HAS_BFD == 1
+// NOTE: defining PACKAGE{,_VERSION} is required before including
+// bfd.h on some platforms, see also:
+// https://sourceware.org/bugzilla/show_bug.cgi?id=14243
+# ifndef PACKAGE
+# define PACKAGE
+# endif
+# ifndef PACKAGE_VERSION
+# define PACKAGE_VERSION
+# endif
+# include
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# include
+# undef _GNU_SOURCE
+# else
+# include
+# endif
+# endif
+
+# if BACKWARD_HAS_DW == 1
+# include
+# include
+# include
+# endif
+
+# if BACKWARD_HAS_DWARF == 1
+# include
+# include
+# include
+# include
+# include
+# ifndef _GNU_SOURCE
+# define _GNU_SOURCE
+# include
+# undef _GNU_SOURCE
+# else
+# include
+# endif
+# endif
+
+# if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1)
+// then we shall rely on backtrace
+# include
+# endif
+
+#endif // defined(BACKWARD_SYSTEM_LINUX)
+
+#if defined(BACKWARD_SYSTEM_DARWIN)
+// On Darwin, backtrace can back-trace or "walk" the stack using the following
+// libraries:
+//
+// #define BACKWARD_HAS_UNWIND 1
+// - unwind comes from libgcc, but I saw an equivalent inside clang itself.
+// - with unwind, the stacktrace is as accurate as it can possibly be, since
+// this is used by the C++ runtine in gcc/clang for stack unwinding on
+// exception.
+// - normally libgcc is already linked to your program by default.
+//
+// #define BACKWARD_HAS_BACKTRACE == 1
+// - backtrace is available by default, though it does not produce as much
+// information as another library might.
+//
+// The default is:
+// #define BACKWARD_HAS_UNWIND == 1
+//
+// Note that only one of the define should be set to 1 at a time.
+//
+# if BACKWARD_HAS_UNWIND == 1
+# elif BACKWARD_HAS_BACKTRACE == 1
+# else
+# undef BACKWARD_HAS_UNWIND
+# define BACKWARD_HAS_UNWIND 1
+# undef BACKWARD_HAS_BACKTRACE
+# define BACKWARD_HAS_BACKTRACE 0
+# endif
+
+// On Darwin, backward can extract detailed information about a stack trace
+// using one of the following libraries:
+//
+// #define BACKWARD_HAS_BACKTRACE_SYMBOL 1
+// - backtrace provides minimal details for a stack trace:
+// - object filename
+// - function name
+//
+// The default is:
+// #define BACKWARD_HAS_BACKTRACE_SYMBOL == 1
+//
+# if BACKWARD_HAS_BACKTRACE_SYMBOL == 1
+# else
+# undef BACKWARD_HAS_BACKTRACE_SYMBOL
+# define BACKWARD_HAS_BACKTRACE_SYMBOL 1
+# endif
+
+# include
+# include
+# include
+# include
+# include
+# include
+
+# if (BACKWARD_HAS_BACKTRACE == 1) || (BACKWARD_HAS_BACKTRACE_SYMBOL == 1)
+# include
+# endif
+#endif // defined(BACKWARD_SYSTEM_DARWIN)
+
+#if defined(BACKWARD_SYSTEM_WINDOWS)
+
+# include
+# include
+# include
+
+# include
+typedef SSIZE_T ssize_t;
+
+# define NOMINMAX
+# include
+# include
+
+# include
+# include
+
+# ifndef __clang__
+# undef NOINLINE
+# define NOINLINE __declspec(noinline)
+# endif
+
+# pragma comment(lib, "psapi.lib")
+# pragma comment(lib, "dbghelp.lib")
+
+// Comment / packing is from stackoverflow:
+// https://stackoverflow.com/questions/6205981/windows-c-stack-trace-from-a-running-app/28276227#28276227
+// Some versions of imagehlp.dll lack the proper packing directives themselves
+// so we need to do it.
+# pragma pack(push, before_imagehlp, 8)
+# include
+# pragma pack(pop, before_imagehlp)
+
+// TODO maybe these should be undefined somewhere else?
+# undef BACKWARD_HAS_UNWIND
+# undef BACKWARD_HAS_BACKTRACE
+# if BACKWARD_HAS_PDB_SYMBOL == 1
+# else
+# undef BACKWARD_HAS_PDB_SYMBOL
+# define BACKWARD_HAS_PDB_SYMBOL 1
+# endif
+
+#endif
+
+#if BACKWARD_HAS_UNWIND == 1
+
+# include
+// while gcc's unwind.h defines something like that:
+// extern _Unwind_Ptr _Unwind_GetIP (struct _Unwind_Context *);
+// extern _Unwind_Ptr _Unwind_GetIPInfo (struct _Unwind_Context *, int *);
+//
+// clang's unwind.h defines something like this:
+// uintptr_t _Unwind_GetIP(struct _Unwind_Context* __context);
+//
+// Even if the _Unwind_GetIPInfo can be linked to, it is not declared, worse we
+// cannot just redeclare it because clang's unwind.h doesn't define _Unwind_Ptr
+// anyway.
+//
+// Luckily we can play on the fact that the guard macros have a different name:
+# ifdef __CLANG_UNWIND_H
+// In fact, this function still comes from libgcc (on my different linux boxes,
+// clang links against libgcc).
+# include
+extern "C" uintptr_t _Unwind_GetIPInfo(_Unwind_Context*, int*);
+# endif
+
+#endif // BACKWARD_HAS_UNWIND == 1
+
+#ifdef BACKWARD_ATLEAST_CXX11
+# include
+# include // for std::swap
+namespace backward {
+namespace details {
+template
+struct hashtable {
+ typedef std::unordered_map type;
+};
+using std::move;
+} // namespace details
+} // namespace backward
+#else // NOT BACKWARD_ATLEAST_CXX11
+# define nullptr NULL
+# define override
+# include
+namespace backward {
+namespace details {
+template
+struct hashtable {
+ typedef std::map type;
+};
+template
+const T& move(const T& v)
+{
+ return v;
+}
+template
+T& move(T& v)
+{
+ return v;
+}
+} // namespace details
+} // namespace backward
+#endif // BACKWARD_ATLEAST_CXX11
+
+namespace backward {
+namespace details {
+#if defined(BACKWARD_SYSTEM_WINDOWS)
+const char kBackwardPathDelimiter[] = ";";
+#else
+const char kBackwardPathDelimiter[] = ":";
+#endif
+} // namespace details
+} // namespace backward
+
+namespace backward {
+
+namespace system_tag {
+struct linux_tag; // seems that I cannot call that "linux" because the name
+// is already defined... so I am adding _tag everywhere.
+struct darwin_tag;
+struct windows_tag;
+struct unknown_tag;
+
+#if defined(BACKWARD_SYSTEM_LINUX)
+typedef linux_tag current_tag;
+#elif defined(BACKWARD_SYSTEM_DARWIN)
+typedef darwin_tag current_tag;
+#elif defined(BACKWARD_SYSTEM_WINDOWS)
+typedef windows_tag current_tag;
+#elif defined(BACKWARD_SYSTEM_UNKNOWN)
+typedef unknown_tag current_tag;
+#else
+# error "May I please get my system defines?"
+#endif
+} // namespace system_tag
+
+namespace trace_resolver_tag {
+#if defined(BACKWARD_SYSTEM_LINUX)
+struct libdw;
+struct libbfd;
+struct libdwarf;
+struct backtrace_symbol;
+
+# if BACKWARD_HAS_DW == 1
+typedef libdw current;
+# elif BACKWARD_HAS_BFD == 1
+typedef libbfd current;
+# elif BACKWARD_HAS_DWARF == 1
+typedef libdwarf current;
+# elif BACKWARD_HAS_BACKTRACE_SYMBOL == 1
+typedef backtrace_symbol current;
+# else
+# error "You shall not pass, until you know what you want."
+# endif
+#elif defined(BACKWARD_SYSTEM_DARWIN)
+struct backtrace_symbol;
+
+# if BACKWARD_HAS_BACKTRACE_SYMBOL == 1
+typedef backtrace_symbol current;
+# else
+# error "You shall not pass, until you know what you want."
+# endif
+#elif defined(BACKWARD_SYSTEM_WINDOWS)
+struct pdb_symbol;
+# if BACKWARD_HAS_PDB_SYMBOL == 1
+typedef pdb_symbol current;
+# else
+# error "You shall not pass, until you know what you want."
+# endif
+#endif
+} // namespace trace_resolver_tag
+
+namespace details {
+
+template
+struct rm_ptr {
+ typedef T type;
+};
+
+template
+struct rm_ptr {
+ typedef T type;
+};
+
+template
+struct rm_ptr {
+ typedef const T type;
+};
+
+template
+struct deleter {
+ template
+ void operator()(U& ptr) const
+ {
+ (*F)(ptr);
+ }
+};
+
+template
+struct default_delete {
+ void operator()(T& ptr) const { delete ptr; }
+};
+
+template >
+class handle {
+ struct dummy;
+ T _val;
+ bool _empty;
+
+#ifdef BACKWARD_ATLEAST_CXX11
+ handle(const handle&) = delete;
+ handle& operator=(const handle&) = delete;
+#endif
+
+public:
+ ~handle()
+ {
+ if (!_empty) {
+ Deleter()(_val);
+ }
+ }
+
+ explicit handle() : _val(), _empty(true) {}
+ explicit handle(T val) : _val(val), _empty(false)
+ {
+ if (!_val)
+ _empty = true;
+ }
+
+#ifdef BACKWARD_ATLEAST_CXX11
+ handle(handle&& from) : _empty(true) { swap(from); }
+ handle& operator=(handle&& from)
+ {
+ swap(from);
+ return *this;
+ }
+#else
+ explicit handle(const handle& from) : _empty(true)
+ {
+ // some sort of poor man's move semantic.
+ swap(const_cast(from));
+ }
+ handle& operator=(const handle& from)
+ {
+ // some sort of poor man's move semantic.
+ swap(const_cast(from));
+ return *this;
+ }
+#endif
+
+ void reset(T new_val)
+ {
+ handle tmp(new_val);
+ swap(tmp);
+ }
+
+ void update(T new_val)
+ {
+ _val = new_val;
+ _empty = static_cast(new_val);
+ }
+
+ operator const dummy*() const
+ {
+ if (_empty) {
+ return nullptr;
+ }
+ return reinterpret_cast(_val);
+ }
+ T get() { return _val; }
+ T release()
+ {
+ _empty = true;
+ return _val;
+ }
+ void swap(handle& b)
+ {
+ using std::swap;
+ swap(b._val, _val); // can throw, we are safe here.
+ swap(b._empty, _empty); // should not throw: if you cannot swap two
+ // bools without throwing... It's a lost cause anyway!
+ }
+
+ T& operator->() { return _val; }
+ const T& operator->() const { return _val; }
+
+ typedef typename rm_ptr::type& ref_t;
+ typedef const typename rm_ptr::type& const_ref_t;
+ ref_t operator*() { return *_val; }
+ const_ref_t operator*() const { return *_val; }
+ ref_t operator[](size_t idx) { return _val[idx]; }
+
+ // Watch out, we've got a badass over here
+ T* operator&()
+ {
+ _empty = false;
+ return &_val;
+ }
+};
+
+// Default demangler implementation (do nothing).
+template
+struct demangler_impl {
+ static std::string demangle(const char* funcname) { return funcname; }
+};
+
+#if defined(BACKWARD_SYSTEM_LINUX) || defined(BACKWARD_SYSTEM_DARWIN)
+
+template <>
+struct demangler_impl {
+ demangler_impl() : _demangle_buffer_length(0) {}
+
+ std::string demangle(const char* funcname)
+ {
+ using namespace details;
+ char* result = abi::__cxa_demangle(funcname, _demangle_buffer.get(), &_demangle_buffer_length, nullptr);
+ if (result) {
+ _demangle_buffer.update(result);
+ return result;
+ }
+ return funcname;
+ }
+
+private:
+ details::handle _demangle_buffer;
+ size_t _demangle_buffer_length;
+};
+
+#endif // BACKWARD_SYSTEM_LINUX || BACKWARD_SYSTEM_DARWIN
+
+struct demangler : public demangler_impl {
+};
+
+// Split a string on the platform's PATH delimiter. Example: if delimiter
+// is ":" then:
+// "" --> []
+// ":" --> ["",""]
+// "::" --> ["","",""]
+// "/a/b/c" --> ["/a/b/c"]
+// "/a/b/c:/d/e/f" --> ["/a/b/c","/d/e/f"]
+// etc.
+inline std::vector split_source_prefixes(const std::string& s)
+{
+ std::vector out;
+ size_t last = 0;
+ size_t next = 0;
+ size_t delimiter_size = sizeof(kBackwardPathDelimiter) - 1;
+ while ((next = s.find(kBackwardPathDelimiter, last)) != std::string::npos) {
+ out.push_back(s.substr(last, next - last));
+ last = next + delimiter_size;
+ }
+ if (last <= s.length()) {
+ out.push_back(s.substr(last));
+ }
+ return out;
+}
+
+} // namespace details
+
+/*************** A TRACE ***************/
+
+struct Trace {
+ void* addr;
+ size_t idx;
+
+ Trace() : addr(nullptr), idx(0) {}
+
+ explicit Trace(void* _addr, size_t _idx) : addr(_addr), idx(_idx) {}
+};
+
+struct ResolvedTrace : public Trace {
+
+ struct SourceLoc {
+ std::string function;
+ std::string filename;
+ unsigned line;
+ unsigned col;
+
+ SourceLoc() : line(0), col(0) {}
+
+ bool operator==(const SourceLoc& b) const
+ {
+ return function == b.function && filename == b.filename && line == b.line && col == b.col;
+ }
+
+ bool operator!=(const SourceLoc& b) const { return !(*this == b); }
+ };
+
+ // In which binary object this trace is located.
+ std::string object_filename;
+
+ // The function in the object that contain the trace. This is not the same
+ // as source.function which can be an function inlined in object_function.
+ std::string object_function;
+
+ // The source location of this trace. It is possible for filename to be
+ // empty and for line/col to be invalid (value 0) if this information
+ // couldn't be deduced, for example if there is no debug information in the
+ // binary object.
+ SourceLoc source;
+
+ // An optionals list of "inliners". All the successive sources location
+ // from where the source location of the trace (the attribute right above)
+ // is inlined. It is especially useful when you compiled with optimization.
+ typedef std::vector source_locs_t;
+ source_locs_t inliners;
+
+ ResolvedTrace() : Trace() {}
+ ResolvedTrace(const Trace& mini_trace) : Trace(mini_trace) {}
+};
+
+/*************** STACK TRACE ***************/
+
+// default implemention.
+template
+class StackTraceImpl {
+public:
+ size_t size() const { return 0; }
+ Trace operator[](size_t) const { return Trace(); }
+ size_t load_here(size_t = 0) { return 0; }
+ size_t load_from(void*, size_t = 0) { return 0; }
+ size_t thread_id() const { return 0; }
+ void skip_n_firsts(size_t) {}
+};
+
+class StackTraceImplBase {
+public:
+ StackTraceImplBase() : _thread_id(0), _skip(0) {}
+
+ size_t thread_id() const { return _thread_id; }
+
+ void skip_n_firsts(size_t n) { _skip = n; }
+
+protected:
+ void load_thread_info()
+ {
+#ifdef BACKWARD_SYSTEM_LINUX
+# ifndef __ANDROID__
+ _thread_id = static_cast(syscall(SYS_gettid));
+# else
+ _thread_id = static_cast(gettid());
+# endif
+ if (_thread_id == static_cast(getpid())) {
+ // If the thread is the main one, let's hide that.
+ // I like to keep little secret sometimes.
+ _thread_id = 0;
+ }
+#elif defined(BACKWARD_SYSTEM_DARWIN)
+ _thread_id = reinterpret_cast(pthread_self());
+ if (pthread_main_np() == 1) {
+ // If the thread is the main one, let's hide that.
+ _thread_id = 0;
+ }
+#endif
+ }
+
+ size_t skip_n_firsts() const { return _skip; }
+
+private:
+ size_t _thread_id;
+ size_t _skip;
+};
+
+class StackTraceImplHolder : public StackTraceImplBase {
+public:
+ size_t size() const { return _stacktrace.size() ? _stacktrace.size() - skip_n_firsts() : 0; }
+ Trace operator[](size_t idx) const
+ {
+ if (idx >= size()) {
+ return Trace();
+ }
+ return Trace(_stacktrace[idx + skip_n_firsts()], idx);
+ }
+ void* const* begin() const
+ {
+ if (size()) {
+ return &_stacktrace[skip_n_firsts()];
+ }
+ return nullptr;
+ }
+
+protected:
+ std::vector _stacktrace;
+};
+
+#if BACKWARD_HAS_UNWIND == 1
+
+namespace details {
+
+template
+class Unwinder {
+public:
+ size_t operator()(F& f, size_t depth)
+ {
+ _f = &f;
+ _index = -1;
+ _depth = depth;
+ _Unwind_Backtrace(&this->backtrace_trampoline, this);
+ return static_cast(_index);
+ }
+
+private:
+ F* _f;
+ ssize_t _index;
+ size_t _depth;
+
+ static _Unwind_Reason_Code backtrace_trampoline(_Unwind_Context* ctx, void* self)
+ {
+ return (static_cast(self))->backtrace(ctx);
+ }
+
+ _Unwind_Reason_Code backtrace(_Unwind_Context* ctx)
+ {
+ if (_index >= 0 && static_cast(_index) >= _depth)
+ return _URC_END_OF_STACK;
+
+ int ip_before_instruction = 0;
+ uintptr_t ip = _Unwind_GetIPInfo(ctx, &ip_before_instruction);
+
+ if (!ip_before_instruction) {
+ // calculating 0-1 for unsigned, looks like a possible bug to sanitiziers,
+ // so let's do it explicitly:
+ if (ip == 0) {
+ ip = std::numeric_limits::max(); // set it to 0xffff... (as
+ // from casting 0-1)
+ }
+ else {
+ ip -= 1; // else just normally decrement it (no overflow/underflow will
+ // happen)
+ }
+ }
+
+ if (_index >= 0) { // ignore first frame.
+ (*_f)(static_cast(_index), reinterpret_cast(ip));
+ }
+ _index += 1;
+ return _URC_NO_REASON;
+ }
+};
+
+template
+size_t unwind(F f, size_t depth)
+{
+ Unwinder unwinder;
+ return unwinder(f, depth);
+}
+
+} // namespace details
+
+template <>
+class StackTraceImpl : public StackTraceImplHolder {
+public:
+ NOINLINE
+ size_t load_here(size_t depth = 32)
+ {
+ load_thread_info();
+ if (depth == 0) {
+ return 0;
+ }
+ _stacktrace.resize(depth);
+ size_t trace_cnt = details::unwind(callback(*this), depth);
+ _stacktrace.resize(trace_cnt);
+ skip_n_firsts(0);
+ return size();
+ }
+ size_t load_from(void* addr, size_t depth = 32)
+ {
+ load_here(depth + 8);
+
+ for (size_t i = 0; i < _stacktrace.size(); ++i) {
+ if (_stacktrace[i] == addr) {
+ skip_n_firsts(i);
+ break;
+ }
+ }
+
+ _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));
+ return size();
+ }
+
+private:
+ struct callback {
+ StackTraceImpl& self;
+ callback(StackTraceImpl& _self) : self(_self) {}
+
+ void operator()(size_t idx, void* addr) { self._stacktrace[idx] = addr; }
+ };
+};
+
+#elif defined(BACKWARD_HAS_BACKTRACE)
+
+template <>
+class StackTraceImpl : public StackTraceImplHolder {
+public:
+ NOINLINE
+ size_t load_here(size_t depth = 32)
+ {
+ load_thread_info();
+ if (depth == 0) {
+ return 0;
+ }
+ _stacktrace.resize(depth + 1);
+ size_t trace_cnt = backtrace(&_stacktrace[0], _stacktrace.size());
+ _stacktrace.resize(trace_cnt);
+ skip_n_firsts(1);
+ return size();
+ }
+
+ size_t load_from(void* addr, size_t depth = 32)
+ {
+ load_here(depth + 8);
+
+ for (size_t i = 0; i < _stacktrace.size(); ++i) {
+ if (_stacktrace[i] == addr) {
+ skip_n_firsts(i);
+ _stacktrace[i] = (void*)((uintptr_t)_stacktrace[i] + 1);
+ break;
+ }
+ }
+
+ _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));
+ return size();
+ }
+};
+
+#elif defined(BACKWARD_SYSTEM_WINDOWS)
+
+template <>
+class StackTraceImpl : public StackTraceImplHolder {
+public:
+ // We have to load the machine type from the image info
+ // So we first initialize the resolver, and it tells us this info
+ void set_machine_type(DWORD machine_type) { machine_type_ = machine_type; }
+ void set_context(CONTEXT* ctx) { ctx_ = ctx; }
+ void set_thread_handle(HANDLE handle) { thd_ = handle; }
+
+ NOINLINE
+ size_t load_here(size_t depth = 32)
+ {
+
+ CONTEXT localCtx; // used when no context is provided
+
+ if (depth == 0) {
+ return 0;
+ }
+
+ if (!ctx_) {
+ ctx_ = &localCtx;
+ RtlCaptureContext(ctx_);
+ }
+
+ if (!thd_) {
+ thd_ = GetCurrentThread();
+ }
+
+ HANDLE process = GetCurrentProcess();
+
+ STACKFRAME64 s;
+ memset(&s, 0, sizeof(STACKFRAME64));
+
+ // TODO: 32 bit context capture
+ s.AddrStack.Mode = AddrModeFlat;
+ s.AddrFrame.Mode = AddrModeFlat;
+ s.AddrPC.Mode = AddrModeFlat;
+# ifdef _M_X64
+ s.AddrPC.Offset = ctx_->Rip;
+ s.AddrStack.Offset = ctx_->Rsp;
+ s.AddrFrame.Offset = ctx_->Rbp;
+# else
+ s.AddrPC.Offset = ctx_->Eip;
+ s.AddrStack.Offset = ctx_->Esp;
+ s.AddrFrame.Offset = ctx_->Ebp;
+# endif
+
+ if (!machine_type_) {
+# ifdef _M_X64
+ machine_type_ = IMAGE_FILE_MACHINE_AMD64;
+# else
+ machine_type_ = IMAGE_FILE_MACHINE_I386;
+# endif
+ }
+
+ for (;;) {
+ // NOTE: this only works if PDBs are already loaded!
+ SetLastError(0);
+ if (!StackWalk64(
+ machine_type_, process, thd_, &s, ctx_, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
+ break;
+
+ if (s.AddrReturn.Offset == 0)
+ break;
+
+ _stacktrace.push_back(reinterpret_cast(s.AddrPC.Offset));
+
+ if (size() >= depth)
+ break;
+ }
+
+ return size();
+ }
+
+ size_t load_from(void* addr, size_t depth = 32)
+ {
+ load_here(depth + 8);
+
+ for (size_t i = 0; i < _stacktrace.size(); ++i) {
+ if (_stacktrace[i] == addr) {
+ skip_n_firsts(i);
+ break;
+ }
+ }
+
+ _stacktrace.resize(std::min(_stacktrace.size(), skip_n_firsts() + depth));
+ return size();
+ }
+
+private:
+ DWORD machine_type_ = 0;
+ HANDLE thd_ = 0;
+ CONTEXT* ctx_ = nullptr;
+};
+
+#endif
+
+class StackTrace : public StackTraceImpl {
+};
+
+/*************** TRACE RESOLVER ***************/
+
+template
+class TraceResolverImpl;
+
+#ifdef BACKWARD_SYSTEM_UNKNOWN
+
+template <>
+class TraceResolverImpl {
+public:
+ template
+ void load_stacktrace(ST&)
+ {
+ }
+ ResolvedTrace resolve(ResolvedTrace t) { return t; }
+};
+
+#endif
+
+class TraceResolverImplBase {
+protected:
+ std::string demangle(const char* funcname) { return _demangler.demangle(funcname); }
+
+private:
+ details::demangler _demangler;
+};
+
+#ifdef BACKWARD_SYSTEM_LINUX
+
+class TraceResolverLinuxBase : public TraceResolverImplBase {
+public:
+ TraceResolverLinuxBase() : argv0_(get_argv0()), exec_path_(read_symlink("/proc/self/exe")) {}
+ std::string resolve_exec_path(Dl_info& symbol_info) const
+ {
+ // mutates symbol_info.dli_fname to be filename to open and returns filename
+ // to display
+ if (symbol_info.dli_fname == argv0_) {
+ // dladdr returns argv[0] in dli_fname for symbols contained in
+ // the main executable, which is not a valid path if the
+ // executable was found by a search of the PATH environment
+ // variable; In that case, we actually open /proc/self/exe, which
+ // is always the actual executable (even if it was deleted/replaced!)
+ // but display the path that /proc/self/exe links to.
+ symbol_info.dli_fname = "/proc/self/exe";
+ return exec_path_;
+ }
+ else {
+ return symbol_info.dli_fname;
+ }
+ }
+
+private:
+ std::string argv0_;
+ std::string exec_path_;
+
+ static std::string get_argv0()
+ {
+ std::string argv0;
+ std::ifstream ifs("/proc/self/cmdline");
+ std::getline(ifs, argv0, '\0');
+ return argv0;
+ }
+
+ static std::string read_symlink(std::string const& symlink_path)
+ {
+ std::string path;
+ path.resize(100);
+
+ while (true) {
+ ssize_t len = ::readlink(symlink_path.c_str(), &*path.begin(), path.size());
+ if (len < 0) {
+ return "";
+ }
+ if (static_cast(len) == path.size()) {
+ path.resize(path.size() * 2);
+ }
+ else {
+ path.resize(static_cast(len));
+ break;
+ }
+ }
+
+ return path;
+ }
+};
+
+template
+class TraceResolverLinuxImpl;
+
+# if BACKWARD_HAS_BACKTRACE_SYMBOL == 1
+
+template <>
+class TraceResolverLinuxImpl : public TraceResolverLinuxBase {
+public:
+ template
+ void load_stacktrace(ST& st)
+ {
+ using namespace details;
+ if (st.size() == 0) {
+ return;
+ }
+ _symbols.reset(backtrace_symbols(st.begin(), (int)st.size()));
+ }
+
+ ResolvedTrace resolve(ResolvedTrace trace)
+ {
+ char* filename = _symbols[trace.idx];
+ char* funcname = filename;
+ while (*funcname && *funcname != '(') {
+ funcname += 1;
+ }
+ trace.object_filename.assign(
+ filename,
+ funcname); // ok even if funcname is the ending
+ // \0 (then we assign entire string)
+
+ if (*funcname) { // if it's not end of string (e.g. from last frame ip==0)
+ funcname += 1;
+ char* funcname_end = funcname;
+ while (*funcname_end && *funcname_end != ')' && *funcname_end != '+') {
+ funcname_end += 1;
+ }
+ *funcname_end = '\0';
+ trace.object_function = this->demangle(funcname);
+ trace.source.function = trace.object_function; // we cannot do better.
+ }
+ return trace;
+ }
+
+private:
+ details::handle _symbols;
+};
+
+# endif // BACKWARD_HAS_BACKTRACE_SYMBOL == 1
+
+# if BACKWARD_HAS_BFD == 1
+
+template <>
+class TraceResolverLinuxImpl : public TraceResolverLinuxBase {
+public:
+ TraceResolverLinuxImpl() : _bfd_loaded(false) {}
+
+ template
+ void load_stacktrace(ST&)
+ {
+ }
+
+ ResolvedTrace resolve(ResolvedTrace trace)
+ {
+ Dl_info symbol_info;
+
+ // trace.addr is a virtual address in memory pointing to some code.
+ // Let's try to find from which loaded object it comes from.
+ // The loaded object can be yourself btw.
+ if (!dladdr(trace.addr, &symbol_info)) {
+ return trace; // dat broken trace...
+ }
+
+ // Now we get in symbol_info:
+ // .dli_fname:
+ // pathname of the shared object that contains the address.
+ // .dli_fbase:
+ // where the object is loaded in memory.
+ // .dli_sname:
+ // the name of the nearest symbol to trace.addr, we expect a
+ // function name.
+ // .dli_saddr:
+ // the exact address corresponding to .dli_sname.
+
+ if (symbol_info.dli_sname) {
+ trace.object_function = demangle(symbol_info.dli_sname);
+ }
+
+ if (!symbol_info.dli_fname) {
+ return trace;
+ }
+
+ trace.object_filename = resolve_exec_path(symbol_info);
+ bfd_fileobject& fobj = load_object_with_bfd(symbol_info.dli_fname);
+ if (!fobj.handle) {
+ return trace; // sad, we couldn't load the object :(
+ }
+
+ find_sym_result* details_selected; // to be filled.
+
+ // trace.addr is the next instruction to be executed after returning
+ // from the nested stack frame. In C++ this usually relate to the next
+ // statement right after the function call that leaded to a new stack
+ // frame. This is not usually what you want to see when printing out a
+ // stacktrace...
+ find_sym_result details_call_site = find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase);
+ details_selected = &details_call_site;
+
+# if BACKWARD_HAS_UNWIND == 0
+ // ...this is why we also try to resolve the symbol that is right
+ // before the return address. If we are lucky enough, we will get the
+ // line of the function that was called. But if the code is optimized,
+ // we might get something absolutely not related since the compiler
+ // can reschedule the return address with inline functions and
+ // tail-call optimisation (among other things that I don't even know
+ // or cannot even dream about with my tiny limited brain).
+ find_sym_result details_adjusted_call_site =
+ find_symbol_details(fobj, (void*)(uintptr_t(trace.addr) - 1), symbol_info.dli_fbase);
+
+ // In debug mode, we should always get the right thing(TM).
+ if (details_call_site.found && details_adjusted_call_site.found) {
+ // Ok, we assume that details_adjusted_call_site is a better estimation.
+ details_selected = &details_adjusted_call_site;
+ trace.addr = (void*)(uintptr_t(trace.addr) - 1);
+ }
+
+ if (details_selected == &details_call_site && details_call_site.found) {
+ // we have to re-resolve the symbol in order to reset some
+ // internal state in BFD... so we can call backtrace_inliners
+ // thereafter...
+ details_call_site = find_symbol_details(fobj, trace.addr, symbol_info.dli_fbase);
+ }
+# endif // BACKWARD_HAS_UNWIND
+
+ if (details_selected->found) {
+ if (details_selected->filename) {
+ trace.source.filename = details_selected->filename;
+ }
+ trace.source.line = details_selected->line;
+
+ if (details_selected->funcname) {
+ // this time we get the name of the function where the code is
+ // located, instead of the function were the address is
+ // located. In short, if the code was inlined, we get the
+ // function correspoding to the code. Else we already got in
+ // trace.function.
+ trace.source.function = demangle(details_selected->funcname);
+
+ if (!symbol_info.dli_sname) {
+ // for the case dladdr failed to find the symbol name of
+ // the function, we might as well try to put something
+ // here.
+ trace.object_function = trace.source.function;
+ }
+ }
+
+ // Maybe the source of the trace got inlined inside the function
+ // (trace.source.function). Let's see if we can get all the inlined
+ // calls along the way up to the initial call site.
+ trace.inliners = backtrace_inliners(fobj, *details_selected);
+
+# if 0
+ if (trace.inliners.size() == 0) {
+ // Maybe the trace was not inlined... or maybe it was and we
+ // are lacking the debug information. Let's try to make the
+ // world better and see if we can get the line number of the
+ // function (trace.source.function) now.
+ //
+ // We will get the location of where the function start (to be
+ // exact: the first instruction that really start the
+ // function), not where the name of the function is defined.
+ // This can be quite far away from the name of the function
+ // btw.
+ //
+ // If the source of the function is the same as the source of
+ // the trace, we cannot say if the trace was really inlined or
+ // not. However, if the filename of the source is different
+ // between the function and the trace... we can declare it as
+ // an inliner. This is not 100% accurate, but better than
+ // nothing.
+
+ if (symbol_info.dli_saddr) {
+ find_sym_result details = find_symbol_details(fobj,
+ symbol_info.dli_saddr,
+ symbol_info.dli_fbase);
+
+ if (details.found) {
+ ResolvedTrace::SourceLoc diy_inliner;
+ diy_inliner.line = details.line;
+ if (details.filename) {
+ diy_inliner.filename = details.filename;
+ }
+ if (details.funcname) {
+ diy_inliner.function = demangle(details.funcname);
+ } else {
+ diy_inliner.function = trace.source.function;
+ }
+ if (diy_inliner != trace.source) {
+ trace.inliners.push_back(diy_inliner);
+ }
+ }
+ }
+ }
+# endif
+ }
+
+ return trace;
+ }
+
+private:
+ bool _bfd_loaded;
+
+ typedef details::handle