switchback_protocols/
http.rs1use switchback_codec_pb::canardleteer::switchback::protocol::http::v1alpha1::{
4 HttpContractMeta, HttpErrorMeta, HttpOperationMeta, HttpParameterMeta, HttpPayload,
5 HttpResponseMeta,
6};
7use switchback_codec_pb::canardleteer::switchback::protocol::http::v1alpha1::__buffa::oneof::http_payload::Kind;
8use switchback_traits::{ProtocolAttachment, ResponseSeverity, Result};
9
10use crate::severity::{http_status_code_severity, http_status_severity};
11use crate::traits::{
12 ErrorProtocol, FieldCarrierProtocol, OperationProtocol, Protocol, ProtocolWire,
13 ResponseProtocol,
14};
15use crate::wire::{decode_message, encode_message};
16
17#[derive(Clone, Copy, Debug, Default)]
19pub struct HttpProtocol;
20
21impl Protocol for HttpProtocol {
22 fn id(&self) -> &'static str {
23 "http"
24 }
25}
26
27impl OperationProtocol for HttpProtocol {
28 type OperationMeta = HttpOperationMeta;
29
30 fn format_signature(&self, meta: &Self::OperationMeta) -> String {
31 format!(
32 "{} {}",
33 meta.method.to_ascii_uppercase(),
34 meta.path_template
35 )
36 }
37}
38
39impl ResponseProtocol for HttpProtocol {
40 fn response_severity(&self, status_key: &str) -> ResponseSeverity {
41 http_status_severity(status_key)
42 }
43}
44
45impl ErrorProtocol for HttpProtocol {
46 type ErrorMeta = HttpErrorMeta;
47
48 fn error_severity(&self, error_key: &str) -> ResponseSeverity {
49 http_status_severity(error_key)
50 }
51
52 fn format_error_label(&self, error_key: &str) -> String {
53 format!("HTTP {error_key}")
54 }
55}
56
57impl FieldCarrierProtocol for HttpProtocol {
58 fn field_carrier_kinds(&self) -> &'static [&'static str] {
59 &["header", "trailer", "cookie"]
60 }
61
62 fn valid_parameter_locations(&self) -> &'static [&'static str] {
63 &["query", "path", "header", "cookie"]
64 }
65}
66
67impl HttpProtocol {
68 pub fn attach_contract(&self, meta: &HttpContractMeta) -> ProtocolAttachment {
70 attachment_from_payload(HttpPayload {
71 kind: Some(Kind::Contract(Box::new(meta.clone()))),
72 ..Default::default()
73 })
74 }
75
76 pub fn attach_operation(&self, meta: &HttpOperationMeta) -> ProtocolAttachment {
78 attachment_from_payload(HttpPayload {
79 kind: Some(Kind::Operation(Box::new(meta.clone()))),
80 ..Default::default()
81 })
82 }
83
84 pub fn attach_response(&self, meta: &HttpResponseMeta) -> ProtocolAttachment {
86 attachment_from_payload(HttpPayload {
87 kind: Some(Kind::Response(Box::new(meta.clone()))),
88 ..Default::default()
89 })
90 }
91
92 pub fn attach_error(&self, meta: &HttpErrorMeta) -> ProtocolAttachment {
94 attachment_from_payload(HttpPayload {
95 kind: Some(Kind::Error(Box::new(meta.clone()))),
96 ..Default::default()
97 })
98 }
99
100 pub fn attach_parameter(&self, meta: &HttpParameterMeta) -> ProtocolAttachment {
102 attachment_from_payload(HttpPayload {
103 kind: Some(Kind::Parameter(Box::new(meta.clone()))),
104 ..Default::default()
105 })
106 }
107
108 pub fn operation_meta(
110 method: &str,
111 path: &str,
112 request_streaming: bool,
113 response_streaming: bool,
114 ) -> HttpOperationMeta {
115 HttpOperationMeta {
116 method: method.to_ascii_uppercase(),
117 path_template: path.to_string(),
118 request_streaming,
119 response_streaming,
120 ..Default::default()
121 }
122 }
123
124 pub fn response_meta(status: &str, media_type: &str) -> HttpResponseMeta {
126 let status_code = status.parse::<u32>().unwrap_or(0);
127 HttpResponseMeta {
128 status_code,
129 media_type: media_type.to_string(),
130 ..Default::default()
131 }
132 }
133
134 pub fn error_meta(status: &str, description: &str) -> HttpErrorMeta {
136 let status_code = status.parse::<u32>().unwrap_or(0);
137 HttpErrorMeta {
138 status_code,
139 detail: description.to_string(),
140 ..Default::default()
141 }
142 }
143
144 pub fn parameter_meta(name: &str, location: &str, required: bool) -> HttpParameterMeta {
146 HttpParameterMeta {
147 name: name.to_string(),
148 location: location.to_string(),
149 required,
150 ..Default::default()
151 }
152 }
153
154 pub fn contract_meta(server_urls: &[String], default: Option<&str>) -> HttpContractMeta {
156 HttpContractMeta {
157 server_urls: server_urls.to_vec(),
158 default_server_url: default.unwrap_or_default().to_string(),
159 ..Default::default()
160 }
161 }
162
163 pub fn severity_for_status_code(code: u16) -> ResponseSeverity {
165 http_status_code_severity(code)
166 }
167
168 pub fn is_error_status(status: &str) -> bool {
170 matches!(
171 http_status_severity(status),
172 ResponseSeverity::ClientError | ResponseSeverity::ServerError
173 )
174 }
175}
176
177impl ProtocolWire for HttpOperationMeta {
178 const PROTOCOL_ID: &'static str = "http";
179
180 fn encode_to_vec(&self) -> Vec<u8> {
181 encode_message(&HttpPayload {
182 kind: Some(Kind::Operation(Box::new(self.clone()))),
183 ..Default::default()
184 })
185 }
186
187 fn decode_from_bytes(bytes: &[u8]) -> Result<Self> {
188 let payload: HttpPayload = decode_message(bytes)?;
189 match payload.kind {
190 Some(Kind::Operation(meta)) => Ok(*meta),
191 _ => Err(switchback_traits::SwitchbackError::codec(
192 "expected HttpOperationMeta payload",
193 )),
194 }
195 }
196}
197
198impl ProtocolWire for HttpErrorMeta {
199 const PROTOCOL_ID: &'static str = "http";
200
201 fn encode_to_vec(&self) -> Vec<u8> {
202 encode_message(&HttpPayload {
203 kind: Some(Kind::Error(Box::new(self.clone()))),
204 ..Default::default()
205 })
206 }
207
208 fn decode_from_bytes(bytes: &[u8]) -> Result<Self> {
209 let payload: HttpPayload = decode_message(bytes)?;
210 match payload.kind {
211 Some(Kind::Error(meta)) => Ok(*meta),
212 _ => Err(switchback_traits::SwitchbackError::codec(
213 "expected HttpErrorMeta payload",
214 )),
215 }
216 }
217}
218
219fn attachment_from_payload(payload: HttpPayload) -> ProtocolAttachment {
220 let protocol = HttpProtocol;
221 ProtocolAttachment {
222 protocol_id: protocol.id().to_string(),
223 payload: encode_message(&payload),
224 }
225}