1use crate::codec::{Codec, FinishSerializer};
9use crate::protocol::ClientProtocol;
10use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer, ShapeSerializer};
11use crate::{Schema, ShapeId};
12use aws_smithy_runtime_api::http::{Request, Response};
13use aws_smithy_types::body::SdkBody;
14use aws_smithy_types::config_bag::ConfigBag;
15
16#[derive(Debug)]
26pub struct HttpRpcProtocol<C> {
27 protocol_id: ShapeId,
28 codec: C,
29 content_type: &'static str,
30}
31
32impl<C: Codec> HttpRpcProtocol<C> {
33 pub fn new(protocol_id: ShapeId, codec: C, content_type: &'static str) -> Self {
35 Self {
36 protocol_id,
37 codec,
38 content_type,
39 }
40 }
41}
42
43impl<C> ClientProtocol for HttpRpcProtocol<C>
44where
45 C: Codec + Send + Sync + std::fmt::Debug + 'static,
46 for<'a> C::Deserializer<'a>: ShapeDeserializer,
47{
48 fn protocol_id(&self) -> &ShapeId {
49 &self.protocol_id
50 }
51
52 fn serialize_request(
53 &self,
54 input: &dyn SerializableStruct,
55 input_schema: &Schema,
56 endpoint: &str,
57 _cfg: &ConfigBag,
58 ) -> Result<Request, SerdeError> {
59 let mut serializer = self.codec.create_serializer();
60 serializer.write_struct(input_schema, input)?;
61 let body = serializer.finish();
62
63 let mut request = Request::new(SdkBody::from(body));
64 request
65 .set_method("POST")
66 .map_err(|e| SerdeError::custom(format!("invalid HTTP method: {e}")))?;
67 let uri = if endpoint.is_empty() { "/" } else { endpoint };
68 request
69 .set_uri(uri)
70 .map_err(|e| SerdeError::custom(format!("invalid endpoint URI: {e}")))?;
71 request
72 .headers_mut()
73 .insert("Content-Type", self.content_type);
74 if let Some(len) = request.body().content_length() {
75 request
76 .headers_mut()
77 .insert("Content-Length", len.to_string());
78 }
79 Ok(request)
80 }
81
82 fn deserialize_response<'a>(
83 &self,
84 response: &'a Response,
85 _output_schema: &Schema,
86 _cfg: &ConfigBag,
87 ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError> {
88 let body = response
89 .body()
90 .bytes()
91 .ok_or_else(|| SerdeError::custom("response body is not available as bytes"))?;
92 Ok(Box::new(self.codec.create_deserializer(body)))
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::serde::SerializableStruct;
100 use crate::{prelude::*, ShapeType};
101
102 struct TestSerializer {
103 output: Vec<u8>,
104 }
105
106 impl FinishSerializer for TestSerializer {
107 fn finish(self) -> Vec<u8> {
108 self.output
109 }
110 }
111
112 impl ShapeSerializer for TestSerializer {
113 fn write_struct(
114 &mut self,
115 _: &Schema,
116 value: &dyn SerializableStruct,
117 ) -> Result<(), SerdeError> {
118 self.output.push(b'{');
119 value.serialize_members(self)?;
120 self.output.push(b'}');
121 Ok(())
122 }
123 fn write_list(
124 &mut self,
125 _: &Schema,
126 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
127 ) -> Result<(), SerdeError> {
128 Ok(())
129 }
130 fn write_map(
131 &mut self,
132 _: &Schema,
133 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
134 ) -> Result<(), SerdeError> {
135 Ok(())
136 }
137 fn write_boolean(&mut self, _: &Schema, _: bool) -> Result<(), SerdeError> {
138 Ok(())
139 }
140 fn write_byte(&mut self, _: &Schema, _: i8) -> Result<(), SerdeError> {
141 Ok(())
142 }
143 fn write_short(&mut self, _: &Schema, _: i16) -> Result<(), SerdeError> {
144 Ok(())
145 }
146 fn write_integer(&mut self, _: &Schema, _: i32) -> Result<(), SerdeError> {
147 Ok(())
148 }
149 fn write_long(&mut self, _: &Schema, _: i64) -> Result<(), SerdeError> {
150 Ok(())
151 }
152 fn write_float(&mut self, _: &Schema, _: f32) -> Result<(), SerdeError> {
153 Ok(())
154 }
155 fn write_double(&mut self, _: &Schema, _: f64) -> Result<(), SerdeError> {
156 Ok(())
157 }
158 fn write_big_integer(
159 &mut self,
160 _: &Schema,
161 _: &aws_smithy_types::BigInteger,
162 ) -> Result<(), SerdeError> {
163 Ok(())
164 }
165 fn write_big_decimal(
166 &mut self,
167 _: &Schema,
168 _: &aws_smithy_types::BigDecimal,
169 ) -> Result<(), SerdeError> {
170 Ok(())
171 }
172 fn write_string(&mut self, _: &Schema, v: &str) -> Result<(), SerdeError> {
173 self.output.extend_from_slice(v.as_bytes());
174 Ok(())
175 }
176 fn write_blob(&mut self, _: &Schema, _: &aws_smithy_types::Blob) -> Result<(), SerdeError> {
177 Ok(())
178 }
179 fn write_timestamp(
180 &mut self,
181 _: &Schema,
182 _: &aws_smithy_types::DateTime,
183 ) -> Result<(), SerdeError> {
184 Ok(())
185 }
186 fn write_document(
187 &mut self,
188 _: &Schema,
189 _: &aws_smithy_types::Document,
190 ) -> Result<(), SerdeError> {
191 Ok(())
192 }
193 fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
194 Ok(())
195 }
196 }
197
198 struct TestDeserializer<'a> {
199 input: &'a [u8],
200 }
201
202 impl ShapeDeserializer for TestDeserializer<'_> {
203 fn read_struct(
204 &mut self,
205 _: &Schema,
206 _: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
207 ) -> Result<(), SerdeError> {
208 Ok(())
209 }
210 fn read_list(
211 &mut self,
212 _: &Schema,
213 _: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
214 ) -> Result<(), SerdeError> {
215 Ok(())
216 }
217 fn read_map(
218 &mut self,
219 _: &Schema,
220 _: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
221 ) -> Result<(), SerdeError> {
222 Ok(())
223 }
224 fn read_boolean(&mut self, _: &Schema) -> Result<bool, SerdeError> {
225 Ok(false)
226 }
227 fn read_byte(&mut self, _: &Schema) -> Result<i8, SerdeError> {
228 Ok(0)
229 }
230 fn read_short(&mut self, _: &Schema) -> Result<i16, SerdeError> {
231 Ok(0)
232 }
233 fn read_integer(&mut self, _: &Schema) -> Result<i32, SerdeError> {
234 Ok(0)
235 }
236 fn read_long(&mut self, _: &Schema) -> Result<i64, SerdeError> {
237 Ok(0)
238 }
239 fn read_float(&mut self, _: &Schema) -> Result<f32, SerdeError> {
240 Ok(0.0)
241 }
242 fn read_double(&mut self, _: &Schema) -> Result<f64, SerdeError> {
243 Ok(0.0)
244 }
245 fn read_big_integer(
246 &mut self,
247 _: &Schema,
248 ) -> Result<aws_smithy_types::BigInteger, SerdeError> {
249 use std::str::FromStr;
250 Ok(aws_smithy_types::BigInteger::from_str("0").unwrap())
251 }
252 fn read_big_decimal(
253 &mut self,
254 _: &Schema,
255 ) -> Result<aws_smithy_types::BigDecimal, SerdeError> {
256 use std::str::FromStr;
257 Ok(aws_smithy_types::BigDecimal::from_str("0").unwrap())
258 }
259 fn read_string(&mut self, _: &Schema) -> Result<String, SerdeError> {
260 Ok(String::from_utf8_lossy(self.input).into_owned())
261 }
262 fn read_blob(&mut self, _: &Schema) -> Result<aws_smithy_types::Blob, SerdeError> {
263 Ok(aws_smithy_types::Blob::new(vec![]))
264 }
265 fn read_timestamp(&mut self, _: &Schema) -> Result<aws_smithy_types::DateTime, SerdeError> {
266 Ok(aws_smithy_types::DateTime::from_secs(0))
267 }
268 fn read_document(&mut self, _: &Schema) -> Result<aws_smithy_types::Document, SerdeError> {
269 Ok(aws_smithy_types::Document::Null)
270 }
271 fn is_null(&self) -> bool {
272 false
273 }
274 fn container_size(&self) -> Option<usize> {
275 None
276 }
277 }
278
279 #[derive(Debug)]
280 struct TestCodec;
281
282 impl Codec for TestCodec {
283 type Serializer = TestSerializer;
284 type Deserializer<'a> = TestDeserializer<'a>;
285 fn create_serializer(&self) -> Self::Serializer {
286 TestSerializer { output: Vec::new() }
287 }
288 fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a> {
289 TestDeserializer { input }
290 }
291 }
292
293 static TEST_SCHEMA: Schema =
294 Schema::new(crate::shape_id!("test", "TestStruct"), ShapeType::Structure);
295
296 struct EmptyStruct;
297 impl SerializableStruct for EmptyStruct {
298 fn serialize_members(&self, _: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
299 Ok(())
300 }
301 }
302
303 static NAME_MEMBER: Schema = Schema::new_member(
304 crate::shape_id!("test", "TestStruct"),
305 ShapeType::String,
306 "name",
307 0,
308 );
309 static MEMBERS: &[&Schema] = &[&NAME_MEMBER];
310 static STRUCT_WITH_MEMBER: Schema = Schema::new_struct(
311 crate::shape_id!("test", "TestStruct"),
312 ShapeType::Structure,
313 MEMBERS,
314 );
315
316 struct NameStruct;
317 impl SerializableStruct for NameStruct {
318 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
319 s.write_string(&NAME_MEMBER, "Alice")
320 }
321 }
322
323 #[test]
324 fn serialize_sets_content_type() {
325 let protocol = HttpRpcProtocol::new(
326 crate::shape_id!("test", "rpc"),
327 TestCodec,
328 "application/x-amz-json-1.0",
329 );
330 let request = protocol
331 .serialize_request(
332 &EmptyStruct,
333 &TEST_SCHEMA,
334 "https://example.com",
335 &ConfigBag::base(),
336 )
337 .unwrap();
338 assert_eq!(
339 request.headers().get("Content-Type").unwrap(),
340 "application/x-amz-json-1.0"
341 );
342 }
343
344 #[test]
345 fn serialize_body() {
346 let protocol = HttpRpcProtocol::new(
347 crate::shape_id!("test", "rpc"),
348 TestCodec,
349 "application/x-amz-json-1.0",
350 );
351 let request = protocol
352 .serialize_request(
353 &NameStruct,
354 &STRUCT_WITH_MEMBER,
355 "https://example.com",
356 &ConfigBag::base(),
357 )
358 .unwrap();
359 assert_eq!(request.body().bytes().unwrap(), b"{Alice}");
360 }
361
362 #[test]
363 fn serialize_empty_endpoint_defaults_to_root() {
364 let protocol = HttpRpcProtocol::new(
365 crate::shape_id!("test", "rpc"),
366 TestCodec,
367 "application/x-amz-json-1.0",
368 );
369 let request = protocol
370 .serialize_request(&EmptyStruct, &TEST_SCHEMA, "", &ConfigBag::base())
371 .unwrap();
372 assert_eq!(request.uri(), "/");
373 }
374
375 #[test]
376 fn deserialize_response() {
377 let protocol = HttpRpcProtocol::new(
378 crate::shape_id!("test", "rpc"),
379 TestCodec,
380 "application/x-amz-json-1.0",
381 );
382 let response = Response::new(
383 200u16.try_into().unwrap(),
384 SdkBody::from(r#"{"result":42}"#),
385 );
386 let mut deser = protocol
387 .deserialize_response(&response, &TEST_SCHEMA, &ConfigBag::base())
388 .unwrap();
389 assert_eq!(deser.read_string(&STRING).unwrap(), r#"{"result":42}"#);
390 }
391
392 #[test]
393 fn update_endpoint() {
394 let protocol = HttpRpcProtocol::new(
395 crate::shape_id!("test", "rpc"),
396 TestCodec,
397 "application/x-amz-json-1.0",
398 );
399 let mut request = protocol
400 .serialize_request(
401 &EmptyStruct,
402 &TEST_SCHEMA,
403 "https://old.example.com",
404 &ConfigBag::base(),
405 )
406 .unwrap();
407 let endpoint = aws_smithy_types::endpoint::Endpoint::builder()
408 .url("https://new.example.com")
409 .build();
410 protocol
411 .update_endpoint(&mut request, &endpoint, &ConfigBag::base())
412 .unwrap();
413 assert_eq!(request.uri(), "https://new.example.com/");
414 }
415
416 #[test]
417 fn protocol_id() {
418 let protocol = HttpRpcProtocol::new(
419 crate::shape_id!("aws.protocols", "awsJson1_0"),
420 TestCodec,
421 "application/x-amz-json-1.0",
422 );
423 assert_eq!(protocol.protocol_id().as_str(), "aws.protocols#awsJson1_0");
424 }
425}