pub mod eventstream;
pub mod json;
pub mod query;
pub mod rest;
use axum::http::{HeaderMap, Method, Uri};
use bytes::Bytes;
use serde_json::Value;
use crate::error::AwsError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Protocol {
AwsJson1_0,
AwsJson1_1,
RestJson1,
RestXml,
AwsQuery,
Ec2Query,
}
impl Protocol {
pub fn response_content_type(&self) -> &'static str {
match self {
Self::AwsJson1_0 | Self::AwsJson1_1 | Self::RestJson1 => "application/x-amz-json-1.0",
Self::RestXml | Self::AwsQuery | Self::Ec2Query => "application/xml",
}
}
pub fn is_json(&self) -> bool {
matches!(self, Self::AwsJson1_0 | Self::AwsJson1_1 | Self::RestJson1)
}
pub fn is_xml(&self) -> bool {
matches!(self, Self::RestXml | Self::AwsQuery | Self::Ec2Query)
}
}
#[derive(Debug)]
pub struct ParsedRequest {
pub operation: String,
pub input: Value,
}
#[derive(Debug, Clone)]
pub struct RouteDefinition {
pub method: &'static str,
pub path_pattern: &'static str,
pub operation: &'static str,
pub required_query_param: Option<&'static str>,
}
pub fn detect_protocol(headers: &HeaderMap, body: &Bytes) -> Option<Protocol> {
if let Some(target) = headers.get("x-amz-target") {
let content_type = headers
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
if content_type.contains("x-amz-json-1.0") {
return Some(Protocol::AwsJson1_0);
}
let _ = target;
return Some(Protocol::AwsJson1_1);
}
let content_type = headers
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("");
if content_type.contains("x-www-form-urlencoded") {
let body_str = std::str::from_utf8(body).unwrap_or("");
if body_str.contains("Action=") {
return Some(Protocol::AwsQuery);
}
}
if content_type.contains("json") {
return Some(Protocol::RestJson1);
}
if content_type.contains("xml") {
return Some(Protocol::RestXml);
}
None
}
pub fn parse_request(
protocol: Protocol,
method: &Method,
uri: &Uri,
headers: &HeaderMap,
body: &Bytes,
routes: &[RouteDefinition],
) -> Result<ParsedRequest, AwsError> {
match protocol {
Protocol::AwsJson1_0 | Protocol::AwsJson1_1 => json::parse_request(headers, body),
Protocol::AwsQuery | Protocol::Ec2Query => query::parse_request(body),
Protocol::RestJson1 => rest::parse_json_request(method, uri, body, routes),
Protocol::RestXml => rest::parse_xml_request(method, uri, headers, body, routes),
}
}
pub fn serialize_response(
protocol: Protocol,
operation: &str,
output: &Value,
request_id: &str,
) -> (axum::http::StatusCode, HeaderMap, Bytes) {
if let Some(body) = eventstream::try_encode(output) {
let mut headers = HeaderMap::new();
if let Ok(v) = "application/vnd.amazon.eventstream".parse() {
headers.insert(axum::http::header::CONTENT_TYPE, v);
}
if let Ok(v) = request_id.parse() {
headers.insert("x-amzn-requestid", v);
}
return (axum::http::StatusCode::OK, headers, Bytes::from(body));
}
match protocol {
Protocol::AwsJson1_0 | Protocol::AwsJson1_1 | Protocol::RestJson1 => {
json::serialize_response(output, request_id)
}
Protocol::AwsQuery | Protocol::Ec2Query => {
query::serialize_response(operation, output, request_id)
}
Protocol::RestXml => rest::serialize_xml_response(output, request_id),
}
}
pub fn serialize_error(
protocol: Protocol,
error: &AwsError,
request_id: &str,
) -> (axum::http::StatusCode, HeaderMap, Bytes) {
match protocol {
Protocol::AwsJson1_0 | Protocol::AwsJson1_1 | Protocol::RestJson1 => {
json::serialize_error(error, request_id)
}
Protocol::AwsQuery | Protocol::Ec2Query => query::serialize_error(error, request_id),
Protocol::RestXml => rest::serialize_error(error, request_id),
}
}