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