From c9d0af09d60da9f390e957dc4bff4809f4c3ab2f Mon Sep 17 00:00:00 2001 From: Michael Howell Date: Tue, 7 Jan 2020 06:28:57 +0000 Subject: [PATCH] Use 'AsyncSeek' for sized bodies in 'Response's. In order to avoid making 'ResponseBuilder::sized_body' an asynchronous function, the seeking is deferred until finalization. 'finalize()' is replaced with '.await', and 'ResponseBuilder::ok()' is an 'async fn'. --- core/codegen/Cargo.toml | 2 +- core/codegen/src/attribute/catch.rs | 1 + core/http/Cargo.toml | 2 +- core/http/src/accept.rs | 4 +- core/http/src/content_type.rs | 4 +- core/lib/Cargo.toml | 4 +- core/lib/src/response/content.rs | 1 + core/lib/src/response/debug.rs | 2 +- core/lib/src/response/redirect.rs | 1 + core/lib/src/response/responder.rs | 13 +- core/lib/src/response/response.rs | 191 ++++++++++++------ core/lib/src/response/status.rs | 7 +- core/lib/src/response/stream.rs | 2 +- .../conditionally-set-server-header-996.rs | 4 +- examples/fairings/src/main.rs | 4 +- 15 files changed, 164 insertions(+), 78 deletions(-) diff --git a/core/codegen/Cargo.toml b/core/codegen/Cargo.toml index 67953ae9..6034434e 100644 --- a/core/codegen/Cargo.toml +++ b/core/codegen/Cargo.toml @@ -28,5 +28,5 @@ version_check = "0.9.1" [dev-dependencies] rocket = { version = "0.5.0-dev", path = "../lib" } -tokio = { version = "0.2.0", features = ["io-util"] } +tokio = { version = "0.2.9", features = ["io-util"] } compiletest_rs = { version = "0.3", features = ["stable"] } diff --git a/core/codegen/src/attribute/catch.rs b/core/codegen/src/attribute/catch.rs index 71d09455..4b6d91b8 100644 --- a/core/codegen/src/attribute/catch.rs +++ b/core/codegen/src/attribute/catch.rs @@ -103,6 +103,7 @@ pub fn _catch(args: TokenStream, input: TokenStream) -> Result { .status(#status) .merge(__response) .ok() + .await }) } diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index cf544858..6fc2701d 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -29,7 +29,7 @@ time = "0.2.11" indexmap = "1.0" state = "0.4" tokio-rustls = { version = "0.12.0", optional = true } -tokio = { version = "0.2.0", features = ["sync", "tcp", "time"] } +tokio = { version = "0.2.9", features = ["sync", "tcp", "time"] } cookie = { version = "0.14.0", features = ["percent-encode"] } pear = "0.1" unicode-xid = "0.2" diff --git a/core/http/src/accept.rs b/core/http/src/accept.rs index e56484fa..a8c944ec 100644 --- a/core/http/src/accept.rs +++ b/core/http/src/accept.rs @@ -159,7 +159,9 @@ impl PartialEq for AcceptParams { /// use rocket::response::Response; /// /// # #[allow(unused_variables)] -/// let response = Response::build().header(Accept::JSON).finalize(); +/// # rocket::async_test(async { +/// let response = Response::build().header(Accept::JSON).await; +/// # }) /// ``` #[derive(Debug, Clone, PartialEq)] pub struct Accept(pub(crate) AcceptParams); diff --git a/core/http/src/content_type.rs b/core/http/src/content_type.rs index 023a4858..d3c8b954 100644 --- a/core/http/src/content_type.rs +++ b/core/http/src/content_type.rs @@ -39,7 +39,9 @@ use crate::ext::IntoCollection; /// use rocket::response::Response; /// /// # #[allow(unused_variables)] -/// let response = Response::build().header(ContentType::HTML).finalize(); +/// # rocket::async_test(async { +/// let response = Response::build().header(ContentType::HTML).await; +/// # }) /// ``` #[derive(Debug, Clone, PartialEq, Hash)] pub struct ContentType(pub MediaType); diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index bb988ea0..85f19ae9 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -28,7 +28,7 @@ ctrl_c_shutdown = ["tokio/signal"] rocket_codegen = { version = "0.5.0-dev", path = "../codegen" } rocket_http = { version = "0.5.0-dev", path = "../http" } futures-util = "0.3.0" -tokio = { version = "0.2.0", features = ["fs", "io-std", "io-util", "rt-threaded", "sync"] } +tokio = { version = "0.2.9", features = ["fs", "io-std", "io-util", "rt-threaded", "sync"] } yansi = "0.5" log = { version = "0.4", features = ["std"] } toml = "0.4.7" @@ -47,4 +47,4 @@ version_check = "0.9.1" [dev-dependencies] # TODO: Find a way to not depend on this. lazy_static = "1.0" -tokio = { version = "0.2.0", features = ["macros"] } +tokio = { version = "0.2.9", features = ["macros"] } diff --git a/core/lib/src/response/content.rs b/core/lib/src/response/content.rs index 846e20c9..1e0862a4 100644 --- a/core/lib/src/response/content.rs +++ b/core/lib/src/response/content.rs @@ -54,6 +54,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Content { .merge(self.1.respond_to(req).await?) .header(self.0) .ok() + .await }) } } diff --git a/core/lib/src/response/debug.rs b/core/lib/src/response/debug.rs index a11d9541..f09084b4 100644 --- a/core/lib/src/response/debug.rs +++ b/core/lib/src/response/debug.rs @@ -68,7 +68,7 @@ impl<'r, E: std::fmt::Debug + Send + 'r> Responder<'r> for Debug { Box::pin(async move { warn_!("Debug: {:?}", Paint::default(self.0)); warn_!("Debug always responds with {}.", Status::InternalServerError); - Response::build().status(Status::InternalServerError).ok() + Response::build().status(Status::InternalServerError).ok().await }) } } diff --git a/core/lib/src/response/redirect.rs b/core/lib/src/response/redirect.rs index a1d68be0..ab64c760 100644 --- a/core/lib/src/response/redirect.rs +++ b/core/lib/src/response/redirect.rs @@ -155,6 +155,7 @@ impl<'r> Responder<'r> for Redirect { .status(self.0) .raw_header("Location", uri.to_string()) .ok() + .await } else { error!("Invalid URI used for redirect."); Err(Status::InternalServerError) diff --git a/core/lib/src/response/responder.rs b/core/lib/src/response/responder.rs index b60aab81..9c78ef61 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -163,6 +163,7 @@ use crate::request::Request; /// .raw_header("X-Person-Age", self.age.to_string()) /// .header(ContentType::new("application", "x-person")) /// .ok() +/// .await /// }) /// } /// } @@ -195,6 +196,7 @@ impl<'r> Responder<'r> for &'r str { .header(ContentType::Plain) .sized_body(Cursor::new(self)) .ok() + .await }) } } @@ -208,6 +210,7 @@ impl Responder<'_> for String { .header(ContentType::Plain) .sized_body(Cursor::new(self)) .ok() + .await }) } } @@ -221,6 +224,7 @@ impl<'r> Responder<'r> for &'r [u8] { .header(ContentType::Binary) .sized_body(Cursor::new(self)) .ok() + .await }) } } @@ -234,6 +238,7 @@ impl Responder<'_> for Vec { .header(ContentType::Binary) .sized_body(Cursor::new(self)) .ok() + .await }) } } @@ -246,8 +251,8 @@ impl Responder<'_> for File { let metadata = file.metadata().await; let stream = BufReader::new(file); match metadata { - Ok(md) => Response::build().raw_body(Body::Sized(stream, md.len())).ok(), - Err(_) => Response::build().streamed_body(stream).ok() + Ok(md) => Response::build().raw_body(Body::Sized(stream, md.len())).ok().await, + Err(_) => Response::build().streamed_body(stream).ok().await } }) } @@ -307,10 +312,10 @@ impl<'r> Responder<'r> for Status { match self.class() { StatusClass::ClientError | StatusClass::ServerError => Err(self), StatusClass::Success if self.code < 206 => { - Response::build().status(self).ok() + Response::build().status(self).ok().await } StatusClass::Informational if self.code == 100 => { - Response::build().status(self).ok() + Response::build().status(self).ok().await } _ => { error_!("Invalid status used as responder: {}.", self); diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs index 9b2c3495..24924c19 100644 --- a/core/lib/src/response/response.rs +++ b/core/lib/src/response/response.rs @@ -2,8 +2,9 @@ use std::{io, fmt, str}; use std::borrow::Cow; use std::future::Future; use std::pin::Pin; +use std::task::{Context, Poll}; -use tokio::io::{AsyncRead, AsyncReadExt}; +use tokio::io::{AsyncRead, AsyncReadExt, AsyncSeek, AsyncSeekExt}; use futures_util::future::FutureExt; use crate::response::{Responder, ResultFuture}; @@ -104,6 +105,12 @@ impl fmt::Debug for Body { } } +/// Internal workaround for `Box` not being allowed. +/// +/// https://github.com/rust-lang/rfcs/issues/2035 +trait AsyncReadAsyncSeek: AsyncRead + AsyncSeek + Unpin + Send {} +impl AsyncReadAsyncSeek for T {} + /// Type for easily building `Response`s. /// /// Building a [`Response`] can be a low-level ordeal; this structure presents a @@ -117,8 +124,9 @@ impl fmt::Debug for Body { /// with field(s) modified in the `Responder` being built. These method calls /// can be chained: `build.a().b()`. /// -/// To finish building and retrieve the built `Response`, use the -/// [`finalize()`](#method.finalize) or [`ok()`](#method.ok) methods. +/// To finish building and retrieve the built `Response`, `.await` the builder. +/// The [`ok()` method](#method.ok) is also provided as a convenience +/// for `Responder` implementations. /// /// ## Headers /// @@ -153,6 +161,8 @@ impl fmt::Debug for Body { /// use rocket::response::Response; /// use rocket::http::{Status, ContentType}; /// +/// # rocket::async_test(async { +/// /// # #[allow(unused_variables)] /// let response = Response::build() /// .status(Status::ImATeapot) @@ -161,10 +171,14 @@ impl fmt::Debug for Body { /// .raw_header("X-Teapot-Model", "Utopia") /// .raw_header_adjoin("X-Teapot-Model", "Series 1") /// .sized_body(Cursor::new("Brewing the best coffee!")) -/// .finalize(); +/// .await; +/// +/// # }) /// ``` pub struct ResponseBuilder<'r> { - response: Response<'r> + response: Response<'r>, + pending_sized_body: Option>, + fut: Option> + Send + 'r>>>, } impl<'r> ResponseBuilder<'r> { @@ -182,7 +196,9 @@ impl<'r> ResponseBuilder<'r> { #[inline(always)] pub fn new(base: Response<'r>) -> ResponseBuilder<'r> { ResponseBuilder { - response: base + response: base, + pending_sized_body: None, + fut: None, } } @@ -194,10 +210,14 @@ impl<'r> ResponseBuilder<'r> { /// use rocket::Response; /// use rocket::http::Status; /// + /// # rocket::async_test(async { + /// /// # #[allow(unused_variables)] /// let response = Response::build() /// .status(Status::NotFound) - /// .finalize(); + /// .await; + /// + /// # }) /// ``` #[inline(always)] pub fn status(&mut self, status: Status) -> &mut ResponseBuilder<'r> { @@ -213,10 +233,13 @@ impl<'r> ResponseBuilder<'r> { /// ```rust /// use rocket::Response; /// + /// # rocket::async_test(async { + /// /// # #[allow(unused_variables)] /// let response = Response::build() /// .raw_status(699, "Alien Encounter") - /// .finalize(); + /// .await; + /// # }) /// ``` #[inline(always)] pub fn raw_status(&mut self, code: u16, reason: &'static str) @@ -240,12 +263,16 @@ impl<'r> ResponseBuilder<'r> { /// use rocket::Response; /// use rocket::http::ContentType; /// + /// # rocket::async_test(async { + /// /// let response = Response::build() /// .header(ContentType::JSON) /// .header(ContentType::HTML) - /// .finalize(); + /// .await; /// /// assert_eq!(response.headers().get("Content-Type").count(), 1); + /// + /// # }) /// ``` #[inline(always)] pub fn header<'h: 'r, H>(&mut self, header: H) -> &mut ResponseBuilder<'r> @@ -271,12 +298,16 @@ impl<'r> ResponseBuilder<'r> { /// use rocket::http::Header; /// use rocket::http::hyper::header::ACCEPT; /// + /// # rocket::async_test(async { + /// /// let response = Response::build() /// .header_adjoin(Header::new(ACCEPT.as_str(), "application/json")) /// .header_adjoin(Header::new(ACCEPT.as_str(), "text/plain")) - /// .finalize(); + /// .await; /// /// assert_eq!(response.headers().get("Accept").count(), 2); + /// + /// # }) /// ``` #[inline(always)] pub fn header_adjoin<'h: 'r, H>(&mut self, header: H) -> &mut ResponseBuilder<'r> @@ -296,12 +327,16 @@ impl<'r> ResponseBuilder<'r> { /// ```rust /// use rocket::Response; /// + /// # rocket::async_test(async { + /// /// let response = Response::build() /// .raw_header("X-Custom", "first") /// .raw_header("X-Custom", "second") - /// .finalize(); + /// .await; /// /// assert_eq!(response.headers().get("X-Custom").count(), 1); + /// + /// # }) /// ``` #[inline(always)] pub fn raw_header<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V) @@ -323,12 +358,16 @@ impl<'r> ResponseBuilder<'r> { /// ```rust /// use rocket::Response; /// + /// # rocket::async_test(async { + /// /// let response = Response::build() /// .raw_header_adjoin("X-Custom", "first") /// .raw_header_adjoin("X-Custom", "second") - /// .finalize(); + /// .await; /// /// assert_eq!(response.headers().get("X-Custom").count(), 2); + /// + /// # }) /// ``` #[inline(always)] pub fn raw_header_adjoin<'a: 'r, 'b: 'r, N, V>(&mut self, name: N, value: V) @@ -339,12 +378,11 @@ impl<'r> ResponseBuilder<'r> { self } - // TODO.async: un-ignore this test once Seek/AsyncSeek situation has been resolved. /// Sets the body of the `Response` to be the fixed-sized `body`. /// /// # Example /// - /// ```rust,ignore + /// ```rust /// use rocket::Response; /// use tokio::fs::File; /// # use std::io; @@ -354,15 +392,15 @@ impl<'r> ResponseBuilder<'r> { /// # #[allow(unused_variables)] /// let response = Response::build() /// .sized_body(File::open("body.txt").await?) - /// .finalize(); + /// .await; /// # Ok(()) /// # } /// ``` #[inline(always)] pub fn sized_body(&mut self, body: B) -> &mut ResponseBuilder<'r> - where B: AsyncRead + io::Seek + Send + Unpin + 'r + where B: AsyncRead + AsyncSeek + Send + Unpin + 'r { - self.response.set_sized_body(body); + self.pending_sized_body = Some(Box::new(body)); self } @@ -380,7 +418,7 @@ impl<'r> ResponseBuilder<'r> { /// # #[allow(unused_variables)] /// let response = Response::build() /// .streamed_body(File::open("body.txt").await?) - /// .finalize(); + /// .await; /// # Ok(()) /// # } /// ``` @@ -389,6 +427,7 @@ impl<'r> ResponseBuilder<'r> { where B: AsyncRead + Send + 'r { self.response.set_streamed_body(body); + self.pending_sized_body = None; self } @@ -407,7 +446,7 @@ impl<'r> ResponseBuilder<'r> { /// # #[allow(unused_variables)] /// let response = Response::build() /// .chunked_body(File::open("body.txt").await?, 8096) - /// .finalize(); + /// .await; /// # Ok(()) /// # } /// ``` @@ -416,6 +455,7 @@ impl<'r> ResponseBuilder<'r> { -> &mut ResponseBuilder<'r> { self.response.set_chunked_body(body, chunk_size); + self.pending_sized_body = None; self } @@ -429,16 +469,21 @@ impl<'r> ResponseBuilder<'r> { /// use std::io::Cursor; /// use rocket::response::{Response, Body}; /// + /// # rocket::async_test(async { + /// /// # #[allow(unused_variables)] /// let response = Response::build() /// .raw_body(Body::Sized(Cursor::new("Hello!"), 6)) - /// .finalize(); + /// .await; + /// + /// # }) /// ``` #[inline(always)] pub fn raw_body(&mut self, body: Body) -> &mut ResponseBuilder<'r> { self.response.set_raw_body(body); + self.pending_sized_body = None; self } @@ -454,18 +499,20 @@ impl<'r> ResponseBuilder<'r> { /// use rocket::Response; /// use rocket::http::{Status, ContentType}; /// + /// # rocket::async_test(async { + /// /// let base = Response::build() /// .status(Status::NotFound) /// .header(ContentType::HTML) /// .raw_header("X-Custom", "value 1") - /// .finalize(); + /// .await; /// /// let response = Response::build() /// .status(Status::ImATeapot) /// .raw_header("X-Custom", "value 2") /// .raw_header_adjoin("X-Custom", "value 3") /// .merge(base) - /// .finalize(); + /// .await; /// /// assert_eq!(response.status(), Status::NotFound); /// @@ -478,9 +525,14 @@ impl<'r> ResponseBuilder<'r> { /// let custom_values: Vec<_> = response.headers().get("X-Custom").collect(); /// assert_eq!(custom_values, vec!["value 1"]); /// # } + /// + /// # }); /// ``` #[inline(always)] - pub fn merge(&mut self, other: Response<'r>) -> &mut ResponseBuilder<'r> { + pub fn merge(&mut self, mut other: Response<'r>) -> &mut ResponseBuilder<'r> { + if other.body().is_some() { + self.pending_sized_body = None; + } self.response.merge(other); self } @@ -498,18 +550,20 @@ impl<'r> ResponseBuilder<'r> { /// use rocket::Response; /// use rocket::http::{Status, ContentType}; /// + /// # rocket::async_test(async { + /// /// let other = Response::build() /// .status(Status::NotFound) /// .header(ContentType::HTML) /// .raw_header("X-Custom", "value 1") - /// .finalize(); + /// .await; /// /// let response = Response::build() /// .status(Status::ImATeapot) /// .raw_header("X-Custom", "value 2") /// .raw_header_adjoin("X-Custom", "value 3") /// .join(other) - /// .finalize(); + /// .await; /// /// assert_eq!(response.status(), Status::ImATeapot); /// @@ -522,6 +576,8 @@ impl<'r> ResponseBuilder<'r> { /// let custom_values: Vec<_> = response.headers().get("X-Custom").collect(); /// assert_eq!(custom_values, vec!["value 2", "value 3", "value 1"]); /// # } + /// + /// # }) /// ``` #[inline(always)] pub fn join(&mut self, other: Response<'r>) -> &mut ResponseBuilder<'r> { @@ -529,23 +585,6 @@ impl<'r> ResponseBuilder<'r> { self } - /// Retrieve the built `Response`. - /// - /// # Example - /// - /// ```rust - /// use rocket::Response; - /// - /// # #[allow(unused_variables)] - /// let response = Response::build() - /// // build the response - /// .finalize(); - /// ``` - #[inline(always)] - pub fn finalize(&mut self) -> Response<'r> { - std::mem::replace(&mut self.response, Response::new()) - } - /// Retrieve the built `Response` wrapped in `Ok`. /// /// # Example @@ -553,15 +592,39 @@ impl<'r> ResponseBuilder<'r> { /// ```rust /// use rocket::Response; /// + /// # rocket::async_test(async { + /// /// let response: Result = Response::build() /// // build the response - /// .ok(); + /// .ok() + /// .await; /// /// assert!(response.is_ok()); + /// + /// # }) /// ``` #[inline(always)] - pub fn ok(&mut self) -> Result, T> { - Ok(self.finalize()) + pub async fn ok(&mut self) -> Result, E> { + Ok(self.await) + } +} + +impl<'r> Future for ResponseBuilder<'r> { + type Output = Response<'r>; + fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + let this = self.get_mut(); + if this.fut.is_none() { + let mut response = std::mem::replace(&mut this.response, Response::new()); + let pending_sized_body = this.pending_sized_body.take(); + this.fut = Some(Box::pin(async { + if let Some(sb) = pending_sized_body { + // TODO: Avoid double boxing (Pin>>>>) + response.set_sized_body(sb).await; + } + response + })); + } + this.fut.as_mut().expect("this.fut.is_none() checked and assigned Some").as_mut().poll(cx) } } @@ -897,7 +960,7 @@ impl<'r> Response<'r> { /// let mut response = Response::new(); /// assert!(response.body().is_none()); /// - /// response.set_sized_body(Cursor::new("Hello, world!")); + /// response.set_sized_body(Cursor::new("Hello, world!")).await; /// assert_eq!(response.body_string().await, Some("Hello, world!".to_string())); /// # }) /// ``` @@ -928,7 +991,7 @@ impl<'r> Response<'r> { /// let mut response = Response::new(); /// assert!(response.body().is_none()); /// - /// response.set_sized_body(Cursor::new("Hello, world!")); + /// response.set_sized_body(Cursor::new("Hello, world!")).await; /// assert_eq!(response.body_string().await, Some("Hello, world!".to_string())); /// assert!(response.body().is_none()); /// # }) @@ -958,7 +1021,7 @@ impl<'r> Response<'r> { /// let mut response = Response::new(); /// assert!(response.body().is_none()); /// - /// response.set_sized_body(Cursor::new("hi!")); + /// response.set_sized_body(Cursor::new("hi!")).await; /// assert_eq!(response.body_bytes().await, Some(vec![0x68, 0x69, 0x21])); /// assert!(response.body().is_none()); /// # }) @@ -987,7 +1050,7 @@ impl<'r> Response<'r> { /// let mut response = Response::new(); /// assert!(response.body().is_none()); /// - /// response.set_sized_body(Cursor::new("Hello, world!")); + /// response.set_sized_body(Cursor::new("Hello, world!")).await; /// assert!(response.body().is_some()); /// /// let body = response.take_body(); @@ -1018,7 +1081,8 @@ impl<'r> Response<'r> { /// Sets the body of `self` to be the fixed-sized `body`. The size of the /// body is obtained by `seek`ing to the end and then `seek`ing back to the - /// start. + /// start. Since this is an asynchronous operation, it returns a future + /// and should be `await`-ed on. /// /// # Panics /// @@ -1034,17 +1098,16 @@ impl<'r> Response<'r> { /// /// # rocket::async_test(async { /// let mut response = Response::new(); - /// response.set_sized_body(Cursor::new("Hello, world!")); + /// response.set_sized_body(Cursor::new("Hello, world!")).await; /// assert_eq!(response.body_string().await, Some("Hello, world!".to_string())); /// # }) /// ``` - #[inline] - pub fn set_sized_body(&mut self, mut body: B) - where B: AsyncRead + io::Seek + Send + Unpin + 'r + pub async fn set_sized_body(&mut self, mut body: B) + where B: AsyncRead + AsyncSeek + Send + Unpin + 'r { - let size = body.seek(io::SeekFrom::End(0)) + let size = body.seek(io::SeekFrom::End(0)).await .expect("Attempted to retrieve size by seeking, but failed."); - body.seek(io::SeekFrom::Start(0)) + body.seek(io::SeekFrom::Start(0)).await .expect("Attempted to reset body by seeking after getting size."); self.body = Some(Body::Sized(Box::pin(body.take(size)), size)); } @@ -1130,18 +1193,20 @@ impl<'r> Response<'r> { /// use rocket::Response; /// use rocket::http::{Status, ContentType}; /// + /// # rocket::async_test(async { + /// /// let base = Response::build() /// .status(Status::NotFound) /// .header(ContentType::HTML) /// .raw_header("X-Custom", "value 1") - /// .finalize(); + /// .await; /// /// let response = Response::build() /// .status(Status::ImATeapot) /// .raw_header("X-Custom", "value 2") /// .raw_header_adjoin("X-Custom", "value 3") /// .merge(base) - /// .finalize(); + /// .await; /// /// assert_eq!(response.status(), Status::NotFound); /// @@ -1154,6 +1219,8 @@ impl<'r> Response<'r> { /// let custom_values: Vec<_> = response.headers().get("X-Custom").collect(); /// assert_eq!(custom_values, vec!["value 1"]); /// # } + /// + /// # }) /// ``` pub fn merge(&mut self, other: Response<'r>) { if let Some(status) = other.status { @@ -1179,18 +1246,20 @@ impl<'r> Response<'r> { /// use rocket::Response; /// use rocket::http::{Status, ContentType}; /// + /// # rocket::async_test(async { + /// /// let other = Response::build() /// .status(Status::NotFound) /// .header(ContentType::HTML) /// .raw_header("X-Custom", "value 1") - /// .finalize(); + /// .await; /// /// let response = Response::build() /// .status(Status::ImATeapot) /// .raw_header("X-Custom", "value 2") /// .raw_header_adjoin("X-Custom", "value 3") /// .join(other) - /// .finalize(); + /// .await; /// /// assert_eq!(response.status(), Status::ImATeapot); /// @@ -1203,6 +1272,8 @@ impl<'r> Response<'r> { /// let custom_values: Vec<_> = response.headers().get("X-Custom").collect(); /// assert_eq!(custom_values, vec!["value 2", "value 3", "value 1"]); /// # } + /// + /// # }) /// ``` pub fn join(&mut self, other: Response<'r>) { if self.status.is_none() { diff --git a/core/lib/src/response/status.rs b/core/lib/src/response/status.rs index 0bac7b13..41c85a56 100644 --- a/core/lib/src/response/status.rs +++ b/core/lib/src/response/status.rs @@ -175,6 +175,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Created { response.status(Status::Created) .raw_header("Location", self.0) .ok() + .await }) } } @@ -216,7 +217,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Accepted { build.merge(responder.respond_to(req).await?); } - build.status(Status::Accepted).ok() + build.status(Status::Accepted).ok().await }) } } @@ -283,7 +284,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for BadRequest { build.merge(responder.respond_to(req).await?); } - build.status(Status::BadRequest).ok() + build.status(Status::BadRequest).ok().await }) } } @@ -390,6 +391,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for NotFound { Response::build_from(self.0.respond_to(req).await?) .status(Status::NotFound) .ok() + .await }) } } @@ -457,6 +459,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Custom { Response::build_from(self.1.respond_to(req).await?) .status(self.0) .ok() + .await }) } } diff --git a/core/lib/src/response/stream.rs b/core/lib/src/response/stream.rs index 1661a384..3f1ddd88 100644 --- a/core/lib/src/response/stream.rs +++ b/core/lib/src/response/stream.rs @@ -69,7 +69,7 @@ impl From for Stream { impl<'r, T: AsyncRead + Send + 'r> Responder<'r> for Stream { fn respond_to(self, _: &'r Request<'_>) -> ResultFuture<'r> { Box::pin(async { - Response::build().chunked_body(self.0, self.1).ok() + Response::build().chunked_body(self.0, self.1).ok().await }) } } diff --git a/core/lib/tests/conditionally-set-server-header-996.rs b/core/lib/tests/conditionally-set-server-header-996.rs index f5d1b533..af160970 100644 --- a/core/lib/tests/conditionally-set-server-header-996.rs +++ b/core/lib/tests/conditionally-set-server-header-996.rs @@ -6,10 +6,10 @@ use rocket::Response; use rocket::http::Header; #[get("/do_not_overwrite")] -fn do_not_overwrite() -> Response<'static> { +async fn do_not_overwrite() -> Response<'static> { Response::build() .header(Header::new("Server", "Test")) - .finalize() + .await } #[get("/use_default")] diff --git a/examples/fairings/src/main.rs b/examples/fairings/src/main.rs index f79aeec6..b6398b8f 100644 --- a/examples/fairings/src/main.rs +++ b/examples/fairings/src/main.rs @@ -54,7 +54,7 @@ impl Fairing for Counter { let body = format!("Get: {}\nPost: {}", get_count, post_count); response.set_status(Status::Ok); response.set_header(ContentType::Plain); - response.set_sized_body(Cursor::new(body)); + response.set_sized_body(Cursor::new(body)).await; } }) } @@ -95,7 +95,7 @@ fn rocket() -> rocket::Rocket { Box::pin(async move { if req.uri().path() == "/" { println!(" => Rewriting response body."); - res.set_sized_body(Cursor::new("Hello, fairings!")); + res.set_sized_body(Cursor::new("Hello, fairings!")).await; } }) }))