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)]
27pub struct HttpBindingProtocol<C> {
28 protocol_id: ShapeId,
29 codec: C,
30 content_type: &'static str,
31}
32
33impl<C: Codec> HttpBindingProtocol<C> {
34 pub fn new(protocol_id: ShapeId, codec: C, content_type: &'static str) -> Self {
36 Self {
37 protocol_id,
38 codec,
39 content_type,
40 }
41 }
42}
43
44pub fn percent_encode(input: &str) -> String {
48 let mut out = String::with_capacity(input.len());
49 for byte in input.bytes() {
50 match byte {
51 b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => {
52 out.push(byte as char);
53 }
54 _ => {
55 out.push('%');
56 out.push(char::from(HEX[(byte >> 4) as usize]));
57 out.push(char::from(HEX[(byte & 0x0f) as usize]));
58 }
59 }
60 }
61 out
62}
63
64pub(crate) const HEX: &[u8; 16] = b"0123456789ABCDEF";
65
66struct HttpBindingSerializer<'a, S> {
72 body: S,
73 headers: Vec<(String, String)>,
74 query_params: Vec<(String, String)>,
75 labels: Vec<(String, String)>,
76 input_schema: Option<&'a Schema>,
80 is_top_level: bool,
83 raw_payload: Option<&'a [u8]>,
89}
90
91impl<'a, S> HttpBindingSerializer<'a, S> {
92 fn new(body: S, input_schema: Option<&'a Schema>) -> Self {
93 Self {
94 body,
95 headers: Vec::new(),
96 query_params: Vec::new(),
97 labels: Vec::new(),
98 input_schema,
99 is_top_level: true,
100 raw_payload: None,
101 }
102 }
103
104 fn resolve_member<'s>(&self, schema: &'s Schema) -> &'s Schema
108 where
109 'a: 's,
110 {
111 if let (Some(input_schema), Some(idx)) = (self.input_schema, schema.member_index()) {
112 input_schema.member_schema_by_index(idx).unwrap_or(schema)
113 } else if let (Some(input_schema), Some(name)) = (self.input_schema, schema.member_name()) {
114 input_schema.member_schema(name).unwrap_or(schema)
116 } else {
117 schema
118 }
119 }
120}
121
122impl<'a, S: ShapeSerializer> ShapeSerializer for HttpBindingSerializer<'a, S> {
123 fn write_struct(
124 &mut self,
125 schema: &Schema,
126 value: &dyn SerializableStruct,
127 ) -> Result<(), SerdeError> {
128 if self.is_top_level {
129 struct Proxy<'a, 'b, S> {
134 binder: &'a mut HttpBindingSerializer<'b, S>,
135 value: &'a dyn SerializableStruct,
136 }
137 impl<S: ShapeSerializer> SerializableStruct for Proxy<'_, '_, S> {
138 fn serialize_members(
139 &self,
140 _serializer: &mut dyn ShapeSerializer,
141 ) -> Result<(), SerdeError> {
142 let binder = self.binder as *const HttpBindingSerializer<'_, S>
143 as *mut HttpBindingSerializer<'_, S>;
144 self.value.serialize_members(unsafe { &mut *binder })
154 }
155 }
156 self.is_top_level = false;
160 let proxy = Proxy {
161 binder: self,
162 value,
163 };
164 let binder_ptr = &mut *proxy.binder as *mut HttpBindingSerializer<'_, S>;
165 unsafe { (*binder_ptr).body.write_struct(schema, &proxy) }
176 } else {
177 let schema = self.resolve_member(schema);
180 if schema.http_payload().is_some() {
181 self.body.write_struct(&crate::prelude::DOCUMENT, value)?;
185 return Ok(());
186 }
187 self.body.write_struct(schema, value)
188 }
189 }
190
191 fn write_list(
192 &mut self,
193 schema: &Schema,
194 write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
195 ) -> Result<(), SerdeError> {
196 let schema = self.resolve_member(schema);
197 if let Some(header) = schema.http_header() {
199 let mut collector = ListElementCollector::for_header();
200 write_elements(&mut collector)?;
201 let header_val = collector
204 .values
205 .iter()
206 .zip(collector.quotable.iter())
207 .map(|(s, "able)| {
208 if quotable && (s.contains(',') || s.contains('"')) {
209 format!("\"{}\"", s.replace('\\', "\\\\").replace('"', "\\\""))
210 } else {
211 s.clone()
212 }
213 })
214 .collect::<Vec<_>>()
215 .join(", ");
216 self.headers.push((header.value().to_string(), header_val));
217 return Ok(());
218 }
219 if let Some(query) = schema.http_query() {
221 let mut collector = ListElementCollector::for_query();
222 write_elements(&mut collector)?;
223 for val in collector.values {
224 self.query_params.push((query.value().to_string(), val));
225 }
226 return Ok(());
227 }
228 self.body.write_list(schema, write_elements)
229 }
230
231 fn write_map(
232 &mut self,
233 schema: &Schema,
234 write_entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
235 ) -> Result<(), SerdeError> {
236 let schema = self.resolve_member(schema);
237 if let Some(prefix) = schema.http_prefix_headers() {
239 let mut collector = MapEntryCollector::new(prefix.value().to_string());
241 write_entries(&mut collector)?;
242 self.headers.extend(collector.entries);
243 return Ok(());
244 }
245 if schema.http_query_params().is_some() {
247 let mut collector = MapEntryCollector::new(String::new());
248 write_entries(&mut collector)?;
249 let explicit_query_keys: Vec<&str> = self
252 .input_schema
253 .map(|s| {
254 s.members()
255 .iter()
256 .filter_map(|m| m.http_query().map(|q| q.value()))
257 .collect()
258 })
259 .unwrap_or_default();
260 for (k, v) in collector.entries {
261 if !explicit_query_keys.contains(&k.as_str()) {
262 self.query_params.push((k, v));
263 }
264 }
265 return Ok(());
266 }
267 self.body.write_map(schema, write_entries)
268 }
269
270 fn write_boolean(&mut self, schema: &Schema, value: bool) -> Result<(), SerdeError> {
271 let schema = self.resolve_member(schema);
272 if let Some(binding) = http_string_binding(schema) {
273 return self.add_binding(binding, schema, &value.to_string());
274 }
275 self.body.write_boolean(schema, value)
276 }
277
278 fn write_byte(&mut self, schema: &Schema, value: i8) -> Result<(), SerdeError> {
279 let schema = self.resolve_member(schema);
280 if let Some(binding) = http_string_binding(schema) {
281 return self.add_binding(binding, schema, &value.to_string());
282 }
283 self.body.write_byte(schema, value)
284 }
285
286 fn write_short(&mut self, schema: &Schema, value: i16) -> Result<(), SerdeError> {
287 let schema = self.resolve_member(schema);
288 if let Some(binding) = http_string_binding(schema) {
289 return self.add_binding(binding, schema, &value.to_string());
290 }
291 self.body.write_short(schema, value)
292 }
293
294 fn write_integer(&mut self, schema: &Schema, value: i32) -> Result<(), SerdeError> {
295 let schema = self.resolve_member(schema);
296 if let Some(binding) = http_string_binding(schema) {
297 return self.add_binding(binding, schema, &value.to_string());
298 }
299 self.body.write_integer(schema, value)
300 }
301
302 fn write_long(&mut self, schema: &Schema, value: i64) -> Result<(), SerdeError> {
303 let schema = self.resolve_member(schema);
304 if let Some(binding) = http_string_binding(schema) {
305 return self.add_binding(binding, schema, &value.to_string());
306 }
307 self.body.write_long(schema, value)
308 }
309
310 fn write_float(&mut self, schema: &Schema, value: f32) -> Result<(), SerdeError> {
311 let schema = self.resolve_member(schema);
312 if let Some(binding) = http_string_binding(schema) {
313 return self.add_binding(binding, schema, &format_float_f32(value));
314 }
315 self.body.write_float(schema, value)
316 }
317
318 fn write_double(&mut self, schema: &Schema, value: f64) -> Result<(), SerdeError> {
319 let schema = self.resolve_member(schema);
320 if let Some(binding) = http_string_binding(schema) {
321 return self.add_binding(binding, schema, &format_float_f64(value));
322 }
323 self.body.write_double(schema, value)
324 }
325
326 fn write_big_integer(
327 &mut self,
328 schema: &Schema,
329 value: &aws_smithy_types::BigInteger,
330 ) -> Result<(), SerdeError> {
331 let schema = self.resolve_member(schema);
332 if let Some(binding) = http_string_binding(schema) {
333 return self.add_binding(binding, schema, value.as_ref());
334 }
335 self.body.write_big_integer(schema, value)
336 }
337
338 fn write_big_decimal(
339 &mut self,
340 schema: &Schema,
341 value: &aws_smithy_types::BigDecimal,
342 ) -> Result<(), SerdeError> {
343 let schema = self.resolve_member(schema);
344 if let Some(binding) = http_string_binding(schema) {
345 return self.add_binding(binding, schema, value.as_ref());
346 }
347 self.body.write_big_decimal(schema, value)
348 }
349
350 fn write_string(&mut self, schema: &Schema, value: &str) -> Result<(), SerdeError> {
351 let schema = self.resolve_member(schema);
352 if let Some(binding) = http_string_binding(schema) {
353 if schema.media_type().is_some() {
355 let encoded = aws_smithy_types::base64::encode(value.as_bytes());
356 return self.add_binding(binding, schema, &encoded);
357 }
358 return self.add_binding(binding, schema, value);
359 }
360 if schema.http_payload().is_some() {
361 self.raw_payload =
372 Some(unsafe { std::mem::transmute::<&[u8], &'a [u8]>(value.as_bytes()) });
373 return Ok(());
374 }
375 self.body.write_string(schema, value)
376 }
377
378 fn write_blob(
379 &mut self,
380 schema: &Schema,
381 value: &aws_smithy_types::Blob,
382 ) -> Result<(), SerdeError> {
383 let schema = self.resolve_member(schema);
384 if schema.http_header().is_some() {
385 let encoded = aws_smithy_types::base64::encode(value.as_ref());
386 self.headers
387 .push((schema.http_header().unwrap().value().to_string(), encoded));
388 return Ok(());
389 }
390 if schema.http_payload().is_some() {
391 self.raw_payload =
402 Some(unsafe { std::mem::transmute::<&[u8], &'a [u8]>(value.as_ref()) });
403 return Ok(());
404 }
405 self.body.write_blob(schema, value)
406 }
407
408 fn write_timestamp(
409 &mut self,
410 schema: &Schema,
411 value: &aws_smithy_types::DateTime,
412 ) -> Result<(), SerdeError> {
413 let schema = self.resolve_member(schema);
414 if let Some(binding) = http_string_binding(schema) {
415 let format = if let Some(ts_trait) = schema.timestamp_format() {
417 match ts_trait.format() {
418 crate::traits::TimestampFormat::EpochSeconds => {
419 aws_smithy_types::date_time::Format::EpochSeconds
420 }
421 crate::traits::TimestampFormat::HttpDate => {
422 aws_smithy_types::date_time::Format::HttpDate
423 }
424 crate::traits::TimestampFormat::DateTime => {
425 aws_smithy_types::date_time::Format::DateTime
426 }
427 }
428 } else {
429 match binding {
430 HttpBinding::Header(_) => aws_smithy_types::date_time::Format::HttpDate,
431 _ => aws_smithy_types::date_time::Format::DateTime,
432 }
433 };
434 let formatted = value
435 .fmt(format)
436 .map_err(|e| SerdeError::custom(format!("failed to format timestamp: {e}")))?;
437 return self.add_binding(binding, schema, &formatted);
438 }
439 self.body.write_timestamp(schema, value)
440 }
441
442 fn write_document(
443 &mut self,
444 schema: &Schema,
445 value: &aws_smithy_types::Document,
446 ) -> Result<(), SerdeError> {
447 self.body.write_document(schema, value)
448 }
449
450 fn write_null(&mut self, schema: &Schema) -> Result<(), SerdeError> {
451 self.body.write_null(schema)
452 }
453}
454
455enum HttpBinding<'a> {
457 Header(&'a str),
458 Query(&'a str),
459 Label,
460}
461
462fn http_string_binding(schema: &Schema) -> Option<HttpBinding<'_>> {
464 if let Some(h) = schema.http_header() {
465 return Some(HttpBinding::Header(h.value()));
466 }
467 if let Some(q) = schema.http_query() {
468 return Some(HttpBinding::Query(q.value()));
469 }
470 if schema.http_label().is_some() {
471 return Some(HttpBinding::Label);
472 }
473 None
474}
475
476impl<'a, S> HttpBindingSerializer<'a, S> {
477 fn add_binding(
478 &mut self,
479 binding: HttpBinding<'_>,
480 schema: &Schema,
481 value: &str,
482 ) -> Result<(), SerdeError> {
483 match binding {
484 HttpBinding::Header(name) => {
485 self.headers.push((name.to_string(), value.to_string()));
486 }
487 HttpBinding::Query(name) => {
488 self.query_params
489 .push((name.to_string(), value.to_string()));
490 }
491 HttpBinding::Label => {
492 let name = schema
493 .member_name()
494 .ok_or_else(|| SerdeError::custom("httpLabel on non-member schema"))?;
495 self.labels.push((name.to_string(), value.to_string()));
496 }
497 }
498 Ok(())
499 }
500}
501
502#[derive(Copy, Clone)]
505enum HttpListTarget {
506 Header,
507 Query,
508}
509
510struct ListElementCollector {
512 values: Vec<String>,
513 quotable: Vec<bool>,
515 target: HttpListTarget,
516}
517
518impl ListElementCollector {
519 fn for_header() -> Self {
520 Self::new(HttpListTarget::Header)
521 }
522
523 fn for_query() -> Self {
524 Self::new(HttpListTarget::Query)
525 }
526
527 fn new(target: HttpListTarget) -> Self {
528 Self {
529 values: Vec::new(),
530 quotable: Vec::new(),
531 target,
532 }
533 }
534
535 fn push(&mut self, value: String) {
536 self.quotable.push(true);
537 self.values.push(value);
538 }
539
540 fn push_unquotable(&mut self, value: String) {
541 self.quotable.push(false);
542 self.values.push(value);
543 }
544}
545
546impl ShapeSerializer for ListElementCollector {
547 fn write_string(&mut self, _schema: &Schema, value: &str) -> Result<(), SerdeError> {
548 self.push(value.to_string());
549 Ok(())
550 }
551 fn write_boolean(&mut self, _: &Schema, value: bool) -> Result<(), SerdeError> {
552 self.push(value.to_string());
553 Ok(())
554 }
555 fn write_byte(&mut self, _: &Schema, value: i8) -> Result<(), SerdeError> {
556 self.push(value.to_string());
557 Ok(())
558 }
559 fn write_short(&mut self, _: &Schema, value: i16) -> Result<(), SerdeError> {
560 self.push(value.to_string());
561 Ok(())
562 }
563 fn write_integer(&mut self, _: &Schema, value: i32) -> Result<(), SerdeError> {
564 self.push(value.to_string());
565 Ok(())
566 }
567 fn write_long(&mut self, _: &Schema, value: i64) -> Result<(), SerdeError> {
568 self.push(value.to_string());
569 Ok(())
570 }
571 fn write_float(&mut self, _: &Schema, value: f32) -> Result<(), SerdeError> {
572 self.push(format_float_f32(value));
573 Ok(())
574 }
575 fn write_double(&mut self, _: &Schema, value: f64) -> Result<(), SerdeError> {
576 self.push(format_float_f64(value));
577 Ok(())
578 }
579 fn write_timestamp(
580 &mut self,
581 schema: &Schema,
582 value: &aws_smithy_types::DateTime,
583 ) -> Result<(), SerdeError> {
584 let format = match schema.timestamp_format() {
585 Some(ts) => match ts.format() {
586 crate::traits::TimestampFormat::EpochSeconds => {
587 aws_smithy_types::date_time::Format::EpochSeconds
588 }
589 crate::traits::TimestampFormat::HttpDate => {
590 aws_smithy_types::date_time::Format::HttpDate
591 }
592 crate::traits::TimestampFormat::DateTime => {
593 aws_smithy_types::date_time::Format::DateTime
594 }
595 },
596 None => match self.target {
598 HttpListTarget::Header => aws_smithy_types::date_time::Format::HttpDate,
599 HttpListTarget::Query => aws_smithy_types::date_time::Format::DateTime,
600 },
601 };
602 self.push_unquotable(
603 value
604 .fmt(format)
605 .map_err(|e| SerdeError::custom(format!("failed to format timestamp: {e}")))?,
606 );
607 Ok(())
608 }
609 fn write_blob(
610 &mut self,
611 _schema: &Schema,
612 value: &aws_smithy_types::Blob,
613 ) -> Result<(), SerdeError> {
614 self.push(aws_smithy_types::base64::encode(value.as_ref()));
615 Ok(())
616 }
617 fn write_struct(&mut self, _: &Schema, _: &dyn SerializableStruct) -> Result<(), SerdeError> {
619 Ok(())
620 }
621 fn write_list(
622 &mut self,
623 _: &Schema,
624 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
625 ) -> Result<(), SerdeError> {
626 Ok(())
627 }
628 fn write_map(
629 &mut self,
630 _: &Schema,
631 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
632 ) -> Result<(), SerdeError> {
633 Ok(())
634 }
635 fn write_big_integer(
636 &mut self,
637 _: &Schema,
638 _: &aws_smithy_types::BigInteger,
639 ) -> Result<(), SerdeError> {
640 Ok(())
641 }
642 fn write_big_decimal(
643 &mut self,
644 _: &Schema,
645 _: &aws_smithy_types::BigDecimal,
646 ) -> Result<(), SerdeError> {
647 Ok(())
648 }
649 fn write_document(
650 &mut self,
651 _: &Schema,
652 _: &aws_smithy_types::Document,
653 ) -> Result<(), SerdeError> {
654 Ok(())
655 }
656 fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
657 Ok(())
658 }
659}
660
661fn format_float_f32(value: f32) -> String {
664 if value.is_infinite() {
665 if value.is_sign_positive() {
666 "Infinity".to_string()
667 } else {
668 "-Infinity".to_string()
669 }
670 } else if value.is_nan() {
671 "NaN".to_string()
672 } else {
673 value.to_string()
674 }
675}
676
677fn format_float_f64(value: f64) -> String {
678 if value.is_infinite() {
679 if value.is_sign_positive() {
680 "Infinity".to_string()
681 } else {
682 "-Infinity".to_string()
683 }
684 } else if value.is_nan() {
685 "NaN".to_string()
686 } else {
687 value.to_string()
688 }
689}
690
691struct MapEntryCollector {
694 prefix: String,
695 entries: Vec<(String, String)>,
696 pending_key: Option<String>,
697}
698
699impl MapEntryCollector {
700 fn new(prefix: String) -> Self {
701 Self {
702 prefix,
703 entries: Vec::new(),
704 pending_key: None,
705 }
706 }
707}
708
709impl ShapeSerializer for MapEntryCollector {
710 fn write_string(&mut self, _schema: &Schema, value: &str) -> Result<(), SerdeError> {
711 if let Some(key) = self.pending_key.take() {
712 self.entries
713 .push((format!("{}{}", self.prefix, key), value.to_string()));
714 } else {
715 self.pending_key = Some(value.to_string());
716 }
717 Ok(())
718 }
719
720 fn write_struct(&mut self, _: &Schema, _: &dyn SerializableStruct) -> Result<(), SerdeError> {
723 Ok(())
724 }
725 fn write_list(
726 &mut self,
727 _: &Schema,
728 write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
729 ) -> Result<(), SerdeError> {
730 if let Some(key) = self.pending_key.take() {
733 let mut collector = ListElementCollector::for_query(); write_elements(&mut collector)?;
735 for val in collector.values {
736 self.entries.push((format!("{}{}", self.prefix, key), val));
737 }
738 }
739 Ok(())
740 }
741 fn write_map(
742 &mut self,
743 _: &Schema,
744 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
745 ) -> Result<(), SerdeError> {
746 Ok(())
747 }
748 fn write_boolean(&mut self, _: &Schema, _: bool) -> Result<(), SerdeError> {
749 Ok(())
750 }
751 fn write_byte(&mut self, _: &Schema, _: i8) -> Result<(), SerdeError> {
752 Ok(())
753 }
754 fn write_short(&mut self, _: &Schema, _: i16) -> Result<(), SerdeError> {
755 Ok(())
756 }
757 fn write_integer(&mut self, _: &Schema, _: i32) -> Result<(), SerdeError> {
758 Ok(())
759 }
760 fn write_long(&mut self, _: &Schema, _: i64) -> Result<(), SerdeError> {
761 Ok(())
762 }
763 fn write_float(&mut self, _: &Schema, _: f32) -> Result<(), SerdeError> {
764 Ok(())
765 }
766 fn write_double(&mut self, _: &Schema, _: f64) -> Result<(), SerdeError> {
767 Ok(())
768 }
769 fn write_big_integer(
770 &mut self,
771 _: &Schema,
772 _: &aws_smithy_types::BigInteger,
773 ) -> Result<(), SerdeError> {
774 Ok(())
775 }
776 fn write_big_decimal(
777 &mut self,
778 _: &Schema,
779 _: &aws_smithy_types::BigDecimal,
780 ) -> Result<(), SerdeError> {
781 Ok(())
782 }
783 fn write_blob(&mut self, _: &Schema, _: &aws_smithy_types::Blob) -> Result<(), SerdeError> {
784 Ok(())
785 }
786 fn write_timestamp(
787 &mut self,
788 _: &Schema,
789 _: &aws_smithy_types::DateTime,
790 ) -> Result<(), SerdeError> {
791 Ok(())
792 }
793 fn write_document(
794 &mut self,
795 _: &Schema,
796 _: &aws_smithy_types::Document,
797 ) -> Result<(), SerdeError> {
798 Ok(())
799 }
800 fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
801 Ok(())
802 }
803}
804
805impl<C> ClientProtocolInner for HttpBindingProtocol<C>
806where
807 C: Codec + Send + Sync + std::fmt::Debug + 'static,
808 for<'a> C::Deserializer<'a>: ShapeDeserializer,
809{
810 type Request = Request;
811 type Response = Response;
812
813 fn protocol_id(&self) -> &ShapeId {
814 &self.protocol_id
815 }
816
817 fn serialize_request(
818 &self,
819 input: &dyn SerializableStruct,
820 input_schema: &Schema,
821 endpoint: &str,
822 _cfg: &ConfigBag,
823 ) -> Result<Request, SerdeError> {
824 let mut binder =
825 HttpBindingSerializer::new(self.codec.create_serializer(), Some(input_schema));
826
827 let has_struct_payload = input_schema.members().iter().any(|m| {
831 m.http_payload().is_some()
832 && matches!(
833 m.shape_type(),
834 crate::ShapeType::Structure | crate::ShapeType::Union
835 )
836 });
837 if has_struct_payload {
838 binder.is_top_level = false;
839 input.serialize_members(&mut binder)?;
840 } else {
841 binder.write_struct(input_schema, input)?;
842 }
843 let raw_payload = binder.raw_payload;
844 let mut body = if raw_payload.is_some() {
845 Vec::new()
847 } else {
848 binder.body.finish()
849 };
850
851 let has_blob_or_string_payload = raw_payload.is_some();
856 let has_body_members = has_struct_payload
857 || input_schema.members().iter().any(|m| {
858 m.http_header().is_none()
859 && m.http_query().is_none()
860 && m.http_label().is_none()
861 && m.http_prefix_headers().is_none()
862 && m.http_query_params().is_none()
863 && m.http_payload().is_none()
864 });
865
866 let set_content_type = if has_blob_or_string_payload {
867 false
871 } else if has_body_members {
872 true
875 } else {
876 body = Vec::new();
878 false
879 };
880
881 let mut uri = match input_schema.http() {
884 Some(h) => {
885 let mut path = h.uri().to_string();
886 for (name, value) in &binder.labels {
887 let greedy = format!("{{{name}+}}");
889 if path.contains(&greedy) {
890 let encoded = value
892 .split('/')
893 .map(percent_encode)
894 .collect::<Vec<_>>()
895 .join("/");
896 path = path.replace(&greedy, &encoded);
897 } else {
898 let placeholder = format!("{{{name}}}");
899 path = path.replace(&placeholder, &percent_encode(value));
900 }
901 }
902 if endpoint.is_empty() {
903 path
904 } else {
905 format!("{}{}", endpoint, path)
906 }
907 }
908 None => {
909 let mut u = if endpoint.is_empty() {
910 "/".to_string()
911 } else {
912 endpoint.to_string()
913 };
914 for (name, value) in &binder.labels {
915 let greedy = format!("{{{name}+}}");
916 if u.contains(&greedy) {
917 let encoded = value
918 .split('/')
919 .map(percent_encode)
920 .collect::<Vec<_>>()
921 .join("/");
922 u = u.replace(&greedy, &encoded);
923 } else {
924 let placeholder = format!("{{{name}}}");
925 u = u.replace(&placeholder, &percent_encode(value));
926 }
927 }
928 u
929 }
930 };
931 if !binder.query_params.is_empty() {
932 uri.push(if uri.contains('?') { '&' } else { '?' });
933 let pairs: Vec<String> = binder
934 .query_params
935 .iter()
936 .map(|(k, v)| format!("{}={}", percent_encode(k), percent_encode(v)))
937 .collect();
938 uri.push_str(&pairs.join("&"));
939 }
940
941 let mut request = if let Some(payload) = raw_payload {
942 Request::new(SdkBody::from(payload))
943 } else {
944 Request::new(SdkBody::from(body))
945 };
946 if let Some(http) = input_schema.http() {
948 request
949 .set_method(http.method())
950 .map_err(|e| SerdeError::custom(format!("invalid HTTP method: {e}")))?;
951 }
952 request
953 .set_uri(uri.as_str())
954 .map_err(|e| SerdeError::custom(format!("invalid endpoint URI: {e}")))?;
955 if set_content_type {
956 request
957 .headers_mut()
958 .insert("Content-Type", self.content_type);
959 }
960 if let Some(len) = request.body().content_length() {
961 if len > 0 || set_content_type {
962 request
963 .headers_mut()
964 .insert("Content-Length", len.to_string());
965 }
966 }
967 for (name, value) in &binder.headers {
968 request.headers_mut().insert(name.clone(), value.clone());
969 }
970 Ok(request)
971 }
972
973 fn deserialize_response<'a>(
974 &self,
975 response: &'a Response,
976 _output_schema: &Schema,
977 _cfg: &ConfigBag,
978 ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError> {
979 let body = response
980 .body()
981 .bytes()
982 .ok_or_else(|| SerdeError::custom("response body is not available as bytes"))?;
983 Ok(Box::new(self.codec.create_deserializer(body)))
984 }
985
986 fn payload_codec(&self) -> Option<&dyn crate::codec::DynCodec> {
987 Some(&self.codec)
988 }
989
990 fn update_endpoint(
991 &self,
992 request: &mut Request,
993 endpoint: &aws_smithy_types::endpoint::Endpoint,
994 cfg: &ConfigBag,
995 ) -> Result<(), SerdeError> {
996 apply_http_endpoint(request, endpoint, cfg)
997 }
998}
999
1000#[cfg(test)]
1001mod tests {
1002 use super::*;
1003 use crate::serde::SerializableStruct;
1004 use crate::{prelude::*, ShapeType};
1005
1006 struct TestSerializer {
1007 output: Vec<u8>,
1008 }
1009
1010 impl FinishSerializer for TestSerializer {
1011 fn finish(self) -> Vec<u8> {
1012 self.output
1013 }
1014 }
1015
1016 impl ShapeSerializer for TestSerializer {
1017 fn write_struct(
1018 &mut self,
1019 _: &Schema,
1020 value: &dyn SerializableStruct,
1021 ) -> Result<(), SerdeError> {
1022 self.output.push(b'{');
1023 value.serialize_members(self)?;
1024 self.output.push(b'}');
1025 Ok(())
1026 }
1027 fn write_list(
1028 &mut self,
1029 _: &Schema,
1030 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1031 ) -> Result<(), SerdeError> {
1032 Ok(())
1033 }
1034 fn write_map(
1035 &mut self,
1036 _: &Schema,
1037 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1038 ) -> Result<(), SerdeError> {
1039 Ok(())
1040 }
1041 fn write_boolean(&mut self, _: &Schema, _: bool) -> Result<(), SerdeError> {
1042 Ok(())
1043 }
1044 fn write_byte(&mut self, _: &Schema, _: i8) -> Result<(), SerdeError> {
1045 Ok(())
1046 }
1047 fn write_short(&mut self, _: &Schema, _: i16) -> Result<(), SerdeError> {
1048 Ok(())
1049 }
1050 fn write_integer(&mut self, _: &Schema, _: i32) -> Result<(), SerdeError> {
1051 Ok(())
1052 }
1053 fn write_long(&mut self, _: &Schema, _: i64) -> Result<(), SerdeError> {
1054 Ok(())
1055 }
1056 fn write_float(&mut self, _: &Schema, _: f32) -> Result<(), SerdeError> {
1057 Ok(())
1058 }
1059 fn write_double(&mut self, _: &Schema, _: f64) -> Result<(), SerdeError> {
1060 Ok(())
1061 }
1062 fn write_big_integer(
1063 &mut self,
1064 _: &Schema,
1065 _: &aws_smithy_types::BigInteger,
1066 ) -> Result<(), SerdeError> {
1067 Ok(())
1068 }
1069 fn write_big_decimal(
1070 &mut self,
1071 _: &Schema,
1072 _: &aws_smithy_types::BigDecimal,
1073 ) -> Result<(), SerdeError> {
1074 Ok(())
1075 }
1076 fn write_string(&mut self, _: &Schema, v: &str) -> Result<(), SerdeError> {
1077 self.output.extend_from_slice(v.as_bytes());
1078 Ok(())
1079 }
1080 fn write_blob(&mut self, _: &Schema, _: &aws_smithy_types::Blob) -> Result<(), SerdeError> {
1081 Ok(())
1082 }
1083 fn write_timestamp(
1084 &mut self,
1085 _: &Schema,
1086 _: &aws_smithy_types::DateTime,
1087 ) -> Result<(), SerdeError> {
1088 Ok(())
1089 }
1090 fn write_document(
1091 &mut self,
1092 _: &Schema,
1093 _: &aws_smithy_types::Document,
1094 ) -> Result<(), SerdeError> {
1095 Ok(())
1096 }
1097 fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
1098 Ok(())
1099 }
1100 }
1101
1102 struct TestDeserializer<'a> {
1103 input: &'a [u8],
1104 }
1105
1106 impl ShapeDeserializer for TestDeserializer<'_> {
1107 fn read_struct(
1108 &mut self,
1109 _: &Schema,
1110 _: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1111 ) -> Result<(), SerdeError> {
1112 Ok(())
1113 }
1114 fn read_list(
1115 &mut self,
1116 _: &Schema,
1117 _: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1118 ) -> Result<(), SerdeError> {
1119 Ok(())
1120 }
1121 fn read_map(
1122 &mut self,
1123 _: &Schema,
1124 _: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1125 ) -> Result<(), SerdeError> {
1126 Ok(())
1127 }
1128 fn read_boolean(&mut self, _: &Schema) -> Result<bool, SerdeError> {
1129 Ok(false)
1130 }
1131 fn read_byte(&mut self, _: &Schema) -> Result<i8, SerdeError> {
1132 Ok(0)
1133 }
1134 fn read_short(&mut self, _: &Schema) -> Result<i16, SerdeError> {
1135 Ok(0)
1136 }
1137 fn read_integer(&mut self, _: &Schema) -> Result<i32, SerdeError> {
1138 Ok(0)
1139 }
1140 fn read_long(&mut self, _: &Schema) -> Result<i64, SerdeError> {
1141 Ok(0)
1142 }
1143 fn read_float(&mut self, _: &Schema) -> Result<f32, SerdeError> {
1144 Ok(0.0)
1145 }
1146 fn read_double(&mut self, _: &Schema) -> Result<f64, SerdeError> {
1147 Ok(0.0)
1148 }
1149 fn read_big_integer(
1150 &mut self,
1151 _: &Schema,
1152 ) -> Result<aws_smithy_types::BigInteger, SerdeError> {
1153 use std::str::FromStr;
1154 Ok(aws_smithy_types::BigInteger::from_str("0").unwrap())
1155 }
1156 fn read_big_decimal(
1157 &mut self,
1158 _: &Schema,
1159 ) -> Result<aws_smithy_types::BigDecimal, SerdeError> {
1160 use std::str::FromStr;
1161 Ok(aws_smithy_types::BigDecimal::from_str("0").unwrap())
1162 }
1163 fn read_string(&mut self, _: &Schema) -> Result<String, SerdeError> {
1164 Ok(String::from_utf8_lossy(self.input).into_owned())
1165 }
1166 fn read_blob(&mut self, _: &Schema) -> Result<aws_smithy_types::Blob, SerdeError> {
1167 Ok(aws_smithy_types::Blob::new(vec![]))
1168 }
1169 fn read_timestamp(&mut self, _: &Schema) -> Result<aws_smithy_types::DateTime, SerdeError> {
1170 Ok(aws_smithy_types::DateTime::from_secs(0))
1171 }
1172 fn read_document(&mut self, _: &Schema) -> Result<aws_smithy_types::Document, SerdeError> {
1173 Ok(aws_smithy_types::Document::Null)
1174 }
1175 fn is_null(&self) -> bool {
1176 false
1177 }
1178 fn container_size(&self) -> Option<usize> {
1179 None
1180 }
1181 }
1182
1183 #[derive(Debug)]
1184 struct TestCodec;
1185
1186 impl Codec for TestCodec {
1187 type Serializer = TestSerializer;
1188 type Deserializer<'a> = TestDeserializer<'a>;
1189 fn create_serializer(&self) -> Self::Serializer {
1190 TestSerializer { output: Vec::new() }
1191 }
1192 fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a> {
1193 TestDeserializer { input }
1194 }
1195 }
1196
1197 static TEST_SCHEMA: Schema =
1198 Schema::new(crate::shape_id!("test", "TestStruct"), ShapeType::Structure);
1199
1200 struct EmptyStruct;
1201 impl SerializableStruct for EmptyStruct {
1202 fn serialize_members(&self, _: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1203 Ok(())
1204 }
1205 }
1206
1207 static NAME_MEMBER: Schema = Schema::new_member(
1208 crate::shape_id!("test", "TestStruct"),
1209 ShapeType::String,
1210 "name",
1211 0,
1212 );
1213 static MEMBERS: &[&Schema] = &[&NAME_MEMBER];
1214 static STRUCT_WITH_MEMBER: Schema = Schema::new_struct(
1215 crate::shape_id!("test", "TestStruct"),
1216 ShapeType::Structure,
1217 MEMBERS,
1218 );
1219
1220 struct NameStruct;
1221 impl SerializableStruct for NameStruct {
1222 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1223 s.write_string(&NAME_MEMBER, "Alice")
1224 }
1225 }
1226
1227 fn make_protocol() -> HttpBindingProtocol<TestCodec> {
1228 HttpBindingProtocol::new(
1229 crate::shape_id!("test", "proto"),
1230 TestCodec,
1231 "application/test",
1232 )
1233 }
1234
1235 #[test]
1236 fn serialize_sets_content_type() {
1237 let request = make_protocol()
1239 .serialize_request(
1240 &EmptyStruct,
1241 &STRUCT_WITH_MEMBER,
1242 "https://example.com",
1243 &ConfigBag::base(),
1244 )
1245 .unwrap();
1246 assert_eq!(
1247 request.headers().get("Content-Type").unwrap(),
1248 "application/test"
1249 );
1250 }
1251
1252 #[test]
1253 fn serialize_no_body_members_omits_content_type() {
1254 let request = make_protocol()
1256 .serialize_request(
1257 &EmptyStruct,
1258 &TEST_SCHEMA,
1259 "https://example.com",
1260 &ConfigBag::base(),
1261 )
1262 .unwrap();
1263 assert!(request.headers().get("Content-Type").is_none());
1264 }
1265
1266 #[test]
1267 fn serialize_sets_uri() {
1268 let request = make_protocol()
1269 .serialize_request(
1270 &EmptyStruct,
1271 &TEST_SCHEMA,
1272 "https://example.com/path",
1273 &ConfigBag::base(),
1274 )
1275 .unwrap();
1276 assert_eq!(request.uri(), "https://example.com/path");
1277 }
1278
1279 #[test]
1280 fn serialize_body() {
1281 let request = make_protocol()
1282 .serialize_request(
1283 &NameStruct,
1284 &STRUCT_WITH_MEMBER,
1285 "https://example.com",
1286 &ConfigBag::base(),
1287 )
1288 .unwrap();
1289 assert_eq!(request.body().bytes().unwrap(), b"{Alice}");
1290 }
1291
1292 #[test]
1293 fn deserialize_response() {
1294 let response = Response::new(
1295 200u16.try_into().unwrap(),
1296 SdkBody::from(r#"{"name":"Bob"}"#),
1297 );
1298 let mut deser = make_protocol()
1299 .deserialize_response(&response, &TEST_SCHEMA, &ConfigBag::base())
1300 .unwrap();
1301 assert_eq!(deser.read_string(&STRING).unwrap(), r#"{"name":"Bob"}"#);
1302 }
1303
1304 #[test]
1305 fn update_endpoint() {
1306 let mut request = make_protocol()
1307 .serialize_request(
1308 &EmptyStruct,
1309 &TEST_SCHEMA,
1310 "https://old.example.com",
1311 &ConfigBag::base(),
1312 )
1313 .unwrap();
1314 let endpoint = aws_smithy_types::endpoint::Endpoint::builder()
1315 .url("https://new.example.com")
1316 .build();
1317 make_protocol()
1318 .update_endpoint(&mut request, &endpoint, &ConfigBag::base())
1319 .unwrap();
1320 assert_eq!(request.uri(), "https://new.example.com/");
1321 }
1322
1323 #[test]
1324 fn protocol_id() {
1325 let protocol = HttpBindingProtocol::new(
1326 crate::shape_id!("aws.protocols", "restJson1"),
1327 TestCodec,
1328 "application/json",
1329 );
1330 assert_eq!(protocol.protocol_id().as_str(), "aws.protocols#restJson1");
1331 }
1332
1333 #[test]
1334 fn invalid_uri_returns_error() {
1335 assert!(make_protocol()
1336 .serialize_request(
1337 &EmptyStruct,
1338 &TEST_SCHEMA,
1339 "not a valid uri\n\n",
1340 &ConfigBag::base()
1341 )
1342 .is_err());
1343 }
1344
1345 static HEADER_MEMBER: Schema = Schema::new_member(
1348 crate::shape_id!("test", "S"),
1349 ShapeType::String,
1350 "xToken",
1351 0,
1352 )
1353 .with_http_header("X-Token");
1354
1355 static HEADER_SCHEMA: Schema = Schema::new_struct(
1356 crate::shape_id!("test", "S"),
1357 ShapeType::Structure,
1358 &[&HEADER_MEMBER],
1359 );
1360
1361 struct HeaderStruct;
1362 impl SerializableStruct for HeaderStruct {
1363 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1364 s.write_string(&HEADER_MEMBER, "my-token-value")
1365 }
1366 }
1367
1368 #[test]
1369 fn http_header_string() {
1370 let request = make_protocol()
1371 .serialize_request(
1372 &HeaderStruct,
1373 &HEADER_SCHEMA,
1374 "https://example.com",
1375 &ConfigBag::base(),
1376 )
1377 .unwrap();
1378 assert_eq!(request.headers().get("X-Token").unwrap(), "my-token-value");
1379 }
1380
1381 static INT_HEADER_MEMBER: Schema = Schema::new_member(
1382 crate::shape_id!("test", "S"),
1383 ShapeType::Integer,
1384 "retryCount",
1385 0,
1386 )
1387 .with_http_header("X-Retry-Count");
1388
1389 static INT_HEADER_SCHEMA: Schema = Schema::new_struct(
1390 crate::shape_id!("test", "S"),
1391 ShapeType::Structure,
1392 &[&INT_HEADER_MEMBER],
1393 );
1394
1395 struct IntHeaderStruct;
1396 impl SerializableStruct for IntHeaderStruct {
1397 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1398 s.write_integer(&INT_HEADER_MEMBER, 3)
1399 }
1400 }
1401
1402 #[test]
1403 fn http_header_integer() {
1404 let request = make_protocol()
1405 .serialize_request(
1406 &IntHeaderStruct,
1407 &INT_HEADER_SCHEMA,
1408 "https://example.com",
1409 &ConfigBag::base(),
1410 )
1411 .unwrap();
1412 assert_eq!(request.headers().get("X-Retry-Count").unwrap(), "3");
1413 }
1414
1415 static BOOL_HEADER_MEMBER: Schema = Schema::new_member(
1416 crate::shape_id!("test", "S"),
1417 ShapeType::Boolean,
1418 "verbose",
1419 0,
1420 )
1421 .with_http_header("X-Verbose");
1422
1423 static BOOL_HEADER_SCHEMA: Schema = Schema::new_struct(
1424 crate::shape_id!("test", "S"),
1425 ShapeType::Structure,
1426 &[&BOOL_HEADER_MEMBER],
1427 );
1428
1429 struct BoolHeaderStruct;
1430 impl SerializableStruct for BoolHeaderStruct {
1431 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1432 s.write_boolean(&BOOL_HEADER_MEMBER, true)
1433 }
1434 }
1435
1436 #[test]
1437 fn http_header_boolean() {
1438 let request = make_protocol()
1439 .serialize_request(
1440 &BoolHeaderStruct,
1441 &BOOL_HEADER_SCHEMA,
1442 "https://example.com",
1443 &ConfigBag::base(),
1444 )
1445 .unwrap();
1446 assert_eq!(request.headers().get("X-Verbose").unwrap(), "true");
1447 }
1448
1449 static QUERY_MEMBER: Schema =
1452 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "color", 0)
1453 .with_http_query("color");
1454
1455 static QUERY_SCHEMA: Schema = Schema::new_struct(
1456 crate::shape_id!("test", "S"),
1457 ShapeType::Structure,
1458 &[&QUERY_MEMBER],
1459 );
1460
1461 struct QueryStruct;
1462 impl SerializableStruct for QueryStruct {
1463 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1464 s.write_string(&QUERY_MEMBER, "blue")
1465 }
1466 }
1467
1468 #[test]
1469 fn http_query_string() {
1470 let request = make_protocol()
1471 .serialize_request(
1472 &QueryStruct,
1473 &QUERY_SCHEMA,
1474 "https://example.com/things",
1475 &ConfigBag::base(),
1476 )
1477 .unwrap();
1478 assert_eq!(request.uri(), "https://example.com/things?color=blue");
1479 }
1480
1481 static INT_QUERY_MEMBER: Schema =
1482 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Integer, "size", 0)
1483 .with_http_query("size");
1484
1485 static INT_QUERY_SCHEMA: Schema = Schema::new_struct(
1486 crate::shape_id!("test", "S"),
1487 ShapeType::Structure,
1488 &[&INT_QUERY_MEMBER],
1489 );
1490
1491 struct IntQueryStruct;
1492 impl SerializableStruct for IntQueryStruct {
1493 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1494 s.write_integer(&INT_QUERY_MEMBER, 42)
1495 }
1496 }
1497
1498 #[test]
1499 fn http_query_integer() {
1500 let request = make_protocol()
1501 .serialize_request(
1502 &IntQueryStruct,
1503 &INT_QUERY_SCHEMA,
1504 "https://example.com/things",
1505 &ConfigBag::base(),
1506 )
1507 .unwrap();
1508 assert_eq!(request.uri(), "https://example.com/things?size=42");
1509 }
1510
1511 static Q1: Schema =
1514 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "a", 0)
1515 .with_http_query("a");
1516 static Q2: Schema =
1517 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "b", 1)
1518 .with_http_query("b");
1519 static MULTI_QUERY_SCHEMA: Schema = Schema::new_struct(
1520 crate::shape_id!("test", "S"),
1521 ShapeType::Structure,
1522 &[&Q1, &Q2],
1523 );
1524
1525 struct MultiQueryStruct;
1526 impl SerializableStruct for MultiQueryStruct {
1527 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1528 s.write_string(&Q1, "x")?;
1529 s.write_string(&Q2, "y")
1530 }
1531 }
1532
1533 #[test]
1534 fn http_query_multiple_params() {
1535 let request = make_protocol()
1536 .serialize_request(
1537 &MultiQueryStruct,
1538 &MULTI_QUERY_SCHEMA,
1539 "https://example.com",
1540 &ConfigBag::base(),
1541 )
1542 .unwrap();
1543 assert_eq!(request.uri(), "https://example.com?a=x&b=y");
1544 }
1545
1546 #[test]
1549 fn http_query_percent_encodes_values() {
1550 struct SpaceQueryStruct;
1551 impl SerializableStruct for SpaceQueryStruct {
1552 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1553 s.write_string(&QUERY_MEMBER, "hello world")
1554 }
1555 }
1556 let request = make_protocol()
1557 .serialize_request(
1558 &SpaceQueryStruct,
1559 &QUERY_SCHEMA,
1560 "https://example.com",
1561 &ConfigBag::base(),
1562 )
1563 .unwrap();
1564 assert_eq!(request.uri(), "https://example.com?color=hello%20world");
1565 }
1566
1567 static LABEL_MEMBER: Schema = Schema::new_member(
1570 crate::shape_id!("test", "S"),
1571 ShapeType::String,
1572 "bucketName",
1573 0,
1574 )
1575 .with_http_label();
1576
1577 static LABEL_SCHEMA: Schema = Schema::new_struct(
1578 crate::shape_id!("test", "S"),
1579 ShapeType::Structure,
1580 &[&LABEL_MEMBER],
1581 );
1582
1583 struct LabelStruct;
1584 impl SerializableStruct for LabelStruct {
1585 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1586 s.write_string(&LABEL_MEMBER, "my-bucket")
1587 }
1588 }
1589
1590 #[test]
1591 fn http_label_substitution() {
1592 let request = make_protocol()
1593 .serialize_request(
1594 &LabelStruct,
1595 &LABEL_SCHEMA,
1596 "https://example.com/{bucketName}/objects",
1597 &ConfigBag::base(),
1598 )
1599 .unwrap();
1600 assert_eq!(request.uri(), "https://example.com/my-bucket/objects");
1601 }
1602
1603 #[test]
1604 fn http_label_percent_encodes() {
1605 struct SpecialLabelStruct;
1606 impl SerializableStruct for SpecialLabelStruct {
1607 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1608 s.write_string(&LABEL_MEMBER, "my bucket/name")
1609 }
1610 }
1611 let request = make_protocol()
1612 .serialize_request(
1613 &SpecialLabelStruct,
1614 &LABEL_SCHEMA,
1615 "https://example.com/{bucketName}",
1616 &ConfigBag::base(),
1617 )
1618 .unwrap();
1619 assert!(request.uri().contains("my%20bucket%2Fname"));
1620 }
1621
1622 static INT_LABEL_MEMBER: Schema = Schema::new_member(
1623 crate::shape_id!("test", "S"),
1624 ShapeType::Integer,
1625 "itemId",
1626 0,
1627 )
1628 .with_http_label();
1629
1630 static INT_LABEL_SCHEMA: Schema = Schema::new_struct(
1631 crate::shape_id!("test", "S"),
1632 ShapeType::Structure,
1633 &[&INT_LABEL_MEMBER],
1634 );
1635
1636 struct IntLabelStruct;
1637 impl SerializableStruct for IntLabelStruct {
1638 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1639 s.write_integer(&INT_LABEL_MEMBER, 123)
1640 }
1641 }
1642
1643 #[test]
1644 fn http_label_integer() {
1645 let request = make_protocol()
1646 .serialize_request(
1647 &IntLabelStruct,
1648 &INT_LABEL_SCHEMA,
1649 "https://example.com/items/{itemId}",
1650 &ConfigBag::base(),
1651 )
1652 .unwrap();
1653 assert_eq!(request.uri(), "https://example.com/items/123");
1654 }
1655
1656 static COMBINED_LABEL: Schema =
1659 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "id", 0)
1660 .with_http_label();
1661 static COMBINED_HEADER: Schema =
1662 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "token", 1)
1663 .with_http_header("X-Token");
1664 static COMBINED_QUERY: Schema = Schema::new_member(
1665 crate::shape_id!("test", "S"),
1666 ShapeType::String,
1667 "filter",
1668 2,
1669 )
1670 .with_http_query("filter");
1671 static COMBINED_BODY: Schema =
1672 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "data", 3);
1673 static COMBINED_SCHEMA: Schema = Schema::new_struct(
1674 crate::shape_id!("test", "S"),
1675 ShapeType::Structure,
1676 &[
1677 &COMBINED_LABEL,
1678 &COMBINED_HEADER,
1679 &COMBINED_QUERY,
1680 &COMBINED_BODY,
1681 ],
1682 );
1683
1684 struct CombinedStruct;
1685 impl SerializableStruct for CombinedStruct {
1686 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1687 s.write_string(&COMBINED_LABEL, "item-42")?;
1688 s.write_string(&COMBINED_HEADER, "secret")?;
1689 s.write_string(&COMBINED_QUERY, "active")?;
1690 s.write_string(&COMBINED_BODY, "payload-data")
1691 }
1692 }
1693
1694 #[test]
1695 fn combined_bindings() {
1696 let request = make_protocol()
1697 .serialize_request(
1698 &CombinedStruct,
1699 &COMBINED_SCHEMA,
1700 "https://example.com/{id}/details",
1701 &ConfigBag::base(),
1702 )
1703 .unwrap();
1704 assert_eq!(
1705 request.uri(),
1706 "https://example.com/item-42/details?filter=active"
1707 );
1708 assert_eq!(request.headers().get("X-Token").unwrap(), "secret");
1710 let body = request.body().bytes().unwrap();
1712 assert!(body
1713 .windows(b"payload-data".len())
1714 .any(|w| w == b"payload-data"));
1715 }
1716
1717 static PREFIX_MEMBER: Schema =
1720 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Map, "metadata", 0)
1721 .with_http_prefix_headers("X-Meta-");
1722
1723 static PREFIX_SCHEMA: Schema = Schema::new_struct(
1724 crate::shape_id!("test", "S"),
1725 ShapeType::Structure,
1726 &[&PREFIX_MEMBER],
1727 );
1728
1729 struct PrefixHeaderStruct;
1730 impl SerializableStruct for PrefixHeaderStruct {
1731 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1732 s.write_map(&PREFIX_MEMBER, &|s| {
1733 s.write_string(&STRING, "Color")?;
1734 s.write_string(&STRING, "red")?;
1735 s.write_string(&STRING, "Size")?;
1736 s.write_string(&STRING, "large")?;
1737 Ok(())
1738 })
1739 }
1740 }
1741
1742 #[test]
1743 fn http_prefix_headers() {
1744 let request = make_protocol()
1745 .serialize_request(
1746 &PrefixHeaderStruct,
1747 &PREFIX_SCHEMA,
1748 "https://example.com",
1749 &ConfigBag::base(),
1750 )
1751 .unwrap();
1752 assert_eq!(request.headers().get("X-Meta-Color").unwrap(), "red");
1753 assert_eq!(request.headers().get("X-Meta-Size").unwrap(), "large");
1754 }
1755
1756 static QUERY_PARAMS_MEMBER: Schema =
1759 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Map, "params", 0)
1760 .with_http_query_params();
1761
1762 static QUERY_PARAMS_SCHEMA: Schema = Schema::new_struct(
1763 crate::shape_id!("test", "S"),
1764 ShapeType::Structure,
1765 &[&QUERY_PARAMS_MEMBER],
1766 );
1767
1768 struct QueryParamsStruct;
1769 impl SerializableStruct for QueryParamsStruct {
1770 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1771 s.write_map(&QUERY_PARAMS_MEMBER, &|s| {
1772 s.write_string(&STRING, "page")?;
1773 s.write_string(&STRING, "2")?;
1774 s.write_string(&STRING, "limit")?;
1775 s.write_string(&STRING, "50")?;
1776 Ok(())
1777 })
1778 }
1779 }
1780
1781 #[test]
1782 fn http_query_params() {
1783 let request = make_protocol()
1784 .serialize_request(
1785 &QueryParamsStruct,
1786 &QUERY_PARAMS_SCHEMA,
1787 "https://example.com",
1788 &ConfigBag::base(),
1789 )
1790 .unwrap();
1791 assert_eq!(request.uri(), "https://example.com?page=2&limit=50");
1792 }
1793
1794 static TS_HEADER_MEMBER: Schema = Schema::new_member(
1797 crate::shape_id!("test", "S"),
1798 ShapeType::Timestamp,
1799 "ifModified",
1800 0,
1801 )
1802 .with_http_header("If-Modified-Since");
1803
1804 static TS_HEADER_SCHEMA: Schema = Schema::new_struct(
1805 crate::shape_id!("test", "S"),
1806 ShapeType::Structure,
1807 &[&TS_HEADER_MEMBER],
1808 );
1809
1810 struct TimestampHeaderStruct;
1811 impl SerializableStruct for TimestampHeaderStruct {
1812 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1813 s.write_timestamp(&TS_HEADER_MEMBER, &aws_smithy_types::DateTime::from_secs(0))
1814 }
1815 }
1816
1817 #[test]
1818 fn timestamp_header_uses_http_date() {
1819 let request = make_protocol()
1820 .serialize_request(
1821 &TimestampHeaderStruct,
1822 &TS_HEADER_SCHEMA,
1823 "https://example.com",
1824 &ConfigBag::base(),
1825 )
1826 .unwrap();
1827 let value = request.headers().get("If-Modified-Since").unwrap();
1828 assert!(value.contains("1970"), "expected http-date, got: {value}");
1830 }
1831
1832 static TS_QUERY_MEMBER: Schema = Schema::new_member(
1835 crate::shape_id!("test", "S"),
1836 ShapeType::Timestamp,
1837 "since",
1838 0,
1839 )
1840 .with_http_query("since");
1841
1842 static TS_QUERY_SCHEMA: Schema = Schema::new_struct(
1843 crate::shape_id!("test", "S"),
1844 ShapeType::Structure,
1845 &[&TS_QUERY_MEMBER],
1846 );
1847
1848 struct TimestampQueryStruct;
1849 impl SerializableStruct for TimestampQueryStruct {
1850 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1851 s.write_timestamp(&TS_QUERY_MEMBER, &aws_smithy_types::DateTime::from_secs(0))
1852 }
1853 }
1854
1855 #[test]
1856 fn timestamp_query_uses_date_time() {
1857 let request = make_protocol()
1858 .serialize_request(
1859 &TimestampQueryStruct,
1860 &TS_QUERY_SCHEMA,
1861 "https://example.com",
1862 &ConfigBag::base(),
1863 )
1864 .unwrap();
1865 assert_eq!(
1866 request.uri(),
1867 "https://example.com?since=1970-01-01T00%3A00%3A00Z"
1868 );
1869 }
1870
1871 static BOUND_MEMBER: Schema = Schema::new_member(
1874 crate::shape_id!("test", "S"),
1875 ShapeType::String,
1876 "headerVal",
1877 0,
1878 )
1879 .with_http_header("X-Val");
1880 static UNBOUND_MEMBER: Schema = Schema::new_member(
1881 crate::shape_id!("test", "S"),
1882 ShapeType::String,
1883 "bodyVal",
1884 1,
1885 );
1886 static MIXED_SCHEMA: Schema = Schema::new_struct(
1887 crate::shape_id!("test", "S"),
1888 ShapeType::Structure,
1889 &[&BOUND_MEMBER, &UNBOUND_MEMBER],
1890 );
1891
1892 struct MixedStruct;
1893 impl SerializableStruct for MixedStruct {
1894 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1895 s.write_string(&BOUND_MEMBER, "in-header")?;
1896 s.write_string(&UNBOUND_MEMBER, "in-body")
1897 }
1898 }
1899
1900 #[test]
1901 fn bound_members_not_in_body() {
1902 let request = make_protocol()
1903 .serialize_request(
1904 &MixedStruct,
1905 &MIXED_SCHEMA,
1906 "https://example.com",
1907 &ConfigBag::base(),
1908 )
1909 .unwrap();
1910 let body = std::str::from_utf8(request.body().bytes().unwrap()).unwrap();
1911 assert!(
1912 body.contains("in-body"),
1913 "body should contain unbound member"
1914 );
1915 assert!(
1916 !body.contains("in-header"),
1917 "body should NOT contain header-bound member"
1918 );
1919 assert_eq!(request.headers().get("X-Val").unwrap(), "in-header");
1920 }
1921}