awsim_core/protocol/
mod.rs1pub mod json;
2pub mod query;
3pub mod rest;
4
5use axum::http::{HeaderMap, Method, Uri};
6use bytes::Bytes;
7use serde_json::Value;
8
9use crate::error::AwsError;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum Protocol {
14 AwsJson1_0,
15 AwsJson1_1,
16 RestJson1,
17 RestXml,
18 AwsQuery,
19 Ec2Query,
20}
21
22impl Protocol {
23 pub fn response_content_type(&self) -> &'static str {
24 match self {
25 Self::AwsJson1_0 | Self::AwsJson1_1 | Self::RestJson1 => {
26 "application/x-amz-json-1.0"
27 }
28 Self::RestXml | Self::AwsQuery | Self::Ec2Query => "application/xml",
29 }
30 }
31
32 pub fn is_json(&self) -> bool {
33 matches!(
34 self,
35 Self::AwsJson1_0 | Self::AwsJson1_1 | Self::RestJson1
36 )
37 }
38
39 pub fn is_xml(&self) -> bool {
40 matches!(self, Self::RestXml | Self::AwsQuery | Self::Ec2Query)
41 }
42}
43
44#[derive(Debug)]
46pub struct ParsedRequest {
47 pub operation: String,
48 pub input: Value,
49}
50
51#[derive(Debug, Clone)]
53pub struct RouteDefinition {
54 pub method: &'static str,
55 pub path_pattern: &'static str,
56 pub operation: &'static str,
57 pub required_query_param: Option<&'static str>,
60}
61
62pub fn detect_protocol(headers: &HeaderMap, body: &Bytes) -> Option<Protocol> {
64 if let Some(target) = headers.get("x-amz-target") {
66 let content_type = headers
67 .get("content-type")
68 .and_then(|v| v.to_str().ok())
69 .unwrap_or("");
70 if content_type.contains("x-amz-json-1.0") {
71 return Some(Protocol::AwsJson1_0);
72 }
73 let _ = target;
75 return Some(Protocol::AwsJson1_1);
76 }
77
78 let content_type = headers
79 .get("content-type")
80 .and_then(|v| v.to_str().ok())
81 .unwrap_or("");
82
83 if content_type.contains("x-www-form-urlencoded") {
85 let body_str = std::str::from_utf8(body).unwrap_or("");
86 if body_str.contains("Action=") {
87 return Some(Protocol::AwsQuery);
88 }
89 }
90
91 if content_type.contains("json") {
93 return Some(Protocol::RestJson1);
94 }
95
96 if content_type.contains("xml") {
98 return Some(Protocol::RestXml);
99 }
100
101 None
104}
105
106pub fn parse_request(
108 protocol: Protocol,
109 method: &Method,
110 uri: &Uri,
111 headers: &HeaderMap,
112 body: &Bytes,
113 routes: &[RouteDefinition],
114) -> Result<ParsedRequest, AwsError> {
115 match protocol {
116 Protocol::AwsJson1_0 | Protocol::AwsJson1_1 => json::parse_request(headers, body),
117 Protocol::AwsQuery | Protocol::Ec2Query => query::parse_request(body),
118 Protocol::RestJson1 => rest::parse_json_request(method, uri, body, routes),
119 Protocol::RestXml => rest::parse_xml_request(method, uri, headers, body, routes),
120 }
121}
122
123pub fn serialize_response(
125 protocol: Protocol,
126 operation: &str,
127 output: &Value,
128 request_id: &str,
129) -> (axum::http::StatusCode, HeaderMap, Bytes) {
130 match protocol {
131 Protocol::AwsJson1_0 | Protocol::AwsJson1_1 | Protocol::RestJson1 => {
132 json::serialize_response(output, request_id)
133 }
134 Protocol::AwsQuery | Protocol::Ec2Query => {
135 query::serialize_response(operation, output, request_id)
136 }
137 Protocol::RestXml => rest::serialize_xml_response(output, request_id),
138 }
139}
140
141pub fn serialize_error(
143 protocol: Protocol,
144 error: &AwsError,
145 request_id: &str,
146) -> (axum::http::StatusCode, HeaderMap, Bytes) {
147 match protocol {
148 Protocol::AwsJson1_0 | Protocol::AwsJson1_1 | Protocol::RestJson1 => {
149 json::serialize_error(error, request_id)
150 }
151 Protocol::AwsQuery | Protocol::Ec2Query | Protocol::RestXml => {
152 query::serialize_error(error, request_id)
153 }
154 }
155}