diff --git a/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md b/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md
index 2391045fc..97d177034 100644
--- a/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md
+++ b/aws-lambda-java-runtime-interface-client/RELEASE.CHANGELOG.md
@@ -1,3 +1,7 @@
+### July 17, 2026
+`2.12.0`
+- Add `Lambda-Runtime-Invocation-Id` header support for cross-wiring protection. The RIC now echoes the invocation ID received from RAPID on `/next` back on `/response` and `/error`, enabling RAPID to detect and reject stale responses from timed-out invocations.
+
### May 13, 2026
`2.11.0`
- Update aws-lambda-java-serialization dependency to 1.4.1
diff --git a/aws-lambda-java-runtime-interface-client/pom.xml b/aws-lambda-java-runtime-interface-client/pom.xml
index fa543df00..ac62fd5b3 100644
--- a/aws-lambda-java-runtime-interface-client/pom.xml
+++ b/aws-lambda-java-runtime-interface-client/pom.xml
@@ -4,7 +4,7 @@
4.0.0
com.amazonaws
aws-lambda-java-runtime-interface-client
- 2.11.0
+ 2.12.0
jar
AWS Lambda Java Runtime Interface Client
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
index e5b221a80..b9aa0fd11 100644
--- 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
@@ -315,7 +315,7 @@ private static void startRuntimeLoop(LambdaRequestHandler lambdaRequestHandler,
try {
ByteArrayOutputStream payload = lambdaRequestHandler.call(request);
- runtimeClient.reportInvocationSuccess(request.getId(), payload.toByteArray());
+ runtimeClient.reportInvocationSuccess(request.getId(), payload.toByteArray(), request.getInvocationId());
// clear interrupted flag in case if it was set by user's code
Thread.interrupted();
} catch (Throwable t) {
@@ -323,7 +323,7 @@ private static void startRuntimeLoop(LambdaRequestHandler lambdaRequestHandler,
userFault = UserFault.makeUserFault(t);
shouldExit = exitLoopOnErrors && (t instanceof VirtualMachineError || t instanceof IOError || userFault.fatal);
LambdaError error = createLambdaErrorFromThrowableOrUserFault(t);
- runtimeClient.reportInvocationError(request.getId(), error);
+ runtimeClient.reportInvocationError(request.getId(), error, request.getInvocationId());
} finally {
if (userFault != null) {
lambdaLogger.log(userFault.reportableError(), lambdaLogger.getLogFormat() == LogFormat.JSON ? LogLevel.ERROR : LogLevel.UNDEFINED);
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java
index a62aeb9b8..042bd2579 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClient.java
@@ -34,15 +34,17 @@ public interface LambdaRuntimeApiClient {
* Report invocation success
* @param requestId request id
* @param response byte array representing response
+ * @param invocationId invocation id for cross-wiring protection (may be null)
*/
- void reportInvocationSuccess(String requestId, byte[] response) throws IOException;
+ void reportInvocationSuccess(String requestId, byte[] response, String invocationId) throws IOException;
/**
* Report invocation error
* @param requestId request id
* @param error error to report
+ * @param invocationId invocation id for cross-wiring protection (may be null)
*/
- void reportInvocationError(String requestId, LambdaError error) throws IOException;
+ void reportInvocationError(String requestId, LambdaError error, String invocationId) throws IOException;
/**
* SnapStart endpoint to report that beforeCheckoint hooks were executed
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java
index caca69aa7..fce12eade 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImpl.java
@@ -34,6 +34,7 @@ public class LambdaRuntimeApiClientImpl implements LambdaRuntimeApiClient {
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 String INVOCATION_ID_HEADER = "Lambda-Runtime-Invocation-Id";
// 1MiB
private static final int XRAY_ERROR_CAUSE_MAX_HEADER_SIZE = 1024 * 1024;
@@ -55,7 +56,7 @@ public LambdaRuntimeApiClientImpl(String hostnameAndPort) {
@Override
public void reportInitError(LambdaError error) throws IOException {
String endpoint = this.baseUrl + "/2018-06-01/runtime/init/error";
- reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE);
+ reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE, null);
}
@Override
@@ -123,14 +124,15 @@ public InvocationRequest nextInvocationWithExponentialBackoff(LambdaContextLogge
}
@Override
- public void reportInvocationSuccess(String requestId, byte[] response) {
- NativeClient.postInvocationResponse(requestId.getBytes(UTF_8), response);
+ public void reportInvocationSuccess(String requestId, byte[] response, String invocationId) {
+ byte[] invocationIdBytes = invocationId != null ? invocationId.getBytes(UTF_8) : null;
+ NativeClient.postInvocationResponse(requestId.getBytes(UTF_8), response, invocationIdBytes);
}
@Override
- public void reportInvocationError(String requestId, LambdaError error) throws IOException {
+ public void reportInvocationError(String requestId, LambdaError error, String invocationId) throws IOException {
String endpoint = invocationEndpoint + requestId + "/error";
- reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE);
+ reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE, invocationId);
}
@Override
@@ -145,13 +147,17 @@ public void restoreNext() throws IOException {
@Override
public void reportRestoreError(LambdaError error) throws IOException {
String endpoint = this.baseUrl + "/2018-06-01/runtime/restore/error";
- reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE);
+ reportLambdaError(endpoint, error, XRAY_ERROR_CAUSE_MAX_HEADER_SIZE, null);
}
- void reportLambdaError(String endpoint, LambdaError error, int maxXrayHeaderSize) throws IOException {
+ void reportLambdaError(String endpoint, LambdaError error, int maxXrayHeaderSize, String invocationId) throws IOException {
Map headers = new HashMap<>();
headers.put(ERROR_TYPE_HEADER, error.errorType.getRapidError());
+ if (invocationId != null) {
+ headers.put(INVOCATION_ID_HEADER, invocationId);
+ }
+
if (error.xRayErrorCause != null) {
byte[] xRayErrorCauseJson = DtoSerializers.serialize(error.xRayErrorCause);
if (xRayErrorCauseJson != null && xRayErrorCauseJson.length < maxXrayHeaderSize) {
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
index 101aea4d0..5c690814b 100644
--- 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
@@ -21,6 +21,6 @@ static void init(String awsLambdaRuntimeApi) {
static native InvocationRequest next();
- static native void postInvocationResponse(byte[] requestId, byte[] response);
+ static native void postInvocationResponse(byte[] requestId, byte[] response, byte[] invocationId);
}
diff --git a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/InvocationRequest.java b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/InvocationRequest.java
index 656945b41..a589cb024 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/InvocationRequest.java
+++ b/aws-lambda-java-runtime-interface-client/src/main/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/dto/InvocationRequest.java
@@ -45,6 +45,11 @@ public class InvocationRequest {
*/
private String tenantId;
+ /**
+ * The invocation ID for cross-wiring protection.
+ */
+ private String invocationId;
+
private byte[] content;
public String getId() {
@@ -107,6 +112,14 @@ public void setTenantId(String tenantId) {
this.tenantId = tenantId;
}
+ public String getInvocationId() {
+ return invocationId;
+ }
+
+ public void setInvocationId(String invocationId) {
+ this.invocationId = invocationId;
+ }
+
public byte[] getContent() {
return content;
}
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
index f06796616..fb6cd3ca3 100644
--- 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
@@ -21,6 +21,7 @@ static jfieldID clientContextField;
static jfieldID cognitoIdentityField;
static jfieldID xrayTraceIdField;
static jfieldID tenantIdField;
+static jfieldID invocationIdField;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
@@ -43,6 +44,7 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
clientContextField = env->GetFieldID(invocationRequestClass , "clientContext", "Ljava/lang/String;");
cognitoIdentityField = env->GetFieldID(invocationRequestClass , "cognitoIdentity", "Ljava/lang/String;");
tenantIdField = env->GetFieldID(invocationRequestClass, "tenantId", "Ljava/lang/String;");
+ invocationIdField = env->GetFieldID(invocationRequestClass, "invocationId", "Ljava/lang/String;");
return JNI_VERSION;
}
@@ -112,6 +114,10 @@ JNIEXPORT jobject JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_
CHECK_EXCEPTION(env, env->SetObjectField(invocationRequest, tenantIdField, env->NewStringUTF(response.tenant_id.c_str())));
}
+ if(response.invocation_id != ""){
+ CHECK_EXCEPTION(env, env->SetObjectField(invocationRequest, invocationIdField, env->NewStringUTF(response.invocation_id.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));
@@ -124,7 +130,7 @@ JNIEXPORT jobject JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_
}
JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_postInvocationResponse
- (JNIEnv *env, jobject thisObject, jbyteArray jrequestId, jbyteArray jresponseArray) {
+ (JNIEnv *env, jobject thisObject, jbyteArray jrequestId, jbyteArray jresponseArray, jbyteArray jinvocationId) {
std::string payload = toNativeString(env, jresponseArray);
if ((env)->ExceptionOccurred()){
return;
@@ -134,8 +140,16 @@ JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_run
return;
}
+ std::string invocationId;
+ if (jinvocationId != nullptr) {
+ invocationId = toNativeString(env, jinvocationId);
+ if ((env)->ExceptionOccurred()){
+ return;
+ }
+ }
+
auto response = aws::lambda_runtime::invocation_response::success(payload, "application/json");
- auto outcome = CLIENT->post_success(requestId, response);
+ auto outcome = CLIENT->post_success(requestId, response, invocationId);
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
index 7219109b0..0f1aaa2ca 100644
--- 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
@@ -17,7 +17,7 @@ JNIEXPORT jobject JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_
(JNIEnv *, jobject);
JNIEXPORT void JNICALL Java_com_amazonaws_services_lambda_runtime_api_client_runtimeapi_NativeClient_postInvocationResponse
- (JNIEnv *, jobject, jbyteArray, jbyteArray);
+ (JNIEnv *, jobject, jbyteArray, jbyteArray, jbyteArray);
#ifdef __cplusplus
}
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
index c4868c1ba..96f90eaa2 100644
--- 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
@@ -66,6 +66,11 @@ struct invocation_request {
*/
std::string tenant_id;
+ /**
+ * Invocation ID for cross-wiring protection.
+ */
+ std::string invocation_id;
+
/**
* The number of milliseconds left before lambda terminates the current execution.
*/
@@ -154,11 +159,15 @@ class runtime {
/**
* Tells lambda that the function has succeeded.
*/
+ post_outcome post_success(std::string const& request_id, invocation_response const& handler_response, std::string const& invocation_id);
+
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, std::string const& invocation_id);
+
post_outcome post_failure(std::string const& request_id, invocation_response const& handler_response);
private:
@@ -167,7 +176,8 @@ class runtime {
post_outcome do_post(
std::string const& url,
std::string const& request_id,
- invocation_response const& handler_response);
+ invocation_response const& handler_response,
+ std::string const& invocation_id);
private:
std::string const m_user_agent_header;
diff --git a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/runtime.cpp b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/runtime.cpp
index 84a84b439..2d86a1d7e 100644
--- a/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/runtime.cpp
+++ b/aws-lambda-java-runtime-interface-client/src/main/jni/deps/aws-lambda-cpp-0.2.7/src/runtime.cpp
@@ -41,6 +41,7 @@ static constexpr auto COGNITO_IDENTITY_HEADER = "lambda-runtime-cognito-identity
static constexpr auto DEADLINE_MS_HEADER = "lambda-runtime-deadline-ms";
static constexpr auto FUNCTION_ARN_HEADER = "lambda-runtime-invoked-function-arn";
static constexpr auto TENANT_ID_HEADER = "lambda-runtime-aws-tenant-id";
+static constexpr auto INVOCATION_ID_HEADER = "lambda-runtime-invocation-id";
thread_local static CURL* m_curl_handle = curl_easy_init();
enum Endpoints {
@@ -306,25 +307,40 @@ runtime::next_outcome runtime::get_next()
if (resp.has_header(TENANT_ID_HEADER)) {
req.tenant_id = resp.get_header(TENANT_ID_HEADER);
}
+
+ if (resp.has_header(INVOCATION_ID_HEADER)) {
+ req.invocation_id = resp.get_header(INVOCATION_ID_HEADER);
+ }
return next_outcome(req);
}
-runtime::post_outcome runtime::post_success(std::string const& request_id, invocation_response const& handler_response)
+runtime::post_outcome runtime::post_success(std::string const& request_id, invocation_response const& handler_response, std::string const& invocation_id)
{
std::string const url = m_endpoints[Endpoints::RESULT] + request_id + "/response";
- return do_post(url, request_id, handler_response);
+ return do_post(url, request_id, handler_response, invocation_id);
}
-runtime::post_outcome runtime::post_failure(std::string const& request_id, invocation_response const& handler_response)
+runtime::post_outcome runtime::post_success(std::string const& request_id, invocation_response const& handler_response)
+{
+ return post_success(request_id, handler_response, "");
+}
+
+runtime::post_outcome runtime::post_failure(std::string const& request_id, invocation_response const& handler_response, std::string const& invocation_id)
{
std::string const url = m_endpoints[Endpoints::RESULT] + request_id + "/error";
- return do_post(url, request_id, handler_response);
+ return do_post(url, request_id, handler_response, invocation_id);
+}
+
+runtime::post_outcome runtime::post_failure(std::string const& request_id, invocation_response const& handler_response)
+{
+ return post_failure(request_id, handler_response, "");
}
runtime::post_outcome runtime::do_post(
std::string const& url,
std::string const& request_id,
- invocation_response const& handler_response)
+ invocation_response const& handler_response,
+ std::string const& invocation_id)
{
set_curl_post_result_options();
curl_easy_setopt(lambda_runtime::m_curl_handle, CURLOPT_URL, url.c_str());
@@ -341,6 +357,9 @@ runtime::post_outcome runtime::do_post(
headers = curl_slist_append(headers, "Expect:");
headers = curl_slist_append(headers, "transfer-encoding:");
headers = curl_slist_append(headers, m_user_agent_header.c_str());
+ if (!invocation_id.empty()) {
+ headers = curl_slist_append(headers, (std::string(INVOCATION_ID_HEADER) + ": " + invocation_id).c_str());
+ }
auto const& payload = handler_response.get_payload();
logging::log_debug(
LOG_TAG, "calculating content length... %s", ("content-length: " + std::to_string(payload.length())).c_str());
@@ -436,13 +455,13 @@ void run_handler(std::function c
logging::log_info(LOG_TAG, "Invoking user handler completed.");
if (res.is_success()) {
- const auto post_outcome = rt.post_success(req.request_id, res);
+ const auto post_outcome = rt.post_success(req.request_id, res, req.invocation_id);
if (!handle_post_outcome(post_outcome, req.request_id)) {
return; // TODO: implement a better retry strategy
}
}
else {
- const auto post_outcome = rt.post_failure(req.request_id, res);
+ const auto post_outcome = rt.post_failure(req.request_id, res, req.invocation_id);
if (!handle_post_outcome(post_outcome, req.request_id)) {
return; // TODO: implement a better retry strategy
}
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambdaTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambdaTest.java
index 49b59c2cd..100465531 100644
--- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambdaTest.java
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/AWSLambdaTest.java
@@ -242,7 +242,7 @@ void testConcurrentRunWithPlatformThreads() throws Throwable {
AWSLambda.startRuntimeLoops(lambdaRequestHandler, lambdaLogger, concurrencyConfig, runtimeClient);
// Success Reports Must Equal number of tasks that ran successfully.
- verify(runtimeClient, times(7)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any());
+ verify(runtimeClient, times(7)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any(), any());
// Hashmap keys should equal the number of threads (runtime loops).
assertEquals(4, SampleHandler.hashMap.size());
// Hashmap total count should equal all tasks that ran * number of iterations per task
@@ -280,11 +280,11 @@ void testConcurrentRunWithPlatformThreadsWithFailures() throws Throwable {
verify(lambdaLogger, times(6)).log(anyString(), eq(LogLevel.ERROR));
// Failed invokes should be reported.
- verify(runtimeClient).reportInvocationError(eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any());
- verify(runtimeClient).reportInvocationError(eq(UserFaultID), any());
+ verify(runtimeClient).reportInvocationError(eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any(), any());
+ verify(runtimeClient).reportInvocationError(eq(UserFaultID), any(), any());
// Success Reports Must Equal number of tasks that ran successfully.
- verify(runtimeClient, times(2)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any());
+ verify(runtimeClient, times(2)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any(), any());
// Hashmap keys should equal the minumum between(number of threads (runtime loops) AND number of tasks that ran successfully).
assertEquals(2, SampleHandler.hashMap.size());
@@ -326,12 +326,12 @@ void testConcurrentModeLoopDoesNotExitExceptForLambdaRuntimeClientMaxRetriesExce
verify(lambdaLogger, times(4)).log(anyString(), eq(LogLevel.ERROR));
// Failed invokes should be reported.
- verify(runtimeClient).reportInvocationError(eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any());
- verify(runtimeClient).reportInvocationError(eq(UserFaultID), any());
- verify(runtimeClient).reportInvocationError(eq(IOErrorID), any());
+ verify(runtimeClient).reportInvocationError(eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any(), any());
+ verify(runtimeClient).reportInvocationError(eq(UserFaultID), any(), any());
+ verify(runtimeClient).reportInvocationError(eq(IOErrorID), any(), any());
// Success Reports Must Equal number of tasks that ran successfully.
- verify(runtimeClient, times(2)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any());
+ verify(runtimeClient, times(2)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any(), any());
// Hashmap keys should equal the minumum between(number of threads (runtime loops) AND number of tasks that ran successfully).
assertEquals(1, SampleHandler.hashMap.size());
@@ -516,12 +516,12 @@ void testSequentialWithFatalUserFaultErrorStopsLoop() throws Throwable {
verify(lambdaLogger, times(2)).log(anyString(), eq(LogLevel.ERROR));
// Failed invokes should be reported.
- verify(runtimeClient).reportInvocationError(eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any());
- verify(runtimeClient).reportInvocationError(eq(UserFaultID), any());
+ verify(runtimeClient).reportInvocationError(eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any(), any());
+ verify(runtimeClient).reportInvocationError(eq(UserFaultID), any(), any());
// Success Reports Must Equal number of tasks that ran successfully. And only 2 Error reports for failImmediatelyRequest and userFaultRequest.
- verify(runtimeClient, times(2)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any());
- verify(runtimeClient, times(2)).reportInvocationError(any(), any());
+ verify(runtimeClient, times(2)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any(), any());
+ verify(runtimeClient, times(2)).reportInvocationError(any(), any(), any());
// Hashmap keys should equal one as it is not multithreaded.
assertEquals(1, SampleHandler.hashMap.size());
@@ -562,12 +562,12 @@ void testSequentialWithVirtualMachineErrorStopsLoop() throws Throwable {
verify(lambdaLogger, times(2)).log(anyString(), eq(LogLevel.ERROR));
// Failed invokes should be reported.
- verify(runtimeClient).reportInvocationError(eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any());
- verify(runtimeClient).reportInvocationError(eq(IOErrorID), any());
+ verify(runtimeClient).reportInvocationError(eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any(), any());
+ verify(runtimeClient).reportInvocationError(eq(IOErrorID), any(), any());
// Success Reports Must Equal number of tasks that ran successfully. And only 2 Error reports for failImmediatelyRequest and virtualMachineErrorRequest.
- verify(runtimeClient, times(2)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any());
- verify(runtimeClient, times(2)).reportInvocationError(any(), any());
+ verify(runtimeClient, times(2)).reportInvocationSuccess(eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any(), any());
+ verify(runtimeClient, times(2)).reportInvocationError(any(), any(), any());
// Hashmap keys should equal one as it is not multithreaded.
assertEquals(1, SampleHandler.hashMap.size());
@@ -575,4 +575,49 @@ void testSequentialWithVirtualMachineErrorStopsLoop() throws Throwable {
// Hashmap total count should equal all tasks that ran * number of iterations per task
assertEquals(2 * SampleHandler.nOfIterations, SampleHandler.globalCounter.get());
}
+
+ @Test
+ @Timeout(value = 1, unit = TimeUnit.MINUTES)
+ void testInvocationIdIsPassedToReportSuccess() throws Throwable {
+ when(concurrencyConfig.isMultiConcurrent()).thenReturn(false);
+
+ InvocationRequest requestWithInvId = getFakeInvocationRequest(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE);
+ requestWithInvId.setInvocationId("test-inv-uuid-1234");
+
+ // Fatal error to stop the loop after one successful invocation
+ InvocationRequest fatalRequest = mock(InvocationRequest.class);
+ when(fatalRequest.getId()).thenThrow(UserFault.makeUserFault(new IOError(new Throwable()), true)).thenReturn("fatal");
+
+ when(runtimeClient.nextInvocation())
+ .thenReturn(requestWithInvId)
+ .thenReturn(fatalRequest);
+
+ AWSLambda.startRuntimeLoops(lambdaRequestHandler, lambdaLogger, concurrencyConfig, runtimeClient);
+
+ verify(runtimeClient).reportInvocationSuccess(
+ eq(SampleHandler.ADD_ENTRY_TO_MAP_ID_OP_MODE), any(), eq("test-inv-uuid-1234"));
+ }
+
+ @Test
+ @Timeout(value = 1, unit = TimeUnit.MINUTES)
+ void testInvocationIdIsPassedToReportError() throws Throwable {
+ when(lambdaLogger.getLogFormat()).thenReturn(LogFormat.JSON);
+ when(concurrencyConfig.isMultiConcurrent()).thenReturn(false);
+
+ InvocationRequest requestWithInvId = getFakeInvocationRequest(SampleHandler.FAIL_IMMEDIATELY_OP_MODE);
+ requestWithInvId.setInvocationId("test-inv-uuid-5678");
+
+ // Fatal error to stop the loop after one error invocation
+ InvocationRequest fatalRequest = mock(InvocationRequest.class);
+ when(fatalRequest.getId()).thenThrow(UserFault.makeUserFault(new IOError(new Throwable()), true)).thenReturn("fatal");
+
+ when(runtimeClient.nextInvocation())
+ .thenReturn(requestWithInvId)
+ .thenReturn(fatalRequest);
+
+ AWSLambda.startRuntimeLoops(lambdaRequestHandler, lambdaLogger, concurrencyConfig, runtimeClient);
+
+ verify(runtimeClient).reportInvocationError(
+ eq(SampleHandler.FAIL_IMMEDIATELY_OP_MODE), any(), eq("test-inv-uuid-5678"));
+ }
}
\ No newline at end of file
diff --git a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImplTest.java b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImplTest.java
index 710c1565e..9d5929263 100644
--- a/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImplTest.java
+++ b/aws-lambda-java-runtime-interface-client/src/test/java/com/amazonaws/services/lambda/runtime/api/client/runtimeapi/LambdaRuntimeApiClientImplTest.java
@@ -244,7 +244,7 @@ public void reportInvocationErrorTest() {
mockWebServer.enqueue(mockResponse);
LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType);
- lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError);
+ lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError, null);
RecordedRequest recordedRequest = mockWebServer.takeRequest();
HttpUrl actualUrl = recordedRequest.getRequestUrl();
String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/invocation/1234/error";
@@ -274,7 +274,7 @@ public void reportInvocationErrorTestWrongStatusCode() {
mockWebServer.enqueue(mockResponse);
LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType);
- lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError);
+ lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError, null);
fail();
} catch(LambdaRuntimeClientException e) {
String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/invocation/1234/error";
@@ -318,7 +318,7 @@ public void reportLambdaErrorWithXRayTest() {
XRayErrorCause xRayErrorCause = new XRayErrorCause(workingDirectory, exceptions, paths);
LambdaError lambdaError = new LambdaError(errorRequest, xRayErrorCause, rapidErrorType);
- lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError);
+ lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError, null);
RecordedRequest recordedRequest = mockWebServer.takeRequest();
String xrayErrorCauseHeader = recordedRequest.getHeader("Lambda-Runtime-Function-XRay-Error-Cause");
@@ -339,7 +339,7 @@ public void reportInvocationSuccessTest() {
mockWebServer.enqueue(mockResponse);
String response = "{\"msg\":\"test\"}";
- lambdaRuntimeApiClientImpl.reportInvocationSuccess(requestId, response.getBytes());
+ lambdaRuntimeApiClientImpl.reportInvocationSuccess(requestId, response.getBytes(), null);
RecordedRequest recordedRequest = mockWebServer.takeRequest();
HttpUrl actualUrl = recordedRequest.getRequestUrl();
String expectedUrl = "http://" + getHostnamePort() + "/2018-06-01/runtime/invocation/1234/response";
@@ -456,7 +456,7 @@ public void createUrlMalformedTest() {
RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError;
LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType);
RuntimeException thrown = assertThrows(RuntimeException.class, ()->{
- lambdaRuntimeApiClientImpl.reportLambdaError("invalidurl", lambdaError, 100);
+ lambdaRuntimeApiClientImpl.reportLambdaError("invalidurl", lambdaError, 100, null);
});
assertTrue(thrown.getLocalizedMessage().contains("java.net.MalformedURLException"));
}
@@ -483,7 +483,7 @@ public void lambdaReportErrorXRayHeaderTooLongTest() {
XRayErrorCause xRayErrorCause = new XRayErrorCause(workingDirectory, exceptions, paths);
LambdaError lambdaError = new LambdaError(errorRequest, xRayErrorCause, rapidErrorType);
- lambdaRuntimeApiClientImpl.reportLambdaError("http://" + getHostnamePort(), lambdaError, 10);
+ lambdaRuntimeApiClientImpl.reportLambdaError("http://" + getHostnamePort(), lambdaError, 10, null);
RecordedRequest recordedRequest = mockWebServer.takeRequest();
String xrayErrorCauseHeader = recordedRequest.getHeader("Lambda-Runtime-Function-XRay-Error-Cause");
@@ -511,6 +511,28 @@ private void verifyNextInvocationRequest() throws Exception {
assertEquals("", actualBody);
}
+ @Test
+ public void reportInvocationErrorWithInvocationIdTest() {
+ try {
+ RapidErrorType rapidErrorType = RapidErrorType.AfterRestoreError;
+
+ MockResponse mockResponse = new MockResponse();
+ mockResponse.setResponseCode(HTTP_ACCEPTED);
+ mockWebServer.enqueue(mockResponse);
+
+ String invocationId = "test-invocation-uuid-1234";
+ LambdaError lambdaError = new LambdaError(errorRequest, rapidErrorType);
+ lambdaRuntimeApiClientImpl.reportInvocationError(requestId, lambdaError, invocationId);
+ RecordedRequest recordedRequest = mockWebServer.takeRequest();
+
+ String invocationIdHeader = recordedRequest.getHeader("Lambda-Runtime-Invocation-Id");
+ assertEquals(invocationId, invocationIdHeader);
+ } catch(Exception e) {
+ e.printStackTrace();
+ fail();
+ }
+ }
+
private String getHostnamePort() {
return mockWebServer.getHostName() + ":" + mockWebServer.getPort();
}
diff --git a/aws-lambda-java-runtime-interface-client/test/integration/codebuild-local/Dockerfile.agent b/aws-lambda-java-runtime-interface-client/test/integration/codebuild-local/Dockerfile.agent
index 350305e81..2b1ba299e 100644
--- a/aws-lambda-java-runtime-interface-client/test/integration/codebuild-local/Dockerfile.agent
+++ b/aws-lambda-java-runtime-interface-client/test/integration/codebuild-local/Dockerfile.agent
@@ -1,9 +1,7 @@
FROM public.ecr.aws/amazoncorretto/amazoncorretto:8
# Install docker and buildx extension
-RUN amazon-linux-extras enable docker && \
- yum clean metadata && \
- yum install -y docker tar gzip unzip file
+RUN yum install -y docker tar gzip unzip file findutils
COPY --from=docker/buildx-bin:latest /buildx /usr/libexec/docker/cli-plugins/docker-buildx