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)]
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> ClientProtocol for HttpBindingProtocol<C>
806where
807 C: Codec + Send + Sync + std::fmt::Debug + 'static,
808 for<'a> C::Deserializer<'a>: ShapeDeserializer,
809{
810 fn protocol_id(&self) -> &ShapeId {
811 &self.protocol_id
812 }
813
814 fn serialize_request(
815 &self,
816 input: &dyn SerializableStruct,
817 input_schema: &Schema,
818 endpoint: &str,
819 _cfg: &ConfigBag,
820 ) -> Result<Request, SerdeError> {
821 let mut binder =
822 HttpBindingSerializer::new(self.codec.create_serializer(), Some(input_schema));
823
824 let has_struct_payload = input_schema.members().iter().any(|m| {
828 m.http_payload().is_some()
829 && matches!(
830 m.shape_type(),
831 crate::ShapeType::Structure | crate::ShapeType::Union
832 )
833 });
834 if has_struct_payload {
835 binder.is_top_level = false;
836 input.serialize_members(&mut binder)?;
837 } else {
838 binder.write_struct(input_schema, input)?;
839 }
840 let raw_payload = binder.raw_payload;
841 let mut body = if raw_payload.is_some() {
842 Vec::new()
844 } else {
845 binder.body.finish()
846 };
847
848 let has_blob_or_string_payload = raw_payload.is_some();
853 let has_body_members = has_struct_payload
854 || input_schema.members().iter().any(|m| {
855 m.http_header().is_none()
856 && m.http_query().is_none()
857 && m.http_label().is_none()
858 && m.http_prefix_headers().is_none()
859 && m.http_query_params().is_none()
860 && m.http_payload().is_none()
861 });
862
863 let set_content_type = if has_blob_or_string_payload {
864 false
868 } else if has_body_members {
869 true
872 } else {
873 body = Vec::new();
875 false
876 };
877
878 let mut uri = match input_schema.http() {
881 Some(h) => {
882 let mut path = h.uri().to_string();
883 for (name, value) in &binder.labels {
884 let greedy = format!("{{{name}+}}");
886 if path.contains(&greedy) {
887 let encoded = value
889 .split('/')
890 .map(|seg| percent_encode(seg))
891 .collect::<Vec<_>>()
892 .join("/");
893 path = path.replace(&greedy, &encoded);
894 } else {
895 let placeholder = format!("{{{name}}}");
896 path = path.replace(&placeholder, &percent_encode(value));
897 }
898 }
899 if endpoint.is_empty() {
900 path
901 } else {
902 format!("{}{}", endpoint, path)
903 }
904 }
905 None => {
906 let mut u = if endpoint.is_empty() {
907 "/".to_string()
908 } else {
909 endpoint.to_string()
910 };
911 for (name, value) in &binder.labels {
912 let greedy = format!("{{{name}+}}");
913 if u.contains(&greedy) {
914 let encoded = value
915 .split('/')
916 .map(|seg| percent_encode(seg))
917 .collect::<Vec<_>>()
918 .join("/");
919 u = u.replace(&greedy, &encoded);
920 } else {
921 let placeholder = format!("{{{name}}}");
922 u = u.replace(&placeholder, &percent_encode(value));
923 }
924 }
925 u
926 }
927 };
928 if !binder.query_params.is_empty() {
929 uri.push(if uri.contains('?') { '&' } else { '?' });
930 let pairs: Vec<String> = binder
931 .query_params
932 .iter()
933 .map(|(k, v)| format!("{}={}", percent_encode(k), percent_encode(v)))
934 .collect();
935 uri.push_str(&pairs.join("&"));
936 }
937
938 let mut request = if let Some(payload) = raw_payload {
939 Request::new(SdkBody::from(payload))
940 } else {
941 Request::new(SdkBody::from(body))
942 };
943 if let Some(http) = input_schema.http() {
945 request
946 .set_method(http.method())
947 .map_err(|e| SerdeError::custom(format!("invalid HTTP method: {e}")))?;
948 }
949 request
950 .set_uri(uri.as_str())
951 .map_err(|e| SerdeError::custom(format!("invalid endpoint URI: {e}")))?;
952 if set_content_type {
953 request
954 .headers_mut()
955 .insert("Content-Type", self.content_type);
956 }
957 if let Some(len) = request.body().content_length() {
958 if len > 0 || set_content_type {
959 request
960 .headers_mut()
961 .insert("Content-Length", len.to_string());
962 }
963 }
964 for (name, value) in &binder.headers {
965 request.headers_mut().insert(name.clone(), value.clone());
966 }
967 Ok(request)
968 }
969
970 fn deserialize_response<'a>(
971 &self,
972 response: &'a Response,
973 _output_schema: &Schema,
974 _cfg: &ConfigBag,
975 ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError> {
976 let body = response
977 .body()
978 .bytes()
979 .ok_or_else(|| SerdeError::custom("response body is not available as bytes"))?;
980 Ok(Box::new(self.codec.create_deserializer(body)))
981 }
982}
983
984#[cfg(test)]
985mod tests {
986 use super::*;
987 use crate::serde::SerializableStruct;
988 use crate::{prelude::*, ShapeType};
989
990 struct TestSerializer {
991 output: Vec<u8>,
992 }
993
994 impl FinishSerializer for TestSerializer {
995 fn finish(self) -> Vec<u8> {
996 self.output
997 }
998 }
999
1000 impl ShapeSerializer for TestSerializer {
1001 fn write_struct(
1002 &mut self,
1003 _: &Schema,
1004 value: &dyn SerializableStruct,
1005 ) -> Result<(), SerdeError> {
1006 self.output.push(b'{');
1007 value.serialize_members(self)?;
1008 self.output.push(b'}');
1009 Ok(())
1010 }
1011 fn write_list(
1012 &mut self,
1013 _: &Schema,
1014 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1015 ) -> Result<(), SerdeError> {
1016 Ok(())
1017 }
1018 fn write_map(
1019 &mut self,
1020 _: &Schema,
1021 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1022 ) -> Result<(), SerdeError> {
1023 Ok(())
1024 }
1025 fn write_boolean(&mut self, _: &Schema, _: bool) -> Result<(), SerdeError> {
1026 Ok(())
1027 }
1028 fn write_byte(&mut self, _: &Schema, _: i8) -> Result<(), SerdeError> {
1029 Ok(())
1030 }
1031 fn write_short(&mut self, _: &Schema, _: i16) -> Result<(), SerdeError> {
1032 Ok(())
1033 }
1034 fn write_integer(&mut self, _: &Schema, _: i32) -> Result<(), SerdeError> {
1035 Ok(())
1036 }
1037 fn write_long(&mut self, _: &Schema, _: i64) -> Result<(), SerdeError> {
1038 Ok(())
1039 }
1040 fn write_float(&mut self, _: &Schema, _: f32) -> Result<(), SerdeError> {
1041 Ok(())
1042 }
1043 fn write_double(&mut self, _: &Schema, _: f64) -> Result<(), SerdeError> {
1044 Ok(())
1045 }
1046 fn write_big_integer(
1047 &mut self,
1048 _: &Schema,
1049 _: &aws_smithy_types::BigInteger,
1050 ) -> Result<(), SerdeError> {
1051 Ok(())
1052 }
1053 fn write_big_decimal(
1054 &mut self,
1055 _: &Schema,
1056 _: &aws_smithy_types::BigDecimal,
1057 ) -> Result<(), SerdeError> {
1058 Ok(())
1059 }
1060 fn write_string(&mut self, _: &Schema, v: &str) -> Result<(), SerdeError> {
1061 self.output.extend_from_slice(v.as_bytes());
1062 Ok(())
1063 }
1064 fn write_blob(&mut self, _: &Schema, _: &aws_smithy_types::Blob) -> Result<(), SerdeError> {
1065 Ok(())
1066 }
1067 fn write_timestamp(
1068 &mut self,
1069 _: &Schema,
1070 _: &aws_smithy_types::DateTime,
1071 ) -> Result<(), SerdeError> {
1072 Ok(())
1073 }
1074 fn write_document(
1075 &mut self,
1076 _: &Schema,
1077 _: &aws_smithy_types::Document,
1078 ) -> Result<(), SerdeError> {
1079 Ok(())
1080 }
1081 fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
1082 Ok(())
1083 }
1084 }
1085
1086 struct TestDeserializer<'a> {
1087 input: &'a [u8],
1088 }
1089
1090 impl ShapeDeserializer for TestDeserializer<'_> {
1091 fn read_struct(
1092 &mut self,
1093 _: &Schema,
1094 _: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1095 ) -> Result<(), SerdeError> {
1096 Ok(())
1097 }
1098 fn read_list(
1099 &mut self,
1100 _: &Schema,
1101 _: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1102 ) -> Result<(), SerdeError> {
1103 Ok(())
1104 }
1105 fn read_map(
1106 &mut self,
1107 _: &Schema,
1108 _: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1109 ) -> Result<(), SerdeError> {
1110 Ok(())
1111 }
1112 fn read_boolean(&mut self, _: &Schema) -> Result<bool, SerdeError> {
1113 Ok(false)
1114 }
1115 fn read_byte(&mut self, _: &Schema) -> Result<i8, SerdeError> {
1116 Ok(0)
1117 }
1118 fn read_short(&mut self, _: &Schema) -> Result<i16, SerdeError> {
1119 Ok(0)
1120 }
1121 fn read_integer(&mut self, _: &Schema) -> Result<i32, SerdeError> {
1122 Ok(0)
1123 }
1124 fn read_long(&mut self, _: &Schema) -> Result<i64, SerdeError> {
1125 Ok(0)
1126 }
1127 fn read_float(&mut self, _: &Schema) -> Result<f32, SerdeError> {
1128 Ok(0.0)
1129 }
1130 fn read_double(&mut self, _: &Schema) -> Result<f64, SerdeError> {
1131 Ok(0.0)
1132 }
1133 fn read_big_integer(
1134 &mut self,
1135 _: &Schema,
1136 ) -> Result<aws_smithy_types::BigInteger, SerdeError> {
1137 use std::str::FromStr;
1138 Ok(aws_smithy_types::BigInteger::from_str("0").unwrap())
1139 }
1140 fn read_big_decimal(
1141 &mut self,
1142 _: &Schema,
1143 ) -> Result<aws_smithy_types::BigDecimal, SerdeError> {
1144 use std::str::FromStr;
1145 Ok(aws_smithy_types::BigDecimal::from_str("0").unwrap())
1146 }
1147 fn read_string(&mut self, _: &Schema) -> Result<String, SerdeError> {
1148 Ok(String::from_utf8_lossy(self.input).into_owned())
1149 }
1150 fn read_blob(&mut self, _: &Schema) -> Result<aws_smithy_types::Blob, SerdeError> {
1151 Ok(aws_smithy_types::Blob::new(vec![]))
1152 }
1153 fn read_timestamp(&mut self, _: &Schema) -> Result<aws_smithy_types::DateTime, SerdeError> {
1154 Ok(aws_smithy_types::DateTime::from_secs(0))
1155 }
1156 fn read_document(&mut self, _: &Schema) -> Result<aws_smithy_types::Document, SerdeError> {
1157 Ok(aws_smithy_types::Document::Null)
1158 }
1159 fn is_null(&self) -> bool {
1160 false
1161 }
1162 fn container_size(&self) -> Option<usize> {
1163 None
1164 }
1165 }
1166
1167 #[derive(Debug)]
1168 struct TestCodec;
1169
1170 impl Codec for TestCodec {
1171 type Serializer = TestSerializer;
1172 type Deserializer<'a> = TestDeserializer<'a>;
1173 fn create_serializer(&self) -> Self::Serializer {
1174 TestSerializer { output: Vec::new() }
1175 }
1176 fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a> {
1177 TestDeserializer { input }
1178 }
1179 }
1180
1181 static TEST_SCHEMA: Schema =
1182 Schema::new(crate::shape_id!("test", "TestStruct"), ShapeType::Structure);
1183
1184 struct EmptyStruct;
1185 impl SerializableStruct for EmptyStruct {
1186 fn serialize_members(&self, _: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1187 Ok(())
1188 }
1189 }
1190
1191 static NAME_MEMBER: Schema = Schema::new_member(
1192 crate::shape_id!("test", "TestStruct"),
1193 ShapeType::String,
1194 "name",
1195 0,
1196 );
1197 static MEMBERS: &[&Schema] = &[&NAME_MEMBER];
1198 static STRUCT_WITH_MEMBER: Schema = Schema::new_struct(
1199 crate::shape_id!("test", "TestStruct"),
1200 ShapeType::Structure,
1201 MEMBERS,
1202 );
1203
1204 struct NameStruct;
1205 impl SerializableStruct for NameStruct {
1206 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1207 s.write_string(&NAME_MEMBER, "Alice")
1208 }
1209 }
1210
1211 fn make_protocol() -> HttpBindingProtocol<TestCodec> {
1212 HttpBindingProtocol::new(
1213 crate::shape_id!("test", "proto"),
1214 TestCodec,
1215 "application/test",
1216 )
1217 }
1218
1219 #[test]
1220 fn serialize_sets_content_type() {
1221 let request = make_protocol()
1223 .serialize_request(
1224 &EmptyStruct,
1225 &STRUCT_WITH_MEMBER,
1226 "https://example.com",
1227 &ConfigBag::base(),
1228 )
1229 .unwrap();
1230 assert_eq!(
1231 request.headers().get("Content-Type").unwrap(),
1232 "application/test"
1233 );
1234 }
1235
1236 #[test]
1237 fn serialize_no_body_members_omits_content_type() {
1238 let request = make_protocol()
1240 .serialize_request(
1241 &EmptyStruct,
1242 &TEST_SCHEMA,
1243 "https://example.com",
1244 &ConfigBag::base(),
1245 )
1246 .unwrap();
1247 assert!(request.headers().get("Content-Type").is_none());
1248 }
1249
1250 #[test]
1251 fn serialize_sets_uri() {
1252 let request = make_protocol()
1253 .serialize_request(
1254 &EmptyStruct,
1255 &TEST_SCHEMA,
1256 "https://example.com/path",
1257 &ConfigBag::base(),
1258 )
1259 .unwrap();
1260 assert_eq!(request.uri(), "https://example.com/path");
1261 }
1262
1263 #[test]
1264 fn serialize_body() {
1265 let request = make_protocol()
1266 .serialize_request(
1267 &NameStruct,
1268 &STRUCT_WITH_MEMBER,
1269 "https://example.com",
1270 &ConfigBag::base(),
1271 )
1272 .unwrap();
1273 assert_eq!(request.body().bytes().unwrap(), b"{Alice}");
1274 }
1275
1276 #[test]
1277 fn deserialize_response() {
1278 let response = Response::new(
1279 200u16.try_into().unwrap(),
1280 SdkBody::from(r#"{"name":"Bob"}"#),
1281 );
1282 let mut deser = make_protocol()
1283 .deserialize_response(&response, &TEST_SCHEMA, &ConfigBag::base())
1284 .unwrap();
1285 assert_eq!(deser.read_string(&STRING).unwrap(), r#"{"name":"Bob"}"#);
1286 }
1287
1288 #[test]
1289 fn update_endpoint() {
1290 let mut request = make_protocol()
1291 .serialize_request(
1292 &EmptyStruct,
1293 &TEST_SCHEMA,
1294 "https://old.example.com",
1295 &ConfigBag::base(),
1296 )
1297 .unwrap();
1298 let endpoint = aws_smithy_types::endpoint::Endpoint::builder()
1299 .url("https://new.example.com")
1300 .build();
1301 make_protocol()
1302 .update_endpoint(&mut request, &endpoint, &ConfigBag::base())
1303 .unwrap();
1304 assert_eq!(request.uri(), "https://new.example.com/");
1305 }
1306
1307 #[test]
1308 fn protocol_id() {
1309 let protocol = HttpBindingProtocol::new(
1310 crate::shape_id!("aws.protocols", "restJson1"),
1311 TestCodec,
1312 "application/json",
1313 );
1314 assert_eq!(protocol.protocol_id().as_str(), "aws.protocols#restJson1");
1315 }
1316
1317 #[test]
1318 fn invalid_uri_returns_error() {
1319 assert!(make_protocol()
1320 .serialize_request(
1321 &EmptyStruct,
1322 &TEST_SCHEMA,
1323 "not a valid uri\n\n",
1324 &ConfigBag::base()
1325 )
1326 .is_err());
1327 }
1328
1329 static HEADER_MEMBER: Schema = Schema::new_member(
1332 crate::shape_id!("test", "S"),
1333 ShapeType::String,
1334 "xToken",
1335 0,
1336 )
1337 .with_http_header("X-Token");
1338
1339 static HEADER_SCHEMA: Schema = Schema::new_struct(
1340 crate::shape_id!("test", "S"),
1341 ShapeType::Structure,
1342 &[&HEADER_MEMBER],
1343 );
1344
1345 struct HeaderStruct;
1346 impl SerializableStruct for HeaderStruct {
1347 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1348 s.write_string(&HEADER_MEMBER, "my-token-value")
1349 }
1350 }
1351
1352 #[test]
1353 fn http_header_string() {
1354 let request = make_protocol()
1355 .serialize_request(
1356 &HeaderStruct,
1357 &HEADER_SCHEMA,
1358 "https://example.com",
1359 &ConfigBag::base(),
1360 )
1361 .unwrap();
1362 assert_eq!(request.headers().get("X-Token").unwrap(), "my-token-value");
1363 }
1364
1365 static INT_HEADER_MEMBER: Schema = Schema::new_member(
1366 crate::shape_id!("test", "S"),
1367 ShapeType::Integer,
1368 "retryCount",
1369 0,
1370 )
1371 .with_http_header("X-Retry-Count");
1372
1373 static INT_HEADER_SCHEMA: Schema = Schema::new_struct(
1374 crate::shape_id!("test", "S"),
1375 ShapeType::Structure,
1376 &[&INT_HEADER_MEMBER],
1377 );
1378
1379 struct IntHeaderStruct;
1380 impl SerializableStruct for IntHeaderStruct {
1381 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1382 s.write_integer(&INT_HEADER_MEMBER, 3)
1383 }
1384 }
1385
1386 #[test]
1387 fn http_header_integer() {
1388 let request = make_protocol()
1389 .serialize_request(
1390 &IntHeaderStruct,
1391 &INT_HEADER_SCHEMA,
1392 "https://example.com",
1393 &ConfigBag::base(),
1394 )
1395 .unwrap();
1396 assert_eq!(request.headers().get("X-Retry-Count").unwrap(), "3");
1397 }
1398
1399 static BOOL_HEADER_MEMBER: Schema = Schema::new_member(
1400 crate::shape_id!("test", "S"),
1401 ShapeType::Boolean,
1402 "verbose",
1403 0,
1404 )
1405 .with_http_header("X-Verbose");
1406
1407 static BOOL_HEADER_SCHEMA: Schema = Schema::new_struct(
1408 crate::shape_id!("test", "S"),
1409 ShapeType::Structure,
1410 &[&BOOL_HEADER_MEMBER],
1411 );
1412
1413 struct BoolHeaderStruct;
1414 impl SerializableStruct for BoolHeaderStruct {
1415 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1416 s.write_boolean(&BOOL_HEADER_MEMBER, true)
1417 }
1418 }
1419
1420 #[test]
1421 fn http_header_boolean() {
1422 let request = make_protocol()
1423 .serialize_request(
1424 &BoolHeaderStruct,
1425 &BOOL_HEADER_SCHEMA,
1426 "https://example.com",
1427 &ConfigBag::base(),
1428 )
1429 .unwrap();
1430 assert_eq!(request.headers().get("X-Verbose").unwrap(), "true");
1431 }
1432
1433 static QUERY_MEMBER: Schema =
1436 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "color", 0)
1437 .with_http_query("color");
1438
1439 static QUERY_SCHEMA: Schema = Schema::new_struct(
1440 crate::shape_id!("test", "S"),
1441 ShapeType::Structure,
1442 &[&QUERY_MEMBER],
1443 );
1444
1445 struct QueryStruct;
1446 impl SerializableStruct for QueryStruct {
1447 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1448 s.write_string(&QUERY_MEMBER, "blue")
1449 }
1450 }
1451
1452 #[test]
1453 fn http_query_string() {
1454 let request = make_protocol()
1455 .serialize_request(
1456 &QueryStruct,
1457 &QUERY_SCHEMA,
1458 "https://example.com/things",
1459 &ConfigBag::base(),
1460 )
1461 .unwrap();
1462 assert_eq!(request.uri(), "https://example.com/things?color=blue");
1463 }
1464
1465 static INT_QUERY_MEMBER: Schema =
1466 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Integer, "size", 0)
1467 .with_http_query("size");
1468
1469 static INT_QUERY_SCHEMA: Schema = Schema::new_struct(
1470 crate::shape_id!("test", "S"),
1471 ShapeType::Structure,
1472 &[&INT_QUERY_MEMBER],
1473 );
1474
1475 struct IntQueryStruct;
1476 impl SerializableStruct for IntQueryStruct {
1477 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1478 s.write_integer(&INT_QUERY_MEMBER, 42)
1479 }
1480 }
1481
1482 #[test]
1483 fn http_query_integer() {
1484 let request = make_protocol()
1485 .serialize_request(
1486 &IntQueryStruct,
1487 &INT_QUERY_SCHEMA,
1488 "https://example.com/things",
1489 &ConfigBag::base(),
1490 )
1491 .unwrap();
1492 assert_eq!(request.uri(), "https://example.com/things?size=42");
1493 }
1494
1495 static Q1: Schema =
1498 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "a", 0)
1499 .with_http_query("a");
1500 static Q2: Schema =
1501 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "b", 1)
1502 .with_http_query("b");
1503 static MULTI_QUERY_SCHEMA: Schema = Schema::new_struct(
1504 crate::shape_id!("test", "S"),
1505 ShapeType::Structure,
1506 &[&Q1, &Q2],
1507 );
1508
1509 struct MultiQueryStruct;
1510 impl SerializableStruct for MultiQueryStruct {
1511 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1512 s.write_string(&Q1, "x")?;
1513 s.write_string(&Q2, "y")
1514 }
1515 }
1516
1517 #[test]
1518 fn http_query_multiple_params() {
1519 let request = make_protocol()
1520 .serialize_request(
1521 &MultiQueryStruct,
1522 &MULTI_QUERY_SCHEMA,
1523 "https://example.com",
1524 &ConfigBag::base(),
1525 )
1526 .unwrap();
1527 assert_eq!(request.uri(), "https://example.com?a=x&b=y");
1528 }
1529
1530 #[test]
1533 fn http_query_percent_encodes_values() {
1534 struct SpaceQueryStruct;
1535 impl SerializableStruct for SpaceQueryStruct {
1536 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1537 s.write_string(&QUERY_MEMBER, "hello world")
1538 }
1539 }
1540 let request = make_protocol()
1541 .serialize_request(
1542 &SpaceQueryStruct,
1543 &QUERY_SCHEMA,
1544 "https://example.com",
1545 &ConfigBag::base(),
1546 )
1547 .unwrap();
1548 assert_eq!(request.uri(), "https://example.com?color=hello%20world");
1549 }
1550
1551 static LABEL_MEMBER: Schema = Schema::new_member(
1554 crate::shape_id!("test", "S"),
1555 ShapeType::String,
1556 "bucketName",
1557 0,
1558 )
1559 .with_http_label();
1560
1561 static LABEL_SCHEMA: Schema = Schema::new_struct(
1562 crate::shape_id!("test", "S"),
1563 ShapeType::Structure,
1564 &[&LABEL_MEMBER],
1565 );
1566
1567 struct LabelStruct;
1568 impl SerializableStruct for LabelStruct {
1569 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1570 s.write_string(&LABEL_MEMBER, "my-bucket")
1571 }
1572 }
1573
1574 #[test]
1575 fn http_label_substitution() {
1576 let request = make_protocol()
1577 .serialize_request(
1578 &LabelStruct,
1579 &LABEL_SCHEMA,
1580 "https://example.com/{bucketName}/objects",
1581 &ConfigBag::base(),
1582 )
1583 .unwrap();
1584 assert_eq!(request.uri(), "https://example.com/my-bucket/objects");
1585 }
1586
1587 #[test]
1588 fn http_label_percent_encodes() {
1589 struct SpecialLabelStruct;
1590 impl SerializableStruct for SpecialLabelStruct {
1591 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1592 s.write_string(&LABEL_MEMBER, "my bucket/name")
1593 }
1594 }
1595 let request = make_protocol()
1596 .serialize_request(
1597 &SpecialLabelStruct,
1598 &LABEL_SCHEMA,
1599 "https://example.com/{bucketName}",
1600 &ConfigBag::base(),
1601 )
1602 .unwrap();
1603 assert!(request.uri().contains("my%20bucket%2Fname"));
1604 }
1605
1606 static INT_LABEL_MEMBER: Schema = Schema::new_member(
1607 crate::shape_id!("test", "S"),
1608 ShapeType::Integer,
1609 "itemId",
1610 0,
1611 )
1612 .with_http_label();
1613
1614 static INT_LABEL_SCHEMA: Schema = Schema::new_struct(
1615 crate::shape_id!("test", "S"),
1616 ShapeType::Structure,
1617 &[&INT_LABEL_MEMBER],
1618 );
1619
1620 struct IntLabelStruct;
1621 impl SerializableStruct for IntLabelStruct {
1622 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1623 s.write_integer(&INT_LABEL_MEMBER, 123)
1624 }
1625 }
1626
1627 #[test]
1628 fn http_label_integer() {
1629 let request = make_protocol()
1630 .serialize_request(
1631 &IntLabelStruct,
1632 &INT_LABEL_SCHEMA,
1633 "https://example.com/items/{itemId}",
1634 &ConfigBag::base(),
1635 )
1636 .unwrap();
1637 assert_eq!(request.uri(), "https://example.com/items/123");
1638 }
1639
1640 static COMBINED_LABEL: Schema =
1643 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "id", 0)
1644 .with_http_label();
1645 static COMBINED_HEADER: Schema =
1646 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "token", 1)
1647 .with_http_header("X-Token");
1648 static COMBINED_QUERY: Schema = Schema::new_member(
1649 crate::shape_id!("test", "S"),
1650 ShapeType::String,
1651 "filter",
1652 2,
1653 )
1654 .with_http_query("filter");
1655 static COMBINED_BODY: Schema =
1656 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "data", 3);
1657 static COMBINED_SCHEMA: Schema = Schema::new_struct(
1658 crate::shape_id!("test", "S"),
1659 ShapeType::Structure,
1660 &[
1661 &COMBINED_LABEL,
1662 &COMBINED_HEADER,
1663 &COMBINED_QUERY,
1664 &COMBINED_BODY,
1665 ],
1666 );
1667
1668 struct CombinedStruct;
1669 impl SerializableStruct for CombinedStruct {
1670 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1671 s.write_string(&COMBINED_LABEL, "item-42")?;
1672 s.write_string(&COMBINED_HEADER, "secret")?;
1673 s.write_string(&COMBINED_QUERY, "active")?;
1674 s.write_string(&COMBINED_BODY, "payload-data")
1675 }
1676 }
1677
1678 #[test]
1679 fn combined_bindings() {
1680 let request = make_protocol()
1681 .serialize_request(
1682 &CombinedStruct,
1683 &COMBINED_SCHEMA,
1684 "https://example.com/{id}/details",
1685 &ConfigBag::base(),
1686 )
1687 .unwrap();
1688 assert_eq!(
1689 request.uri(),
1690 "https://example.com/item-42/details?filter=active"
1691 );
1692 assert_eq!(request.headers().get("X-Token").unwrap(), "secret");
1694 let body = request.body().bytes().unwrap();
1696 assert!(body
1697 .windows(b"payload-data".len())
1698 .any(|w| w == b"payload-data"));
1699 }
1700
1701 static PREFIX_MEMBER: Schema =
1704 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Map, "metadata", 0)
1705 .with_http_prefix_headers("X-Meta-");
1706
1707 static PREFIX_SCHEMA: Schema = Schema::new_struct(
1708 crate::shape_id!("test", "S"),
1709 ShapeType::Structure,
1710 &[&PREFIX_MEMBER],
1711 );
1712
1713 struct PrefixHeaderStruct;
1714 impl SerializableStruct for PrefixHeaderStruct {
1715 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1716 s.write_map(&PREFIX_MEMBER, &|s| {
1717 s.write_string(&STRING, "Color")?;
1718 s.write_string(&STRING, "red")?;
1719 s.write_string(&STRING, "Size")?;
1720 s.write_string(&STRING, "large")?;
1721 Ok(())
1722 })
1723 }
1724 }
1725
1726 #[test]
1727 fn http_prefix_headers() {
1728 let request = make_protocol()
1729 .serialize_request(
1730 &PrefixHeaderStruct,
1731 &PREFIX_SCHEMA,
1732 "https://example.com",
1733 &ConfigBag::base(),
1734 )
1735 .unwrap();
1736 assert_eq!(request.headers().get("X-Meta-Color").unwrap(), "red");
1737 assert_eq!(request.headers().get("X-Meta-Size").unwrap(), "large");
1738 }
1739
1740 static QUERY_PARAMS_MEMBER: Schema =
1743 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Map, "params", 0)
1744 .with_http_query_params();
1745
1746 static QUERY_PARAMS_SCHEMA: Schema = Schema::new_struct(
1747 crate::shape_id!("test", "S"),
1748 ShapeType::Structure,
1749 &[&QUERY_PARAMS_MEMBER],
1750 );
1751
1752 struct QueryParamsStruct;
1753 impl SerializableStruct for QueryParamsStruct {
1754 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1755 s.write_map(&QUERY_PARAMS_MEMBER, &|s| {
1756 s.write_string(&STRING, "page")?;
1757 s.write_string(&STRING, "2")?;
1758 s.write_string(&STRING, "limit")?;
1759 s.write_string(&STRING, "50")?;
1760 Ok(())
1761 })
1762 }
1763 }
1764
1765 #[test]
1766 fn http_query_params() {
1767 let request = make_protocol()
1768 .serialize_request(
1769 &QueryParamsStruct,
1770 &QUERY_PARAMS_SCHEMA,
1771 "https://example.com",
1772 &ConfigBag::base(),
1773 )
1774 .unwrap();
1775 assert_eq!(request.uri(), "https://example.com?page=2&limit=50");
1776 }
1777
1778 static TS_HEADER_MEMBER: Schema = Schema::new_member(
1781 crate::shape_id!("test", "S"),
1782 ShapeType::Timestamp,
1783 "ifModified",
1784 0,
1785 )
1786 .with_http_header("If-Modified-Since");
1787
1788 static TS_HEADER_SCHEMA: Schema = Schema::new_struct(
1789 crate::shape_id!("test", "S"),
1790 ShapeType::Structure,
1791 &[&TS_HEADER_MEMBER],
1792 );
1793
1794 struct TimestampHeaderStruct;
1795 impl SerializableStruct for TimestampHeaderStruct {
1796 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1797 s.write_timestamp(&TS_HEADER_MEMBER, &aws_smithy_types::DateTime::from_secs(0))
1798 }
1799 }
1800
1801 #[test]
1802 fn timestamp_header_uses_http_date() {
1803 let request = make_protocol()
1804 .serialize_request(
1805 &TimestampHeaderStruct,
1806 &TS_HEADER_SCHEMA,
1807 "https://example.com",
1808 &ConfigBag::base(),
1809 )
1810 .unwrap();
1811 let value = request.headers().get("If-Modified-Since").unwrap();
1812 assert!(value.contains("1970"), "expected http-date, got: {value}");
1814 }
1815
1816 static TS_QUERY_MEMBER: Schema = Schema::new_member(
1819 crate::shape_id!("test", "S"),
1820 ShapeType::Timestamp,
1821 "since",
1822 0,
1823 )
1824 .with_http_query("since");
1825
1826 static TS_QUERY_SCHEMA: Schema = Schema::new_struct(
1827 crate::shape_id!("test", "S"),
1828 ShapeType::Structure,
1829 &[&TS_QUERY_MEMBER],
1830 );
1831
1832 struct TimestampQueryStruct;
1833 impl SerializableStruct for TimestampQueryStruct {
1834 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1835 s.write_timestamp(&TS_QUERY_MEMBER, &aws_smithy_types::DateTime::from_secs(0))
1836 }
1837 }
1838
1839 #[test]
1840 fn timestamp_query_uses_date_time() {
1841 let request = make_protocol()
1842 .serialize_request(
1843 &TimestampQueryStruct,
1844 &TS_QUERY_SCHEMA,
1845 "https://example.com",
1846 &ConfigBag::base(),
1847 )
1848 .unwrap();
1849 assert_eq!(
1850 request.uri(),
1851 "https://example.com?since=1970-01-01T00%3A00%3A00Z"
1852 );
1853 }
1854
1855 static BOUND_MEMBER: Schema = Schema::new_member(
1858 crate::shape_id!("test", "S"),
1859 ShapeType::String,
1860 "headerVal",
1861 0,
1862 )
1863 .with_http_header("X-Val");
1864 static UNBOUND_MEMBER: Schema = Schema::new_member(
1865 crate::shape_id!("test", "S"),
1866 ShapeType::String,
1867 "bodyVal",
1868 1,
1869 );
1870 static MIXED_SCHEMA: Schema = Schema::new_struct(
1871 crate::shape_id!("test", "S"),
1872 ShapeType::Structure,
1873 &[&BOUND_MEMBER, &UNBOUND_MEMBER],
1874 );
1875
1876 struct MixedStruct;
1877 impl SerializableStruct for MixedStruct {
1878 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1879 s.write_string(&BOUND_MEMBER, "in-header")?;
1880 s.write_string(&UNBOUND_MEMBER, "in-body")
1881 }
1882 }
1883
1884 #[test]
1885 fn bound_members_not_in_body() {
1886 let request = make_protocol()
1887 .serialize_request(
1888 &MixedStruct,
1889 &MIXED_SCHEMA,
1890 "https://example.com",
1891 &ConfigBag::base(),
1892 )
1893 .unwrap();
1894 let body = std::str::from_utf8(request.body().bytes().unwrap()).unwrap();
1895 assert!(
1896 body.contains("in-body"),
1897 "body should contain unbound member"
1898 );
1899 assert!(
1900 !body.contains("in-header"),
1901 "body should NOT contain header-bound member"
1902 );
1903 assert_eq!(request.headers().get("X-Val").unwrap(), "in-header");
1904 }
1905}