mirror of https://github.com/rwf2/Rocket.git
Use 'async_trait' for 'Responder'.
Also: * Remove 'response::ResultFuture'. * Re-export 'tokio' and 'futures' from the crate root. * Make 'ResponseBuilder::sized_body()' and 'async fn'. * Remove the 'Future' implementation for 'ResponseBuilder'. * Add 'ResponseBuilder::finalize()' for finalizing the builder.
This commit is contained in:
parent
58f81d392e
commit
c0c6c79a7f
|
@ -19,6 +19,7 @@ use std::io;
|
|||
use std::iter::FromIterator;
|
||||
|
||||
use tokio::io::AsyncReadExt;
|
||||
use rocket::futures::future::BoxFuture;
|
||||
|
||||
use rocket::request::Request;
|
||||
use rocket::outcome::Outcome::*;
|
||||
|
@ -169,15 +170,20 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for Json<T> {
|
|||
/// JSON and a fixed-size body with the serialized value. If serialization
|
||||
/// fails, an `Err` of `Status::InternalServerError` is returned.
|
||||
impl<'r, T: Serialize> Responder<'r> for Json<T> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
match serde_json::to_string(&self.0) {
|
||||
Ok(string) => Box::pin(async move { Ok(content::Json(string).respond_to(req).await.unwrap()) }),
|
||||
Err(e) => Box::pin(async move {
|
||||
fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>>
|
||||
where 'a: 'x, 'r: 'x, Self: 'x
|
||||
{
|
||||
let json_string = serde_json::to_string(&self.0);
|
||||
Box::pin(async move {
|
||||
match json_string {
|
||||
Ok(string) => Ok(content::Json(string).respond_to(req).await.unwrap()),
|
||||
Err(e) => {
|
||||
error_!("JSON failed to serialize: {:?}", e);
|
||||
Err(Status::InternalServerError)
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for Json<T> {
|
||||
|
@ -291,10 +297,10 @@ impl<T> FromIterator<T> for JsonValue where serde_json::Value: FromIterator<T> {
|
|||
|
||||
/// Serializes the value into JSON. Returns a response with Content-Type JSON
|
||||
/// and a fixed-size body with the serialized value.
|
||||
#[rocket::async_trait]
|
||||
impl<'r> Responder<'r> for JsonValue {
|
||||
#[inline]
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
content::Json(self.0.to_string()).respond_to(req)
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
content::Json(self.0.to_string()).respond_to(req).await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ use rocket::outcome::Outcome::*;
|
|||
use rocket::data::{Data, FromData, FromDataFuture, Transform::*, TransformFuture, Transformed};
|
||||
use rocket::http::Status;
|
||||
use rocket::response::{self, content, Responder};
|
||||
use rocket::futures::future::BoxFuture;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde::de::Deserialize;
|
||||
|
@ -157,13 +158,15 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for MsgPack<T> {
|
|||
/// Content-Type `MsgPack` and a fixed-size body with the serialization. If
|
||||
/// serialization fails, an `Err` of `Status::InternalServerError` is returned.
|
||||
impl<'r, T: Serialize> Responder<'r> for MsgPack<T> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>>
|
||||
where 'a: 'x, 'r: 'x, Self: 'x
|
||||
{
|
||||
match rmp_serde::to_vec(&self.0) {
|
||||
Ok(buf) => content::MsgPack(buf).respond_to(req),
|
||||
Err(e) => Box::pin(async move {
|
||||
Err(e) => {
|
||||
error_!("MsgPack failed to serialize: {:?}", e);
|
||||
Err(Status::InternalServerError)
|
||||
}),
|
||||
Box::pin(async { Err(Status::InternalServerError) })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -383,9 +383,9 @@ impl Template {
|
|||
/// Returns a response with the Content-Type derived from the template's
|
||||
/// extension and a fixed-size body containing the rendered template. If
|
||||
/// rendering fails, an `Err` of `Status::InternalServerError` is returned.
|
||||
#[rocket::async_trait]
|
||||
impl<'r> Responder<'r> for Template {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
let (render, content_type) = {
|
||||
let ctxt = req.guard::<State<'_, ContextManager>>().await.succeeded().ok_or_else(|| {
|
||||
error_!("Uninitialized template context: missing fairing.");
|
||||
|
@ -398,6 +398,5 @@ impl<'r> Responder<'r> for Template {
|
|||
};
|
||||
|
||||
Content(content_type, render).respond_to(req).await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -103,7 +103,6 @@ pub fn _catch(args: TokenStream, input: TokenStream) -> Result<TokenStream> {
|
|||
.status(#status)
|
||||
.merge(__response)
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -30,10 +30,12 @@ pub fn derive_responder(input: TokenStream) -> TokenStream {
|
|||
false => Ok(())
|
||||
})
|
||||
.function(|_, inner| quote! {
|
||||
fn respond_to(
|
||||
fn respond_to<'__i, '__x>(
|
||||
self,
|
||||
__req: &'__r ::rocket::Request
|
||||
) -> ::rocket::response::ResultFuture<'__r> {
|
||||
__req: &'__r ::rocket::request::Request<'__i>
|
||||
) -> ::rocket::futures::future::BoxFuture<'__x, ::rocket::response::Result<'__r>>
|
||||
where '__i: '__x, '__r: '__x, Self: '__x
|
||||
{
|
||||
#inner
|
||||
}
|
||||
})
|
||||
|
|
|
@ -28,8 +28,7 @@ async fn responder_foo() {
|
|||
let req = local_req.inner();
|
||||
|
||||
let mut response = Foo::First("hello".into())
|
||||
.respond_to(req)
|
||||
.await
|
||||
.respond_to(req).await
|
||||
.expect("response okay");
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
|
@ -37,8 +36,7 @@ async fn responder_foo() {
|
|||
assert_eq!(response.body_string().await, Some("hello".into()));
|
||||
|
||||
let mut response = Foo::Second("just a test".into())
|
||||
.respond_to(req)
|
||||
.await
|
||||
.respond_to(req).await
|
||||
.expect("response okay");
|
||||
|
||||
assert_eq!(response.status(), Status::InternalServerError);
|
||||
|
@ -46,8 +44,7 @@ async fn responder_foo() {
|
|||
assert_eq!(response.body_string().await, Some("just a test".into()));
|
||||
|
||||
let mut response = Foo::Third { responder: "well, hi", ct: ContentType::JSON }
|
||||
.respond_to(req)
|
||||
.await
|
||||
.respond_to(req).await
|
||||
.expect("response okay");
|
||||
|
||||
assert_eq!(response.status(), Status::NotFound);
|
||||
|
@ -55,8 +52,7 @@ async fn responder_foo() {
|
|||
assert_eq!(response.body_string().await, Some("well, hi".into()));
|
||||
|
||||
let mut response = Foo::Fourth { string: "goodbye", ct: ContentType::JSON }
|
||||
.respond_to(req)
|
||||
.await
|
||||
.respond_to(req).await
|
||||
.expect("response okay");
|
||||
|
||||
assert_eq!(response.status(), Status::raw(105));
|
||||
|
@ -106,8 +102,7 @@ async fn responder_baz() {
|
|||
let req = local_req.inner();
|
||||
|
||||
let mut response = Baz { responder: "just a custom" }
|
||||
.respond_to(req)
|
||||
.await
|
||||
.respond_to(req).await
|
||||
.expect("response okay");
|
||||
|
||||
assert_eq!(response.status(), Status::Ok);
|
||||
|
|
|
@ -158,10 +158,7 @@ impl PartialEq for AcceptParams {
|
|||
/// use rocket::http::Accept;
|
||||
/// use rocket::response::Response;
|
||||
///
|
||||
/// # #[allow(unused_variables)]
|
||||
/// # rocket::async_test(async {
|
||||
/// let response = Response::build().header(Accept::JSON).await;
|
||||
/// # })
|
||||
/// let response = Response::build().header(Accept::JSON).finalize();
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Accept(pub(crate) AcceptParams);
|
||||
|
|
|
@ -38,10 +38,7 @@ use crate::ext::IntoCollection;
|
|||
/// use rocket::http::ContentType;
|
||||
/// use rocket::response::Response;
|
||||
///
|
||||
/// # #[allow(unused_variables)]
|
||||
/// # rocket::async_test(async {
|
||||
/// let response = Response::build().header(ContentType::HTML).await;
|
||||
/// # })
|
||||
/// let response = Response::build().header(ContentType::HTML).finalize();
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Hash)]
|
||||
pub struct ContentType(pub MediaType);
|
||||
|
|
|
@ -28,6 +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"
|
||||
futures = "0.3.0"
|
||||
tokio = { version = "0.2.9", features = ["fs", "io-std", "io-util", "rt-threaded", "sync"] }
|
||||
yansi = "0.5"
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
|
|
|
@ -99,6 +99,9 @@ pub use async_trait::*;
|
|||
#[macro_use] extern crate log;
|
||||
#[macro_use] extern crate pear;
|
||||
|
||||
pub use futures;
|
||||
pub use tokio;
|
||||
|
||||
#[doc(hidden)] #[macro_use] pub mod logger;
|
||||
#[macro_use] pub mod outcome;
|
||||
pub mod local;
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
//! ```
|
||||
|
||||
use crate::request::Request;
|
||||
use crate::response::{Response, Responder, ResultFuture};
|
||||
use crate::response::{self, Response, Responder};
|
||||
use crate::http::ContentType;
|
||||
|
||||
/// Sets the Content-Type of a `Responder` to a chosen value.
|
||||
|
@ -46,21 +46,21 @@ pub struct Content<R>(pub ContentType, pub R);
|
|||
|
||||
/// Overrides the Content-Type of the response to the wrapped `ContentType` then
|
||||
/// delegates the remainder of the response to the wrapped responder.
|
||||
#[crate::async_trait]
|
||||
impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Content<R> {
|
||||
#[inline(always)]
|
||||
fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
Response::build()
|
||||
.merge(self.1.respond_to(req).await?)
|
||||
.header(self.0)
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! ctrs {
|
||||
($($name:ident: $ct:ident, $name_str:expr, $ct_str:expr),+) => {
|
||||
use futures_util::future::BoxFuture;
|
||||
|
||||
$(
|
||||
#[doc="Override the `Content-Type` of the response to <b>"]
|
||||
#[doc=$name_str]
|
||||
|
@ -75,7 +75,9 @@ macro_rules! ctrs {
|
|||
/// Sets the Content-Type of the response then delegates the
|
||||
/// remainder of the response to the wrapped responder.
|
||||
impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for $name<R> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>>
|
||||
where 'a: 'x, 'r: 'x, Self: 'x
|
||||
{
|
||||
Content(ContentType::$ct, self.0).respond_to(req)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -63,12 +63,11 @@ impl<E> From<E> for Debug<E> {
|
|||
}
|
||||
}
|
||||
|
||||
#[crate::async_trait]
|
||||
impl<'r, E: std::fmt::Debug + Send + 'r> Responder<'r> for Debug<E> {
|
||||
fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
warn_!("Debug: {:?}", Paint::default(self.0));
|
||||
warn_!("Debug always responds with {}.", Status::InternalServerError);
|
||||
Response::build().status(Status::InternalServerError).ok().await
|
||||
})
|
||||
Response::build().status(Status::InternalServerError).ok()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::convert::AsRef;
|
|||
use time::Duration;
|
||||
|
||||
use crate::outcome::IntoOutcome;
|
||||
use crate::response::{Responder, ResultFuture};
|
||||
use crate::response::{self, Responder};
|
||||
use crate::request::{self, Request, FromRequest};
|
||||
use crate::http::{Status, Cookie};
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
|
@ -198,11 +198,12 @@ impl<'r, R: Responder<'r>> Flash<R> {
|
|||
/// response. In other words, simply sets a cookie and delegates the rest of the
|
||||
/// response handling to the wrapped responder. As a result, the `Outcome` of
|
||||
/// the response is the `Outcome` of the wrapped `Responder`.
|
||||
#[crate::async_trait]
|
||||
impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Flash<R> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
trace_!("Flash: setting message: {}:{}", self.name, self.message);
|
||||
req.cookies().add(self.cookie());
|
||||
self.inner.respond_to(req)
|
||||
self.inner.respond_to(req).await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,8 +46,5 @@ pub use self::stream::Stream;
|
|||
pub use self::debug::Debug;
|
||||
#[doc(inline)] pub use self::content::Content;
|
||||
|
||||
/// Type alias for the `Result` of a `Responder::respond` call.
|
||||
/// Type alias for the `Result` of a [`Responder::respond_to()`] call.
|
||||
pub type Result<'r> = std::result::Result<self::Response<'r>, crate::http::Status>;
|
||||
|
||||
/// Type alias for the `Future` returned by a `Responder::respond` call.
|
||||
pub type ResultFuture<'r> = futures_util::future::BoxFuture<'r, Result<'r>>;
|
||||
|
|
|
@ -78,9 +78,9 @@ impl NamedFile {
|
|||
/// recognized. See [`ContentType::from_extension()`] for more information. If
|
||||
/// you would like to stream a file with a different Content-Type than that
|
||||
/// implied by its extension, use a [`File`] directly.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for NamedFile {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
let mut response = self.1.respond_to(req).await?;
|
||||
if let Some(ext) = self.0.extension() {
|
||||
if let Some(ct) = ContentType::from_extension(&ext.to_string_lossy()) {
|
||||
|
@ -89,7 +89,6 @@ impl<'r> Responder<'r> for NamedFile {
|
|||
}
|
||||
|
||||
Ok(response)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::convert::TryInto;
|
||||
|
||||
use crate::request::Request;
|
||||
use crate::response::{Response, Responder, ResultFuture};
|
||||
use crate::response::{self, Response, Responder};
|
||||
use crate::http::uri::Uri;
|
||||
use crate::http::Status;
|
||||
|
||||
|
@ -147,19 +147,17 @@ impl Redirect {
|
|||
/// the `Location` header field. The body of the response is empty. If the URI
|
||||
/// value used to create the `Responder` is an invalid URI, an error of
|
||||
/// `Status::InternalServerError` is returned.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for Redirect {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
Box::pin(async {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
if let Some(uri) = self.1 {
|
||||
Response::build()
|
||||
.status(self.0)
|
||||
.raw_header("Location", uri.to_string())
|
||||
.ok()
|
||||
.await
|
||||
} else {
|
||||
error!("Invalid URI used for redirect.");
|
||||
Err(Status::InternalServerError)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use std::fs::File;
|
||||
use std::io::Cursor;
|
||||
|
||||
use futures_util::future::ready;
|
||||
use tokio::io::BufReader;
|
||||
use futures_util::future::BoxFuture;
|
||||
|
||||
use crate::http::{Status, ContentType, StatusClass};
|
||||
use crate::response::{self, Response, Body};
|
||||
|
@ -113,13 +113,50 @@ use crate::request::Request;
|
|||
/// regardless of the incoming request. Thus, knowing the type is sufficient to
|
||||
/// fully determine its functionality.
|
||||
///
|
||||
/// # Async Trait
|
||||
///
|
||||
/// [`Responder`] is an _async_ trait. Implementations of `Responder` may be
|
||||
/// decorated with an attribute of `#[rocket::async_trait]`, allowing the
|
||||
/// `respond_to` method to be implemented as an `async fn`:
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::{self, Responder};
|
||||
/// use rocket::request::Request;
|
||||
/// # struct MyType;
|
||||
///
|
||||
/// #[rocket::async_trait]
|
||||
/// impl<'r> Responder<'r> for MyType {
|
||||
/// async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
/// /* .. */
|
||||
/// # unimplemented!()
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// In certain cases, including when implementing a _wrapping_ responder, it may
|
||||
/// be desirable to perform work before entering an `async` section, or to
|
||||
/// return a future directly. In this case, `Responder` can be implemented as:
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::response::{self, Responder};
|
||||
/// use rocket::futures::future::BoxFuture;
|
||||
/// use rocket::request::Request;
|
||||
/// # struct MyType<R> { inner: R };
|
||||
///
|
||||
/// impl<'r, R: Responder<'r>> Responder<'r> for MyType<R> {
|
||||
/// fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>>
|
||||
/// where 'a: 'x, 'r: 'x, Self: 'x
|
||||
/// {
|
||||
/// self.inner.respond_to(req)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// Say that you have a custom type, `Person`:
|
||||
///
|
||||
/// ```rust
|
||||
///
|
||||
/// # #[allow(dead_code)]
|
||||
/// struct Person {
|
||||
/// name: String,
|
||||
/// age: u16
|
||||
|
@ -129,11 +166,16 @@ use crate::request::Request;
|
|||
/// You'd like to use `Person` as a `Responder` so that you can return a
|
||||
/// `Person` directly from a handler:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// ```rust
|
||||
/// # #[macro_use] extern crate rocket;
|
||||
/// # type Person = String;
|
||||
/// #[get("/person/<id>")]
|
||||
/// fn person(id: usize) -> Option<Person> {
|
||||
/// # /*
|
||||
/// Person::from_id(id)
|
||||
/// # */ None
|
||||
/// }
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// You want the `Person` responder to set two header fields: `X-Person-Name`
|
||||
|
@ -154,17 +196,16 @@ use crate::request::Request;
|
|||
/// use rocket::response::{self, Response, Responder};
|
||||
/// use rocket::http::ContentType;
|
||||
///
|
||||
/// #[rocket::async_trait]
|
||||
/// impl<'r> Responder<'r> for Person {
|
||||
/// fn respond_to(self, _: &'r Request) -> response::ResultFuture<'r> {
|
||||
/// Box::pin(async move {
|
||||
/// async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
/// let person_string = format!("{}:{}", self.name, self.age);
|
||||
/// Response::build()
|
||||
/// .sized_body(Cursor::new(format!("{}:{}", self.name, self.age)))
|
||||
/// .sized_body(Cursor::new(person_string)).await
|
||||
/// .raw_header("X-Person-Name", self.name)
|
||||
/// .raw_header("X-Person-Age", self.age.to_string())
|
||||
/// .header(ContentType::new("application", "x-person"))
|
||||
/// .ok()
|
||||
/// .await
|
||||
/// })
|
||||
/// }
|
||||
/// }
|
||||
/// #
|
||||
|
@ -172,6 +213,7 @@ use crate::request::Request;
|
|||
/// # fn person() -> Person { Person { name: "a".to_string(), age: 20 } }
|
||||
/// # fn main() { }
|
||||
/// ```
|
||||
#[crate::async_trait]
|
||||
pub trait Responder<'r> {
|
||||
/// Returns `Ok` if a `Response` could be generated successfully. Otherwise,
|
||||
/// returns an `Err` with a failing `Status`.
|
||||
|
@ -184,113 +226,108 @@ pub trait Responder<'r> {
|
|||
/// returned, the error catcher for the given status is retrieved and called
|
||||
/// to generate a final error response, which is then written out to the
|
||||
/// client.
|
||||
fn respond_to(self, request: &'r Request<'_>) -> response::ResultFuture<'r>;
|
||||
async fn respond_to(self, request: &'r Request<'_>) -> response::Result<'r>;
|
||||
}
|
||||
|
||||
/// Returns a response with Content-Type `text/plain` and a fixed-size body
|
||||
/// containing the string `self`. Always returns `Ok`.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for &'r str {
|
||||
fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
Response::build()
|
||||
.header(ContentType::Plain)
|
||||
.sized_body(Cursor::new(self))
|
||||
.sized_body(Cursor::new(self)).await
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a response with Content-Type `text/plain` and a fixed-size body
|
||||
/// containing the string `self`. Always returns `Ok`.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for String {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
Response::build()
|
||||
.header(ContentType::Plain)
|
||||
.sized_body(Cursor::new(self))
|
||||
.sized_body(Cursor::new(self)).await
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a response with Content-Type `application/octet-stream` and a
|
||||
/// fixed-size body containing the data in `self`. Always returns `Ok`.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for &'r [u8] {
|
||||
fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
Response::build()
|
||||
.header(ContentType::Binary)
|
||||
.sized_body(Cursor::new(self))
|
||||
.sized_body(Cursor::new(self)).await
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a response with Content-Type `application/octet-stream` and a
|
||||
/// fixed-size body containing the data in `self`. Always returns `Ok`.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for Vec<u8> {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
Response::build()
|
||||
.header(ContentType::Binary)
|
||||
.sized_body(Cursor::new(self))
|
||||
.sized_body(Cursor::new(self)).await
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a response with a sized body for the file. Always returns `Ok`.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for File {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
tokio::fs::File::from(self).respond_to(req)
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
tokio::fs::File::from(self).respond_to(req).await
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a response with a sized body for the file. Always returns `Ok`.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for tokio::fs::File {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
let metadata = self.metadata().await;
|
||||
let stream = BufReader::new(self);
|
||||
match metadata {
|
||||
Ok(md) => Response::build().raw_body(Body::Sized(stream, md.len())).ok().await,
|
||||
Err(_) => Response::build().streamed_body(stream).ok().await
|
||||
Ok(md) => Response::build().raw_body(Body::Sized(stream, md.len())).ok(),
|
||||
Err(_) => Response::build().streamed_body(stream).ok()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an empty, default `Response`. Always returns `Ok`.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for () {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
Ok(Response::new())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// If `self` is `Some`, responds with the wrapped `Responder`. Otherwise prints
|
||||
/// a warning message and returns an `Err` of `Status::NotFound`.
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for Option<R> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>>
|
||||
where 'a: 'x, 'r: 'x, Self: 'x
|
||||
{
|
||||
match self {
|
||||
Some(r) => r.respond_to(req),
|
||||
None => {
|
||||
warn_!("Response was `None`.");
|
||||
Box::pin(ready(Err(Status::NotFound)))
|
||||
Box::pin(async { Err(Status::NotFound) })
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Responds with the wrapped `Responder` in `self`, whether it is `Ok` or
|
||||
// Responds with the wrapped `Responder` in `self`, whether it is `Ok` or
|
||||
/// `Err`.
|
||||
impl<'r, R: Responder<'r>, E: Responder<'r>> Responder<'r> for Result<R, E> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
fn respond_to<'a, 'x>(self, req: &'r Request<'a>) -> BoxFuture<'x, response::Result<'r>>
|
||||
where 'a: 'x, 'r: 'x, Self: 'x
|
||||
{
|
||||
match self {
|
||||
Ok(responder) => responder.respond_to(req),
|
||||
Err(responder) => responder.respond_to(req),
|
||||
|
@ -312,16 +349,16 @@ impl<'r, R: Responder<'r>, E: Responder<'r>> Responder<'r> for Result<R, E> {
|
|||
/// `100` responds with any empty body and the given status code, and all other
|
||||
/// status code emit an error message and forward to the `500` (internal server
|
||||
/// error) catcher.
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for Status {
|
||||
fn respond_to(self, _: &'r Request<'_>) -> response::ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
match self.class() {
|
||||
StatusClass::ClientError | StatusClass::ServerError => Err(self),
|
||||
StatusClass::Success if self.code < 206 => {
|
||||
Response::build().status(self).ok().await
|
||||
Response::build().status(self).ok()
|
||||
}
|
||||
StatusClass::Informational if self.code == 100 => {
|
||||
Response::build().status(self).ok().await
|
||||
Response::build().status(self).ok()
|
||||
}
|
||||
_ => {
|
||||
error_!("Invalid status used as responder: {}.", self);
|
||||
|
@ -329,6 +366,5 @@ impl<'r> Responder<'r> for Status {
|
|||
Err(Status::InternalServerError)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
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, AsyncSeek, AsyncSeekExt};
|
||||
|
||||
use crate::response::{Responder, ResultFuture};
|
||||
use crate::response::{self, Responder};
|
||||
use crate::http::{Header, HeaderMap, Status, ContentType, Cookie};
|
||||
|
||||
/// The default size, in bytes, of a chunk for streamed responses.
|
||||
|
@ -100,12 +98,6 @@ 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
|
||||
|
@ -119,9 +111,8 @@ impl<T: AsyncRead + AsyncSeek + Unpin + Send> AsyncReadAsyncSeek for 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`, `.await` the builder.
|
||||
/// The [`ok()` method](#method.ok) is also provided as a convenience
|
||||
/// for `Responder` implementations.
|
||||
/// To finish building and retrieve the built `Response`, use the
|
||||
/// [`finalize()`](#method.finalize) or [`ok()`](#method.ok) methods.
|
||||
///
|
||||
/// ## Headers
|
||||
///
|
||||
|
@ -157,23 +148,18 @@ impl<T: AsyncRead + AsyncSeek + Unpin + Send> AsyncReadAsyncSeek for T {}
|
|||
/// use rocket::http::{Status, ContentType};
|
||||
///
|
||||
/// # rocket::async_test(async {
|
||||
///
|
||||
/// # #[allow(unused_variables)]
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .header(ContentType::Plain)
|
||||
/// .raw_header("X-Teapot-Make", "Rocket")
|
||||
/// .raw_header("X-Teapot-Model", "Utopia")
|
||||
/// .raw_header_adjoin("X-Teapot-Model", "Series 1")
|
||||
/// .sized_body(Cursor::new("Brewing the best coffee!"))
|
||||
/// .await;
|
||||
///
|
||||
/// # })
|
||||
/// .sized_body(Cursor::new("Brewing the best coffee!")).await
|
||||
/// .finalize();
|
||||
/// # });
|
||||
/// ```
|
||||
pub struct ResponseBuilder<'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> {
|
||||
|
@ -192,8 +178,6 @@ impl<'r> ResponseBuilder<'r> {
|
|||
pub fn new(base: Response<'r>) -> ResponseBuilder<'r> {
|
||||
ResponseBuilder {
|
||||
response: base,
|
||||
pending_sized_body: None,
|
||||
fut: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -205,14 +189,9 @@ 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)
|
||||
/// .await;
|
||||
///
|
||||
/// # })
|
||||
/// .finalize();
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn status(&mut self, status: Status) -> &mut ResponseBuilder<'r> {
|
||||
|
@ -227,14 +206,13 @@ impl<'r> ResponseBuilder<'r> {
|
|||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// # rocket::async_test(async {
|
||||
///
|
||||
/// # #[allow(unused_variables)]
|
||||
/// let response = Response::build()
|
||||
/// .raw_status(699, "Alien Encounter")
|
||||
/// .await;
|
||||
/// # })
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.status(), Status::new(699, "Alien Encounter"));
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn raw_status(&mut self, code: u16, reason: &'static str) -> &mut ResponseBuilder<'r> {
|
||||
|
@ -257,16 +235,12 @@ 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)
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.headers().get("Content-Type").count(), 1);
|
||||
///
|
||||
/// # })
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn header<'h: 'r, H>(&mut self, header: H) -> &mut ResponseBuilder<'r>
|
||||
|
@ -292,16 +266,12 @@ 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"))
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.headers().get("Accept").count(), 2);
|
||||
///
|
||||
/// # })
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn header_adjoin<'h: 'r, H>(&mut self, header: H) -> &mut ResponseBuilder<'r>
|
||||
|
@ -321,16 +291,12 @@ 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")
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.headers().get("X-Custom").count(), 1);
|
||||
///
|
||||
/// # })
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn raw_header<'a, 'b, N, V>(&mut self, name: N, value: V) -> &mut ResponseBuilder<'r>
|
||||
|
@ -351,16 +317,12 @@ 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")
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.headers().get("X-Custom").count(), 2);
|
||||
///
|
||||
/// # })
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn raw_header_adjoin<'a, 'b, N, V>(&mut self, name: N, value: V) -> &mut ResponseBuilder<'r>
|
||||
|
@ -375,24 +337,19 @@ impl<'r> ResponseBuilder<'r> {
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::Cursor;
|
||||
/// use rocket::Response;
|
||||
/// use tokio::fs::File;
|
||||
/// # use std::io;
|
||||
///
|
||||
/// # #[allow(dead_code)]
|
||||
/// # async fn test() -> io::Result<()> {
|
||||
/// # #[allow(unused_variables)]
|
||||
/// # rocket::async_test(async {
|
||||
/// let response = Response::build()
|
||||
/// .sized_body(File::open("body.txt").await?)
|
||||
/// .await;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// .sized_body(Cursor::new("Hello, world!")).await
|
||||
/// .finalize();
|
||||
/// # })
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn sized_body<B>(&mut self, body: B) -> &mut ResponseBuilder<'r>
|
||||
pub async fn sized_body<B>(&mut self, body: B) -> &mut ResponseBuilder<'r>
|
||||
where B: AsyncRead + AsyncSeek + Send + Unpin + 'r
|
||||
{
|
||||
self.pending_sized_body = Some(Box::new(body));
|
||||
self.response.set_sized_body(body).await;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -401,25 +358,18 @@ impl<'r> ResponseBuilder<'r> {
|
|||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::Cursor;
|
||||
/// use rocket::Response;
|
||||
/// use tokio::fs::File;
|
||||
/// # use std::io;
|
||||
///
|
||||
/// # #[allow(dead_code)]
|
||||
/// # async fn test() -> io::Result<()> {
|
||||
/// # #[allow(unused_variables)]
|
||||
/// let response = Response::build()
|
||||
/// .streamed_body(File::open("body.txt").await?)
|
||||
/// .await;
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// .streamed_body(Cursor::new("Hello, world!"))
|
||||
/// .finalize();
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn streamed_body<B>(&mut self, body: B) -> &mut ResponseBuilder<'r>
|
||||
where B: AsyncRead + Send + 'r
|
||||
{
|
||||
self.response.set_streamed_body(body);
|
||||
self.pending_sized_body = None;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -433,21 +383,18 @@ impl<'r> ResponseBuilder<'r> {
|
|||
/// use tokio::fs::File;
|
||||
/// # use std::io;
|
||||
///
|
||||
/// # #[allow(dead_code)]
|
||||
/// # async fn test() -> io::Result<()> {
|
||||
/// # #[allow(unused_variables)]
|
||||
/// # async fn test<'r>() -> io::Result<Response<'r>> {
|
||||
/// let response = Response::build()
|
||||
/// .chunked_body(File::open("body.txt").await?, 8096)
|
||||
/// .await;
|
||||
/// # Ok(())
|
||||
/// .ok();
|
||||
/// # response
|
||||
/// # }
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn chunked_body<B: AsyncRead + Send + 'r>(&mut self, body: B, chunk_size: u64)
|
||||
-> &mut ResponseBuilder<'r>
|
||||
pub fn chunked_body<B>(&mut self, body: B, chunk_size: u64) -> &mut ResponseBuilder<'r>
|
||||
where B: AsyncRead + Send + 'r
|
||||
{
|
||||
self.response.set_chunked_body(body, chunk_size);
|
||||
self.pending_sized_body = None;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -462,20 +409,16 @@ impl<'r> ResponseBuilder<'r> {
|
|||
/// use rocket::response::{Response, Body};
|
||||
///
|
||||
/// # rocket::async_test(async {
|
||||
///
|
||||
/// # #[allow(unused_variables)]
|
||||
/// let response = Response::build()
|
||||
/// .raw_body(Body::Sized(Cursor::new("Hello!"), 6))
|
||||
/// .await;
|
||||
///
|
||||
/// .finalize();
|
||||
/// # })
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn raw_body<T: AsyncRead + Send + Unpin + 'r>(&mut self, body: Body<T>)
|
||||
-> &mut ResponseBuilder<'r>
|
||||
pub fn raw_body<T>(&mut self, body: Body<T>) -> &mut ResponseBuilder<'r>
|
||||
where T: AsyncRead + Send + Unpin + 'r
|
||||
{
|
||||
self.response.set_raw_body(body);
|
||||
self.pending_sized_body = None;
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -491,40 +434,29 @@ 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")
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .raw_header("X-Custom", "value 2")
|
||||
/// .raw_header_adjoin("X-Custom", "value 3")
|
||||
/// .merge(base)
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.status(), Status::NotFound);
|
||||
///
|
||||
/// # {
|
||||
/// let ctype: Vec<_> = response.headers().get("Content-Type").collect();
|
||||
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
|
||||
/// # }
|
||||
///
|
||||
/// # {
|
||||
/// let custom_values: Vec<_> = response.headers().get("X-Custom").collect();
|
||||
/// assert_eq!(custom_values, vec!["value 1"]);
|
||||
/// # }
|
||||
///
|
||||
/// # });
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn merge(&mut self, mut other: Response<'r>) -> &mut ResponseBuilder<'r> {
|
||||
if other.body().is_some() {
|
||||
self.pending_sized_body = None;
|
||||
}
|
||||
pub fn merge(&mut self, other: Response<'r>) -> &mut ResponseBuilder<'r> {
|
||||
self.response.merge(other);
|
||||
self
|
||||
}
|
||||
|
@ -542,34 +474,26 @@ 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")
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .raw_header("X-Custom", "value 2")
|
||||
/// .raw_header_adjoin("X-Custom", "value 3")
|
||||
/// .join(other)
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.status(), Status::ImATeapot);
|
||||
///
|
||||
/// # {
|
||||
/// let ctype: Vec<_> = response.headers().get("Content-Type").collect();
|
||||
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
|
||||
/// # }
|
||||
///
|
||||
/// # {
|
||||
/// 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> {
|
||||
|
@ -577,48 +501,47 @@ impl<'r> ResponseBuilder<'r> {
|
|||
self
|
||||
}
|
||||
|
||||
/// Retrieve the built `Response` wrapped in `Ok`.
|
||||
/// Return the `Response` structure that was being built by this builder.
|
||||
/// After calling this method, `self` is cleared and must be rebuilt as if
|
||||
/// from `new()`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::io::Cursor;
|
||||
///
|
||||
/// use rocket::Response;
|
||||
/// use rocket::http::Status;
|
||||
///
|
||||
/// # rocket::async_test(async {
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .sized_body(Cursor::new("Brewing the best coffee!")).await
|
||||
/// .raw_header("X-Custom", "value 2")
|
||||
/// .finalize();
|
||||
/// # })
|
||||
/// ```
|
||||
pub fn finalize(&mut self) -> Response<'r> {
|
||||
std::mem::replace(&mut self.response, Response::new())
|
||||
}
|
||||
|
||||
/// Retrieve the built `Response` wrapped in `Ok`. After calling this
|
||||
/// method, `self` is cleared and must be rebuilt as if from `new()`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use rocket::Response;
|
||||
///
|
||||
/// # rocket::async_test(async {
|
||||
///
|
||||
/// let response: Result<Response, ()> = Response::build()
|
||||
/// // build the response
|
||||
/// .ok()
|
||||
/// .await;
|
||||
/// .ok();
|
||||
///
|
||||
/// assert!(response.is_ok());
|
||||
///
|
||||
/// # })
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
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)
|
||||
pub fn ok<E>(&mut self) -> Result<Response<'r>, E> {
|
||||
Ok(self.finalize())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1139,7 +1062,8 @@ impl<'r> Response<'r> {
|
|||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn set_chunked_body<B>(&mut self, body: B, chunk_size: u64)
|
||||
where B: AsyncRead + Send + 'r {
|
||||
where B: AsyncRead + Send + 'r
|
||||
{
|
||||
self.body = Some(Body::Chunked(Box::pin(body), chunk_size));
|
||||
}
|
||||
|
||||
|
@ -1181,34 +1105,26 @@ 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")
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .raw_header("X-Custom", "value 2")
|
||||
/// .raw_header_adjoin("X-Custom", "value 3")
|
||||
/// .merge(base)
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.status(), Status::NotFound);
|
||||
///
|
||||
/// # {
|
||||
/// let ctype: Vec<_> = response.headers().get("Content-Type").collect();
|
||||
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
|
||||
/// # }
|
||||
///
|
||||
/// # {
|
||||
/// 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 {
|
||||
|
@ -1234,34 +1150,26 @@ 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")
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// let response = Response::build()
|
||||
/// .status(Status::ImATeapot)
|
||||
/// .raw_header("X-Custom", "value 2")
|
||||
/// .raw_header_adjoin("X-Custom", "value 3")
|
||||
/// .join(other)
|
||||
/// .await;
|
||||
/// .finalize();
|
||||
///
|
||||
/// assert_eq!(response.status(), Status::ImATeapot);
|
||||
///
|
||||
/// # {
|
||||
/// let ctype: Vec<_> = response.headers().get("Content-Type").collect();
|
||||
/// assert_eq!(ctype, vec![ContentType::HTML.to_string()]);
|
||||
/// # }
|
||||
///
|
||||
/// # {
|
||||
/// 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() {
|
||||
|
@ -1295,11 +1203,10 @@ impl fmt::Debug for Response<'_> {
|
|||
|
||||
use crate::request::Request;
|
||||
|
||||
#[crate::async_trait]
|
||||
impl<'r> Responder<'r> for Response<'r> {
|
||||
/// This is the identity implementation. It simply returns `Ok(self)`.
|
||||
fn respond_to(self, _: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
Box::pin(async {
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
Ok(self)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use std::collections::hash_map::DefaultHasher;
|
|||
use std::borrow::Cow;
|
||||
|
||||
use crate::request::Request;
|
||||
use crate::response::{Responder, Response, ResultFuture};
|
||||
use crate::response::{self, Responder, Response};
|
||||
use crate::http::Status;
|
||||
|
||||
/// Sets the status of the response to 201 (Created).
|
||||
|
@ -160,9 +160,9 @@ impl<'r, R> Created<R> {
|
|||
/// the response with the `Responder`, the `ETag` header is set conditionally if
|
||||
/// a hashable `Responder` is provided via [`Created::tagged_body()`]. The `ETag`
|
||||
/// header is set to a hash value of the responder.
|
||||
#[crate::async_trait]
|
||||
impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Created<R> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
let mut response = Response::build();
|
||||
if let Some(responder) = self.1 {
|
||||
response.merge(responder.respond_to(req).await?);
|
||||
|
@ -175,8 +175,6 @@ impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Created<R> {
|
|||
response.status(Status::Created)
|
||||
.raw_header("Location", self.0)
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,16 +207,15 @@ pub struct Accepted<R>(pub Option<R>);
|
|||
|
||||
/// Sets the status code of the response to 202 Accepted. If the responder is
|
||||
/// `Some`, it is used to finalize the response.
|
||||
#[crate::async_trait]
|
||||
impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Accepted<R> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
let mut build = Response::build();
|
||||
if let Some(responder) = self.0 {
|
||||
build.merge(responder.respond_to(req).await?);
|
||||
}
|
||||
|
||||
build.status(Status::Accepted).ok().await
|
||||
})
|
||||
build.status(Status::Accepted).ok()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -276,16 +273,15 @@ pub struct BadRequest<R>(pub Option<R>);
|
|||
|
||||
/// Sets the status code of the response to 400 Bad Request. If the responder is
|
||||
/// `Some`, it is used to finalize the response.
|
||||
#[crate::async_trait]
|
||||
impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for BadRequest<R> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
let mut build = Response::build();
|
||||
if let Some(responder) = self.0 {
|
||||
build.merge(responder.respond_to(req).await?);
|
||||
}
|
||||
|
||||
build.status(Status::BadRequest).ok().await
|
||||
})
|
||||
build.status(Status::BadRequest).ok()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -385,14 +381,12 @@ impl<'r, R: Responder<'r>> Responder<'r> for Forbidden<R> {
|
|||
pub struct NotFound<R>(pub R);
|
||||
|
||||
/// Sets the status code of the response to 404 Not Found.
|
||||
#[crate::async_trait]
|
||||
impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for NotFound<R> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
Response::build_from(self.0.respond_to(req).await?)
|
||||
.status(Status::NotFound)
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -453,14 +447,12 @@ pub struct Custom<R>(pub Status, pub R);
|
|||
|
||||
/// Sets the status code of the response and then delegates the remainder of the
|
||||
/// response to the wrapped responder.
|
||||
#[crate::async_trait]
|
||||
impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Custom<R> {
|
||||
fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> {
|
||||
Box::pin(async move {
|
||||
async fn respond_to(self, req: &'r Request<'_>) -> response::Result<'r> {
|
||||
Response::build_from(self.1.respond_to(req).await?)
|
||||
.status(self.0)
|
||||
.ok()
|
||||
.await
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::fmt::{self, Debug};
|
|||
use tokio::io::AsyncRead;
|
||||
|
||||
use crate::request::Request;
|
||||
use crate::response::{Response, Responder, ResultFuture, DEFAULT_CHUNK_SIZE};
|
||||
use crate::response::{self, Response, Responder, DEFAULT_CHUNK_SIZE};
|
||||
|
||||
/// Streams a response to a client from an arbitrary `AsyncRead`er type.
|
||||
///
|
||||
|
@ -66,10 +66,9 @@ impl<T: AsyncRead> From<T> for Stream<T> {
|
|||
/// If reading from the input stream fails at any point during the response, the
|
||||
/// response is abandoned, and the response ends abruptly. An error is printed
|
||||
/// to the console with an indication of what went wrong.
|
||||
#[crate::async_trait]
|
||||
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().await
|
||||
})
|
||||
async fn respond_to(self, _: &'r Request<'_>) -> response::Result<'r> {
|
||||
Response::build().chunked_body(self.0, self.1).ok()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,9 @@ use crate::http::Method;
|
|||
type Selector = Method;
|
||||
|
||||
// A handler to use when one is needed temporarily.
|
||||
pub(crate) fn dummy_handler<'r>(r: &'r Request<'_>, _: crate::Data) -> BoxFuture<'r, crate::handler::Outcome<'r>> {
|
||||
pub(crate) fn dummy_handler<'r>(
|
||||
r: &'r Request<'_>, _: crate::Data
|
||||
) -> BoxFuture<'r, crate::handler::Outcome<'r>> {
|
||||
crate::Outcome::from(r, ())
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use rocket::http::Header;
|
|||
async fn do_not_overwrite() -> Response<'static> {
|
||||
Response::build()
|
||||
.header(Header::new("Server", "Test"))
|
||||
.await
|
||||
.finalize()
|
||||
}
|
||||
|
||||
#[get("/use_default")]
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
|
||||
#[macro_use] extern crate rocket;
|
||||
|
||||
use rocket::State;
|
||||
use rocket::response::{self, Responder};
|
||||
use rocket::{Request, State};
|
||||
use rocket::futures::future::BoxFuture;
|
||||
use rocket::response::{Responder, Result};
|
||||
|
||||
struct SomeState;
|
||||
|
||||
|
@ -14,7 +15,9 @@ pub struct CustomResponder<'r, R> {
|
|||
}
|
||||
|
||||
impl<'r, R: Responder<'r>> Responder<'r> for CustomResponder<'r, R> {
|
||||
fn respond_to(self, _: &rocket::Request) -> response::ResultFuture<'r> {
|
||||
fn respond_to<'a, 'x>(self, _: &'r Request<'a>) -> BoxFuture<'x, Result<'r>>
|
||||
where 'a: 'x, 'r: 'x, Self: 'x
|
||||
{
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,14 +5,14 @@ mod tests;
|
|||
|
||||
use std::env;
|
||||
|
||||
use tokio::fs::File;
|
||||
|
||||
use rocket::{Request, Handler, Route, Data, Catcher, try_outcome};
|
||||
use rocket::http::{Status, RawStr};
|
||||
use rocket::response::{self, Responder, status::Custom};
|
||||
use rocket::handler::{Outcome, HandlerFuture};
|
||||
use rocket::outcome::IntoOutcome;
|
||||
use rocket::http::Method::*;
|
||||
use rocket::futures::future::BoxFuture;
|
||||
use rocket::tokio::fs::File;
|
||||
|
||||
fn forward<'r>(_req: &'r Request, data: Data) -> HandlerFuture<'r> {
|
||||
Box::pin(async move { Outcome::forward(data) })
|
||||
|
@ -68,7 +68,7 @@ fn get_upload<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> {
|
|||
Outcome::from(req, std::fs::File::open(env::temp_dir().join("upload.txt")).ok())
|
||||
}
|
||||
|
||||
fn not_found_handler<'r>(req: &'r Request) -> response::ResultFuture<'r> {
|
||||
fn not_found_handler<'r>(req: &'r Request) -> BoxFuture<'r, response::Result<'r>> {
|
||||
let res = Custom(Status::NotFound, format!("Couldn't find: {}", req.uri()));
|
||||
res.respond_to(req)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue