mirror of https://github.com/rwf2/Rocket.git
Use read_to_string (from futures-preview 0.3.0-alpha.18) to more closely match the pre-async code.
This commit is contained in:
parent
0e6841da66
commit
aee7f35095
|
@ -42,7 +42,7 @@ memcache_pool = ["databases", "memcache", "r2d2-memcache"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Global dependencies.
|
# Global dependencies.
|
||||||
futures-preview = { version = "0.3.0-alpha.17" }
|
futures-preview = { version = "0.3.0-alpha.18" }
|
||||||
rocket_contrib_codegen = { version = "0.5.0-dev", path = "../codegen", optional = true }
|
rocket_contrib_codegen = { version = "0.5.0-dev", path = "../codegen", optional = true }
|
||||||
rocket = { version = "0.5.0-dev", path = "../../core/lib/", default-features = false }
|
rocket = { version = "0.5.0-dev", path = "../../core/lib/", default-features = false }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
|
|
@ -139,15 +139,10 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for Json<T> {
|
||||||
fn transform(r: &Request<'_>, d: Data) -> TransformFuture<'a, Self::Owned, Self::Error> {
|
fn transform(r: &Request<'_>, d: Data) -> TransformFuture<'a, Self::Owned, Self::Error> {
|
||||||
let size_limit = r.limits().get("json").unwrap_or(LIMIT);
|
let size_limit = r.limits().get("json").unwrap_or(LIMIT);
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut v = Vec::with_capacity(512);
|
let mut s = String::with_capacity(512);
|
||||||
let mut reader = d.open().take(size_limit);
|
let mut reader = d.open().take(size_limit);
|
||||||
match reader.read_to_end(&mut v).await {
|
match reader.read_to_string(&mut s).await {
|
||||||
Ok(_) => {
|
Ok(_) => Borrowed(Success(s)),
|
||||||
match String::from_utf8(v) {
|
|
||||||
Ok(s) => Borrowed(Success(s)),
|
|
||||||
Err(e) => Borrowed(Failure((Status::BadRequest, JsonError::Io(std::io::Error::new(std::io::ErrorKind::Other, e))))),
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => Borrowed(Failure((Status::BadRequest, JsonError::Io(e))))
|
Err(e) => Borrowed(Failure((Status::BadRequest, JsonError::Io(e))))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -26,7 +26,7 @@ private-cookies = ["rocket_http/private-cookies"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket_codegen = { version = "0.5.0-dev", path = "../codegen" }
|
rocket_codegen = { version = "0.5.0-dev", path = "../codegen" }
|
||||||
rocket_http = { version = "0.5.0-dev", path = "../http" }
|
rocket_http = { version = "0.5.0-dev", path = "../http" }
|
||||||
futures-preview = { version = "0.3.0-alpha.14", features = ["compat", "io-compat"] }
|
futures-preview = { version = "0.3.0-alpha.18", features = ["compat", "io-compat"] }
|
||||||
tokio = "0.1.16"
|
tokio = "0.1.16"
|
||||||
yansi = "0.5"
|
yansi = "0.5"
|
||||||
log = { version = "0.4", features = ["std"] }
|
log = { version = "0.4", features = ["std"] }
|
||||||
|
|
|
@ -142,7 +142,7 @@ pub type FromDataFuture<'a, T, E> = Pin<Box<dyn Future<Output = Outcome<T, E>> +
|
||||||
/// if the guard returns successfully.
|
/// if the guard returns successfully.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # #![feature(proc_macro_hygiene)]
|
/// # #![feature(proc_macro_hygiene, async_await)]
|
||||||
/// # #[macro_use] extern crate rocket;
|
/// # #[macro_use] extern crate rocket;
|
||||||
/// # type DataGuard = rocket::data::Data;
|
/// # type DataGuard = rocket::data::Data;
|
||||||
/// #[post("/submit", data = "<var>")]
|
/// #[post("/submit", data = "<var>")]
|
||||||
|
@ -188,16 +188,20 @@ pub type FromDataFuture<'a, T, E> = Pin<Box<dyn Future<Output = Outcome<T, E>> +
|
||||||
/// `String` (an `&str`).
|
/// `String` (an `&str`).
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # #![feature(proc_macro_hygiene)]
|
/// # #![feature(proc_macro_hygiene, async_await)]
|
||||||
/// # #[macro_use] extern crate rocket;
|
/// # #[macro_use] extern crate rocket;
|
||||||
/// # #[derive(Debug)]
|
/// # #[derive(Debug)]
|
||||||
/// # struct Name<'a> { first: &'a str, last: &'a str, }
|
/// # struct Name<'a> { first: &'a str, last: &'a str, }
|
||||||
/// use std::io::{self, Read};
|
/// use std::io::{self, Read};
|
||||||
///
|
///
|
||||||
|
/// use futures::io::AsyncReadExt;
|
||||||
|
///
|
||||||
/// use rocket::{Request, Data, Outcome::*};
|
/// use rocket::{Request, Data, Outcome::*};
|
||||||
/// use rocket::data::{FromData, Outcome, Transform, Transformed};
|
/// use rocket::data::{FromData, Outcome, Transform, Transformed, TransformFuture, FromDataFuture};
|
||||||
/// use rocket::http::Status;
|
/// use rocket::http::Status;
|
||||||
///
|
///
|
||||||
|
/// use rocket::AsyncReadExt as _;
|
||||||
|
///
|
||||||
/// const NAME_LIMIT: u64 = 256;
|
/// const NAME_LIMIT: u64 = 256;
|
||||||
///
|
///
|
||||||
/// enum NameError {
|
/// enum NameError {
|
||||||
|
@ -210,32 +214,36 @@ pub type FromDataFuture<'a, T, E> = Pin<Box<dyn Future<Output = Outcome<T, E>> +
|
||||||
/// type Owned = String;
|
/// type Owned = String;
|
||||||
/// type Borrowed = str;
|
/// type Borrowed = str;
|
||||||
///
|
///
|
||||||
/// fn transform(_: &Request, data: Data) -> Transform<Outcome<Self::Owned, Self::Error>> {
|
/// fn transform(_: &Request, data: Data) -> TransformFuture<'a, Self::Owned, Self::Error> {
|
||||||
/// let mut stream = data.open().take(NAME_LIMIT);
|
/// Box::pin(async move {
|
||||||
/// let mut string = String::with_capacity((NAME_LIMIT / 2) as usize);
|
/// let mut stream = data.open().take(NAME_LIMIT);
|
||||||
/// let outcome = match stream.read_to_string(&mut string) {
|
/// let mut string = String::with_capacity((NAME_LIMIT / 2) as usize);
|
||||||
/// Ok(_) => Success(string),
|
/// let outcome = match stream.read_to_string(&mut string).await {
|
||||||
/// Err(e) => Failure((Status::InternalServerError, NameError::Io(e)))
|
/// Ok(_) => Success(string),
|
||||||
/// };
|
/// Err(e) => Failure((Status::InternalServerError, NameError::Io(e)))
|
||||||
|
/// };
|
||||||
///
|
///
|
||||||
/// // Returning `Borrowed` here means we get `Borrowed` in `from_data`.
|
/// // Returning `Borrowed` here means we get `Borrowed` in `from_data`.
|
||||||
/// Transform::Borrowed(outcome)
|
/// Transform::Borrowed(outcome)
|
||||||
|
/// })
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// fn from_data(_: &Request, outcome: Transformed<'a, Self>) -> Outcome<Self, Self::Error> {
|
/// fn from_data(_: &Request, outcome: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error> {
|
||||||
/// // Retrieve a borrow to the now transformed `String` (an &str). This
|
/// Box::pin(async move {
|
||||||
/// // is only correct because we know we _always_ return a `Borrowed` from
|
/// // Retrieve a borrow to the now transformed `String` (an &str). This
|
||||||
/// // `transform` above.
|
/// // is only correct because we know we _always_ return a `Borrowed` from
|
||||||
/// let string = try_outcome!(outcome.borrowed());
|
/// // `transform` above.
|
||||||
|
/// let string = try_outcome!(outcome.borrowed());
|
||||||
///
|
///
|
||||||
/// // Perform a crude, inefficient parse.
|
/// // Perform a crude, inefficient parse.
|
||||||
/// let splits: Vec<&str> = string.split(" ").collect();
|
/// let splits: Vec<&str> = string.split(" ").collect();
|
||||||
/// if splits.len() != 2 || splits.iter().any(|s| s.is_empty()) {
|
/// if splits.len() != 2 || splits.iter().any(|s| s.is_empty()) {
|
||||||
/// return Failure((Status::UnprocessableEntity, NameError::Parse));
|
/// return Failure((Status::UnprocessableEntity, NameError::Parse));
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// // Return successfully.
|
/// // Return successfully.
|
||||||
/// Success(Name { first: splits[0], last: splits[1] })
|
/// Success(Name { first: splits[0], last: splits[1] })
|
||||||
|
/// })
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// # #[post("/person", data = "<person>")]
|
/// # #[post("/person", data = "<person>")]
|
||||||
|
@ -435,7 +443,7 @@ impl<'a> FromData<'a> for Data {
|
||||||
/// that you can retrieve it directly from a client's request body:
|
/// that you can retrieve it directly from a client's request body:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # #![feature(proc_macro_hygiene)]
|
/// # #![feature(proc_macro_hygiene, async_await)]
|
||||||
/// # #[macro_use] extern crate rocket;
|
/// # #[macro_use] extern crate rocket;
|
||||||
/// # type Person = rocket::data::Data;
|
/// # type Person = rocket::data::Data;
|
||||||
/// #[post("/person", data = "<person>")]
|
/// #[post("/person", data = "<person>")]
|
||||||
|
@ -447,7 +455,7 @@ impl<'a> FromData<'a> for Data {
|
||||||
/// A `FromDataSimple` implementation allowing this looks like:
|
/// A `FromDataSimple` implementation allowing this looks like:
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// # #![feature(proc_macro_hygiene)]
|
/// # #![feature(proc_macro_hygiene, async_await)]
|
||||||
/// # #[macro_use] extern crate rocket;
|
/// # #[macro_use] extern crate rocket;
|
||||||
/// #
|
/// #
|
||||||
/// # #[derive(Debug)]
|
/// # #[derive(Debug)]
|
||||||
|
@ -455,43 +463,50 @@ impl<'a> FromData<'a> for Data {
|
||||||
/// #
|
/// #
|
||||||
/// use std::io::Read;
|
/// use std::io::Read;
|
||||||
///
|
///
|
||||||
|
/// use futures::io::AsyncReadExt;
|
||||||
|
///
|
||||||
/// use rocket::{Request, Data, Outcome, Outcome::*};
|
/// use rocket::{Request, Data, Outcome, Outcome::*};
|
||||||
/// use rocket::data::{self, FromDataSimple};
|
/// use rocket::data::{self, FromDataSimple, FromDataFuture};
|
||||||
/// use rocket::http::{Status, ContentType};
|
/// use rocket::http::{Status, ContentType};
|
||||||
///
|
///
|
||||||
|
/// use rocket::AsyncReadExt as _;
|
||||||
|
///
|
||||||
/// // Always use a limit to prevent DoS attacks.
|
/// // Always use a limit to prevent DoS attacks.
|
||||||
/// const LIMIT: u64 = 256;
|
/// const LIMIT: u64 = 256;
|
||||||
///
|
///
|
||||||
/// impl FromDataSimple for Person {
|
/// impl FromDataSimple for Person {
|
||||||
/// type Error = String;
|
/// type Error = String;
|
||||||
///
|
///
|
||||||
/// fn from_data(req: &Request, data: Data) -> data::Outcome<Self, String> {
|
/// fn from_data(req: &Request, data: Data) -> FromDataFuture<'static, Self, String> {
|
||||||
/// // Ensure the content type is correct before opening the data.
|
/// // Ensure the content type is correct before opening the data.
|
||||||
/// let person_ct = ContentType::new("application", "x-person");
|
/// let person_ct = ContentType::new("application", "x-person");
|
||||||
/// if req.content_type() != Some(&person_ct) {
|
/// if req.content_type() != Some(&person_ct) {
|
||||||
/// return Outcome::Forward(data);
|
/// return Box::pin(async move { Outcome::Forward(data) });
|
||||||
/// }
|
/// }
|
||||||
///
|
///
|
||||||
/// // Read the data into a String.
|
/// Box::pin(async move {
|
||||||
/// let mut string = String::new();
|
/// // Read the data into a String.
|
||||||
/// if let Err(e) = data.open().take(LIMIT).read_to_string(&mut string) {
|
/// let mut string = String::new();
|
||||||
/// return Failure((Status::InternalServerError, format!("{:?}", e)));
|
/// let mut reader = data.open().take(LIMIT);
|
||||||
/// }
|
/// if let Err(e) = reader.read_to_string(&mut string).await {
|
||||||
|
/// return Failure((Status::InternalServerError, format!("{:?}", e)));
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// // Split the string into two pieces at ':'.
|
/// // Split the string into two pieces at ':'.
|
||||||
/// let (name, age) = match string.find(':') {
|
/// let (name, age) = match string.find(':') {
|
||||||
/// Some(i) => (string[..i].to_string(), &string[(i + 1)..]),
|
/// Some(i) => (string[..i].to_string(), &string[(i + 1)..]),
|
||||||
/// None => return Failure((Status::UnprocessableEntity, "':'".into()))
|
/// None => return Failure((Status::UnprocessableEntity, "':'".into()))
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// // Parse the age.
|
/// // Parse the age.
|
||||||
/// let age: u16 = match age.parse() {
|
/// let age: u16 = match age.parse() {
|
||||||
/// Ok(age) => age,
|
/// Ok(age) => age,
|
||||||
/// Err(_) => return Failure((Status::UnprocessableEntity, "Age".into()))
|
/// Err(_) => return Failure((Status::UnprocessableEntity, "Age".into()))
|
||||||
/// };
|
/// };
|
||||||
///
|
///
|
||||||
/// // Return successfully.
|
/// // Return successfully.
|
||||||
/// Success(Person { name, age })
|
/// Success(Person { name, age })
|
||||||
|
/// })
|
||||||
/// }
|
/// }
|
||||||
/// }
|
/// }
|
||||||
/// # #[post("/person", data = "<person>")]
|
/// # #[post("/person", data = "<person>")]
|
||||||
|
@ -579,14 +594,11 @@ impl FromDataSimple for String {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from_data(_: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error> {
|
fn from_data(_: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error> {
|
||||||
Box::pin(async {
|
Box::pin(async {
|
||||||
let mut stream = data.open();
|
let mut string = String::new();
|
||||||
let mut buf = Vec::new();
|
let mut reader = data.open();
|
||||||
if let Err(e) = stream.read_to_end(&mut buf).await {
|
match reader.read_to_string(&mut string).await {
|
||||||
return Failure((Status::BadRequest, e));
|
Ok(_) => Success(string),
|
||||||
}
|
Err(e) => Failure((Status::BadRequest, e)),
|
||||||
match String::from_utf8(buf) {
|
|
||||||
Ok(s) => Success(s),
|
|
||||||
Err(e) => Failure((Status::BadRequest, std::io::Error::new(std::io::ErrorKind::Other, e))),
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -196,6 +196,8 @@ impl<'f, T: FromForm<'f> + Send + 'f> FromData<'f> for Form<T> {
|
||||||
request: &Request<'_>,
|
request: &Request<'_>,
|
||||||
data: Data
|
data: Data
|
||||||
) -> TransformFuture<'f, Self::Owned, Self::Error> {
|
) -> TransformFuture<'f, Self::Owned, Self::Error> {
|
||||||
|
use std::cmp::min;
|
||||||
|
|
||||||
if !request.content_type().map_or(false, |ct| ct.is_form()) {
|
if !request.content_type().map_or(false, |ct| ct.is_form()) {
|
||||||
warn_!("Form data does not have form content type.");
|
warn_!("Form data does not have form content type.");
|
||||||
return Box::pin(futures::future::ready(Transform::Borrowed(Forward(data))));
|
return Box::pin(futures::future::ready(Transform::Borrowed(Forward(data))));
|
||||||
|
@ -204,15 +206,12 @@ impl<'f, T: FromForm<'f> + Send + 'f> FromData<'f> for Form<T> {
|
||||||
let limit = request.limits().forms;
|
let limit = request.limits().forms;
|
||||||
let mut stream = data.open().take(limit);
|
let mut stream = data.open().take(limit);
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let mut buf = Vec::new();
|
let mut form_string = String::with_capacity(min(4096, limit) as usize);
|
||||||
if let Err(e) = stream.read_to_end(&mut buf).await {
|
if let Err(e) = stream.read_to_string(&mut form_string).await {
|
||||||
return Transform::Borrowed(Failure((Status::InternalServerError, FormDataError::Io(e))));
|
return Transform::Borrowed(Failure((Status::InternalServerError, FormDataError::Io(e))));
|
||||||
}
|
}
|
||||||
|
|
||||||
Transform::Borrowed(match String::from_utf8(buf) {
|
Transform::Borrowed(Success(form_string))
|
||||||
Ok(s) => Success(s),
|
|
||||||
Err(e) => Failure((Status::BadRequest, FormDataError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))),
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue