Skip to main content

aws_smithy_http_server/
config.rs

1/*
2 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6//! Typed service configuration shared by generated and schema-based implementations.
7
8use crate::settings::{
9    DEFAULT_EVENT_STREAM_IDLE_TIMEOUT, DEFAULT_EVENT_STREAM_INITIAL_MESSAGE_TIMEOUT,
10    DEFAULT_EVENT_STREAM_MESSAGE_COMPLETION_TIMEOUT,
11};
12use crate::settings::{DEFAULT_REQUEST_BODY_MAX_BYTES, DEFAULT_REQUEST_BODY_READ_POLL_BUDGET};
13use aws_smithy_schema::ShapeId;
14use std::{collections::HashMap, num::NonZeroUsize, time::Duration};
15
16/// Typed service-wide configuration shared by every protocol.
17#[derive(Clone, Debug)]
18#[non_exhaustive]
19pub struct ServiceConfig {
20    /// Request-head deadline applied by the generated SDK's `serve` function.
21    ///
22    /// Defaults to one minute. `None` disables it. Covers the first request head
23    /// for HTTP/1 and HTTP/2, and later HTTP/1 heads; later HTTP/2 streams are not covered.
24    pub header_read_timeout: Option<Duration>,
25    /// Global request body collection allowances, poll budget, and per-operation overrides.
26    ///
27    /// Generated serde reads the immutable SDK configuration.
28    /// Operation overrides replace the whole allowance record.
29    pub request_body: ServiceRequestBodyConfig,
30    /// Event-stream read deadlines and per-operation overrides.
31    pub event_stream: ServiceEventStreamConfig,
32}
33
34impl Default for ServiceConfig {
35    fn default() -> Self {
36        Self {
37            header_read_timeout: Some(crate::settings::DEFAULT_HEADER_READ_TIMEOUT),
38            request_body: ServiceRequestBodyConfig::default(),
39            event_stream: ServiceEventStreamConfig::default(),
40        }
41    }
42}
43
44impl ServiceConfig {
45    /// Sets the request-head deadline. `None` or zero disables it.
46    pub fn with_header_read_timeout(mut self, timeout: Option<Duration>) -> Self {
47        self.header_read_timeout = timeout.filter(|timeout| !timeout.is_zero());
48        self
49    }
50
51    pub fn with_event_stream(mut self, event_stream: ServiceEventStreamConfig) -> Self {
52        self.event_stream = event_stream;
53        self
54    }
55
56    /// Sets the request body collection and streaming poll configuration.
57    pub fn with_request_body(mut self, request_body: ServiceRequestBodyConfig) -> Self {
58        self.request_body = request_body;
59        self
60    }
61}
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64#[non_exhaustive]
65pub struct RequestBodyCollectionConfig {
66    pub max_bytes: Option<NonZeroUsize>,
67    pub read_timeout: Option<Duration>,
68    /// Consecutive ready HTTP body frame polls before yielding, for collection and streaming.
69    /// Defaults to 32; `None` disables forced yielding.
70    pub read_poll_budget: Option<NonZeroUsize>,
71}
72
73impl Default for RequestBodyCollectionConfig {
74    fn default() -> Self {
75        Self {
76            max_bytes: NonZeroUsize::new(DEFAULT_REQUEST_BODY_MAX_BYTES),
77            read_timeout: None,
78            read_poll_budget: NonZeroUsize::new(DEFAULT_REQUEST_BODY_READ_POLL_BUDGET),
79        }
80    }
81}
82
83#[derive(Debug, Default, Clone)]
84#[non_exhaustive]
85pub struct ServiceRequestBodyConfig {
86    pub global: RequestBodyCollectionConfig,
87    pub per_operation: HashMap<String, RequestBodyCollectionConfig>,
88}
89
90impl RequestBodyCollectionConfig {
91    /// Sets the byte limit; `None` allows any size.
92    pub fn with_max_bytes(mut self, max_bytes: Option<NonZeroUsize>) -> Self {
93        self.max_bytes = max_bytes;
94        self
95    }
96
97    /// Sets the read timeout; `None` disables it.
98    pub fn with_read_timeout(mut self, read_timeout: Option<Duration>) -> Self {
99        self.read_timeout = read_timeout;
100        self
101    }
102
103    /// Sets the frame poll budget for collection and streaming; `None` disables forced yielding.
104    pub fn with_read_poll_budget(mut self, read_poll_budget: Option<NonZeroUsize>) -> Self {
105        self.read_poll_budget = read_poll_budget;
106        self
107    }
108}
109
110impl ServiceRequestBodyConfig {
111    /// Sets the default body-read allowances.
112    pub fn with_global(mut self, global: RequestBodyCollectionConfig) -> Self {
113        self.global = global;
114        self
115    }
116
117    /// Sets operation overrides, keyed by operation shape ID.
118    pub fn with_per_operation(mut self, per_operation: HashMap<String, RequestBodyCollectionConfig>) -> Self {
119        self.per_operation = per_operation;
120        self
121    }
122
123    /// Computes the provisional allowance before the operation is known.
124    /// An absent operation override inherits `global`; an unlimited field dominates the maximum.
125    pub fn for_routing(&self) -> RequestBodyCollectionConfig {
126        let mut maximum = self.global;
127        for config in self.per_operation.values() {
128            maximum.max_bytes = match (maximum.max_bytes, config.max_bytes) {
129                (Some(left), Some(right)) => Some(left.max(right)),
130                _ => None,
131            };
132            maximum.read_timeout = match (maximum.read_timeout, config.read_timeout) {
133                (Some(left), Some(right)) => Some(left.max(right)),
134                _ => None,
135            };
136            maximum.read_poll_budget = match (maximum.read_poll_budget, config.read_poll_budget) {
137                (Some(left), Some(right)) => Some(left.max(right)),
138                _ => None,
139            };
140        }
141        maximum
142    }
143
144    pub fn for_operation(&self, operation: &ShapeId<'_>) -> RequestBodyCollectionConfig {
145        self.per_operation
146            .get(operation.as_str())
147            .copied()
148            .unwrap_or(self.global)
149    }
150}
151
152/// Independent event-stream read deadlines. `None` disables a deadline.
153///
154/// The first RPC frame is read before invoking the handler, including when it is
155/// an ordinary event from a stream-only input. Only `initial_message` bounds this
156/// read. If it is disabled, neither `message_completion` nor `idle` provides a
157/// fallback, so the first RPC frame has no event-stream read deadline.
158#[derive(Debug, Clone, Copy, PartialEq, Eq)]
159#[non_exhaustive]
160pub struct EventStreamTimeoutConfig {
161    /// Bounds the entire first RPC frame, including waiting for its first bytes.
162    /// Does not apply to REST streams.
163    pub initial_message: Option<Duration>,
164    /// Bounds reading a frame from its first bytes until completion. Applies to
165    /// every REST frame and to RPC frames after the first.
166    pub message_completion: Option<Duration>,
167    /// Bounds waiting for a frame's first bytes. Applies before the first REST
168    /// frame and between frames, after the first RPC frame completes.
169    pub idle: Option<Duration>,
170}
171
172impl Default for EventStreamTimeoutConfig {
173    fn default() -> Self {
174        Self {
175            initial_message: DEFAULT_EVENT_STREAM_INITIAL_MESSAGE_TIMEOUT,
176            message_completion: DEFAULT_EVENT_STREAM_MESSAGE_COMPLETION_TIMEOUT,
177            idle: DEFAULT_EVENT_STREAM_IDLE_TIMEOUT,
178        }
179    }
180}
181
182impl EventStreamTimeoutConfig {
183    pub fn with_initial_message(mut self, timeout: Option<Duration>) -> Self {
184        self.initial_message = timeout.filter(|timeout| !timeout.is_zero());
185        self
186    }
187
188    pub fn with_message_completion(mut self, timeout: Option<Duration>) -> Self {
189        self.message_completion = timeout.filter(|timeout| !timeout.is_zero());
190        self
191    }
192
193    pub fn with_idle(mut self, timeout: Option<Duration>) -> Self {
194        self.idle = timeout.filter(|timeout| !timeout.is_zero());
195        self
196    }
197}
198
199/// Service defaults and complete per-operation event-stream timeout records.
200#[derive(Debug, Default, Clone)]
201#[non_exhaustive]
202pub struct ServiceEventStreamConfig {
203    pub global: EventStreamTimeoutConfig,
204    pub per_operation: HashMap<String, EventStreamTimeoutConfig>,
205}
206
207impl ServiceEventStreamConfig {
208    pub fn with_global(mut self, global: EventStreamTimeoutConfig) -> Self {
209        self.global = global;
210        self
211    }
212
213    pub fn with_per_operation(mut self, per_operation: HashMap<String, EventStreamTimeoutConfig>) -> Self {
214        self.per_operation = per_operation;
215        self
216    }
217
218    pub fn for_operation(&self, operation: &ShapeId<'_>) -> EventStreamTimeoutConfig {
219        self.per_operation
220            .get(operation.as_str())
221            .copied()
222            .unwrap_or(self.global)
223    }
224}