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 => "application/x-amz-json-1.0",
26 Self::RestXml | Self::AwsQuery | Self::Ec2Query => "application/xml",
27 }
28 }
29
30 pub fn is_json(&self) -> bool {
31 matches!(self, Self::AwsJson1_0 | Self::AwsJson1_1 | Self::RestJson1)
32 }
33
34 pub fn is_xml(&self) -> bool {
35 matches!(self, Self::RestXml | Self::AwsQuery | Self::Ec2Query)
36 }
37}
38
39#[derive(Debug)]
41pub struct ParsedRequest {
42 pub operation: String,
43 pub input: Value,
44}
45
46#[derive(Debug, Clone)]
48pub struct RouteDefinition {
49 pub method: &'static str,
50 pub path_pattern: &'static str,
51 pub operation: &'static str,
52 pub required_query_param: Option<&'static str>,
55}
56
57pub fn detect_protocol(headers: &HeaderMap, body: &Bytes) -> Option<Protocol> {
59 if let Some(target) = headers.get("x-amz-target") {
61 let content_type = headers
62 .get("content-type")
63 .and_then(|v| v.to_str().ok())
64 .unwrap_or("");
65 if content_type.contains("x-amz-json-1.0") {
66 return Some(Protocol::AwsJson1_0);
67 }
68 let _ = target;
70 return Some(Protocol::AwsJson1_1);
71 }
72
73 let content_type = headers
74 .get("content-type")
75 .and_then(|v| v.to_str().ok())
76 .unwrap_or("");
77
78 if content_type.contains("x-www-form-urlencoded") {
80 let body_str = std::str::from_utf8(body).unwrap_or("");
81 if body_str.contains("Action=") {
82 return Some(Protocol::AwsQuery);
83 }
84 }
85
86 if content_type.contains("json") {
88 return Some(Protocol::RestJson1);
89 }
90
91 if content_type.contains("xml") {
93 return Some(Protocol::RestXml);
94 }
95
96 None
99}
100
101pub fn parse_request(
103 protocol: Protocol,
104 method: &Method,
105 uri: &Uri,
106 headers: &HeaderMap,
107 body: &Bytes,
108 routes: &[RouteDefinition],
109) -> Result<ParsedRequest, AwsError> {
110 match protocol {
111 Protocol::AwsJson1_0 | Protocol::AwsJson1_1 => json::parse_request(headers, body),
112 Protocol::AwsQuery | Protocol::Ec2Query => query::parse_request(body),
113 Protocol::RestJson1 => rest::parse_json_request(method, uri, body, routes),
114 Protocol::RestXml => rest::parse_xml_request(method, uri, headers, body, routes),
115 }
116}
117
118pub fn serialize_response(
120 protocol: Protocol,
121 operation: &str,
122 output: &Value,
123 request_id: &str,
124) -> (axum::http::StatusCode, HeaderMap, Bytes) {
125 match protocol {
126 Protocol::AwsJson1_0 | Protocol::AwsJson1_1 | Protocol::RestJson1 => {
127 json::serialize_response(output, request_id)
128 }
129 Protocol::AwsQuery | Protocol::Ec2Query => {
130 query::serialize_response(operation, output, request_id)
131 }
132 Protocol::RestXml => rest::serialize_xml_response(output, request_id),
133 }
134}
135
136pub fn serialize_error(
138 protocol: Protocol,
139 error: &AwsError,
140 request_id: &str,
141) -> (axum::http::StatusCode, HeaderMap, Bytes) {
142 match protocol {
143 Protocol::AwsJson1_0 | Protocol::AwsJson1_1 | Protocol::RestJson1 => {
144 json::serialize_error(error, request_id)
145 }
146 Protocol::AwsQuery | Protocol::Ec2Query | Protocol::RestXml => {
147 query::serialize_error(error, request_id)
148 }
149 }
150}