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
502struct ListElementCollector {
504 values: Vec<String>,
505 quotable: Vec<bool>,
507 is_header: bool,
510}
511
512impl ListElementCollector {
513 fn for_header() -> Self {
514 Self::new(true)
515 }
516
517 fn for_query() -> Self {
518 Self::new(false)
519 }
520
521 fn new(is_header: bool) -> Self {
522 Self {
523 values: Vec::new(),
524 quotable: Vec::new(),
525 is_header,
526 }
527 }
528
529 fn push(&mut self, value: String) {
530 self.quotable.push(true);
531 self.values.push(value);
532 }
533
534 fn push_unquotable(&mut self, value: String) {
535 self.quotable.push(false);
536 self.values.push(value);
537 }
538}
539
540impl ShapeSerializer for ListElementCollector {
541 fn write_string(&mut self, _schema: &Schema, value: &str) -> Result<(), SerdeError> {
542 self.push(value.to_string());
543 Ok(())
544 }
545 fn write_boolean(&mut self, _: &Schema, value: bool) -> Result<(), SerdeError> {
546 self.push(value.to_string());
547 Ok(())
548 }
549 fn write_byte(&mut self, _: &Schema, value: i8) -> Result<(), SerdeError> {
550 self.push(value.to_string());
551 Ok(())
552 }
553 fn write_short(&mut self, _: &Schema, value: i16) -> Result<(), SerdeError> {
554 self.push(value.to_string());
555 Ok(())
556 }
557 fn write_integer(&mut self, _: &Schema, value: i32) -> Result<(), SerdeError> {
558 self.push(value.to_string());
559 Ok(())
560 }
561 fn write_long(&mut self, _: &Schema, value: i64) -> Result<(), SerdeError> {
562 self.push(value.to_string());
563 Ok(())
564 }
565 fn write_float(&mut self, _: &Schema, value: f32) -> Result<(), SerdeError> {
566 self.push(format_float_f32(value));
567 Ok(())
568 }
569 fn write_double(&mut self, _: &Schema, value: f64) -> Result<(), SerdeError> {
570 self.push(format_float_f64(value));
571 Ok(())
572 }
573 fn write_timestamp(
574 &mut self,
575 schema: &Schema,
576 value: &aws_smithy_types::DateTime,
577 ) -> Result<(), SerdeError> {
578 let format = match schema.timestamp_format() {
579 Some(ts) => match ts.format() {
580 crate::traits::TimestampFormat::EpochSeconds => {
581 aws_smithy_types::date_time::Format::EpochSeconds
582 }
583 crate::traits::TimestampFormat::HttpDate => {
584 aws_smithy_types::date_time::Format::HttpDate
585 }
586 crate::traits::TimestampFormat::DateTime => {
587 aws_smithy_types::date_time::Format::DateTime
588 }
589 },
590 None => {
592 if self.is_header {
593 aws_smithy_types::date_time::Format::HttpDate
594 } else {
595 aws_smithy_types::date_time::Format::DateTime
596 }
597 }
598 };
599 self.push_unquotable(
600 value
601 .fmt(format)
602 .map_err(|e| SerdeError::custom(format!("failed to format timestamp: {e}")))?,
603 );
604 Ok(())
605 }
606 fn write_blob(
607 &mut self,
608 _schema: &Schema,
609 value: &aws_smithy_types::Blob,
610 ) -> Result<(), SerdeError> {
611 self.push(aws_smithy_types::base64::encode(value.as_ref()));
612 Ok(())
613 }
614 fn write_struct(&mut self, _: &Schema, _: &dyn SerializableStruct) -> Result<(), SerdeError> {
616 Ok(())
617 }
618 fn write_list(
619 &mut self,
620 _: &Schema,
621 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
622 ) -> Result<(), SerdeError> {
623 Ok(())
624 }
625 fn write_map(
626 &mut self,
627 _: &Schema,
628 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
629 ) -> Result<(), SerdeError> {
630 Ok(())
631 }
632 fn write_big_integer(
633 &mut self,
634 _: &Schema,
635 _: &aws_smithy_types::BigInteger,
636 ) -> Result<(), SerdeError> {
637 Ok(())
638 }
639 fn write_big_decimal(
640 &mut self,
641 _: &Schema,
642 _: &aws_smithy_types::BigDecimal,
643 ) -> Result<(), SerdeError> {
644 Ok(())
645 }
646 fn write_document(
647 &mut self,
648 _: &Schema,
649 _: &aws_smithy_types::Document,
650 ) -> Result<(), SerdeError> {
651 Ok(())
652 }
653 fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
654 Ok(())
655 }
656}
657
658fn format_float_f32(value: f32) -> String {
661 if value.is_infinite() {
662 if value.is_sign_positive() {
663 "Infinity".to_string()
664 } else {
665 "-Infinity".to_string()
666 }
667 } else if value.is_nan() {
668 "NaN".to_string()
669 } else {
670 value.to_string()
671 }
672}
673
674fn format_float_f64(value: f64) -> String {
675 if value.is_infinite() {
676 if value.is_sign_positive() {
677 "Infinity".to_string()
678 } else {
679 "-Infinity".to_string()
680 }
681 } else if value.is_nan() {
682 "NaN".to_string()
683 } else {
684 value.to_string()
685 }
686}
687
688struct MapEntryCollector {
691 prefix: String,
692 entries: Vec<(String, String)>,
693 pending_key: Option<String>,
694}
695
696impl MapEntryCollector {
697 fn new(prefix: String) -> Self {
698 Self {
699 prefix,
700 entries: Vec::new(),
701 pending_key: None,
702 }
703 }
704}
705
706impl ShapeSerializer for MapEntryCollector {
707 fn write_string(&mut self, _schema: &Schema, value: &str) -> Result<(), SerdeError> {
708 if let Some(key) = self.pending_key.take() {
709 self.entries
710 .push((format!("{}{}", self.prefix, key), value.to_string()));
711 } else {
712 self.pending_key = Some(value.to_string());
713 }
714 Ok(())
715 }
716
717 fn write_struct(&mut self, _: &Schema, _: &dyn SerializableStruct) -> Result<(), SerdeError> {
720 Ok(())
721 }
722 fn write_list(
723 &mut self,
724 _: &Schema,
725 write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
726 ) -> Result<(), SerdeError> {
727 if let Some(key) = self.pending_key.take() {
730 let mut collector = ListElementCollector::for_query(); write_elements(&mut collector)?;
732 for val in collector.values {
733 self.entries.push((format!("{}{}", self.prefix, key), val));
734 }
735 }
736 Ok(())
737 }
738 fn write_map(
739 &mut self,
740 _: &Schema,
741 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
742 ) -> Result<(), SerdeError> {
743 Ok(())
744 }
745 fn write_boolean(&mut self, _: &Schema, _: bool) -> Result<(), SerdeError> {
746 Ok(())
747 }
748 fn write_byte(&mut self, _: &Schema, _: i8) -> Result<(), SerdeError> {
749 Ok(())
750 }
751 fn write_short(&mut self, _: &Schema, _: i16) -> Result<(), SerdeError> {
752 Ok(())
753 }
754 fn write_integer(&mut self, _: &Schema, _: i32) -> Result<(), SerdeError> {
755 Ok(())
756 }
757 fn write_long(&mut self, _: &Schema, _: i64) -> Result<(), SerdeError> {
758 Ok(())
759 }
760 fn write_float(&mut self, _: &Schema, _: f32) -> Result<(), SerdeError> {
761 Ok(())
762 }
763 fn write_double(&mut self, _: &Schema, _: f64) -> Result<(), SerdeError> {
764 Ok(())
765 }
766 fn write_big_integer(
767 &mut self,
768 _: &Schema,
769 _: &aws_smithy_types::BigInteger,
770 ) -> Result<(), SerdeError> {
771 Ok(())
772 }
773 fn write_big_decimal(
774 &mut self,
775 _: &Schema,
776 _: &aws_smithy_types::BigDecimal,
777 ) -> Result<(), SerdeError> {
778 Ok(())
779 }
780 fn write_blob(&mut self, _: &Schema, _: &aws_smithy_types::Blob) -> Result<(), SerdeError> {
781 Ok(())
782 }
783 fn write_timestamp(
784 &mut self,
785 _: &Schema,
786 _: &aws_smithy_types::DateTime,
787 ) -> Result<(), SerdeError> {
788 Ok(())
789 }
790 fn write_document(
791 &mut self,
792 _: &Schema,
793 _: &aws_smithy_types::Document,
794 ) -> Result<(), SerdeError> {
795 Ok(())
796 }
797 fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
798 Ok(())
799 }
800}
801
802impl<C> ClientProtocol for HttpBindingProtocol<C>
803where
804 C: Codec + Send + Sync + std::fmt::Debug + 'static,
805 for<'a> C::Deserializer<'a>: ShapeDeserializer,
806{
807 fn protocol_id(&self) -> &ShapeId {
808 &self.protocol_id
809 }
810
811 fn serialize_request(
812 &self,
813 input: &dyn SerializableStruct,
814 input_schema: &Schema,
815 endpoint: &str,
816 _cfg: &ConfigBag,
817 ) -> Result<Request, SerdeError> {
818 let mut binder =
819 HttpBindingSerializer::new(self.codec.create_serializer(), Some(input_schema));
820
821 let has_struct_payload = input_schema.members().iter().any(|m| {
825 m.http_payload().is_some()
826 && matches!(
827 m.shape_type(),
828 crate::ShapeType::Structure | crate::ShapeType::Union
829 )
830 });
831 if has_struct_payload {
832 binder.is_top_level = false;
833 input.serialize_members(&mut binder)?;
834 } else {
835 binder.write_struct(input_schema, input)?;
836 }
837 let raw_payload = binder.raw_payload;
838 let mut body = if raw_payload.is_some() {
839 Vec::new()
841 } else {
842 binder.body.finish()
843 };
844
845 let has_blob_or_string_payload = raw_payload.is_some();
850 let has_body_members = has_struct_payload
851 || input_schema.members().iter().any(|m| {
852 m.http_header().is_none()
853 && m.http_query().is_none()
854 && m.http_label().is_none()
855 && m.http_prefix_headers().is_none()
856 && m.http_query_params().is_none()
857 && m.http_payload().is_none()
858 });
859
860 let set_content_type = if has_blob_or_string_payload {
861 false
865 } else if has_body_members {
866 true
869 } else {
870 body = Vec::new();
872 false
873 };
874
875 let mut uri = match input_schema.http() {
878 Some(h) => {
879 let mut path = h.uri().to_string();
880 for (name, value) in &binder.labels {
881 let greedy = format!("{{{name}+}}");
883 if path.contains(&greedy) {
884 let encoded = value
886 .split('/')
887 .map(|seg| percent_encode(seg))
888 .collect::<Vec<_>>()
889 .join("/");
890 path = path.replace(&greedy, &encoded);
891 } else {
892 let placeholder = format!("{{{name}}}");
893 path = path.replace(&placeholder, &percent_encode(value));
894 }
895 }
896 if endpoint.is_empty() {
897 path
898 } else {
899 format!("{}{}", endpoint, path)
900 }
901 }
902 None => {
903 let mut u = if endpoint.is_empty() {
904 "/".to_string()
905 } else {
906 endpoint.to_string()
907 };
908 for (name, value) in &binder.labels {
909 let greedy = format!("{{{name}+}}");
910 if u.contains(&greedy) {
911 let encoded = value
912 .split('/')
913 .map(|seg| percent_encode(seg))
914 .collect::<Vec<_>>()
915 .join("/");
916 u = u.replace(&greedy, &encoded);
917 } else {
918 let placeholder = format!("{{{name}}}");
919 u = u.replace(&placeholder, &percent_encode(value));
920 }
921 }
922 u
923 }
924 };
925 if !binder.query_params.is_empty() {
926 uri.push(if uri.contains('?') { '&' } else { '?' });
927 let pairs: Vec<String> = binder
928 .query_params
929 .iter()
930 .map(|(k, v)| format!("{}={}", percent_encode(k), percent_encode(v)))
931 .collect();
932 uri.push_str(&pairs.join("&"));
933 }
934
935 let mut request = if let Some(payload) = raw_payload {
936 Request::new(SdkBody::from(payload))
937 } else {
938 Request::new(SdkBody::from(body))
939 };
940 if let Some(http) = input_schema.http() {
942 request
943 .set_method(http.method())
944 .map_err(|e| SerdeError::custom(format!("invalid HTTP method: {e}")))?;
945 }
946 request
947 .set_uri(uri.as_str())
948 .map_err(|e| SerdeError::custom(format!("invalid endpoint URI: {e}")))?;
949 if set_content_type {
950 request
951 .headers_mut()
952 .insert("Content-Type", self.content_type);
953 }
954 if let Some(len) = request.body().content_length() {
955 if len > 0 || set_content_type {
956 request
957 .headers_mut()
958 .insert("Content-Length", len.to_string());
959 }
960 }
961 for (name, value) in &binder.headers {
962 request.headers_mut().insert(name.clone(), value.clone());
963 }
964 Ok(request)
965 }
966
967 fn deserialize_response<'a>(
968 &self,
969 response: &'a Response,
970 _output_schema: &Schema,
971 _cfg: &ConfigBag,
972 ) -> Result<Box<dyn ShapeDeserializer + 'a>, SerdeError> {
973 let body = response
974 .body()
975 .bytes()
976 .ok_or_else(|| SerdeError::custom("response body is not available as bytes"))?;
977 Ok(Box::new(self.codec.create_deserializer(body)))
978 }
979}
980
981#[cfg(test)]
982mod tests {
983 use super::*;
984 use crate::serde::SerializableStruct;
985 use crate::{prelude::*, ShapeType};
986
987 struct TestSerializer {
988 output: Vec<u8>,
989 }
990
991 impl FinishSerializer for TestSerializer {
992 fn finish(self) -> Vec<u8> {
993 self.output
994 }
995 }
996
997 impl ShapeSerializer for TestSerializer {
998 fn write_struct(
999 &mut self,
1000 _: &Schema,
1001 value: &dyn SerializableStruct,
1002 ) -> Result<(), SerdeError> {
1003 self.output.push(b'{');
1004 value.serialize_members(self)?;
1005 self.output.push(b'}');
1006 Ok(())
1007 }
1008 fn write_list(
1009 &mut self,
1010 _: &Schema,
1011 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1012 ) -> Result<(), SerdeError> {
1013 Ok(())
1014 }
1015 fn write_map(
1016 &mut self,
1017 _: &Schema,
1018 _: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1019 ) -> Result<(), SerdeError> {
1020 Ok(())
1021 }
1022 fn write_boolean(&mut self, _: &Schema, _: bool) -> Result<(), SerdeError> {
1023 Ok(())
1024 }
1025 fn write_byte(&mut self, _: &Schema, _: i8) -> Result<(), SerdeError> {
1026 Ok(())
1027 }
1028 fn write_short(&mut self, _: &Schema, _: i16) -> Result<(), SerdeError> {
1029 Ok(())
1030 }
1031 fn write_integer(&mut self, _: &Schema, _: i32) -> Result<(), SerdeError> {
1032 Ok(())
1033 }
1034 fn write_long(&mut self, _: &Schema, _: i64) -> Result<(), SerdeError> {
1035 Ok(())
1036 }
1037 fn write_float(&mut self, _: &Schema, _: f32) -> Result<(), SerdeError> {
1038 Ok(())
1039 }
1040 fn write_double(&mut self, _: &Schema, _: f64) -> Result<(), SerdeError> {
1041 Ok(())
1042 }
1043 fn write_big_integer(
1044 &mut self,
1045 _: &Schema,
1046 _: &aws_smithy_types::BigInteger,
1047 ) -> Result<(), SerdeError> {
1048 Ok(())
1049 }
1050 fn write_big_decimal(
1051 &mut self,
1052 _: &Schema,
1053 _: &aws_smithy_types::BigDecimal,
1054 ) -> Result<(), SerdeError> {
1055 Ok(())
1056 }
1057 fn write_string(&mut self, _: &Schema, v: &str) -> Result<(), SerdeError> {
1058 self.output.extend_from_slice(v.as_bytes());
1059 Ok(())
1060 }
1061 fn write_blob(&mut self, _: &Schema, _: &aws_smithy_types::Blob) -> Result<(), SerdeError> {
1062 Ok(())
1063 }
1064 fn write_timestamp(
1065 &mut self,
1066 _: &Schema,
1067 _: &aws_smithy_types::DateTime,
1068 ) -> Result<(), SerdeError> {
1069 Ok(())
1070 }
1071 fn write_document(
1072 &mut self,
1073 _: &Schema,
1074 _: &aws_smithy_types::Document,
1075 ) -> Result<(), SerdeError> {
1076 Ok(())
1077 }
1078 fn write_null(&mut self, _: &Schema) -> Result<(), SerdeError> {
1079 Ok(())
1080 }
1081 }
1082
1083 struct TestDeserializer<'a> {
1084 input: &'a [u8],
1085 }
1086
1087 impl ShapeDeserializer for TestDeserializer<'_> {
1088 fn read_struct(
1089 &mut self,
1090 _: &Schema,
1091 _: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1092 ) -> Result<(), SerdeError> {
1093 Ok(())
1094 }
1095 fn read_list(
1096 &mut self,
1097 _: &Schema,
1098 _: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1099 ) -> Result<(), SerdeError> {
1100 Ok(())
1101 }
1102 fn read_map(
1103 &mut self,
1104 _: &Schema,
1105 _: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1106 ) -> Result<(), SerdeError> {
1107 Ok(())
1108 }
1109 fn read_boolean(&mut self, _: &Schema) -> Result<bool, SerdeError> {
1110 Ok(false)
1111 }
1112 fn read_byte(&mut self, _: &Schema) -> Result<i8, SerdeError> {
1113 Ok(0)
1114 }
1115 fn read_short(&mut self, _: &Schema) -> Result<i16, SerdeError> {
1116 Ok(0)
1117 }
1118 fn read_integer(&mut self, _: &Schema) -> Result<i32, SerdeError> {
1119 Ok(0)
1120 }
1121 fn read_long(&mut self, _: &Schema) -> Result<i64, SerdeError> {
1122 Ok(0)
1123 }
1124 fn read_float(&mut self, _: &Schema) -> Result<f32, SerdeError> {
1125 Ok(0.0)
1126 }
1127 fn read_double(&mut self, _: &Schema) -> Result<f64, SerdeError> {
1128 Ok(0.0)
1129 }
1130 fn read_big_integer(
1131 &mut self,
1132 _: &Schema,
1133 ) -> Result<aws_smithy_types::BigInteger, SerdeError> {
1134 use std::str::FromStr;
1135 Ok(aws_smithy_types::BigInteger::from_str("0").unwrap())
1136 }
1137 fn read_big_decimal(
1138 &mut self,
1139 _: &Schema,
1140 ) -> Result<aws_smithy_types::BigDecimal, SerdeError> {
1141 use std::str::FromStr;
1142 Ok(aws_smithy_types::BigDecimal::from_str("0").unwrap())
1143 }
1144 fn read_string(&mut self, _: &Schema) -> Result<String, SerdeError> {
1145 Ok(String::from_utf8_lossy(self.input).into_owned())
1146 }
1147 fn read_blob(&mut self, _: &Schema) -> Result<aws_smithy_types::Blob, SerdeError> {
1148 Ok(aws_smithy_types::Blob::new(vec![]))
1149 }
1150 fn read_timestamp(&mut self, _: &Schema) -> Result<aws_smithy_types::DateTime, SerdeError> {
1151 Ok(aws_smithy_types::DateTime::from_secs(0))
1152 }
1153 fn read_document(&mut self, _: &Schema) -> Result<aws_smithy_types::Document, SerdeError> {
1154 Ok(aws_smithy_types::Document::Null)
1155 }
1156 fn is_null(&self) -> bool {
1157 false
1158 }
1159 fn container_size(&self) -> Option<usize> {
1160 None
1161 }
1162 }
1163
1164 #[derive(Debug)]
1165 struct TestCodec;
1166
1167 impl Codec for TestCodec {
1168 type Serializer = TestSerializer;
1169 type Deserializer<'a> = TestDeserializer<'a>;
1170 fn create_serializer(&self) -> Self::Serializer {
1171 TestSerializer { output: Vec::new() }
1172 }
1173 fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a> {
1174 TestDeserializer { input }
1175 }
1176 }
1177
1178 static TEST_SCHEMA: Schema =
1179 Schema::new(crate::shape_id!("test", "TestStruct"), ShapeType::Structure);
1180
1181 struct EmptyStruct;
1182 impl SerializableStruct for EmptyStruct {
1183 fn serialize_members(&self, _: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1184 Ok(())
1185 }
1186 }
1187
1188 static NAME_MEMBER: Schema = Schema::new_member(
1189 crate::shape_id!("test", "TestStruct"),
1190 ShapeType::String,
1191 "name",
1192 0,
1193 );
1194 static MEMBERS: &[&Schema] = &[&NAME_MEMBER];
1195 static STRUCT_WITH_MEMBER: Schema = Schema::new_struct(
1196 crate::shape_id!("test", "TestStruct"),
1197 ShapeType::Structure,
1198 MEMBERS,
1199 );
1200
1201 struct NameStruct;
1202 impl SerializableStruct for NameStruct {
1203 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1204 s.write_string(&NAME_MEMBER, "Alice")
1205 }
1206 }
1207
1208 fn make_protocol() -> HttpBindingProtocol<TestCodec> {
1209 HttpBindingProtocol::new(
1210 crate::shape_id!("test", "proto"),
1211 TestCodec,
1212 "application/test",
1213 )
1214 }
1215
1216 #[test]
1217 fn serialize_sets_content_type() {
1218 let request = make_protocol()
1220 .serialize_request(
1221 &EmptyStruct,
1222 &STRUCT_WITH_MEMBER,
1223 "https://example.com",
1224 &ConfigBag::base(),
1225 )
1226 .unwrap();
1227 assert_eq!(
1228 request.headers().get("Content-Type").unwrap(),
1229 "application/test"
1230 );
1231 }
1232
1233 #[test]
1234 fn serialize_no_body_members_omits_content_type() {
1235 let request = make_protocol()
1237 .serialize_request(
1238 &EmptyStruct,
1239 &TEST_SCHEMA,
1240 "https://example.com",
1241 &ConfigBag::base(),
1242 )
1243 .unwrap();
1244 assert!(request.headers().get("Content-Type").is_none());
1245 }
1246
1247 #[test]
1248 fn serialize_sets_uri() {
1249 let request = make_protocol()
1250 .serialize_request(
1251 &EmptyStruct,
1252 &TEST_SCHEMA,
1253 "https://example.com/path",
1254 &ConfigBag::base(),
1255 )
1256 .unwrap();
1257 assert_eq!(request.uri(), "https://example.com/path");
1258 }
1259
1260 #[test]
1261 fn serialize_body() {
1262 let request = make_protocol()
1263 .serialize_request(
1264 &NameStruct,
1265 &STRUCT_WITH_MEMBER,
1266 "https://example.com",
1267 &ConfigBag::base(),
1268 )
1269 .unwrap();
1270 assert_eq!(request.body().bytes().unwrap(), b"{Alice}");
1271 }
1272
1273 #[test]
1274 fn deserialize_response() {
1275 let response = Response::new(
1276 200u16.try_into().unwrap(),
1277 SdkBody::from(r#"{"name":"Bob"}"#),
1278 );
1279 let mut deser = make_protocol()
1280 .deserialize_response(&response, &TEST_SCHEMA, &ConfigBag::base())
1281 .unwrap();
1282 assert_eq!(deser.read_string(&STRING).unwrap(), r#"{"name":"Bob"}"#);
1283 }
1284
1285 #[test]
1286 fn update_endpoint() {
1287 let mut request = make_protocol()
1288 .serialize_request(
1289 &EmptyStruct,
1290 &TEST_SCHEMA,
1291 "https://old.example.com",
1292 &ConfigBag::base(),
1293 )
1294 .unwrap();
1295 let endpoint = aws_smithy_types::endpoint::Endpoint::builder()
1296 .url("https://new.example.com")
1297 .build();
1298 make_protocol()
1299 .update_endpoint(&mut request, &endpoint, &ConfigBag::base())
1300 .unwrap();
1301 assert_eq!(request.uri(), "https://new.example.com/");
1302 }
1303
1304 #[test]
1305 fn protocol_id() {
1306 let protocol = HttpBindingProtocol::new(
1307 crate::shape_id!("aws.protocols", "restJson1"),
1308 TestCodec,
1309 "application/json",
1310 );
1311 assert_eq!(protocol.protocol_id().as_str(), "aws.protocols#restJson1");
1312 }
1313
1314 #[test]
1315 fn invalid_uri_returns_error() {
1316 assert!(make_protocol()
1317 .serialize_request(
1318 &EmptyStruct,
1319 &TEST_SCHEMA,
1320 "not a valid uri\n\n",
1321 &ConfigBag::base()
1322 )
1323 .is_err());
1324 }
1325
1326 static HEADER_MEMBER: Schema = Schema::new_member(
1329 crate::shape_id!("test", "S"),
1330 ShapeType::String,
1331 "xToken",
1332 0,
1333 )
1334 .with_http_header("X-Token");
1335
1336 static HEADER_SCHEMA: Schema = Schema::new_struct(
1337 crate::shape_id!("test", "S"),
1338 ShapeType::Structure,
1339 &[&HEADER_MEMBER],
1340 );
1341
1342 struct HeaderStruct;
1343 impl SerializableStruct for HeaderStruct {
1344 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1345 s.write_string(&HEADER_MEMBER, "my-token-value")
1346 }
1347 }
1348
1349 #[test]
1350 fn http_header_string() {
1351 let request = make_protocol()
1352 .serialize_request(
1353 &HeaderStruct,
1354 &HEADER_SCHEMA,
1355 "https://example.com",
1356 &ConfigBag::base(),
1357 )
1358 .unwrap();
1359 assert_eq!(request.headers().get("X-Token").unwrap(), "my-token-value");
1360 }
1361
1362 static INT_HEADER_MEMBER: Schema = Schema::new_member(
1363 crate::shape_id!("test", "S"),
1364 ShapeType::Integer,
1365 "retryCount",
1366 0,
1367 )
1368 .with_http_header("X-Retry-Count");
1369
1370 static INT_HEADER_SCHEMA: Schema = Schema::new_struct(
1371 crate::shape_id!("test", "S"),
1372 ShapeType::Structure,
1373 &[&INT_HEADER_MEMBER],
1374 );
1375
1376 struct IntHeaderStruct;
1377 impl SerializableStruct for IntHeaderStruct {
1378 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1379 s.write_integer(&INT_HEADER_MEMBER, 3)
1380 }
1381 }
1382
1383 #[test]
1384 fn http_header_integer() {
1385 let request = make_protocol()
1386 .serialize_request(
1387 &IntHeaderStruct,
1388 &INT_HEADER_SCHEMA,
1389 "https://example.com",
1390 &ConfigBag::base(),
1391 )
1392 .unwrap();
1393 assert_eq!(request.headers().get("X-Retry-Count").unwrap(), "3");
1394 }
1395
1396 static BOOL_HEADER_MEMBER: Schema = Schema::new_member(
1397 crate::shape_id!("test", "S"),
1398 ShapeType::Boolean,
1399 "verbose",
1400 0,
1401 )
1402 .with_http_header("X-Verbose");
1403
1404 static BOOL_HEADER_SCHEMA: Schema = Schema::new_struct(
1405 crate::shape_id!("test", "S"),
1406 ShapeType::Structure,
1407 &[&BOOL_HEADER_MEMBER],
1408 );
1409
1410 struct BoolHeaderStruct;
1411 impl SerializableStruct for BoolHeaderStruct {
1412 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1413 s.write_boolean(&BOOL_HEADER_MEMBER, true)
1414 }
1415 }
1416
1417 #[test]
1418 fn http_header_boolean() {
1419 let request = make_protocol()
1420 .serialize_request(
1421 &BoolHeaderStruct,
1422 &BOOL_HEADER_SCHEMA,
1423 "https://example.com",
1424 &ConfigBag::base(),
1425 )
1426 .unwrap();
1427 assert_eq!(request.headers().get("X-Verbose").unwrap(), "true");
1428 }
1429
1430 static QUERY_MEMBER: Schema =
1433 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "color", 0)
1434 .with_http_query("color");
1435
1436 static QUERY_SCHEMA: Schema = Schema::new_struct(
1437 crate::shape_id!("test", "S"),
1438 ShapeType::Structure,
1439 &[&QUERY_MEMBER],
1440 );
1441
1442 struct QueryStruct;
1443 impl SerializableStruct for QueryStruct {
1444 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1445 s.write_string(&QUERY_MEMBER, "blue")
1446 }
1447 }
1448
1449 #[test]
1450 fn http_query_string() {
1451 let request = make_protocol()
1452 .serialize_request(
1453 &QueryStruct,
1454 &QUERY_SCHEMA,
1455 "https://example.com/things",
1456 &ConfigBag::base(),
1457 )
1458 .unwrap();
1459 assert_eq!(request.uri(), "https://example.com/things?color=blue");
1460 }
1461
1462 static INT_QUERY_MEMBER: Schema =
1463 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Integer, "size", 0)
1464 .with_http_query("size");
1465
1466 static INT_QUERY_SCHEMA: Schema = Schema::new_struct(
1467 crate::shape_id!("test", "S"),
1468 ShapeType::Structure,
1469 &[&INT_QUERY_MEMBER],
1470 );
1471
1472 struct IntQueryStruct;
1473 impl SerializableStruct for IntQueryStruct {
1474 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1475 s.write_integer(&INT_QUERY_MEMBER, 42)
1476 }
1477 }
1478
1479 #[test]
1480 fn http_query_integer() {
1481 let request = make_protocol()
1482 .serialize_request(
1483 &IntQueryStruct,
1484 &INT_QUERY_SCHEMA,
1485 "https://example.com/things",
1486 &ConfigBag::base(),
1487 )
1488 .unwrap();
1489 assert_eq!(request.uri(), "https://example.com/things?size=42");
1490 }
1491
1492 static Q1: Schema =
1495 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "a", 0)
1496 .with_http_query("a");
1497 static Q2: Schema =
1498 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "b", 1)
1499 .with_http_query("b");
1500 static MULTI_QUERY_SCHEMA: Schema = Schema::new_struct(
1501 crate::shape_id!("test", "S"),
1502 ShapeType::Structure,
1503 &[&Q1, &Q2],
1504 );
1505
1506 struct MultiQueryStruct;
1507 impl SerializableStruct for MultiQueryStruct {
1508 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1509 s.write_string(&Q1, "x")?;
1510 s.write_string(&Q2, "y")
1511 }
1512 }
1513
1514 #[test]
1515 fn http_query_multiple_params() {
1516 let request = make_protocol()
1517 .serialize_request(
1518 &MultiQueryStruct,
1519 &MULTI_QUERY_SCHEMA,
1520 "https://example.com",
1521 &ConfigBag::base(),
1522 )
1523 .unwrap();
1524 assert_eq!(request.uri(), "https://example.com?a=x&b=y");
1525 }
1526
1527 #[test]
1530 fn http_query_percent_encodes_values() {
1531 struct SpaceQueryStruct;
1532 impl SerializableStruct for SpaceQueryStruct {
1533 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1534 s.write_string(&QUERY_MEMBER, "hello world")
1535 }
1536 }
1537 let request = make_protocol()
1538 .serialize_request(
1539 &SpaceQueryStruct,
1540 &QUERY_SCHEMA,
1541 "https://example.com",
1542 &ConfigBag::base(),
1543 )
1544 .unwrap();
1545 assert_eq!(request.uri(), "https://example.com?color=hello%20world");
1546 }
1547
1548 static LABEL_MEMBER: Schema = Schema::new_member(
1551 crate::shape_id!("test", "S"),
1552 ShapeType::String,
1553 "bucketName",
1554 0,
1555 )
1556 .with_http_label();
1557
1558 static LABEL_SCHEMA: Schema = Schema::new_struct(
1559 crate::shape_id!("test", "S"),
1560 ShapeType::Structure,
1561 &[&LABEL_MEMBER],
1562 );
1563
1564 struct LabelStruct;
1565 impl SerializableStruct for LabelStruct {
1566 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1567 s.write_string(&LABEL_MEMBER, "my-bucket")
1568 }
1569 }
1570
1571 #[test]
1572 fn http_label_substitution() {
1573 let request = make_protocol()
1574 .serialize_request(
1575 &LabelStruct,
1576 &LABEL_SCHEMA,
1577 "https://example.com/{bucketName}/objects",
1578 &ConfigBag::base(),
1579 )
1580 .unwrap();
1581 assert_eq!(request.uri(), "https://example.com/my-bucket/objects");
1582 }
1583
1584 #[test]
1585 fn http_label_percent_encodes() {
1586 struct SpecialLabelStruct;
1587 impl SerializableStruct for SpecialLabelStruct {
1588 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1589 s.write_string(&LABEL_MEMBER, "my bucket/name")
1590 }
1591 }
1592 let request = make_protocol()
1593 .serialize_request(
1594 &SpecialLabelStruct,
1595 &LABEL_SCHEMA,
1596 "https://example.com/{bucketName}",
1597 &ConfigBag::base(),
1598 )
1599 .unwrap();
1600 assert!(request.uri().contains("my%20bucket%2Fname"));
1601 }
1602
1603 static INT_LABEL_MEMBER: Schema = Schema::new_member(
1604 crate::shape_id!("test", "S"),
1605 ShapeType::Integer,
1606 "itemId",
1607 0,
1608 )
1609 .with_http_label();
1610
1611 static INT_LABEL_SCHEMA: Schema = Schema::new_struct(
1612 crate::shape_id!("test", "S"),
1613 ShapeType::Structure,
1614 &[&INT_LABEL_MEMBER],
1615 );
1616
1617 struct IntLabelStruct;
1618 impl SerializableStruct for IntLabelStruct {
1619 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1620 s.write_integer(&INT_LABEL_MEMBER, 123)
1621 }
1622 }
1623
1624 #[test]
1625 fn http_label_integer() {
1626 let request = make_protocol()
1627 .serialize_request(
1628 &IntLabelStruct,
1629 &INT_LABEL_SCHEMA,
1630 "https://example.com/items/{itemId}",
1631 &ConfigBag::base(),
1632 )
1633 .unwrap();
1634 assert_eq!(request.uri(), "https://example.com/items/123");
1635 }
1636
1637 static COMBINED_LABEL: Schema =
1640 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "id", 0)
1641 .with_http_label();
1642 static COMBINED_HEADER: Schema =
1643 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "token", 1)
1644 .with_http_header("X-Token");
1645 static COMBINED_QUERY: Schema = Schema::new_member(
1646 crate::shape_id!("test", "S"),
1647 ShapeType::String,
1648 "filter",
1649 2,
1650 )
1651 .with_http_query("filter");
1652 static COMBINED_BODY: Schema =
1653 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::String, "data", 3);
1654 static COMBINED_SCHEMA: Schema = Schema::new_struct(
1655 crate::shape_id!("test", "S"),
1656 ShapeType::Structure,
1657 &[
1658 &COMBINED_LABEL,
1659 &COMBINED_HEADER,
1660 &COMBINED_QUERY,
1661 &COMBINED_BODY,
1662 ],
1663 );
1664
1665 struct CombinedStruct;
1666 impl SerializableStruct for CombinedStruct {
1667 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1668 s.write_string(&COMBINED_LABEL, "item-42")?;
1669 s.write_string(&COMBINED_HEADER, "secret")?;
1670 s.write_string(&COMBINED_QUERY, "active")?;
1671 s.write_string(&COMBINED_BODY, "payload-data")
1672 }
1673 }
1674
1675 #[test]
1676 fn combined_bindings() {
1677 let request = make_protocol()
1678 .serialize_request(
1679 &CombinedStruct,
1680 &COMBINED_SCHEMA,
1681 "https://example.com/{id}/details",
1682 &ConfigBag::base(),
1683 )
1684 .unwrap();
1685 assert_eq!(
1686 request.uri(),
1687 "https://example.com/item-42/details?filter=active"
1688 );
1689 assert_eq!(request.headers().get("X-Token").unwrap(), "secret");
1691 let body = request.body().bytes().unwrap();
1693 assert!(body
1694 .windows(b"payload-data".len())
1695 .any(|w| w == b"payload-data"));
1696 }
1697
1698 static PREFIX_MEMBER: Schema =
1701 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Map, "metadata", 0)
1702 .with_http_prefix_headers("X-Meta-");
1703
1704 static PREFIX_SCHEMA: Schema = Schema::new_struct(
1705 crate::shape_id!("test", "S"),
1706 ShapeType::Structure,
1707 &[&PREFIX_MEMBER],
1708 );
1709
1710 struct PrefixHeaderStruct;
1711 impl SerializableStruct for PrefixHeaderStruct {
1712 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1713 s.write_map(&PREFIX_MEMBER, &|s| {
1714 s.write_string(&STRING, "Color")?;
1715 s.write_string(&STRING, "red")?;
1716 s.write_string(&STRING, "Size")?;
1717 s.write_string(&STRING, "large")?;
1718 Ok(())
1719 })
1720 }
1721 }
1722
1723 #[test]
1724 fn http_prefix_headers() {
1725 let request = make_protocol()
1726 .serialize_request(
1727 &PrefixHeaderStruct,
1728 &PREFIX_SCHEMA,
1729 "https://example.com",
1730 &ConfigBag::base(),
1731 )
1732 .unwrap();
1733 assert_eq!(request.headers().get("X-Meta-Color").unwrap(), "red");
1734 assert_eq!(request.headers().get("X-Meta-Size").unwrap(), "large");
1735 }
1736
1737 static QUERY_PARAMS_MEMBER: Schema =
1740 Schema::new_member(crate::shape_id!("test", "S"), ShapeType::Map, "params", 0)
1741 .with_http_query_params();
1742
1743 static QUERY_PARAMS_SCHEMA: Schema = Schema::new_struct(
1744 crate::shape_id!("test", "S"),
1745 ShapeType::Structure,
1746 &[&QUERY_PARAMS_MEMBER],
1747 );
1748
1749 struct QueryParamsStruct;
1750 impl SerializableStruct for QueryParamsStruct {
1751 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1752 s.write_map(&QUERY_PARAMS_MEMBER, &|s| {
1753 s.write_string(&STRING, "page")?;
1754 s.write_string(&STRING, "2")?;
1755 s.write_string(&STRING, "limit")?;
1756 s.write_string(&STRING, "50")?;
1757 Ok(())
1758 })
1759 }
1760 }
1761
1762 #[test]
1763 fn http_query_params() {
1764 let request = make_protocol()
1765 .serialize_request(
1766 &QueryParamsStruct,
1767 &QUERY_PARAMS_SCHEMA,
1768 "https://example.com",
1769 &ConfigBag::base(),
1770 )
1771 .unwrap();
1772 assert_eq!(request.uri(), "https://example.com?page=2&limit=50");
1773 }
1774
1775 static TS_HEADER_MEMBER: Schema = Schema::new_member(
1778 crate::shape_id!("test", "S"),
1779 ShapeType::Timestamp,
1780 "ifModified",
1781 0,
1782 )
1783 .with_http_header("If-Modified-Since");
1784
1785 static TS_HEADER_SCHEMA: Schema = Schema::new_struct(
1786 crate::shape_id!("test", "S"),
1787 ShapeType::Structure,
1788 &[&TS_HEADER_MEMBER],
1789 );
1790
1791 struct TimestampHeaderStruct;
1792 impl SerializableStruct for TimestampHeaderStruct {
1793 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1794 s.write_timestamp(&TS_HEADER_MEMBER, &aws_smithy_types::DateTime::from_secs(0))
1795 }
1796 }
1797
1798 #[test]
1799 fn timestamp_header_uses_http_date() {
1800 let request = make_protocol()
1801 .serialize_request(
1802 &TimestampHeaderStruct,
1803 &TS_HEADER_SCHEMA,
1804 "https://example.com",
1805 &ConfigBag::base(),
1806 )
1807 .unwrap();
1808 let value = request.headers().get("If-Modified-Since").unwrap();
1809 assert!(value.contains("1970"), "expected http-date, got: {value}");
1811 }
1812
1813 static TS_QUERY_MEMBER: Schema = Schema::new_member(
1816 crate::shape_id!("test", "S"),
1817 ShapeType::Timestamp,
1818 "since",
1819 0,
1820 )
1821 .with_http_query("since");
1822
1823 static TS_QUERY_SCHEMA: Schema = Schema::new_struct(
1824 crate::shape_id!("test", "S"),
1825 ShapeType::Structure,
1826 &[&TS_QUERY_MEMBER],
1827 );
1828
1829 struct TimestampQueryStruct;
1830 impl SerializableStruct for TimestampQueryStruct {
1831 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1832 s.write_timestamp(&TS_QUERY_MEMBER, &aws_smithy_types::DateTime::from_secs(0))
1833 }
1834 }
1835
1836 #[test]
1837 fn timestamp_query_uses_date_time() {
1838 let request = make_protocol()
1839 .serialize_request(
1840 &TimestampQueryStruct,
1841 &TS_QUERY_SCHEMA,
1842 "https://example.com",
1843 &ConfigBag::base(),
1844 )
1845 .unwrap();
1846 assert_eq!(
1847 request.uri(),
1848 "https://example.com?since=1970-01-01T00%3A00%3A00Z"
1849 );
1850 }
1851
1852 static BOUND_MEMBER: Schema = Schema::new_member(
1855 crate::shape_id!("test", "S"),
1856 ShapeType::String,
1857 "headerVal",
1858 0,
1859 )
1860 .with_http_header("X-Val");
1861 static UNBOUND_MEMBER: Schema = Schema::new_member(
1862 crate::shape_id!("test", "S"),
1863 ShapeType::String,
1864 "bodyVal",
1865 1,
1866 );
1867 static MIXED_SCHEMA: Schema = Schema::new_struct(
1868 crate::shape_id!("test", "S"),
1869 ShapeType::Structure,
1870 &[&BOUND_MEMBER, &UNBOUND_MEMBER],
1871 );
1872
1873 struct MixedStruct;
1874 impl SerializableStruct for MixedStruct {
1875 fn serialize_members(&self, s: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1876 s.write_string(&BOUND_MEMBER, "in-header")?;
1877 s.write_string(&UNBOUND_MEMBER, "in-body")
1878 }
1879 }
1880
1881 #[test]
1882 fn bound_members_not_in_body() {
1883 let request = make_protocol()
1884 .serialize_request(
1885 &MixedStruct,
1886 &MIXED_SCHEMA,
1887 "https://example.com",
1888 &ConfigBag::base(),
1889 )
1890 .unwrap();
1891 let body = std::str::from_utf8(request.body().bytes().unwrap()).unwrap();
1892 assert!(
1893 body.contains("in-body"),
1894 "body should contain unbound member"
1895 );
1896 assert!(
1897 !body.contains("in-header"),
1898 "body should NOT contain header-bound member"
1899 );
1900 assert_eq!(request.headers().get("X-Val").unwrap(), "in-header");
1901 }
1902}