Skip to main content

aws_smithy_http_server/schema/
event_stream.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Schema-driven event stream marshalling and unmarshalling.
7//!
8//! Frames are built and read against the shapes' runtime [`Schema`]s instead of generated
9//! per-shape marshallers: member schemas carry the `@eventHeader` and `@eventPayload` traits,
10//! and the payload travels through the codec of whichever protocol routing selected. Generated
11//! code contributes only the pieces that need shape types — a [`DeserializableEventStream`]
12//! dispatch per stream union and a [`SerializableEventError`] dispatch per error enum — and
13//! aliases its `*Marshaller`/`*Unmarshaller` types to the generics here.
14//!
15//! The event framing follows the AWS event stream contract:
16//! - a data event carries `:message-type: event` and `:event-type` (the Smithy member name),
17//! - an in-band error carries `:message-type: exception` and `:exception-type`,
18//! - `@eventHeader` members become message headers, an `@eventPayload` member becomes the
19//!   payload (raw bytes for blobs, UTF-8 for strings, codec-encoded otherwise). Without an
20//!   explicit payload member, non-header members form a codec-encoded payload document,
21//! - `:content-type` labels the payload: `application/octet-stream` and `text/plain` are fixed
22//!   by the member's shape, everything else is the selected protocol's event stream media type.
23
24use std::fmt;
25use std::marker::PhantomData;
26use std::sync::OnceLock;
27
28use aws_smithy_eventstream::error::Error;
29use aws_smithy_eventstream::frame::{
30    write_message_to, MarshallMessage, NoOpSigner, UnmarshallMessage, UnmarshalledMessage,
31};
32use aws_smithy_eventstream::smithy as expect_fns;
33use aws_smithy_http::event_stream::{EventStreamSender, InitialMessageType};
34use aws_smithy_schema::serde::{SerdeError, SerializableStruct, ShapeDeserializer, ShapeSerializer};
35use aws_smithy_schema::{intern_header_name, Schema, ShapeType};
36use aws_smithy_types::event_stream::{Header, HeaderValue, Message};
37use aws_smithy_types::str_bytes::StrBytes;
38use aws_smithy_types::{BigDecimal, BigInteger, Blob, DateTime, Document};
39use bytes::Bytes;
40
41use crate::body::{boxed, BoxBody};
42use crate::schema::{DeserializeError, EventStreamFraming, SharedServerProtocol};
43
44// ---------------------------------------------------------------------------
45// Operation glue
46// ---------------------------------------------------------------------------
47
48/// Applies the `initial-request` frame, when the protocol carries one, to the operation input
49/// being built.
50///
51/// `apply` receives a codec deserializer over the frame's payload and reads the non-stream
52/// input members into the caller's builder. When the protocol does not frame initial messages,
53/// or the stream starts with an ordinary event, nothing is consumed and `apply` is not called.
54///
55/// `recv_initial` reads the frame — pass the receiver's `try_recv_initial`, e.g.
56/// `|message_type| receiver.try_recv_initial(message_type)`. Taking a closure rather than
57/// `&mut Receiver` keeps this usable with generated receiver wrappers (such as the SigV4
58/// unwrapping receiver) that expose the same method on a different type.
59pub async fn apply_initial_request<Fut, Err>(
60    recv_initial: impl FnOnce(InitialMessageType) -> Fut,
61    protocol: &SharedServerProtocol,
62    apply: impl FnOnce(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
63) -> Result<(), DeserializeError>
64where
65    Fut: std::future::Future<Output = Result<Option<Message>, Err>>,
66    Err: fmt::Display,
67{
68    let capability = protocol
69        .event_stream_framing()
70        .ok_or_else(|| DeserializeError::Serde(SerdeError::custom(NO_EVENT_STREAM_SUPPORT)))?;
71    if !capability.initial_messages_in_frames {
72        return Ok(());
73    }
74    match recv_initial(InitialMessageType::Request).await {
75        Ok(Some(initial)) => {
76            let mut deser = capability.payload_codec.create_deserializer(&initial.payload()[..]);
77            apply(&mut *deser)?;
78            Ok(())
79        }
80        Ok(None) => Ok(()),
81        Err(err) => Err(DeserializeError::InitialRequestFrame(err.to_string())),
82    }
83}
84
85/// Whether to emit an initial response before output events.
86///
87/// Additional policies may be introduced in the future.
88#[non_exhaustive]
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub enum InitialResponsePolicy {
91    /// Emit an initial response before the events, using the protocol's initial-message payload.
92    Send,
93    /// Omit the initial response.
94    Omit,
95}
96
97/// Builds the response body of an event stream output: the marshalled events, preceded by an
98/// `initial-response` frame when `initial_response` is [`InitialResponsePolicy::Send`].
99///
100/// Protocols that carry non-stream members in initial messages serialize those members into
101/// the frame. HTTP-bound protocols use their unset payload instead, matching generated serde servers.
102///
103/// `output_schema` and `output` describe the non-stream members; the streaming member must
104/// already have been moved out of `output` into `events`.
105pub fn event_stream_response_body<T, E>(
106    output_schema: &Schema<'_>,
107    output: &dyn SerializableStruct,
108    events: EventStreamSender<T, E>,
109    marshaller: impl MarshallMessage<Input = T> + Send + Sync + 'static,
110    error_marshaller: impl MarshallMessage<Input = E> + Send + Sync + 'static,
111    protocol: &SharedServerProtocol,
112    initial_response: InitialResponsePolicy,
113) -> Result<BoxBody, SerdeError>
114where
115    T: Send + Sync + 'static,
116    E: std::error::Error + Send + Sync + 'static,
117{
118    let capability = protocol
119        .event_stream_framing()
120        .ok_or_else(|| SerdeError::custom(NO_EVENT_STREAM_SUPPORT))?;
121    let signer = NoOpSigner {};
122    if initial_response == InitialResponsePolicy::Send {
123        use futures_util::StreamExt;
124        let payload = if capability.initial_messages_in_frames {
125            let mut ser = capability.payload_codec.create_serializer();
126            ser.write_struct(output_schema, output)?;
127            Bytes::from(ser.finish_boxed())
128        } else {
129            Bytes::from_static(capability.unset_initial_response_payload)
130        };
131        let initial_message = Message::new_from_parts(
132            vec![
133                Header::new(":message-type", HeaderValue::String("event".into())),
134                Header::new(":event-type", HeaderValue::String("initial-response".into())),
135                Header::new(
136                    ":content-type",
137                    HeaderValue::String(capability.media_type.to_string().into()),
138                ),
139            ],
140            payload,
141        );
142        // Encode the initial frame up front and chain it ahead of the event frames, as the
143        // generated marshallers do, so events are not wrapped and dispatched per frame.
144        // The no-op signer leaves the frame unchanged.
145        let mut initial_frame = Vec::new();
146        write_message_to(&initial_message, &mut initial_frame)
147            .map_err(|err| SerdeError::custom(format!("failed to write the initial-response frame: {err}")))?;
148        let initial = futures_util::stream::iter([Ok(http_body::Frame::data(Bytes::from(initial_frame)))]);
149        let adapter = events.into_body_stream(marshaller, error_marshaller, signer);
150        Ok(boxed(http_body_util::StreamBody::new(initial.chain(adapter))))
151    } else {
152        let adapter = events.into_body_stream(marshaller, error_marshaller, signer);
153        Ok(boxed(http_body_util::StreamBody::new(adapter)))
154    }
155}
156
157// ---------------------------------------------------------------------------
158// Marshalling
159// ---------------------------------------------------------------------------
160
161/// Marshals the events of a schema-mode event stream union into frames.
162///
163/// `T` is the generated stream union; its [`SerializableStruct::serialize_members`] names the
164/// active variant, and the variant's member schemas drive header and payload placement.
165pub struct SchemaEventMarshaller<T> {
166    protocol: SharedServerProtocol,
167    /// Header strings per union variant, indexed by variant member index and filled on first
168    /// use, so each event clones shared names rather than allocating them.
169    variants: OnceLock<Box<[OnceLock<FrameStrings>]>>,
170    _phantom: PhantomData<fn() -> T>,
171}
172
173impl<T> SchemaEventMarshaller<T> {
174    /// Creates a marshaller that frames events through `protocol`'s event stream capability.
175    pub fn new(protocol: SharedServerProtocol) -> Self {
176        Self {
177            protocol,
178            variants: OnceLock::new(),
179            _phantom: PhantomData,
180        }
181    }
182}
183
184impl<T> fmt::Debug for SchemaEventMarshaller<T> {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        f.debug_struct("SchemaEventMarshaller")
187            .field("protocol", &self.protocol)
188            .finish()
189    }
190}
191
192impl<T: SerializableStruct> MarshallMessage for SchemaEventMarshaller<T> {
193    type Input = T;
194
195    fn marshall(&self, input: Self::Input) -> Result<Message, Error> {
196        // A protocol's framing support is fixed, so it is checked once; later frames look the
197        // framing up only when they need it.
198        let variants = match self.variants.get() {
199            Some(variants) => variants,
200            None => {
201                capability_or_marshalling_error(&self.protocol)?;
202                self.variants
203                    .get_or_init(|| input.schema().members().iter().map(|_| OnceLock::new()).collect())
204            }
205        };
206        let mut serializer = UnionVariantSerializer {
207            framing: Framing::Deferred(&self.protocol),
208            kind: FrameKind::Event,
209            variants,
210            message: None,
211        };
212        input
213            .serialize_members(&mut serializer)
214            .map_err(|err| Error::marshalling(format!("{err}")))?;
215        serializer
216            .message
217            .ok_or_else(|| Error::marshalling("event stream union serialized no variant".to_owned()))
218    }
219}
220
221/// Names the active variant of a generated event stream error enum.
222///
223/// Implemented by generated code; the returned name is the original Smithy member name of the
224/// error in the stream union, which becomes the frame's `:exception-type`.
225pub trait SerializableEventError {
226    /// Returns the `:exception-type` value, target schema, and modeled error to frame.
227    fn variant(&self) -> (&'static str, &Schema<'_>, &dyn SerializableStruct);
228}
229
230/// Marshals the modeled errors of a schema-mode event stream union into `exception` frames.
231pub struct SchemaEventErrorMarshaller<E> {
232    protocol: SharedServerProtocol,
233    _phantom: PhantomData<fn() -> E>,
234}
235
236impl<E> SchemaEventErrorMarshaller<E> {
237    /// Creates an error marshaller that frames errors through `protocol`'s event stream capability.
238    pub fn new(protocol: SharedServerProtocol) -> Self {
239        Self {
240            protocol,
241            _phantom: PhantomData,
242        }
243    }
244}
245
246impl<E> fmt::Debug for SchemaEventErrorMarshaller<E> {
247    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248        f.debug_struct("SchemaEventErrorMarshaller")
249            .field("protocol", &self.protocol)
250            .finish()
251    }
252}
253
254impl<E: SerializableEventError> MarshallMessage for SchemaEventErrorMarshaller<E> {
255    type Input = E;
256
257    fn marshall(&self, input: Self::Input) -> Result<Message, Error> {
258        let capability = capability_or_marshalling_error(&self.protocol)?;
259        let (exception_type, schema, value) = input.variant();
260        build_frame(
261            Framing::Known(capability),
262            exception_type,
263            schema,
264            value,
265            FrameKind::Exception,
266            None,
267        )
268        .map_err(|err| Error::marshalling(format!("{err}")))
269    }
270}
271
272/// The error marshaller of a stream union with no modeled `@error` members.
273///
274/// Only [`aws_smithy_http::event_stream::MessageStreamError`] can reach it, and that error is
275/// not modeled on the wire: the frame carries `:message-type: exception` and nothing else.
276#[derive(Debug)]
277pub struct NoModeledEventErrorMarshaller {
278    protocol: SharedServerProtocol,
279}
280
281impl NoModeledEventErrorMarshaller {
282    /// Creates the marshaller. The protocol is only used to reject protocols without event
283    /// stream support, keeping the constructor uniform with the modeled marshallers.
284    pub fn new(protocol: SharedServerProtocol) -> Self {
285        Self { protocol }
286    }
287}
288
289impl MarshallMessage for NoModeledEventErrorMarshaller {
290    type Input = aws_smithy_http::event_stream::MessageStreamError;
291
292    fn marshall(&self, _input: Self::Input) -> Result<Message, Error> {
293        capability_or_marshalling_error(&self.protocol)?;
294        let headers = vec![Header::new(":message-type", HeaderValue::String("exception".into()))];
295        Ok(Message::new_from_parts(headers, Bytes::new()))
296    }
297}
298
299// ---------------------------------------------------------------------------
300// Unmarshalling
301// ---------------------------------------------------------------------------
302
303/// A schema-mode event stream union that can be read back from frames.
304///
305/// Implemented by generated code: both methods dispatch on the member name and hand
306/// [`EventFrame::deserializer`] to the event struct's schema-guided walker, returning
307/// `Ok(None)` for names the union does not model.
308pub trait DeserializableEventStream: Sized {
309    /// The generated error type of the stream union, or
310    /// [`MessageStreamError`](aws_smithy_http::event_stream::MessageStreamError) when the union
311    /// models no errors.
312    type Error;
313
314    /// Reads the event named by the frame's `:event-type`.
315    fn deserialize_event(event_type: &str, frame: &EventFrame<'_>) -> Result<Option<Self>, Error>;
316
317    /// Reads the error named by the frame's `:exception-type`.
318    fn deserialize_error(exception_type: &str, frame: &EventFrame<'_>) -> Result<Option<Self::Error>, Error>;
319}
320
321/// Unmarshals schema-mode event stream frames into a generated stream union.
322pub struct SchemaEventUnmarshaller<T> {
323    protocol: SharedServerProtocol,
324    _phantom: PhantomData<fn() -> T>,
325}
326
327impl<T> SchemaEventUnmarshaller<T> {
328    /// Creates an unmarshaller that reads payloads through `protocol`'s event stream capability.
329    pub fn new(protocol: SharedServerProtocol) -> Self {
330        Self {
331            protocol,
332            _phantom: PhantomData,
333        }
334    }
335}
336
337impl<T> fmt::Debug for SchemaEventUnmarshaller<T> {
338    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
339        f.debug_struct("SchemaEventUnmarshaller")
340            .field("protocol", &self.protocol)
341            .finish()
342    }
343}
344
345impl<T: DeserializableEventStream> UnmarshallMessage for SchemaEventUnmarshaller<T> {
346    type Output = T;
347    type Error = T::Error;
348
349    fn unmarshall(&self, message: &Message) -> Result<UnmarshalledMessage<Self::Output, Self::Error>, Error> {
350        let capability = self
351            .protocol
352            .event_stream_framing()
353            .ok_or_else(|| Error::unmarshalling(NO_EVENT_STREAM_SUPPORT))?;
354        let response_headers = expect_fns::parse_response_headers(message)?;
355        let kind = match response_headers.message_type.as_str() {
356            "event" => FrameKind::Event,
357            "exception" => FrameKind::Exception,
358            value => return Err(Error::unmarshalling(format!("unrecognized :message-type: {value}"))),
359        };
360        let frame = EventFrame {
361            message,
362            capability,
363            kind,
364            content_type: response_headers.content_type,
365        };
366        match kind {
367            FrameKind::Event => {
368                let event_type = response_headers.smithy_type.as_str();
369                match T::deserialize_event(event_type, &frame)? {
370                    Some(event) => Ok(UnmarshalledMessage::Event(event)),
371                    None => Err(Error::unmarshalling(format!("unrecognized :event-type: {event_type}"))),
372                }
373            }
374            FrameKind::Exception => {
375                let exception_type = response_headers.smithy_type.as_str();
376                match T::deserialize_error(exception_type, &frame)? {
377                    Some(error) => Ok(UnmarshalledMessage::Error(error)),
378                    None => Err(Error::unmarshalling(format!(
379                        "unrecognized exception: {exception_type}"
380                    ))),
381                }
382            }
383        }
384    }
385}
386
387/// One received event stream frame together with the protocol capability that decodes it.
388pub struct EventFrame<'a> {
389    message: &'a Message,
390    capability: EventStreamFraming<'a>,
391    kind: FrameKind,
392    content_type: Option<&'a StrBytes>,
393}
394
395impl<'a> EventFrame<'a> {
396    /// Creates a frame view over `message`.
397    ///
398    /// Protocol header lookups select the last occurrence, as in generated serde.
399    /// [`SchemaEventUnmarshaller`] also validates the protocol headers before dispatching.
400    pub fn new(message: &'a Message, capability: EventStreamFraming<'a>) -> Self {
401        let last_string_header = |name: &str| {
402            message
403                .headers()
404                .iter()
405                .rfind(|header| header.name().as_str() == name)
406                .and_then(|header| header.value().as_string().ok())
407        };
408        let kind = match last_string_header(":message-type").map(StrBytes::as_str) {
409            Some("exception") => FrameKind::Exception,
410            _ => FrameKind::Event,
411        };
412        Self {
413            message,
414            capability,
415            kind,
416            content_type: last_string_header(":content-type"),
417        }
418    }
419
420    /// Returns a [`ShapeDeserializer`] that reads an event struct from this frame, routing
421    /// `@eventHeader` members from the message headers and the payload through the codec.
422    pub fn deserializer(&self) -> EventFrameDeserializer<'a> {
423        EventFrameDeserializer {
424            message: self.message,
425            capability: self.capability,
426            kind: self.kind,
427            content_type: self.content_type,
428        }
429    }
430}
431
432/// Reads an event struct from a frame, guided by the struct's schema.
433///
434/// Only [`ShapeDeserializer::read_struct`] is meaningful at the top level; the member values it
435/// hands to the consumer come from the message headers or from a codec deserializer over the
436/// payload.
437pub struct EventFrameDeserializer<'a> {
438    message: &'a Message,
439    capability: EventStreamFraming<'a>,
440    kind: FrameKind,
441    content_type: Option<&'a StrBytes>,
442}
443
444impl EventFrameDeserializer<'_> {
445    fn check_content_type(&self, expected: &str) -> Result<(), SerdeError> {
446        let content_type = self.content_type.map(StrBytes::as_str).unwrap_or_default();
447        if content_type != expected {
448            return Err(SerdeError::custom(format!(
449                "expected :content-type to be '{expected}', but was '{content_type}'"
450            )));
451        }
452        Ok(())
453    }
454}
455
456/// Returns the `<Error>` element of a REST XML error envelope, with the rules of the generated serde
457/// `rest_xml_wrapped_errors::error_scope`: the root must be `<ErrorResponse>`, and its first
458/// `<Error>` child holds the error members.
459fn wrapped_xml_error_element(payload: &[u8]) -> Result<&[u8], SerdeError> {
460    let mut document =
461        aws_smithy_xml::decode::Document::try_from(payload).map_err(|err| SerdeError::custom(err.to_string()))?;
462    let root = document
463        .next_start_element()
464        .ok_or_else(|| SerdeError::custom("no root found searching for an Error"))?;
465    if !root.matches("ErrorResponse") {
466        return Err(SerdeError::custom("expected ErrorResponse as root"));
467    }
468    aws_smithy_xml::codec::find_depth2_element_slice_by(payload, |name| name == "Error")
469        .ok_or_else(|| SerdeError::custom("no error found inside of ErrorResponse"))
470}
471
472/// Checks a `@restXml(noErrorWrapping: true)` error, with the rules of the generated serde
473/// `rest_xml_unwrapped_errors::error_scope`: the root must be `<Error>`.
474fn unwrapped_xml_error_element(payload: &[u8]) -> Result<&[u8], SerdeError> {
475    let mut document =
476        aws_smithy_xml::decode::Document::try_from(payload).map_err(|err| SerdeError::custom(err.to_string()))?;
477    let root = document
478        .root_element()
479        .map_err(|err| SerdeError::custom(err.to_string()))?;
480    if !root.start_el().matches("Error") {
481        return Err(SerdeError::custom("expected error as root"));
482    }
483    Ok(payload)
484}
485
486macro_rules! frames_deserialize_structs {
487    ($($fn_name:ident() -> $result_type:ty,)+) => {
488        $(fn $fn_name(&mut self, _schema: &Schema<'_>) -> Result<$result_type, SerdeError> {
489            Err(SerdeError::unsupported(
490                "an event stream frame deserializes a structure",
491            ))
492        })+
493    };
494}
495
496impl ShapeDeserializer for EventFrameDeserializer<'_> {
497    fn read_struct(
498        &mut self,
499        schema: &Schema<'_>,
500        state: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
501    ) -> Result<(), SerdeError> {
502        let members = schema.members();
503        // Don't attempt to parse the payload for an empty struct. The payload can be empty, or
504        // if the model was updated since the code was generated, it can have content that would
505        // not be understood.
506        if members.is_empty() {
507            return Ok(());
508        }
509        if let Some(codec) = self
510            .capability
511            .xml_exception_codec
512            .filter(|_| matches!(self.kind, FrameKind::Exception))
513        {
514            // Like generated serde restXml servers: an empty payload is an error with no members set, and
515            // anything else must be the configured error envelope.
516            let payload = &self.message.payload()[..];
517            if payload.is_empty() {
518                return Ok(());
519            }
520            let error = if self.capability.xml_exceptions_wrapped {
521                wrapped_xml_error_element(payload)?
522            } else {
523                unwrapped_xml_error_element(payload)?
524            };
525            return codec.create_deserializer(error).read_struct(schema, state);
526        }
527        let payload_member = members.iter().copied().find(|m| m.event_payload().is_some());
528        let has_header_members = members.iter().any(|m| m.event_header().is_some());
529        if payload_member.is_none() && !has_header_members {
530            // No event traits at all: the whole struct is codec-encoded in the payload.
531            let mut deser = self
532                .capability
533                .payload_codec
534                .create_deserializer(&self.message.payload()[..]);
535            return deser.read_struct(schema, state);
536        }
537
538        if has_header_members {
539            for header in self.message.headers() {
540                let name = header.name().as_str();
541                let member = members
542                    .iter()
543                    .copied()
544                    .find(|m| m.event_header().is_some() && m.member_name() == Some(name));
545                match member {
546                    Some(member) => {
547                        state(member, &mut EventHeaderDeserializer { header })?;
548                    }
549                    // Event stream protocol headers start with ':'
550                    None if !name.starts_with(':') => {
551                        tracing::trace!("Unrecognized event stream message header: {}", name);
552                    }
553                    None => {}
554                }
555            }
556        }
557
558        if let Some(member) = payload_member {
559            match member.shape_type() {
560                ShapeType::Blob => {
561                    self.check_content_type("application/octet-stream")?;
562                    state(
563                        member,
564                        &mut RawPayloadDeserializer {
565                            payload: self.message.payload(),
566                        },
567                    )?;
568                }
569                ShapeType::String => {
570                    self.check_content_type("text/plain")?;
571                    state(
572                        member,
573                        &mut RawPayloadDeserializer {
574                            payload: self.message.payload(),
575                        },
576                    )?;
577                }
578                _ => {
579                    let mut deser = self
580                        .capability
581                        .payload_codec
582                        .create_deserializer(&self.message.payload()[..]);
583                    state(member, &mut *deser)?;
584                }
585            }
586        } else if members
587            .iter()
588            .any(|m| m.event_header().is_none() && m.event_payload().is_none())
589        {
590            // Header-bearing events without an explicit payload can carry no document.
591            // Keep the decoded headers and leave ordinary members unset, as generated serde does.
592            if self.message.payload().is_empty() {
593                return Ok(());
594            }
595            // Generated unmarshallers parse the whole implicit payload structure after
596            // applying event headers. Body copies therefore overwrite header values and
597            // undergo the same type validation as ordinary document members.
598            let mut deser = self
599                .capability
600                .payload_codec
601                .create_deserializer(&self.message.payload()[..]);
602            deser.read_struct(schema, state)?;
603        }
604        Ok(())
605    }
606
607    fn read_list(
608        &mut self,
609        _schema: &Schema<'_>,
610        _state: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
611    ) -> Result<(), SerdeError> {
612        Err(SerdeError::unsupported(
613            "an event stream frame deserializes a structure",
614        ))
615    }
616
617    fn read_map(
618        &mut self,
619        _schema: &Schema<'_>,
620        _state: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
621    ) -> Result<(), SerdeError> {
622        Err(SerdeError::unsupported(
623            "an event stream frame deserializes a structure",
624        ))
625    }
626
627    frames_deserialize_structs!(
628        read_boolean() -> bool,
629        read_byte() -> i8,
630        read_short() -> i16,
631        read_integer() -> i32,
632        read_long() -> i64,
633        read_float() -> f32,
634        read_double() -> f64,
635        read_big_integer() -> BigInteger,
636        read_big_decimal() -> BigDecimal,
637        read_string() -> String,
638        read_blob() -> Blob,
639        read_timestamp() -> DateTime,
640        read_document() -> Document,
641    );
642
643    fn is_null(&self) -> bool {
644        false
645    }
646
647    fn container_size(&self) -> Option<usize> {
648        None
649    }
650}
651
652const NO_EVENT_STREAM_SUPPORT: &str = "protocol does not support event streams";
653
654fn capability_or_marshalling_error(protocol: &SharedServerProtocol) -> Result<EventStreamFraming<'_>, Error> {
655    protocol
656        .event_stream_framing()
657        .ok_or_else(|| Error::marshalling(NO_EVENT_STREAM_SUPPORT.to_owned()))
658}
659
660/// A protocol's event stream framing, either already known or looked up when first needed.
661///
662/// The lookup is a dynamic call, which is measurable per event; frames with cached header
663/// strings and raw payloads never need it.
664#[derive(Clone, Copy)]
665enum Framing<'a> {
666    Known(EventStreamFraming<'a>),
667    Deferred(&'a SharedServerProtocol),
668}
669
670impl<'a> Framing<'a> {
671    fn get(self) -> Result<EventStreamFraming<'a>, SerdeError> {
672        match self {
673            Self::Known(capability) => Ok(capability),
674            Self::Deferred(protocol) => protocol
675                .event_stream_framing()
676                .ok_or_else(|| SerdeError::custom(NO_EVENT_STREAM_SUPPORT)),
677        }
678    }
679}
680
681#[derive(Clone, Copy)]
682enum FrameKind {
683    Event,
684    Exception,
685}
686
687/// `:content-type` of a frame whose payload member has `payload_type`, or whose whole struct
688/// is the payload when `None`.
689fn content_type(payload_type: Option<ShapeType>, media_type: &str) -> StrBytes {
690    match payload_type {
691        Some(ShapeType::Blob) => "application/octet-stream".into(),
692        Some(ShapeType::String) => "text/plain".into(),
693        _ => media_type.to_owned().into(),
694    }
695}
696
697/// The header strings of one event struct's frames, derived from its schema once.
698///
699/// Schema strings are only borrowed for `'_`, so building a header from them allocates, and
700/// sharing an owned copy costs two atomic reference count updates per header. Interned
701/// `'static` strings make each header free to build. The interner is bounded by the distinct
702/// names in the process's models.
703struct FrameStrings {
704    event_type: &'static str,
705    content_type: &'static str,
706    /// `@eventHeader` member names, indexed by member index.
707    header_names: Box<[Option<&'static str>]>,
708}
709
710impl FrameStrings {
711    fn new(event_type: &str, schema: &Schema<'_>, capability: EventStreamFraming<'_>) -> Self {
712        let members = schema.members();
713        let payload_type = members
714            .iter()
715            .find(|m| m.event_payload().is_some())
716            .map(|m| m.shape_type());
717        let mut header_names = vec![None; members.len()];
718        for member in members.iter().filter(|m| m.event_header().is_some()) {
719            if let (Some(index), Some(name)) = (member.member_index(), member.member_name()) {
720                if let Some(slot) = header_names.get_mut(index) {
721                    *slot = Some(intern_header_name(name));
722                }
723            }
724        }
725        Self {
726            event_type: intern_header_name(event_type),
727            content_type: intern_header_name(content_type(payload_type, capability.media_type).as_str()),
728            header_names: header_names.into(),
729        }
730    }
731}
732
733/// Pushes a header whose name and value are both `'static`.
734///
735/// Callers push from separate branches rather than first selecting a [`StrBytes`]: moving a
736/// branch-selected value into the header enum copies it through misaligned stack slots, which
737/// stalls on store forwarding and measurably slows per-event marshalling.
738fn push_static_header(headers: &mut Vec<Header>, name: &'static str, value: &'static str) {
739    headers.push(Header::new(name, HeaderValue::String(value.into())));
740}
741
742/// Builds one event or exception frame from a serializable event struct.
743///
744/// `strings`, when given, must have been derived from `schema`; it only avoids allocations.
745#[inline]
746fn build_frame(
747    framing: Framing<'_>,
748    event_type: &str,
749    schema: &Schema<'_>,
750    value: &dyn SerializableStruct,
751    kind: FrameKind,
752    strings: Option<&FrameStrings>,
753) -> Result<Message, SerdeError> {
754    let (message_type, type_header) = match kind {
755        FrameKind::Event => ("event", ":event-type"),
756        FrameKind::Exception => ("exception", ":exception-type"),
757    };
758    // Most frames add a modeled header and content type to these two protocol
759    // headers. Match the generated marshaller's initial capacity to avoid growth.
760    let mut headers = Vec::with_capacity(4);
761    headers.push(Header::new(":message-type", HeaderValue::String(message_type.into())));
762    match strings {
763        Some(strings) if strings.event_type == event_type => {
764            push_static_header(&mut headers, type_header, strings.event_type)
765        }
766        _ => headers.push(Header::new(
767            type_header,
768            HeaderValue::String(StrBytes::copy_from_str(event_type)),
769        )),
770    }
771    let push_content_type = |headers: &mut Vec<Header>, payload_type| -> Result<(), SerdeError> {
772        match strings {
773            Some(strings) => push_static_header(headers, ":content-type", strings.content_type),
774            None => headers.push(Header::new(
775                ":content-type",
776                HeaderValue::String(content_type(payload_type, framing.get()?.media_type)),
777            )),
778        }
779        Ok(())
780    };
781    let payload_member = schema.members().iter().copied().find(|m| m.event_payload().is_some());
782    let has_header_members = schema.members().iter().any(|m| m.event_header().is_some());
783
784    if payload_member.is_none() && !has_header_members {
785        // No event traits at all (including empty structs): the whole struct is the payload,
786        // encoded by the selected protocol's codec.
787        let capability = framing.get()?;
788        push_content_type(&mut headers, None)?;
789        let framed = capability
790            .exception_discriminator
791            .filter(|_| matches!(kind, FrameKind::Exception))
792            .map(|discriminator| discriminator.frame(schema, value));
793        let value = framed
794            .as_ref()
795            .map(|framed| framed as &dyn SerializableStruct)
796            .unwrap_or(value);
797        let projected = ExceptionBody(value);
798        let value = if capability.exception_http_bindings && matches!(kind, FrameKind::Exception) {
799            &projected as &dyn SerializableStruct
800        } else {
801            value
802        };
803        let mut ser = capability.payload_codec.create_serializer();
804        ser.write_struct(schema, value)?;
805        let payload = Bytes::from(ser.finish_boxed());
806        return Ok(Message::new_from_parts(headers, payload));
807    }
808
809    let mut payload = None;
810    {
811        let mut member_ser = EventMemberSerializer {
812            framing,
813            header_names: strings.map(|strings| &strings.header_names[..]).unwrap_or_default(),
814            headers: &mut headers,
815            payload: &mut payload,
816        };
817        value.serialize_members(&mut member_ser)?;
818    }
819    match payload_member {
820        Some(member) => {
821            // `:content-type` is decided by the payload member's shape, not by whether the
822            // optional value was present, matching the generated marshallers this replaces.
823            push_content_type(&mut headers, Some(member.shape_type()))?;
824            Ok(Message::new_from_parts(headers, payload.unwrap_or_default()))
825        }
826        // Generated serde marshallers emit an empty payload whenever headers are modeled and
827        // there is no explicit payload, even if ordinary members are also present.
828        None => Ok(Message::new_from_parts(headers, Bytes::new())),
829    }
830}
831
832/// REST exception frames use the same document members as HTTP modeled errors.
833struct ExceptionBody<'a>(&'a dyn SerializableStruct);
834impl SerializableStruct for ExceptionBody<'_> {
835    fn schema(&self) -> &Schema<'_> {
836        self.0.schema()
837    }
838    fn serialize_members(&self, serializer: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
839        self.0.serialize_members(&mut ExceptionBodySerializer(serializer))
840    }
841}
842struct ExceptionBodySerializer<'a>(&'a mut dyn ShapeSerializer);
843macro_rules! exception_body_members {
844    ($($method:ident($($argument:ident: $ty:ty),*);)*) => {
845        $(fn $method(&mut self, schema: &Schema<'_>, $($argument: $ty),*) -> Result<(), SerdeError> {
846            if schema.http_header().is_some() || schema.http_prefix_headers().is_some() || schema.http_response_code().is_some() {
847                Ok(())
848            } else {
849                self.0.$method(schema, $($argument),*)
850            }
851        })*
852    };
853}
854impl ShapeSerializer for ExceptionBodySerializer<'_> {
855    exception_body_members! {
856        write_struct(value: &dyn SerializableStruct);
857        write_list(elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>);
858        write_map(entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>);
859        write_boolean(value: bool);
860        write_byte(value: i8);
861        write_short(value: i16);
862        write_integer(value: i32);
863        write_long(value: i64);
864        write_float(value: f32);
865        write_double(value: f64);
866        write_big_integer(value: &BigInteger);
867        write_big_decimal(value: &BigDecimal);
868        write_string(value: &str);
869        write_blob(value: Blob);
870        write_timestamp(value: &DateTime);
871        write_document(value: &Document);
872        write_null();
873    }
874}
875
876/// Receives the single `write_struct` a stream union's `serialize_members` makes for its active
877/// variant and builds the frame from it.
878struct UnionVariantSerializer<'a> {
879    framing: Framing<'a>,
880    kind: FrameKind,
881    variants: &'a [OnceLock<FrameStrings>],
882    message: Option<Message>,
883}
884
885macro_rules! not_a_union_variant {
886    ($($fn_name:ident($value_type:ty),)+) => {
887        $(fn $fn_name(&mut self, _schema: &Schema<'_>, _value: $value_type) -> Result<(), SerdeError> {
888            Err(SerdeError::unsupported(
889                "an event stream union serializes exactly one structure variant",
890            ))
891        })+
892    };
893}
894
895impl ShapeSerializer for UnionVariantSerializer<'_> {
896    fn write_struct(&mut self, schema: &Schema<'_>, value: &dyn SerializableStruct) -> Result<(), SerdeError> {
897        let event_type = schema
898            .member_name()
899            .ok_or_else(|| SerdeError::unsupported("event stream union variant schema has no member name"))?;
900        let strings = match schema.member_index().and_then(|index| self.variants.get(index)) {
901            Some(slot) => Some(match slot.get() {
902                Some(strings) => strings,
903                None => {
904                    let capability = self.framing.get()?;
905                    slot.get_or_init(|| FrameStrings::new(event_type, value.schema(), capability))
906                }
907            }),
908            None => None,
909        };
910        self.message = Some(build_frame(
911            self.framing,
912            event_type,
913            value.schema(),
914            value,
915            self.kind,
916            strings,
917        )?);
918        Ok(())
919    }
920
921    fn write_list(
922        &mut self,
923        _schema: &Schema<'_>,
924        _write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
925    ) -> Result<(), SerdeError> {
926        Err(SerdeError::unsupported(
927            "an event stream union serializes exactly one structure variant",
928        ))
929    }
930
931    fn write_map(
932        &mut self,
933        _schema: &Schema<'_>,
934        _write_entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
935    ) -> Result<(), SerdeError> {
936        Err(SerdeError::unsupported(
937            "an event stream union serializes exactly one structure variant",
938        ))
939    }
940
941    not_a_union_variant!(
942        write_boolean(bool),
943        write_byte(i8),
944        write_short(i16),
945        write_integer(i32),
946        write_long(i64),
947        write_float(f32),
948        write_double(f64),
949        write_big_integer(&BigInteger),
950        write_big_decimal(&BigDecimal),
951        write_string(&str),
952        write_blob(Blob),
953        write_timestamp(&DateTime),
954        write_document(&Document),
955    );
956
957    fn write_null(&mut self, _schema: &Schema<'_>) -> Result<(), SerdeError> {
958        Err(SerdeError::unsupported(
959            "an event stream union serializes exactly one structure variant",
960        ))
961    }
962}
963
964/// Routes the members of one event struct into the frame being built.
965///
966/// `@eventHeader` members become message headers in write order; the `@eventPayload` member
967/// becomes the payload. Ordinary members are omitted when event headers are modeled.
968struct EventMemberSerializer<'a> {
969    framing: Framing<'a>,
970    /// Cached `@eventHeader` names by member index; may be empty.
971    header_names: &'a [Option<&'static str>],
972    headers: &'a mut Vec<Header>,
973    payload: &'a mut Option<Bytes>,
974}
975
976enum MemberRole {
977    Header,
978    Payload,
979    Dropped,
980}
981
982fn member_role(schema: &Schema<'_>) -> MemberRole {
983    if schema.event_header().is_some() {
984        MemberRole::Header
985    } else if schema.event_payload().is_some() {
986        MemberRole::Payload
987    } else {
988        MemberRole::Dropped
989    }
990}
991
992impl EventMemberSerializer<'_> {
993    #[inline]
994    fn push_header(&mut self, schema: &Schema<'_>, value: HeaderValue) -> Result<(), SerdeError> {
995        let name = schema
996            .member_name()
997            .ok_or_else(|| SerdeError::unsupported("event header schema has no member name"))?;
998        let cached = schema
999            .member_index()
1000            .and_then(|index| *self.header_names.get(index)?)
1001            .filter(|&cached| cached == name);
1002        match cached {
1003            Some(cached) => self.headers.push(Header::new(cached, value)),
1004            None => self.headers.push(Header::new(StrBytes::copy_from_str(name), value)),
1005        }
1006        Ok(())
1007    }
1008
1009    fn unsupported(&self, what: &str, schema: &Schema<'_>) -> SerdeError {
1010        SerdeError::unsupported(format!(
1011            "unsupported event stream {what} type: {:?}",
1012            schema.shape_type()
1013        ))
1014    }
1015}
1016
1017macro_rules! header_only_member {
1018    ($($fn_name:ident($value_type:ty) => $header_value:expr,)+) => {
1019        $(fn $fn_name(&mut self, schema: &Schema<'_>, value: $value_type) -> Result<(), SerdeError> {
1020            match member_role(schema) {
1021                MemberRole::Header => {
1022                    #[allow(clippy::redundant_closure_call)]
1023                    self.push_header(schema, ($header_value)(value))
1024                }
1025                MemberRole::Payload => Err(self.unsupported("payload", schema)),
1026                MemberRole::Dropped => Ok(()),
1027            }
1028        })+
1029    };
1030}
1031
1032macro_rules! dropped_only_member {
1033    ($($fn_name:ident($value_type:ty),)+) => {
1034        $(fn $fn_name(&mut self, schema: &Schema<'_>, _value: $value_type) -> Result<(), SerdeError> {
1035            match member_role(schema) {
1036                MemberRole::Header => Err(self.unsupported("header", schema)),
1037                MemberRole::Payload => Err(self.unsupported("payload", schema)),
1038                MemberRole::Dropped => Ok(()),
1039            }
1040        })+
1041    };
1042}
1043
1044impl ShapeSerializer for EventMemberSerializer<'_> {
1045    fn write_struct(&mut self, schema: &Schema<'_>, value: &dyn SerializableStruct) -> Result<(), SerdeError> {
1046        match member_role(schema) {
1047            MemberRole::Header => Err(self.unsupported("header", schema)),
1048            MemberRole::Payload => {
1049                let mut ser = self.framing.get()?.payload_codec.create_serializer();
1050                ser.write_struct(value.schema(), value)?;
1051                *self.payload = Some(Bytes::from(ser.finish_boxed()));
1052                Ok(())
1053            }
1054            MemberRole::Dropped => Ok(()),
1055        }
1056    }
1057
1058    fn write_list(
1059        &mut self,
1060        schema: &Schema<'_>,
1061        _write_elements: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1062    ) -> Result<(), SerdeError> {
1063        match member_role(schema) {
1064            MemberRole::Header => Err(self.unsupported("header", schema)),
1065            MemberRole::Payload => Err(self.unsupported("payload", schema)),
1066            MemberRole::Dropped => Ok(()),
1067        }
1068    }
1069
1070    fn write_map(
1071        &mut self,
1072        schema: &Schema<'_>,
1073        _write_entries: &dyn Fn(&mut dyn ShapeSerializer) -> Result<(), SerdeError>,
1074    ) -> Result<(), SerdeError> {
1075        match member_role(schema) {
1076            MemberRole::Header => Err(self.unsupported("header", schema)),
1077            MemberRole::Payload => Err(self.unsupported("payload", schema)),
1078            MemberRole::Dropped => Ok(()),
1079        }
1080    }
1081
1082    fn write_string(&mut self, schema: &Schema<'_>, value: &str) -> Result<(), SerdeError> {
1083        match member_role(schema) {
1084            MemberRole::Header => self.push_header(schema, HeaderValue::String(value.to_owned().into())),
1085            MemberRole::Payload => {
1086                *self.payload = Some(Bytes::from(value.to_owned().into_bytes()));
1087                Ok(())
1088            }
1089            MemberRole::Dropped => Ok(()),
1090        }
1091    }
1092
1093    fn write_blob(&mut self, schema: &Schema<'_>, value: Blob) -> Result<(), SerdeError> {
1094        match member_role(schema) {
1095            MemberRole::Header => self.push_header(schema, HeaderValue::ByteArray(value.into_bytes())),
1096            MemberRole::Payload => {
1097                *self.payload = Some(value.into_bytes());
1098                Ok(())
1099            }
1100            MemberRole::Dropped => Ok(()),
1101        }
1102    }
1103
1104    // Event stream header types: https://smithy.io/2.0/spec/streaming.html#eventheader-trait
1105    // Note: there are no floating point header types for Event Stream.
1106    header_only_member!(
1107        write_boolean(bool) => HeaderValue::Bool,
1108        write_byte(i8) => HeaderValue::Byte,
1109        write_short(i16) => HeaderValue::Int16,
1110        write_integer(i32) => HeaderValue::Int32,
1111        write_long(i64) => HeaderValue::Int64,
1112    );
1113
1114    fn write_timestamp(&mut self, schema: &Schema<'_>, value: &DateTime) -> Result<(), SerdeError> {
1115        match member_role(schema) {
1116            MemberRole::Header => self.push_header(schema, HeaderValue::Timestamp(*value)),
1117            MemberRole::Payload => Err(self.unsupported("payload", schema)),
1118            MemberRole::Dropped => Ok(()),
1119        }
1120    }
1121
1122    dropped_only_member!(
1123        write_float(f32),
1124        write_double(f64),
1125        write_big_integer(&BigInteger),
1126        write_big_decimal(&BigDecimal),
1127        write_document(&Document),
1128    );
1129
1130    fn write_null(&mut self, _schema: &Schema<'_>) -> Result<(), SerdeError> {
1131        Ok(())
1132    }
1133}
1134
1135/// Reads one `@eventHeader` member value from a message header.
1136struct EventHeaderDeserializer<'a> {
1137    header: &'a Header,
1138}
1139
1140fn header_error(err: Error) -> SerdeError {
1141    SerdeError::custom(format!("{err}"))
1142}
1143
1144macro_rules! typed_header_reads {
1145    ($($fn_name:ident($expect_fn:ident) -> $result_type:ty,)+) => {
1146        $(fn $fn_name(&mut self, _schema: &Schema<'_>) -> Result<$result_type, SerdeError> {
1147            expect_fns::$expect_fn(self.header).map_err(header_error)
1148        })+
1149    };
1150}
1151
1152macro_rules! not_a_header_type {
1153    ($($fn_name:ident() -> $result_type:ty,)+) => {
1154        $(fn $fn_name(&mut self, _schema: &Schema<'_>) -> Result<$result_type, SerdeError> {
1155            Err(SerdeError::unsupported(
1156                "unsupported event stream header type",
1157            ))
1158        })+
1159    };
1160}
1161
1162impl ShapeDeserializer for EventHeaderDeserializer<'_> {
1163    fn read_struct(
1164        &mut self,
1165        _schema: &Schema<'_>,
1166        _state: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1167    ) -> Result<(), SerdeError> {
1168        Err(SerdeError::unsupported("unsupported event stream header type"))
1169    }
1170
1171    fn read_list(
1172        &mut self,
1173        _schema: &Schema<'_>,
1174        _state: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1175    ) -> Result<(), SerdeError> {
1176        Err(SerdeError::unsupported("unsupported event stream header type"))
1177    }
1178
1179    fn read_map(
1180        &mut self,
1181        _schema: &Schema<'_>,
1182        _state: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1183    ) -> Result<(), SerdeError> {
1184        Err(SerdeError::unsupported("unsupported event stream header type"))
1185    }
1186
1187    typed_header_reads!(
1188        read_boolean(expect_bool) -> bool,
1189        read_byte(expect_byte) -> i8,
1190        read_short(expect_int16) -> i16,
1191        read_integer(expect_int32) -> i32,
1192        read_long(expect_int64) -> i64,
1193        read_string(expect_string) -> String,
1194        read_blob(expect_byte_array) -> Blob,
1195        read_timestamp(expect_timestamp) -> DateTime,
1196    );
1197
1198    not_a_header_type!(
1199        read_float() -> f32,
1200        read_double() -> f64,
1201        read_big_integer() -> BigInteger,
1202        read_big_decimal() -> BigDecimal,
1203        read_document() -> Document,
1204    );
1205
1206    fn is_null(&self) -> bool {
1207        false
1208    }
1209
1210    fn container_size(&self) -> Option<usize> {
1211        None
1212    }
1213}
1214
1215/// Reads a blob or string `@eventPayload` member directly from the message payload.
1216struct RawPayloadDeserializer<'a> {
1217    payload: &'a Bytes,
1218}
1219
1220macro_rules! not_a_raw_payload_type {
1221    ($($fn_name:ident() -> $result_type:ty,)+) => {
1222        $(fn $fn_name(&mut self, _schema: &Schema<'_>) -> Result<$result_type, SerdeError> {
1223            Err(SerdeError::unsupported(
1224                "an event stream payload is a blob or a string",
1225            ))
1226        })+
1227    };
1228}
1229
1230impl ShapeDeserializer for RawPayloadDeserializer<'_> {
1231    fn read_struct(
1232        &mut self,
1233        _schema: &Schema<'_>,
1234        _state: &mut dyn FnMut(&Schema<'_>, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1235    ) -> Result<(), SerdeError> {
1236        Err(SerdeError::unsupported("an event stream payload is a blob or a string"))
1237    }
1238
1239    fn read_list(
1240        &mut self,
1241        _schema: &Schema<'_>,
1242        _state: &mut dyn FnMut(&mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1243    ) -> Result<(), SerdeError> {
1244        Err(SerdeError::unsupported("an event stream payload is a blob or a string"))
1245    }
1246
1247    fn read_map(
1248        &mut self,
1249        _schema: &Schema<'_>,
1250        _state: &mut dyn FnMut(String, &mut dyn ShapeDeserializer) -> Result<(), SerdeError>,
1251    ) -> Result<(), SerdeError> {
1252        Err(SerdeError::unsupported("an event stream payload is a blob or a string"))
1253    }
1254
1255    fn read_string(&mut self, _schema: &Schema<'_>) -> Result<String, SerdeError> {
1256        Ok(std::str::from_utf8(self.payload)
1257            .map_err(|_| SerdeError::custom("message payload is not valid UTF-8"))?
1258            .to_owned())
1259    }
1260
1261    fn read_blob(&mut self, _schema: &Schema<'_>) -> Result<Blob, SerdeError> {
1262        Ok(Blob::from_maybe_shared(self.payload.clone()))
1263    }
1264
1265    not_a_raw_payload_type!(
1266        read_boolean() -> bool,
1267        read_byte() -> i8,
1268        read_short() -> i16,
1269        read_integer() -> i32,
1270        read_long() -> i64,
1271        read_float() -> f32,
1272        read_double() -> f64,
1273        read_big_integer() -> BigInteger,
1274        read_big_decimal() -> BigDecimal,
1275        read_timestamp() -> DateTime,
1276        read_document() -> Document,
1277    );
1278
1279    fn is_null(&self) -> bool {
1280        false
1281    }
1282
1283    fn container_size(&self) -> Option<usize> {
1284        None
1285    }
1286}
1287
1288#[cfg(test)]
1289mod tests {
1290    use super::*;
1291    use crate::schema::protocol::RestJson1;
1292    use crate::schema::protocol::RpcV2Cbor;
1293    use aws_smithy_http::event_stream::Receiver;
1294    use aws_smithy_schema::ShapeId;
1295
1296    fn json_protocol() -> SharedServerProtocol {
1297        SharedServerProtocol::metadata_routed(RestJson1::default())
1298    }
1299
1300    fn cbor_protocol() -> SharedServerProtocol {
1301        SharedServerProtocol::metadata_routed(RpcV2Cbor::default())
1302    }
1303
1304    fn header<'a>(message: &'a Message, name: &str) -> Option<&'a HeaderValue> {
1305        message
1306            .headers()
1307            .iter()
1308            .find(|h| h.name().as_str() == name)
1309            .map(|h| h.value())
1310    }
1311
1312    fn string_header<'a>(message: &'a Message, name: &str) -> Option<&'a str> {
1313        header(message, name).map(|v| v.as_string().unwrap().as_str())
1314    }
1315
1316    macro_rules! member_schema {
1317        ($name:ident, $shape:literal, $member:literal, $shape_type:expr, $index:literal $(, $with:ident)*) => {
1318            static $name: Schema<'static> = Schema::new_member(
1319                ShapeId::from_parts(
1320                    concat!("test#", $shape, "$", $member),
1321                    "test",
1322                    $shape,
1323                ),
1324                $shape_type,
1325                $member,
1326                $index,
1327            )$(.$with())*;
1328        };
1329    }
1330
1331    macro_rules! struct_schema {
1332        ($name:ident, $shape:literal, [$($member:ident),*]) => {
1333            static $name: Schema<'static> = Schema::new_struct(
1334                ShapeId::from_parts(concat!("test#", $shape), "test", $shape),
1335                ShapeType::Structure,
1336                &[$(&$member),*],
1337            );
1338        };
1339    }
1340
1341    // An event with every header type the event stream contract supports, one optional header
1342    // left `None`, and one member with no event trait at all.
1343    member_schema!(AH_BOOL, "AllHeaders", "flag", ShapeType::Boolean, 0, with_event_header);
1344    member_schema!(AH_BYTE, "AllHeaders", "small", ShapeType::Byte, 1, with_event_header);
1345    member_schema!(AH_SHORT, "AllHeaders", "short", ShapeType::Short, 2, with_event_header);
1346    member_schema!(AH_INT, "AllHeaders", "int", ShapeType::Integer, 3, with_event_header);
1347    member_schema!(AH_LONG, "AllHeaders", "long", ShapeType::Long, 4, with_event_header);
1348    member_schema!(AH_BLOB, "AllHeaders", "bin", ShapeType::Blob, 5, with_event_header);
1349    member_schema!(AH_STRING, "AllHeaders", "name", ShapeType::String, 6, with_event_header);
1350    member_schema!(AH_TIME, "AllHeaders", "at", ShapeType::Timestamp, 7, with_event_header);
1351    member_schema!(
1352        AH_SKIPPED,
1353        "AllHeaders",
1354        "skipped",
1355        ShapeType::String,
1356        8,
1357        with_event_header
1358    );
1359    member_schema!(AH_EXTRA, "AllHeaders", "extra", ShapeType::String, 9);
1360    struct_schema!(
1361        ALL_HEADERS_SCHEMA,
1362        "AllHeaders",
1363        [AH_BOOL, AH_BYTE, AH_SHORT, AH_INT, AH_LONG, AH_BLOB, AH_STRING, AH_TIME, AH_SKIPPED, AH_EXTRA]
1364    );
1365
1366    #[derive(Debug, Default, PartialEq)]
1367    struct AllHeaders {
1368        flag: Option<bool>,
1369        small: Option<i8>,
1370        short: Option<i16>,
1371        int: Option<i32>,
1372        long: Option<i64>,
1373        bin: Option<Blob>,
1374        name: Option<String>,
1375        at: Option<DateTime>,
1376        skipped: Option<String>,
1377        extra: Option<String>,
1378    }
1379
1380    impl SerializableStruct for AllHeaders {
1381        fn schema(&self) -> &Schema<'_> {
1382            &ALL_HEADERS_SCHEMA
1383        }
1384
1385        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1386            if let Some(v) = self.flag {
1387                ser.write_boolean(&AH_BOOL, v)?;
1388            }
1389            if let Some(v) = self.small {
1390                ser.write_byte(&AH_BYTE, v)?;
1391            }
1392            if let Some(v) = self.short {
1393                ser.write_short(&AH_SHORT, v)?;
1394            }
1395            if let Some(v) = self.int {
1396                ser.write_integer(&AH_INT, v)?;
1397            }
1398            if let Some(v) = self.long {
1399                ser.write_long(&AH_LONG, v)?;
1400            }
1401            if let Some(ref v) = self.bin {
1402                ser.write_blob(&AH_BLOB, v.clone())?;
1403            }
1404            if let Some(ref v) = self.name {
1405                ser.write_string(&AH_STRING, v)?;
1406            }
1407            if let Some(ref v) = self.at {
1408                ser.write_timestamp(&AH_TIME, v)?;
1409            }
1410            if let Some(ref v) = self.skipped {
1411                ser.write_string(&AH_SKIPPED, v)?;
1412            }
1413            if let Some(ref v) = self.extra {
1414                ser.write_string(&AH_EXTRA, v)?;
1415            }
1416            Ok(())
1417        }
1418    }
1419
1420    impl AllHeaders {
1421        fn deserialize(deser: &mut dyn ShapeDeserializer) -> Result<Self, SerdeError> {
1422            let mut out = AllHeaders::default();
1423            deser.read_struct(&ALL_HEADERS_SCHEMA, &mut |member, d| {
1424                match member.member_index() {
1425                    Some(0) => out.flag = Some(d.read_boolean(member)?),
1426                    Some(1) => out.small = Some(d.read_byte(member)?),
1427                    Some(2) => out.short = Some(d.read_short(member)?),
1428                    Some(3) => out.int = Some(d.read_integer(member)?),
1429                    Some(4) => out.long = Some(d.read_long(member)?),
1430                    Some(5) => out.bin = Some(d.read_blob(member)?),
1431                    Some(6) => out.name = Some(d.read_string(member)?),
1432                    Some(7) => out.at = Some(d.read_timestamp(member)?),
1433                    Some(8) => out.skipped = Some(d.read_string(member)?),
1434                    Some(9) => {
1435                        if d.is_null() {
1436                            d.read_null()?;
1437                        } else {
1438                            out.extra = Some(d.read_string(member)?);
1439                        }
1440                    }
1441                    _ => {}
1442                }
1443                Ok(())
1444            })?;
1445            Ok(out)
1446        }
1447    }
1448
1449    // A float `@eventHeader` is not representable; marshalling must fail.
1450    member_schema!(FH_RATE, "FloatHeader", "rate", ShapeType::Float, 0, with_event_header);
1451    struct_schema!(FLOAT_HEADER_SCHEMA, "FloatHeader", [FH_RATE]);
1452
1453    #[derive(Debug)]
1454    struct FloatHeader {
1455        rate: f32,
1456    }
1457
1458    impl SerializableStruct for FloatHeader {
1459        fn schema(&self) -> &Schema<'_> {
1460            &FLOAT_HEADER_SCHEMA
1461        }
1462
1463        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1464            ser.write_float(&FH_RATE, self.rate)
1465        }
1466    }
1467
1468    // String and blob `@eventPayload` members carry raw bytes with fixed content types.
1469    member_schema!(
1470        TEXT_VALUE,
1471        "TextEvent",
1472        "value",
1473        ShapeType::String,
1474        0,
1475        with_event_payload
1476    );
1477    struct_schema!(TEXT_EVENT_SCHEMA, "TextEvent", [TEXT_VALUE]);
1478
1479    #[derive(Debug, Default, PartialEq)]
1480    struct TextEvent {
1481        value: Option<String>,
1482    }
1483
1484    impl SerializableStruct for TextEvent {
1485        fn schema(&self) -> &Schema<'_> {
1486            &TEXT_EVENT_SCHEMA
1487        }
1488
1489        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1490            if let Some(ref v) = self.value {
1491                ser.write_string(&TEXT_VALUE, v)?;
1492            }
1493            Ok(())
1494        }
1495    }
1496
1497    impl TextEvent {
1498        fn deserialize(deser: &mut dyn ShapeDeserializer) -> Result<Self, SerdeError> {
1499            let mut out = TextEvent::default();
1500            deser.read_struct(&TEXT_EVENT_SCHEMA, &mut |member, d| {
1501                if member.member_index() == Some(0) {
1502                    out.value = Some(d.read_string(member)?);
1503                }
1504                Ok(())
1505            })?;
1506            Ok(out)
1507        }
1508    }
1509
1510    member_schema!(BIN_VALUE, "BinEvent", "value", ShapeType::Blob, 0, with_event_payload);
1511    struct_schema!(BIN_EVENT_SCHEMA, "BinEvent", [BIN_VALUE]);
1512
1513    #[derive(Debug, Default, PartialEq)]
1514    struct BinEvent {
1515        value: Option<Blob>,
1516    }
1517
1518    impl SerializableStruct for BinEvent {
1519        fn schema(&self) -> &Schema<'_> {
1520            &BIN_EVENT_SCHEMA
1521        }
1522
1523        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1524            if let Some(ref v) = self.value {
1525                ser.write_blob(&BIN_VALUE, v.clone())?;
1526            }
1527            Ok(())
1528        }
1529    }
1530
1531    impl BinEvent {
1532        fn deserialize(deser: &mut dyn ShapeDeserializer) -> Result<Self, SerdeError> {
1533            let mut out = BinEvent::default();
1534            deser.read_struct(&BIN_EVENT_SCHEMA, &mut |member, d| {
1535                if member.member_index() == Some(0) {
1536                    out.value = Some(d.read_blob(member)?);
1537                }
1538                Ok(())
1539            })?;
1540            Ok(out)
1541        }
1542    }
1543
1544    // A structured `@eventPayload` goes through the protocol's codec, alongside a header.
1545    member_schema!(BODY_TEXT, "MessageBody", "text", ShapeType::String, 0);
1546    struct_schema!(MESSAGE_BODY_SCHEMA, "MessageBody", [BODY_TEXT]);
1547
1548    #[derive(Debug, Default, PartialEq)]
1549    struct MessageBody {
1550        text: Option<String>,
1551    }
1552
1553    impl SerializableStruct for MessageBody {
1554        fn schema(&self) -> &Schema<'_> {
1555            &MESSAGE_BODY_SCHEMA
1556        }
1557
1558        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1559            if let Some(ref v) = self.text {
1560                ser.write_string(&BODY_TEXT, v)?;
1561            }
1562            Ok(())
1563        }
1564    }
1565
1566    impl MessageBody {
1567        fn deserialize(deser: &mut dyn ShapeDeserializer) -> Result<Self, SerdeError> {
1568            let mut out = MessageBody::default();
1569            deser.read_struct(&MESSAGE_BODY_SCHEMA, &mut |member, d| {
1570                if member.member_index() == Some(0) {
1571                    out.text = Some(d.read_string(member)?);
1572                }
1573                Ok(())
1574            })?;
1575            Ok(out)
1576        }
1577    }
1578
1579    member_schema!(
1580        STRUCT_FROM,
1581        "StructEvent",
1582        "from",
1583        ShapeType::String,
1584        0,
1585        with_event_header
1586    );
1587    member_schema!(
1588        STRUCT_BODY,
1589        "StructEvent",
1590        "body",
1591        ShapeType::Structure,
1592        1,
1593        with_event_payload
1594    );
1595    struct_schema!(STRUCT_EVENT_SCHEMA, "StructEvent", [STRUCT_FROM, STRUCT_BODY]);
1596
1597    #[derive(Debug, Default, PartialEq)]
1598    struct StructEvent {
1599        from: Option<String>,
1600        body: Option<MessageBody>,
1601    }
1602
1603    impl SerializableStruct for StructEvent {
1604        fn schema(&self) -> &Schema<'_> {
1605            &STRUCT_EVENT_SCHEMA
1606        }
1607
1608        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1609            if let Some(ref v) = self.from {
1610                ser.write_string(&STRUCT_FROM, v)?;
1611            }
1612            if let Some(ref v) = self.body {
1613                ser.write_struct(&STRUCT_BODY, v)?;
1614            }
1615            Ok(())
1616        }
1617    }
1618
1619    impl StructEvent {
1620        fn deserialize(deser: &mut dyn ShapeDeserializer) -> Result<Self, SerdeError> {
1621            let mut out = StructEvent::default();
1622            deser.read_struct(&STRUCT_EVENT_SCHEMA, &mut |member, d| {
1623                match member.member_index() {
1624                    Some(0) => out.from = Some(d.read_string(member)?),
1625                    Some(1) => out.body = Some(MessageBody::deserialize(d)?),
1626                    _ => {}
1627                }
1628                Ok(())
1629            })?;
1630            Ok(out)
1631        }
1632    }
1633
1634    // No members at all: the payload is skipped entirely on the way in.
1635    struct_schema!(EMPTY_EVENT_SCHEMA, "EmptyEvent", []);
1636
1637    #[derive(Debug, Default, PartialEq)]
1638    struct EmptyEvent;
1639
1640    impl SerializableStruct for EmptyEvent {
1641        fn schema(&self) -> &Schema<'_> {
1642            &EMPTY_EVENT_SCHEMA
1643        }
1644
1645        fn serialize_members(&self, _ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1646            Ok(())
1647        }
1648    }
1649
1650    // No event traits: the whole struct is the codec-encoded payload.
1651    member_schema!(PLAIN_TEXT, "PlainEvent", "text", ShapeType::String, 0);
1652    struct_schema!(PLAIN_EVENT_SCHEMA, "PlainEvent", [PLAIN_TEXT]);
1653
1654    #[derive(Debug, Default, PartialEq)]
1655    struct PlainEvent {
1656        text: Option<String>,
1657    }
1658
1659    impl SerializableStruct for PlainEvent {
1660        fn schema(&self) -> &Schema<'_> {
1661            &PLAIN_EVENT_SCHEMA
1662        }
1663
1664        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1665            if let Some(ref v) = self.text {
1666                ser.write_string(&PLAIN_TEXT, v)?;
1667            }
1668            Ok(())
1669        }
1670    }
1671
1672    impl PlainEvent {
1673        fn deserialize(deser: &mut dyn ShapeDeserializer) -> Result<Self, SerdeError> {
1674            let mut out = PlainEvent::default();
1675            deser.read_struct(&PLAIN_EVENT_SCHEMA, &mut |member, d| {
1676                if member.member_index() == Some(0) {
1677                    out.text = Some(d.read_string(member)?);
1678                }
1679                Ok(())
1680            })?;
1681            Ok(out)
1682        }
1683    }
1684
1685    // The stream union: one member schema per event, named by its Smithy member name.
1686    member_schema!(EV_ALL_HEADERS, "TestEvents", "allHeaders", ShapeType::Structure, 0);
1687    member_schema!(EV_FLOAT_HEADER, "TestEvents", "floatHeader", ShapeType::Structure, 1);
1688    member_schema!(EV_TEXT, "TestEvents", "text", ShapeType::Structure, 2);
1689    member_schema!(EV_BIN, "TestEvents", "bin", ShapeType::Structure, 3);
1690    member_schema!(EV_STRUCTURED, "TestEvents", "structured", ShapeType::Structure, 4);
1691    member_schema!(EV_EMPTY, "TestEvents", "empty", ShapeType::Structure, 5);
1692    member_schema!(EV_PLAIN, "TestEvents", "plain", ShapeType::Structure, 6);
1693
1694    #[derive(Debug, PartialEq)]
1695    enum TestEvents {
1696        AllHeaders(AllHeaders),
1697        FloatHeader(FloatHeader),
1698        Text(TextEvent),
1699        Bin(BinEvent),
1700        Structured(StructEvent),
1701        Empty(EmptyEvent),
1702        Plain(PlainEvent),
1703    }
1704
1705    impl PartialEq for FloatHeader {
1706        fn eq(&self, other: &Self) -> bool {
1707            self.rate == other.rate
1708        }
1709    }
1710
1711    static TEST_EVENTS_SCHEMA: Schema<'static> = Schema::new_struct(
1712        ShapeId::from_parts("test#TestEvents", "test", "TestEvents"),
1713        ShapeType::Union,
1714        &[
1715            &EV_ALL_HEADERS,
1716            &EV_FLOAT_HEADER,
1717            &EV_TEXT,
1718            &EV_BIN,
1719            &EV_STRUCTURED,
1720            &EV_EMPTY,
1721            &EV_PLAIN,
1722        ],
1723    )
1724    .with_streaming();
1725
1726    impl SerializableStruct for TestEvents {
1727        fn schema(&self) -> &Schema<'_> {
1728            &TEST_EVENTS_SCHEMA
1729        }
1730
1731        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1732            match self {
1733                Self::AllHeaders(v) => ser.write_struct(&EV_ALL_HEADERS, v),
1734                Self::FloatHeader(v) => ser.write_struct(&EV_FLOAT_HEADER, v),
1735                Self::Text(v) => ser.write_struct(&EV_TEXT, v),
1736                Self::Bin(v) => ser.write_struct(&EV_BIN, v),
1737                Self::Structured(v) => ser.write_struct(&EV_STRUCTURED, v),
1738                Self::Empty(v) => ser.write_struct(&EV_EMPTY, v),
1739                Self::Plain(v) => ser.write_struct(&EV_PLAIN, v),
1740            }
1741        }
1742    }
1743
1744    #[derive(Debug)]
1745    enum TestEventsError {
1746        Boom(BoomError),
1747    }
1748
1749    impl fmt::Display for TestEventsError {
1750        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1751            write!(f, "boom")
1752        }
1753    }
1754
1755    impl std::error::Error for TestEventsError {}
1756
1757    member_schema!(BOOM_MESSAGE, "BoomError", "message", ShapeType::String, 0);
1758    struct_schema!(BOOM_ERROR_SCHEMA, "BoomError", [BOOM_MESSAGE]);
1759
1760    #[derive(Debug, Default, PartialEq)]
1761    struct BoomError {
1762        message: Option<String>,
1763    }
1764
1765    impl SerializableStruct for BoomError {
1766        fn schema(&self) -> &Schema<'_> {
1767            &BOOM_ERROR_SCHEMA
1768        }
1769
1770        fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1771            if let Some(ref v) = self.message {
1772                ser.write_string(&BOOM_MESSAGE, v)?;
1773            }
1774            Ok(())
1775        }
1776    }
1777
1778    impl BoomError {
1779        fn deserialize(deser: &mut dyn ShapeDeserializer) -> Result<Self, SerdeError> {
1780            let mut out = BoomError::default();
1781            deser.read_struct(&BOOM_ERROR_SCHEMA, &mut |member, d| {
1782                if member.member_index() == Some(0) {
1783                    out.message = Some(d.read_string(member)?);
1784                }
1785                Ok(())
1786            })?;
1787            Ok(out)
1788        }
1789    }
1790
1791    impl SerializableEventError for TestEventsError {
1792        fn variant(&self) -> (&'static str, &Schema<'_>, &dyn SerializableStruct) {
1793            match self {
1794                Self::Boom(inner) => ("boom", &BOOM_ERROR_SCHEMA, inner),
1795            }
1796        }
1797    }
1798
1799    impl DeserializableEventStream for TestEvents {
1800        type Error = TestEventsError;
1801
1802        fn deserialize_event(event_type: &str, frame: &EventFrame<'_>) -> Result<Option<Self>, Error> {
1803            let mut deser = frame.deserializer();
1804            let wrap = |err: SerdeError| Error::unmarshalling(format!("failed to unmarshall {event_type}: {err}"));
1805            Ok(Some(match event_type {
1806                "allHeaders" => Self::AllHeaders(AllHeaders::deserialize(&mut deser).map_err(wrap)?),
1807                "text" => Self::Text(TextEvent::deserialize(&mut deser).map_err(wrap)?),
1808                "bin" => Self::Bin(BinEvent::deserialize(&mut deser).map_err(wrap)?),
1809                "structured" => Self::Structured(StructEvent::deserialize(&mut deser).map_err(wrap)?),
1810                "empty" => Self::Empty(EmptyEvent),
1811                "plain" => Self::Plain(PlainEvent::deserialize(&mut deser).map_err(wrap)?),
1812                _ => return Ok(None),
1813            }))
1814        }
1815
1816        fn deserialize_error(exception_type: &str, frame: &EventFrame<'_>) -> Result<Option<Self::Error>, Error> {
1817            match exception_type {
1818                "boom" => {
1819                    let mut deser = frame.deserializer();
1820                    let parsed = BoomError::deserialize(&mut deser)
1821                        .map_err(|err| Error::unmarshalling(format!("failed to unmarshall exception: {err}")))?;
1822                    Ok(Some(TestEventsError::Boom(parsed)))
1823                }
1824                _ => Ok(None),
1825            }
1826        }
1827    }
1828
1829    fn marshall(protocol: &SharedServerProtocol, event: TestEvents) -> Message {
1830        SchemaEventMarshaller::<TestEvents>::new(protocol.clone())
1831            .marshall(event)
1832            .expect("marshalls")
1833    }
1834
1835    fn unmarshall(
1836        protocol: &SharedServerProtocol,
1837        message: &Message,
1838    ) -> Result<UnmarshalledMessage<TestEvents, TestEventsError>, Error> {
1839        SchemaEventUnmarshaller::<TestEvents>::new(protocol.clone()).unmarshall(message)
1840    }
1841
1842    #[test]
1843    fn all_header_types_round_trip_and_keep_declaration_order() {
1844        let protocol = json_protocol();
1845        let event = AllHeaders {
1846            flag: Some(true),
1847            small: Some(-3),
1848            short: Some(-300),
1849            int: Some(70_000),
1850            long: Some(5_000_000_000),
1851            bin: Some(Blob::new(&b"\x00\xff"[..])),
1852            name: Some("ann".to_owned()),
1853            at: Some(DateTime::from_secs(1_700_000_000)),
1854            skipped: None,
1855            extra: Some("payload".to_owned()),
1856        };
1857        let message = marshall(&protocol, TestEvents::AllHeaders(event));
1858
1859        let names: Vec<_> = message.headers().iter().map(|h| h.name().as_str()).collect();
1860        // Without an explicit payload, ordinary members are omitted by generated serde marshallers.
1861        assert_eq!(
1862            names,
1863            [
1864                ":message-type",
1865                ":event-type",
1866                "flag",
1867                "small",
1868                "short",
1869                "int",
1870                "long",
1871                "bin",
1872                "name",
1873                "at"
1874            ]
1875        );
1876        assert_eq!(string_header(&message, ":event-type"), Some("allHeaders"));
1877        assert!(message.payload().is_empty());
1878        assert!(matches!(header(&message, "flag"), Some(HeaderValue::Bool(true))));
1879        assert!(matches!(header(&message, "small"), Some(HeaderValue::Byte(-3))));
1880        assert!(matches!(header(&message, "short"), Some(HeaderValue::Int16(-300))));
1881        assert!(matches!(header(&message, "int"), Some(HeaderValue::Int32(70_000))));
1882        assert!(matches!(
1883            header(&message, "long"),
1884            Some(HeaderValue::Int64(5_000_000_000))
1885        ));
1886        assert!(matches!(header(&message, "bin"), Some(HeaderValue::ByteArray(b)) if &b[..] == b"\x00\xff"));
1887        assert!(matches!(header(&message, "at"), Some(HeaderValue::Timestamp(_))));
1888
1889        match unmarshall(&protocol, &message).expect("unmarshalls") {
1890            UnmarshalledMessage::Event(TestEvents::AllHeaders(parsed)) => {
1891                assert_eq!(parsed.flag, Some(true));
1892                assert_eq!(parsed.small, Some(-3));
1893                assert_eq!(parsed.short, Some(-300));
1894                assert_eq!(parsed.int, Some(70_000));
1895                assert_eq!(parsed.long, Some(5_000_000_000));
1896                assert_eq!(parsed.bin.as_ref().map(|b| b.as_ref()), Some(&b"\x00\xff"[..]));
1897                assert_eq!(parsed.name.as_deref(), Some("ann"));
1898                assert_eq!(parsed.at, Some(DateTime::from_secs(1_700_000_000)));
1899                assert_eq!(parsed.skipped, None);
1900                assert_eq!(parsed.extra, None);
1901            }
1902            other => panic!("unexpected result: {other:?}"),
1903        }
1904    }
1905
1906    #[test]
1907    fn headers_without_explicit_payload_omit_ordinary_members() {
1908        static HEADER: Schema<'static> = Schema::new_member(
1909            ShapeId::from_parts("test#Implicit$header", "test", "Implicit"),
1910            ShapeType::String,
1911            "header",
1912            0,
1913        )
1914        .with_event_header();
1915        static DATA: Schema<'static> = Schema::new_member(
1916            ShapeId::from_parts("test#Implicit$data", "test", "Implicit"),
1917            ShapeType::String,
1918            "data",
1919            1,
1920        )
1921        .with_json_name("wireData")
1922        .with_xml_name("WireData");
1923        static SCHEMA: Schema<'static> = Schema::new_struct(
1924            ShapeId::from_parts("test#Implicit", "test", "Implicit"),
1925            ShapeType::Structure,
1926            &[&HEADER, &DATA],
1927        )
1928        .with_xml_name("WireEvent")
1929        .with_xml_namespace("urn:test", None);
1930        struct Implicit;
1931        impl SerializableStruct for Implicit {
1932            fn schema(&self) -> &Schema<'_> {
1933                &SCHEMA
1934            }
1935
1936            fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
1937                ser.write_string(&HEADER, "ann")?;
1938                ser.write_string(&DATA, "hi")
1939            }
1940        }
1941        for (protocol, _media_type, expected) in [
1942            (json_protocol(), "application/json", br#"{"wireData":"hi"}"#.as_slice()),
1943            (
1944                cbor_protocol(),
1945                "application/cbor",
1946                b"\xbf\x64data\x62hi\xff".as_slice(),
1947            ),
1948            (
1949                SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default()),
1950                "application/xml",
1951                br#"<WireEvent xmlns="urn:test"><WireData>hi</WireData></WireEvent>"#.as_slice(),
1952            ),
1953        ] {
1954            let capability = protocol.event_stream_framing().unwrap();
1955            let message = build_frame(
1956                Framing::Known(capability),
1957                "implicit",
1958                &SCHEMA,
1959                &Implicit,
1960                FrameKind::Event,
1961                None,
1962            )
1963            .unwrap();
1964            assert_eq!(string_header(&message, "header"), Some("ann"));
1965            assert_eq!(string_header(&message, ":content-type"), None);
1966            assert!(message.payload().is_empty());
1967            // Received implicit payloads still parse as the generated serde unmarshaller does.
1968            let message = Message::new_from_parts(message.headers().to_vec(), Bytes::copy_from_slice(expected));
1969            let mut values = [None, None];
1970            EventFrame::new(&message, capability)
1971                .deserializer()
1972                .read_struct(&SCHEMA, &mut |member, d| {
1973                    values[member.member_index().unwrap()] = Some(d.read_string(member)?);
1974                    Ok(())
1975                })
1976                .unwrap();
1977            assert_eq!(values, [Some("ann".to_owned()), Some("hi".to_owned())]);
1978        }
1979    }
1980
1981    #[test]
1982    fn header_only_event_has_no_payload_or_content_type() {
1983        member_schema!(
1984            HEADER_NAME,
1985            "HeaderOnly",
1986            "name",
1987            ShapeType::String,
1988            0,
1989            with_event_header
1990        );
1991        static SCHEMA: Schema<'static> = Schema::new_struct(
1992            ShapeId::from_parts("test#HeaderOnly", "test", "HeaderOnly"),
1993            ShapeType::Structure,
1994            &[&HEADER_NAME],
1995        );
1996        struct HeaderOnly;
1997        impl SerializableStruct for HeaderOnly {
1998            fn schema(&self) -> &Schema<'_> {
1999                &SCHEMA
2000            }
2001
2002            fn serialize_members(&self, ser: &mut dyn ShapeSerializer) -> Result<(), SerdeError> {
2003                ser.write_string(&HEADER_NAME, "ann")
2004            }
2005        }
2006        let protocol = json_protocol();
2007        let capability = protocol.event_stream_framing().unwrap();
2008        let message = build_frame(
2009            Framing::Known(capability),
2010            "headerOnly",
2011            &SCHEMA,
2012            &HeaderOnly,
2013            FrameKind::Event,
2014            None,
2015        )
2016        .unwrap();
2017        assert_eq!(string_header(&message, "name"), Some("ann"));
2018        assert_eq!(string_header(&message, ":content-type"), None);
2019        assert!(message.payload().is_empty());
2020    }
2021
2022    #[test]
2023    fn absent_implicit_members_do_not_form_a_payload_document() {
2024        for protocol in [
2025            json_protocol(),
2026            cbor_protocol(),
2027            SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default()),
2028        ] {
2029            for name in [None, Some("ann".to_owned())] {
2030                for extra in [None, Some("omitted".to_owned())] {
2031                    let message = marshall(
2032                        &protocol,
2033                        TestEvents::AllHeaders(AllHeaders {
2034                            name: name.clone(),
2035                            extra,
2036                            ..Default::default()
2037                        }),
2038                    );
2039                    assert!(message.payload().is_empty());
2040                    assert_eq!(string_header(&message, ":content-type"), None);
2041                    match unmarshall(&protocol, &message).unwrap() {
2042                        UnmarshalledMessage::Event(TestEvents::AllHeaders(parsed)) => {
2043                            assert_eq!(parsed.name, name);
2044                            assert_eq!(parsed.extra, None);
2045                        }
2046                        other => panic!("unexpected result: {other:?}"),
2047                    }
2048                }
2049            }
2050        }
2051    }
2052
2053    #[test]
2054    fn implicit_xml_payloads_still_validate_headers_and_nonempty_documents() {
2055        let protocol = SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default());
2056        let message = marshall(
2057            &protocol,
2058            TestEvents::AllHeaders(AllHeaders {
2059                name: Some("ann".to_owned()),
2060                ..Default::default()
2061            }),
2062        );
2063        for payload in [b" ".as_slice(), b"<", b"not XML"] {
2064            let malformed = Message::new_from_parts(message.headers().to_vec(), Bytes::copy_from_slice(payload));
2065            assert!(unmarshall(&protocol, &malformed).is_err());
2066        }
2067        let headers = message
2068            .headers()
2069            .iter()
2070            .map(|header| {
2071                if header.name().as_str() == "name" {
2072                    Header::new("name", HeaderValue::Int32(42))
2073                } else {
2074                    header.clone()
2075                }
2076            })
2077            .collect();
2078        let invalid_header = Message::new_from_parts(headers, Bytes::new());
2079        assert!(unmarshall(&protocol, &invalid_header).is_err());
2080    }
2081
2082    #[test]
2083    fn float_header_fails_to_marshall() {
2084        let err = SchemaEventMarshaller::<TestEvents>::new(json_protocol())
2085            .marshall(TestEvents::FloatHeader(FloatHeader { rate: 1.5 }))
2086            .expect_err("floats are not event stream header types");
2087        assert!(err.to_string().contains("unsupported event stream header type"));
2088    }
2089
2090    #[test]
2091    fn string_payload_is_raw_text() {
2092        let protocol = json_protocol();
2093        let message = marshall(
2094            &protocol,
2095            TestEvents::Text(TextEvent {
2096                value: Some("raw string".to_owned()),
2097            }),
2098        );
2099        assert_eq!(string_header(&message, ":content-type"), Some("text/plain"));
2100        assert_eq!(&message.payload()[..], b"raw string");
2101        match unmarshall(&protocol, &message).expect("unmarshalls") {
2102            UnmarshalledMessage::Event(TestEvents::Text(parsed)) => {
2103                assert_eq!(parsed.value.as_deref(), Some("raw string"));
2104            }
2105            other => panic!("unexpected result: {other:?}"),
2106        }
2107    }
2108
2109    #[test]
2110    fn optional_string_payload_none_is_empty_with_content_type() {
2111        let message = marshall(&json_protocol(), TestEvents::Text(TextEvent { value: None }));
2112        assert_eq!(string_header(&message, ":content-type"), Some("text/plain"));
2113        assert!(message.payload().is_empty());
2114    }
2115
2116    #[test]
2117    fn blob_payload_is_raw_bytes() {
2118        let protocol = json_protocol();
2119        let message = marshall(
2120            &protocol,
2121            TestEvents::Bin(BinEvent {
2122                value: Some(Blob::new(&b"\x00\xff"[..])),
2123            }),
2124        );
2125        assert_eq!(
2126            string_header(&message, ":content-type"),
2127            Some("application/octet-stream")
2128        );
2129        assert_eq!(&message.payload()[..], b"\x00\xff");
2130        match unmarshall(&protocol, &message).expect("unmarshalls") {
2131            UnmarshalledMessage::Event(TestEvents::Bin(parsed)) => {
2132                assert_eq!(parsed.value.as_ref().map(|b| b.as_ref()), Some(&b"\x00\xff"[..]));
2133            }
2134            other => panic!("unexpected result: {other:?}"),
2135        }
2136    }
2137
2138    #[test]
2139    fn structured_payload_goes_through_the_selected_codec() {
2140        for (protocol, media_type) in [
2141            (json_protocol(), "application/json"),
2142            (cbor_protocol(), "application/cbor"),
2143        ] {
2144            let message = marshall(
2145                &protocol,
2146                TestEvents::Structured(StructEvent {
2147                    from: Some("ann".to_owned()),
2148                    body: Some(MessageBody {
2149                        text: Some("hi".to_owned()),
2150                    }),
2151                }),
2152            );
2153            // Header order: prelude headers, member headers in write order, `:content-type` last.
2154            let names: Vec<_> = message.headers().iter().map(|h| h.name().as_str()).collect();
2155            assert_eq!(names, [":message-type", ":event-type", "from", ":content-type"]);
2156            assert_eq!(string_header(&message, ":content-type"), Some(media_type));
2157            if media_type == "application/json" {
2158                assert_eq!(&message.payload()[..], br#"{"text":"hi"}"#);
2159            }
2160            match unmarshall(&protocol, &message).expect("unmarshalls") {
2161                UnmarshalledMessage::Event(TestEvents::Structured(parsed)) => {
2162                    assert_eq!(parsed.from.as_deref(), Some("ann"));
2163                    assert_eq!(parsed.body.and_then(|b| b.text).as_deref(), Some("hi"));
2164                }
2165                other => panic!("unexpected result: {other:?}"),
2166            }
2167        }
2168    }
2169
2170    #[test]
2171    fn optional_structured_payload_none_is_empty_with_content_type() {
2172        let message = marshall(
2173            &json_protocol(),
2174            TestEvents::Structured(StructEvent {
2175                from: Some("ann".to_owned()),
2176                body: None,
2177            }),
2178        );
2179        assert_eq!(string_header(&message, ":content-type"), Some("application/json"));
2180        assert!(message.payload().is_empty());
2181    }
2182
2183    #[test]
2184    fn no_event_traits_serializes_the_whole_struct_as_payload() {
2185        let protocol = json_protocol();
2186        let message = marshall(
2187            &protocol,
2188            TestEvents::Plain(PlainEvent {
2189                text: Some("hi".to_owned()),
2190            }),
2191        );
2192        assert_eq!(string_header(&message, ":content-type"), Some("application/json"));
2193        assert_eq!(&message.payload()[..], br#"{"text":"hi"}"#);
2194        match unmarshall(&protocol, &message).expect("unmarshalls") {
2195            UnmarshalledMessage::Event(TestEvents::Plain(parsed)) => {
2196                assert_eq!(parsed.text.as_deref(), Some("hi"));
2197            }
2198            other => panic!("unexpected result: {other:?}"),
2199        }
2200    }
2201
2202    #[test]
2203    fn empty_struct_serializes_as_codec_payload_and_skips_it_on_read() {
2204        let protocol = json_protocol();
2205        let message = marshall(&protocol, TestEvents::Empty(EmptyEvent));
2206        assert_eq!(string_header(&message, ":content-type"), Some("application/json"));
2207        assert_eq!(&message.payload()[..], br#"{}"#);
2208        // Reading back skips the payload entirely, even if it is garbage.
2209        let garbage = Message::new_from_parts(message.headers().to_vec(), Bytes::from_static(b"!!"));
2210        assert!(matches!(
2211            unmarshall(&protocol, &garbage).expect("unmarshalls"),
2212            UnmarshalledMessage::Event(TestEvents::Empty(_))
2213        ));
2214    }
2215
2216    #[test]
2217    fn untraited_members_alongside_headers_are_read_from_the_payload() {
2218        // A peer-supplied payload document supplies ordinary members alongside headers.
2219        let protocol = json_protocol();
2220        let message = Message::new_from_parts(
2221            vec![
2222                Header::new(":message-type", HeaderValue::String("event".into())),
2223                Header::new(":event-type", HeaderValue::String("allHeaders".into())),
2224                Header::new("name", HeaderValue::String("ann".into())),
2225                Header::new("unknown", HeaderValue::String("traced, not fatal".into())),
2226            ],
2227            Bytes::from_static(br#"{"extra":"from payload"}"#),
2228        );
2229        match unmarshall(&protocol, &message).expect("unmarshalls") {
2230            UnmarshalledMessage::Event(TestEvents::AllHeaders(parsed)) => {
2231                assert_eq!(parsed.name.as_deref(), Some("ann"));
2232                assert_eq!(parsed.extra.as_deref(), Some("from payload"));
2233            }
2234            other => panic!("unexpected result: {other:?}"),
2235        }
2236    }
2237
2238    #[test]
2239    fn implicit_payload_overwrites_event_headers() {
2240        // Generated unmarshallers apply the implicit payload parser after event headers.
2241        let protocol = json_protocol();
2242        let message = Message::new_from_parts(
2243            vec![
2244                Header::new(":message-type", HeaderValue::String("event".into())),
2245                Header::new(":event-type", HeaderValue::String("allHeaders".into())),
2246                Header::new("name", HeaderValue::String("ann".into())),
2247            ],
2248            Bytes::from_static(br#"{"name":"bob","extra":"from payload"}"#),
2249        );
2250        match unmarshall(&protocol, &message).expect("unmarshalls") {
2251            UnmarshalledMessage::Event(TestEvents::AllHeaders(parsed)) => {
2252                assert_eq!(parsed.name.as_deref(), Some("bob"));
2253                assert_eq!(parsed.extra.as_deref(), Some("from payload"));
2254            }
2255            other => panic!("unexpected result: {other:?}"),
2256        }
2257    }
2258
2259    #[test]
2260    fn implicit_payloads_validate_event_header_types_for_every_codec() {
2261        let cases: [(SharedServerProtocol, &[u8]); 3] = [
2262            (
2263                json_protocol(),
2264                br#"{"name":{"extra":"decoy"},"flag":"not a boolean","extra":"from payload"}"#,
2265            ),
2266            (
2267                cbor_protocol(),
2268                // {"name": {"extra": "decoy"}, "flag": "not a boolean", "extra": "from payload"}
2269                b"\xa3\x64name\xa1\x65extra\x65decoy\x64flag\x6dnot a boolean\x65extra\x6cfrom payload",
2270            ),
2271            (
2272                SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default()),
2273                b"<AllHeaders><name><extra>decoy</extra></name><flag>not a boolean</flag>\
2274                    <extra>from payload</extra></AllHeaders>",
2275            ),
2276        ];
2277        for (protocol, payload) in cases {
2278            let message = Message::new_from_parts(
2279                vec![
2280                    Header::new(":message-type", HeaderValue::String("event".into())),
2281                    Header::new(":event-type", HeaderValue::String("allHeaders".into())),
2282                    Header::new("name", HeaderValue::String("ann".into())),
2283                    Header::new("flag", HeaderValue::Bool(false)),
2284                ],
2285                Bytes::copy_from_slice(payload),
2286            );
2287            assert!(unmarshall(&protocol, &message).is_err());
2288        }
2289    }
2290
2291    /// Generated serde restXml servers read a received exception frame through
2292    /// `rest_xml_wrapped_errors::error_scope`: the error members sit in the `<Error>` child of an
2293    /// `<ErrorResponse>` root, and an empty payload is an error with no members set.
2294    #[test]
2295    fn rest_xml_exception_frames_read_the_legacy_error_envelope() {
2296        let protocol = SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default());
2297        let decoded = |payload: &'static [u8]| {
2298            let message = Message::new_from_parts(
2299                vec![
2300                    Header::new(":message-type", HeaderValue::String("exception".into())),
2301                    Header::new(":exception-type", HeaderValue::String("boom".into())),
2302                    Header::new(":content-type", HeaderValue::String("application/xml".into())),
2303                ],
2304                Bytes::from_static(payload),
2305            );
2306            match unmarshall(&protocol, &message) {
2307                Ok(UnmarshalledMessage::Error(TestEventsError::Boom(error))) => Ok(error),
2308                Ok(_) => panic!("an exception frame decodes as an error"),
2309                Err(err) => Err(err.to_string()),
2310            }
2311        };
2312        let failed = BoomError {
2313            message: Some("failed".to_owned()),
2314        };
2315        assert_eq!(
2316            decoded(b"<ErrorResponse><Error><message>failed</message></Error></ErrorResponse>"),
2317            Ok(failed)
2318        );
2319        assert_eq!(
2320            decoded(b"<ErrorResponse><RequestId>r</RequestId><Error><message>m</message></Error></ErrorResponse>"),
2321            Ok(BoomError {
2322                message: Some("m".to_owned())
2323            })
2324        );
2325        assert_eq!(decoded(b""), Ok(BoomError::default()));
2326        for bare in [
2327            &b"<BoomError><message>failed</message></BoomError>"[..],
2328            b"<Error><message>failed</message></Error>",
2329        ] {
2330            let bare: &'static [u8] = bare;
2331            assert!(decoded(bare).unwrap_err().contains("expected ErrorResponse as root"));
2332        }
2333        assert!(decoded(b"<ErrorResponse><Other/></ErrorResponse>")
2334            .unwrap_err()
2335            .contains("no error found inside of ErrorResponse"));
2336    }
2337
2338    /// With `@restXml(noErrorWrapping: true)`, generated serde servers read a received exception frame
2339    /// through `rest_xml_unwrapped_errors::error_scope`: the error members sit in an `<Error>`
2340    /// root. The setting comes from the protocol trait codegen records on the service schema.
2341    #[test]
2342    fn rest_xml_no_error_wrapping_exception_frames_are_rooted_at_error() {
2343        use crate::schema::protocol::RestXml;
2344        use crate::schema::traits::RestXmlTrait;
2345        use crate::schema::{MetadataRoutedProtocol, ProtocolBuildContext, ServiceSchema};
2346
2347        static TRAITS: std::sync::LazyLock<aws_smithy_schema::TraitMap> = std::sync::LazyLock::new(|| {
2348            let mut traits = aws_smithy_schema::TraitMap::new();
2349            traits.insert(Box::new(RestXmlTrait::new().with_no_error_wrapping(true)));
2350            traits
2351        });
2352        static SERVICE: ServiceSchema<'static> = ServiceSchema::new(
2353            aws_smithy_schema::shape_id!("test", "Service"),
2354            None,
2355            &[RestXmlTrait::TRAIT_ID],
2356            &[],
2357        )
2358        .with_traits(&TRAITS);
2359        let protocol = SharedServerProtocol::metadata_routed(
2360            RestXml::from_build_context(&ProtocolBuildContext::new(&SERVICE)).unwrap(),
2361        );
2362        let decoded = |payload: &'static [u8]| {
2363            let message = Message::new_from_parts(
2364                vec![
2365                    Header::new(":message-type", HeaderValue::String("exception".into())),
2366                    Header::new(":exception-type", HeaderValue::String("boom".into())),
2367                    Header::new(":content-type", HeaderValue::String("application/xml".into())),
2368                ],
2369                Bytes::from_static(payload),
2370            );
2371            match unmarshall(&protocol, &message) {
2372                Ok(UnmarshalledMessage::Error(TestEventsError::Boom(error))) => Ok(error),
2373                Ok(_) => panic!("an exception frame decodes as an error"),
2374                Err(err) => Err(err.to_string()),
2375            }
2376        };
2377        assert_eq!(
2378            decoded(b"<Error><message>failed</message></Error>"),
2379            Ok(BoomError {
2380                message: Some("failed".to_owned())
2381            })
2382        );
2383        assert_eq!(decoded(b""), Ok(BoomError::default()));
2384        assert!(
2385            decoded(b"<ErrorResponse><Error><message>failed</message></Error></ErrorResponse>")
2386                .unwrap_err()
2387                .contains("expected error as root")
2388        );
2389        assert!(decoded(b"<BoomError><message>failed</message></BoomError>")
2390            .unwrap_err()
2391            .contains("expected error as root"));
2392    }
2393
2394    #[test]
2395    fn duplicate_message_types_share_dispatch_and_xml_payload_kind() {
2396        let protocol = SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default());
2397        for payload in [
2398            "",
2399            "hello",
2400            "<ErrorResponse><Error><value>hello</value></Error></ErrorResponse>",
2401        ] {
2402            let message = Message::new_from_parts(
2403                vec![
2404                    Header::new(":message-type", HeaderValue::String("exception".into())),
2405                    Header::new(":message-type", HeaderValue::String("event".into())),
2406                    Header::new(":event-type", HeaderValue::String("text".into())),
2407                    Header::new(":content-type", HeaderValue::String("text/plain".into())),
2408                ],
2409                Bytes::copy_from_slice(payload.as_bytes()),
2410            );
2411            let expected = TextEvent {
2412                value: Some(payload.to_owned()),
2413            };
2414            match unmarshall(&protocol, &message).unwrap() {
2415                UnmarshalledMessage::Event(TestEvents::Text(event)) => assert_eq!(event, expected),
2416                other => panic!("expected a text event, got {other:?}"),
2417            }
2418            let frame = EventFrame::new(&message, protocol.event_stream_framing().unwrap());
2419            assert_eq!(TextEvent::deserialize(&mut frame.deserializer()).unwrap(), expected);
2420        }
2421
2422        let message = Message::new_from_parts(
2423            vec![
2424                Header::new(":message-type", HeaderValue::String("event".into())),
2425                Header::new(":message-type", HeaderValue::String("exception".into())),
2426                Header::new(":exception-type", HeaderValue::String("boom".into())),
2427            ],
2428            Bytes::from_static(b"<ErrorResponse><Error><message>failed</message></Error></ErrorResponse>"),
2429        );
2430        match unmarshall(&protocol, &message).unwrap() {
2431            UnmarshalledMessage::Error(TestEventsError::Boom(error)) => {
2432                assert_eq!(error.message.as_deref(), Some("failed"));
2433            }
2434            other => panic!("expected an exception, got {other:?}"),
2435        }
2436        let frame = EventFrame::new(&message, protocol.event_stream_framing().unwrap());
2437        assert_eq!(
2438            BoomError::deserialize(&mut frame.deserializer())
2439                .unwrap()
2440                .message
2441                .as_deref(),
2442            Some("failed")
2443        );
2444    }
2445
2446    #[test]
2447    fn duplicate_content_types_validate_the_last_value() {
2448        for protocol in [
2449            json_protocol(),
2450            cbor_protocol(),
2451            SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default()),
2452        ] {
2453            for event in [
2454                TestEvents::Text(TextEvent {
2455                    value: Some("hello".to_owned()),
2456                }),
2457                TestEvents::Bin(BinEvent {
2458                    value: Some(Blob::new(b"hello")),
2459                }),
2460            ] {
2461                let original = marshall(&protocol, event);
2462                for incorrect in [HeaderValue::String("application/json".into()), HeaderValue::Int32(123)] {
2463                    for incorrect_is_last in [false, true] {
2464                        let mut headers = original.headers().to_vec();
2465                        let duplicate = Header::new(":content-type", incorrect.clone());
2466                        if incorrect_is_last {
2467                            headers.push(duplicate);
2468                        } else {
2469                            headers.insert(0, duplicate);
2470                        }
2471                        let message = Message::new_from_parts(headers, original.payload().clone());
2472                        let result = unmarshall(&protocol, &message);
2473                        if incorrect_is_last {
2474                            assert!(result.is_err(), "the final content type must be validated");
2475                        } else {
2476                            let parsed = match result.unwrap() {
2477                                UnmarshalledMessage::Event(event) => event,
2478                                other => panic!("expected an event, got {other:?}"),
2479                            };
2480                            let baseline = match unmarshall(&protocol, &original).unwrap() {
2481                                UnmarshalledMessage::Event(event) => event,
2482                                other => panic!("expected an event, got {other:?}"),
2483                            };
2484                            assert_eq!(parsed, baseline);
2485                        }
2486                        // Direct frame views use the same last-header selection for MIME checks.
2487                        let frame = EventFrame::new(&message, protocol.event_stream_framing().unwrap());
2488                        let result = match string_header(&original, ":event-type").unwrap() {
2489                            "text" => TextEvent::deserialize(&mut frame.deserializer()).map(|_| ()),
2490                            "bin" => BinEvent::deserialize(&mut frame.deserializer()).map(|_| ()),
2491                            _ => unreachable!(),
2492                        };
2493                        assert_eq!(result.is_err(), incorrect_is_last);
2494                    }
2495                }
2496            }
2497        }
2498    }
2499
2500    #[test]
2501    fn duplicate_protocol_type_headers_validate_only_the_selected_value() {
2502        let protocol = json_protocol();
2503        for (name, selected, message_type) in [
2504            (":message-type", "event", "event"),
2505            (":event-type", "text", "event"),
2506            (":exception-type", "boom", "exception"),
2507        ] {
2508            for incorrect_is_last in [false, true] {
2509                let mut headers = vec![
2510                    Header::new(":message-type", HeaderValue::String(message_type.into())),
2511                    Header::new(":event-type", HeaderValue::String("text".into())),
2512                    Header::new(":exception-type", HeaderValue::String("boom".into())),
2513                    Header::new(":content-type", HeaderValue::String("text/plain".into())),
2514                ];
2515                headers.retain(|header| header.name().as_str() != name);
2516                let mut duplicates = vec![
2517                    Header::new(name, HeaderValue::Int32(123)),
2518                    Header::new(name, HeaderValue::String(selected.into())),
2519                ];
2520                if incorrect_is_last {
2521                    duplicates.reverse();
2522                }
2523                headers.extend(duplicates);
2524                let payload = if message_type == "event" {
2525                    Bytes::from_static(b"hello")
2526                } else {
2527                    Bytes::from_static(br#"{"message":"failed"}"#)
2528                };
2529                let message = Message::new_from_parts(headers, payload);
2530                let result = unmarshall(&protocol, &message);
2531                if incorrect_is_last {
2532                    assert!(result.unwrap_err().to_string().contains(name));
2533                } else {
2534                    match result.unwrap() {
2535                        UnmarshalledMessage::Event(TestEvents::Text(event)) => {
2536                            assert_eq!(event.value.as_deref(), Some("hello"));
2537                        }
2538                        UnmarshalledMessage::Error(TestEventsError::Boom(error)) => {
2539                            assert_eq!(error.message.as_deref(), Some("failed"));
2540                        }
2541                        other => panic!("unexpected result: {other:?}"),
2542                    }
2543                }
2544            }
2545        }
2546    }
2547
2548    #[test]
2549    fn duplicate_modeled_headers_validate_every_value_before_overwriting() {
2550        let protocol = json_protocol();
2551        let headers = vec![
2552            Header::new(":message-type", HeaderValue::String("event".into())),
2553            Header::new(":event-type", HeaderValue::String("allHeaders".into())),
2554            Header::new("name", HeaderValue::String("first".into())),
2555            Header::new("name", HeaderValue::String("last".into())),
2556        ];
2557        let message = Message::new_from_parts(headers.clone(), Bytes::new());
2558        match unmarshall(&protocol, &message).unwrap() {
2559            UnmarshalledMessage::Event(TestEvents::AllHeaders(event)) => {
2560                assert_eq!(event.name.as_deref(), Some("last"));
2561            }
2562            other => panic!("expected an event, got {other:?}"),
2563        }
2564        for index in [2, 3] {
2565            let mut headers = headers.clone();
2566            headers[index] = Header::new("name", HeaderValue::Int32(123));
2567            let message = Message::new_from_parts(headers, Bytes::new());
2568            assert!(unmarshall(&protocol, &message).is_err());
2569        }
2570    }
2571
2572    #[test]
2573    fn unmarshalling_error_strings() {
2574        let protocol = json_protocol();
2575
2576        let bogus_message_type = Message::new_from_parts(
2577            vec![
2578                Header::new(":message-type", HeaderValue::String("bogus".into())),
2579                Header::new(":event-type", HeaderValue::String("text".into())),
2580            ],
2581            Bytes::new(),
2582        );
2583        // `parse_response_headers` rejects the unknown type before the dispatch does, exactly
2584        // as it did on the generated path.
2585        assert_eq!(
2586            unmarshall(&protocol, &bogus_message_type).unwrap_err().to_string(),
2587            "failed to unmarshall message: unrecognized `:message-type`: bogus"
2588        );
2589
2590        let unknown_event = Message::new_from_parts(
2591            vec![
2592                Header::new(":message-type", HeaderValue::String("event".into())),
2593                Header::new(":event-type", HeaderValue::String("nope".into())),
2594            ],
2595            Bytes::new(),
2596        );
2597        assert_eq!(
2598            unmarshall(&protocol, &unknown_event).unwrap_err().to_string(),
2599            "failed to unmarshall message: unrecognized :event-type: nope"
2600        );
2601
2602        let unknown_exception = Message::new_from_parts(
2603            vec![
2604                Header::new(":message-type", HeaderValue::String("exception".into())),
2605                Header::new(":exception-type", HeaderValue::String("nope".into())),
2606            ],
2607            Bytes::new(),
2608        );
2609        assert_eq!(
2610            unmarshall(&protocol, &unknown_exception).unwrap_err().to_string(),
2611            "failed to unmarshall message: unrecognized exception: nope"
2612        );
2613
2614        let wrong_content_type = Message::new_from_parts(
2615            vec![
2616                Header::new(":message-type", HeaderValue::String("event".into())),
2617                Header::new(":event-type", HeaderValue::String("text".into())),
2618                Header::new(":content-type", HeaderValue::String("application/json".into())),
2619            ],
2620            Bytes::from_static(b"raw"),
2621        );
2622        assert!(unmarshall(&protocol, &wrong_content_type)
2623            .unwrap_err()
2624            .to_string()
2625            .contains("expected :content-type to be 'text/plain', but was 'application/json'"));
2626
2627        let invalid_utf8 = Message::new_from_parts(
2628            vec![
2629                Header::new(":message-type", HeaderValue::String("event".into())),
2630                Header::new(":event-type", HeaderValue::String("text".into())),
2631                Header::new(":content-type", HeaderValue::String("text/plain".into())),
2632            ],
2633            Bytes::from_static(b"\xff\xfe"),
2634        );
2635        assert!(unmarshall(&protocol, &invalid_utf8)
2636            .unwrap_err()
2637            .to_string()
2638            .contains("message payload is not valid UTF-8"));
2639    }
2640
2641    #[test]
2642    fn modeled_error_marshals_as_exception_frame() {
2643        let protocol = json_protocol();
2644        let marshaller = SchemaEventErrorMarshaller::<TestEventsError>::new(protocol.clone());
2645        let message = marshaller
2646            .marshall(TestEventsError::Boom(BoomError {
2647                message: Some("failure".to_owned()),
2648            }))
2649            .expect("marshalls");
2650        assert_eq!(string_header(&message, ":message-type"), Some("exception"));
2651        assert_eq!(string_header(&message, ":exception-type"), Some("boom"));
2652        assert_eq!(string_header(&message, ":content-type"), Some("application/json"));
2653        match unmarshall(&protocol, &message).expect("unmarshalls") {
2654            UnmarshalledMessage::Error(TestEventsError::Boom(parsed)) => {
2655                assert_eq!(parsed.message.as_deref(), Some("failure"));
2656            }
2657            other => panic!("unexpected result: {other:?}"),
2658        }
2659    }
2660
2661    #[test]
2662    fn aws_json_exception_frames_include_the_protocol_discriminator() {
2663        use crate::schema::protocol::{AwsJson1_0, AwsJson1_1};
2664        for (protocol, expected) in [
2665            (
2666                SharedServerProtocol::metadata_routed(AwsJson1_0::default()),
2667                "test#BoomError",
2668            ),
2669            (
2670                SharedServerProtocol::metadata_routed(AwsJson1_1::default()),
2671                "BoomError",
2672            ),
2673        ] {
2674            let marshaller = SchemaEventErrorMarshaller::<TestEventsError>::new(protocol.clone());
2675            let message = marshaller
2676                .marshall(TestEventsError::Boom(BoomError {
2677                    message: Some("failure".into()),
2678                }))
2679                .unwrap();
2680            assert_eq!(
2681                message.payload().as_ref(),
2682                format!(r#"{{"message":"failure","__type":"{expected}"}}"#).as_bytes()
2683            );
2684            assert!(matches!(
2685                unmarshall(&protocol, &message).unwrap(),
2686                UnmarshalledMessage::Error(TestEventsError::Boom(_))
2687            ));
2688        }
2689    }
2690
2691    #[test]
2692    fn unmodeled_error_marshaller_emits_a_bare_exception_frame() {
2693        let marshaller = NoModeledEventErrorMarshaller::new(json_protocol());
2694        let message = marshaller
2695            .marshall(aws_smithy_http::event_stream::MessageStreamError::unhandled(
2696                std::io::Error::other("boom"),
2697            ))
2698            .expect("marshalls");
2699        let names: Vec<_> = message.headers().iter().map(|h| h.name().as_str()).collect();
2700        assert_eq!(names, [":message-type"]);
2701        assert_eq!(string_header(&message, ":message-type"), Some("exception"));
2702        assert!(message.payload().is_empty());
2703    }
2704
2705    /// A protocol with no event stream capability rejects every operation up front.
2706    #[test]
2707    fn protocols_without_event_stream_support_are_rejected() {
2708        #[derive(Debug)]
2709        struct HttpOnly;
2710        impl crate::schema::ServerProtocol for HttpOnly {
2711            fn serialize_runtime_error(&self, error: crate::runtime_error::RuntimeError) -> crate::response::Response {
2712                panic!("unexpected extractor runtime error: {error}")
2713            }
2714
2715            fn protocol_id(&self) -> &'static ShapeId<'static> {
2716                static ID: ShapeId<'static> = aws_smithy_schema::shape_id!("test", "HttpOnly");
2717                &ID
2718            }
2719            fn deserialize_request<'a>(
2720                &'a self,
2721                _: &Schema<'_>,
2722                _: &'a aws_smithy_runtime_api::http::Request<bytes::Bytes>,
2723            ) -> Result<Box<dyn ShapeDeserializer + 'a>, DeserializeError> {
2724                unreachable!()
2725            }
2726            fn serialize_response(&self, _: &Schema<'_>, _: &dyn SerializableStruct) -> crate::response::Response {
2727                unreachable!()
2728            }
2729            fn serialize_streaming_response(
2730                &self,
2731                _: &Schema<'_>,
2732                _: &dyn SerializableStruct,
2733                _streaming_member: &Schema<'_>,
2734                _: Option<BoxBody>,
2735            ) -> crate::response::Response {
2736                unreachable!()
2737            }
2738            fn serialize_error(&self, _: &dyn crate::schema::HttpModeledError) -> crate::response::Response {
2739                unreachable!()
2740            }
2741            fn serialize_rejection(&self, _: DeserializeError) -> crate::response::Response {
2742                unreachable!()
2743            }
2744        }
2745
2746        let protocol = SharedServerProtocol::serde_only(HttpOnly);
2747        assert!(SchemaEventMarshaller::<TestEvents>::new(protocol.clone())
2748            .marshall(TestEvents::Empty(EmptyEvent))
2749            .is_err());
2750        assert!(SchemaEventMarshaller::<TestEvents>::new(protocol.clone())
2751            .marshall(TestEvents::Text(TextEvent {
2752                value: Some("raw".to_owned())
2753            }))
2754            .is_err());
2755        assert!(SchemaEventErrorMarshaller::<TestEventsError>::new(protocol.clone())
2756            .marshall(TestEventsError::Boom(BoomError { message: None }))
2757            .is_err());
2758        assert!(NoModeledEventErrorMarshaller::new(protocol.clone())
2759            .marshall(aws_smithy_http::event_stream::MessageStreamError::unhandled(
2760                std::io::Error::other("boom"),
2761            ))
2762            .is_err());
2763        assert!(SchemaEventUnmarshaller::<TestEvents>::new(protocol)
2764            .unmarshall(&Message::new(Bytes::new()))
2765            .is_err());
2766    }
2767
2768    // ---- operation glue ----
2769
2770    fn write_frame(message: &Message, out: &mut Vec<u8>) {
2771        aws_smithy_eventstream::frame::write_message_to(message, out).expect("writes");
2772    }
2773
2774    fn initial_request_frame(payload: &'static [u8], content_type: &str) -> Message {
2775        Message::new_from_parts(
2776            vec![
2777                Header::new(":message-type", HeaderValue::String("event".into())),
2778                Header::new(":event-type", HeaderValue::String("initial-request".into())),
2779                Header::new(":content-type", HeaderValue::String(content_type.to_owned().into())),
2780            ],
2781            Bytes::from_static(payload),
2782        )
2783    }
2784
2785    #[tokio::test]
2786    async fn apply_initial_request_reads_the_initial_frame() {
2787        let protocol = cbor_protocol();
2788        // CBOR map { "text": "hi" }, followed by an ordinary event.
2789        let mut body = Vec::new();
2790        write_frame(
2791            &initial_request_frame(b"\xa1\x64text\x62hi", "application/cbor"),
2792            &mut body,
2793        );
2794        write_frame(
2795            &marshall(
2796                &protocol,
2797                TestEvents::Text(TextEvent {
2798                    value: Some("evt".to_owned()),
2799                }),
2800            ),
2801            &mut body,
2802        );
2803        let unmarshaller = SchemaEventUnmarshaller::<TestEvents>::new(protocol.clone());
2804        let mut receiver = Receiver::new(unmarshaller, aws_smithy_types::body::SdkBody::from(body));
2805        let mut applied = None;
2806        apply_initial_request(
2807            |mt| receiver.try_recv_initial(mt),
2808            &protocol,
2809            |deser| {
2810                applied = Some(PlainEvent::deserialize(deser)?);
2811                Ok(())
2812            },
2813        )
2814        .await
2815        .expect("applies");
2816        assert_eq!(applied.and_then(|p| p.text).as_deref(), Some("hi"));
2817        // The ordinary event that follows is preserved.
2818        let event = receiver.recv().await.expect("a frame").expect("an event");
2819        assert_eq!(
2820            event,
2821            TestEvents::Text(TextEvent {
2822                value: Some("evt".to_owned())
2823            })
2824        );
2825    }
2826
2827    #[tokio::test]
2828    async fn apply_initial_request_preserves_a_first_ordinary_event() {
2829        let protocol = cbor_protocol();
2830        let mut body = Vec::new();
2831        write_frame(
2832            &marshall(
2833                &protocol,
2834                TestEvents::Text(TextEvent {
2835                    value: Some("evt".to_owned()),
2836                }),
2837            ),
2838            &mut body,
2839        );
2840        let unmarshaller = SchemaEventUnmarshaller::<TestEvents>::new(protocol.clone());
2841        let mut receiver = Receiver::new(unmarshaller, aws_smithy_types::body::SdkBody::from(body));
2842        apply_initial_request(
2843            |mt| receiver.try_recv_initial(mt),
2844            &protocol,
2845            |_| panic!("no initial frame to apply"),
2846        )
2847        .await
2848        .expect("no initial frame is fine");
2849        let event = receiver.recv().await.expect("a frame").expect("an event");
2850        assert_eq!(
2851            event,
2852            TestEvents::Text(TextEvent {
2853                value: Some("evt".to_owned())
2854            })
2855        );
2856    }
2857
2858    #[tokio::test]
2859    async fn apply_initial_request_skips_protocols_without_initial_frames() {
2860        let protocol = json_protocol();
2861        let unmarshaller = SchemaEventUnmarshaller::<TestEvents>::new(protocol.clone());
2862        let mut receiver = Receiver::new(unmarshaller, aws_smithy_types::body::SdkBody::from(Vec::<u8>::new()));
2863        apply_initial_request(
2864            |mt| receiver.try_recv_initial(mt),
2865            &protocol,
2866            |_| panic!("restJson1 carries no initial frames"),
2867        )
2868        .await
2869        .expect("nothing to do");
2870    }
2871
2872    #[tokio::test]
2873    async fn apply_initial_request_reports_malformed_frames() {
2874        let protocol = cbor_protocol();
2875        let unmarshaller = SchemaEventUnmarshaller::<TestEvents>::new(protocol.clone());
2876        let mut receiver = Receiver::new(
2877            unmarshaller,
2878            aws_smithy_types::body::SdkBody::from(&b"malformed frame"[..]),
2879        );
2880        let err = apply_initial_request(|mt| receiver.try_recv_initial(mt), &protocol, |_| Ok(()))
2881            .await
2882            .expect_err("malformed frame");
2883        assert!(matches!(&err, DeserializeError::InitialRequestFrame(_)));
2884        assert!(err.to_string().contains("failed to read the initial-request frame"));
2885    }
2886
2887    async fn collect_frames(body: BoxBody) -> Vec<Message> {
2888        use http_body_util::BodyExt;
2889        let mut bytes = body.collect().await.expect("collects").to_bytes();
2890        let mut frames = Vec::new();
2891        while !bytes.is_empty() {
2892            frames.push(aws_smithy_eventstream::frame::read_message_from(&mut bytes).expect("valid frame"));
2893        }
2894        frames
2895    }
2896
2897    fn response_body_for(
2898        protocol: &SharedServerProtocol,
2899        initial_response: InitialResponsePolicy,
2900    ) -> Result<BoxBody, SerdeError> {
2901        let events: EventStreamSender<TestEvents, TestEventsError> =
2902            futures_util::stream::iter([Ok(TestEvents::Text(TextEvent {
2903                value: Some("evt".to_owned()),
2904            }))])
2905            .into();
2906        // The non-stream output members: reuse a simple struct as the output shape.
2907        let output = PlainEvent {
2908            text: Some("topic".to_owned()),
2909        };
2910        event_stream_response_body(
2911            &PLAIN_EVENT_SCHEMA,
2912            &output,
2913            events,
2914            SchemaEventMarshaller::<TestEvents>::new(protocol.clone()),
2915            SchemaEventErrorMarshaller::<TestEventsError>::new(protocol.clone()),
2916            protocol,
2917            initial_response,
2918        )
2919    }
2920
2921    #[tokio::test]
2922    async fn response_body_prepends_the_initial_response_frame_when_asked() {
2923        let body = response_body_for(&cbor_protocol(), InitialResponsePolicy::Send).expect("builds");
2924        let frames = collect_frames(body).await;
2925        assert_eq!(frames.len(), 2);
2926        assert_eq!(string_header(&frames[0], ":event-type"), Some("initial-response"));
2927        assert_eq!(string_header(&frames[0], ":content-type"), Some("application/cbor"));
2928        assert_eq!(string_header(&frames[1], ":event-type"), Some("text"));
2929    }
2930
2931    #[tokio::test]
2932    async fn response_body_rpc_initial_response_preserves_output_members() {
2933        use crate::schema::protocol::{AwsJson1_0, AwsJson1_1};
2934        for protocol in [
2935            cbor_protocol(),
2936            SharedServerProtocol::metadata_routed(AwsJson1_0::default()),
2937            SharedServerProtocol::metadata_routed(AwsJson1_1::default()),
2938        ] {
2939            let frames = collect_frames(response_body_for(&protocol, InitialResponsePolicy::Send).unwrap()).await;
2940            assert_eq!(frames.len(), 2);
2941            assert_eq!(string_header(&frames[0], ":event-type"), Some("initial-response"));
2942            let capability = protocol.event_stream_framing().unwrap();
2943            assert!(capability.initial_messages_in_frames);
2944            let mut deser = capability.payload_codec.create_deserializer(frames[0].payload());
2945            assert_eq!(
2946                PlainEvent::deserialize(&mut *deser).unwrap(),
2947                PlainEvent {
2948                    text: Some("topic".to_owned())
2949                }
2950            );
2951        }
2952    }
2953
2954    #[tokio::test]
2955    async fn response_body_omits_the_initial_response_frame_when_disabled() {
2956        let body = response_body_for(&cbor_protocol(), InitialResponsePolicy::Omit).expect("builds");
2957        let frames = collect_frames(body).await;
2958        assert_eq!(frames.len(), 1);
2959        assert_eq!(string_header(&frames[0], ":event-type"), Some("text"));
2960    }
2961
2962    #[tokio::test]
2963    async fn response_body_rest_initial_response_frames_match_legacy() {
2964        // Observed with generated serde servers and alwaysSendEventStreamInitialResponse=true:
2965        // REST JSON sends {}, REST XML sends zero bytes, and HTTP-bound output members stay out.
2966        for (protocol, media_type, payload) in [
2967            (json_protocol(), "application/json", &b"{}"[..]),
2968            (
2969                SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default()),
2970                "application/xml",
2971                &b""[..],
2972            ),
2973        ] {
2974            assert!(!protocol.event_stream_framing().unwrap().initial_messages_in_frames);
2975            let frames = collect_frames(response_body_for(&protocol, InitialResponsePolicy::Send).unwrap()).await;
2976            assert_eq!(frames.len(), 2);
2977            assert_eq!(string_header(&frames[0], ":message-type"), Some("event"));
2978            assert_eq!(string_header(&frames[0], ":event-type"), Some("initial-response"));
2979            assert_eq!(string_header(&frames[0], ":content-type"), Some(media_type));
2980            assert_eq!(frames[0].payload().as_ref(), payload);
2981            assert_eq!(string_header(&frames[1], ":event-type"), Some("text"));
2982
2983            let omitted = collect_frames(response_body_for(&protocol, InitialResponsePolicy::Omit).unwrap()).await;
2984            assert_eq!(omitted.len(), 1);
2985            assert_eq!(omitted[0], frames[1]);
2986        }
2987    }
2988
2989    #[tokio::test]
2990    async fn response_body_rest_initial_response_does_not_poll_application_events() {
2991        use http_body_util::BodyExt;
2992        for protocol in [
2993            json_protocol(),
2994            SharedServerProtocol::metadata_routed(crate::schema::protocol::RestXml::default()),
2995        ] {
2996            let events: EventStreamSender<TestEvents, TestEventsError> = futures_util::stream::poll_fn(|_| {
2997                panic!("the initial response must arrive before polling application events");
2998                #[allow(unreachable_code)]
2999                std::task::Poll::Ready(None)
3000            })
3001            .into();
3002            let output = PlainEvent {
3003                text: Some("topic".to_owned()),
3004            };
3005            let mut body = event_stream_response_body(
3006                &PLAIN_EVENT_SCHEMA,
3007                &output,
3008                events,
3009                SchemaEventMarshaller::<TestEvents>::new(protocol.clone()),
3010                SchemaEventErrorMarshaller::<TestEventsError>::new(protocol.clone()),
3011                &protocol,
3012                InitialResponsePolicy::Send,
3013            )
3014            .unwrap();
3015            let bytes = tokio::time::timeout(std::time::Duration::from_secs(1), body.frame())
3016                .await
3017                .expect("initial response arrives on an idle stream")
3018                .unwrap()
3019                .unwrap()
3020                .into_data()
3021                .unwrap();
3022            let mut bytes = bytes.as_ref();
3023            let frame = aws_smithy_eventstream::frame::read_message_from(&mut bytes).unwrap();
3024            assert_eq!(string_header(&frame, ":event-type"), Some("initial-response"));
3025            assert!(bytes.is_empty());
3026        }
3027    }
3028
3029    #[tokio::test]
3030    async fn response_body_carries_modeled_errors_as_exception_frames() {
3031        let protocol = json_protocol();
3032        let events: EventStreamSender<TestEvents, TestEventsError> =
3033            futures_util::stream::iter([Err(TestEventsError::Boom(BoomError {
3034                message: Some("failure".to_owned()),
3035            }))])
3036            .into();
3037        let output = PlainEvent { text: None };
3038        let body = event_stream_response_body(
3039            &PLAIN_EVENT_SCHEMA,
3040            &output,
3041            events,
3042            SchemaEventMarshaller::<TestEvents>::new(protocol.clone()),
3043            SchemaEventErrorMarshaller::<TestEventsError>::new(protocol.clone()),
3044            &protocol,
3045            InitialResponsePolicy::Omit,
3046        )
3047        .expect("builds");
3048        let frames = collect_frames(body).await;
3049        assert_eq!(frames.len(), 1);
3050        assert_eq!(string_header(&frames[0], ":message-type"), Some("exception"));
3051        assert_eq!(string_header(&frames[0], ":exception-type"), Some("boom"));
3052    }
3053}