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