1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
// SPDX-License-Identifier: MIT
//!
//! Lambda event deserialize
//!
use serde::Deserialize;
use std::borrow::Cow;
use std::collections::HashMap;

/// API Gateway payload format version 2.0
/// https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ApiGatewayV2<'a> {
    // version: Cow<'a, str>,
    // route_key: Cow<'a, str>,
    pub(crate) raw_path: Cow<'a, str>,
    pub(crate) raw_query_string: Cow<'a, str>,
    pub(crate) cookies: Option<Vec<Cow<'a, str>>>,
    pub(crate) headers: HashMap<Cow<'a, str>, Cow<'a, str>>,
    // #[serde(default)]
    // query_string_parameters: StrMap,
    // #[serde(default)]
    // path_parameters: StrMap,
    // #[serde(default)]
    // stage_variables: StrMap,
    pub(crate) body: Option<Cow<'a, str>>,
    #[serde(default)]
    pub(crate) is_base64_encoded: bool,
    pub(crate) request_context: ApiGatewayV2RequestContext<'a>,
}

#[derive(Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ApiGatewayV2RequestContext<'a> {
    // The API owner's AWS account ID.
    // pub account_id: String,
    // The identifier API Gateway assigns to your API.
    // pub api_id: String,
    // The stringified value of the specified key-value pair of the context map returned from an API Gateway Lambda authorizer function.
    // #[serde(default)]
    // pub authorizer: HashMap<String, serde_json::Value>,
    /// The full domain name used to invoke the API. This should be the same as the incoming Host header.
    pub(crate) domain_name: Cow<'a, str>,
    // The first label of the $context.domainName. This is often used as a caller/customer identifier.
    // pub domain_prefix: String,
    /// The HTTP method used.
    pub(crate) http: Http<'a>,
    // The ID that API Gateway assigns to the API request.
    // pub request_id: String,
    // Undocumented, could be resourcePath
    // pub route_key: String,
    // The deployment stage of the API request (for example, Beta or Prod).
    // pub stage: String,
    // Undocumented, could be requestTime
    // pub time: String,
    // Undocumented, could be requestTimeEpoch
    // pub time_epoch: usize,
}

#[derive(Deserialize, Debug, Default, Clone)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Http<'a> {
    /// The HTTP method used. Valid values include: DELETE, GET, HEAD, OPTIONS, PATCH, POST, and PUT.
    pub(crate) method: Cow<'a, str>,
    // The request path. For example, for a non-proxy request URL of
    // `https://{rest-api-id.execute-api.{region}.amazonaws.com/{stage}/root/child`,
    // the $context.path value is `/{stage}/root/child`.
    // pub path: Cow<'a, str>,
    // The request protocol, for example, HTTP/1.1.
    // pub protocol: Cow<'a, str>,
    /// The source IP address of the TCP connection making the request to API Gateway.
    pub(crate) source_ip: Cow<'a, str>,
    // The User-Agent header of the API caller.
    // pub user_agent: Cow<'a, str>,
}

// raw_path in API Gateway V2 is percent decoded
// Path containing space or UTF-8 char is
// required to percent encoded again before passed to web frameworks
// See RFC3986 3.3 Path for valid chars.
const RFC3986_PATH_ESCAPE_SET: &percent_encoding::AsciiSet = &percent_encoding::CONTROLS
    .add(b' ')
    .add(b'"')
    .add(b'#')
    .add(b'%')
    .add(b'+')
    .add(b':')
    .add(b'<')
    .add(b'>')
    .add(b'?')
    .add(b'@')
    .add(b'[')
    .add(b'\\')
    .add(b']')
    .add(b'^')
    .add(b'`')
    .add(b'{')
    .add(b'|')
    .add(b'}');

impl<'a> ApiGatewayV2<'a> {
    pub(crate) fn encoded_path(&'a self) -> Cow<'a, str> {
        Cow::from(percent_encoding::utf8_percent_encode(
            &self.raw_path,
            &RFC3986_PATH_ESCAPE_SET,
        ))
    }
}