Skip to main content

aws_smithy_legacy_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, defaulting to one minute; `None` disables it.
21    ///
22    /// Legacy servers manage their own Hyper accept loop and must apply this
23    /// value to their connection builder. This field alone does not enforce it.
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    pub fn for_operation(&self, operation: &ShapeId<'_>) -> RequestBodyCollectionConfig {
124        self.per_operation
125            .get(operation.as_str())
126            .copied()
127            .unwrap_or(self.global)
128    }
129}
130
131/// Independent event-stream read deadlines. `None` disables a deadline.
132///
133/// The first RPC frame is read before invoking the handler, including when it is
134/// an ordinary event from a stream-only input. Only `initial_message` bounds this
135/// read. If it is disabled, neither `message_completion` nor `idle` provides a
136/// fallback, so the first RPC frame has no event-stream read deadline.
137#[derive(Debug, Clone, Copy, PartialEq, Eq)]
138#[non_exhaustive]
139pub struct EventStreamTimeoutConfig {
140    /// Bounds the entire first RPC frame, including waiting for its first bytes.
141    /// Does not apply to REST streams.
142    pub initial_message: Option<Duration>,
143    /// Bounds reading a frame from its first bytes until completion. Applies to
144    /// every REST frame and to RPC frames after the first.
145    pub message_completion: Option<Duration>,
146    /// Bounds waiting for a frame's first bytes. Applies before the first REST
147    /// frame and between frames, after the first RPC frame completes.
148    pub idle: Option<Duration>,
149}
150
151impl Default for EventStreamTimeoutConfig {
152    fn default() -> Self {
153        Self {
154            initial_message: DEFAULT_EVENT_STREAM_INITIAL_MESSAGE_TIMEOUT,
155            message_completion: DEFAULT_EVENT_STREAM_MESSAGE_COMPLETION_TIMEOUT,
156            idle: DEFAULT_EVENT_STREAM_IDLE_TIMEOUT,
157        }
158    }
159}
160
161impl EventStreamTimeoutConfig {
162    pub fn with_initial_message(mut self, timeout: Option<Duration>) -> Self {
163        self.initial_message = timeout.filter(|timeout| !timeout.is_zero());
164        self
165    }
166
167    pub fn with_message_completion(mut self, timeout: Option<Duration>) -> Self {
168        self.message_completion = timeout.filter(|timeout| !timeout.is_zero());
169        self
170    }
171
172    pub fn with_idle(mut self, timeout: Option<Duration>) -> Self {
173        self.idle = timeout.filter(|timeout| !timeout.is_zero());
174        self
175    }
176}
177
178/// Service defaults and complete per-operation event-stream timeout records.
179#[derive(Debug, Default, Clone)]
180#[non_exhaustive]
181pub struct ServiceEventStreamConfig {
182    pub global: EventStreamTimeoutConfig,
183    pub per_operation: HashMap<String, EventStreamTimeoutConfig>,
184}
185
186impl ServiceEventStreamConfig {
187    pub fn with_global(mut self, global: EventStreamTimeoutConfig) -> Self {
188        self.global = global;
189        self
190    }
191
192    pub fn with_per_operation(mut self, per_operation: HashMap<String, EventStreamTimeoutConfig>) -> Self {
193        self.per_operation = per_operation;
194        self
195    }
196
197    pub fn for_operation(&self, operation: &ShapeId<'_>) -> EventStreamTimeoutConfig {
198        self.per_operation
199            .get(operation.as_str())
200            .copied()
201            .unwrap_or(self.global)
202    }
203}