lambda_extension/
requests.rs

1use crate::{Error, LogBuffering};
2use http::{Method, Request};
3use lambda_runtime_api_client::{body::Body, build_request};
4use serde::Serialize;
5
6const EXTENSION_NAME_HEADER: &str = "Lambda-Extension-Name";
7pub(crate) const EXTENSION_ID_HEADER: &str = "Lambda-Extension-Identifier";
8const EXTENSION_ERROR_TYPE_HEADER: &str = "Lambda-Extension-Function-Error-Type";
9const CONTENT_TYPE_HEADER_NAME: &str = "Content-Type";
10const CONTENT_TYPE_HEADER_VALUE: &str = "application/json";
11
12// Comma separated list of features the extension supports.
13// `accountId` is currently the only supported feature.
14const EXTENSION_ACCEPT_FEATURE: &str = "Lambda-Extension-Accept-Feature";
15const EXTENSION_ACCEPT_FEATURE_VALUE: &str = "accountId";
16
17pub(crate) fn next_event_request(extension_id: &str) -> Result<Request<Body>, Error> {
18    let req = build_request()
19        .method(Method::GET)
20        .header(EXTENSION_ID_HEADER, extension_id)
21        .uri("/2020-01-01/extension/event/next")
22        .body(Body::empty())?;
23    Ok(req)
24}
25
26pub(crate) fn register_request(extension_name: &str, events: &[&str]) -> Result<Request<Body>, Error> {
27    let events = serde_json::json!({ "events": events });
28
29    let req = build_request()
30        .method(Method::POST)
31        .uri("/2020-01-01/extension/register")
32        .header(EXTENSION_NAME_HEADER, extension_name)
33        .header(EXTENSION_ACCEPT_FEATURE, EXTENSION_ACCEPT_FEATURE_VALUE)
34        .header(CONTENT_TYPE_HEADER_NAME, CONTENT_TYPE_HEADER_VALUE)
35        .body(Body::from(serde_json::to_string(&events)?))?;
36
37    Ok(req)
38}
39
40pub(crate) enum Api {
41    LogsApi,
42    TelemetryApi,
43}
44
45impl Api {
46    pub(crate) fn schema_version(&self) -> &str {
47        match *self {
48            Api::LogsApi => "2021-03-18",
49            Api::TelemetryApi => "2022-07-01",
50        }
51    }
52
53    pub(crate) fn uri(&self) -> &str {
54        match *self {
55            Api::LogsApi => "/2020-08-15/logs",
56            Api::TelemetryApi => "/2022-07-01/telemetry",
57        }
58    }
59}
60
61pub(crate) fn subscribe_request(
62    api: Api,
63    extension_id: &str,
64    types: Option<&[&str]>,
65    buffering: Option<LogBuffering>,
66    port_number: u16,
67) -> Result<Request<Body>, Error> {
68    let types = types.unwrap_or(&["platform", "function"]);
69
70    let data = serde_json::json!({
71        "schemaVersion": api.schema_version(),
72        "types": types,
73        "buffering": buffering.unwrap_or_default(),
74        "destination": {
75            "protocol": "HTTP",
76            "URI": format!("http://sandbox.localdomain:{port_number}"),
77        }
78    });
79
80    let req = build_request()
81        .method(Method::PUT)
82        .uri(api.uri())
83        .header(EXTENSION_ID_HEADER, extension_id)
84        .header(CONTENT_TYPE_HEADER_NAME, CONTENT_TYPE_HEADER_VALUE)
85        .body(Body::from(serde_json::to_string(&data)?))?;
86
87    Ok(req)
88}
89
90/// Payload to send error information to the Extensions API.
91#[derive(Debug, Serialize)]
92#[serde(rename_all = "camelCase")]
93pub struct ErrorRequest<'a> {
94    /// Human readable error description
95    pub error_message: &'a str,
96    /// The type of error to categorize
97    pub error_type: &'a str,
98    /// The error backtrace
99    pub stack_trace: Vec<&'a str>,
100}
101
102/// Create a new init error request to send to the Extensions API
103pub fn init_error(
104    extension_id: &str,
105    error_type: &str,
106    request: Option<ErrorRequest<'_>>,
107) -> Result<Request<Body>, Error> {
108    error_request("init", extension_id, error_type, request)
109}
110
111/// Create a new exit error request to send to the Extensions API
112pub fn exit_error(
113    extension_id: &str,
114    error_type: &str,
115    request: Option<ErrorRequest<'_>>,
116) -> Result<Request<Body>, Error> {
117    error_request("exit", extension_id, error_type, request)
118}
119
120fn error_request(
121    error_type: &str,
122    extension_id: &str,
123    error_str: &str,
124    request: Option<ErrorRequest<'_>>,
125) -> Result<Request<Body>, Error> {
126    let uri = format!("/2020-01-01/extension/{error_type}/error");
127
128    let body = match request {
129        None => Body::empty(),
130        Some(err) => Body::from(serde_json::to_string(&err)?),
131    };
132
133    let req = build_request()
134        .method(Method::POST)
135        .uri(uri)
136        .header(EXTENSION_ID_HEADER, extension_id)
137        .header(EXTENSION_ERROR_TYPE_HEADER, error_str)
138        .body(body)?;
139    Ok(req)
140}