Skip to main content

aws_smithy_http_server/schema/
mod.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6use crate::config::RequestBodyCollectionConfig;
7
8mod deserialize;
9pub mod event_stream;
10mod modeled_error;
11pub mod protocol;
12pub(crate) mod request_bindings;
13pub(crate) mod response_bindings;
14pub mod routing;
15mod service;
16pub mod settings;
17mod timestamp;
18pub mod traits;
19
20pub use deserialize::{DeserializableShape, DeserializeError};
21pub use event_stream::InitialResponsePolicy;
22pub use modeled_error::HttpModeledError;
23pub(crate) use protocol::body_collection_rejection;
24pub use protocol::{
25    collect_request_body, BodyDirective, BodyRoutedProtocol, EventStreamFraming, MetadataRoutedProtocol,
26    ProtocolBuildContext, ProtocolFactory, ProtocolOrder, ProtocolRegistration, ProtocolRegistry,
27    RequestBodyCollectionError, ServerProtocol, SharedServerProtocol,
28};
29
30pub use service::{OperationSchema, ServiceSchema};
31
32/// A read-only view of the protocol and operation selected by routing, with the operation's
33/// request-body limits.
34///
35/// The protocol is erased: everything after routing works through `dyn ServerProtocol`.
36///
37/// The operation schema is stored for consumers that are not generic over the operation:
38/// middleware and [`FromParts`](crate::request::FromParts) extractors use
39/// [`get_from_request`](Self::get_from_request) or [`get_from_extensions`](Self::get_from_extensions) to
40/// learn which operation was selected — for logging, auth, or metrics — without naming an `Op`
41/// type. See the middleware example on [`ServerProtocol`].
42///
43/// Routing stores this view in a private extension carrier. Inserting or removing
44/// `SelectedOperation` directly in request extensions does not change the routed selection.
45/// Middleware must preserve request extensions: clearing or replacing all extensions removes
46/// the carrier, causing the upgrade or missing-handler fallback to return an empty `500`
47/// without polling the body or invoking the handler. Routing may already have collected the body
48/// before middleware runs.
49#[derive(Clone)]
50pub struct SelectedOperation {
51    protocol: SharedServerProtocol,
52    operation: &'static OperationSchema<'static>,
53    request_body: RequestBodyCollectionConfig,
54}
55
56#[derive(Clone)]
57struct SelectedOperationExtension(SelectedOperation);
58
59impl SelectedOperation {
60    /// Borrows the protocol and operation selected for this request from its extensions.
61    ///
62    /// Returns `None` if no selection is stored, including before schema routing runs.
63    pub fn get_from_request<B>(request: &http::Request<B>) -> Option<&Self> {
64        Self::get_from_extensions(request.extensions())
65    }
66
67    /// Borrows the routed selection from request extensions, including in a
68    /// [`FromParts`](crate::request::FromParts) extractor.
69    ///
70    /// Returns `None` before routing or if middleware discarded the request extensions.
71    pub fn get_from_extensions(extensions: &http::Extensions) -> Option<&Self> {
72        extensions.get::<SelectedOperationExtension>().map(|stored| &stored.0)
73    }
74
75    pub(crate) fn get_from_runtime_request<B>(request: &aws_smithy_runtime_api::http::Request<B>) -> Option<&Self> {
76        request
77            .extension::<SelectedOperationExtension>()
78            .map(|stored| &stored.0)
79    }
80
81    pub(crate) fn insert_into(self, extensions: &mut http::Extensions) {
82        extensions.insert(SelectedOperationExtension(self));
83    }
84
85    /// Only the router creates the selection; middleware cannot replace its operation,
86    /// protocol, or request-body limits.
87    pub(crate) fn new(
88        protocol: SharedServerProtocol,
89        operation: &'static OperationSchema<'static>,
90        request_body: RequestBodyCollectionConfig,
91    ) -> Self {
92        Self {
93            protocol,
94            operation,
95            request_body,
96        }
97    }
98
99    /// The selected protocol.
100    pub fn protocol(&self) -> &SharedServerProtocol {
101        &self.protocol
102    }
103
104    /// The routed operation's schema, for code that cannot name the operation type.
105    pub fn operation(&self) -> &'static OperationSchema<'static> {
106        self.operation
107    }
108
109    /// The routed operation's request-body collection limits, resolved when the router was built.
110    ///
111    /// Carrying them here keeps the per-operation upgrade services free of per-operation state.
112    pub fn request_body_config(&self) -> RequestBodyCollectionConfig {
113        self.request_body
114    }
115}
116
117impl std::fmt::Debug for SelectedOperation {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        f.debug_struct("SelectedOperation")
120            .field("protocol", &self.protocol.protocol_id())
121            .field("operation", &self.operation.shape_id())
122            .field("request_body", &self.request_body)
123            .finish()
124    }
125}