1use std::{
7 convert::Infallible,
8 future::{Future, Ready},
9 marker::PhantomData,
10 pin::Pin,
11 task::{Context, Poll},
12 time::Duration,
13};
14
15use futures_util::ready;
16use http::{header, StatusCode, Version};
17use pin_project_lite::pin_project;
18use tower::{util::Oneshot, Service, ServiceExt};
19use tracing::error;
20
21use crate::{
22 body::BoxBody, plugin::Plugin, request::FromRequest, response::IntoResponse,
23 runtime_error::InternalFailureException, service::ServiceShape, settings::RequestBodyReadTimeoutConfig,
24};
25
26use super::OperationShape;
27
28#[derive(Debug, Clone)]
33pub struct UpgradePlugin<Extractors> {
34 _extractors: PhantomData<Extractors>,
35 request_body_read_timeout: Option<RequestBodyReadTimeoutConfig>,
36}
37
38impl<Extractors> Default for UpgradePlugin<Extractors> {
39 fn default() -> Self {
40 Self {
41 _extractors: PhantomData,
42 request_body_read_timeout: None,
43 }
44 }
45}
46
47impl<Extractors> UpgradePlugin<Extractors> {
48 pub fn new() -> Self {
50 Self::default()
51 }
52
53 #[doc(hidden)]
55 pub fn with_default_request_body_read_timeout(mut self, payload: bool, operation: &'static str) -> Self {
56 self.request_body_read_timeout = RequestBodyReadTimeoutConfig::default_for_operation(payload, operation);
57 self
58 }
59
60 #[doc(hidden)]
64 pub fn with_request_body_read_timeout(mut self, timeout: Duration, operation: &'static str) -> Self {
65 self.request_body_read_timeout = RequestBodyReadTimeoutConfig::new(timeout, operation);
66 self
67 }
68}
69
70impl<Ser, Op, T, Extractors> Plugin<Ser, Op, T> for UpgradePlugin<Extractors>
71where
72 Ser: ServiceShape,
73 Op: OperationShape,
74{
75 type Output = Upgrade<Ser::Protocol, (Op::Input, Extractors), T>;
76
77 fn apply(&self, inner: T) -> Self::Output {
78 Upgrade {
79 _protocol: PhantomData,
80 _input: PhantomData,
81 inner,
82 request_body_read_timeout: self.request_body_read_timeout,
83 }
84 }
85}
86
87pub struct Upgrade<Protocol, Input, S> {
90 _protocol: PhantomData<Protocol>,
91 _input: PhantomData<Input>,
92 inner: S,
93 request_body_read_timeout: Option<RequestBodyReadTimeoutConfig>,
94}
95
96impl<P, Input, S> Clone for Upgrade<P, Input, S>
97where
98 S: Clone,
99{
100 fn clone(&self) -> Self {
101 Self {
102 _protocol: PhantomData,
103 _input: PhantomData,
104 inner: self.inner.clone(),
105 request_body_read_timeout: self.request_body_read_timeout,
106 }
107 }
108}
109
110struct RequestBodyReadTimeoutState {
111 sleep: Option<Pin<Box<tokio::time::Sleep>>>,
115 timeout: Duration,
116 version: Version,
117 operation: &'static str,
118 #[cfg(feature = "request-id")]
119 request_id: Option<String>,
120}
121
122impl RequestBodyReadTimeoutState {
123 fn is_expired(&mut self, cx: &mut Context<'_>) -> bool {
124 self.sleep.as_mut().is_some_and(|sleep| {
125 tokio::time::Instant::now() >= sleep.deadline() || sleep.as_mut().poll(cx).is_ready()
128 })
129 }
130
131 fn response(&self) -> http::Response<crate::body::BoxBody> {
132 #[cfg(feature = "request-id")]
133 if let Some(request_id) = self.request_id.as_deref() {
134 tracing::debug!(
135 operation = self.operation,
136 request_id,
137 timeout_millis = self.timeout.as_millis(),
138 "request body read timed out"
139 );
140 } else {
141 tracing::debug!(
142 operation = self.operation,
143 timeout_millis = self.timeout.as_millis(),
144 "request body read timed out"
145 );
146 }
147 #[cfg(not(feature = "request-id"))]
148 tracing::debug!(
149 operation = self.operation,
150 timeout_millis = self.timeout.as_millis(),
151 "request body read timed out"
152 );
153
154 let mut response = http::Response::builder().status(StatusCode::REQUEST_TIMEOUT);
155 if matches!(self.version, Version::HTTP_10 | Version::HTTP_11) {
156 response = response.header(header::CONNECTION, "close");
157 }
158 response.body(crate::body::empty()).expect("valid response")
159 }
160}
161
162#[cfg(feature = "request-id")]
163fn request_id<B>(req: &http::Request<B>) -> Option<String> {
164 req.extensions()
165 .get::<crate::request::request_id::ServerRequestId>()
166 .map(ToString::to_string)
167}
168
169pin_project! {
170 #[project = InnerProj]
171 #[project_replace = InnerProjReplace]
172 enum Inner<FromFut, HandlerFut> {
173 FromRequest {
174 #[pin]
175 inner: FromFut
176 },
177 Inner {
178 #[pin]
179 call: HandlerFut
180 }
181 }
182}
183
184type InnerAlias<Input, Protocol, B, S> = Inner<<Input as FromRequest<Protocol, B>>::Future, Oneshot<S, Input>>;
185
186pin_project! {
187 pub struct UpgradeFuture<Protocol, Input, B, S>
189 where
190 Input: FromRequest<Protocol, B>,
191 S: Service<Input>,
192 {
193 service: Option<S>,
194 #[pin]
195 inner: InnerAlias<Input, Protocol, B, S>,
196 request_body_read_timeout: Option<RequestBodyReadTimeoutState>,
197 }
198}
199
200impl<P, Input, B, S> Future for UpgradeFuture<P, Input, B, S>
201where
202 Input: FromRequest<P, B>,
203 <Input as FromRequest<P, B>>::Rejection: std::fmt::Display,
204 S: Service<Input>,
205 S::Response: IntoResponse<P>,
206 S::Error: IntoResponse<P>,
207{
208 type Output = Result<http::Response<crate::body::BoxBody>, Infallible>;
209
210 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
211 loop {
212 let mut this = self.as_mut().project();
213 let this2 = this.inner.as_mut().project();
214
215 let call = match this2 {
216 InnerProj::FromRequest { inner } => {
217 if let Some(timeout) = this.request_body_read_timeout {
218 if timeout.is_expired(cx) {
219 return Poll::Ready(Ok(timeout.response()));
220 }
221 }
222 let result = inner.poll(cx);
223 if let Some(timeout) = this.request_body_read_timeout {
224 if result.is_pending() && timeout.sleep.is_none() {
225 timeout.sleep = Some(Box::pin(tokio::time::sleep(timeout.timeout)));
226 }
227 if timeout.is_expired(cx) {
230 return Poll::Ready(Ok(timeout.response()));
231 }
232 }
233 let result = match result {
234 Poll::Ready(result) => result,
235 Poll::Pending => return Poll::Pending,
236 };
237 *this.request_body_read_timeout = None;
238 match result {
239 Ok(ok) => this
240 .service
241 .take()
242 .expect("futures cannot be polled after completion")
243 .oneshot(ok),
244 Err(err) => {
245 tracing::trace!(error = %err, "parameter for the handler cannot be constructed");
249 return Poll::Ready(Ok(err.into_response()));
250 }
251 }
252 }
253 InnerProj::Inner { call } => {
254 let result = ready!(call.poll(cx));
255 let output = match result {
256 Ok(ok) => ok.into_response(),
257 Err(err) => err.into_response(),
258 };
259 return Poll::Ready(Ok(output));
260 }
261 };
262
263 this.inner.as_mut().project_replace(Inner::Inner { call });
264 }
265 }
266}
267
268impl<P, Input, B, S> Service<http::Request<B>> for Upgrade<P, Input, S>
269where
270 Input: FromRequest<P, B>,
271 <Input as FromRequest<P, B>>::Rejection: std::fmt::Display,
272 S: Service<Input> + Clone,
273 S::Response: IntoResponse<P>,
274 S::Error: IntoResponse<P>,
275{
276 type Response = http::Response<crate::body::BoxBody>;
277 type Error = Infallible;
278 type Future = UpgradeFuture<P, Input, B, S>;
279
280 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
281 Poll::Ready(Ok(()))
284 }
285
286 fn call(&mut self, req: http::Request<B>) -> Self::Future {
287 let clone = self.inner.clone();
288 let service = std::mem::replace(&mut self.inner, clone);
289 let request_body_read_timeout = self
290 .request_body_read_timeout
291 .map(|config| RequestBodyReadTimeoutState {
292 sleep: None,
293 timeout: config.timeout,
294 version: req.version(),
295 operation: config.operation,
296 #[cfg(feature = "request-id")]
297 request_id: request_id(&req),
298 });
299 UpgradeFuture {
300 service: Some(service),
301 inner: Inner::FromRequest {
302 inner: <Input as FromRequest<P, B>>::from_request(req),
303 },
304 request_body_read_timeout,
305 }
306 }
307}
308
309#[derive(Copy)]
311pub struct MissingFailure<P> {
312 _protocol: PhantomData<fn(P)>,
313}
314
315impl<P> Default for MissingFailure<P> {
316 fn default() -> Self {
317 Self { _protocol: PhantomData }
318 }
319}
320
321impl<P> Clone for MissingFailure<P> {
322 fn clone(&self) -> Self {
323 MissingFailure { _protocol: PhantomData }
324 }
325}
326
327impl<R, P> Service<R> for MissingFailure<P>
328where
329 InternalFailureException: IntoResponse<P>,
330{
331 type Response = http::Response<BoxBody>;
332 type Error = Infallible;
333 type Future = Ready<Result<Self::Response, Self::Error>>;
334
335 fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
336 Poll::Ready(Ok(()))
337 }
338
339 fn call(&mut self, _request: R) -> Self::Future {
340 error!("the operation has not been set");
341 std::future::ready(Ok(InternalFailureException.into_response()))
342 }
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use std::sync::{
349 atomic::{AtomicUsize, Ordering},
350 Arc,
351 };
352 use std::task::Waker;
353 use tokio::sync::oneshot;
354
355 struct TestProtocol;
356 struct TestInput;
357 type InputResult = Result<TestInput, StatusCode>;
358 type ResponseFuture = Pin<Box<dyn Future<Output = Result<http::Response<BoxBody>, Infallible>>>>;
359
360 impl IntoResponse<TestProtocol> for StatusCode {
361 fn into_response(self) -> http::Response<BoxBody> {
362 http::Response::builder()
363 .status(self)
364 .body(crate::body::empty())
365 .unwrap()
366 }
367 }
368
369 impl FromRequest<TestProtocol, oneshot::Receiver<InputResult>> for TestInput {
370 type Rejection = StatusCode;
371 type Future = Pin<Box<dyn Future<Output = InputResult> + Send>>;
372
373 fn from_request(request: http::Request<oneshot::Receiver<InputResult>>) -> Self::Future {
374 Box::pin(async { request.into_body().await.expect("input sender must remain alive") })
375 }
376 }
377
378 fn request(
379 timeout: Option<Duration>,
380 version: Version,
381 handler_delay: Option<Duration>,
382 ) -> (ResponseFuture, oneshot::Sender<InputResult>, Arc<AtomicUsize>) {
383 let calls = Arc::new(AtomicUsize::new(0));
384 let handler_calls = calls.clone();
385 let mut upgrade = Upgrade::<TestProtocol, TestInput, _> {
386 _protocol: PhantomData,
387 _input: PhantomData,
388 inner: tower::service_fn(move |_input| {
389 handler_calls.fetch_add(1, Ordering::SeqCst);
390 async move {
391 if let Some(delay) = handler_delay {
392 tokio::time::sleep(delay).await;
393 }
394 Ok::<_, Infallible>(StatusCode::OK)
395 }
396 }),
397 request_body_read_timeout: timeout.map(|timeout| RequestBodyReadTimeoutConfig {
398 timeout,
399 operation: "test#Operation",
400 }),
401 };
402 let (sender, receiver) = oneshot::channel();
403 let request = http::Request::builder().version(version).body(receiver).unwrap();
404 (Box::pin(upgrade.call(request)), sender, calls)
405 }
406
407 fn assert_pending(future: &mut ResponseFuture) {
408 let mut cx = Context::from_waker(Waker::noop());
409 assert!(future.as_mut().poll(&mut cx).is_pending());
410 }
411
412 #[tokio::test(start_paused = true)]
413 async fn expired_deadline_rejects_ready_input() {
414 for version in [Version::HTTP_11, Version::HTTP_2] {
415 for succeeds in [true, false] {
416 let (mut response, sender, calls) = request(Some(Duration::from_millis(10)), version, None);
417 assert_pending(&mut response);
418 tokio::time::advance(Duration::from_millis(10)).await;
419 assert!(sender
422 .send(if succeeds {
423 Ok(TestInput)
424 } else {
425 Err(StatusCode::BAD_REQUEST)
426 })
427 .is_ok());
428 let response = response.await.unwrap();
429 assert_eq!(response.status(), StatusCode::REQUEST_TIMEOUT);
430 assert_eq!(calls.load(Ordering::SeqCst), 0);
431 if version == Version::HTTP_11 {
432 assert_eq!(response.headers()[header::CONNECTION], "close");
433 } else {
434 assert!(!response.headers().contains_key(header::CONNECTION));
435 }
436 }
437 }
438 }
439
440 #[tokio::test(start_paused = true)]
441 async fn input_ready_before_deadline_reaches_handler() {
442 let (mut response, sender, calls) = request(Some(Duration::from_millis(10)), Version::HTTP_11, None);
443 assert_pending(&mut response);
444 tokio::time::advance(Duration::from_millis(9)).await;
445 assert!(sender.send(Ok(TestInput)).is_ok());
446 assert_eq!(response.await.unwrap().status(), StatusCode::OK);
447 assert_eq!(calls.load(Ordering::SeqCst), 1);
448 }
449
450 #[tokio::test(start_paused = true)]
451 async fn body_deadline_does_not_time_out_handler() {
452 let (mut response, sender, calls) = request(
453 Some(Duration::from_millis(10)),
454 Version::HTTP_11,
455 Some(Duration::from_millis(20)),
456 );
457 assert_pending(&mut response);
458 tokio::time::advance(Duration::from_millis(9)).await;
459 assert!(sender.send(Ok(TestInput)).is_ok());
460 assert_pending(&mut response);
461 tokio::time::advance(Duration::from_millis(100)).await;
462 assert_eq!(response.await.unwrap().status(), StatusCode::OK);
463 assert_eq!(calls.load(Ordering::SeqCst), 1);
464 }
465
466 #[tokio::test(start_paused = true)]
467 async fn disabled_deadline_allows_late_input() {
468 let (mut response, sender, calls) = request(None, Version::HTTP_11, None);
469 assert_pending(&mut response);
470 tokio::time::advance(Duration::from_secs(60)).await;
471 assert!(sender.send(Ok(TestInput)).is_ok());
472 assert_eq!(response.await.unwrap().status(), StatusCode::OK);
473 assert_eq!(calls.load(Ordering::SeqCst), 1);
474 }
475
476 #[test]
477 fn immediately_ready_input_does_not_require_timer_driver() {
478 let (mut response, sender, calls) = request(Some(Duration::from_millis(10)), Version::HTTP_11, None);
479 assert!(sender.send(Ok(TestInput)).is_ok());
480 let mut cx = Context::from_waker(Waker::noop());
481 match response.as_mut().poll(&mut cx) {
482 Poll::Ready(Ok(response)) => assert_eq!(response.status(), StatusCode::OK),
483 _ => panic!("immediately ready input should complete without a timer driver"),
484 }
485 assert_eq!(calls.load(Ordering::SeqCst), 1);
486 }
487}