Skip to content

Commit 1152b7c

Browse files
committed
chore: code review
1 parent 2cabdac commit 1152b7c

4 files changed

Lines changed: 63 additions & 80 deletions

File tree

examples/invocation-id-concurrent/src/main.rs

Lines changed: 12 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ struct Request {
1313

1414
#[derive(Serialize, Debug, PartialEq)]
1515
struct Response {
16-
req_id: String,
17-
inv_id: Option<String>,
16+
from: String,
1817
}
1918

2019
#[derive(Debug)]
@@ -70,12 +69,9 @@ pub(crate) async fn my_handler(event: LambdaEvent<Request>) -> Result<Response,
7069
tokio::time::sleep(tokio::time::Duration::from_secs(event.payload.sleep.into())).await;
7170
}
7271

73-
let resp = Response {
74-
req_id: event.context.request_id,
75-
inv_id: event.context.invocation_id,
76-
};
77-
78-
Ok(resp)
72+
Ok(Response {
73+
from: event.payload._command,
74+
})
7975
}
8076

8177
#[cfg(test)]
@@ -84,46 +80,17 @@ mod tests {
8480
use lambda_runtime::{Context, LambdaEvent};
8581

8682
#[tokio::test]
87-
async fn handler_returns_request_and_invocation_ids() {
88-
let mut context = Context::default();
89-
context.request_id = "req-123".to_string();
90-
context.invocation_id = Some("inv-456".to_string());
91-
92-
let payload = Request {
93-
_command: "test".to_string(),
94-
sleep: 0,
83+
async fn handler_echoes_marker() {
84+
let event = LambdaEvent {
85+
payload: Request {
86+
_command: "invoke-B".into(),
87+
sleep: 0,
88+
},
89+
context: Context::default(),
9590
};
96-
let event = LambdaEvent { payload, context };
97-
let result = my_handler(event).await.unwrap();
98-
99-
assert_eq!(
100-
result,
101-
Response {
102-
req_id: "req-123".to_string(),
103-
inv_id: Some("inv-456".to_string()),
104-
}
105-
);
106-
}
10791

108-
#[tokio::test]
109-
async fn handler_works_without_invocation_id() {
110-
let mut context = Context::default();
111-
context.request_id = "req-789".to_string();
112-
// invocation_id defaults to None
113-
114-
let payload = Request {
115-
_command: "test".to_string(),
116-
sleep: 0,
117-
};
118-
let event = LambdaEvent { payload, context };
11992
let result = my_handler(event).await.unwrap();
12093

121-
assert_eq!(
122-
result,
123-
Response {
124-
req_id: "req-789".to_string(),
125-
inv_id: None,
126-
}
127-
);
94+
assert_eq!(result, Response { from: "invoke-B".into() });
12895
}
12996
}

lambda-runtime/src/layers/api_response.rs

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{
2+
constants::LAMBDA_RUNTIME_INVOCATION_ID,
23
deserializer,
34
requests::{EventCompletionRequest, IntoRequest},
45
runtime::LambdaInvocation,
@@ -123,7 +124,17 @@ where
123124
};
124125

125126
let request_id = req.context.request_id.clone();
126-
let invocation_id = req.context.invocation_id.clone();
127+
128+
// The invocation ID assigned by the Lambda runtime for cross-wiring protection.
129+
// Echoed back on `/response` and `/error` to allow RAPID to reject stale responses
130+
// from timed-out invocations. `None` when running against older RAPID versions
131+
// that don't send this header
132+
let invocation_id = req
133+
.parts
134+
.headers
135+
.get(LAMBDA_RUNTIME_INVOCATION_ID)
136+
.map(|v| String::from_utf8_lossy(v.as_bytes()).to_string());
137+
127138
let lambda_event = match deserializer::deserialize::<EventPayload>(&req.body, req.context) {
128139
Ok(lambda_event) => lambda_event,
129140
Err(err) => match build_event_error_request(request_id, invocation_id, err) {
@@ -197,3 +208,39 @@ where
197208
})
198209
}
199210
}
211+
212+
#[cfg(test)]
213+
mod tests {
214+
use super::*;
215+
use crate::{constants::LAMBDA_RUNTIME_INVOCATION_ID, runtime::LambdaInvocation, Context};
216+
use http::{HeaderValue, Response};
217+
use serde_json::json;
218+
use tower::{service_fn, Service};
219+
220+
#[tokio::test]
221+
async fn forwards_invocation_id_from_next_response_headers() {
222+
let mut response = Response::new(());
223+
response
224+
.headers_mut()
225+
.insert(LAMBDA_RUNTIME_INVOCATION_ID, HeaderValue::from_static("invocation-123"));
226+
let (parts, _) = response.into_parts();
227+
228+
let mut service = RuntimeApiResponseService::new(service_fn(|_event: LambdaEvent<serde_json::Value>| async {
229+
Ok::<_, Diagnostic>(json!({"ok": true}))
230+
}));
231+
232+
let request = service
233+
.call(LambdaInvocation {
234+
parts,
235+
body: bytes::Bytes::from_static(b"{}"),
236+
context: Context::default(),
237+
})
238+
.await
239+
.expect("response request should be created");
240+
241+
assert_eq!(
242+
request.headers().get(LAMBDA_RUNTIME_INVOCATION_ID),
243+
Some(&HeaderValue::from_static("invocation-123")),
244+
);
245+
}
246+
}

lambda-runtime/src/types.rs

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use crate::{
22
constants::{
33
LAMBDA_RUNTIME_CLIENT_CONTEXT, LAMBDA_RUNTIME_COGNITO_IDENTITY, LAMBDA_RUNTIME_DEADLINE_MS,
4-
LAMBDA_RUNTIME_INVOCATION_ID, LAMBDA_RUNTIME_INVOKED_FUNCTION_ARN, LAMBDA_RUNTIME_REQUEST_ID,
5-
LAMBDA_RUNTIME_TENANT_ID, LAMBDA_RUNTIME_TRACE_ID,
4+
LAMBDA_RUNTIME_INVOKED_FUNCTION_ARN, LAMBDA_RUNTIME_REQUEST_ID, LAMBDA_RUNTIME_TENANT_ID,
5+
LAMBDA_RUNTIME_TRACE_ID,
66
},
77
Error, RefConfig,
88
};
@@ -92,11 +92,6 @@ pub struct Context {
9292
/// Includes information such as the function name, memory allocation,
9393
/// version, and log streams.
9494
pub env_config: RefConfig,
95-
/// The invocation ID assigned by the Lambda runtime for cross-wiring protection.
96-
/// Echoed back on `/response` and `/error` to allow RAPID to reject stale responses
97-
/// from timed-out invocations. `None` when running against older RAPID versions
98-
/// that don't send this header.
99-
pub invocation_id: Option<String>,
10095
}
10196

10297
impl Default for Context {
@@ -110,7 +105,6 @@ impl Default for Context {
110105
identity: None,
111106
tenant_id: None,
112107
env_config: std::sync::Arc::new(crate::Config::default()),
113-
invocation_id: None,
114108
}
115109
}
116110
}
@@ -164,9 +158,6 @@ impl Context {
164158
.get(LAMBDA_RUNTIME_TENANT_ID)
165159
.map(|v| String::from_utf8_lossy(v.as_bytes()).to_string()),
166160
env_config,
167-
invocation_id: headers
168-
.get(LAMBDA_RUNTIME_INVOCATION_ID)
169-
.map(|v| String::from_utf8_lossy(v.as_bytes()).to_string()),
170161
};
171162

172163
Ok(ctx)
@@ -552,26 +543,4 @@ mod test {
552543
let context = Context::new("id", config, &headers).unwrap();
553544
assert_eq!(context.tenant_id, None);
554545
}
555-
556-
#[test]
557-
fn context_with_invocation_id_resolves() {
558-
let config = Arc::new(Config::default());
559-
let mut headers = HeaderMap::new();
560-
headers.insert("lambda-runtime-aws-request-id", HeaderValue::from_static("my-id"));
561-
headers.insert("lambda-runtime-deadline-ms", HeaderValue::from_static("123"));
562-
563-
let context = Context::new("id", config, &headers).unwrap();
564-
565-
assert_eq!(context.invocation_id, None);
566-
567-
let config = Arc::new(Config::default());
568-
headers.insert(
569-
"lambda-runtime-invocation-id",
570-
HeaderValue::from_static("invocation-123"),
571-
);
572-
573-
let context = Context::new("id", config, &headers).unwrap();
574-
575-
assert_eq!(context.invocation_id, Some("invocation-123".to_string()));
576-
}
577546
}

test/dockerized/scenarios/concurrent_scenarios.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def get_invocation_id_scenarios():
8282
)],
8383
[Request.create(
8484
payload={"command": "invoke-B", "sleep": TIMEOUT - 1},
85-
assertions=[{"transform": ".req_id", "response": SAME_REQUEST_ID}],
85+
assertions=[{"response": {"from": "invoke-B"}}],
8686
headers={"X-Amzn-RequestId": SAME_REQUEST_ID},
8787
)],
8888
]

0 commit comments

Comments
 (0)