AWS SDK

AWS SDK

rev. 96f5a1b4ad139d2f1ad1e8e40f300e1cd1ff574c (ignoring whitespace)

Files changed:

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-runtime/tests/stalled_stream_common.rs

@@ -15,15 +75,76 @@
   35     35   
        orchestrator::{HttpRequest, HttpResponse, OrchestratorError},
   36     36   
        result::SdkError,
   37     37   
        runtime_components::RuntimeComponents,
   38     38   
        ser_de::DeserializeResponse,
   39     39   
        stalled_stream_protection::StalledStreamProtectionConfig,
   40     40   
    },
   41     41   
    http::{Response, StatusCode},
   42     42   
    shared::IntoShared,
   43     43   
};
   44     44   
pub use aws_smithy_types::{
   45         -
    body::SdkBody, error::display::DisplayErrorContext, timeout::TimeoutConfig,
          45  +
    body::SdkBody, config_bag::ConfigBag, error::display::DisplayErrorContext,
          46  +
    timeout::TimeoutConfig,
   46     47   
};
   47     48   
pub use bytes::Bytes;
   48     49   
pub use pin_utils::pin_mut;
   49     50   
pub use std::{
   50     51   
    collections::VecDeque,
   51     52   
    convert::Infallible,
   52     53   
    future::poll_fn,
   53     54   
    mem,
   54     55   
    pin::Pin,
   55     56   
    sync::{Arc, Mutex},

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/Cargo.toml

@@ -1,1 +24,33 @@
    9      9   
repository = "https://github.com/smithy-lang/smithy-rs"
   10     10   
rust-version = "1.91"
   11     11   
[package.metadata.docs.rs]
   12     12   
all-features = true
   13     13   
targets = ["x86_64-unknown-linux-gnu"]
   14     14   
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]
   15     15   
rustdoc-args = ["--cfg", "docsrs"]
   16     16   
   17     17   
[package.metadata.smithy-rs-release-tooling]
   18     18   
stable = true
          19  +
          20  +
[dependencies]
          21  +
http = "1.3.1"
          22  +
          23  +
[dependencies.aws-smithy-runtime-api]
          24  +
path = "../aws-smithy-runtime-api"
          25  +
features = ["client"]
          26  +
version = "1.11.7"
          27  +
   19     28   
[dependencies.aws-smithy-types]
   20     29   
path = "../aws-smithy-types"
   21     30   
default-features = false
   22     31   
version = "1.4.7"
   23     32   
   24     33   
[dev-dependencies]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/external-types.toml

@@ -1,1 +3,5 @@
    1      1   
allowed_external_types = [
           2  +
    "aws_smithy_runtime_api::http::request::Request",
           3  +
    "aws_smithy_runtime_api::http::response::Response",
    2      4   
    "aws_smithy_types::*",
    3      5   
]

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/lib.rs

@@ -1,1 +165,179 @@
   13     13   
//! enabling protocol-agnostic serialization and deserialization.
   14     14   
   15     15   
mod schema {
   16     16   
    pub mod shape_id;
   17     17   
    pub mod shape_type;
   18     18   
    pub mod trait_map;
   19     19   
    pub mod trait_type;
   20     20   
    pub mod traits;
   21     21   
   22     22   
    pub mod codec;
          23  +
    pub mod http_protocol;
   23     24   
    pub mod prelude;
          25  +
    pub mod protocol;
   24     26   
    pub mod serde;
   25     27   
}
   26     28   
   27     29   
pub use schema::shape_id::ShapeId;
   28     30   
pub use schema::shape_type::ShapeType;
   29     31   
pub use schema::trait_map::TraitMap;
   30     32   
pub use schema::trait_type::Trait;
   31     33   
pub use schema::trait_type::{AnnotationTrait, DocumentTrait, StringTrait};
   32     34   
   33     35   
pub mod prelude {
   34     36   
    pub use crate::schema::prelude::*;
   35     37   
}
   36     38   
   37     39   
pub mod serde {
   38     40   
    pub use crate::schema::serde::*;
   39     41   
}
   40     42   
   41     43   
pub mod traits {
   42     44   
    pub use crate::schema::traits::*;
   43     45   
}
   44     46   
   45     47   
pub mod codec {
   46     48   
    pub use crate::schema::codec::*;
   47     49   
}
   48     50   
          51  +
pub mod protocol {
          52  +
    pub use crate::schema::protocol::*;
          53  +
}
          54  +
          55  +
pub mod http_protocol {
          56  +
    pub use crate::schema::http_protocol::*;
          57  +
}
          58  +
   49     59   
/// A Smithy schema — a lightweight runtime representation of a Smithy shape.
   50     60   
///
   51     61   
/// Contains the shape's ID, type, traits relevant to serialization, and
   52     62   
/// references to member schemas (for aggregate types).
   53     63   
///
   54     64   
/// Schemas are constructed at compile time (via `const`) for generated code
   55     65   
/// and prelude types. The Smithy type system is closed, so no extensibility
   56     66   
/// via trait objects is needed.
   57     67   
use schema::traits as trait_types;
   58     68   
   59     69   
#[derive(Debug)]
   60     70   
pub struct Schema {
   61     71   
    id: ShapeId,
   62     72   
    shape_type: ShapeType,
   63     73   
    /// Member name if this is a member schema.
   64     74   
    member_name: Option<&'static str>,
   65     75   
    /// Member index for position-based lookup in generated code.
   66     76   
    member_index: Option<usize>,
   67     77   
    /// Shape-type-specific member data.
   68     78   
    members: SchemaMembers,
   69     79   
   70     80   
    // -- Known serde trait fields (const-constructable) --
   71     81   
    // IMPORTANT: These fields and their `with_*` setters must stay in sync with
   72     82   
    // `knownTraitSetter` in `SchemaGenerator.kt`. If a new known trait is added
   73     83   
    // here, a corresponding entry must be added in the codegen.
   74     84   
    sensitive: Option<trait_types::SensitiveTrait>,
   75     85   
    json_name: Option<trait_types::JsonNameTrait>,
   76     86   
    timestamp_format: Option<trait_types::TimestampFormatTrait>,
   77     87   
    xml_name: Option<trait_types::XmlNameTrait>,
   78     88   
    xml_attribute: Option<trait_types::XmlAttributeTrait>,
   79     89   
    xml_flattened: Option<trait_types::XmlFlattenedTrait>,
   80     90   
    xml_namespace: Option<trait_types::XmlNamespaceTrait>,
   81     91   
    http_header: Option<trait_types::HttpHeaderTrait>,
   82     92   
    http_label: Option<trait_types::HttpLabelTrait>,
   83     93   
    http_payload: Option<trait_types::HttpPayloadTrait>,
   84     94   
    http_prefix_headers: Option<trait_types::HttpPrefixHeadersTrait>,
   85     95   
    http_query: Option<trait_types::HttpQueryTrait>,
   86     96   
    http_query_params: Option<trait_types::HttpQueryParamsTrait>,
   87     97   
    http_response_code: Option<trait_types::HttpResponseCodeTrait>,
          98  +
    /// The `@http` trait — an operation-level trait included on the input schema
          99  +
    /// for convenience so the protocol serializer can construct the request URI.
         100  +
    http: Option<trait_types::HttpTrait>,
   88    101   
    streaming: Option<trait_types::StreamingTrait>,
   89    102   
    event_header: Option<trait_types::EventHeaderTrait>,
   90    103   
    event_payload: Option<trait_types::EventPayloadTrait>,
   91    104   
    host_label: Option<trait_types::HostLabelTrait>,
   92    105   
    media_type: Option<trait_types::MediaTypeTrait>,
   93    106   
   94    107   
    /// Fallback for unknown/custom traits. `None` in const contexts (no allocation).
   95    108   
    traits: Option<&'static std::sync::LazyLock<TraitMap>>,
   96    109   
}
   97    110   
   98    111   
/// Shape-type-specific member references.
   99    112   
#[derive(Debug)]
  100    113   
enum SchemaMembers {
  101    114   
    /// No members (simple types).
  102    115   
    None,
  103    116   
    /// Structure or union members.
  104    117   
    Struct { members: &'static [&'static Schema] },
  105    118   
    /// List member schema.
  106    119   
    List { member: &'static Schema },
  107    120   
    /// Map key and value schemas.
  108    121   
    Map {
  109    122   
        key: &'static Schema,
  110    123   
        value: &'static Schema,
  111    124   
    },
  112    125   
}
  113    126   
  114    127   
impl Schema {
  115    128   
    /// Default values for all trait fields (should only be used by constructors as a spread source).
  116    129   
    const EMPTY_TRAITS: Self = Self {
  117    130   
        id: ShapeId::from_static("", "", ""),
  118    131   
        shape_type: ShapeType::Boolean,
  119    132   
        member_name: None,
  120    133   
        member_index: None,
  121    134   
        members: SchemaMembers::None,
  122    135   
        sensitive: None,
  123    136   
        json_name: None,
  124    137   
        timestamp_format: None,
  125    138   
        xml_name: None,
  126    139   
        xml_attribute: None,
  127    140   
        xml_flattened: None,
  128    141   
        xml_namespace: None,
  129    142   
        http_header: None,
  130    143   
        http_label: None,
  131    144   
        http_payload: None,
  132    145   
        http_prefix_headers: None,
  133    146   
        http_query: None,
  134    147   
        http_query_params: None,
  135    148   
        http_response_code: None,
         149  +
        http: None,
  136    150   
        streaming: None,
  137    151   
        event_header: None,
  138    152   
        event_payload: None,
  139    153   
        host_label: None,
  140    154   
        media_type: None,
  141    155   
        traits: None,
  142    156   
    };
  143    157   
  144    158   
    /// Creates a schema for a simple type (no members).
  145    159   
    pub const fn new(id: ShapeId, shape_type: ShapeType) -> Self {
@@ -211,225 +279,340 @@
  231    245   
    pub fn timestamp_format(&self) -> Option<&trait_types::TimestampFormatTrait> {
  232    246   
        self.timestamp_format.as_ref()
  233    247   
    }
  234    248   
  235    249   
    /// Returns the `@xmlName` value if present.
  236    250   
    pub fn xml_name(&self) -> Option<&trait_types::XmlNameTrait> {
  237    251   
        self.xml_name.as_ref()
  238    252   
    }
  239    253   
  240    254   
    /// Returns the `@httpHeader` value if present.
         255  +
    /// Returns `true` if this member schema has any HTTP response binding trait
         256  +
    /// (`@httpHeader`, `@httpResponseCode`, `@httpPrefixHeaders`, or `@httpPayload`).
         257  +
    pub fn has_http_response_binding(&self) -> bool {
         258  +
        self.http_header.is_some()
         259  +
            || self.http_response_code.is_some()
         260  +
            || self.http_prefix_headers.is_some()
         261  +
            || self.http_payload.is_some()
         262  +
    }
         263  +
  241    264   
    pub fn http_header(&self) -> Option<&trait_types::HttpHeaderTrait> {
  242    265   
        self.http_header.as_ref()
  243    266   
    }
  244    267   
  245    268   
    /// Returns the `@httpQuery` value if present.
  246    269   
    pub fn http_query(&self) -> Option<&trait_types::HttpQueryTrait> {
  247    270   
        self.http_query.as_ref()
  248    271   
    }
  249    272   
         273  +
    /// Returns the `@httpLabel` trait if present.
         274  +
    pub fn http_label(&self) -> Option<&trait_types::HttpLabelTrait> {
         275  +
        self.http_label.as_ref()
         276  +
    }
         277  +
         278  +
    /// Returns the `@httpPayload` trait if present.
         279  +
    pub fn http_payload(&self) -> Option<&trait_types::HttpPayloadTrait> {
         280  +
        self.http_payload.as_ref()
         281  +
    }
         282  +
         283  +
    /// Returns the `@httpPrefixHeaders` value if present.
         284  +
    pub fn http_prefix_headers(&self) -> Option<&trait_types::HttpPrefixHeadersTrait> {
         285  +
        self.http_prefix_headers.as_ref()
         286  +
    }
         287  +
         288  +
    /// Returns the `@mediaType` trait if present.
         289  +
    pub fn media_type(&self) -> Option<&trait_types::MediaTypeTrait> {
         290  +
        self.media_type.as_ref()
         291  +
    }
         292  +
         293  +
    /// Returns the `@httpQueryParams` trait if present.
         294  +
    pub fn http_query_params(&self) -> Option<&trait_types::HttpQueryParamsTrait> {
         295  +
        self.http_query_params.as_ref()
         296  +
    }
         297  +
         298  +
    /// Returns the `@httpResponseCode` trait if present.
         299  +
    pub fn http_response_code(&self) -> Option<&trait_types::HttpResponseCodeTrait> {
         300  +
        self.http_response_code.as_ref()
         301  +
    }
         302  +
         303  +
    /// Returns the `@http` trait if present.
         304  +
    ///
         305  +
    /// This is an operation-level trait included on the input schema for
         306  +
    /// convenience so the protocol serializer can construct the request URI.
         307  +
    pub fn http(&self) -> Option<&trait_types::HttpTrait> {
         308  +
        self.http.as_ref()
         309  +
    }
         310  +
  250    311   
    // -- Const setters for builder-style construction in generated code --
  251    312   
  252    313   
    /// Sets the `@sensitive` trait.
  253    314   
    pub const fn with_sensitive(mut self) -> Self {
  254    315   
        self.sensitive = Some(trait_types::SensitiveTrait);
  255    316   
        self
  256    317   
    }
  257    318   
  258    319   
    /// Sets the `@jsonName` trait.
  259    320   
    pub const fn with_json_name(mut self, value: &'static str) -> Self {
@@ -300,361 +359,426 @@
  320    381   
        self.http_query_params = Some(trait_types::HttpQueryParamsTrait);
  321    382   
        self
  322    383   
    }
  323    384   
  324    385   
    /// Sets the `@httpResponseCode` trait.
  325    386   
    pub const fn with_http_response_code(mut self) -> Self {
  326    387   
        self.http_response_code = Some(trait_types::HttpResponseCodeTrait);
  327    388   
        self
  328    389   
    }
  329    390   
         391  +
    /// Sets the `@http` trait (operation-level, included on input schema for convenience).
         392  +
    pub const fn with_http(mut self, http: trait_types::HttpTrait) -> Self {
         393  +
        self.http = Some(http);
         394  +
        self
         395  +
    }
         396  +
  330    397   
    /// Sets the `@streaming` trait.
  331    398   
    pub const fn with_streaming(mut self) -> Self {
  332    399   
        self.streaming = Some(trait_types::StreamingTrait);
  333    400   
        self
  334    401   
    }
  335    402   
  336    403   
    /// Sets the `@eventHeader` trait.
  337    404   
    pub const fn with_event_header(mut self) -> Self {
  338    405   
        self.event_header = Some(trait_types::EventHeaderTrait);
  339    406   
        self

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/codec.rs

@@ -1,1 +105,120 @@
    5      5   
    6      6   
//! Codec trait for creating shape serializers and deserializers.
    7      7   
//!
    8      8   
//! A codec represents a specific serialization format (e.g., JSON, XML, CBOR)
    9      9   
//! and provides methods to create serializers and deserializers for that format.
   10     10   
   11     11   
pub mod http_string;
   12     12   
   13     13   
use crate::serde::{ShapeDeserializer, ShapeSerializer};
   14     14   
          15  +
/// Trait for serializers that can produce a final byte output.
          16  +
///
          17  +
/// This is separate from [`ShapeSerializer`] to preserve object safety on
          18  +
/// `ShapeSerializer` (which is used as `&mut dyn ShapeSerializer` in generated code).
          19  +
pub trait FinishSerializer {
          20  +
    /// Consumes the serializer and returns the serialized bytes.
          21  +
    fn finish(self) -> Vec<u8>;
          22  +
}
          23  +
   15     24   
/// A codec for a specific serialization format.
   16     25   
///
   17     26   
/// Codecs are responsible for creating [`ShapeSerializer`] and [`ShapeDeserializer`]
   18     27   
/// instances that can serialize and deserialize shapes to and from a specific format.
   19     28   
///
   20     29   
/// # Examples
   21     30   
///
   22     31   
/// Implementing a custom codec:
   23     32   
///
   24     33   
/// ```ignore
   25     34   
/// use aws_smithy_schema::codec::Codec;
   26     35   
/// use aws_smithy_schema::serde::{ShapeSerializer, ShapeDeserializer};
   27     36   
///
   28     37   
/// struct MyCodec {
   29     38   
///     // codec configuration
   30     39   
/// }
   31     40   
///
   32     41   
/// impl Codec for MyCodec {
   33     42   
///     type Serializer = MySerializer;
   34     43   
///     type Deserializer = MyDeserializer;
   35     44   
///
   36     45   
///     fn create_serializer(&self) -> Self::Serializer {
   37     46   
///         MySerializer::new()
   38     47   
///     }
   39     48   
///
   40     49   
///     fn create_deserializer(&self, input: &[u8]) -> Self::Deserializer {
   41     50   
///         MyDeserializer::new(input)
   42     51   
///     }
   43     52   
/// }
   44     53   
/// ```
   45     54   
pub trait Codec {
   46     55   
    /// The serializer type for this codec.
   47         -
    type Serializer: ShapeSerializer;
          56  +
    type Serializer: ShapeSerializer + FinishSerializer;
   48     57   
   49     58   
    /// The deserializer type for this codec.
   50     59   
    type Deserializer<'a>: ShapeDeserializer;
   51     60   
   52     61   
    /// Creates a new serializer for this codec.
   53     62   
    fn create_serializer(&self) -> Self::Serializer;
   54     63   
   55     64   
    /// Creates a new deserializer for this codec from the given input bytes.
   56     65   
    fn create_deserializer<'a>(&self, input: &'a [u8]) -> Self::Deserializer<'a>;
   57     66   
}
   58     67   
   59     68   
#[cfg(test)]
   60     69   
mod test {
   61     70   
    use super::*;
   62     71   
    use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer, ShapeSerializer};
   63     72   
    use crate::{prelude::*, Schema};
   64     73   
   65     74   
    // Mock serializer
   66     75   
    struct MockSerializer {
   67     76   
        output: Vec<u8>,
   68     77   
    }
   69     78   
   70     79   
    impl MockSerializer {
   71     80   
        fn finish(self) -> Vec<u8> {
   72     81   
            self.output
   73     82   
        }
   74     83   
    }
   75     84   
          85  +
    impl FinishSerializer for MockSerializer {
          86  +
        fn finish(self) -> Vec<u8> {
          87  +
            self.output
          88  +
        }
          89  +
    }
          90  +
   76     91   
    impl ShapeSerializer for MockSerializer {
   77     92   
        fn write_struct(
   78     93   
            &mut self,
   79     94   
            _schema: &Schema,
   80     95   
            _value: &dyn SerializableStruct,
   81     96   
        ) -> Result<(), SerdeError> {
   82     97   
            Ok(())
   83     98   
        }
   84     99   
   85    100   
        fn write_list(
@@ -155,170 +248,254 @@
  175    190   
        }
  176    191   
    }
  177    192   
  178    193   
    // Mock deserializer
  179    194   
    struct MockDeserializer<'a> {
  180    195   
        #[allow(dead_code)]
  181    196   
        input: &'a [u8],
  182    197   
    }
  183    198   
  184    199   
    impl<'a> ShapeDeserializer for MockDeserializer<'a> {
  185         -
        fn read_struct<T, F>(
         200  +
        fn read_struct(
  186    201   
            &mut self,
  187    202   
            _schema: &Schema,
  188         -
            state: T,
  189         -
            _consumer: F,
  190         -
        ) -> Result<T, SerdeError>
  191         -
        where
  192         -
            F: FnMut(T, &Schema, &mut Self) -> Result<T, SerdeError>,
  193         -
        {
  194         -
            Ok(state)
         203  +
            _consumer: &mut dyn FnMut(
         204  +
                &Schema,
         205  +
                &mut dyn ShapeDeserializer,
         206  +
            ) -> Result<(), SerdeError>,
         207  +
        ) -> Result<(), SerdeError> {
         208  +
            Ok(())
  195    209   
        }
  196    210   
  197         -
        fn read_list<T, F>(
         211  +
        fn read_list(
  198    212   
            &mut self,
  199    213   
            _schema: &Schema,
  200         -
            state: T,
  201         -
            _consumer: F,
  202         -
        ) -> Result<T, SerdeError>
  203         -
        where
  204         -
            F: FnMut(T, &mut Self) -> Result<T, SerdeError>,
  205         -
        {
  206         -
            Ok(state)
         214  +
            _consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         215  +
        ) -> Result<(), SerdeError> {
         216  +
            Ok(())
  207    217   
        }
  208    218   
  209         -
        fn read_map<T, F>(
         219  +
        fn read_map(
  210    220   
            &mut self,
  211    221   
            _schema: &Schema,
  212         -
            state: T,
  213         -
            _consumer: F,
  214         -
        ) -> Result<T, SerdeError>
  215         -
        where
  216         -
            F: FnMut(T, String, &mut Self) -> Result<T, SerdeError>,
  217         -
        {
  218         -
            Ok(state)
         222  +
            _consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         223  +
        ) -> Result<(), SerdeError> {
         224  +
            Ok(())
  219    225   
        }
  220    226   
  221    227   
        fn read_boolean(&mut self, _schema: &Schema) -> Result<bool, SerdeError> {
  222    228   
            Ok(false)
  223    229   
        }
  224    230   
  225    231   
        fn read_byte(&mut self, _schema: &Schema) -> Result<i8, SerdeError> {
  226    232   
            Ok(0)
  227    233   
        }
  228    234   

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/codec/http_string.rs

@@ -1,1 +60,66 @@
   21     21   
            output: String::new(),
   22     22   
        }
   23     23   
    }
   24     24   
   25     25   
    /// Finalizes the serialization and returns the output string.
   26     26   
    pub fn finish(self) -> String {
   27     27   
        self.output
   28     28   
    }
   29     29   
}
   30     30   
          31  +
impl super::FinishSerializer for HttpStringSerializer {
          32  +
    fn finish(self) -> Vec<u8> {
          33  +
        self.output.into_bytes()
          34  +
    }
          35  +
}
          36  +
   31     37   
impl Default for HttpStringSerializer {
   32     38   
    fn default() -> Self {
   33     39   
        Self::new()
   34     40   
    }
   35     41   
}
   36     42   
   37     43   
impl ShapeSerializer for HttpStringSerializer {
   38     44   
    fn write_struct(
   39     45   
        &mut self,
   40     46   
        _schema: &Schema,
@@ -220,226 +306,310 @@
  240    246   
            Some(&self.input[start..])
  241    247   
        }
  242    248   
    }
  243    249   
  244    250   
    fn current_value(&self) -> &str {
  245    251   
        &self.input[self.position..]
  246    252   
    }
  247    253   
}
  248    254   
  249    255   
impl<'a> ShapeDeserializer for HttpStringDeserializer<'a> {
  250         -
    fn read_struct<T, F>(
         256  +
    fn read_struct(
  251    257   
        &mut self,
  252    258   
        _schema: &Schema,
  253         -
        _state: T,
  254         -
        _consumer: F,
  255         -
    ) -> Result<T, SerdeError>
  256         -
    where
  257         -
        F: FnMut(T, &Schema, &mut Self) -> Result<T, SerdeError>,
  258         -
    {
         259  +
        _consumer: &mut dyn FnMut(&Schema, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         260  +
    ) -> Result<(), SerdeError> {
  259    261   
        Err(SerdeError::UnsupportedOperation {
  260    262   
            message: "structures cannot be deserialized from strings".into(),
  261    263   
        })
  262    264   
    }
  263    265   
  264         -
    fn read_list<T, F>(&mut self, _schema: &Schema, state: T, _consumer: F) -> Result<T, SerdeError>
  265         -
    where
  266         -
        F: FnMut(T, &mut Self) -> Result<T, SerdeError>,
  267         -
    {
         266  +
    fn read_list(
         267  +
        &mut self,
         268  +
        _schema: &Schema,
         269  +
        _consumer: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         270  +
    ) -> Result<(), SerdeError> {
  268    271   
        // Lists are comma-separated values
  269    272   
        // The consumer will call read methods for each element
  270         -
        Ok(state)
         273  +
        Ok(())
  271    274   
    }
  272    275   
  273         -
    fn read_map<T, F>(&mut self, _schema: &Schema, _state: T, _consumer: F) -> Result<T, SerdeError>
  274         -
    where
  275         -
        F: FnMut(T, String, &mut Self) -> Result<T, SerdeError>,
  276         -
    {
         276  +
    fn read_map(
         277  +
        &mut self,
         278  +
        _schema: &Schema,
         279  +
        _consumer: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
         280  +
    ) -> Result<(), SerdeError> {
  277    281   
        Err(SerdeError::UnsupportedOperation {
  278    282   
            message: "maps cannot be deserialized from strings".into(),
  279    283   
        })
  280    284   
    }
  281    285   
  282    286   
    fn read_boolean(&mut self, _schema: &Schema) -> Result<bool, SerdeError> {
  283    287   
        let value = self.next_value().ok_or_else(|| SerdeError::InvalidInput {
  284    288   
            message: "expected boolean value".into(),
  285    289   
        })?;
  286    290   
        value.parse().map_err(|_| SerdeError::InvalidInput {

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/http_protocol.rs

@@ -0,1 +0,31 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
//! HTTP-based client protocol implementations.
           7  +
//!
           8  +
//! This module provides two concrete protocol types that implement [`crate::schema::protocol::ClientProtocol`]
           9  +
//! for HTTP transports:
          10  +
//!
          11  +
//! - [`HttpBindingProtocol`] — for REST-style protocols (e.g., `restJson1`, `restXml`)
          12  +
//!   that split members across HTTP headers, query strings, URI labels, and the payload.
          13  +
//! - [`HttpRpcProtocol`] — for RPC-style protocols (e.g., `awsJson1_0`, `rpcv2Cbor`)
          14  +
//!   that put everything in the body and ignore HTTP bindings.
          15  +
//!
          16  +
//! # Protocol hierarchy
          17  +
//!
          18  +
//! ```text
          19  +
//! ClientProtocol
          20  +
//!   ├─ HttpBindingProtocol<C>   (REST: restJson, restXml)
          21  +
//!   └─ HttpRpcProtocol<C>       (RPC: awsJson, rpcv2Cbor)
          22  +
//! ```
          23  +
//!
          24  +
//! Concrete protocol types like `AwsRestJsonProtocol` are thin wrappers that
          25  +
//! construct one of these with the appropriate codec and settings.
          26  +
          27  +
mod binding;
          28  +
mod rpc;
          29  +
          30  +
pub use binding::{percent_encode, HttpBindingProtocol};
          31  +
pub use rpc::HttpRpcProtocol;

tmp-codegen-diff/aws-sdk/sdk/aws-smithy-schema/src/schema/http_protocol/binding.rs

@@ -0,1 +0,1902 @@
           1  +
/*
           2  +
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
           3  +
 * SPDX-License-Identifier: Apache-2.0
           4  +
 */
           5  +
           6  +
//! HTTP binding protocol for REST-style APIs.
           7  +
           8  +
use crate::codec::{Codec, FinishSerializer};
           9  +
use crate::protocol::ClientProtocol;
          10  +
use crate::serde::{SerdeError, SerializableStruct, ShapeDeserializer, ShapeSerializer};
          11  +
use crate::{Schema, ShapeId};
          12  +
use aws_smithy_runtime_api::http::{Request, Response};
          13  +
use aws_smithy_types::body::SdkBody;
          14  +
use aws_smithy_types::config_bag::ConfigBag;
          15  +
          16  +
/// An HTTP protocol for REST-style APIs that use HTTP bindings.
          17  +
///
          18  +
/// This protocol splits input members between HTTP locations (headers, query
          19  +
/// strings, URI labels) and the payload based on HTTP binding traits
          20  +
/// (`@httpHeader`, `@httpQuery`, `@httpLabel`, `@httpPayload`, etc.).
          21  +
/// Non-bound members are serialized into the body using the provided codec.
          22  +
///
          23  +
/// # Type parameters
          24  +
///
          25  +
/// * `C` — the payload codec (e.g., `JsonCodec`, `XmlCodec`)
          26  +
#[derive(Debug)]
          27  +
pub struct HttpBindingProtocol<C> {
          28  +
    protocol_id: ShapeId,
          29  +
    codec: C,
          30  +
    content_type: &'static str,
          31  +
}
          32  +
          33  +
impl<C: Codec> HttpBindingProtocol<C> {
          34  +
    /// Creates a new HTTP binding protocol.
          35  +
    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  +
          44  +
// Note: there is a percent_encoding crate we use some other places for this, but I'm trying to keep
          45  +
// the dependencies to a minimum.
          46  +
/// Percent-encode a string per RFC 3986 section 2.3 (unreserved characters only).
          47  +
pub 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  +
          64  +
pub(crate) const HEX: &[u8; 16] = b"0123456789ABCDEF";
          65  +
          66  +
/// A ShapeSerializer that intercepts member writes and routes HTTP-bound
          67  +
/// members to headers, query params, or URI labels instead of the body.
          68  +
///
          69  +
/// Members without HTTP binding traits are forwarded to the inner body
          70  +
/// serializer unchanged.
          71  +
struct HttpBindingSerializer<'a, S> {
          72  +
    body: S,
          73  +
    headers: Vec<(String, String)>,
          74  +
    query_params: Vec<(String, String)>,
          75  +
    labels: Vec<(String, String)>,
          76  +
    /// When set, member schemas are resolved from this schema by name to find
          77  +
    /// HTTP binding traits. This allows the protocol to override bindings
          78  +
    /// (e.g., for presigning where body members become query params).
          79  +
    input_schema: Option<&'a Schema>,
          80  +
    /// True for the top-level input struct in serialize_request.
          81  +
    /// Cleared after the first write_struct so nested structs delegate directly.
          82  +
    is_top_level: bool,
          83  +
    /// Raw payload bytes for `@httpPayload` blob/string members. When a member
          84  +
    /// has `@httpPayload` and targets a blob or string, the raw bytes bypass
          85  +
    /// the codec serializer entirely and are used as the HTTP body directly.
          86  +
    /// Safety: the referenced bytes are borrowed from the input struct passed to
          87  +
    /// `serialize_request`, which outlives this serializer.
          88  +
    raw_payload: Option<&'a [u8]>,
          89  +
}
          90  +
          91  +
impl<'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  +
    /// Resolve the effective member schema: if an input_schema override is set,
         105  +
    /// look up the member by name there (to get the correct HTTP bindings).
         106  +
    /// Otherwise use the schema as-is.
         107  +
    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  +
            // Fallback to name lookup for schemas without a member index
         115  +
            input_schema.member_schema(name).unwrap_or(schema)
         116  +
        } else {
         117  +
            schema
         118  +
        }
         119  +
    }
         120  +
}
         121  +
         122  +
impl<'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  +
            // Top-level input struct: route serialize_members through the binder
         130  +
            // so HTTP-bound members are intercepted. The body serializer's
         131  +
            // write_struct is used for framing (e.g., { } for JSON), with a
         132  +
            // proxy whose serialize_members delegates back to the binder.
         133  +
            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  +
                    // SAFETY: The body serializer called serialize_members on
         145  +
                    // this proxy, passing &mut self (body). The binder wraps
         146  +
                    // that same body serializer. We need mutable access to the
         147  +
                    // binder to route writes. This is safe because:
         148  +
                    // 1. The body serializer's write_struct only calls
         149  +
                    //    serialize_members once, synchronously.
         150  +
                    // 2. Body member writes from the binder go back to the
         151  +
                    //    body serializer, which is in a valid state (between
         152  +
                    //    the { and } it emitted).
         153  +
                    self.value.serialize_members(unsafe { &mut *binder })
         154  +
                }
         155  +
            }
         156  +
            // Clear is_top_level so nested write_struct calls (from body members)
         157  +
            // take the else branch and delegate directly to the body serializer.
         158  +
            // input_schema is preserved so resolve_member continues to work.
         159  +
            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  +
            // SAFETY: `proxy` holds a shared reference to `binder` (via &mut that
         166  +
            // we reborrow). We need to call `binder.body.write_struct(schema, &proxy)`
         167  +
            // but can't do so through normal references because `proxy` borrows `binder`.
         168  +
            // The raw pointer dereference is safe because:
         169  +
            // 1. `binder_ptr` points to a valid, live `HttpBindingSerializer` (it was
         170  +
            //    just derived from `proxy.binder`).
         171  +
            // 2. `body.write_struct` is called synchronously and returns before `proxy`
         172  +
            //    is dropped, so the binder is not moved or deallocated.
         173  +
            // 3. The only re-entrant access is through `proxy.serialize_members`, which
         174  +
            //    uses the same raw-pointer pattern with its own safety justification above.
         175  +
            unsafe { (*binder_ptr).body.write_struct(schema, &proxy) }
         176  +
        } else {
         177  +
            // Nested struct (a body member targeting a structure): delegate
         178  +
            // entirely to the body serializer.
         179  +
            let schema = self.resolve_member(schema);
         180  +
            if schema.http_payload().is_some() {
         181  +
                // @httpPayload struct/union: write as the body's top-level object
         182  +
                // without a member name prefix. Use a non-member schema for the
         183  +
                // write_struct call so prefix() doesn't emit a field name.
         184  +
                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  +
        // @httpHeader on a list: collect elements as comma-separated header value
         198  +
        if let Some(header) = schema.http_header() {
         199  +
            let mut collector = ListElementCollector::for_header();
         200  +
            write_elements(&mut collector)?;
         201  +
            // RFC 7230: string values containing commas or quotes need quoting.
         202  +
            // Timestamps are NOT quoted even though http-date contains commas.
         203  +
            let header_val = collector
         204  +
                .values
         205  +
                .iter()
         206  +
                .zip(collector.quotable.iter())
         207  +
                .map(|(s, &quotable)| {
         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  +
        // @httpQuery on a list: add each element as a separate query param
         220  +
        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  +
        // @httpPrefixHeaders: serialize map entries as prefixed headers
         238  +
        if let Some(prefix) = schema.http_prefix_headers() {
         239  +
            // Collect entries via a temporary serializer
         240  +
            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  +
        // @httpQueryParams: serialize map entries as query params
         246  +
        if schema.http_query_params().is_some() {
         247  +
            let mut collector = MapEntryCollector::new(String::new());
         248  +
            write_entries(&mut collector)?;
         249  +
            // Filter out keys that overlap with explicit @httpQuery params
         250  +
            // (query params take precedence over query params map entries)
         251  +
            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  +
            // @mediaType on a header: base64-encode the value
         354  +
            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  +
            // SAFETY: We extend the lifetime of `value.as_bytes()` from its anonymous
         362  +
            // lifetime to `'a`. This is sound because:
         363  +
            // 1. `value` is borrowed from the input struct passed to `serialize_request`.
         364  +
            // 2. `HttpBindingSerializer` is a local variable within `serialize_request`
         365  +
            //    and is dropped before `serialize_request` returns.
         366  +
            // 3. The input struct (and thus `value`) outlives the serializer.
         367  +
            // 4. `raw_payload` is read in `serialize_request` immediately after
         368  +
            //    `serialize_members` returns, before the input is dropped.
         369  +
            // We use transmute rather than copying to avoid allocating for potentially
         370  +
            // multi-GB string payloads.
         371  +
            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  +
            // SAFETY: We extend the lifetime of `value.as_ref()` (a `&[u8]`) from its
         392  +
            // anonymous lifetime to `'a`. This is sound because:
         393  +
            // 1. `value` is borrowed from the input struct passed to `serialize_request`.
         394  +
            // 2. `HttpBindingSerializer` is a local variable within `serialize_request`
         395  +
            //    and is dropped before `serialize_request` returns.
         396  +
            // 3. The input struct (and thus `value`) outlives the serializer.
         397  +
            // 4. `raw_payload` is read in `serialize_request` immediately after
         398  +
            //    `serialize_members` returns, before the input is dropped.
         399  +
            // We use transmute rather than copying to avoid allocating for potentially
         400  +
            // multi-GB blob payloads.
         401  +
            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  +
            // Headers default to http-date, query/label default to date-time
         416  +
            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  +
         455  +
/// Which HTTP location a member is bound to.
         456  +
enum HttpBinding<'a> {
         457  +
    Header(&'a str),
         458  +
    Query(&'a str),
         459  +
    Label,
         460  +
}
         461  +
         462  +
/// Determine the HTTP binding for a member schema, if any.
         463  +
fn 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  +
         476  +
impl<'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  +
/// Collects list element values as strings for @httpHeader and @httpQuery on lists.
         503  +
struct ListElementCollector {
         504  +
    values: Vec<String>,
         505  +
    /// Whether each value should be quoted if it contains commas (strings yes, timestamps no)
         506  +
    quotable: Vec<bool>,
         507  +
    /// Whether this collector is for a header (true) or query param (false).
         508  +
    /// Affects default timestamp format: http-date for headers, date-time for query.
         509  +
    is_header: bool,
         510  +
}
         511  +
         512  +
impl 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  +
         540  +
impl 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  +
            // Default: headers use http-date, query params use date-time
         591  +
            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  +
    // Remaining methods are no-ops for list element collection
         615  +
    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  +
         658  +
/// Format a float for HTTP headers/query/labels.
         659  +
/// Rust's Display writes "inf"/"-inf" but HTTP requires "Infinity"/"-Infinity".
         660  +
fn 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  +
         674  +
fn 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  +
         688  +
/// Collects map key-value pairs written via ShapeSerializer for
         689  +
/// @httpPrefixHeaders and @httpQueryParams.
         690  +
struct MapEntryCollector {
         691  +
    prefix: String,
         692  +
    entries: Vec<(String, String)>,
         693  +
    pending_key: Option<String>,
         694  +
}
         695  +
         696  +
impl MapEntryCollector {
         697  +
    fn new(prefix: String) -> Self {
         698  +
        Self {
         699  +
            prefix,
         700  +
            entries: Vec::new(),
         701  +
            pending_key: None,
         702  +
        }
         703  +
    }
         704  +
}
         705  +
         706  +
impl 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  +
    // All other methods are no-ops — maps in HTTP bindings only have string keys/values.
         718  +
    // Exception: write_list handles Map<String, List<String>> for @httpQueryParams.
         719  +
    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  +
        // Map<String, List<String>>: each list element becomes a separate entry
         728  +
        // with the same key (for @httpQueryParams).
         729  +
        if let Some(key) = self.pending_key.take() {
         730  +
            let mut collector = ListElementCollector::for_query(); // query params context
         731  +
            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  +
         802  +
impl<C> ClientProtocol for HttpBindingProtocol<C>
         803  +
where
         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  +
        // Check if there's an @httpPayload member targeting a structure/union.
         822  +
        // In that case, the payload member's own write_struct provides the body
         823  +
        // framing, so we must not add top-level struct framing.
         824  +
        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  +
            // @httpPayload blob/string — don't use the codec output
         840  +
            Vec::new()
         841  +
        } else {
         842  +
            binder.body.finish()
         843  +
        };
         844  +
         845  +
        // Per the REST-JSON content-type handling spec:
         846  +
        // - If @httpPayload targets a blob/string: send raw bytes, no Content-Type when empty
         847  +
        // - If body members exist (even if all optional and unset): send `{}` with Content-Type
         848  +
        // - If no body members at all (everything is in headers/query/labels): empty body, no Content-Type
         849  +
        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  +
            // Blob/string payload: Content-Type comes from the @httpHeader("Content-Type")
         862  +
            // member if present, or defaults to application/octet-stream for blobs.
         863  +
            // Don't set the protocol's codec content type (e.g., application/json).
         864  +
            false
         865  +
        } else if has_body_members {
         866  +
            // Operation has body members — body includes framing (e.g., `{}`).
         867  +
            // Per the REST-JSON spec, even if all members are optional and unset, send `{}`.
         868  +
            true
         869  +
        } else {
         870  +
            // No body members at all — empty body, no Content-Type.
         871  +
            body = Vec::new();
         872  +
            false
         873  +
        };
         874  +
         875  +
        // Build URI: use @http trait if available (with label substitution from binder),
         876  +
        // otherwise fall back to endpoint with manual label substitution.
         877  +
        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  +
                    // Try greedy label first ({name+}), then regular ({name})
         882  +
                    let greedy = format!("{{{name}+}}");
         883  +
                    if path.contains(&greedy) {
         884  +
                        // Greedy labels: encode each path segment separately, preserve /
         885  +
                        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  +
        // Set HTTP method from @http trait
         941  +
        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)]
         982  +
mod 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  +
        // A struct with body members gets Content-Type
        1219  +
        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  +
        // A struct with no members gets no Content-Type per REST-JSON spec
        1236  +
        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  +
    // -- @httpHeader tests --
        1327  +
        1328  +
    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  +
    // -- @httpQuery tests --
        1431  +
        1432  +
    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  +
    // -- Multiple @httpQuery params --
        1493  +
        1494  +
    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  +
    // -- @httpQuery with percent-encoding --
        1528  +
        1529  +
    #[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  +
    // -- @httpLabel tests --
        1549  +
        1550  +
    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  +
    // -- Combined: @httpHeader + @httpQuery + @httpLabel + body --
        1638  +
        1639  +
    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  +
        // Header
        1690  +
        assert_eq!(request.headers().get("X-Token").unwrap(), "secret");
        1691  +
        // Body contains only the unbound member
        1692  +
        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  +
    // -- @httpPrefixHeaders tests --
        1699  +
        1700  +
    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  +
    // -- @httpQueryParams tests --
        1738  +
        1739  +
    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  +
    // -- Timestamp in header defaults to http-date --
        1776  +
        1777  +
    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  +
        // http-date format: "Thu, 01 Jan 1970 00:00:00 GMT"
        1810  +
        assert!(value.contains("1970"), "expected http-date, got: {value}");
        1811  +
    }
        1812  +
        1813  +
    // -- Timestamp in query defaults to date-time --
        1814  +
        1815  +
    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  +
    // -- Unbound members go to body, bound members do not --
        1853  +
        1854  +
    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  +
}