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'.
This commit is contained in:
Michael Howell 2020-01-07 06:28:57 +00:00 committed by Sergio Benitez
parent dcd4068ca0
commit c9d0af09d6
15 changed files with 164 additions and 78 deletions

View File

@ -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"] }

View File

@ -103,6 +103,7 @@ pub fn _catch(args: TokenStream, input: TokenStream) -> Result<TokenStream> {
.status(#status)
.merge(__response)
.ok()
.await
})
}

View File

@ -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"

View File

@ -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);

View File

@ -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);

View File

@ -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"] }

View File

@ -54,6 +54,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Content<R> {
.merge(self.1.respond_to(req).await?)
.header(self.0)
.ok()
.await
})
}
}

View File

@ -68,7 +68,7 @@ impl<'r, E: std::fmt::Debug + Send + 'r> Responder<'r> for Debug<E> {
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
})
}
}

View File

@ -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)

View File

@ -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<u8> {
.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);

View File

@ -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<T> fmt::Debug for Body<T> {
}
}
/// Internal workaround for `Box<dyn AsyncRead + AsyncSeek>` not being allowed.
///
/// https://github.com/rust-lang/rfcs/issues/2035
trait AsyncReadAsyncSeek: AsyncRead + AsyncSeek + Unpin + Send {}
impl<T: AsyncRead + AsyncSeek + Unpin + Send> 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<T> fmt::Debug for Body<T> {
/// 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<T> fmt::Debug for Body<T> {
/// 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<T> fmt::Debug for Body<T> {
/// .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<Box<dyn AsyncReadAsyncSeek + 'r>>,
fut: Option<Pin<Box<dyn Future<Output=Response<'r>> + 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<B>(&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<T: AsyncRead + Send + Unpin + 'r>(&mut self, body: Body<T>)
-> &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, ()> = Response::build()
/// // build the response
/// .ok();
/// .ok()
/// .await;
///
/// assert!(response.is_ok());
///
/// # })
/// ```
#[inline(always)]
pub fn ok<T>(&mut self) -> Result<Response<'r>, T> {
Ok(self.finalize())
pub async fn ok<E>(&mut self) -> Result<Response<'r>, E> {
Ok(self.await)
}
}
impl<'r> Future for ResponseBuilder<'r> {
type Output = Response<'r>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
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<Box<Take<Pin<Box<dyn AsyncReadAsyncSeek>>>>>)
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<B>(&mut self, mut body: B)
where B: AsyncRead + io::Seek + Send + Unpin + 'r
pub async fn set_sized_body<B>(&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() {

View File

@ -175,6 +175,7 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Created<R> {
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<R> {
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<R> {
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<R> {
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<R> {
Response::build_from(self.1.respond_to(req).await?)
.status(self.0)
.ok()
.await
})
}
}

View File

@ -69,7 +69,7 @@ impl<T: AsyncRead> From<T> for Stream<T> {
impl<'r, T: AsyncRead + Send + 'r> Responder<'r> for Stream<T> {
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
})
}
}

View File

@ -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")]

View File

@ -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;
}
})
}))