From 2a694efe5dc227b722b51c1befc11080d161a303 Mon Sep 17 00:00:00 2001 From: Marc Schreiber Date: Sun, 19 May 2019 19:58:19 +0200 Subject: [PATCH 01/25] Upgrade to hyper 0.12: - Use hyper's MakeService implementation with futures API - Use tokio runtime to serve HTTP backend --- core/http/Cargo.toml | 4 +- core/http/src/content_type.rs | 6 +- core/http/src/hyper.rs | 92 ++++---- core/http/src/method.rs | 24 +- core/http/src/uri/uri.rs | 14 ++ core/lib/Cargo.toml | 2 + core/lib/src/catcher.rs | 1 + core/lib/src/data/data.rs | 127 ++--------- core/lib/src/data/data_stream.rs | 17 +- core/lib/src/data/net_stream.rs | 84 +++---- core/lib/src/error.rs | 4 +- core/lib/src/local/request.rs | 6 +- core/lib/src/logger.rs | 6 +- core/lib/src/request/request.rs | 70 +++--- core/lib/src/response/status.rs | 17 +- core/lib/src/rocket.rs | 367 +++++++++++++++++++------------ core/lib/src/router/mod.rs | 2 +- 17 files changed, 431 insertions(+), 412 deletions(-) diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index 9a2241b920..6e55053885 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -22,7 +22,9 @@ private-cookies = ["cookie/secure"] [dependencies] smallvec = "0.6" percent-encoding = "1" -hyper = { version = "0.10.13", default-features = false } +hyper = { version = "0.12.31", default-features = false, features = ["tokio"] } +http = "0.1.17" +mime = "0.3.13" time = "0.1" indexmap = "1.0" rustls = { version = "0.15", optional = true } diff --git a/core/http/src/content_type.rs b/core/http/src/content_type.rs index be8f881f3d..688b7da98c 100644 --- a/core/http/src/content_type.rs +++ b/core/http/src/content_type.rs @@ -6,7 +6,7 @@ use std::fmt; use crate::header::Header; use crate::media_type::{MediaType, Source}; use crate::ext::IntoCollection; -use crate::hyper::mime::Mime; +use mime::Mime; /// Representation of HTTP Content-Types. /// @@ -281,11 +281,11 @@ impl From for ContentType { #[inline] fn from(mime: Mime) -> ContentType { // soooo inefficient. - let params = mime.2.into_iter() + let params = mime.params().into_iter() .map(|(attr, value)| (attr.to_string(), value.to_string())) .collect::>(); - ContentType::with_params(mime.0.to_string(), mime.1.to_string(), params) + ContentType::with_params(mime.type_().to_string(), mime.subtype().to_string(), params) } } diff --git a/core/http/src/hyper.rs b/core/http/src/hyper.rs index b56e41166a..5b5ba964ef 100644 --- a/core/http/src/hyper.rs +++ b/core/http/src/hyper.rs @@ -4,74 +4,66 @@ //! These types will, with certainty, be removed with time, but they reside here //! while necessary. -#[doc(hidden)] pub use hyper::server::Request as Request; -#[doc(hidden)] pub use hyper::server::Response as Response; -#[doc(hidden)] pub use hyper::server::Server as Server; -#[doc(hidden)] pub use hyper::server::Handler as Handler; - -#[doc(hidden)] pub use hyper::net; - -#[doc(hidden)] pub use hyper::method::Method; -#[doc(hidden)] pub use hyper::status::StatusCode; +#[doc(hidden)] pub use hyper::{Body, Request, Response}; +#[doc(hidden)] pub use hyper::body::Payload as Payload; #[doc(hidden)] pub use hyper::error::Error; -#[doc(hidden)] pub use hyper::uri::RequestUri; -#[doc(hidden)] pub use hyper::http::h1; -#[doc(hidden)] pub use hyper::buffer; +#[doc(hidden)] pub use hyper::server::Server; +#[doc(hidden)] pub use hyper::service::{MakeService, Service}; -pub use hyper::mime; +#[doc(hidden)] pub use hyper::Chunk; +#[doc(hidden)] pub use http::header::HeaderName as HeaderName; +#[doc(hidden)] pub use http::header::HeaderValue as HeaderValue; +#[doc(hidden)] pub use http::method::Method; +#[doc(hidden)] pub use http::request::Parts; +#[doc(hidden)] pub use http::status::StatusCode; +#[doc(hidden)] pub use http::uri::Uri; /// Type alias to `hyper::Response<'a, hyper::net::Fresh>`. -#[doc(hidden)] pub type FreshResponse<'a> = self::Response<'a, self::net::Fresh>; +// TODO #[doc(hidden)] pub type FreshResponse<'a> = self::Response<'a, self::net::Fresh>; /// Reexported Hyper header types. pub mod header { use crate::Header; - use hyper::header::Header as HyperHeaderTrait; - macro_rules! import_hyper_items { ($($item:ident),*) => ($(pub use hyper::header::$item;)*) } macro_rules! import_hyper_headers { ($($name:ident),*) => ($( - impl std::convert::From for Header<'static> { - fn from(header: self::$name) -> Header<'static> { - Header::new($name::header_name(), header.to_string()) - } - } + pub use http::header::$name as $name; )*) } - import_hyper_items! { - Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, AcceptRanges, - AccessControlAllowCredentials, AccessControlAllowHeaders, - AccessControlAllowMethods, AccessControlExposeHeaders, - AccessControlMaxAge, AccessControlRequestHeaders, - AccessControlRequestMethod, Allow, Authorization, Basic, Bearer, - CacheControl, Connection, ContentDisposition, ContentEncoding, - ContentLanguage, ContentLength, ContentRange, ContentType, Date, ETag, - EntityTag, Expires, From, Headers, Host, HttpDate, IfModifiedSince, - IfUnmodifiedSince, LastModified, Location, Origin, Prefer, - PreferenceApplied, Protocol, Quality, QualityItem, Referer, - StrictTransportSecurity, TransferEncoding, Upgrade, UserAgent, - AccessControlAllowOrigin, ByteRangeSpec, CacheDirective, Charset, - ConnectionOption, ContentRangeSpec, DispositionParam, DispositionType, - Encoding, Expect, IfMatch, IfNoneMatch, IfRange, Pragma, Preference, - ProtocolName, Range, RangeUnit, ReferrerPolicy, Vary, Scheme, q, qitem - } - +// import_hyper_items! { +// Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, AcceptRanges, +// AccessControlAllowCredentials, AccessControlAllowHeaders, +// AccessControlAllowMethods, AccessControlExposeHeaders, +// AccessControlMaxAge, AccessControlRequestHeaders, +// AccessControlRequestMethod, Allow, Authorization, Basic, Bearer, +// CacheControl, Connection, ContentDisposition, ContentEncoding, +// ContentLanguage, ContentLength, ContentRange, ContentType, Date, ETag, +// EntityTag, Expires, From, Headers, Host, HttpDate, IfModifiedSince, +// IfUnmodifiedSince, LastModified, Location, Origin, Prefer, +// PreferenceApplied, Protocol, Quality, QualityItem, Referer, +// StrictTransportSecurity, TransferEncoding, Upgrade, UserAgent, +// AccessControlAllowOrigin, ByteRangeSpec, CacheDirective, Charset, +// ConnectionOption, ContentRangeSpec, DispositionParam, DispositionType, +// Encoding, Expect, IfMatch, IfNoneMatch, IfRange, Pragma, Preference, +// ProtocolName, Range, RangeUnit, ReferrerPolicy, Vary, Scheme, q, qitem +// } +// import_hyper_headers! { - Accept, AccessControlAllowCredentials, AccessControlAllowHeaders, - AccessControlAllowMethods, AccessControlAllowOrigin, - AccessControlExposeHeaders, AccessControlMaxAge, - AccessControlRequestHeaders, AccessControlRequestMethod, AcceptCharset, - AcceptEncoding, AcceptLanguage, AcceptRanges, Allow, CacheControl, - Connection, ContentDisposition, ContentEncoding, ContentLanguage, - ContentLength, ContentRange, Date, ETag, Expect, Expires, Host, IfMatch, - IfModifiedSince, IfNoneMatch, IfRange, IfUnmodifiedSince, LastModified, - Location, Origin, Pragma, Prefer, PreferenceApplied, Range, Referer, - ReferrerPolicy, StrictTransportSecurity, TransferEncoding, Upgrade, - UserAgent, Vary + ACCEPT, ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, + ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, + ACCESS_CONTROL_EXPOSE_HEADERS, ACCESS_CONTROL_MAX_AGE, + ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, ACCEPT_CHARSET, + ACCEPT_ENCODING, ACCEPT_LANGUAGE, ACCEPT_RANGES, ALLOW, CACHE_CONTROL, + CONNECTION, CONTENT_DISPOSITION, CONTENT_ENCODING, CONTENT_LANGUAGE, + CONTENT_LENGTH, CONTENT_RANGE, DATE, ETAG, EXPECT, EXPIRES, HOST, IF_MATCH, + IF_MODIFIED_SINCE, IF_NONE_MATCH, IF_RANGE, IF_UNMODIFIED_SINCE, LAST_MODIFIED, + LOCATION, ORIGIN, PRAGMA, RANGE, REFERER, + REFERRER_POLICY, STRICT_TRANSPORT_SECURITY, TRANSFER_ENCODING, UPGRADE, + USER_AGENT, VARY } } diff --git a/core/http/src/method.rs b/core/http/src/method.rs index ce83d67a29..1a4722f895 100644 --- a/core/http/src/method.rs +++ b/core/http/src/method.rs @@ -1,3 +1,5 @@ +extern crate http; + use std::fmt; use std::str::FromStr; @@ -24,18 +26,18 @@ pub enum Method { impl Method { /// WARNING: This is unstable! Do not use this method outside of Rocket! #[doc(hidden)] - pub fn from_hyp(method: &hyper::Method) -> Option { + pub fn from_hyp(method: &http::method::Method) -> Option { match *method { - hyper::Method::Get => Some(Get), - hyper::Method::Put => Some(Put), - hyper::Method::Post => Some(Post), - hyper::Method::Delete => Some(Delete), - hyper::Method::Options => Some(Options), - hyper::Method::Head => Some(Head), - hyper::Method::Trace => Some(Trace), - hyper::Method::Connect => Some(Connect), - hyper::Method::Patch => Some(Patch), - hyper::Method::Extension(_) => None, + http::method::Method::GET => Some(Get), + http::method::Method::PUT => Some(Put), + http::method::Method::POST => Some(Post), + http::method::Method::DELETE => Some(Delete), + http::method::Method::OPTIONS => Some(Options), + http::method::Method::HEAD => Some(Head), + http::method::Method::TRACE => Some(Trace), + http::method::Method::CONNECT => Some(Connect), + http::method::Method::PATCH => Some(Patch), + _ => None, } } diff --git a/core/http/src/uri/uri.rs b/core/http/src/uri/uri.rs index bb61177ef7..325dede806 100644 --- a/core/http/src/uri/uri.rs +++ b/core/http/src/uri/uri.rs @@ -94,6 +94,20 @@ impl<'a> Uri<'a> { crate::parse::uri::from_str(string) } +// pub fn from_hyp(uri: &'a hyper::Uri) -> Uri<'a> { +// match uri.is_absolute() { +// true => Uri::Absolute(Absolute::new( +// uri.scheme().unwrap(), +// match uri.host() { +// Some(host) => Some(Authority::new(None, Host::Raw(host), uri.port())), +// None => None +// }, +// None +// )), +// false => Uri::Asterisk +// } +// } + /// Returns the internal instance of `Origin` if `self` is a `Uri::Origin`. /// Otherwise, returns `None`. /// diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 072a5032c7..2a1b3deadf 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -24,8 +24,10 @@ tls = ["rocket_http/tls"] private-cookies = ["rocket_http/private-cookies"] [dependencies] +futures = "0.1" rocket_codegen = { version = "0.5.0-dev", path = "../codegen" } rocket_http = { version = "0.5.0-dev", path = "../http" } +tokio = "0.1.16" yansi = "0.5" log = "0.4" toml = "0.4.7" diff --git a/core/lib/src/catcher.rs b/core/lib/src/catcher.rs index 91c5b550a1..f4dcde52ce 100644 --- a/core/lib/src/catcher.rs +++ b/core/lib/src/catcher.rs @@ -59,6 +59,7 @@ use yansi::Color::*; /// /// A function decorated with `catch` must take exactly zero or one arguments. /// If the catcher takes an argument, it must be of type [`&Request`](Request). +#[derive(Clone)] pub struct Catcher { /// The HTTP status code to match against. pub code: u16, diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 83fcbfc2b6..f041415d4e 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -5,20 +5,12 @@ use std::time::Duration; #[cfg(feature = "tls")] use super::net_stream::HttpsStream; -use super::data_stream::{DataStream, kill_stream}; +use super::data_stream::{DataStream, /* TODO kill_stream */}; use super::net_stream::NetStream; use crate::ext::ReadExt; -use crate::http::hyper; -use crate::http::hyper::h1::HttpReader; -use crate::http::hyper::h1::HttpReader::*; -use crate::http::hyper::net::{HttpStream, NetworkStream}; - -pub type HyperBodyReader<'a, 'b> = - self::HttpReader<&'a mut hyper::buffer::BufReader<&'b mut dyn NetworkStream>>; - -// |---- from hyper ----| -pub type BodyReader = HttpReader>, NetStream>>; +use crate::http::hyper::{self, Payload}; +use futures::{Async, Future}; /// The number of bytes to read into the "peek" buffer. const PEEK_BYTES: usize = 512; @@ -56,9 +48,7 @@ const PEEK_BYTES: usize = 512; /// body data. This enables partially or fully reading from a `Data` object /// without consuming the `Data` object. pub struct Data { - buffer: Vec, - is_complete: bool, - stream: BodyReader, + body: Vec, } impl Data { @@ -79,62 +69,11 @@ impl Data { /// } /// ``` pub fn open(mut self) -> DataStream { - let buffer = std::mem::replace(&mut self.buffer, vec![]); - let empty_stream = Cursor::new(vec![]).chain(NetStream::Empty); - // FIXME: Insert a `BufReader` in front of the `NetStream` with capacity // 4096. We need the new `Chain` methods to get the inner reader to // actually do this, however. - let empty_http_stream = HttpReader::SizedReader(empty_stream, 0); - let stream = std::mem::replace(&mut self.stream, empty_http_stream); - DataStream(Cursor::new(buffer).chain(stream)) - } - - // FIXME: This is absolutely terrible (downcasting!), thanks to Hyper. - crate fn from_hyp(mut body: HyperBodyReader<'_, '_>) -> Result { - #[inline(always)] - #[cfg(feature = "tls")] - fn concrete_stream(stream: &mut dyn NetworkStream) -> Option { - stream.downcast_ref::() - .map(|s| NetStream::Https(s.clone())) - .or_else(|| { - stream.downcast_ref::() - .map(|s| NetStream::Http(s.clone())) - }) - } - - #[inline(always)] - #[cfg(not(feature = "tls"))] - fn concrete_stream(stream: &mut dyn NetworkStream) -> Option { - stream.downcast_ref::() - .map(|s| NetStream::Http(s.clone())) - } - - // Retrieve the underlying Http(s)Stream from Hyper. - let net_stream = match concrete_stream(*body.get_mut().get_mut()) { - Some(net_stream) => net_stream, - None => return Err("Stream is not an HTTP(s) stream!") - }; - - // Set the read timeout to 5 seconds. - let _ = net_stream.set_read_timeout(Some(Duration::from_secs(5))); - - // Steal the internal, undecoded data buffer from Hyper. - let (mut hyper_buf, pos, cap) = body.get_mut().take_buf(); - hyper_buf.truncate(cap); // slow, but safe - let mut cursor = Cursor::new(hyper_buf); - cursor.set_position(pos as u64); - - // Create an HTTP reader from the buffer + stream. - let inner_data = cursor.chain(net_stream); - let http_stream = match body { - SizedReader(_, n) => SizedReader(inner_data, n), - EofReader(_) => EofReader(inner_data), - EmptyReader(_) => EmptyReader(inner_data), - ChunkedReader(_, n) => ChunkedReader(inner_data, n) - }; - - Ok(Data::new(http_stream)) + let stream = ::std::mem::replace(&mut self.body, vec![]); + DataStream(Cursor::new(stream)) } /// Retrieve the `peek` buffer. @@ -155,10 +94,10 @@ impl Data { /// ``` #[inline(always)] pub fn peek(&self) -> &[u8] { - if self.buffer.len() > PEEK_BYTES { - &self.buffer[..PEEK_BYTES] + if self.body.len() > PEEK_BYTES { + &self.body[..PEEK_BYTES] } else { - &self.buffer + &self.body } } @@ -179,7 +118,8 @@ impl Data { /// ``` #[inline(always)] pub fn peek_complete(&self) -> bool { - self.is_complete + // TODO self.is_complete + true } /// A helper method to write the body of the request to any `Write` type. @@ -230,49 +170,8 @@ impl Data { // bytes `vec[pos..cap]` are buffered and unread. The remainder of the data // bytes can be read from `stream`. #[inline(always)] - crate fn new(mut stream: BodyReader) -> Data { - trace_!("Data::new({:?})", stream); - let mut peek_buf: Vec = vec![0; PEEK_BYTES]; - - // Fill the buffer with as many bytes as possible. If we read less than - // that buffer's length, we know we reached the EOF. Otherwise, it's - // unclear, so we just say we didn't reach EOF. - let eof = match stream.read_max(&mut peek_buf[..]) { - Ok(n) => { - trace_!("Filled peek buf with {} bytes.", n); - // We can use `set_len` here instead of `truncate`, but we'll - // take the performance hit to avoid `unsafe`. All of this code - // should go away when we migrate away from hyper 0.10.x. - peek_buf.truncate(n); - n < PEEK_BYTES - } - Err(e) => { - error_!("Failed to read into peek buffer: {:?}.", e); - // Likewise here as above. - peek_buf.truncate(0); - false - }, - }; - - trace_!("Peek bytes: {}/{} bytes.", peek_buf.len(), PEEK_BYTES); - Data { buffer: peek_buf, stream, is_complete: eof } - } - - /// This creates a `data` object from a local data source `data`. - #[inline] - crate fn local(data: Vec) -> Data { - let empty_stream = Cursor::new(vec![]).chain(NetStream::Empty); - - Data { - buffer: data, - stream: HttpReader::SizedReader(empty_stream, 0), - is_complete: true, - } + crate fn new(body: Vec) -> Data { + Data { body } } -} -impl Drop for Data { - fn drop(&mut self) { - kill_stream(&mut self.stream); - } } diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index 70c41b5ad9..fe237edc9d 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -1,12 +1,7 @@ -use std::io::{self, Read, Cursor, Chain}; +use std::io::{self, Chain, Cursor, Read, Write}; use std::net::Shutdown; -use super::data::BodyReader; -use crate::http::hyper::net::NetworkStream; -use crate::http::hyper::h1::HttpReader; - -// |-- peek buf --| -pub type InnerStream = Chain>, BodyReader>; +pub type InnerStream = Cursor>; /// Raw data stream of a request body. /// @@ -26,9 +21,9 @@ impl Read for DataStream { } } -pub fn kill_stream(stream: &mut BodyReader) { +/* pub fn kill_stream(stream: &mut BodyReader) { // Only do the expensive reading if we're not sure we're done. - use self::HttpReader::*; + // TODO use self::HttpReader::*; match *stream { SizedReader(_, n) | ChunkedReader(_, Some(n)) if n > 0 => { /* continue */ }, _ => return @@ -46,10 +41,10 @@ pub fn kill_stream(stream: &mut BodyReader) { } Ok(n) => debug!("flushed {} unread bytes", n) } -} +}*/ impl Drop for DataStream { fn drop(&mut self) { - kill_stream(&mut self.0.get_mut().1); + // TODO kill_stream(&mut self.0.get_mut().1); } } diff --git a/core/lib/src/data/net_stream.rs b/core/lib/src/data/net_stream.rs index b9a8099cf6..09e0762bf7 100644 --- a/core/lib/src/data/net_stream.rs +++ b/core/lib/src/data/net_stream.rs @@ -3,7 +3,7 @@ use std::net::{SocketAddr, Shutdown}; use std::time::Duration; #[cfg(feature = "tls")] use crate::http::tls::{WrappedStream, ServerSession}; -use crate::http::hyper::net::{HttpStream, NetworkStream}; +// TODO use http::hyper::net::{HttpStream, NetworkStream}; use self::NetStream::*; @@ -13,7 +13,7 @@ use self::NetStream::*; // This really shouldn't be necessary, but, you know, Hyper. #[derive(Clone)] pub enum NetStream { - Http(HttpStream), + Http/* TODO (HttpStream) */, #[cfg(feature = "tls")] Https(HttpsStream), Empty, @@ -24,7 +24,7 @@ impl io::Read for NetStream { fn read(&mut self, buf: &mut [u8]) -> io::Result { trace_!("NetStream::read()"); let res = match *self { - Http(ref mut stream) => stream.read(buf), + Http/*(ref mut stream)*/ => Ok(0) /* TODO stream.read(buf)*/, #[cfg(feature = "tls")] Https(ref mut stream) => stream.read(buf), Empty => Ok(0), }; @@ -39,7 +39,7 @@ impl io::Write for NetStream { fn write(&mut self, buf: &[u8]) -> io::Result { trace_!("NetStream::write()"); match *self { - Http(ref mut stream) => stream.write(buf), + Http/* TODO (ref mut stream) => stream.write(buf)*/ => Ok(0), #[cfg(feature = "tls")] Https(ref mut stream) => stream.write(buf), Empty => Ok(0), } @@ -48,47 +48,47 @@ impl io::Write for NetStream { #[inline(always)] fn flush(&mut self) -> io::Result<()> { match *self { - Http(ref mut stream) => stream.flush(), + Http/* TODO (ref mut stream) => stream.flush()*/ => Ok(()), #[cfg(feature = "tls")] Https(ref mut stream) => stream.flush(), Empty => Ok(()), } } } -impl NetworkStream for NetStream { - #[inline(always)] - fn peer_addr(&mut self) -> io::Result { - match *self { - Http(ref mut stream) => stream.peer_addr(), - #[cfg(feature = "tls")] Https(ref mut stream) => stream.peer_addr(), - Empty => Err(io::Error::from(io::ErrorKind::AddrNotAvailable)), - } - } - - #[inline(always)] - fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - match *self { - Http(ref stream) => stream.set_read_timeout(dur), - #[cfg(feature = "tls")] Https(ref stream) => stream.set_read_timeout(dur), - Empty => Ok(()), - } - } - - #[inline(always)] - fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - match *self { - Http(ref stream) => stream.set_write_timeout(dur), - #[cfg(feature = "tls")] Https(ref stream) => stream.set_write_timeout(dur), - Empty => Ok(()), - } - } - - #[inline(always)] - fn close(&mut self, how: Shutdown) -> io::Result<()> { - match *self { - Http(ref mut stream) => stream.close(how), - #[cfg(feature = "tls")] Https(ref mut stream) => stream.close(how), - Empty => Ok(()), - } - } -} +//impl NetworkStream for NetStream { +// #[inline(always)] +// fn peer_addr(&mut self) -> io::Result { +// match *self { +// Http/* TODO (ref mut stream) => stream.peer_addr()*/ => Err(io::Error::from(io::ErrorKind::AddrNotAvailable)), +// #[cfg(feature = "tls")] Https(ref mut stream) => stream.peer_addr(), +// Empty => Err(io::Error::from(io::ErrorKind::AddrNotAvailable)), +// } +// } +// +// #[inline(always)] +// fn set_read_timeout(&self, dur: Option) -> io::Result<()> { +// match *self { +// Http/* TODO (ref stream) => stream.set_read_timeout(dur)*/ => Ok(()), +// #[cfg(feature = "tls")] Https(ref stream) => stream.set_read_timeout(dur), +// Empty => Ok(()), +// } +// } +// +// #[inline(always)] +// fn set_write_timeout(&self, dur: Option) -> io::Result<()> { +// match *self { +// Http/* TODO (ref stream) => stream.set_write_timeout(dur)*/ => Ok(()), +// #[cfg(feature = "tls")] Https(ref stream) => stream.set_write_timeout(dur), +// Empty => Ok(()), +// } +// } +// +// #[inline(always)] +// fn close(&mut self, how: Shutdown) -> io::Result<()> { +// match *self { +// Http/* TODO (ref mut stream) => stream.close(how)*/ => Ok(()), +// #[cfg(feature = "tls")] Https(ref mut stream) => stream.close(how), +// Empty => Ok(()), +// } +// } +//} diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index 1993794e8e..71cf0ddd30 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -19,7 +19,7 @@ use crate::router::Route; #[derive(Debug)] pub enum LaunchErrorKind { /// Binding to the provided address/port failed. - Bind(hyper::Error), + Bind(std::io::Error), /// An I/O error occurred during launch. Io(io::Error), /// Route collisions were detected. @@ -124,7 +124,7 @@ impl From for LaunchError { #[inline] fn from(error: hyper::Error) -> LaunchError { match error { - hyper::Error::Io(e) => LaunchError::new(LaunchErrorKind::Io(e)), + // TODO hyper::Error::Io(e) => LaunchError::new(LaunchErrorKind::Io(e)), e => LaunchError::new(LaunchErrorKind::Unknown(Box::new(e))) } } diff --git a/core/lib/src/local/request.rs b/core/lib/src/local/request.rs index 31b8b638b3..5091ccc476 100644 --- a/core/lib/src/local/request.rs +++ b/core/lib/src/local/request.rs @@ -107,7 +107,9 @@ impl<'c> LocalRequest<'c> { uri: Cow<'c, str> ) -> LocalRequest<'c> { // We set a dummy string for now and check the user's URI on dispatch. - let request = Request::new(client.rocket(), method, Origin::dummy()); + let config = &client.rocket().config; + let state = &client.rocket().state; + let request = Request::new(config, state, method, Origin::dummy()); // Set up any cookies we know about. if let Some(ref jar) = client.cookies { @@ -408,7 +410,7 @@ impl<'c> LocalRequest<'c> { } // Actually dispatch the request. - let response = client.rocket().dispatch(request, Data::local(data)); + let response = client.rocket().dispatch(request, Data::new(data)); // If the client is tracking cookies, updates the internal cookie jar // with the changes reflected by `response`. diff --git a/core/lib/src/logger.rs b/core/lib/src/logger.rs index 81e62750e5..3dcf4faff1 100644 --- a/core/lib/src/logger.rs +++ b/core/lib/src/logger.rs @@ -158,16 +158,16 @@ crate fn try_init(level: LoggingLevel, verbose: bool) -> bool { } push_max_level(level); - if let Err(e) = log::set_boxed_logger(Box::new(RocketLogger(level))) { +/* if let Err(e) = log::set_boxed_logger(Box::new(RocketLogger(level))) { if verbose { eprintln!("Logger failed to initialize: {}", e); } pop_max_level(); return false; - } + }*/ - true + false } use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering}; diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index 6a5b06aa82..ec90c3f685 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -3,6 +3,8 @@ use std::cell::{Cell, RefCell}; use std::net::{IpAddr, SocketAddr}; use std::fmt; use std::str; +use std::str::FromStr; +use std::sync::Arc; use yansi::Paint; use state::{Container, Storage}; @@ -13,7 +15,7 @@ use crate::request::{FromFormValue, FormItems, FormItem}; use crate::rocket::Rocket; use crate::router::Route; use crate::config::{Config, Limits}; -use crate::http::{hyper, uri::{Origin, Segments}}; +use crate::http::{hyper, uri::{Origin, Segments, Uri}}; use crate::http::{Method, Header, HeaderMap, Cookies}; use crate::http::{RawStr, ContentType, Accept, MediaType}; use crate::http::private::{Indexed, SmallVec, CookieJar}; @@ -59,7 +61,8 @@ impl<'r> Request<'r> { /// Create a new `Request` with the given `method` and `uri`. #[inline(always)] crate fn new<'s: 'r>( - rocket: &'r Rocket, + config: &'r Config, + managed: &'r Container, method: Method, uri: Origin<'s> ) -> Request<'r> { @@ -71,8 +74,8 @@ impl<'r> Request<'r> { state: RequestState { path_segments: SmallVec::new(), query_items: None, - config: &rocket.config, - managed: &rocket.state, + config, + managed, route: Cell::new(None), cookies: RefCell::new(CookieJar::new()), accept: Storage::new(), @@ -699,7 +702,7 @@ impl<'r> Request<'r> { pub fn example)>(method: Method, uri: &str, f: F) { let rocket = Rocket::custom(Config::development()); let uri = Origin::parse(uri).expect("invalid URI in example"); - let mut request = Request::new(&rocket, method, uri); + let mut request = Request::new(&rocket.config, &rocket.state, method, uri); f(&mut request); } @@ -782,36 +785,42 @@ impl<'r> Request<'r> { /// Convert from Hyper types into a Rocket Request. crate fn from_hyp( - rocket: &'r Rocket, - h_method: hyper::Method, - h_headers: hyper::header::Headers, - h_uri: hyper::RequestUri, - h_addr: SocketAddr, + config: &'r Config, + managed: &'r Container, + request_parts: &hyper::Parts, ) -> Result, String> { - // Get a copy of the URI for later use. - let uri = match h_uri { - hyper::RequestUri::AbsolutePath(s) => s, - _ => return Err(format!("Bad URI: {}", h_uri)), - }; + + let h_uri = &request_parts.uri; + let h_headers = &request_parts.headers; + let h_version = &request_parts.version; + let h_method = &request_parts.method;; + +// if !h_uri.is_absolute() { +// return Err(format!("Bad URI: {}", h_uri)); +// }; // Ensure that the method is known. TODO: Allow made-up methods? - let method = match Method::from_hyp(&h_method) { + let method = match Method::from_hyp(h_method) { Some(method) => method, - None => return Err(format!("Invalid method: {}", h_method)) + None => return Err(format!("Unknown method: {}", h_method)) }; // We need to re-parse the URI since we don't trust Hyper... :( - let uri = Origin::parse_owned(uri).map_err(|e| e.to_string())?; + let uri = Origin::parse_owned(format!("{}", h_uri)).map_err(|e| e.to_string())?; // Construct the request object. - let mut request = Request::new(rocket, method, uri); - request.set_remote(h_addr); + let mut request = Request::new(config, managed, method, uri); +// request.set_remote(match hyp_req.remote_addr() { +// Some(remote) => remote, +// None => return Err(String::from("Missing remote address")) +// }); // Set the request cookies, if they exist. - if let Some(cookie_headers) = h_headers.get_raw("Cookie") { + let cookie_headers = h_headers.get_all("Cookie").iter(); + // TODO if cookie_headers.peek().is_some() { let mut cookie_jar = CookieJar::new(); for header in cookie_headers { - let raw_str = match std::str::from_utf8(header) { + let raw_str = match ::std::str::from_utf8(header.as_bytes()) { Ok(string) => string, Err(_) => continue }; @@ -824,18 +833,19 @@ impl<'r> Request<'r> { } request.state.cookies = RefCell::new(cookie_jar); - } + // TODO } // Set the rest of the headers. - for hyp in h_headers.iter() { - if let Some(header_values) = h_headers.get_raw(hyp.name()) { - for value in header_values { + for (name, value) in h_headers.iter() { + + // TODO if let Some(header_values) = h_headers.get_all(hyp.name()) { + // This is not totally correct since values needn't be UTF8. - let value_str = String::from_utf8_lossy(value).into_owned(); - let header = Header::new(hyp.name().to_string(), value_str); + let value_str = String::from_utf8_lossy(value.as_bytes()).into_owned(); + let header = Header::new(name.to_string(), value_str); request.add_header(header); - } - } + + // TODO } } Ok(request) diff --git a/core/lib/src/response/status.rs b/core/lib/src/response/status.rs index 2753e7750d..a8d24403ad 100644 --- a/core/lib/src/response/status.rs +++ b/core/lib/src/response/status.rs @@ -13,7 +13,7 @@ use std::collections::hash_map::DefaultHasher; use crate::request::Request; use crate::response::{Responder, Response}; use crate::http::hyper::header; -use crate::http::Status; +use crate::http::{Header, Status}; /// Sets the status of the response to 201 (Created). /// @@ -47,7 +47,10 @@ impl<'r, R: Responder<'r>> Responder<'r> for Created { build.merge(responder.respond_to(req)?); } - build.status(Status::Created).header(header::Location(self.0)).ok() + build.status(Status::Created).header(Header::new( + header::LOCATION.as_str(), + self.0 + )).ok() } } @@ -64,10 +67,16 @@ impl<'r, R: Responder<'r> + Hash> Responder<'r> for Created { let hash = hasher.finish().to_string(); build.merge(responder.respond_to(req)?); - build.header(header::ETag(header::EntityTag::strong(hash))); + build.header(Header::new( + header::ETAG.as_str(), + hash, // TODO header::EntityTag::strong(hash) + )); } - build.status(Status::Created).header(header::Location(self.0)).ok() + build.status(Status::Created).header(Header::new( + header::LOCATION.as_str(), + self.0 + )).ok() } } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index c8fe76ef77..d5720e6e75 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -1,14 +1,22 @@ use std::collections::HashMap; -use std::str::from_utf8; +use std::convert::From; +use std::str::{from_utf8, FromStr}; use std::cmp::min; use std::io::{self, Write}; use std::time::Duration; use std::mem; +use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; +use std::sync::Arc; + +use futures::{Future, Stream}; +use futures::future::{self, FutureResult}; use yansi::Paint; use state::Container; +use tokio::net::TcpListener; +use tokio::prelude::{Future as _, Stream as _}; -#[cfg(feature = "tls")] use crate::http::tls::TlsServer; +#[cfg(feature = "tls")] use crate::http::tls::TlsAcceptor; use crate::{logger, handler}; use crate::ext::ReadExt; @@ -37,23 +45,59 @@ pub struct Rocket { fairings: Fairings, } +struct RocketArcs { + config: Arc, + router: Arc, + default_catchers: Arc>, + catchers: Arc>, + state: Arc, + fairings: Arc, +} + +impl hyper::MakeService for RocketArcs { + type ReqBody = hyper::Body; + type ResBody = hyper::Body; + type Error = hyper::Error; + type Service = RocketHyperService; + type Future = FutureResult; + type MakeError = Self::Error; + + fn make_service(&mut self, _: Ctx) -> Self::Future { + future::ok(RocketHyperService::new(self)) + } +} + +#[derive(Clone)] +pub struct RocketHyperService { + config: Arc, + router: Arc, + default_catchers: Arc>, + catchers: Arc>, + state: Arc, + fairings: Arc, +} + #[doc(hidden)] -impl hyper::Handler for Rocket { +impl hyper::Service for RocketHyperService { + type ReqBody = hyper::Body; + type ResBody = hyper::Body; + type Error = hyper::Error; + //type Future = FutureResult, Self::Error>; + type Future = Box, Error = Self::Error> + Send>; + // This function tries to hide all of the Hyper-ness from Rocket. It // essentially converts Hyper types into Rocket types, then calls the // `dispatch` function, which knows nothing about Hyper. Because responding // depends on the `HyperResponse` type, this function does the actual // response processing. - fn handle<'h, 'k>( - &self, - hyp_req: hyper::Request<'h, 'k>, - res: hyper::FreshResponse<'h>, - ) { - // Get all of the information from Hyper. - let (h_addr, h_method, h_headers, h_uri, _, h_body) = hyp_req.deconstruct(); + fn call<'h>( + &mut self, + hyp_req: hyper::Request, + ) -> Self::Future { + let (parts, body) = hyp_req.into_parts(); // Convert the Hyper request into a Rocket request. - let req_res = Request::from_hyp(self, h_method, h_headers, h_uri, h_addr); + let req_res = Request::from_hyp(&self.config, &self.state, &parts); let mut req = match req_res { Ok(req) => req, Err(e) => { @@ -62,111 +106,42 @@ impl hyper::Handler for Rocket { // fabricate one. This is weird. We should let the user know // that we failed to parse a request (by invoking some special // handler) instead of doing this. - let dummy = Request::new(self, Method::Get, Origin::dummy()); + let dummy = Request::new(&self.config, &self.state, Method::Get, Origin::dummy()); let r = self.handle_error(Status::BadRequest, &dummy); - return self.issue_response(r, res); + return Box::new(future::ok(hyper::Response::from(r))); } }; - // Retrieve the data from the hyper body. - let data = match Data::from_hyp(h_body) { - Ok(data) => data, - Err(reason) => { - error_!("Bad data in request: {}", reason); - let r = self.handle_error(Status::InternalServerError, &req); - return self.issue_response(r, res); - } - }; + let this = self.clone(); - // Dispatch the request to get a response, then write that response out. - let response = self.dispatch(&mut req, data); - self.issue_response(response, res) - } -} + let response = body.concat2() + .map(move |chunk| { + let body = chunk.iter().rev().cloned().collect::>(); + let data = Data::new(body); -// This macro is a terrible hack to get around Hyper's Server type. What we -// want is to use almost exactly the same launch code when we're serving over -// HTTPS as over HTTP. But Hyper forces two different types, so we can't use the -// same code, at least not trivially. These macros get around that by passing in -// the same code as a continuation in `$continue`. This wouldn't work as a -// regular function taking in a closure because the types of the inputs to the -// closure would be different depending on whether TLS was enabled or not. -#[cfg(not(feature = "tls"))] -macro_rules! serve { - ($rocket:expr, $addr:expr, |$server:ident, $proto:ident| $continue:expr) => ({ - let ($proto, $server) = ("http://", hyper::Server::http($addr)); - $continue - }) -} + // TODO: Due to life time constraints the clone of the service has been made. + // TODO: It should not be necessary but it is required to find a better solution + let mut req = Request::from_hyp(&this.config, &this.state, &parts).unwrap(); + // Dispatch the request to get a response, then write that response out. + let response = this.dispatch(&mut req, data); + hyper::Response::from(response) + }); -#[cfg(feature = "tls")] -macro_rules! serve { - ($rocket:expr, $addr:expr, |$server:ident, $proto:ident| $continue:expr) => ({ - if let Some(tls) = $rocket.config.tls.clone() { - let tls = TlsServer::new(tls.certs, tls.key); - let ($proto, $server) = ("https://", hyper::Server::https($addr, tls)); - $continue - } else { - let ($proto, $server) = ("http://", hyper::Server::http($addr)); - $continue - } - }) + Box::new(response) + } } -impl Rocket { - #[inline] - fn issue_response(&self, response: Response<'_>, hyp_res: hyper::FreshResponse<'_>) { - match self.write_response(response, hyp_res) { - Ok(_) => info_!("{}", Paint::green("Response succeeded.")), - Err(e) => error_!("Failed to write response: {:?}.", e), - } - } +impl RocketHyperService { #[inline] - fn write_response( - &self, - mut response: Response<'_>, - mut hyp_res: hyper::FreshResponse<'_>, - ) -> io::Result<()> { - *hyp_res.status_mut() = hyper::StatusCode::from_u16(response.status().code); - - for header in response.headers().iter() { - // FIXME: Using hyper here requires two allocations. - let name = header.name.into_string(); - let value = Vec::from(header.value.as_bytes()); - hyp_res.headers_mut().append_raw(name, value); - } - - match response.body() { - None => { - hyp_res.headers_mut().set(header::ContentLength(0)); - hyp_res.start()?.end() - } - Some(Body::Sized(body, size)) => { - hyp_res.headers_mut().set(header::ContentLength(size)); - let mut stream = hyp_res.start()?; - io::copy(body, &mut stream)?; - stream.end() - } - Some(Body::Chunked(mut body, chunk_size)) => { - // This _might_ happen on a 32-bit machine! - if chunk_size > (usize::max_value() as u64) { - let msg = "chunk size exceeds limits of usize type"; - return Err(io::Error::new(io::ErrorKind::Other, msg)); - } - - // The buffer stores the current chunk being written out. - let mut buffer = vec![0; chunk_size as usize]; - let mut stream = hyp_res.start()?; - loop { - match body.read_max(&mut buffer)? { - 0 => break, - n => stream.write_all(&buffer[..n])?, - } - } - - stream.end() - } + fn new(rocket: &RocketArcs) -> RocketHyperService { + RocketHyperService { + config: rocket.config.clone(), + router: rocket.router.clone(), + default_catchers: rocket.default_catchers.clone(), + catchers: rocket.catchers.clone(), + state: rocket.state.clone(), + fairings: rocket.fairings.clone(), } } @@ -331,6 +306,26 @@ impl Rocket { default.handle(req).expect("Default 500 response.") }) } +} + +impl Rocket { + + #[inline] + crate fn dispatch<'s, 'r>( + &'s self, + request: &'r mut Request<'s>, + data: Data + ) -> Response<'r> { + unimplemented!("TODO") + } + + crate fn handle_error<'r>( + &self, + status: Status, + req: &'r Request + ) -> Response<'r> { + unimplemented!("TODO") + } /// Create a new `Rocket` application using the configuration information in /// `Rocket.toml`. If the file does not exist or if there is an I/O error @@ -510,6 +505,7 @@ impl Rocket { panic!("Invalid mount point."); } + let mut router = self.router.clone(); for mut route in routes.into() { let path = route.uri.clone(); if let Err(e) = route.set_uri(base_uri.clone(), path) { @@ -518,9 +514,11 @@ impl Rocket { } info_!("{}", route); - self.router.add(route); + router.add(route); } + self.router = router; + self } @@ -554,6 +552,9 @@ impl Rocket { #[inline] pub fn register(mut self, catchers: Vec) -> Self { info!("{}{}", Paint::masked("👾 "), Paint::magenta("Catchers:")); + + let mut current_catchers = self.catchers.clone(); + for c in catchers { if self.catchers.get(&c.code).map_or(false, |e| !e.is_default) { info_!("{} {}", c, Paint::yellow("(warning: duplicate catcher!)")); @@ -561,9 +562,11 @@ impl Rocket { info_!("{}", c); } - self.catchers.insert(c.code, c); + current_catchers.insert(c.code, c); } + self.catchers = current_catchers; + self } @@ -687,46 +690,90 @@ impl Rocket { self.fairings.pretty_print_counts(); - let full_addr = format!("{}:{}", self.config.address, self.config.port); - serve!(self, &full_addr, |server, proto| { - let mut server = match server { - Ok(server) => server, - Err(e) => return LaunchError::new(LaunchErrorKind::Bind(e)), - }; + // TODO.async What meaning should config.workers have now? + // Initialize the tokio runtime + let mut runtime = tokio::runtime::Builder::new() + .core_threads(self.config.workers as usize) + .build() + .expect("Cannot build runtime!"); + + let threads = self.config.workers as usize; + + let full_addr = format!("{}:{}", self.config.address, self.config.port) + .to_socket_addrs() + .expect("A valid socket address") + .next() + .unwrap(); - // Determine the address and port we actually binded to. - match server.local_addr() { - Ok(server_addr) => self.config.port = server_addr.port(), - Err(e) => return LaunchError::from(e), + let listener = match TcpListener::bind(&full_addr) { + Ok(listener) => listener, + Err(e) => return LaunchError::new(LaunchErrorKind::Bind(e)), + }; + + // Determine the address and port we actually binded to. + match listener.local_addr() { + Ok(server_addr) => /* TODO self.config.port = */ server_addr.port(), + Err(e) => return LaunchError::from(e), + }; + + let proto; + let incoming; + + #[cfg(feature = "tls")] + { + // TODO.async: Can/should we make the clone unnecessary (by reference, or by moving out?) + if let Some(tls) = self.config.tls.clone() { + proto = "https://"; + let mut config = tls::rustls::ServerConfig::new(tls::rustls::NoClientAuth::new()); + config.set_single_cert(tls.certs, tls.key).expect("invalid key or certificate"); + + // TODO.async: I once observed an unhandled AlertReceived(UnknownCA) but + // have no idea what happened and cannot reproduce. + let config = TlsAcceptor::from(Arc::new(config)); + + incoming = Box::new(listener.incoming().and_then(move |stream| { + config.accept(stream) + .map(|stream| Box::new(stream)) + })); + } + else { + proto = "http://"; + incoming = Box::new(listener.incoming().map(|stream| Box::new(stream))); } + } - // Set the keep-alive. - let timeout = self.config.keep_alive.map(|s| Duration::from_secs(s as u64)); - server.keep_alive(timeout); + #[cfg(not(feature = "tls"))] + { + proto = "http://"; + incoming = Box::new(listener.incoming().map(|stream| Box::new(stream))); + } - // Freeze managed state for synchronization-free accesses later. - self.state.freeze(); + // Freeze managed state for synchronization-free accesses later. + self.state.freeze(); - // Run the launch fairings. - self.fairings.handle_launch(&self); + // Run the launch fairings. + self.fairings.handle_launch(&self); - let full_addr = format!("{}:{}", self.config.address, self.config.port); - launch_info!("{}{} {}{}", - Paint::masked("🚀 "), - Paint::default("Rocket has launched from").bold(), - Paint::default(proto).bold().underline(), - Paint::default(&full_addr).bold().underline()); + launch_info!("{}{} {}{}", + Paint::masked("🚀 "), + Paint::default("Rocket has launched from").bold(), + Paint::default(proto).bold().underline(), + Paint::default(&full_addr).bold().underline()); - // Restore the log level back to what it originally was. - logger::pop_max_level(); + // Restore the log level back to what it originally was. + logger::pop_max_level(); - let threads = self.config.workers as usize; - if let Err(e) = server.handle_threads(self, threads) { - return LaunchError::from(e); - } + let arcs = RocketArcs::from(self); - unreachable!("the call to `handle_threads` should block on success") - }) + // NB: executor must be passed manually here, see hyperium/hyper#1537 + let server = hyper::Server::builder(incoming) + .executor(runtime.executor()) + .serve(arcs); + + // TODO.async: Use with_graceful_shutdown, and let launch() return a Result<(), Error> + runtime.block_on(server).expect("TODO.async handle error"); + + unreachable!("the call to `handle_threads` should block on success") } /// Returns an iterator over all of the routes mounted on this instance of @@ -811,3 +858,47 @@ impl Rocket { &self.config } } + +impl From for RocketArcs { + fn from(mut rocket: Rocket) -> Self { + RocketArcs { + config: Arc::new(rocket.config), + router: Arc::new(rocket.router), + default_catchers: Arc::new(rocket.default_catchers), + catchers: Arc::new(rocket.catchers), + state: Arc::new(rocket.state), + fairings: Arc::new(rocket.fairings), + } + } +} + +// TODO: consider try_from here? +impl<'a> From> for hyper::Response { + fn from(mut response: Response) -> Self { + + let mut builder = hyper::Response::builder(); + builder.status(hyper::StatusCode::from_u16(response.status().code).expect("")); + + for header in response.headers().iter() { + // FIXME: Using hyper here requires two allocations. + let name = hyper::HeaderName::from_str(&header.name.into_string()).unwrap(); + let value = hyper::HeaderValue::from_bytes(header.value.as_bytes()).unwrap(); + builder.header(name, value); + } + + match response.body() { + None => { + builder.body(hyper::Body::empty()) + }, + Some(Body::Sized(body, size)) => { + let mut buffer = Vec::with_capacity(size as usize); + body.read_to_end(&mut buffer); + builder.header(header::CONTENT_LENGTH, hyper::HeaderValue::from(size)); + builder.body(hyper::Body::from(buffer)) + }, + Some(Body::Chunked(mut body, chunk_size)) => { + unimplemented!() + } + }.unwrap() + } +} \ No newline at end of file diff --git a/core/lib/src/router/mod.rs b/core/lib/src/router/mod.rs index 7e666fd5d2..771f2399d0 100644 --- a/core/lib/src/router/mod.rs +++ b/core/lib/src/router/mod.rs @@ -16,7 +16,7 @@ crate fn dummy_handler<'r>(r: &'r crate::Request<'_>, _: crate::Data) -> crate:: crate::Outcome::from(r, ()) } -#[derive(Default)] +#[derive(Default, Clone)] pub struct Router { routes: HashMap>, } From f478221dba52c55e7ceec77822faf72ab0057a46 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 30 Jun 2019 09:45:17 -0700 Subject: [PATCH 02/25] Clean up, preparing to merge in other changes. --- core/http/src/hyper.rs | 54 ++++++---------------- core/http/src/method.rs | 4 +- core/lib/src/catcher.rs | 1 - core/lib/src/request/request.rs | 6 +-- core/lib/src/rocket.rs | 82 +++++++++------------------------ 5 files changed, 39 insertions(+), 108 deletions(-) diff --git a/core/http/src/hyper.rs b/core/http/src/hyper.rs index 5b5ba964ef..788c55e50e 100644 --- a/core/http/src/hyper.rs +++ b/core/http/src/hyper.rs @@ -18,52 +18,28 @@ #[doc(hidden)] pub use http::status::StatusCode; #[doc(hidden)] pub use http::uri::Uri; -/// Type alias to `hyper::Response<'a, hyper::net::Fresh>`. -// TODO #[doc(hidden)] pub type FreshResponse<'a> = self::Response<'a, self::net::Fresh>; - -/// Reexported Hyper header types. +/// Reexported http header types. pub mod header { - use crate::Header; - - macro_rules! import_hyper_items { - ($($item:ident),*) => ($(pub use hyper::header::$item;)*) - } - - macro_rules! import_hyper_headers { + macro_rules! import_http_headers { ($($name:ident),*) => ($( pub use http::header::$name as $name; )*) } -// import_hyper_items! { -// Accept, AcceptCharset, AcceptEncoding, AcceptLanguage, AcceptRanges, -// AccessControlAllowCredentials, AccessControlAllowHeaders, -// AccessControlAllowMethods, AccessControlExposeHeaders, -// AccessControlMaxAge, AccessControlRequestHeaders, -// AccessControlRequestMethod, Allow, Authorization, Basic, Bearer, -// CacheControl, Connection, ContentDisposition, ContentEncoding, -// ContentLanguage, ContentLength, ContentRange, ContentType, Date, ETag, -// EntityTag, Expires, From, Headers, Host, HttpDate, IfModifiedSince, -// IfUnmodifiedSince, LastModified, Location, Origin, Prefer, -// PreferenceApplied, Protocol, Quality, QualityItem, Referer, -// StrictTransportSecurity, TransferEncoding, Upgrade, UserAgent, -// AccessControlAllowOrigin, ByteRangeSpec, CacheDirective, Charset, -// ConnectionOption, ContentRangeSpec, DispositionParam, DispositionType, -// Encoding, Expect, IfMatch, IfNoneMatch, IfRange, Pragma, Preference, -// ProtocolName, Range, RangeUnit, ReferrerPolicy, Vary, Scheme, q, qitem -// } -// - import_hyper_headers! { - ACCEPT, ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, + import_http_headers! { + ACCEPT, ACCEPT_CHARSET, ACCEPT_ENCODING, ACCEPT_LANGUAGE, ACCEPT_RANGES, + ACCESS_CONTROL_ALLOW_CREDENTIALS, ACCESS_CONTROL_ALLOW_HEADERS, ACCESS_CONTROL_ALLOW_METHODS, ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_EXPOSE_HEADERS, ACCESS_CONTROL_MAX_AGE, - ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, ACCEPT_CHARSET, - ACCEPT_ENCODING, ACCEPT_LANGUAGE, ACCEPT_RANGES, ALLOW, CACHE_CONTROL, - CONNECTION, CONTENT_DISPOSITION, CONTENT_ENCODING, CONTENT_LANGUAGE, - CONTENT_LENGTH, CONTENT_RANGE, DATE, ETAG, EXPECT, EXPIRES, HOST, IF_MATCH, - IF_MODIFIED_SINCE, IF_NONE_MATCH, IF_RANGE, IF_UNMODIFIED_SINCE, LAST_MODIFIED, - LOCATION, ORIGIN, PRAGMA, RANGE, REFERER, - REFERRER_POLICY, STRICT_TRANSPORT_SECURITY, TRANSFER_ENCODING, UPGRADE, - USER_AGENT, VARY + ACCESS_CONTROL_REQUEST_HEADERS, ACCESS_CONTROL_REQUEST_METHOD, ALLOW, + AUTHORIZATION, CACHE_CONTROL, CONNECTION, CONTENT_DISPOSITION, + CONTENT_ENCODING, CONTENT_LANGUAGE, CONTENT_LENGTH, CONTENT_LOCATION, + CONTENT_RANGE, CONTENT_SECURITY_POLICY, + CONTENT_SECURITY_POLICY_REPORT_ONLY, CONTENT_TYPE, DATE, ETAG, EXPECT, + EXPIRES, FORWARDED, FROM, HOST, IF_MATCH, IF_MODIFIED_SINCE, + IF_NONE_MATCH, IF_RANGE, IF_UNMODIFIED_SINCE, LAST_MODIFIED, LINK, + LOCATION, ORIGIN, PRAGMA, RANGE, REFERER, REFERRER_POLICY, REFRESH, + STRICT_TRANSPORT_SECURITY, TE, TRANSFER_ENCODING, UPGRADE, USER_AGENT, + VARY } } diff --git a/core/http/src/method.rs b/core/http/src/method.rs index 1a4722f895..f8b582554b 100644 --- a/core/http/src/method.rs +++ b/core/http/src/method.rs @@ -1,9 +1,7 @@ -extern crate http; - use std::fmt; use std::str::FromStr; -use crate::{hyper, uncased::uncased_eq}; +use crate::uncased::uncased_eq; use self::Method::*; diff --git a/core/lib/src/catcher.rs b/core/lib/src/catcher.rs index f4dcde52ce..91c5b550a1 100644 --- a/core/lib/src/catcher.rs +++ b/core/lib/src/catcher.rs @@ -59,7 +59,6 @@ use yansi::Color::*; /// /// A function decorated with `catch` must take exactly zero or one arguments. /// If the catcher takes an argument, it must be of type [`&Request`](Request). -#[derive(Clone)] pub struct Catcher { /// The HTTP status code to match against. pub code: u16, diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index ec90c3f685..94a3cb9286 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -3,8 +3,6 @@ use std::cell::{Cell, RefCell}; use std::net::{IpAddr, SocketAddr}; use std::fmt; use std::str; -use std::str::FromStr; -use std::sync::Arc; use yansi::Paint; use state::{Container, Storage}; @@ -15,7 +13,7 @@ use crate::request::{FromFormValue, FormItems, FormItem}; use crate::rocket::Rocket; use crate::router::Route; use crate::config::{Config, Limits}; -use crate::http::{hyper, uri::{Origin, Segments, Uri}}; +use crate::http::{hyper, uri::{Origin, Segments}}; use crate::http::{Method, Header, HeaderMap, Cookies}; use crate::http::{RawStr, ContentType, Accept, MediaType}; use crate::http::private::{Indexed, SmallVec, CookieJar}; @@ -820,7 +818,7 @@ impl<'r> Request<'r> { // TODO if cookie_headers.peek().is_some() { let mut cookie_jar = CookieJar::new(); for header in cookie_headers { - let raw_str = match ::std::str::from_utf8(header.as_bytes()) { + let raw_str = match std::str::from_utf8(header.as_bytes()) { Ok(string) => string, Err(_) => continue }; diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index d5720e6e75..707845a538 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -2,10 +2,8 @@ use std::collections::HashMap; use std::convert::From; use std::str::{from_utf8, FromStr}; use std::cmp::min; -use std::io::{self, Write}; -use std::time::Duration; use std::mem; -use std::net::{IpAddr, SocketAddr, ToSocketAddrs}; +use std::net::ToSocketAddrs; use std::sync::Arc; use futures::{Future, Stream}; @@ -19,7 +17,6 @@ use tokio::prelude::{Future as _, Stream as _}; #[cfg(feature = "tls")] use crate::http::tls::TlsAcceptor; use crate::{logger, handler}; -use crate::ext::ReadExt; use crate::config::{self, Config, LoggedValue}; use crate::request::{Request, FormItems}; use crate::data::Data; @@ -45,16 +42,20 @@ pub struct Rocket { fairings: Fairings, } -struct RocketArcs { - config: Arc, - router: Arc, - default_catchers: Arc>, - catchers: Arc>, - state: Arc, - fairings: Arc, +#[derive(Clone)] +struct RocketHyperService { + rocket: Arc, +} + +impl std::ops::Deref for RocketHyperService { + type Target = Rocket; + + fn deref(&self) -> &Self::Target { + &*self.rocket + } } -impl hyper::MakeService for RocketArcs { +impl hyper::MakeService for RocketHyperService { type ReqBody = hyper::Body; type ResBody = hyper::Body; type Error = hyper::Error; @@ -63,27 +64,16 @@ impl hyper::MakeService for RocketArcs { type MakeError = Self::Error; fn make_service(&mut self, _: Ctx) -> Self::Future { - future::ok(RocketHyperService::new(self)) + future::ok(RocketHyperService { rocket: self.rocket.clone() }) } } -#[derive(Clone)] -pub struct RocketHyperService { - config: Arc, - router: Arc, - default_catchers: Arc>, - catchers: Arc>, - state: Arc, - fairings: Arc, -} - #[doc(hidden)] impl hyper::Service for RocketHyperService { type ReqBody = hyper::Body; type ResBody = hyper::Body; type Error = hyper::Error; - //type Future = FutureResult, Self::Error>; - type Future = Box, Error = Self::Error> + Send>; + type Future = Box, Error = Self::Error> + Send>; // This function tries to hide all of the Hyper-ness from Rocket. It // essentially converts Hyper types into Rocket types, then calls the @@ -132,19 +122,6 @@ impl hyper::Service for RocketHyperService { } impl RocketHyperService { - - #[inline] - fn new(rocket: &RocketArcs) -> RocketHyperService { - RocketHyperService { - config: rocket.config.clone(), - router: rocket.router.clone(), - default_catchers: rocket.default_catchers.clone(), - catchers: rocket.catchers.clone(), - state: rocket.state.clone(), - fairings: rocket.fairings.clone(), - } - } - /// Preprocess the request for Rocket things. Currently, this means: /// /// * Rewriting the method in the request if _method form field exists. @@ -322,7 +299,7 @@ impl Rocket { crate fn handle_error<'r>( &self, status: Status, - req: &'r Request + req: &'r Request<'_> ) -> Response<'r> { unimplemented!("TODO") } @@ -553,8 +530,6 @@ impl Rocket { pub fn register(mut self, catchers: Vec) -> Self { info!("{}{}", Paint::masked("👾 "), Paint::magenta("Catchers:")); - let mut current_catchers = self.catchers.clone(); - for c in catchers { if self.catchers.get(&c.code).map_or(false, |e| !e.is_default) { info_!("{} {}", c, Paint::yellow("(warning: duplicate catcher!)")); @@ -562,11 +537,9 @@ impl Rocket { info_!("{}", c); } - current_catchers.insert(c.code, c); + self.catchers.insert(c.code, c); } - self.catchers = current_catchers; - self } @@ -763,12 +736,12 @@ impl Rocket { // Restore the log level back to what it originally was. logger::pop_max_level(); - let arcs = RocketArcs::from(self); + let service = RocketHyperService { rocket: Arc::new(self) }; // NB: executor must be passed manually here, see hyperium/hyper#1537 let server = hyper::Server::builder(incoming) .executor(runtime.executor()) - .serve(arcs); + .serve(service); // TODO.async: Use with_graceful_shutdown, and let launch() return a Result<(), Error> runtime.block_on(server).expect("TODO.async handle error"); @@ -859,22 +832,9 @@ impl Rocket { } } -impl From for RocketArcs { - fn from(mut rocket: Rocket) -> Self { - RocketArcs { - config: Arc::new(rocket.config), - router: Arc::new(rocket.router), - default_catchers: Arc::new(rocket.default_catchers), - catchers: Arc::new(rocket.catchers), - state: Arc::new(rocket.state), - fairings: Arc::new(rocket.fairings), - } - } -} - // TODO: consider try_from here? impl<'a> From> for hyper::Response { - fn from(mut response: Response) -> Self { + fn from(mut response: Response<'_>) -> Self { let mut builder = hyper::Response::builder(); builder.status(hyper::StatusCode::from_u16(response.status().code).expect("")); @@ -901,4 +861,4 @@ impl<'a> From> for hyper::Response { } }.unwrap() } -} \ No newline at end of file +} From 52ec70cd127cb94c7a31a4b02967f244a658d009 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 10 Mar 2019 22:06:46 -0700 Subject: [PATCH 03/25] Convert core to async and add support for async routes. Minimum rustc bump required for rust-lang/rust#61775 --- core/codegen/src/attribute/catch.rs | 16 +- core/codegen/src/attribute/route.rs | 32 +- core/codegen/src/lib.rs | 3 + core/codegen/tests/route.rs | 2 +- core/http/Cargo.toml | 9 +- core/http/src/cookies.rs | 39 ++- core/http/src/hyper.rs | 6 +- core/http/src/tls.rs | 7 +- core/lib/Cargo.toml | 4 +- core/lib/build.rs | 4 +- core/lib/src/catcher.rs | 15 +- core/lib/src/codegen.rs | 4 +- core/lib/src/config/config.rs | 32 +- core/lib/src/data/data.rs | 117 +++++-- core/lib/src/data/data_stream.rs | 71 ++-- core/lib/src/data/from_data.rs | 101 +++--- core/lib/src/data/mod.rs | 3 +- core/lib/src/data/net_stream.rs | 94 ------ core/lib/src/error.rs | 9 +- core/lib/src/ext.rs | 63 +++- core/lib/src/handler.rs | 15 +- core/lib/src/lib.rs | 1 + core/lib/src/local/request.rs | 56 ++-- core/lib/src/logger.rs | 6 +- core/lib/src/request/form/form.rs | 46 +-- core/lib/src/request/form/from_form.rs | 2 +- core/lib/src/request/form/lenient.rs | 12 +- core/lib/src/request/request.rs | 127 ++++--- core/lib/src/response/responder.rs | 9 +- core/lib/src/response/response.rs | 91 +++-- core/lib/src/response/status.rs | 20 +- core/lib/src/response/stream.rs | 15 +- core/lib/src/rocket.rs | 443 ++++++++++++++----------- core/lib/src/router/mod.rs | 8 +- examples/cookies/src/main.rs | 2 +- examples/errors/src/main.rs | 2 +- examples/form_kitchen_sink/src/main.rs | 2 +- examples/hello_world/src/main.rs | 2 +- 38 files changed, 823 insertions(+), 667 deletions(-) delete mode 100644 core/lib/src/data/net_stream.rs diff --git a/core/codegen/src/attribute/catch.rs b/core/codegen/src/attribute/catch.rs index 372620d955..9b7feb594f 100644 --- a/core/codegen/src/attribute/catch.rs +++ b/core/codegen/src/attribute/catch.rs @@ -51,7 +51,7 @@ pub fn _catch(args: TokenStream, input: TokenStream) -> Result { let status_code = status.0.code; // Variables names we'll use and reuse. - define_vars_and_mods!(req, catcher, response, Request, Response); + define_vars_and_mods!(req, catcher, Request, Response, ErrorHandlerFuture); // Determine the number of parameters that will be passed in. let (fn_sig, inputs) = match catch.function.decl.inputs.len() { @@ -82,12 +82,14 @@ pub fn _catch(args: TokenStream, input: TokenStream) -> Result { #user_catcher_fn /// Rocket code generated wrapping catch function. - #vis fn #generated_fn_name<'_b>(#req: &'_b #Request) -> #response::Result<'_b> { - let __response = #catcher_response; - #Response::build() - .status(#status) - .merge(__response) - .ok() + #vis fn #generated_fn_name<'_b>(#req: &'_b #Request) -> #ErrorHandlerFuture<'_b> { + Box::pin(async move { + let __response = #catcher_response; + #Response::build() + .status(#status) + .merge(__response) + .ok() + }) } /// Rocket code generated static catcher info. diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 6b90e11172..8515159209 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -178,7 +178,7 @@ fn data_expr(ident: &syn::Ident, ty: &syn::Type) -> TokenStream2 { define_vars_and_mods!(req, data, FromData, Outcome, Transform); let span = ident.span().unstable().join(ty.span()).unwrap().into(); quote_spanned! { span => - let __transform = <#ty as #FromData>::transform(#req, #data); + let __transform = <#ty as #FromData>::transform(#req, #data).await; #[allow(unreachable_patterns, unreachable_code)] let __outcome = match __transform { @@ -195,7 +195,7 @@ fn data_expr(ident: &syn::Ident, ty: &syn::Type) -> TokenStream2 { }; #[allow(non_snake_case, unreachable_patterns, unreachable_code)] - let #ident: #ty = match <#ty as #FromData>::from_data(#req, __outcome) { + let #ident: #ty = match <#ty as #FromData>::from_data(#req, __outcome).await { #Outcome::Success(__d) => __d, #Outcome::Forward(__d) => return #Outcome::Forward(__d), #Outcome::Failure((__c, _)) => return #Outcome::Failure(__c), @@ -384,7 +384,7 @@ fn codegen_route(route: Route) -> Result { } // Gather everything we need. - define_vars_and_mods!(req, data, handler, Request, Data, StaticRouteInfo); + define_vars_and_mods!(req, data, handler, Request, Data, StaticRouteInfo, HandlerFuture); let (vis, user_handler_fn) = (&route.function.vis, &route.function); let user_handler_fn_name = &user_handler_fn.ident; let generated_fn_name = user_handler_fn_name.prepend(ROUTE_FN_PREFIX); @@ -396,6 +396,16 @@ fn codegen_route(route: Route) -> Result { let rank = Optional(route.attribute.rank); let format = Optional(route.attribute.format); + let responder_stmt = if user_handler_fn.asyncness.is_some() { + quote! { + let ___responder = #user_handler_fn_name(#(#parameter_names),*).await; + } + } else { + quote! { + let ___responder = #user_handler_fn_name(#(#parameter_names),*); + } + }; + Ok(quote! { #user_handler_fn @@ -403,13 +413,15 @@ fn codegen_route(route: Route) -> Result { #vis fn #generated_fn_name<'_b>( #req: &'_b #Request, #data: #Data - ) -> #handler::Outcome<'_b> { - #(#req_guard_definitions)* - #(#parameter_definitions)* - #data_stmt - - let ___responder = #user_handler_fn_name(#(#parameter_names),*); - #handler::Outcome::from(#req, ___responder) + ) -> #HandlerFuture<'_b> { + Box::pin(async move { + #(#req_guard_definitions)* + #(#parameter_definitions)* + #data_stmt + + #responder_stmt + #handler::Outcome::from(#req, ___responder) + }) } /// Rocket code generated wrapping URI macro. diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 87a8f8593b..643969bfe4 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -1,5 +1,6 @@ #![feature(proc_macro_diagnostic, proc_macro_span)] #![feature(crate_visibility_modifier)] +#![feature(async_await)] #![recursion_limit="128"] #![doc(html_root_url = "https://api.rocket.rs/v0.5")] @@ -87,6 +88,8 @@ macro_rules! define_vars_and_mods { (@Data as $v:ident) => (define!(::rocket::Data as $v)); (@StaticRouteInfo as $v:ident) => (define!(::rocket::StaticRouteInfo as $v)); (@SmallVec as $v:ident) => (define!(::rocket::http::private::SmallVec as $v)); + (@HandlerFuture as $v:ident) => (define!(::rocket::handler::HandlerFuture as $v)); + (@ErrorHandlerFuture as $v:ident) => (define!(::rocket::handler::ErrorHandlerFuture as $v)); ($($name:ident),*) => ($(define_vars_and_mods!(@$name as $name);)*) } diff --git a/core/codegen/tests/route.rs b/core/codegen/tests/route.rs index e752bfa482..bb8f325505 100644 --- a/core/codegen/tests/route.rs +++ b/core/codegen/tests/route.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] // Rocket sometimes generates mangled identifiers that activate the // non_snake_case lint. We deny the lint in this test to ensure that diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index 6e55053885..ce987dff70 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -16,7 +16,7 @@ edition = "2018" [features] default = [] -tls = ["rustls", "hyper-sync-rustls"] +tls = ["tokio-rustls"] private-cookies = ["cookie/secure"] [dependencies] @@ -27,16 +27,11 @@ http = "0.1.17" mime = "0.3.13" time = "0.1" indexmap = "1.0" -rustls = { version = "0.15", optional = true } state = "0.4" +tokio-rustls = { version = "0.9.2", optional = true } cookie = { version = "0.12", features = ["percent-encode"] } pear = "0.1" unicode-xid = "0.1" -[dependencies.hyper-sync-rustls] -version = "=0.3.0-rc.5" -features = ["server"] -optional = true - [dev-dependencies] rocket = { version = "0.5.0-dev", path = "../lib" } diff --git a/core/http/src/cookies.rs b/core/http/src/cookies.rs index c9e82b5daf..8240c5c017 100644 --- a/core/http/src/cookies.rs +++ b/core/http/src/cookies.rs @@ -1,5 +1,4 @@ use std::fmt; -use std::cell::RefMut; use crate::Header; use cookie::Delta; @@ -128,7 +127,7 @@ mod key { /// 32`. pub enum Cookies<'a> { #[doc(hidden)] - Jarred(RefMut<'a, CookieJar>, &'a Key), + Jarred(CookieJar, &'a Key, Box), #[doc(hidden)] Empty(CookieJar) } @@ -137,8 +136,8 @@ impl<'a> Cookies<'a> { /// WARNING: This is unstable! Do not use this method outside of Rocket! #[inline] #[doc(hidden)] - pub fn new(jar: RefMut<'a, CookieJar>, key: &'a Key) -> Cookies<'a> { - Cookies::Jarred(jar, key) + pub fn new(jar: CookieJar, key: &'a Key, on_drop: F) -> Cookies<'a> { + Cookies::Jarred(jar, key, Box::new(on_drop)) } /// WARNING: This is unstable! Do not use this method outside of Rocket! @@ -160,7 +159,7 @@ impl<'a> Cookies<'a> { #[inline] #[doc(hidden)] pub fn add_original(&mut self, cookie: Cookie<'static>) { - if let Cookies::Jarred(ref mut jar, _) = *self { + if let Cookies::Jarred(ref mut jar, _, _) = *self { jar.add_original(cookie) } } @@ -180,7 +179,7 @@ impl<'a> Cookies<'a> { /// ``` pub fn get(&self, name: &str) -> Option<&Cookie<'static>> { match *self { - Cookies::Jarred(ref jar, _) => jar.get(name), + Cookies::Jarred(ref jar, _, _) => jar.get(name), Cookies::Empty(_) => None } } @@ -205,7 +204,7 @@ impl<'a> Cookies<'a> { /// } /// ``` pub fn add(&mut self, cookie: Cookie<'static>) { - if let Cookies::Jarred(ref mut jar, _) = *self { + if let Cookies::Jarred(ref mut jar, _, _) = *self { jar.add(cookie) } } @@ -231,7 +230,7 @@ impl<'a> Cookies<'a> { /// } /// ``` pub fn remove(&mut self, cookie: Cookie<'static>) { - if let Cookies::Jarred(ref mut jar, _) = *self { + if let Cookies::Jarred(ref mut jar, _, _) = *self { jar.remove(cookie) } } @@ -252,7 +251,7 @@ impl<'a> Cookies<'a> { /// ``` pub fn iter(&self) -> impl Iterator> { match *self { - Cookies::Jarred(ref jar, _) => jar.iter(), + Cookies::Jarred(ref jar, _, _) => jar.iter(), Cookies::Empty(ref jar) => jar.iter() } } @@ -262,12 +261,22 @@ impl<'a> Cookies<'a> { #[doc(hidden)] pub fn delta(&self) -> Delta<'_> { match *self { - Cookies::Jarred(ref jar, _) => jar.delta(), + Cookies::Jarred(ref jar, _, _) => jar.delta(), Cookies::Empty(ref jar) => jar.delta() } } } +impl<'a> Drop for Cookies<'a> { + fn drop(&mut self) { + if let Cookies::Jarred(ref mut jar, _, ref mut on_drop) = *self { + let jar = std::mem::replace(jar, CookieJar::new()); + let on_drop = std::mem::replace(on_drop, Box::new(|_| {})); + on_drop(jar); + } + } +} + #[cfg(feature = "private-cookies")] impl Cookies<'_> { /// Returns a reference to the `Cookie` inside this collection with the name @@ -290,7 +299,7 @@ impl Cookies<'_> { /// ``` pub fn get_private(&mut self, name: &str) -> Option> { match *self { - Cookies::Jarred(ref mut jar, key) => jar.private(key).get(name), + Cookies::Jarred(ref mut jar, key, _) => jar.private(key).get(name), Cookies::Empty(_) => None } } @@ -326,7 +335,7 @@ impl Cookies<'_> { /// } /// ``` pub fn add_private(&mut self, mut cookie: Cookie<'static>) { - if let Cookies::Jarred(ref mut jar, key) = *self { + if let Cookies::Jarred(ref mut jar, key, _) = *self { Cookies::set_private_defaults(&mut cookie); jar.private(key).add(cookie) } @@ -336,7 +345,7 @@ impl Cookies<'_> { /// WARNING: This is unstable! Do not use this method outside of Rocket! #[doc(hidden)] pub fn add_original_private(&mut self, mut cookie: Cookie<'static>) { - if let Cookies::Jarred(ref mut jar, key) = *self { + if let Cookies::Jarred(ref mut jar, key, _) = *self { Cookies::set_private_defaults(&mut cookie); jar.private(key).add_original(cookie) } @@ -390,7 +399,7 @@ impl Cookies<'_> { /// } /// ``` pub fn remove_private(&mut self, mut cookie: Cookie<'static>) { - if let Cookies::Jarred(ref mut jar, key) = *self { + if let Cookies::Jarred(ref mut jar, key, _) = *self { if cookie.path().is_none() { cookie.set_path("/"); } @@ -403,7 +412,7 @@ impl Cookies<'_> { impl fmt::Debug for Cookies<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - Cookies::Jarred(ref jar, _) => write!(f, "{:?}", jar), + Cookies::Jarred(ref jar, _, _) => write!(f, "{:?}", jar), Cookies::Empty(ref jar) => write!(f, "{:?}", jar) } } diff --git a/core/http/src/hyper.rs b/core/http/src/hyper.rs index 788c55e50e..f6c93421a8 100644 --- a/core/http/src/hyper.rs +++ b/core/http/src/hyper.rs @@ -4,17 +4,17 @@ //! These types will, with certainty, be removed with time, but they reside here //! while necessary. -#[doc(hidden)] pub use hyper::{Body, Request, Response}; +#[doc(hidden)] pub use hyper::{Body, Request, Response, Server}; #[doc(hidden)] pub use hyper::body::Payload as Payload; #[doc(hidden)] pub use hyper::error::Error; -#[doc(hidden)] pub use hyper::server::Server; #[doc(hidden)] pub use hyper::service::{MakeService, Service}; #[doc(hidden)] pub use hyper::Chunk; +#[doc(hidden)] pub use http::header::HeaderMap; #[doc(hidden)] pub use http::header::HeaderName as HeaderName; #[doc(hidden)] pub use http::header::HeaderValue as HeaderValue; #[doc(hidden)] pub use http::method::Method; -#[doc(hidden)] pub use http::request::Parts; +#[doc(hidden)] pub use http::request::Parts as RequestParts; #[doc(hidden)] pub use http::status::StatusCode; #[doc(hidden)] pub use http::uri::Uri; diff --git a/core/http/src/tls.rs b/core/http/src/tls.rs index b0311be862..97ae1289a0 100644 --- a/core/http/src/tls.rs +++ b/core/http/src/tls.rs @@ -1,2 +1,5 @@ -pub use hyper_sync_rustls::{util, WrappedStream, ServerSession, TlsServer}; -pub use rustls::{Certificate, PrivateKey}; +pub use tokio_rustls::TlsAcceptor; +pub use tokio_rustls::rustls; + +pub use rustls::internal::pemfile; +pub use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 2a1b3deadf..16bdb95cfa 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -24,12 +24,12 @@ tls = ["rocket_http/tls"] private-cookies = ["rocket_http/private-cookies"] [dependencies] -futures = "0.1" rocket_codegen = { version = "0.5.0-dev", path = "../codegen" } rocket_http = { version = "0.5.0-dev", path = "../http" } +futures-preview = { version = "0.3.0-alpha.14", features = ["compat", "io-compat"] } tokio = "0.1.16" yansi = "0.5" -log = "0.4" +log = { version = "0.4", features = ["std"] } toml = "0.4.7" num_cpus = "1.0" state = "0.4.1" diff --git a/core/lib/build.rs b/core/lib/build.rs index 0f71316dfe..b6ca3322eb 100644 --- a/core/lib/build.rs +++ b/core/lib/build.rs @@ -3,8 +3,8 @@ use yansi::{Paint, Color::{Red, Yellow, Blue}}; // Specifies the minimum nightly version needed to compile Rocket. -const MIN_DATE: &'static str = "2019-04-05"; -const MIN_VERSION: &'static str = "1.35.0-nightly"; +const MIN_DATE: &'static str = "2019-07-03"; +const MIN_VERSION: &'static str = "1.37.0-nightly"; macro_rules! err { ($version:expr, $date:expr, $msg:expr) => ( diff --git a/core/lib/src/catcher.rs b/core/lib/src/catcher.rs index 91c5b550a1..07bff3a0d5 100644 --- a/core/lib/src/catcher.rs +++ b/core/lib/src/catcher.rs @@ -1,3 +1,5 @@ +use futures::future::Future; + use crate::response; use crate::handler::ErrorHandler; use crate::codegen::StaticCatchInfo; @@ -98,7 +100,7 @@ impl Catcher { } #[inline(always)] - crate fn handle<'r>(&self, req: &'r Request<'_>) -> response::Result<'r> { + crate fn handle<'r>(&self, req: &'r Request<'_>) -> impl Future> { (self.handler)(req) } @@ -149,10 +151,12 @@ macro_rules! default_catchers { let mut map = HashMap::new(); $( - fn $fn_name<'r>(req: &'r Request<'_>) -> response::Result<'r> { - status::Custom(Status::from_code($code).unwrap(), - content::Html(error_page_template!($code, $name, $description)) - ).respond_to(req) + fn $fn_name<'r>(req: &'r Request<'_>) -> std::pin::Pin> + Send + 'r>> { + (async move { + status::Custom(Status::from_code($code).unwrap(), + content::Html(error_page_template!($code, $name, $description)) + ).respond_to(req) + }).boxed() } map.insert($code, Catcher::new_default($code, $fn_name)); @@ -164,6 +168,7 @@ macro_rules! default_catchers { pub mod defaults { use super::Catcher; + use futures::future::FutureExt; use std::collections::HashMap; diff --git a/core/lib/src/codegen.rs b/core/lib/src/codegen.rs index 276eea1a32..894cf85403 100644 --- a/core/lib/src/codegen.rs +++ b/core/lib/src/codegen.rs @@ -1,9 +1,11 @@ +use futures::future::Future; + use crate::{Request, Data}; use crate::handler::{Outcome, ErrorHandler}; use crate::http::{Method, MediaType}; /// Type of a static handler, which users annotate with Rocket's attribute. -pub type StaticHandler = for<'r> fn(&'r Request<'_>, Data) -> Outcome<'r>; +pub type StaticHandler = for<'r> fn(&'r Request<'_>, Data) -> std::pin::Pin> + Send + 'r>>; /// Information generated by the `route` attribute during codegen. pub struct StaticRouteInfo { diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index d07e680bd9..b237aa3ea5 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -516,23 +516,33 @@ impl Config { /// ``` #[cfg(feature = "tls")] pub fn set_tls(&mut self, certs_path: &str, key_path: &str) -> Result<()> { - use crate::http::tls::util::{self, Error}; + use crate::http::tls::pemfile::{certs, rsa_private_keys}; + use std::fs::File; + use std::io::BufReader; let pem_err = "malformed PEM file"; + // TODO.async: Fully copy from hyper-sync-rustls, move to http/src/tls + // Partially extracted from hyper-sync-rustls + // Load the certificates. - let certs = util::load_certs(self.root_relative(certs_path)) - .map_err(|e| match e { - Error::Io(e) => ConfigError::Io(e, "tls.certs"), - _ => self.bad_type("tls", pem_err, "a valid certificates file") - })?; + let certs = match File::open(self.root_relative(certs_path)) { + Ok(file) => certs(&mut BufReader::new(file)).map_err(|_| { + self.bad_type("tls", pem_err, "a valid certificates file") + }), + Err(e) => Err(ConfigError::Io(e, "tls.certs"))?, + }?; // And now the private key. - let key = util::load_private_key(self.root_relative(key_path)) - .map_err(|e| match e { - Error::Io(e) => ConfigError::Io(e, "tls.key"), - _ => self.bad_type("tls", pem_err, "a valid private key file") - })?; + let mut keys = match File::open(self.root_relative(key_path)) { + Ok(file) => rsa_private_keys(&mut BufReader::new(file)).map_err(|_| { + self.bad_type("tls", pem_err, "a valid private key file") + }), + Err(e) => Err(ConfigError::Io(e, "tls.key")), + }?; + + // TODO.async: Proper check for one key + let key = keys.remove(0); self.tls = Some(TlsConfig { certs, key }); Ok(()) diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index f041415d4e..986dadd081 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -1,16 +1,16 @@ -use std::io::{self, Read, Write, Cursor, Chain}; use std::path::Path; -use std::fs::File; -use std::time::Duration; +use std::pin::Pin; -#[cfg(feature = "tls")] use super::net_stream::HttpsStream; +use futures::compat::{Future01CompatExt, Stream01CompatExt, AsyncWrite01CompatExt}; +use futures::io::{self, AsyncRead, AsyncReadExt as _, AsyncWrite}; +use futures::future::Future; +use futures::stream::TryStreamExt; -use super::data_stream::{DataStream, /* TODO kill_stream */}; -use super::net_stream::NetStream; -use crate::ext::ReadExt; +use super::data_stream::{DataStream, kill_stream}; -use crate::http::hyper::{self, Payload}; -use futures::{Async, Future}; +use crate::http::hyper; + +use crate::ext::AsyncReadExt; /// The number of bytes to read into the "peek" buffer. const PEEK_BYTES: usize = 512; @@ -48,7 +48,9 @@ const PEEK_BYTES: usize = 512; /// body data. This enables partially or fully reading from a `Data` object /// without consuming the `Data` object. pub struct Data { - body: Vec, + buffer: Vec, + is_complete: bool, + stream: Box, } impl Data { @@ -69,11 +71,15 @@ impl Data { /// } /// ``` pub fn open(mut self) -> DataStream { - // FIXME: Insert a `BufReader` in front of the `NetStream` with capacity - // 4096. We need the new `Chain` methods to get the inner reader to - // actually do this, however. - let stream = ::std::mem::replace(&mut self.body, vec![]); - DataStream(Cursor::new(stream)) + let buffer = std::mem::replace(&mut self.buffer, vec![]); + let stream = std::mem::replace(&mut self.stream, Box::new(&[][..])); + DataStream(buffer, stream) + } + + crate fn from_hyp(body: hyper::Body) -> impl Future { + // TODO.async: This used to also set the read timeout to 5 seconds. + + Data::new(body) } /// Retrieve the `peek` buffer. @@ -94,10 +100,10 @@ impl Data { /// ``` #[inline(always)] pub fn peek(&self) -> &[u8] { - if self.body.len() > PEEK_BYTES { - &self.body[..PEEK_BYTES] + if self.buffer.len() > PEEK_BYTES { + &self.buffer[..PEEK_BYTES] } else { - &self.body + &self.buffer } } @@ -118,8 +124,7 @@ impl Data { /// ``` #[inline(always)] pub fn peek_complete(&self) -> bool { - // TODO self.is_complete - true + self.is_complete } /// A helper method to write the body of the request to any `Write` type. @@ -139,8 +144,11 @@ impl Data { /// } /// ``` #[inline(always)] - pub fn stream_to(self, writer: &mut W) -> io::Result { - io::copy(&mut self.open(), writer) + pub fn stream_to<'w, W: AsyncWrite + Unpin>(self, writer: &'w mut W) -> impl Future> + 'w { + Box::pin(async move { + let stream = self.open(); + stream.copy_into(writer).await + }) } /// A helper method to write the body of the request to a file at the path @@ -161,8 +169,11 @@ impl Data { /// } /// ``` #[inline(always)] - pub fn stream_to_file>(self, path: P) -> io::Result { - io::copy(&mut self.open(), &mut File::create(path)?) + pub fn stream_to_file + Send + 'static>(self, path: P) -> impl Future> { + Box::pin(async move { + let mut file = tokio::fs::File::create(path).compat().await?.compat(); + self.stream_to(&mut file).await + }) } // Creates a new data object with an internal buffer `buf`, where the cursor @@ -170,8 +181,62 @@ impl Data { // bytes `vec[pos..cap]` are buffered and unread. The remainder of the data // bytes can be read from `stream`. #[inline(always)] - crate fn new(body: Vec) -> Data { - Data { body } + crate fn new(body: hyper::Body) -> Pin + Send>> { + trace_!("Data::new({:?})", body); + + let mut stream = body.compat().map_err(|e| { + io::Error::new(io::ErrorKind::Other, e) + }).into_async_read(); + + Box::pin(async { + let mut peek_buf = vec![0; PEEK_BYTES]; + + let eof = match stream.read_max(&mut peek_buf[..]).await { + Ok(n) => { + trace_!("Filled peek buf with {} bytes.", n); + + // TODO.async: This has not gone away, and I don't entirely + // understand what's happening here + + // We can use `set_len` here instead of `truncate`, but we'll + // take the performance hit to avoid `unsafe`. All of this code + // should go away when we migrate away from hyper 0.10.x. + + peek_buf.truncate(n); + n < PEEK_BYTES + } + Err(e) => { + error_!("Failed to read into peek buffer: {:?}.", e); + // Likewise here as above. + peek_buf.truncate(0); + false + } + }; + + trace_!("Peek bytes: {}/{} bytes.", peek_buf.len(), PEEK_BYTES); + Data { buffer: peek_buf, stream: Box::new(stream), is_complete: eof } + }) } + /// This creates a `data` object from a local data source `data`. + #[inline] + crate fn local(data: Vec) -> Data { + Data { + buffer: data, + stream: Box::new(&[][..]), + is_complete: true, + } + } +} + +impl std::borrow::Borrow<()> for Data { + fn borrow(&self) -> &() { + &() + } +} + +impl Drop for Data { + fn drop(&mut self) { + kill_stream(&mut self.stream); + } } diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index fe237edc9d..3aad522d4c 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -1,50 +1,61 @@ -use std::io::{self, Chain, Cursor, Read, Write}; -use std::net::Shutdown; +use std::pin::Pin; -pub type InnerStream = Cursor>; +use futures::io::{AsyncRead, Error as IoError}; +use futures::task::{Poll, Context}; +// TODO.async: Consider storing the real type here instead of a Box to avoid +// the dynamic dispatch /// Raw data stream of a request body. /// /// This stream can only be obtained by calling /// [`Data::open()`](crate::data::Data::open()). The stream contains all of the data /// in the body of the request. It exposes no methods directly. Instead, it must /// be used as an opaque [`Read`] structure. -pub struct DataStream(crate InnerStream); +pub struct DataStream(crate Vec, crate Box); + +// TODO.async: Consider implementing `AsyncBufRead` // TODO: Have a `BufRead` impl for `DataStream`. At the moment, this isn't // possible since Hyper's `HttpReader` doesn't implement `BufRead`. -impl Read for DataStream { +impl AsyncRead for DataStream { #[inline(always)] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - trace_!("DataStream::read()"); - self.0.read(buf) + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + trace_!("DataStream::poll_read()"); + if self.0.len() > 0 { + let count = std::cmp::min(buf.len(), self.0.len()); + trace_!("Reading peeked {} into dest {} = {} bytes", self.0.len(), buf.len(), count); + let next = self.0.split_off(count); + (&mut buf[..count]).copy_from_slice(&self.0[..]); + self.0 = next; + Poll::Ready(Ok(count)) + } else { + trace_!("Delegating to remaining stream"); + Pin::new(&mut self.1).poll_read(cx, buf) + } } } -/* pub fn kill_stream(stream: &mut BodyReader) { - // Only do the expensive reading if we're not sure we're done. - // TODO use self::HttpReader::*; - match *stream { - SizedReader(_, n) | ChunkedReader(_, Some(n)) if n > 0 => { /* continue */ }, - _ => return - }; - - // Take <= 1k from the stream. If there might be more data, force close. - const FLUSH_LEN: u64 = 1024; - match io::copy(&mut stream.take(FLUSH_LEN), &mut io::sink()) { - Ok(FLUSH_LEN) | Err(_) => { - warn_!("Data left unread. Force closing network stream."); - let (_, network) = stream.get_mut().get_mut(); - if let Err(e) = network.close(Shutdown::Read) { - error_!("Failed to close network stream: {:?}", e); - } - } - Ok(n) => debug!("flushed {} unread bytes", n) - } -}*/ +// TODO.async: Either implement this somehow, or remove the +// `Drop` impl and other references to kill_stream +pub fn kill_stream(_stream: &mut dyn AsyncRead) { +// // Only do the expensive reading if we're not sure we're done. +// +// // Take <= 1k from the stream. If there might be more data, force close. +// const FLUSH_LEN: u64 = 1024; +// match io::copy(&mut stream.take(FLUSH_LEN), &mut io::sink()) { +// Ok(FLUSH_LEN) | Err(_) => { +// warn_!("Data left unread. Force closing network stream."); +// let (_, network) = stream.get_mut().get_mut(); +// if let Err(e) = network.close(Shutdown::Read) { +// error_!("Failed to close network stream: {:?}", e); +// } +// } +// Ok(n) => debug!("flushed {} unread bytes", n) +// } +} impl Drop for DataStream { fn drop(&mut self) { - // TODO kill_stream(&mut self.0.get_mut().1); + kill_stream(&mut self.1); } } diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index 3aa5779b5e..60b34d3bf2 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -1,4 +1,8 @@ use std::borrow::Borrow; +use std::pin::Pin; + +use futures::future::{ready, Future, FutureExt}; +use futures::io::AsyncReadExt; use crate::outcome::{self, IntoOutcome}; use crate::outcome::Outcome::*; @@ -108,6 +112,9 @@ pub type Transformed<'a, T> = Outcome<&'a >::Borrowed, >::Error> >; +pub type TransformFuture<'a, T, E> = Pin>> + Send + 'a>>; +pub type FromDataFuture<'a, T, E> = Pin> + Send + 'a>>; + /// Trait implemented by data guards to derive a value from request body data. /// /// # Data Guards @@ -321,7 +328,7 @@ pub type Transformed<'a, T> = /// [`FromDataSimple`] documentation. pub trait FromData<'a>: Sized { /// The associated error to be returned when the guard fails. - type Error; + type Error: Send; /// The owned type returned from [`FromData::transform()`]. /// @@ -354,7 +361,7 @@ pub trait FromData<'a>: Sized { /// If transformation succeeds, an outcome of `Success` is returned. /// If the data is not appropriate given the type of `Self`, `Forward` is /// returned. On failure, `Failure` is returned. - fn transform(request: &Request<'_>, data: Data) -> Transform>; + fn transform(request: &Request<'_>, data: Data) -> TransformFuture<'a, Self::Owned, Self::Error>; /// Validates, parses, and converts the incoming request body data into an /// instance of `Self`. @@ -383,23 +390,23 @@ pub trait FromData<'a>: Sized { /// # unimplemented!() /// # } /// ``` - fn from_data(request: &Request<'_>, outcome: Transformed<'a, Self>) -> Outcome; + fn from_data(request: &Request<'_>, outcome: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error>; } /// The identity implementation of `FromData`. Always returns `Success`. -impl<'f> FromData<'f> for Data { +impl<'a> FromData<'a> for Data { type Error = std::convert::Infallible; type Owned = Data; - type Borrowed = Data; + type Borrowed = (); #[inline(always)] - fn transform(_: &Request<'_>, data: Data) -> Transform> { - Transform::Owned(Success(data)) + fn transform(_: &Request<'_>, data: Data) -> TransformFuture<'a, Self::Owned, Self::Error> { + Box::pin(ready(Transform::Owned(Success(data)))) } #[inline(always)] - fn from_data(_: &Request<'_>, outcome: Transformed<'f, Self>) -> Outcome { - Success(outcome.owned()?) + fn from_data(_: &Request<'_>, outcome: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error> { + Box::pin(ready(outcome.owned())) } } @@ -493,8 +500,9 @@ impl<'f> FromData<'f> for Data { /// # fn main() { } /// ``` pub trait FromDataSimple: Sized { + // TODO.async: Can/should we relax this 'static? And how? /// The associated error to be returned when the guard fails. - type Error; + type Error: Send + 'static; /// Validates, parses, and converts an instance of `Self` from the incoming /// request body data. @@ -502,22 +510,25 @@ pub trait FromDataSimple: Sized { /// If validation and parsing succeeds, an outcome of `Success` is returned. /// If the data is not appropriate given the type of `Self`, `Forward` is /// returned. If parsing fails, `Failure` is returned. - fn from_data(request: &Request<'_>, data: Data) -> Outcome; + fn from_data(request: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error>; } -impl<'a, T: FromDataSimple> FromData<'a> for T { +impl<'a, T: FromDataSimple + 'a> FromData<'a> for T { type Error = T::Error; type Owned = Data; - type Borrowed = Data; + type Borrowed = (); #[inline(always)] - fn transform(_: &Request<'_>, d: Data) -> Transform> { - Transform::Owned(Success(d)) + fn transform(_: &Request<'_>, d: Data) -> TransformFuture<'a, Self::Owned, Self::Error> { + Box::pin(ready(Transform::Owned(Success(d)))) } #[inline(always)] - fn from_data(req: &Request<'_>, o: Transformed<'a, Self>) -> Outcome { - T::from_data(req, o.owned()?) + fn from_data(req: &Request<'_>, o: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error> { + match o.owned() { + Success(data) => T::from_data(req, data), + _ => unreachable!(), + } } } @@ -527,17 +538,17 @@ impl<'a, T: FromData<'a> + 'a> FromData<'a> for Result { type Borrowed = T::Borrowed; #[inline(always)] - fn transform(r: &Request<'_>, d: Data) -> Transform> { + fn transform(r: &Request<'_>, d: Data) -> TransformFuture<'a, Self::Owned, Self::Error> { T::transform(r, d) } #[inline(always)] - fn from_data(r: &Request<'_>, o: Transformed<'a, Self>) -> Outcome { - match T::from_data(r, o) { + fn from_data(r: &Request<'_>, o: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error> { + Box::pin(T::from_data(r, o).map(|x| match x { Success(val) => Success(Ok(val)), Forward(data) => Forward(data), Failure((_, e)) => Success(Err(e)), - } + })) } } @@ -547,46 +558,52 @@ impl<'a, T: FromData<'a> + 'a> FromData<'a> for Option { type Borrowed = T::Borrowed; #[inline(always)] - fn transform(r: &Request<'_>, d: Data) -> Transform> { + fn transform(r: &Request<'_>, d: Data) -> TransformFuture<'a, Self::Owned, Self::Error> { T::transform(r, d) } #[inline(always)] - fn from_data(r: &Request<'_>, o: Transformed<'a, Self>) -> Outcome { - match T::from_data(r, o) { + fn from_data(r: &Request<'_>, o: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error> { + Box::pin(T::from_data(r, o).map(|x| match x { Success(val) => Success(Some(val)), Failure(_) | Forward(_) => Success(None), - } + })) } } -#[cfg(debug_assertions)] -use std::io::{self, Read}; - #[cfg(debug_assertions)] impl FromDataSimple for String { - type Error = io::Error; + type Error = std::io::Error; #[inline(always)] - fn from_data(_: &Request<'_>, data: Data) -> Outcome { - let mut string = String::new(); - match data.open().read_to_string(&mut string) { - Ok(_) => Success(string), - Err(e) => Failure((Status::BadRequest, e)) - } + fn from_data(_: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error> { + Box::pin(async { + let mut stream = data.open(); + let mut buf = Vec::new(); + if let Err(e) = stream.read_to_end(&mut buf).await { + return 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))), + } + }) } } #[cfg(debug_assertions)] impl FromDataSimple for Vec { - type Error = io::Error; + type Error = std::io::Error; #[inline(always)] - fn from_data(_: &Request<'_>, data: Data) -> Outcome { - let mut bytes = Vec::new(); - match data.open().read_to_end(&mut bytes) { - Ok(_) => Success(bytes), - Err(e) => Failure((Status::BadRequest, e)) - } + fn from_data(_: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error> { + Box::pin(async { + let mut stream = data.open(); + let mut buf = Vec::new(); + match stream.read_to_end(&mut buf).await { + Ok(_) => Success(buf), + Err(e) => Failure((Status::BadRequest, e)), + } + }) } } diff --git a/core/lib/src/data/mod.rs b/core/lib/src/data/mod.rs index 20523fac52..350b268574 100644 --- a/core/lib/src/data/mod.rs +++ b/core/lib/src/data/mod.rs @@ -2,9 +2,8 @@ mod data; mod data_stream; -mod net_stream; mod from_data; pub use self::data::Data; pub use self::data_stream::DataStream; -pub use self::from_data::{FromData, FromDataSimple, Outcome, Transform, Transformed}; +pub use self::from_data::{FromData, FromDataFuture, FromDataSimple, Outcome, Transform, Transformed, TransformFuture}; diff --git a/core/lib/src/data/net_stream.rs b/core/lib/src/data/net_stream.rs deleted file mode 100644 index 09e0762bf7..0000000000 --- a/core/lib/src/data/net_stream.rs +++ /dev/null @@ -1,94 +0,0 @@ -use std::io; -use std::net::{SocketAddr, Shutdown}; -use std::time::Duration; - -#[cfg(feature = "tls")] use crate::http::tls::{WrappedStream, ServerSession}; -// TODO use http::hyper::net::{HttpStream, NetworkStream}; - -use self::NetStream::*; - -#[cfg(feature = "tls")] pub type HttpsStream = WrappedStream; - -// This is a representation of all of the possible network streams we might get. -// This really shouldn't be necessary, but, you know, Hyper. -#[derive(Clone)] -pub enum NetStream { - Http/* TODO (HttpStream) */, - #[cfg(feature = "tls")] - Https(HttpsStream), - Empty, -} - -impl io::Read for NetStream { - #[inline(always)] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - trace_!("NetStream::read()"); - let res = match *self { - Http/*(ref mut stream)*/ => Ok(0) /* TODO stream.read(buf)*/, - #[cfg(feature = "tls")] Https(ref mut stream) => stream.read(buf), - Empty => Ok(0), - }; - - trace_!("NetStream::read() -- complete"); - res - } -} - -impl io::Write for NetStream { - #[inline(always)] - fn write(&mut self, buf: &[u8]) -> io::Result { - trace_!("NetStream::write()"); - match *self { - Http/* TODO (ref mut stream) => stream.write(buf)*/ => Ok(0), - #[cfg(feature = "tls")] Https(ref mut stream) => stream.write(buf), - Empty => Ok(0), - } - } - - #[inline(always)] - fn flush(&mut self) -> io::Result<()> { - match *self { - Http/* TODO (ref mut stream) => stream.flush()*/ => Ok(()), - #[cfg(feature = "tls")] Https(ref mut stream) => stream.flush(), - Empty => Ok(()), - } - } -} - -//impl NetworkStream for NetStream { -// #[inline(always)] -// fn peer_addr(&mut self) -> io::Result { -// match *self { -// Http/* TODO (ref mut stream) => stream.peer_addr()*/ => Err(io::Error::from(io::ErrorKind::AddrNotAvailable)), -// #[cfg(feature = "tls")] Https(ref mut stream) => stream.peer_addr(), -// Empty => Err(io::Error::from(io::ErrorKind::AddrNotAvailable)), -// } -// } -// -// #[inline(always)] -// fn set_read_timeout(&self, dur: Option) -> io::Result<()> { -// match *self { -// Http/* TODO (ref stream) => stream.set_read_timeout(dur)*/ => Ok(()), -// #[cfg(feature = "tls")] Https(ref stream) => stream.set_read_timeout(dur), -// Empty => Ok(()), -// } -// } -// -// #[inline(always)] -// fn set_write_timeout(&self, dur: Option) -> io::Result<()> { -// match *self { -// Http/* TODO (ref stream) => stream.set_write_timeout(dur)*/ => Ok(()), -// #[cfg(feature = "tls")] Https(ref stream) => stream.set_write_timeout(dur), -// Empty => Ok(()), -// } -// } -// -// #[inline(always)] -// fn close(&mut self, how: Shutdown) -> io::Result<()> { -// match *self { -// Http/* TODO (ref mut stream) => stream.close(how)*/ => Ok(()), -// #[cfg(feature = "tls")] Https(ref mut stream) => stream.close(how), -// Empty => Ok(()), -// } -// } -//} diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index 71cf0ddd30..bcdb84443a 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -19,7 +19,7 @@ use crate::router::Route; #[derive(Debug)] pub enum LaunchErrorKind { /// Binding to the provided address/port failed. - Bind(std::io::Error), + Bind(io::Error), /// An I/O error occurred during launch. Io(io::Error), /// Route collisions were detected. @@ -123,10 +123,9 @@ impl LaunchError { impl From for LaunchError { #[inline] fn from(error: hyper::Error) -> LaunchError { - match error { - // TODO hyper::Error::Io(e) => LaunchError::new(LaunchErrorKind::Io(e)), - e => LaunchError::new(LaunchErrorKind::Unknown(Box::new(e))) - } + // TODO.async: Should "hyper error" be another variant of LaunchErrorKind? + // Or should this use LaunchErrorKind::Io? + LaunchError::new(LaunchErrorKind::Unknown(Box::new(error))) } } diff --git a/core/lib/src/ext.rs b/core/lib/src/ext.rs index 8813b74177..6cb1c16bf0 100644 --- a/core/lib/src/ext.rs +++ b/core/lib/src/ext.rs @@ -1,19 +1,58 @@ use std::io; +use std::pin::Pin; -pub trait ReadExt: io::Read { - fn read_max(&mut self, mut buf: &mut [u8]) -> io::Result { - let start_len = buf.len(); - while !buf.is_empty() { - match self.read(buf) { - Ok(0) => break, - Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; } - Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} - Err(e) => return Err(e), - } +use futures::io::{AsyncRead, AsyncReadExt as _}; +use futures::future::{Future}; +use futures::task::{Poll, Context}; + +// Based on std::io::Take, but for AsyncRead instead of Read +pub struct Take{ + inner: R, + limit: u64, +} + +// TODO.async: Verify correctness of this implementation. +impl AsyncRead for Take where R: AsyncRead + Unpin { + fn poll_read(mut self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll> { + if self.limit == 0 { + return Poll::Ready(Ok(0)); + } + + let max = std::cmp::min(buf.len() as u64, self.limit) as usize; + match Pin::new(&mut self.inner).poll_read(cx, &mut buf[..max]) { + Poll::Pending => Poll::Pending, + Poll::Ready(Ok(n)) => { + self.limit -= n as u64; + Poll::Ready(Ok(n)) + }, + Poll::Ready(Err(e)) => Poll::Ready(Err(e)), } + } +} + +pub trait AsyncReadExt: AsyncRead { + fn take(self, limit: u64) -> Take where Self: Sized { + Take { inner: self, limit } + } + + // TODO.async: Verify correctness of this implementation. + fn read_max<'a>(&'a mut self, mut buf: &'a mut [u8]) -> Pin> + Send + '_>> + where Self: Send + Unpin + { + Box::pin(async move { + let start_len = buf.len(); + while !buf.is_empty() { + match self.read(buf).await { + Ok(0) => break, + Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; } + Err(ref e) if e.kind() == io::ErrorKind::Interrupted => {} + Err(e) => return Err(e), + } + } - Ok(start_len - buf.len()) + Ok(start_len - buf.len()) + }) } } -impl ReadExt for T { } +impl AsyncReadExt for T { } diff --git a/core/lib/src/handler.rs b/core/lib/src/handler.rs index dcd71a80af..98107b3475 100644 --- a/core/lib/src/handler.rs +++ b/core/lib/src/handler.rs @@ -1,5 +1,7 @@ //! Types and traits for request and error handlers and their return values. +use futures::future::Future; + use crate::data::Data; use crate::request::Request; use crate::response::{self, Response, Responder}; @@ -9,6 +11,9 @@ use crate::outcome; /// Type alias for the `Outcome` of a `Handler`. pub type Outcome<'r> = outcome::Outcome, Status, Data>; +/// Type alias for the unwieldy `Handler` return type +pub type HandlerFuture<'r> = std::pin::Pin> + Send + 'r>>; + /// Trait implemented by types that can handle requests. /// /// In general, you will never need to implement `Handler` manually or be @@ -142,7 +147,7 @@ pub trait Handler: Cloneable + Send + Sync + 'static { /// a response. Otherwise, if the return value is `Forward(Data)`, the next /// matching route is attempted. If there are no other matching routes, the /// `404` error catcher is invoked. - fn handle<'r>(&self, request: &'r Request<'_>, data: Data) -> Outcome<'r>; + fn handle<'r>(&self, request: &'r Request<'_>, data: Data) -> HandlerFuture<'r>; } /// Unfortunate but necessary hack to be able to clone a `Box`. @@ -170,16 +175,18 @@ impl Clone for Box { } impl Handler for F - where for<'r> F: Fn(&'r Request<'_>, Data) -> Outcome<'r> + where for<'r> F: Fn(&'r Request<'_>, Data) -> HandlerFuture<'r> { #[inline(always)] - fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> Outcome<'r> { + fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> HandlerFuture<'r> { self(req, data) } } /// The type of an error handler. -pub type ErrorHandler = for<'r> fn(&'r Request<'_>) -> response::Result<'r>; +pub type ErrorHandler = for<'r> fn(&'r Request<'_>) -> ErrorHandlerFuture<'r>; + +pub type ErrorHandlerFuture<'r> = std::pin::Pin> + Send + 'r>>; impl<'r> Outcome<'r> { /// Return the `Outcome` of response to `req` from `responder`. diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index a96221bc27..b857d81891 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -3,6 +3,7 @@ #![feature(proc_macro_hygiene)] #![feature(crate_visibility_modifier)] #![feature(label_break_value)] +#![feature(async_await)] #![recursion_limit="256"] diff --git a/core/lib/src/local/request.rs b/core/lib/src/local/request.rs index 5091ccc476..1138da49aa 100644 --- a/core/lib/src/local/request.rs +++ b/core/lib/src/local/request.rs @@ -107,9 +107,7 @@ impl<'c> LocalRequest<'c> { uri: Cow<'c, str> ) -> LocalRequest<'c> { // We set a dummy string for now and check the user's URI on dispatch. - let config = &client.rocket().config; - let state = &client.rocket().state; - let request = Request::new(config, state, method, Origin::dummy()); + let request = Request::new(client.rocket(), method, Origin::dummy()); // Set up any cookies we know about. if let Some(ref jar) = client.cookies { @@ -399,40 +397,46 @@ impl<'c> LocalRequest<'c> { uri: &str, data: Vec ) -> LocalResponse<'c> { + let maybe_uri = Origin::parse(uri); + // First, validate the URI, returning an error response (generated from // an error catcher) immediately if it's invalid. - if let Ok(uri) = Origin::parse(uri) { + if let Ok(uri) = maybe_uri { request.set_uri(uri.into_owned()); } else { error!("Malformed request URI: {}", uri); - let res = client.rocket().handle_error(Status::BadRequest, request); - return LocalResponse { _request: owned_request, response: res }; + return futures::executor::block_on(async move { + let res = client.rocket().handle_error(Status::BadRequest, request).await; + LocalResponse { _request: owned_request, response: res } + }) } - // Actually dispatch the request. - let response = client.rocket().dispatch(request, Data::new(data)); - - // If the client is tracking cookies, updates the internal cookie jar - // with the changes reflected by `response`. - if let Some(ref jar) = client.cookies { - let mut jar = jar.write().expect("LocalRequest::_dispatch() write lock"); - let current_time = time::now(); - for cookie in response.cookies() { - if let Some(expires) = cookie.expires() { - if expires <= current_time { - jar.force_remove(cookie); - continue; + futures::executor::block_on(async move { + // Actually dispatch the request. + let response = client.rocket().dispatch(request, Data::local(data)).await; + + // If the client is tracking cookies, updates the internal cookie jar + // with the changes reflected by `response`. + if let Some(ref jar) = client.cookies { + let mut jar = jar.write().expect("LocalRequest::_dispatch() write lock"); + let current_time = time::now(); + for cookie in response.cookies() { + if let Some(expires) = cookie.expires() { + if expires <= current_time { + jar.force_remove(cookie); + continue; + } } - } - jar.add(cookie.into_owned()); + jar.add(cookie.into_owned()); + } } - } - LocalResponse { - _request: owned_request, - response: response - } + LocalResponse { + _request: owned_request, + response: response + } + }) } } diff --git a/core/lib/src/logger.rs b/core/lib/src/logger.rs index 3dcf4faff1..81e62750e5 100644 --- a/core/lib/src/logger.rs +++ b/core/lib/src/logger.rs @@ -158,16 +158,16 @@ crate fn try_init(level: LoggingLevel, verbose: bool) -> bool { } push_max_level(level); -/* if let Err(e) = log::set_boxed_logger(Box::new(RocketLogger(level))) { + if let Err(e) = log::set_boxed_logger(Box::new(RocketLogger(level))) { if verbose { eprintln!("Logger failed to initialize: {}", e); } pop_max_level(); return false; - }*/ + } - false + true } use std::sync::atomic::{AtomicUsize, AtomicBool, Ordering}; diff --git a/core/lib/src/request/form/form.rs b/core/lib/src/request/form/form.rs index d3f56ed4ae..dd63caa2d0 100644 --- a/core/lib/src/request/form/form.rs +++ b/core/lib/src/request/form/form.rs @@ -1,9 +1,12 @@ use std::ops::Deref; +use futures::io::AsyncReadExt; + use crate::outcome::Outcome::*; use crate::request::{Request, form::{FromForm, FormItems, FormDataError}}; -use crate::data::{Outcome, Transform, Transformed, Data, FromData}; +use crate::data::{Outcome, Transform, Transformed, Data, FromData, TransformFuture, FromDataFuture}; use crate::http::{Status, uri::{Query, FromUriParam}}; +use crate::ext::AsyncReadExt as _; /// A data guard for parsing [`FromForm`] types strictly. /// @@ -184,7 +187,7 @@ impl<'f, T: FromForm<'f>> Form { /// /// All relevant warnings and errors are written to the console in Rocket /// logging format. -impl<'f, T: FromForm<'f>> FromData<'f> for Form { +impl<'f, T: FromForm<'f> + Send + 'f> FromData<'f> for Form { type Error = FormDataError<'f, T::Error>; type Owned = String; type Borrowed = str; @@ -192,30 +195,31 @@ impl<'f, T: FromForm<'f>> FromData<'f> for Form { fn transform( request: &Request<'_>, data: Data - ) -> Transform> { - use std::{cmp::min, io::Read}; - - let outcome = 'o: { - if !request.content_type().map_or(false, |ct| ct.is_form()) { - warn_!("Form data does not have form content type."); - break 'o Forward(data); - } + ) -> TransformFuture<'f, Self::Owned, Self::Error> { + if !request.content_type().map_or(false, |ct| ct.is_form()) { + warn_!("Form data does not have form content type."); + return Box::pin(futures::future::ready(Transform::Borrowed(Forward(data)))); + } - let limit = request.limits().forms; - let mut stream = data.open().take(limit); - let mut form_string = String::with_capacity(min(4096, limit) as usize); - if let Err(e) = stream.read_to_string(&mut form_string) { - break 'o Failure((Status::InternalServerError, FormDataError::Io(e))); + let limit = request.limits().forms; + let mut stream = data.open().take(limit); + Box::pin(async move { + let mut buf = Vec::new(); + if let Err(e) = stream.read_to_end(&mut buf).await { + return Transform::Borrowed(Failure((Status::InternalServerError, FormDataError::Io(e)))); } - break 'o Success(form_string); - }; - - Transform::Borrowed(outcome) + Transform::Borrowed(match String::from_utf8(buf) { + Ok(s) => Success(s), + Err(e) => Failure((Status::BadRequest, FormDataError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))), + }) + }) } - fn from_data(_: &Request<'_>, o: Transformed<'f, Self>) -> Outcome { - >::from_data(o.borrowed()?, true).map(Form) + fn from_data(_: &Request<'_>, o: Transformed<'f, Self>) -> FromDataFuture<'f, Self, Self::Error> { + Box::pin(futures::future::ready(o.borrowed().and_then(|data| { + >::from_data(data, true).map(Form) + }))) } } diff --git a/core/lib/src/request/form/from_form.rs b/core/lib/src/request/form/from_form.rs index dc59043091..08c5598518 100644 --- a/core/lib/src/request/form/from_form.rs +++ b/core/lib/src/request/form/from_form.rs @@ -93,7 +93,7 @@ use crate::request::FormItems; /// ``` pub trait FromForm<'f>: Sized { /// The associated error to be returned when parsing fails. - type Error; + type Error: Send; /// Parses an instance of `Self` from the iterator of form items `it`. /// diff --git a/core/lib/src/request/form/lenient.rs b/core/lib/src/request/form/lenient.rs index e7756ec48c..d25b3f1c33 100644 --- a/core/lib/src/request/form/lenient.rs +++ b/core/lib/src/request/form/lenient.rs @@ -1,7 +1,7 @@ use std::ops::Deref; use crate::request::{Request, form::{Form, FormDataError, FromForm}}; -use crate::data::{Data, Transform, Transformed, FromData, Outcome}; +use crate::data::{Data, Transformed, FromData, TransformFuture, FromDataFuture}; use crate::http::uri::{Query, FromUriParam}; /// A data guard for parsing [`FromForm`] types leniently. @@ -95,17 +95,19 @@ impl Deref for LenientForm { } } -impl<'f, T: FromForm<'f>> FromData<'f> for LenientForm { +impl<'f, T: FromForm<'f> + Send + 'f> FromData<'f> for LenientForm { type Error = FormDataError<'f, T::Error>; type Owned = String; type Borrowed = str; - fn transform(r: &Request<'_>, d: Data) -> Transform> { + fn transform(r: &Request<'_>, d: Data) -> TransformFuture<'f, Self::Owned, Self::Error> { >::transform(r, d) } - fn from_data(_: &Request<'_>, o: Transformed<'f, Self>) -> Outcome { - >::from_data(o.borrowed()?, false).map(LenientForm) + fn from_data(_: &Request<'_>, o: Transformed<'f, Self>) -> FromDataFuture<'f, Self, Self::Error> { + Box::pin(futures::future::ready(o.borrowed().and_then(|form| { + >::from_data(form, false).map(LenientForm) + }))) } } diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index 94a3cb9286..c1d55e73be 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -1,5 +1,4 @@ -use std::rc::Rc; -use std::cell::{Cell, RefCell}; +use std::sync::{Arc, RwLock, Mutex}; use std::net::{IpAddr, SocketAddr}; use std::fmt; use std::str; @@ -26,26 +25,26 @@ type Indices = (usize, usize); /// should likely only be used when writing [`FromRequest`] implementations. It /// contains all of the information for a given web request except for the body /// data. This includes the HTTP method, URI, cookies, headers, and more. -#[derive(Clone)] +//#[derive(Clone)] pub struct Request<'r> { - method: Cell, + method: RwLock, uri: Origin<'r>, headers: HeaderMap<'r>, remote: Option, crate state: RequestState<'r>, } -#[derive(Clone)] +//#[derive(Clone)] crate struct RequestState<'r> { crate config: &'r Config, crate managed: &'r Container, crate path_segments: SmallVec<[Indices; 12]>, crate query_items: Option>, - crate route: Cell>, - crate cookies: RefCell, + crate route: RwLock>, + crate cookies: Mutex>, crate accept: Storage>, crate content_type: Storage>, - crate cache: Rc, + crate cache: Arc, } #[derive(Clone)] @@ -59,26 +58,25 @@ impl<'r> Request<'r> { /// Create a new `Request` with the given `method` and `uri`. #[inline(always)] crate fn new<'s: 'r>( - config: &'r Config, - managed: &'r Container, + rocket: &'r Rocket, method: Method, uri: Origin<'s> ) -> Request<'r> { let mut request = Request { - method: Cell::new(method), + method: RwLock::new(method), uri: uri, headers: HeaderMap::new(), remote: None, state: RequestState { path_segments: SmallVec::new(), query_items: None, - config, - managed, - route: Cell::new(None), - cookies: RefCell::new(CookieJar::new()), + config: &rocket.config, + managed: &rocket.state, + route: RwLock::new(None), + cookies: Mutex::new(Some(CookieJar::new())), accept: Storage::new(), content_type: Storage::new(), - cache: Rc::new(Container::new()), + cache: Arc::new(Container::new()), } }; @@ -101,7 +99,7 @@ impl<'r> Request<'r> { /// ``` #[inline(always)] pub fn method(&self) -> Method { - self.method.get() + *self.method.read().unwrap() } /// Set the method of `self`. @@ -290,9 +288,13 @@ impl<'r> Request<'r> { /// ``` pub fn cookies(&self) -> Cookies<'_> { // FIXME: Can we do better? This is disappointing. - match self.state.cookies.try_borrow_mut() { - Ok(jar) => Cookies::new(jar, self.state.config.secret_key()), - Err(_) => { + let mut guard = self.state.cookies.lock().expect("cookies lock"); + match guard.take() { + Some(jar) => { + let mutex = &self.state.cookies; + Cookies::new(jar, self.state.config.secret_key(), move |jar| *mutex.lock().expect("cookies lock") = Some(jar)) + } + None => { error_!("Multiple `Cookies` instances are active at once."); info_!("An instance of `Cookies` must be dropped before another \ can be retrieved."); @@ -497,7 +499,7 @@ impl<'r> Request<'r> { /// # }); /// ``` pub fn route(&self) -> Option<&'r Route> { - self.state.route.get() + *self.state.route.read().unwrap() } /// Invokes the request guard implementation for `T`, returning its outcome. @@ -700,7 +702,7 @@ impl<'r> Request<'r> { pub fn example)>(method: Method, uri: &str, f: F) { let rocket = Rocket::custom(Config::development()); let uri = Origin::parse(uri).expect("invalid URI in example"); - let mut request = Request::new(&rocket.config, &rocket.state, method, uri); + let mut request = Request::new(&rocket, method, uri); f(&mut request); } @@ -771,79 +773,66 @@ impl<'r> Request<'r> { /// was `route`. Use during routing when attempting a given route. #[inline(always)] crate fn set_route(&self, route: &'r Route) { - self.state.route.set(Some(route)); + * self.state.route.write().unwrap() = Some(route); } /// Set the method of `self`, even when `self` is a shared reference. Used /// during routing to override methods for re-routing. #[inline(always)] crate fn _set_method(&self, method: Method) { - self.method.set(method); + *self.method.write().unwrap() = method; } /// Convert from Hyper types into a Rocket Request. crate fn from_hyp( - config: &'r Config, - managed: &'r Container, - request_parts: &hyper::Parts, + rocket: &'r Rocket, + h_method: hyper::Method, + h_headers: hyper::HeaderMap, + h_uri: hyper::Uri, + h_addr: SocketAddr, ) -> Result, String> { - - let h_uri = &request_parts.uri; - let h_headers = &request_parts.headers; - let h_version = &request_parts.version; - let h_method = &request_parts.method;; - -// if !h_uri.is_absolute() { -// return Err(format!("Bad URI: {}", h_uri)); -// }; + // TODO.async: Can we avoid this allocation? + // TODO.async: Assert that uri is "absolute" + // Get a copy of the URI for later use. + let uri = h_uri.to_string(); // Ensure that the method is known. TODO: Allow made-up methods? - let method = match Method::from_hyp(h_method) { + let method = match Method::from_hyp(&h_method) { Some(method) => method, - None => return Err(format!("Unknown method: {}", h_method)) + None => return Err(format!("Unknown or invalid method: {}", h_method)) }; // We need to re-parse the URI since we don't trust Hyper... :( - let uri = Origin::parse_owned(format!("{}", h_uri)).map_err(|e| e.to_string())?; + let uri = Origin::parse_owned(format!("{}", uri)).map_err(|e| e.to_string())?; // Construct the request object. - let mut request = Request::new(config, managed, method, uri); -// request.set_remote(match hyp_req.remote_addr() { -// Some(remote) => remote, -// None => return Err(String::from("Missing remote address")) -// }); + let mut request = Request::new(rocket, method, uri); + request.set_remote(h_addr); // Set the request cookies, if they exist. - let cookie_headers = h_headers.get_all("Cookie").iter(); - // TODO if cookie_headers.peek().is_some() { - let mut cookie_jar = CookieJar::new(); - for header in cookie_headers { - let raw_str = match std::str::from_utf8(header.as_bytes()) { - Ok(string) => string, - Err(_) => continue - }; - - for cookie_str in raw_str.split(';').map(|s| s.trim()) { - if let Some(cookie) = Cookies::parse_cookie(cookie_str) { - cookie_jar.add_original(cookie); - } + let mut cookie_jar = CookieJar::new(); + for header in h_headers.get_all("Cookie") { + // TODO.async: This used to only allow UTF-8 but now only allows ASCII + // (needs verification) + let raw_str = match header.to_str() { + Ok(string) => string, + Err(_) => continue + }; + + for cookie_str in raw_str.split(';').map(|s| s.trim()) { + if let Some(cookie) = Cookies::parse_cookie(cookie_str) { + cookie_jar.add_original(cookie); } } - - request.state.cookies = RefCell::new(cookie_jar); - // TODO } + } + request.state.cookies = Mutex::new(Some(cookie_jar)); // Set the rest of the headers. for (name, value) in h_headers.iter() { - - // TODO if let Some(header_values) = h_headers.get_all(hyp.name()) { - - // This is not totally correct since values needn't be UTF8. - let value_str = String::from_utf8_lossy(value.as_bytes()).into_owned(); - let header = Header::new(name.to_string(), value_str); - request.add_header(header); - - // TODO } + // This is not totally correct since values needn't be UTF8. + let value_str = String::from_utf8_lossy(value.as_bytes()).into_owned(); + let header = Header::new(name.to_string(), value_str); + request.add_header(header); } Ok(request) diff --git a/core/lib/src/response/responder.rs b/core/lib/src/response/responder.rs index 8a541dbcdc..783417bcc5 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -2,6 +2,8 @@ use std::fs::File; use std::io::{Cursor, BufReader}; use std::fmt; +use futures::compat::AsyncRead01CompatExt; + use crate::http::{Status, ContentType, StatusClass}; use crate::response::{self, Response, Body}; use crate::request::Request; @@ -242,10 +244,11 @@ impl Responder<'_> for Vec { /// Returns a response with a sized body for the file. Always returns `Ok`. impl Responder<'_> for File { fn respond_to(self, _: &Request<'_>) -> response::Result<'static> { - let (metadata, file) = (self.metadata(), BufReader::new(self)); + let metadata = self.metadata(); + let stream = BufReader::new(tokio::fs::File::from_std(self)).compat(); match metadata { - Ok(md) => Response::build().raw_body(Body::Sized(file, md.len())).ok(), - Err(_) => Response::build().streamed_body(file).ok() + Ok(md) => Response::build().raw_body(Body::Sized(stream, md.len())).ok(), + Err(_) => Response::build().streamed_body(stream).ok() } } } diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs index 64a2afaa2d..3339189f14 100644 --- a/core/lib/src/response/response.rs +++ b/core/lib/src/response/response.rs @@ -1,8 +1,13 @@ use std::{io, fmt, str}; use std::borrow::Cow; +use std::pin::Pin; + +use futures::future::{Future, FutureExt}; +use futures::io::{AsyncRead, AsyncReadExt}; use crate::response::Responder; use crate::http::{Header, HeaderMap, Status, ContentType, Cookie}; +use crate::ext::AsyncReadExt as _; /// The default size, in bytes, of a chunk for streamed responses. pub const DEFAULT_CHUNK_SIZE: u64 = 4096; @@ -59,31 +64,34 @@ impl Body { } } -impl Body { +impl Body { /// Attempts to read `self` into a `Vec` and returns it. If reading fails, /// returns `None`. - pub fn into_bytes(self) -> Option> { - let mut vec = Vec::new(); - let mut body = self.into_inner(); - if let Err(e) = body.read_to_end(&mut vec) { - error_!("Error reading body: {:?}", e); - return None; - } + pub fn into_bytes(self) -> impl Future>> { + Box::pin(async move { + let mut vec = Vec::new(); + let mut body = self.into_inner(); + if let Err(e) = body.read_to_end(&mut vec).await { + error_!("Error reading body: {:?}", e); + return None; + } - Some(vec) + Some(vec) + }) } /// Attempts to read `self` into a `String` and returns it. If reading or /// conversion fails, returns `None`. - pub fn into_string(self) -> Option { - self.into_bytes() - .and_then(|bytes| match String::from_utf8(bytes) { + pub fn into_string(self) -> impl Future> { + self.into_bytes().map(|bytes| { + bytes.and_then(|bytes| match String::from_utf8(bytes) { Ok(string) => Some(string), Err(e) => { error_!("Body is invalid UTF-8: {}", e); None } }) + }) } } @@ -350,7 +358,7 @@ impl<'r> ResponseBuilder<'r> { /// ``` #[inline(always)] pub fn sized_body(&mut self, body: B) -> &mut ResponseBuilder<'r> - where B: io::Read + io::Seek + 'r + where B: AsyncRead + io::Seek + Send + Unpin + 'r { self.response.set_sized_body(body); self @@ -376,7 +384,7 @@ impl<'r> ResponseBuilder<'r> { /// ``` #[inline(always)] pub fn streamed_body(&mut self, body: B) -> &mut ResponseBuilder<'r> - where B: io::Read + 'r + where B: AsyncRead + Send + 'r { self.response.set_streamed_body(body); self @@ -402,7 +410,7 @@ impl<'r> ResponseBuilder<'r> { /// # } /// ``` #[inline(always)] - pub fn chunked_body(&mut self, body: B, chunk_size: u64) + pub fn chunked_body(&mut self, body: B, chunk_size: u64) -> &mut ResponseBuilder<'r> { self.response.set_chunked_body(body, chunk_size); @@ -425,7 +433,7 @@ impl<'r> ResponseBuilder<'r> { /// .finalize(); /// ``` #[inline(always)] - pub fn raw_body(&mut self, body: Body) + pub fn raw_body(&mut self, body: Body) -> &mut ResponseBuilder<'r> { self.response.set_raw_body(body); @@ -560,7 +568,7 @@ impl<'r> ResponseBuilder<'r> { pub struct Response<'r> { status: Option, headers: HeaderMap<'r>, - body: Option>>, + body: Option>>>, } impl<'r> Response<'r> { @@ -889,7 +897,7 @@ impl<'r> Response<'r> { /// assert_eq!(response.body_string(), Some("Hello, world!".to_string())); /// ``` #[inline(always)] - pub fn body(&mut self) -> Option> { + pub fn body(&mut self) -> Option> { // Looks crazy, right? Needed so Rust infers lifetime correctly. Weird. match self.body.as_mut() { Some(body) => Some(match body.as_mut() { @@ -919,8 +927,14 @@ impl<'r> Response<'r> { /// assert!(response.body().is_none()); /// ``` #[inline(always)] - pub fn body_string(&mut self) -> Option { - self.take_body().and_then(Body::into_string) + pub fn body_string(&mut self) -> impl Future> + 'r { + let body = self.take_body(); + Box::pin(async move { + match body { + Some(body) => body.into_string().await, + None => None, + } + }) } /// Consumes `self's` body and reads it into a `Vec` of `u8` bytes. If @@ -941,8 +955,14 @@ impl<'r> Response<'r> { /// assert!(response.body().is_none()); /// ``` #[inline(always)] - pub fn body_bytes(&mut self) -> Option> { - self.take_body().and_then(Body::into_bytes) + pub fn body_bytes(&mut self) -> impl Future>> + 'r { + let body = self.take_body(); + Box::pin(async move { + match body { + Some(body) => body.into_bytes().await, + None => None, + } + }) } /// Moves the body of `self` out and returns it, if there is one, leaving no @@ -966,17 +986,17 @@ impl<'r> Response<'r> { /// assert!(response.body().is_none()); /// ``` #[inline(always)] - pub fn take_body(&mut self) -> Option>> { + pub fn take_body(&mut self) -> Option>>> { self.body.take() } - // Makes the `Read`er in the body empty but leaves the size of the body if + // Makes the `AsyncRead`er in the body empty but leaves the size of the body if // it exists. Only meant to be used to handle HEAD requests automatically. #[inline(always)] crate fn strip_body(&mut self) { if let Some(body) = self.take_body() { self.body = match body { - Body::Sized(_, n) => Some(Body::Sized(Box::new(io::empty()), n)), + Body::Sized(_, n) => Some(Body::Sized(Box::pin(io::empty()), n)), Body::Chunked(..) => None }; } @@ -1004,13 +1024,13 @@ impl<'r> Response<'r> { /// ``` #[inline] pub fn set_sized_body(&mut self, mut body: B) - where B: io::Read + io::Seek + 'r + where B: AsyncRead + io::Seek + Send + Unpin + 'r { let size = body.seek(io::SeekFrom::End(0)) .expect("Attempted to retrieve size by seeking, but failed."); body.seek(io::SeekFrom::Start(0)) .expect("Attempted to reset body by seeking after getting size."); - self.body = Some(Body::Sized(Box::new(body.take(size)), size)); + self.body = Some(Body::Sized(Box::pin(body.take(size)), size)); } /// Sets the body of `self` to be `body`, which will be streamed. The chunk @@ -1021,7 +1041,7 @@ impl<'r> Response<'r> { /// # Example /// /// ```rust - /// use std::io::{Read, repeat}; + /// use std::io::{AsyncRead, repeat}; /// use rocket::Response; /// /// let mut response = Response::new(); @@ -1029,7 +1049,7 @@ impl<'r> Response<'r> { /// assert_eq!(response.body_string(), Some("aaaaa".to_string())); /// ``` #[inline(always)] - pub fn set_streamed_body(&mut self, body: B) where B: io::Read + 'r { + pub fn set_streamed_body(&mut self, body: B) where B: AsyncRead + Send + 'r { self.set_chunked_body(body, DEFAULT_CHUNK_SIZE); } @@ -1039,7 +1059,7 @@ impl<'r> Response<'r> { /// # Example /// /// ```rust - /// use std::io::{Read, repeat}; + /// use std::io::{AsyncRead, repeat}; /// use rocket::Response; /// /// let mut response = Response::new(); @@ -1048,8 +1068,8 @@ impl<'r> Response<'r> { /// ``` #[inline(always)] pub fn set_chunked_body(&mut self, body: B, chunk_size: u64) - where B: io::Read + 'r { - self.body = Some(Body::Chunked(Box::new(body), chunk_size)); + where B: AsyncRead + Send + 'r { + self.body = Some(Body::Chunked(Box::pin(body), chunk_size)); } /// Sets the body of `self` to be `body`. This method should typically not @@ -1070,10 +1090,11 @@ impl<'r> Response<'r> { /// assert_eq!(response.body_string(), Some("Hello!".to_string())); /// ``` #[inline(always)] - pub fn set_raw_body(&mut self, body: Body) { + pub fn set_raw_body(&mut self, body: Body) + where T: AsyncRead + Send + Unpin + 'r { self.body = Some(match body { - Body::Sized(b, n) => Body::Sized(Box::new(b.take(n)), n), - Body::Chunked(b, n) => Body::Chunked(Box::new(b), n), + Body::Sized(b, n) => Body::Sized(Box::pin(b.take(n)), n), + Body::Chunked(b, n) => Body::Chunked(Box::pin(b), n), }); } diff --git a/core/lib/src/response/status.rs b/core/lib/src/response/status.rs index a8d24403ad..8bce41a570 100644 --- a/core/lib/src/response/status.rs +++ b/core/lib/src/response/status.rs @@ -13,7 +13,7 @@ use std::collections::hash_map::DefaultHasher; use crate::request::Request; use crate::response::{Responder, Response}; use crate::http::hyper::header; -use crate::http::{Header, Status}; +use crate::http::Status; /// Sets the status of the response to 201 (Created). /// @@ -47,10 +47,8 @@ impl<'r, R: Responder<'r>> Responder<'r> for Created { build.merge(responder.respond_to(req)?); } - build.status(Status::Created).header(Header::new( - header::LOCATION.as_str(), - self.0 - )).ok() + // TODO.async: Using a raw header + build.status(Status::Created).raw_header(header::LOCATION.as_str(), self.0).ok() } } @@ -67,16 +65,12 @@ impl<'r, R: Responder<'r> + Hash> Responder<'r> for Created { let hash = hasher.finish().to_string(); build.merge(responder.respond_to(req)?); - build.header(Header::new( - header::ETAG.as_str(), - hash, // TODO header::EntityTag::strong(hash) - )); + // TODO.async: Using a raw header + build.raw_header(header::ETAG.as_str(), format!("\"{}\"", hash)); } - build.status(Status::Created).header(Header::new( - header::LOCATION.as_str(), - self.0 - )).ok() + // TODO.async: Using a raw header + build.status(Status::Created).raw_header(header::LOCATION.as_str(), self.0).ok() } } diff --git a/core/lib/src/response/stream.rs b/core/lib/src/response/stream.rs index 84e106cc57..37dcfbdbbb 100644 --- a/core/lib/src/response/stream.rs +++ b/core/lib/src/response/stream.rs @@ -1,19 +1,20 @@ -use std::io::Read; use std::fmt::{self, Debug}; +use futures::io::AsyncRead; + use crate::request::Request; use crate::response::{Response, Responder, DEFAULT_CHUNK_SIZE}; use crate::http::Status; -/// Streams a response to a client from an arbitrary `Read`er type. +/// Streams a response to a client from an arbitrary `AsyncRead`er type. /// /// The client is sent a "chunked" response, where the chunk size is at most /// 4KiB. This means that at most 4KiB are stored in memory while the response /// is being sent. This type should be used when sending responses that are /// arbitrarily large in size, such as when streaming from a local socket. -pub struct Stream(T, u64); +pub struct Stream(T, u64); -impl Stream { +impl Stream { /// Create a new stream from the given `reader` and sets the chunk size for /// each streamed chunk to `chunk_size` bytes. /// @@ -34,7 +35,7 @@ impl Stream { } } -impl Debug for Stream { +impl Debug for Stream { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Stream({:?})", self.0) } @@ -54,7 +55,7 @@ impl Debug for Stream { /// # #[allow(unused_variables)] /// let response = Stream::from(io::stdin()); /// ``` -impl From for Stream { +impl From for Stream { fn from(reader: T) -> Self { Stream(reader, DEFAULT_CHUNK_SIZE) } @@ -68,7 +69,7 @@ impl From for Stream { /// 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. -impl<'r, T: Read + 'r> Responder<'r> for Stream { +impl<'r, T: AsyncRead + Send + 'r> Responder<'r> for Stream { fn respond_to(self, _: &Request<'_>) -> Result, Status> { Response::build().chunked_body(self.0, self.1).ok() } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 707845a538..ecf68d3b13 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -1,18 +1,20 @@ use std::collections::HashMap; use std::convert::From; -use std::str::{from_utf8, FromStr}; use std::cmp::min; +use std::io; use std::mem; use std::net::ToSocketAddrs; use std::sync::Arc; +use std::pin::Pin; -use futures::{Future, Stream}; -use futures::future::{self, FutureResult}; +use futures::compat::Compat; +use futures::future::{Future, FutureExt, TryFutureExt}; +use futures::io::AsyncReadExt; use yansi::Paint; use state::Container; use tokio::net::TcpListener; -use tokio::prelude::{Future as _, Stream as _}; +use tokio::prelude::Stream as _; #[cfg(feature = "tls")] use crate::http::tls::TlsAcceptor; @@ -58,13 +60,13 @@ impl std::ops::Deref for RocketHyperService { impl hyper::MakeService for RocketHyperService { type ReqBody = hyper::Body; type ResBody = hyper::Body; - type Error = hyper::Error; + type Error = io::Error; type Service = RocketHyperService; - type Future = FutureResult; + type Future = Compat>>; type MakeError = Self::Error; fn make_service(&mut self, _: Ctx) -> Self::Future { - future::ok(RocketHyperService { rocket: self.rocket.clone() }) + futures::future::ok(RocketHyperService { rocket: self.rocket.clone() }).compat() } } @@ -72,8 +74,8 @@ impl hyper::MakeService for RocketHyperService { impl hyper::Service for RocketHyperService { type ReqBody = hyper::Body; type ResBody = hyper::Body; - type Error = hyper::Error; - type Future = Box, Error = Self::Error> + Send>; + type Error = io::Error; + type Future = Compat, Self::Error>> + Send>>>; // This function tries to hide all of the Hyper-ness from Rocket. It // essentially converts Hyper types into Rocket types, then calls the @@ -84,44 +86,120 @@ impl hyper::Service for RocketHyperService { &mut self, hyp_req: hyper::Request, ) -> Self::Future { - let (parts, body) = hyp_req.into_parts(); - - // Convert the Hyper request into a Rocket request. - let req_res = Request::from_hyp(&self.config, &self.state, &parts); - let mut req = match req_res { - Ok(req) => req, - Err(e) => { - error!("Bad incoming request: {}", e); - // TODO: We don't have a request to pass in, so we just - // fabricate one. This is weird. We should let the user know - // that we failed to parse a request (by invoking some special - // handler) instead of doing this. - let dummy = Request::new(&self.config, &self.state, Method::Get, Origin::dummy()); - let r = self.handle_error(Status::BadRequest, &dummy); - return Box::new(future::ok(hyper::Response::from(r))); + let rocket = self.rocket.clone(); + async move { + // Get all of the information from Hyper. + let (h_parts, h_body) = hyp_req.into_parts(); + + // TODO.async: Get the client address somehow. + let h_addr = "0.0.0.0:0".parse().expect("socket addr"); + + // Convert the Hyper request into a Rocket request. + let req_res = Request::from_hyp(&rocket, h_parts.method, h_parts.headers, h_parts.uri, h_addr); + let mut req = match req_res { + Ok(req) => req, + Err(e) => { + error!("Bad incoming request: {}", e); + // TODO: We don't have a request to pass in, so we just + // fabricate one. This is weird. We should let the user know + // that we failed to parse a request (by invoking some special + // handler) instead of doing this. + let dummy = Request::new(&rocket, Method::Get, Origin::dummy()); + let r = rocket.handle_error(Status::BadRequest, &dummy).await; + return rocket.issue_response(r).await; + } + }; + + // Retrieve the data from the hyper body. + let data = Data::from_hyp(h_body).await; + + // Dispatch the request to get a response, then write that response out. + let r = rocket.dispatch(&mut req, data).await; + rocket.issue_response(r).await + }.boxed().compat() + } +} + +impl Rocket { + // TODO.async: Reconsider io::Result + #[inline] + fn issue_response<'r>(&self, response: Response<'r>) -> impl Future>> + 'r { + let result = self.write_response(response); + Box::pin(async move { + match result.await { + Ok(r) => { + info_!("{}", Paint::green("Response succeeded.")); + Ok(r) + } + Err(e) => { + error_!("Failed to write response: {:?}.", e); + Err(e) + } } - }; + }) + } - let this = self.clone(); + #[inline] + fn write_response<'r>( + &self, + mut response: Response<'r>, + ) -> impl Future>> + 'r { + Box::pin(async move { + let mut hyp_res = hyper::Response::builder(); + hyp_res.status(response.status().code); + + for header in response.headers().iter() { + let name = header.name.as_str(); + let value = header.value.as_bytes(); + hyp_res.header(name, value); + } - let response = body.concat2() - .map(move |chunk| { - let body = chunk.iter().rev().cloned().collect::>(); - let data = Data::new(body); + let body = match response.body() { + None => { + hyp_res.header(header::CONTENT_LENGTH, "0"); + hyper::Body::empty() + } + Some(Body::Sized(body, size)) => { + hyp_res.header(header::CONTENT_LENGTH, size.to_string()); + + // TODO.async: Stream the data instead of buffering. + // TODO.async: Possible truncation (u64 -> usize) + let mut buffer = Vec::with_capacity(size as usize); + body.read_to_end(&mut buffer).await?; + hyper::Body::from(buffer) + } + Some(Body::Chunked(body, _chunk_size)) => { + // // This _might_ happen on a 32-bit machine! + // if chunk_size > (usize::max_value() as u64) { + // let msg = "chunk size exceeds limits of usize type"; + // return Err(io::Error::new(io::ErrorKind::Other, msg)); + // } + // + // // The buffer stores the current chunk being written out. + // let mut buffer = vec![0; chunk_size as usize]; + // let mut stream = hyp_res.start()?; + // loop { + // match body.read_max(&mut buffer)? { + // 0 => break, + // n => stream.write_all(&buffer[..n])?, + // } + // } + // + // stream.end() - // TODO: Due to life time constraints the clone of the service has been made. - // TODO: It should not be necessary but it is required to find a better solution - let mut req = Request::from_hyp(&this.config, &this.state, &parts).unwrap(); - // Dispatch the request to get a response, then write that response out. - let response = this.dispatch(&mut req, data); - hyper::Response::from(response) - }); + // TODO.async: Stream the data instead of buffering. + let mut buffer = Vec::new(); + body.read_to_end(&mut buffer).await?; + hyper::Body::from(buffer) + } + }; - Box::new(response) + Ok(hyp_res.body(body).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?) + }) } } -impl RocketHyperService { +impl Rocket { /// Preprocess the request for Rocket things. Currently, this means: /// /// * Rewriting the method in the request if _method form field exists. @@ -135,7 +213,7 @@ impl RocketHyperService { let is_form = req.content_type().map_or(false, |ct| ct.is_form()); if is_form && req.method() == Method::Post && data_len >= min_len { - if let Ok(form) = from_utf8(&data.peek()[..min(data_len, max_len)]) { + if let Ok(form) = std::str::from_utf8(&data.peek()[..min(data_len, max_len)]) { let method: Option> = FormItems::from(form) .filter(|item| item.key.as_str() == "_method") .map(|item| item.value.parse()) @@ -149,71 +227,76 @@ impl RocketHyperService { } #[inline] - crate fn dispatch<'s, 'r>( + crate fn dispatch<'s, 'r: 's>( &'s self, request: &'r mut Request<'s>, data: Data - ) -> Response<'r> { - info!("{}:", request); + ) -> impl Future> + 's { + async move { + info!("{}:", request); - // Do a bit of preprocessing before routing. - self.preprocess_request(request, &data); + // Do a bit of preprocessing before routing. + self.preprocess_request(request, &data); - // Run the request fairings. - self.fairings.handle_request(request, &data); + // Run the request fairings. + self.fairings.handle_request(request, &data); - // Remember if the request is a `HEAD` request for later body stripping. - let was_head_request = request.method() == Method::Head; + // Remember if the request is a `HEAD` request for later body stripping. + let was_head_request = request.method() == Method::Head; - // Route the request and run the user's handlers. - let mut response = self.route_and_process(request, data); + // Route the request and run the user's handlers. + let mut response = self.route_and_process(request, data).await; - // Add a default 'Server' header if it isn't already there. - // TODO: If removing Hyper, write out `Date` header too. - if !response.headers().contains("Server") { - response.set_header(Header::new("Server", "Rocket")); - } + // Add a default 'Server' header if it isn't already there. + // TODO: If removing Hyper, write out `Date` header too. + if !response.headers().contains("Server") { + response.set_header(Header::new("Server", "Rocket")); + } - // Run the response fairings. - self.fairings.handle_response(request, &mut response); + // Run the response fairings. + self.fairings.handle_response(request, &mut response); - // Strip the body if this is a `HEAD` request. - if was_head_request { - response.strip_body(); - } + // Strip the body if this is a `HEAD` request. + if was_head_request { + response.strip_body(); + } - response + response + } } /// Route the request and process the outcome to eventually get a response. - fn route_and_process<'s, 'r>( + fn route_and_process<'s, 'r: 's>( &'s self, request: &'r Request<'s>, data: Data - ) -> Response<'r> { - match self.route(request, data) { - Outcome::Success(mut response) => { - // A user's route responded! Set the cookies. - for cookie in request.cookies().delta() { - response.adjoin_header(cookie); + ) -> impl Future> + Send + 's { + async move { + match self.route(request, data).await { + Outcome::Success(mut response) => { + // A user's route responded! Set the cookies. + for cookie in request.cookies().delta() { + response.adjoin_header(cookie); + } + + response } - - response - } - Outcome::Forward(data) => { - // There was no matching route. Autohandle `HEAD` requests. - if request.method() == Method::Head { - info_!("Autohandling {} request.", Paint::default("HEAD").bold()); - - // Dispatch the request again with Method `GET`. - request._set_method(Method::Get); - self.route_and_process(request, data) - } else { - // No match was found and it can't be autohandled. 404. - self.handle_error(Status::NotFound, request) + Outcome::Forward(data) => { + // There was no matching route. Autohandle `HEAD` requests. + if request.method() == Method::Head { + info_!("Autohandling {} request.", Paint::default("HEAD").bold()); + + // Dispatch the request again with Method `GET`. + request._set_method(Method::Get); + let try_next: Pin + Send>> = Box::pin(self.route_and_process(request, data)); + try_next.await + } else { + // No match was found and it can't be autohandled. 404. + self.handle_error(Status::NotFound, request).await + } } + Outcome::Failure(status) => self.handle_error(status, request).await } - Outcome::Failure(status) => self.handle_error(status, request) } } @@ -229,32 +312,34 @@ impl RocketHyperService { // (ensuring `handler` takes an immutable borrow), any caller to `route` // should be able to supply an `&mut` and retain an `&` after the call. #[inline] - crate fn route<'s, 'r>( + crate fn route<'s, 'r: 's>( &'s self, request: &'r Request<'s>, mut data: Data, - ) -> handler::Outcome<'r> { - // Go through the list of matching routes until we fail or succeed. - let matches = self.router.route(request); - for route in matches { - // Retrieve and set the requests parameters. - info_!("Matched: {}", route); - request.set_route(route); - - // Dispatch the request to the handler. - let outcome = route.handler.handle(request, data); - - // Check if the request processing completed or if the request needs - // to be forwarded. If it does, continue the loop to try again. - info_!("{} {}", Paint::default("Outcome:").bold(), outcome); - match outcome { - o@Outcome::Success(_) | o@Outcome::Failure(_) => return o, - Outcome::Forward(unused_data) => data = unused_data, - }; - } + ) -> impl Future> + 's { + async move { + // Go through the list of matching routes until we fail or succeed. + let matches = self.router.route(request); + for route in matches { + // Retrieve and set the requests parameters. + info_!("Matched: {}", route); + request.set_route(route); + + // Dispatch the request to the handler. + let outcome = route.handler.handle(request, data).await; + + // Check if the request processing completed (Some) or if the request needs + // to be forwarded. If it does, continue the loop (None) to try again. + info_!("{} {}", Paint::default("Outcome:").bold(), outcome); + match outcome { + o@Outcome::Success(_) | o@Outcome::Failure(_) => return o, + Outcome::Forward(unused_data) => data = unused_data, + } + } - error_!("No matching routes for {}.", request); - Outcome::Forward(data) + error_!("No matching routes for {}.", request); + Outcome::Forward(data) + } } // Finds the error catcher for the status `status` and executes it for the @@ -262,48 +347,35 @@ impl RocketHyperService { // catcher is called. If the catcher fails to return a good response, the // 500 catcher is executed. If there is no registered catcher for `status`, // the default catcher is used. - crate fn handle_error<'r>( - &self, + crate fn handle_error<'s, 'r: 's>( + &'s self, status: Status, - req: &'r Request<'_> - ) -> Response<'r> { - warn_!("Responding with {} catcher.", Paint::red(&status)); - - // Try to get the active catcher but fallback to user's 500 catcher. - let catcher = self.catchers.get(&status.code).unwrap_or_else(|| { - error_!("No catcher found for {}. Using 500 catcher.", status); - self.catchers.get(&500).expect("500 catcher.") - }); - - // Dispatch to the user's catcher. If it fails, use the default 500. - catcher.handle(req).unwrap_or_else(|err_status| { - error_!("Catcher failed with status: {}!", err_status); - warn_!("Using default 500 error catcher."); - let default = self.default_catchers.get(&500).expect("Default 500"); - default.handle(req).expect("Default 500 response.") - }) + req: &'r Request<'s> + ) -> impl Future> + 's { + async move { + warn_!("Responding with {} catcher.", Paint::red(&status)); + + // Try to get the active catcher but fallback to user's 500 catcher. + let catcher = self.catchers.get(&status.code).unwrap_or_else(|| { + error_!("No catcher found for {}. Using 500 catcher.", status); + self.catchers.get(&500).expect("500 catcher.") + }); + + // Dispatch to the user's catcher. If it fails, use the default 500. + match catcher.handle(req).await { + Ok(r) => return r, + Err(err_status) => { + error_!("Catcher failed with status: {}!", err_status); + warn_!("Using default 500 error catcher."); + let default = self.default_catchers.get(&500).expect("Default 500"); + default.handle(req).await.expect("Default 500 response.") + } + } + } } } impl Rocket { - - #[inline] - crate fn dispatch<'s, 'r>( - &'s self, - request: &'r mut Request<'s>, - data: Data - ) -> Response<'r> { - unimplemented!("TODO") - } - - crate fn handle_error<'r>( - &self, - status: Status, - req: &'r Request<'_> - ) -> Response<'r> { - unimplemented!("TODO") - } - /// Create a new `Rocket` application using the configuration information in /// `Rocket.toml`. If the file does not exist or if there is an I/O error /// reading the file, the defaults are used. See the [`config`] @@ -482,7 +554,6 @@ impl Rocket { panic!("Invalid mount point."); } - let mut router = self.router.clone(); for mut route in routes.into() { let path = route.uri.clone(); if let Err(e) = route.set_uri(base_uri.clone(), path) { @@ -491,11 +562,9 @@ impl Rocket { } info_!("{}", route); - router.add(route); + self.router.add(route); } - self.router = router; - self } @@ -656,6 +725,8 @@ impl Rocket { /// # } /// ``` pub fn launch(mut self) -> LaunchError { + #[cfg(feature = "tls")] use crate::http::tls; + self = match self.prelaunch_check() { Ok(rocket) => rocket, Err(launch_error) => return launch_error @@ -670,30 +741,36 @@ impl Rocket { .build() .expect("Cannot build runtime!"); - let threads = self.config.workers as usize; - - let full_addr = format!("{}:{}", self.config.address, self.config.port) - .to_socket_addrs() - .expect("A valid socket address") - .next() - .unwrap(); + let full_addr = format!("{}:{}", self.config.address, self.config.port); + let addrs = match full_addr.to_socket_addrs() { + Ok(a) => a.collect::>(), + // TODO.async: Reconsider this error type + Err(e) => return From::from(io::Error::new(io::ErrorKind::Other, e)), + }; - let listener = match TcpListener::bind(&full_addr) { + let listener = match TcpListener::bind(&addrs[0]) { Ok(listener) => listener, Err(e) => return LaunchError::new(LaunchErrorKind::Bind(e)), }; // Determine the address and port we actually binded to. match listener.local_addr() { - Ok(server_addr) => /* TODO self.config.port = */ server_addr.port(), + Ok(server_addr) => self.config.port = server_addr.port(), Err(e) => return LaunchError::from(e), - }; + } + + // TODO.async Move all of this to http crate somewhere + // TODO.async Is boxing everything really the best we can do here? + trait AsyncReadWrite: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send { } + impl AsyncReadWrite for T where T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send { } let proto; - let incoming; + let incoming: Box, Error=std::io::Error> + Send>; #[cfg(feature = "tls")] { + use tokio::prelude::Future; + // TODO.async: Can/should we make the clone unnecessary (by reference, or by moving out?) if let Some(tls) = self.config.tls.clone() { proto = "https://"; @@ -706,21 +783,26 @@ impl Rocket { incoming = Box::new(listener.incoming().and_then(move |stream| { config.accept(stream) - .map(|stream| Box::new(stream)) + .map(|stream| Box::new(stream) as Box) })); - } - else { + } else { proto = "http://"; - incoming = Box::new(listener.incoming().map(|stream| Box::new(stream))); + incoming = Box::new(listener.incoming().map(|stream| Box::new(stream) as Box)); } } + // TODO.async: Duplicated code #[cfg(not(feature = "tls"))] { proto = "http://"; - incoming = Box::new(listener.incoming().map(|stream| Box::new(stream))); + incoming = Box::new(listener.incoming().map(|stream| Box::new(stream) as Box)); } + // TODO.async: Set the keep-alive. +// // Set the keep-alive. +// let timeout = self.config.keep_alive.map(|s| Duration::from_secs(s as u64)); +// server.keep_alive(timeout); + // Freeze managed state for synchronization-free accesses later. self.state.freeze(); @@ -746,7 +828,7 @@ impl Rocket { // TODO.async: Use with_graceful_shutdown, and let launch() return a Result<(), Error> runtime.block_on(server).expect("TODO.async handle error"); - unreachable!("the call to `handle_threads` should block on success") + unreachable!("the call to `block_on` should block on success") } /// Returns an iterator over all of the routes mounted on this instance of @@ -831,34 +913,3 @@ impl Rocket { &self.config } } - -// TODO: consider try_from here? -impl<'a> From> for hyper::Response { - fn from(mut response: Response<'_>) -> Self { - - let mut builder = hyper::Response::builder(); - builder.status(hyper::StatusCode::from_u16(response.status().code).expect("")); - - for header in response.headers().iter() { - // FIXME: Using hyper here requires two allocations. - let name = hyper::HeaderName::from_str(&header.name.into_string()).unwrap(); - let value = hyper::HeaderValue::from_bytes(header.value.as_bytes()).unwrap(); - builder.header(name, value); - } - - match response.body() { - None => { - builder.body(hyper::Body::empty()) - }, - Some(Body::Sized(body, size)) => { - let mut buffer = Vec::with_capacity(size as usize); - body.read_to_end(&mut buffer); - builder.header(header::CONTENT_LENGTH, hyper::HeaderValue::from(size)); - builder.body(hyper::Body::from(buffer)) - }, - Some(Body::Chunked(mut body, chunk_size)) => { - unimplemented!() - } - }.unwrap() - } -} diff --git a/core/lib/src/router/mod.rs b/core/lib/src/router/mod.rs index 771f2399d0..d2e8c3b94a 100644 --- a/core/lib/src/router/mod.rs +++ b/core/lib/src/router/mod.rs @@ -3,6 +3,8 @@ mod route; use std::collections::hash_map::HashMap; +use futures::future::{Future, FutureExt}; + pub use self::route::Route; use crate::request::Request; @@ -12,11 +14,11 @@ use crate::http::Method; type Selector = Method; // A handler to use when one is needed temporarily. -crate fn dummy_handler<'r>(r: &'r crate::Request<'_>, _: crate::Data) -> crate::handler::Outcome<'r> { - crate::Outcome::from(r, ()) +crate fn dummy_handler<'r>(r: &'r Request<'_>, _: crate::Data) -> std::pin::Pin> + Send + 'r>> { + futures::future::ready(crate::Outcome::from(r, ())).boxed() } -#[derive(Default, Clone)] +#[derive(Default)] pub struct Router { routes: HashMap>, } diff --git a/examples/cookies/src/main.rs b/examples/cookies/src/main.rs index 1232def393..a53740c128 100644 --- a/examples/cookies/src/main.rs +++ b/examples/cookies/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/errors/src/main.rs b/examples/errors/src/main.rs index 3aa27066d7..1f5a24c1e4 100644 --- a/examples/errors/src/main.rs +++ b/examples/errors/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/form_kitchen_sink/src/main.rs b/examples/form_kitchen_sink/src/main.rs index 9b841c3088..32d6cbfa79 100644 --- a/examples/form_kitchen_sink/src/main.rs +++ b/examples/form_kitchen_sink/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/hello_world/src/main.rs b/examples/hello_world/src/main.rs index 6c1111f4dd..de05d854b2 100644 --- a/examples/hello_world/src/main.rs +++ b/examples/hello_world/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; From cf68c9b1ab4a4e4be13ce84fc631a28866092b48 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Thu, 16 May 2019 17:22:51 -0700 Subject: [PATCH 04/25] Add async_await feature to examples to make them more check-able. --- examples/config/tests/development.rs | 2 +- examples/config/tests/production.rs | 2 +- examples/config/tests/staging.rs | 2 +- examples/content_types/src/main.rs | 2 +- examples/fairings/src/main.rs | 2 +- examples/form_validation/src/main.rs | 2 +- examples/handlebars_templates/src/main.rs | 2 +- examples/hello_2015/src/main.rs | 2 +- examples/hello_2018/src/main.rs | 2 +- examples/hello_person/src/main.rs | 2 +- examples/json/src/main.rs | 2 +- examples/managed_queue/src/main.rs | 2 +- examples/msgpack/src/main.rs | 2 +- examples/optional_redirect/src/main.rs | 2 +- examples/pastebin/src/main.rs | 2 +- examples/query_params/src/main.rs | 2 +- examples/ranking/src/main.rs | 2 +- examples/raw_sqlite/src/main.rs | 2 +- examples/raw_upload/src/main.rs | 2 +- examples/redirect/src/main.rs | 2 +- examples/request_guard/src/main.rs | 2 +- examples/request_local_state/src/main.rs | 2 +- examples/session/src/main.rs | 2 +- examples/state/src/main.rs | 2 +- examples/stream/src/main.rs | 2 +- examples/tera_templates/src/main.rs | 2 +- examples/testing/src/main.rs | 2 +- examples/tls/src/main.rs | 2 +- examples/todo/src/main.rs | 2 +- examples/uuid/src/main.rs | 2 +- 30 files changed, 30 insertions(+), 30 deletions(-) diff --git a/examples/config/tests/development.rs b/examples/config/tests/development.rs index 1b5753bc3b..53dd3ae228 100644 --- a/examples/config/tests/development.rs +++ b/examples/config/tests/development.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/config/tests/production.rs b/examples/config/tests/production.rs index 7208a7a462..262c29ff34 100644 --- a/examples/config/tests/production.rs +++ b/examples/config/tests/production.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/config/tests/staging.rs b/examples/config/tests/staging.rs index fc88fee86b..e33a05173e 100644 --- a/examples/config/tests/staging.rs +++ b/examples/config/tests/staging.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/content_types/src/main.rs b/examples/content_types/src/main.rs index ac1d379e82..35b63b8950 100644 --- a/examples/content_types/src/main.rs +++ b/examples/content_types/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; #[macro_use] extern crate serde_derive; diff --git a/examples/fairings/src/main.rs b/examples/fairings/src/main.rs index d1a9e44cfa..7b7ca500e1 100644 --- a/examples/fairings/src/main.rs +++ b/examples/fairings/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/form_validation/src/main.rs b/examples/form_validation/src/main.rs index 0495409bdb..bddca2ae84 100644 --- a/examples/form_validation/src/main.rs +++ b/examples/form_validation/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/handlebars_templates/src/main.rs b/examples/handlebars_templates/src/main.rs index 829940f9cd..465cf82523 100644 --- a/examples/handlebars_templates/src/main.rs +++ b/examples/handlebars_templates/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; #[macro_use] extern crate serde_derive; diff --git a/examples/hello_2015/src/main.rs b/examples/hello_2015/src/main.rs index 1001fb529c..e77a36a0d0 100644 --- a/examples/hello_2015/src/main.rs +++ b/examples/hello_2015/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/hello_2018/src/main.rs b/examples/hello_2018/src/main.rs index d8d7c4738d..abe16b4493 100644 --- a/examples/hello_2018/src/main.rs +++ b/examples/hello_2018/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[cfg(test)] mod tests; diff --git a/examples/hello_person/src/main.rs b/examples/hello_person/src/main.rs index 96c4ae2e06..0bd7c99987 100644 --- a/examples/hello_person/src/main.rs +++ b/examples/hello_person/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/json/src/main.rs b/examples/json/src/main.rs index 65698c944b..20bcb71ef4 100644 --- a/examples/json/src/main.rs +++ b/examples/json/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; #[macro_use] extern crate rocket_contrib; diff --git a/examples/managed_queue/src/main.rs b/examples/managed_queue/src/main.rs index d0588bc62f..9ca5626421 100644 --- a/examples/managed_queue/src/main.rs +++ b/examples/managed_queue/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/msgpack/src/main.rs b/examples/msgpack/src/main.rs index 2af8c51db7..d500ac2172 100644 --- a/examples/msgpack/src/main.rs +++ b/examples/msgpack/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; #[macro_use] extern crate serde_derive; diff --git a/examples/optional_redirect/src/main.rs b/examples/optional_redirect/src/main.rs index 8c81c24ae6..c3b0c0085d 100644 --- a/examples/optional_redirect/src/main.rs +++ b/examples/optional_redirect/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/pastebin/src/main.rs b/examples/pastebin/src/main.rs index bc0d23192f..0ac155c282 100644 --- a/examples/pastebin/src/main.rs +++ b/examples/pastebin/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/query_params/src/main.rs b/examples/query_params/src/main.rs index 518902cefa..b1abf531b7 100644 --- a/examples/query_params/src/main.rs +++ b/examples/query_params/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/ranking/src/main.rs b/examples/ranking/src/main.rs index 2267fc3d2c..26e1700da9 100644 --- a/examples/ranking/src/main.rs +++ b/examples/ranking/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/raw_sqlite/src/main.rs b/examples/raw_sqlite/src/main.rs index 37e582cc32..e5e0e652d2 100644 --- a/examples/raw_sqlite/src/main.rs +++ b/examples/raw_sqlite/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/raw_upload/src/main.rs b/examples/raw_upload/src/main.rs index 15bc96d737..728ffa18e6 100644 --- a/examples/raw_upload/src/main.rs +++ b/examples/raw_upload/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/redirect/src/main.rs b/examples/redirect/src/main.rs index cfd7db2787..3df8f7bf2a 100644 --- a/examples/redirect/src/main.rs +++ b/examples/redirect/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/request_guard/src/main.rs b/examples/request_guard/src/main.rs index 48efc9377b..6ff46d76cf 100644 --- a/examples/request_guard/src/main.rs +++ b/examples/request_guard/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/request_local_state/src/main.rs b/examples/request_local_state/src/main.rs index 8bfe071e78..670c78b7a4 100644 --- a/examples/request_local_state/src/main.rs +++ b/examples/request_local_state/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/session/src/main.rs b/examples/session/src/main.rs index d874495c09..8899feb859 100644 --- a/examples/session/src/main.rs +++ b/examples/session/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/state/src/main.rs b/examples/state/src/main.rs index 9bd48352b0..5109da7965 100644 --- a/examples/state/src/main.rs +++ b/examples/state/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/stream/src/main.rs b/examples/stream/src/main.rs index d7a1b88600..68bf167184 100644 --- a/examples/stream/src/main.rs +++ b/examples/stream/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/tera_templates/src/main.rs b/examples/tera_templates/src/main.rs index 83c53122c7..061ef2b8cf 100644 --- a/examples/tera_templates/src/main.rs +++ b/examples/tera_templates/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; #[macro_use] extern crate serde_derive; diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 647d76b700..9027cda38a 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/tls/src/main.rs b/examples/tls/src/main.rs index 6c1111f4dd..de05d854b2 100644 --- a/examples/tls/src/main.rs +++ b/examples/tls/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/examples/todo/src/main.rs b/examples/todo/src/main.rs index 8e17a21dbf..8ce5fd2215 100644 --- a/examples/todo/src/main.rs +++ b/examples/todo/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; #[macro_use] extern crate diesel; diff --git a/examples/uuid/src/main.rs b/examples/uuid/src/main.rs index 38639da90f..0a0bb27188 100644 --- a/examples/uuid/src/main.rs +++ b/examples/uuid/src/main.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; #[macro_use] extern crate lazy_static; From 6660fee32bb9e3a8f691cb8053767807adf60262 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 10 Jul 2019 23:38:58 -0700 Subject: [PATCH 05/25] Use AddrIncoming/AddrStream. This lets us keep support for keep-alive and remote address while doing other work on async, at the cost of TLS. Abstracting over the connection type will be done more thoroughly later. --- core/http/Cargo.toml | 2 +- core/http/src/hyper.rs | 3 +- core/http/src/tls.rs | 3 ++ core/lib/src/data/data.rs | 8 +-- core/lib/src/data/data_stream.rs | 25 --------- core/lib/src/error.rs | 2 +- core/lib/src/rocket.rs | 88 ++++++++------------------------ 7 files changed, 29 insertions(+), 102 deletions(-) diff --git a/core/http/Cargo.toml b/core/http/Cargo.toml index ce987dff70..650bef8bae 100644 --- a/core/http/Cargo.toml +++ b/core/http/Cargo.toml @@ -22,7 +22,7 @@ private-cookies = ["cookie/secure"] [dependencies] smallvec = "0.6" percent-encoding = "1" -hyper = { version = "0.12.31", default-features = false, features = ["tokio"] } +hyper = { version = "0.12.31", default-features = false, features = ["runtime"] } http = "0.1.17" mime = "0.3.13" time = "0.1" diff --git a/core/http/src/hyper.rs b/core/http/src/hyper.rs index f6c93421a8..7f1a4f012e 100644 --- a/core/http/src/hyper.rs +++ b/core/http/src/hyper.rs @@ -7,7 +7,8 @@ #[doc(hidden)] pub use hyper::{Body, Request, Response, Server}; #[doc(hidden)] pub use hyper::body::Payload as Payload; #[doc(hidden)] pub use hyper::error::Error; -#[doc(hidden)] pub use hyper::service::{MakeService, Service}; +#[doc(hidden)] pub use hyper::service::{make_service_fn, MakeService, Service}; +#[doc(hidden)] pub use hyper::server::conn::{AddrIncoming, AddrStream}; #[doc(hidden)] pub use hyper::Chunk; #[doc(hidden)] pub use http::header::HeaderMap; diff --git a/core/http/src/tls.rs b/core/http/src/tls.rs index 97ae1289a0..5e236d9b6d 100644 --- a/core/http/src/tls.rs +++ b/core/http/src/tls.rs @@ -3,3 +3,6 @@ pub use tokio_rustls::rustls; pub use rustls::internal::pemfile; pub use rustls::{Certificate, NoClientAuth, PrivateKey, ServerConfig}; + +// TODO.async: extract from hyper-sync-rustls some convenience +// functions to load certs and keys diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 986dadd081..66fd295b7b 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -6,7 +6,7 @@ use futures::io::{self, AsyncRead, AsyncReadExt as _, AsyncWrite}; use futures::future::Future; use futures::stream::TryStreamExt; -use super::data_stream::{DataStream, kill_stream}; +use super::data_stream::DataStream; use crate::http::hyper; @@ -234,9 +234,3 @@ impl std::borrow::Borrow<()> for Data { &() } } - -impl Drop for Data { - fn drop(&mut self) { - kill_stream(&mut self.stream); - } -} diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index 3aad522d4c..f2e79e2d8f 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -34,28 +34,3 @@ impl AsyncRead for DataStream { } } } - -// TODO.async: Either implement this somehow, or remove the -// `Drop` impl and other references to kill_stream -pub fn kill_stream(_stream: &mut dyn AsyncRead) { -// // Only do the expensive reading if we're not sure we're done. -// -// // Take <= 1k from the stream. If there might be more data, force close. -// const FLUSH_LEN: u64 = 1024; -// match io::copy(&mut stream.take(FLUSH_LEN), &mut io::sink()) { -// Ok(FLUSH_LEN) | Err(_) => { -// warn_!("Data left unread. Force closing network stream."); -// let (_, network) = stream.get_mut().get_mut(); -// if let Err(e) = network.close(Shutdown::Read) { -// error_!("Failed to close network stream: {:?}", e); -// } -// } -// Ok(n) => debug!("flushed {} unread bytes", n) -// } -} - -impl Drop for DataStream { - fn drop(&mut self) { - kill_stream(&mut self.1); - } -} diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index bcdb84443a..422dee33fa 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -19,7 +19,7 @@ use crate::router::Route; #[derive(Debug)] pub enum LaunchErrorKind { /// Binding to the provided address/port failed. - Bind(io::Error), + Bind(hyper::Error), /// An I/O error occurred during launch. Io(io::Error), /// Route collisions were detected. diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index ecf68d3b13..a1b65ab494 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -5,6 +5,7 @@ use std::io; use std::mem; use std::net::ToSocketAddrs; use std::sync::Arc; +use std::time::Duration; use std::pin::Pin; use futures::compat::Compat; @@ -13,8 +14,6 @@ use futures::io::AsyncReadExt; use yansi::Paint; use state::Container; -use tokio::net::TcpListener; -use tokio::prelude::Stream as _; #[cfg(feature = "tls")] use crate::http::tls::TlsAcceptor; @@ -47,6 +46,7 @@ pub struct Rocket { #[derive(Clone)] struct RocketHyperService { rocket: Arc, + remote_addr: std::net::SocketAddr, } impl std::ops::Deref for RocketHyperService { @@ -57,19 +57,6 @@ impl std::ops::Deref for RocketHyperService { } } -impl hyper::MakeService for RocketHyperService { - type ReqBody = hyper::Body; - type ResBody = hyper::Body; - type Error = io::Error; - type Service = RocketHyperService; - type Future = Compat>>; - type MakeError = Self::Error; - - fn make_service(&mut self, _: Ctx) -> Self::Future { - futures::future::ok(RocketHyperService { rocket: self.rocket.clone() }).compat() - } -} - #[doc(hidden)] impl hyper::Service for RocketHyperService { type ReqBody = hyper::Body; @@ -87,13 +74,12 @@ impl hyper::Service for RocketHyperService { hyp_req: hyper::Request, ) -> Self::Future { let rocket = self.rocket.clone(); + let h_addr = self.remote_addr; + async move { // Get all of the information from Hyper. let (h_parts, h_body) = hyp_req.into_parts(); - // TODO.async: Get the client address somehow. - let h_addr = "0.0.0.0:0".parse().expect("socket addr"); - // Convert the Hyper request into a Rocket request. let req_res = Request::from_hyp(&rocket, h_parts.method, h_parts.headers, h_parts.uri, h_addr); let mut req = match req_res { @@ -748,60 +734,22 @@ impl Rocket { Err(e) => return From::from(io::Error::new(io::ErrorKind::Other, e)), }; - let listener = match TcpListener::bind(&addrs[0]) { - Ok(listener) => listener, + // TODO.async: support for TLS, unix sockets. + // Likely will be implemented with a custom "Incoming" type. + + let mut incoming = match hyper::AddrIncoming::bind(&addrs[0]) { + Ok(incoming) => incoming, Err(e) => return LaunchError::new(LaunchErrorKind::Bind(e)), }; // Determine the address and port we actually binded to. - match listener.local_addr() { - Ok(server_addr) => self.config.port = server_addr.port(), - Err(e) => return LaunchError::from(e), - } + self.config.port = incoming.local_addr().port(); - // TODO.async Move all of this to http crate somewhere - // TODO.async Is boxing everything really the best we can do here? - trait AsyncReadWrite: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send { } - impl AsyncReadWrite for T where T: tokio::io::AsyncRead + tokio::io::AsyncWrite + Send { } - - let proto; - let incoming: Box, Error=std::io::Error> + Send>; - - #[cfg(feature = "tls")] - { - use tokio::prelude::Future; - - // TODO.async: Can/should we make the clone unnecessary (by reference, or by moving out?) - if let Some(tls) = self.config.tls.clone() { - proto = "https://"; - let mut config = tls::rustls::ServerConfig::new(tls::rustls::NoClientAuth::new()); - config.set_single_cert(tls.certs, tls.key).expect("invalid key or certificate"); - - // TODO.async: I once observed an unhandled AlertReceived(UnknownCA) but - // have no idea what happened and cannot reproduce. - let config = TlsAcceptor::from(Arc::new(config)); - - incoming = Box::new(listener.incoming().and_then(move |stream| { - config.accept(stream) - .map(|stream| Box::new(stream) as Box) - })); - } else { - proto = "http://"; - incoming = Box::new(listener.incoming().map(|stream| Box::new(stream) as Box)); - } - } - - // TODO.async: Duplicated code - #[cfg(not(feature = "tls"))] - { - proto = "http://"; - incoming = Box::new(listener.incoming().map(|stream| Box::new(stream) as Box)); - } + let proto = "http://"; - // TODO.async: Set the keep-alive. -// // Set the keep-alive. -// let timeout = self.config.keep_alive.map(|s| Duration::from_secs(s as u64)); -// server.keep_alive(timeout); + // Set the keep-alive. + let timeout = self.config.keep_alive.map(|s| Duration::from_secs(s as u64)); + incoming.set_keepalive(timeout); // Freeze managed state for synchronization-free accesses later. self.state.freeze(); @@ -818,7 +766,13 @@ impl Rocket { // Restore the log level back to what it originally was. logger::pop_max_level(); - let service = RocketHyperService { rocket: Arc::new(self) }; + let rocket = Arc::new(self); + let service = hyper::make_service_fn(move |socket: &hyper::AddrStream| { + futures::future::ok::<_, Box>(RocketHyperService { + rocket: rocket.clone(), + remote_addr: socket.remote_addr(), + }).compat() + }); // NB: executor must be passed manually here, see hyperium/hyper#1537 let server = hyper::Server::builder(incoming) From 52db3642add320ce1e1b439f4f555d1524c0d3c2 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Thu, 11 Jul 2019 18:54:48 -0700 Subject: [PATCH 06/25] Stream body data instead of buffering it. This requires some awkward channel and spawning work because Body might contain borrowed data. --- core/http/src/hyper.rs | 1 + core/lib/src/ext.rs | 37 +++++++++++++ core/lib/src/rocket.rs | 122 +++++++++++++++++++++++++---------------- 3 files changed, 113 insertions(+), 47 deletions(-) diff --git a/core/http/src/hyper.rs b/core/http/src/hyper.rs index 7f1a4f012e..143afcdcba 100644 --- a/core/http/src/hyper.rs +++ b/core/http/src/hyper.rs @@ -16,6 +16,7 @@ #[doc(hidden)] pub use http::header::HeaderValue as HeaderValue; #[doc(hidden)] pub use http::method::Method; #[doc(hidden)] pub use http::request::Parts as RequestParts; +#[doc(hidden)] pub use http::response::Builder as ResponseBuilder; #[doc(hidden)] pub use http::status::StatusCode; #[doc(hidden)] pub use http::uri::Uri; diff --git a/core/lib/src/ext.rs b/core/lib/src/ext.rs index 6cb1c16bf0..f7996e4f3c 100644 --- a/core/lib/src/ext.rs +++ b/core/lib/src/ext.rs @@ -3,8 +3,11 @@ use std::pin::Pin; use futures::io::{AsyncRead, AsyncReadExt as _}; use futures::future::{Future}; +use futures::stream::Stream; use futures::task::{Poll, Context}; +use crate::http::hyper::Chunk; + // Based on std::io::Take, but for AsyncRead instead of Read pub struct Take{ inner: R, @@ -30,11 +33,45 @@ impl AsyncRead for Take where R: AsyncRead + Unpin { } } +pub struct IntoChunkStream { + inner: R, + buf_size: usize, + buffer: Vec, +} + +// TODO.async: Verify correctness of this implementation. +impl Stream for IntoChunkStream + where R: AsyncRead + Unpin +{ + type Item = Result; + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll>{ + assert!(self.buffer.len() == self.buf_size); + + let Self { ref mut inner, ref mut buffer, buf_size } = *self; + + match Pin::new(inner).poll_read(cx, &mut buffer[..]) { + Poll::Pending => Poll::Pending, + Poll::Ready(Err(e)) => Poll::Ready(Some(Err(e))), + Poll::Ready(Ok(n)) if n == 0 => Poll::Ready(None), + Poll::Ready(Ok(n)) => { + let mut next = std::mem::replace(buffer, vec![0; buf_size]); + next.truncate(n); + Poll::Ready(Some(Ok(Chunk::from(next)))) + } + } + } +} + pub trait AsyncReadExt: AsyncRead { fn take(self, limit: u64) -> Take where Self: Sized { Take { inner: self, limit } } + fn into_chunk_stream(self, buf_size: usize) -> IntoChunkStream where Self: Sized { + IntoChunkStream { inner: self, buf_size, buffer: vec![0; buf_size] } + } + // TODO.async: Verify correctness of this implementation. fn read_max<'a>(&'a mut self, mut buf: &'a mut [u8]) -> Pin> + Send + '_>> where Self: Send + Unpin diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index a1b65ab494..0cc43b15f3 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -1,5 +1,5 @@ use std::collections::HashMap; -use std::convert::From; +use std::convert::{From, TryInto}; use std::cmp::min; use std::io; use std::mem; @@ -8,9 +8,11 @@ use std::sync::Arc; use std::time::Duration; use std::pin::Pin; -use futures::compat::Compat; +use futures::compat::{Compat, Executor01CompatExt, Sink01CompatExt}; use futures::future::{Future, FutureExt, TryFutureExt}; -use futures::io::AsyncReadExt; +use futures::sink::SinkExt; +use futures::stream::StreamExt; +use futures::task::SpawnExt; use yansi::Paint; use state::Container; @@ -27,6 +29,7 @@ use crate::catcher::{self, Catcher}; use crate::outcome::Outcome; use crate::error::{LaunchError, LaunchErrorKind}; use crate::fairing::{Fairing, Fairings}; +use crate::ext::AsyncReadExt; use crate::http::{Method, Status, Header}; use crate::http::hyper::{self, header}; @@ -43,9 +46,9 @@ pub struct Rocket { fairings: Fairings, } -#[derive(Clone)] struct RocketHyperService { rocket: Arc, + spawn: Box, remote_addr: std::net::SocketAddr, } @@ -76,7 +79,13 @@ impl hyper::Service for RocketHyperService { let rocket = self.rocket.clone(); let h_addr = self.remote_addr; - async move { + // This future must return a hyper::Response, but that's not easy + // because the response body might borrow from the request. Instead, + // we do the body writing in another future that will send us + // the response metadata (and a body channel) beforehand. + let (tx, rx) = futures::channel::oneshot::channel(); + + self.spawn.spawn(async move { // Get all of the information from Hyper. let (h_parts, h_body) = hyp_req.into_parts(); @@ -92,7 +101,7 @@ impl hyper::Service for RocketHyperService { // handler) instead of doing this. let dummy = Request::new(&rocket, Method::Get, Origin::dummy()); let r = rocket.handle_error(Status::BadRequest, &dummy).await; - return rocket.issue_response(r).await; + return rocket.issue_response(r, tx).await; } }; @@ -101,7 +110,11 @@ impl hyper::Service for RocketHyperService { // Dispatch the request to get a response, then write that response out. let r = rocket.dispatch(&mut req, data).await; - rocket.issue_response(r).await + rocket.issue_response(r, tx).await; + }).expect("failed to spawn handler"); + + async move { + Ok(rx.await.expect("TODO.async: sender was dropped, error instead")) }.boxed().compat() } } @@ -109,28 +122,31 @@ impl hyper::Service for RocketHyperService { impl Rocket { // TODO.async: Reconsider io::Result #[inline] - fn issue_response<'r>(&self, response: Response<'r>) -> impl Future>> + 'r { - let result = self.write_response(response); - Box::pin(async move { + fn issue_response<'r>( + &self, + response: Response<'r>, + tx: futures::channel::oneshot::Sender>, + ) -> impl Future + 'r { + let result = self.write_response(response, tx); + async move { match result.await { - Ok(r) => { + Ok(()) => { info_!("{}", Paint::green("Response succeeded.")); - Ok(r) } Err(e) => { error_!("Failed to write response: {:?}.", e); - Err(e) } } - }) + } } #[inline] fn write_response<'r>( &self, mut response: Response<'r>, - ) -> impl Future>> + 'r { - Box::pin(async move { + tx: futures::channel::oneshot::Sender>, + ) -> impl Future> + 'r { + async move { let mut hyp_res = hyper::Response::builder(); hyp_res.status(response.status().code); @@ -140,48 +156,58 @@ impl Rocket { hyp_res.header(name, value); } - let body = match response.body() { + let send_response = move |mut hyp_res: hyper::ResponseBuilder, body| -> io::Result<()> { + let response = hyp_res.body(body).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; + tx.send(response).expect("channel receiver should not be dropped"); + Ok(()) + }; + + match response.body() { None => { hyp_res.header(header::CONTENT_LENGTH, "0"); - hyper::Body::empty() + send_response(hyp_res, hyper::Body::empty())?; } Some(Body::Sized(body, size)) => { hyp_res.header(header::CONTENT_LENGTH, size.to_string()); + let (sender, hyp_body) = hyper::Body::channel(); + send_response(hyp_res, hyp_body)?; - // TODO.async: Stream the data instead of buffering. - // TODO.async: Possible truncation (u64 -> usize) - let mut buffer = Vec::with_capacity(size as usize); - body.read_to_end(&mut buffer).await?; - hyper::Body::from(buffer) + let mut stream = body.into_chunk_stream(4096); + let mut sink = sender.sink_compat().sink_map_err(|e| { + io::Error::new(io::ErrorKind::Other, e) + }); + + while let Some(next) = stream.next().await { + sink.send(next?).await?; + } + + // TODO.async: This should be better, but it creates an + // incomprehensible error messasge instead + // stream.forward(sink).await; } - Some(Body::Chunked(body, _chunk_size)) => { - // // This _might_ happen on a 32-bit machine! - // if chunk_size > (usize::max_value() as u64) { - // let msg = "chunk size exceeds limits of usize type"; - // return Err(io::Error::new(io::ErrorKind::Other, msg)); - // } - // - // // The buffer stores the current chunk being written out. - // let mut buffer = vec![0; chunk_size as usize]; - // let mut stream = hyp_res.start()?; - // loop { - // match body.read_max(&mut buffer)? { - // 0 => break, - // n => stream.write_all(&buffer[..n])?, - // } - // } - // - // stream.end() + Some(Body::Chunked(body, chunk_size)) => { + // TODO.async: This is identical to Body::Sized except for the chunk size - // TODO.async: Stream the data instead of buffering. - let mut buffer = Vec::new(); - body.read_to_end(&mut buffer).await?; - hyper::Body::from(buffer) + let (sender, hyp_body) = hyper::Body::channel(); + send_response(hyp_res, hyp_body)?; + + let mut stream = body.into_chunk_stream(chunk_size.try_into().expect("u64 -> usize overflow")); + let mut sink = sender.sink_compat().sink_map_err(|e| { + io::Error::new(io::ErrorKind::Other, e) + }); + + while let Some(next) = stream.next().await { + sink.send(next?).await?; + } + + // TODO.async: This should be better, but it creates an + // incomprehensible error messasge instead + // stream.forward(sink).await; } }; - Ok(hyp_res.body(body).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?) - }) + Ok(()) + } } } @@ -767,9 +793,11 @@ impl Rocket { logger::pop_max_level(); let rocket = Arc::new(self); + let spawn = Box::new(runtime.executor().compat()); let service = hyper::make_service_fn(move |socket: &hyper::AddrStream| { futures::future::ok::<_, Box>(RocketHyperService { rocket: rocket.clone(), + spawn: spawn.clone(), remote_addr: socket.remote_addr(), }).compat() }); From 37c0c6900de8b9c59a2736ffc3841bee794fcc4a Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sat, 13 Jul 2019 13:55:09 -0700 Subject: [PATCH 07/25] Update core lib tests for async. Adds body_string_wait and body_bytes_wait functions to LocalRequest for convenience. --- core/lib/src/config/mod.rs | 4 +-- core/lib/src/local/request.rs | 10 ++++++ core/lib/src/request/tests.rs | 8 ++--- .../lib/tests/absolute-uris-okay-issue-443.rs | 2 +- .../conditionally-set-server-header-996.rs | 2 +- core/lib/tests/derive-reexports.rs | 6 ++-- .../fairing_before_head_strip-issue-546.rs | 14 +++++--- .../lib/tests/flash-lazy-removes-issue-466.rs | 4 +-- core/lib/tests/form_method-issue-45.rs | 4 +-- .../lib/tests/form_value_decoding-issue-82.rs | 4 +-- core/lib/tests/head_handling.rs | 14 ++++---- core/lib/tests/limits.rs | 8 ++--- .../local-request-content-type-issue-505.rs | 32 +++++++++---------- .../local_request_private_cookie-issue-368.rs | 4 +-- core/lib/tests/nested-fairing-attaches.rs | 8 ++--- .../tests/precise-content-type-matching.rs | 4 +-- .../tests/redirect_from_catcher-issue-113.rs | 2 +- .../lib/tests/responder_lifetime-issue-345.rs | 2 +- core/lib/tests/route_guard.rs | 4 +-- core/lib/tests/segments-issues-41-86.rs | 4 +-- core/lib/tests/strict_and_lenient_forms.rs | 8 ++--- .../tests/uri-percent-encoding-issue-808.rs | 4 +-- 22 files changed, 84 insertions(+), 68 deletions(-) diff --git a/core/lib/src/config/mod.rs b/core/lib/src/config/mod.rs index a3120d5535..6f48d18e60 100644 --- a/core/lib/src/config/mod.rs +++ b/core/lib/src/config/mod.rs @@ -1096,10 +1096,10 @@ mod test { let check_value = |key: &str, val: &str, config: &Config| { match key { "log" => assert_eq!(config.log_level, val.parse().unwrap()), - "port" => assert_eq!(config.port, val.parse().unwrap()), + "port" => assert_eq!(config.port, val.parse::().unwrap()), "address" => assert_eq!(config.address, val), "extra_extra" => assert_eq!(config.get_bool(key).unwrap(), true), - "workers" => assert_eq!(config.workers, val.parse().unwrap()), + "workers" => assert_eq!(config.workers, val.parse::().unwrap()), _ => panic!("Unexpected key: {}", key) } }; diff --git a/core/lib/src/local/request.rs b/core/lib/src/local/request.rs index 1138da49aa..1a7076b2cf 100644 --- a/core/lib/src/local/request.rs +++ b/core/lib/src/local/request.rs @@ -458,6 +458,16 @@ pub struct LocalResponse<'c> { response: Response<'c>, } +impl LocalResponse<'_> { + pub fn body_string_wait(&mut self) -> Option { + futures::executor::block_on(self.body_string()) + } + + pub fn body_bytes_wait(&mut self) -> Option> { + futures::executor::block_on(self.body_bytes()) + } +} + impl<'c> Deref for LocalResponse<'c> { type Target = Response<'c>; diff --git a/core/lib/src/request/tests.rs b/core/lib/src/request/tests.rs index ac21bb41a0..3a9cafe5e9 100644 --- a/core/lib/src/request/tests.rs +++ b/core/lib/src/request/tests.rs @@ -7,13 +7,13 @@ use crate::http::hyper; macro_rules! assert_headers { ($($key:expr => [$($value:expr),+]),+) => ({ // Set up the parameters to the hyper request object. - let h_method = hyper::Method::Get; - let h_uri = hyper::RequestUri::AbsolutePath("/test".to_string()); + let h_method = hyper::Method::GET; + let h_uri = "/test".parse().unwrap(); let h_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 8000); - let mut h_headers = hyper::header::Headers::new(); + let mut h_headers = hyper::HeaderMap::new(); // Add all of the passed in headers to the request. - $($(h_headers.append_raw($key.to_string(), $value.as_bytes().into());)+)+ + $($(h_headers.append($key, hyper::HeaderValue::from_str($value).unwrap());)+)+ // Build up what we expect the headers to actually be. let mut expected = HashMap::new(); diff --git a/core/lib/tests/absolute-uris-okay-issue-443.rs b/core/lib/tests/absolute-uris-okay-issue-443.rs index 758d1d727a..7f57295e1c 100644 --- a/core/lib/tests/absolute-uris-okay-issue-443.rs +++ b/core/lib/tests/absolute-uris-okay-issue-443.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/lib/tests/conditionally-set-server-header-996.rs b/core/lib/tests/conditionally-set-server-header-996.rs index f20f18a1cb..a6f8fac5cf 100644 --- a/core/lib/tests/conditionally-set-server-header-996.rs +++ b/core/lib/tests/conditionally-set-server-header-996.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/lib/tests/derive-reexports.rs b/core/lib/tests/derive-reexports.rs index 3d3ac70e3b..18513acb6e 100644 --- a/core/lib/tests/derive-reexports.rs +++ b/core/lib/tests/derive-reexports.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] use rocket; @@ -51,8 +51,8 @@ fn test_derive_reexports() { let client = Client::new(rocket).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string().unwrap(), "hello"); + assert_eq!(response.body_string_wait().unwrap(), "hello"); let mut response = client.get("/?thing=b").dispatch(); - assert_eq!(response.body_string().unwrap(), "b"); + assert_eq!(response.body_string_wait().unwrap(), "b"); } diff --git a/core/lib/tests/fairing_before_head_strip-issue-546.rs b/core/lib/tests/fairing_before_head_strip-issue-546.rs index 546e7a78b7..648046acc6 100644 --- a/core/lib/tests/fairing_before_head_strip-issue-546.rs +++ b/core/lib/tests/fairing_before_head_strip-issue-546.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -35,13 +35,15 @@ mod fairing_before_head_strip { })) .attach(AdHoc::on_response("Check HEAD 2", |req, res| { assert_eq!(req.method(), Method::Head); - assert_eq!(res.body_string(), Some(RESPONSE_STRING.into())); + // TODO.async: Needs async on_response fairings + // assert_eq!(res.body_string().await, Some(RESPONSE_STRING.into())); })); let client = Client::new(rocket).unwrap(); let mut response = client.head("/").dispatch(); assert_eq!(response.status(), Status::Ok); - assert!(response.body().is_none()); + // TODO.async: See above + // assert!(response.body().is_none()); } #[test] @@ -62,12 +64,14 @@ mod fairing_before_head_strip { })) .attach(AdHoc::on_response("Check GET", |req, res| { assert_eq!(req.method(), Method::Get); - assert_eq!(res.body_string(), Some(RESPONSE_STRING.into())); + // TODO.async: Needs async on_response fairings + // assert_eq!(res.body_string().await, Some(RESPONSE_STRING.into())); })); let client = Client::new(rocket).unwrap(); let mut response = client.head("/").dispatch(); assert_eq!(response.status(), Status::Ok); - assert!(response.body().is_none()); + // TODO.async: See above + // assert!(response.body().is_none()); } } diff --git a/core/lib/tests/flash-lazy-removes-issue-466.rs b/core/lib/tests/flash-lazy-removes-issue-466.rs index 584c97db7c..9a3c73beed 100644 --- a/core/lib/tests/flash-lazy-removes-issue-466.rs +++ b/core/lib/tests/flash-lazy-removes-issue-466.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -49,7 +49,7 @@ mod flash_lazy_remove_tests { // Now use it. let mut response = client.get("/use").dispatch(); - assert_eq!(response.body_string(), Some(FLASH_MESSAGE.into())); + assert_eq!(response.body_string_wait(), Some(FLASH_MESSAGE.into())); // Now it should be gone. let response = client.get("/unused").dispatch(); diff --git a/core/lib/tests/form_method-issue-45.rs b/core/lib/tests/form_method-issue-45.rs index 5acaff8224..b65230e8b9 100644 --- a/core/lib/tests/form_method-issue-45.rs +++ b/core/lib/tests/form_method-issue-45.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -28,7 +28,7 @@ mod tests { .body("_method=patch&form_data=Form+data") .dispatch(); - assert_eq!(response.body_string(), Some("OK".into())); + assert_eq!(response.body_string_wait(), Some("OK".into())); } #[test] diff --git a/core/lib/tests/form_value_decoding-issue-82.rs b/core/lib/tests/form_value_decoding-issue-82.rs index 2780eeedd6..b6fc477f0b 100644 --- a/core/lib/tests/form_value_decoding-issue-82.rs +++ b/core/lib/tests/form_value_decoding-issue-82.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -28,7 +28,7 @@ mod tests { .dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(Some(decoded.to_string()), response.body_string()); + assert_eq!(Some(decoded.to_string()), response.body_string_wait()); } #[test] diff --git a/core/lib/tests/head_handling.rs b/core/lib/tests/head_handling.rs index 5e3dd96d67..e5a4887722 100644 --- a/core/lib/tests/head_handling.rs +++ b/core/lib/tests/head_handling.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -22,7 +22,7 @@ fn other() -> content::Json<&'static str> { mod head_handling_tests { use super::*; - use std::io::Read; + use futures::io::AsyncReadExt; use rocket::Route; use rocket::local::Client; @@ -33,13 +33,15 @@ mod head_handling_tests { routes![index, empty, other] } - fn assert_empty_sized_body(body: Body, expected_size: u64) { + fn assert_empty_sized_body(body: Body, expected_size: u64) { match body { Body::Sized(mut body, size) => { let mut buffer = vec![]; - let n = body.read_to_end(&mut buffer).unwrap(); + futures::executor::block_on(async { + body.read_to_end(&mut buffer).await.unwrap(); + }); assert_eq!(size, expected_size); - assert_eq!(n, 0); + assert_eq!(buffer.len(), 0); } _ => panic!("Expected a sized body.") } @@ -57,7 +59,7 @@ mod head_handling_tests { let mut response = client.head("/empty").dispatch(); assert_eq!(response.status(), Status::NoContent); - assert!(response.body_bytes().is_none()); + assert!(response.body_bytes_wait().is_none()); } #[test] diff --git a/core/lib/tests/limits.rs b/core/lib/tests/limits.rs index 9e23abb14f..7f3dca6503 100644 --- a/core/lib/tests/limits.rs +++ b/core/lib/tests/limits.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -36,7 +36,7 @@ mod limits_tests { .header(ContentType::Form) .dispatch(); - assert_eq!(response.body_string(), Some("Hello world".into())); + assert_eq!(response.body_string_wait(), Some("Hello world".into())); } #[test] @@ -47,7 +47,7 @@ mod limits_tests { .header(ContentType::Form) .dispatch(); - assert_eq!(response.body_string(), Some("Hello world".into())); + assert_eq!(response.body_string_wait(), Some("Hello world".into())); } #[test] @@ -69,6 +69,6 @@ mod limits_tests { .header(ContentType::Form) .dispatch(); - assert_eq!(response.body_string(), Some("Hell".into())); + assert_eq!(response.body_string_wait(), Some("Hell".into())); } } diff --git a/core/lib/tests/local-request-content-type-issue-505.rs b/core/lib/tests/local-request-content-type-issue-505.rs index 4803e929bf..8d2f934421 100644 --- a/core/lib/tests/local-request-content-type-issue-505.rs +++ b/core/lib/tests/local-request-content-type-issue-505.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -25,12 +25,12 @@ use rocket::data::{self, FromDataSimple}; impl FromDataSimple for HasContentType { type Error = (); - fn from_data(request: &Request, data: Data) -> data::Outcome { - if request.content_type().is_some() { + fn from_data(request: &Request<'_>, data: Data) -> data::FromDataFuture<'static, Self, Self::Error> { + Box::pin(futures::future::ready(if request.content_type().is_some() { Success(HasContentType) } else { Forward(data) - } + })) } } @@ -65,14 +65,14 @@ mod local_request_content_type_tests { let client = Client::new(rocket()).unwrap(); let mut req = client.post("/"); - assert_eq!(req.clone().dispatch().body_string(), Some("Absent".to_string())); - assert_eq!(req.mut_dispatch().body_string(), Some("Absent".to_string())); - assert_eq!(req.dispatch().body_string(), Some("Absent".to_string())); + assert_eq!(req.clone().dispatch().body_string_wait(), Some("Absent".to_string())); + assert_eq!(req.mut_dispatch().body_string_wait(), Some("Absent".to_string())); + assert_eq!(req.dispatch().body_string_wait(), Some("Absent".to_string())); let mut req = client.post("/data"); - assert_eq!(req.clone().dispatch().body_string(), Some("Data Absent".to_string())); - assert_eq!(req.mut_dispatch().body_string(), Some("Data Absent".to_string())); - assert_eq!(req.dispatch().body_string(), Some("Data Absent".to_string())); + assert_eq!(req.clone().dispatch().body_string_wait(), Some("Data Absent".to_string())); + assert_eq!(req.mut_dispatch().body_string_wait(), Some("Data Absent".to_string())); + assert_eq!(req.dispatch().body_string_wait(), Some("Data Absent".to_string())); } #[test] @@ -80,13 +80,13 @@ mod local_request_content_type_tests { let client = Client::new(rocket()).unwrap(); let mut req = client.post("/").header(ContentType::JSON); - assert_eq!(req.clone().dispatch().body_string(), Some("Present".to_string())); - assert_eq!(req.mut_dispatch().body_string(), Some("Present".to_string())); - assert_eq!(req.dispatch().body_string(), Some("Present".to_string())); + assert_eq!(req.clone().dispatch().body_string_wait(), Some("Present".to_string())); + assert_eq!(req.mut_dispatch().body_string_wait(), Some("Present".to_string())); + assert_eq!(req.dispatch().body_string_wait(), Some("Present".to_string())); let mut req = client.post("/data").header(ContentType::JSON); - assert_eq!(req.clone().dispatch().body_string(), Some("Data Present".to_string())); - assert_eq!(req.mut_dispatch().body_string(), Some("Data Present".to_string())); - assert_eq!(req.dispatch().body_string(), Some("Data Present".to_string())); + assert_eq!(req.clone().dispatch().body_string_wait(), Some("Data Present".to_string())); + assert_eq!(req.mut_dispatch().body_string_wait(), Some("Data Present".to_string())); + assert_eq!(req.dispatch().body_string_wait(), Some("Data Present".to_string())); } } diff --git a/core/lib/tests/local_request_private_cookie-issue-368.rs b/core/lib/tests/local_request_private_cookie-issue-368.rs index deba440e27..247ec6c2e3 100644 --- a/core/lib/tests/local_request_private_cookie-issue-368.rs +++ b/core/lib/tests/local_request_private_cookie-issue-368.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] #[cfg(feature = "private-cookies")] @@ -30,7 +30,7 @@ mod private_cookie_test { let req = client.get("/").private_cookie(Cookie::new("cookie_name", "cookie_value")); let mut response = req.dispatch(); - assert_eq!(response.body_string(), Some("cookie_value".into())); + assert_eq!(response.body_string_wait(), Some("cookie_value".into())); assert_eq!(response.headers().get_one("Set-Cookie"), None); } diff --git a/core/lib/tests/nested-fairing-attaches.rs b/core/lib/tests/nested-fairing-attaches.rs index 19137f4f8b..ba1554cda9 100644 --- a/core/lib/tests/nested-fairing-attaches.rs +++ b/core/lib/tests/nested-fairing-attaches.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -47,14 +47,14 @@ mod nested_fairing_attaches_tests { fn test_counts() { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("1, 1".into())); + assert_eq!(response.body_string_wait(), Some("1, 1".into())); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("1, 2".into())); + assert_eq!(response.body_string_wait(), Some("1, 2".into())); client.get("/").dispatch(); client.get("/").dispatch(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("1, 5".into())); + assert_eq!(response.body_string_wait(), Some("1, 5".into())); } } diff --git a/core/lib/tests/precise-content-type-matching.rs b/core/lib/tests/precise-content-type-matching.rs index da7349c610..f1c651ddbb 100644 --- a/core/lib/tests/precise-content-type-matching.rs +++ b/core/lib/tests/precise-content-type-matching.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -45,7 +45,7 @@ mod tests { } let mut response = req.dispatch(); - let body_str = response.body_string(); + let body_str = response.body_string_wait(); let body: Option<&'static str> = $body; match body { Some(string) => assert_eq!(body_str, Some(string.to_string())), diff --git a/core/lib/tests/redirect_from_catcher-issue-113.rs b/core/lib/tests/redirect_from_catcher-issue-113.rs index f50f2ba32c..15c46f2753 100644 --- a/core/lib/tests/redirect_from_catcher-issue-113.rs +++ b/core/lib/tests/redirect_from_catcher-issue-113.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/lib/tests/responder_lifetime-issue-345.rs b/core/lib/tests/responder_lifetime-issue-345.rs index b35cddde65..8ca2964a54 100644 --- a/core/lib/tests/responder_lifetime-issue-345.rs +++ b/core/lib/tests/responder_lifetime-issue-345.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #![allow(dead_code)] // This test is only here so that we can ensure it compiles. #[macro_use] extern crate rocket; diff --git a/core/lib/tests/route_guard.rs b/core/lib/tests/route_guard.rs index 64bfe8f7f9..d914cc656e 100644 --- a/core/lib/tests/route_guard.rs +++ b/core/lib/tests/route_guard.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -17,7 +17,7 @@ mod route_guard_tests { fn assert_path(client: &Client, path: &str) { let mut res = client.get(path).dispatch(); - assert_eq!(res.body_string(), Some(path.into())); + assert_eq!(res.body_string_wait(), Some(path.into())); } #[test] diff --git a/core/lib/tests/segments-issues-41-86.rs b/core/lib/tests/segments-issues-41-86.rs index f9bd50a806..0c32d3afe5 100644 --- a/core/lib/tests/segments-issues-41-86.rs +++ b/core/lib/tests/segments-issues-41-86.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -48,7 +48,7 @@ mod tests { { let path = "this/is/the/path/we/want"; let mut response = client.get(format!("{}/{}", prefix, path)).dispatch(); - assert_eq!(response.body_string(), Some(path.into())); + assert_eq!(response.body_string_wait(), Some(path.into())); } } } diff --git a/core/lib/tests/strict_and_lenient_forms.rs b/core/lib/tests/strict_and_lenient_forms.rs index 4ba5300d27..6643b62230 100644 --- a/core/lib/tests/strict_and_lenient_forms.rs +++ b/core/lib/tests/strict_and_lenient_forms.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -40,7 +40,7 @@ mod strict_and_lenient_forms_tests { .dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some(FIELD_VALUE.into())); + assert_eq!(response.body_string_wait(), Some(FIELD_VALUE.into())); let response = client.post("/strict") .header(ContentType::Form) @@ -59,7 +59,7 @@ mod strict_and_lenient_forms_tests { .dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some(FIELD_VALUE.into())); + assert_eq!(response.body_string_wait(), Some(FIELD_VALUE.into())); let mut response = client.post("/lenient") .header(ContentType::Form) @@ -67,6 +67,6 @@ mod strict_and_lenient_forms_tests { .dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some(FIELD_VALUE.into())); + assert_eq!(response.body_string_wait(), Some(FIELD_VALUE.into())); } } diff --git a/core/lib/tests/uri-percent-encoding-issue-808.rs b/core/lib/tests/uri-percent-encoding-issue-808.rs index b46cc8929a..f8c88597bf 100644 --- a/core/lib/tests/uri-percent-encoding-issue-808.rs +++ b/core/lib/tests/uri-percent-encoding-issue-808.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; @@ -54,6 +54,6 @@ mod tests { let name = Uri::percent_encode(NAME); let mut response = client.get(format!("/hello/{}", name)).dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string().unwrap(), format!("Hello, {}!", NAME)); + assert_eq!(response.body_string_wait().unwrap(), format!("Hello, {}!", NAME)); } } From c0a0f774b78e790c79df077d03cdbceccd35c9e7 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 24 Jul 2019 08:21:52 -0700 Subject: [PATCH 08/25] Make response fairings async. This is required to be able to do anything useful with the body in the outgoing response. Request fairings do not appear to need to be async as everything on Data that returns a future moves self and on_request only gets &Data, but the same change in this commit should work for on_request if desired. --- core/lib/src/fairing/ad_hoc.rs | 10 +++++++--- core/lib/src/fairing/fairings.rs | 13 ++++++++---- core/lib/src/fairing/mod.rs | 9 +++++++-- core/lib/src/rocket.rs | 2 +- .../fairing_before_head_strip-issue-546.rs | 20 +++++++++---------- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/core/lib/src/fairing/ad_hoc.rs b/core/lib/src/fairing/ad_hoc.rs index 1952b6d4b5..dc16049167 100644 --- a/core/lib/src/fairing/ad_hoc.rs +++ b/core/lib/src/fairing/ad_hoc.rs @@ -1,3 +1,5 @@ +use std::future::Future; +use std::pin::Pin; use std::sync::Mutex; use crate::{Rocket, Request, Response, Data}; @@ -49,7 +51,7 @@ enum AdHocKind { Request(Box, &Data) + Send + Sync + 'static>), /// An ad-hoc **response** fairing. Called when a response is ready to be /// sent to a client. - Response(Box, &mut Response<'_>) + Send + Sync + 'static>), + Response(Box Fn(&'a Request<'r>, &'a mut Response<'r>) -> Pin + Send + 'a>> + Send + Sync + 'static>), } impl AdHoc { @@ -124,7 +126,7 @@ impl AdHoc { /// }); /// ``` pub fn on_response(name: &'static str, f: F) -> AdHoc - where F: Fn(&Request<'_>, &mut Response<'_>) + Send + Sync + 'static + where F: for<'a, 'r> Fn(&'a Request<'r>, &'a mut Response<'r>) -> Pin + Send + 'a>> + Send + Sync + 'static { AdHoc { name, kind: AdHocKind::Response(Box::new(f)) } } @@ -166,9 +168,11 @@ impl Fairing for AdHoc { } } - fn on_response(&self, request: &Request<'_>, response: &mut Response<'_>) { + fn on_response<'a, 'r>(&'a self, request: &'a Request<'r>, response: &'a mut Response<'r>) -> Pin + Send + 'a>> { if let AdHocKind::Response(ref callback) = self.kind { callback(request, response) + } else { + Box::pin(async { }) } } } diff --git a/core/lib/src/fairing/fairings.rs b/core/lib/src/fairing/fairings.rs index a0425f1e2d..7933ac434f 100644 --- a/core/lib/src/fairing/fairings.rs +++ b/core/lib/src/fairing/fairings.rs @@ -1,3 +1,6 @@ +use std::pin::Pin; +use std::future::Future; + use crate::{Rocket, Request, Response, Data}; use crate::fairing::{Fairing, Kind}; @@ -65,10 +68,12 @@ impl Fairings { } #[inline(always)] - pub fn handle_response(&self, request: &Request<'_>, response: &mut Response<'_>) { - for &i in &self.response { - self.all_fairings[i].on_response(request, response); - } + pub fn handle_response<'a, 'r>(&'a self, request: &'a Request<'r>, response: &'a mut Response<'r>) -> Pin + Send + 'a>> { + Box::pin(async move { + for &i in &self.response { + self.all_fairings[i].on_response(request, response).await; + } + }) } pub fn failures(&self) -> Option<&[&'static str]> { diff --git a/core/lib/src/fairing/mod.rs b/core/lib/src/fairing/mod.rs index f11f1c108d..538e192c32 100644 --- a/core/lib/src/fairing/mod.rs +++ b/core/lib/src/fairing/mod.rs @@ -47,6 +47,9 @@ //! of other `Fairings` are not jeopardized. For instance, unless it is made //! abundantly clear, a fairing should not rewrite every request. +use std::pin::Pin; +use std::future::Future; + use crate::{Rocket, Request, Response, Data}; mod fairings; @@ -408,7 +411,9 @@ pub trait Fairing: Send + Sync + 'static { /// /// The default implementation of this method does nothing. #[allow(unused_variables)] - fn on_response(&self, request: &Request<'_>, response: &mut Response<'_>) {} + fn on_response<'a, 'r>(&'a self, request: &'a Request<'r>, response: &'a mut Response<'r>) -> Pin + Send + 'a>> { + Box::pin(async { }) + } } impl Fairing for std::sync::Arc { @@ -433,7 +438,7 @@ impl Fairing for std::sync::Arc { } #[inline] - fn on_response(&self, request: &Request<'_>, response: &mut Response<'_>) { + fn on_response<'a, 'r>(&'a self, request: &'a Request<'r>, response: &'a mut Response<'r>) -> Pin + Send + 'a>> { (self as &T).on_response(request, response) } } diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index 0cc43b15f3..ccc3ae2a46 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -266,7 +266,7 @@ impl Rocket { } // Run the response fairings. - self.fairings.handle_response(request, &mut response); + self.fairings.handle_response(request, &mut response).await; // Strip the body if this is a `HEAD` request. if was_head_request { diff --git a/core/lib/tests/fairing_before_head_strip-issue-546.rs b/core/lib/tests/fairing_before_head_strip-issue-546.rs index 648046acc6..cc1536b4d4 100644 --- a/core/lib/tests/fairing_before_head_strip-issue-546.rs +++ b/core/lib/tests/fairing_before_head_strip-issue-546.rs @@ -34,16 +34,16 @@ mod fairing_before_head_strip { assert_eq!(req.method(), Method::Head); })) .attach(AdHoc::on_response("Check HEAD 2", |req, res| { - assert_eq!(req.method(), Method::Head); - // TODO.async: Needs async on_response fairings - // assert_eq!(res.body_string().await, Some(RESPONSE_STRING.into())); + Box::pin(async move { + assert_eq!(req.method(), Method::Head); + assert_eq!(res.body_string().await, Some(RESPONSE_STRING.into())); + }) })); let client = Client::new(rocket).unwrap(); let mut response = client.head("/").dispatch(); assert_eq!(response.status(), Status::Ok); - // TODO.async: See above - // assert!(response.body().is_none()); + assert!(response.body().is_none()); } #[test] @@ -63,15 +63,15 @@ mod fairing_before_head_strip { assert_eq!(c.0.fetch_add(1, Ordering::SeqCst), 0); })) .attach(AdHoc::on_response("Check GET", |req, res| { - assert_eq!(req.method(), Method::Get); - // TODO.async: Needs async on_response fairings - // assert_eq!(res.body_string().await, Some(RESPONSE_STRING.into())); + Box::pin(async move { + assert_eq!(req.method(), Method::Get); + assert_eq!(res.body_string().await, Some(RESPONSE_STRING.into())); + }) })); let client = Client::new(rocket).unwrap(); let mut response = client.head("/").dispatch(); assert_eq!(response.status(), Status::Ok); - // TODO.async: See above - // assert!(response.body().is_none()); + assert!(response.body().is_none()); } } From b1bdbba28f3804b6e06b0cb3b9b80cc78542c031 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Wed, 24 Jul 2019 20:27:54 -0700 Subject: [PATCH 09/25] Make respond_to async. Like with response fairings, this is required to be able to do anything useful with the body. --- core/codegen/src/attribute/catch.rs | 2 +- core/codegen/src/attribute/route.rs | 2 +- core/codegen/src/derive/responder.rs | 18 ++- core/lib/src/catcher.rs | 2 +- core/lib/src/handler.rs | 26 +-- core/lib/src/response/content.rs | 20 +-- core/lib/src/response/flash.rs | 6 +- core/lib/src/response/mod.rs | 2 + core/lib/src/response/named_file.rs | 18 ++- core/lib/src/response/redirect.rs | 26 +-- core/lib/src/response/responder.rs | 150 ++++++++++-------- core/lib/src/response/response.rs | 8 +- core/lib/src/response/status.rs | 112 +++++++------ core/lib/src/response/stream.rs | 8 +- core/lib/src/router/mod.rs | 2 +- .../lib/tests/responder_lifetime-issue-345.rs | 2 +- 16 files changed, 230 insertions(+), 174 deletions(-) diff --git a/core/codegen/src/attribute/catch.rs b/core/codegen/src/attribute/catch.rs index 9b7feb594f..997fb69444 100644 --- a/core/codegen/src/attribute/catch.rs +++ b/core/codegen/src/attribute/catch.rs @@ -74,7 +74,7 @@ pub fn _catch(args: TokenStream, input: TokenStream) -> Result { let catcher_response = quote_spanned!(return_type_span => { // Emit this to force a type signature check. let #catcher: #fn_sig = #user_catcher_fn_name; - ::rocket::response::Responder::respond_to(#catcher(#inputs), #req)? + ::rocket::response::Responder::respond_to(#catcher(#inputs), #req).await? }); // Generate the catcher, keeping the user's input around. diff --git a/core/codegen/src/attribute/route.rs b/core/codegen/src/attribute/route.rs index 8515159209..ecf01b91bb 100644 --- a/core/codegen/src/attribute/route.rs +++ b/core/codegen/src/attribute/route.rs @@ -420,7 +420,7 @@ fn codegen_route(route: Route) -> Result { #data_stmt #responder_stmt - #handler::Outcome::from(#req, ___responder) + #handler::Outcome::from(#req, ___responder).await }) } diff --git a/core/codegen/src/derive/responder.rs b/core/codegen/src/derive/responder.rs index 4e181209d2..a7c4c110c7 100644 --- a/core/codegen/src/derive/responder.rs +++ b/core/codegen/src/derive/responder.rs @@ -32,8 +32,8 @@ pub fn derive_responder(input: TokenStream) -> TokenStream { .function(|_, inner| quote! { fn respond_to( self, - __req: &::rocket::Request - ) -> ::rocket::response::Result<'__r> { + __req: &'__r ::rocket::Request + ) -> ::rocket::response::ResultFuture<'__r> { #inner } }) @@ -50,7 +50,7 @@ pub fn derive_responder(input: TokenStream) -> TokenStream { quote_spanned! { f.span().into() => let mut __res = <#ty as ::rocket::response::Responder>::respond_to( #accessor, __req - )?; + ).await?; } }).expect("have at least one field"); @@ -70,11 +70,13 @@ pub fn derive_responder(input: TokenStream) -> TokenStream { }); Ok(quote! { - #responder - #(#headers)* - #content_type - #status - Ok(__res) + Box::pin(async move { + #responder + #(#headers)* + #content_type + #status + Ok(__res) + }) }) }) .to_tokens() diff --git a/core/lib/src/catcher.rs b/core/lib/src/catcher.rs index 07bff3a0d5..afe059532d 100644 --- a/core/lib/src/catcher.rs +++ b/core/lib/src/catcher.rs @@ -155,7 +155,7 @@ macro_rules! default_catchers { (async move { status::Custom(Status::from_code($code).unwrap(), content::Html(error_page_template!($code, $name, $description)) - ).respond_to(req) + ).respond_to(req).await }).boxed() } diff --git a/core/lib/src/handler.rs b/core/lib/src/handler.rs index 98107b3475..607f24fe09 100644 --- a/core/lib/src/handler.rs +++ b/core/lib/src/handler.rs @@ -206,11 +206,13 @@ impl<'r> Outcome<'r> { /// } /// ``` #[inline] - pub fn from>(req: &Request<'_>, responder: T) -> Outcome<'r> { - match responder.respond_to(req) { - Ok(response) => outcome::Outcome::Success(response), - Err(status) => outcome::Outcome::Failure(status) - } + pub fn from + Send + 'r>(req: &'r Request<'_>, responder: T) -> HandlerFuture<'r> { + Box::pin(async move { + match responder.respond_to(req).await { + Ok(response) => outcome::Outcome::Success(response), + Err(status) => outcome::Outcome::Failure(status) + } + }) } /// Return the `Outcome` of response to `req` from `responder`. @@ -230,13 +232,15 @@ impl<'r> Outcome<'r> { /// } /// ``` #[inline] - pub fn from_or_forward(req: &Request<'_>, data: Data, responder: T) -> Outcome<'r> - where T: Responder<'r> + pub fn from_or_forward(req: &'r Request<'_>, data: Data, responder: T) -> HandlerFuture<'r> + where T: Responder<'r> + Send { - match responder.respond_to(req) { - Ok(response) => outcome::Outcome::Success(response), - Err(_) => outcome::Outcome::Forward(data) - } + Box::pin(async move { + match responder.respond_to(req).await { + Ok(response) => outcome::Outcome::Success(response), + Err(_) => outcome::Outcome::Forward(data) + } + }) } /// Return an `Outcome` of `Failure` with the status code `code`. This is diff --git a/core/lib/src/response/content.rs b/core/lib/src/response/content.rs index 84cb60cb4c..d44bf23a87 100644 --- a/core/lib/src/response/content.rs +++ b/core/lib/src/response/content.rs @@ -23,7 +23,7 @@ //! ``` use crate::request::Request; -use crate::response::{Response, Responder}; +use crate::response::{Response, Responder, ResultFuture}; use crate::http::{Status, ContentType}; /// Sets the Content-Type of a `Responder` to a chosen value. @@ -46,13 +46,15 @@ pub struct Content(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. -impl<'r, R: Responder<'r>> Responder<'r> for Content { +impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Content { #[inline(always)] - fn respond_to(self, req: &Request<'_>) -> Result, Status> { - Response::build() - .merge(self.1.respond_to(req)?) - .header(self.0) - .ok() + fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async move { + Response::build() + .merge(self.1.respond_to(req).await?) + .header(self.0) + .ok() + }) } } @@ -71,8 +73,8 @@ 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>> Responder<'r> for $name { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { + impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for $name { + fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { Content(ContentType::$ct, self.0).respond_to(req) } } diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index b7c2db54ca..b1f8b2124d 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -3,7 +3,7 @@ use std::convert::AsRef; use time::Duration; use crate::outcome::IntoOutcome; -use crate::response::{Response, Responder}; +use crate::response::{Response, Responder, ResultFuture}; use crate::request::{self, Request, FromRequest}; use crate::http::{Status, Cookie}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -193,8 +193,8 @@ impl<'r, R: Responder<'r>> Flash { /// 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`. -impl<'r, R: Responder<'r>> Responder<'r> for Flash { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { +impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Flash { + fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { trace_!("Flash: setting message: {}:{}", self.name, self.message); req.cookies().add(self.cookie()); self.inner.respond_to(req) diff --git a/core/lib/src/response/mod.rs b/core/lib/src/response/mod.rs index d183710868..bb0bb5e1fa 100644 --- a/core/lib/src/response/mod.rs +++ b/core/lib/src/response/mod.rs @@ -43,3 +43,5 @@ pub use self::stream::Stream; /// Type alias for the `Result` of a `Responder::respond` call. pub type Result<'r> = std::result::Result, crate::http::Status>; +/// Type alias for the `Result` of a `Responder::respond` call. +pub type ResultFuture<'r> = std::pin::Pin> + Send + 'r>>; diff --git a/core/lib/src/response/named_file.rs b/core/lib/src/response/named_file.rs index 5c98d6aafe..e5cfdcd5e5 100644 --- a/core/lib/src/response/named_file.rs +++ b/core/lib/src/response/named_file.rs @@ -78,16 +78,18 @@ 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. -impl Responder<'_> for NamedFile { - fn respond_to(self, req: &Request<'_>) -> response::Result<'static> { - let mut response = self.1.respond_to(req)?; - if let Some(ext) = self.0.extension() { - if let Some(ct) = ContentType::from_extension(&ext.to_string_lossy()) { - response.set_header(ct); +impl<'r> Responder<'r> for NamedFile { + fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { + Box::pin(async move { + 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()) { + response.set_header(ct); + } } - } - Ok(response) + Ok(response) + }) } } diff --git a/core/lib/src/response/redirect.rs b/core/lib/src/response/redirect.rs index 66fde42ca7..d891fdacbc 100644 --- a/core/lib/src/response/redirect.rs +++ b/core/lib/src/response/redirect.rs @@ -1,7 +1,7 @@ use std::convert::TryInto; use crate::request::Request; -use crate::response::{Response, Responder}; +use crate::response::{Response, Responder, ResultFuture}; use crate::http::uri::Uri; use crate::http::Status; @@ -147,16 +147,18 @@ 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. -impl Responder<'_> for Redirect { - fn respond_to(self, _: &Request<'_>) -> Result, Status> { - if let Some(uri) = self.1 { - Response::build() - .status(self.0) - .raw_header("Location", uri.to_string()) - .ok() - } else { - error!("Invalid URI used for redirect."); - Err(Status::InternalServerError) - } +impl<'r> Responder<'r> for Redirect { + fn respond_to(self, _: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async { + if let Some(uri) = self.1 { + Response::build() + .status(self.0) + .raw_header("Location", uri.to_string()) + .ok() + } 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 783417bcc5..c003b5032f 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -194,103 +194,127 @@ 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: &Request<'_>) -> response::Result<'r>; + fn respond_to(self, request: &'r Request<'_>) -> response::ResultFuture<'r>; } /// Returns a response with Content-Type `text/plain` and a fixed-size body /// containing the string `self`. Always returns `Ok`. impl<'r> Responder<'r> for &'r str { - fn respond_to(self, _: &Request<'_>) -> response::Result<'r> { - Response::build() - .header(ContentType::Plain) - .sized_body(Cursor::new(self)) - .ok() + fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'r> { + Box::pin(async move { + Response::build() + .header(ContentType::Plain) + .sized_body(Cursor::new(self)) + .ok() + }) } } /// Returns a response with Content-Type `text/plain` and a fixed-size body /// containing the string `self`. Always returns `Ok`. impl Responder<'_> for String { - fn respond_to(self, _: &Request<'_>) -> response::Result<'static> { - Response::build() - .header(ContentType::Plain) - .sized_body(Cursor::new(self)) - .ok() + fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'static> { + Box::pin(async move { + Response::build() + .header(ContentType::Plain) + .sized_body(Cursor::new(self)) + .ok() + }) } } /// Returns a response with Content-Type `application/octet-stream` and a /// fixed-size body containing the data in `self`. Always returns `Ok`. impl<'r> Responder<'r> for &'r [u8] { - fn respond_to(self, _: &Request<'_>) -> response::Result<'r> { - Response::build() - .header(ContentType::Binary) - .sized_body(Cursor::new(self)) - .ok() + fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'r> { + Box::pin(async move { + Response::build() + .header(ContentType::Binary) + .sized_body(Cursor::new(self)) + .ok() + }) } } /// Returns a response with Content-Type `application/octet-stream` and a /// fixed-size body containing the data in `self`. Always returns `Ok`. impl Responder<'_> for Vec { - fn respond_to(self, _: &Request<'_>) -> response::Result<'static> { - Response::build() - .header(ContentType::Binary) - .sized_body(Cursor::new(self)) - .ok() + fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'static> { + Box::pin(async move { + Response::build() + .header(ContentType::Binary) + .sized_body(Cursor::new(self)) + .ok() + }) } } /// Returns a response with a sized body for the file. Always returns `Ok`. impl Responder<'_> for File { - fn respond_to(self, _: &Request<'_>) -> response::Result<'static> { - let metadata = self.metadata(); - let stream = BufReader::new(tokio::fs::File::from_std(self)).compat(); - match metadata { - Ok(md) => Response::build().raw_body(Body::Sized(stream, md.len())).ok(), - Err(_) => Response::build().streamed_body(stream).ok() - } + fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'static> { + Box::pin(async move { + let metadata = self.metadata(); + let stream = BufReader::new(tokio::fs::File::from_std(self)).compat(); + match metadata { + 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`. impl Responder<'_> for () { - fn respond_to(self, _: &Request<'_>) -> response::Result<'static> { - Ok(Response::new()) + fn respond_to(self, _: &Request<'_>) -> response::ResultFuture<'static> { + Box::pin(async move { + 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 { - fn respond_to(self, req: &Request<'_>) -> response::Result<'r> { - self.map_or_else(|| { - warn_!("Response was `None`."); - Err(Status::NotFound) - }, |r| r.respond_to(req)) +impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Option { + fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { + Box::pin(async move { + match self { + Some(r) => r.respond_to(req).await, + None => { + warn_!("Response was `None`."); + Err(Status::NotFound) + }, + } + }) } } /// If `self` is `Ok`, responds with the wrapped `Responder`. Otherwise prints /// an error message with the `Err` value returns an `Err` of /// `Status::InternalServerError`. -impl<'r, R: Responder<'r>, E: fmt::Debug> Responder<'r> for Result { - default fn respond_to(self, req: &Request<'_>) -> response::Result<'r> { - self.map(|r| r.respond_to(req)).unwrap_or_else(|e| { - error_!("Response was a non-`Responder` `Err`: {:?}.", e); - Err(Status::InternalServerError) +impl<'r, R: Responder<'r> + Send + 'r, E: fmt::Debug + Send + 'r> Responder<'r> for Result { + default fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { + Box::pin(async move { + match self { + Ok(r) => r.respond_to(req).await, + Err(e) => { + error_!("Response was a non-`Responder` `Err`: {:?}.", e); + Err(Status::InternalServerError) + } + } }) } } /// Responds with the wrapped `Responder` in `self`, whether it is `Ok` or /// `Err`. -impl<'r, R: Responder<'r>, E: Responder<'r> + fmt::Debug> Responder<'r> for Result { - fn respond_to(self, req: &Request<'_>) -> response::Result<'r> { - match self { - Ok(responder) => responder.respond_to(req), - Err(responder) => responder.respond_to(req), - } +impl<'r, R: Responder<'r> + Send + 'r, E: Responder<'r> + fmt::Debug + Send + 'r> Responder<'r> for Result { + fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { + Box::pin(async move { + match self { + Ok(responder) => responder.respond_to(req).await, + Err(responder) => responder.respond_to(req).await, + } + }) } } @@ -308,21 +332,23 @@ impl<'r, R: Responder<'r>, E: Responder<'r> + fmt::Debug> Responder<'r> for Resu /// `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. -impl Responder<'_> for Status { - fn respond_to(self, _: &Request<'_>) -> response::Result<'static> { - match self.class() { - StatusClass::ClientError | StatusClass::ServerError => Err(self), - StatusClass::Success if self.code < 206 => { - Response::build().status(self).ok() +impl<'r> Responder<'r> for Status { + fn respond_to(self, _: &'r Request<'_>) -> response::ResultFuture<'r> { + Box::pin(async move { + match self.class() { + StatusClass::ClientError | StatusClass::ServerError => Err(self), + StatusClass::Success if self.code < 206 => { + Response::build().status(self).ok() + } + StatusClass::Informational if self.code == 100 => { + Response::build().status(self).ok() + } + _ => { + error_!("Invalid status used as responder: {}.", self); + warn_!("Fowarding to 500 (Internal Server Error) catcher."); + Err(Status::InternalServerError) + } } - StatusClass::Informational if self.code == 100 => { - Response::build().status(self).ok() - } - _ => { - error_!("Invalid status used as responder: {}.", self); - warn_!("Fowarding to 500 (Internal Server Error) catcher."); - Err(Status::InternalServerError) - } - } + }) } } diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs index 3339189f14..dd57b5180b 100644 --- a/core/lib/src/response/response.rs +++ b/core/lib/src/response/response.rs @@ -5,7 +5,7 @@ use std::pin::Pin; use futures::future::{Future, FutureExt}; use futures::io::{AsyncRead, AsyncReadExt}; -use crate::response::Responder; +use crate::response::{Responder, ResultFuture}; use crate::http::{Header, HeaderMap, Status, ContentType, Cookie}; use crate::ext::AsyncReadExt as _; @@ -1216,7 +1216,9 @@ use crate::request::Request; impl<'r> Responder<'r> for Response<'r> { /// This is the identity implementation. It simply returns `Ok(self)`. - fn respond_to(self, _: &Request<'_>) -> Result, Status> { - Ok(self) + fn respond_to(self, _: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async { + Ok(self) + }) } } diff --git a/core/lib/src/response/status.rs b/core/lib/src/response/status.rs index 8bce41a570..474f5e352e 100644 --- a/core/lib/src/response/status.rs +++ b/core/lib/src/response/status.rs @@ -11,7 +11,7 @@ use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; use crate::request::Request; -use crate::response::{Responder, Response}; +use crate::response::{Responder, Response, ResultFuture}; use crate::http::hyper::header; use crate::http::Status; @@ -40,15 +40,17 @@ pub struct Created(pub String, pub Option); /// responder should write the body of the response so that it contains /// information about the created resource. If no responder is provided, the /// response body will be empty. -impl<'r, R: Responder<'r>> Responder<'r> for Created { - default fn respond_to(self, req: &Request<'_>) -> Result, Status> { - let mut build = Response::build(); - if let Some(responder) = self.1 { - build.merge(responder.respond_to(req)?); - } - - // TODO.async: Using a raw header - build.status(Status::Created).raw_header(header::LOCATION.as_str(), self.0).ok() +impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Created { + default fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async move { + let mut build = Response::build(); + if let Some(responder) = self.1 { + build.merge(responder.respond_to(req).await?); + } + + // TODO.async: Using a raw header + build.status(Status::Created).raw_header(header::LOCATION.as_str(), self.0).ok() + }) } } @@ -56,21 +58,23 @@ impl<'r, R: Responder<'r>> Responder<'r> for Created { /// the response with the `Responder`, the `ETag` header is set conditionally if /// a `Responder` is provided that implements `Hash`. The `ETag` header is set /// to a hash value of the responder. -impl<'r, R: Responder<'r> + Hash> Responder<'r> for Created { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { - let mut hasher = DefaultHasher::default(); - let mut build = Response::build(); - if let Some(responder) = self.1 { - responder.hash(&mut hasher); - let hash = hasher.finish().to_string(); - - build.merge(responder.respond_to(req)?); - // TODO.async: Using a raw header - build.raw_header(header::ETAG.as_str(), format!("\"{}\"", hash)); - } +impl<'r, R: Responder<'r> + Hash + Send + 'r> Responder<'r> for Created { + fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async move { + let mut hasher = DefaultHasher::default(); + let mut build = Response::build(); + if let Some(responder) = self.1 { + responder.hash(&mut hasher); + let hash = hasher.finish().to_string(); + + build.merge(responder.respond_to(req).await?); + // TODO.async: Using a raw header + build.raw_header(header::ETAG.as_str(), format!("\"{}\"", hash)); + } - // TODO.async: Using a raw header - build.status(Status::Created).raw_header(header::LOCATION.as_str(), self.0).ok() + // TODO.async: Using a raw header + build.status(Status::Created).raw_header(header::LOCATION.as_str(), self.0).ok() + }) } } @@ -103,14 +107,16 @@ pub struct Accepted(pub Option); /// Sets the status code of the response to 202 Accepted. If the responder is /// `Some`, it is used to finalize the response. -impl<'r, R: Responder<'r>> Responder<'r> for Accepted { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { - let mut build = Response::build(); - if let Some(responder) = self.0 { - build.merge(responder.respond_to(req)?); - } - - build.status(Status::Accepted).ok() +impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Accepted { + fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async move { + let mut build = Response::build(); + if let Some(responder) = self.0 { + build.merge(responder.respond_to(req).await?); + } + + build.status(Status::Accepted).ok() + }) } } @@ -143,14 +149,16 @@ pub struct BadRequest(pub Option); /// Sets the status code of the response to 400 Bad Request. If the responder is /// `Some`, it is used to finalize the response. -impl<'r, R: Responder<'r>> Responder<'r> for BadRequest { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { - let mut build = Response::build(); - if let Some(responder) = self.0 { - build.merge(responder.respond_to(req)?); - } - - build.status(Status::BadRequest).ok() +impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for BadRequest { + fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async move { + let mut build = Response::build(); + if let Some(responder) = self.0 { + build.merge(responder.respond_to(req).await?); + } + + build.status(Status::BadRequest).ok() + }) } } @@ -170,11 +178,13 @@ impl<'r, R: Responder<'r>> Responder<'r> for BadRequest { pub struct NotFound(pub R); /// Sets the status code of the response to 404 Not Found. -impl<'r, R: Responder<'r>> Responder<'r> for NotFound { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { - Response::build_from(self.0.respond_to(req)?) - .status(Status::NotFound) - .ok() +impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for NotFound { + fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async move { + Response::build_from(self.0.respond_to(req).await?) + .status(Status::NotFound) + .ok() + }) } } @@ -194,11 +204,13 @@ pub struct Custom(pub Status, pub R); /// Sets the status code of the response and then delegates the remainder of the /// response to the wrapped responder. -impl<'r, R: Responder<'r>> Responder<'r> for Custom { - fn respond_to(self, req: &Request<'_>) -> Result, Status> { - Response::build_from(self.1.respond_to(req)?) - .status(self.0) - .ok() +impl<'r, R: Responder<'r> + Send + 'r> Responder<'r> for Custom { + fn respond_to(self, req: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async move { + Response::build_from(self.1.respond_to(req).await?) + .status(self.0) + .ok() + }) } } diff --git a/core/lib/src/response/stream.rs b/core/lib/src/response/stream.rs index 37dcfbdbbb..b5b49d0b72 100644 --- a/core/lib/src/response/stream.rs +++ b/core/lib/src/response/stream.rs @@ -3,7 +3,7 @@ use std::fmt::{self, Debug}; use futures::io::AsyncRead; use crate::request::Request; -use crate::response::{Response, Responder, DEFAULT_CHUNK_SIZE}; +use crate::response::{Response, Responder, ResultFuture, DEFAULT_CHUNK_SIZE}; use crate::http::Status; /// Streams a response to a client from an arbitrary `AsyncRead`er type. @@ -70,7 +70,9 @@ impl From for Stream { /// response is abandoned, and the response ends abruptly. An error is printed /// to the console with an indication of what went wrong. impl<'r, T: AsyncRead + Send + 'r> Responder<'r> for Stream { - fn respond_to(self, _: &Request<'_>) -> Result, Status> { - Response::build().chunked_body(self.0, self.1).ok() + fn respond_to(self, _: &'r Request<'_>) -> ResultFuture<'r> { + Box::pin(async { + Response::build().chunked_body(self.0, self.1).ok() + }) } } diff --git a/core/lib/src/router/mod.rs b/core/lib/src/router/mod.rs index d2e8c3b94a..2357728964 100644 --- a/core/lib/src/router/mod.rs +++ b/core/lib/src/router/mod.rs @@ -15,7 +15,7 @@ type Selector = Method; // A handler to use when one is needed temporarily. crate fn dummy_handler<'r>(r: &'r Request<'_>, _: crate::Data) -> std::pin::Pin> + Send + 'r>> { - futures::future::ready(crate::Outcome::from(r, ())).boxed() + crate::Outcome::from(r, ()) } #[derive(Default)] diff --git a/core/lib/tests/responder_lifetime-issue-345.rs b/core/lib/tests/responder_lifetime-issue-345.rs index 8ca2964a54..8e5b3ed2f2 100644 --- a/core/lib/tests/responder_lifetime-issue-345.rs +++ b/core/lib/tests/responder_lifetime-issue-345.rs @@ -14,7 +14,7 @@ pub struct CustomResponder<'r, R> { } impl<'r, R: Responder<'r>> Responder<'r> for CustomResponder<'r, R> { - fn respond_to(self, _: &rocket::Request) -> response::Result<'r> { + fn respond_to(self, _: &rocket::Request) -> response::ResultFuture<'r> { unimplemented!() } } From 50e3609713c75213f031fcf0f45aaa0e068692b8 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sat, 27 Jul 2019 09:15:23 -0700 Subject: [PATCH 10/25] Update helmet in contrib. --- contrib/lib/src/helmet/helmet.rs | 6 ++++-- contrib/lib/src/lib.rs | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/contrib/lib/src/helmet/helmet.rs b/contrib/lib/src/helmet/helmet.rs index c18251d87f..dd54bf0de7 100644 --- a/contrib/lib/src/helmet/helmet.rs +++ b/contrib/lib/src/helmet/helmet.rs @@ -196,8 +196,10 @@ impl Fairing for SpaceHelmet { } } - fn on_response(&self, _request: &Request<'_>, response: &mut Response<'_>) { - self.apply(response); + fn on_response<'a>(&'a self, _request: &'a Request<'_>, response: &'a mut Response<'_>) -> std::pin::Pin + Send + 'a>> { + Box::pin(async move { + self.apply(response); + }) } fn on_launch(&self, rocket: &Rocket) { diff --git a/contrib/lib/src/lib.rs b/contrib/lib/src/lib.rs index a30cda83bb..7b50f8dbf0 100644 --- a/contrib/lib/src/lib.rs +++ b/contrib/lib/src/lib.rs @@ -1,5 +1,6 @@ #![feature(crate_visibility_modifier)] #![feature(doc_cfg)] +#![feature(async_await)] #![doc(html_root_url = "https://api.rocket.rs/v0.5")] #![doc(html_favicon_url = "https://rocket.rs/v0.5/images/favicon.ico")] From 21fae467392079f3e22f4796c1b6e972e8891fad Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sat, 27 Jul 2019 16:43:13 -0700 Subject: [PATCH 11/25] Update rocket_contrib::json and json example for async. --- contrib/lib/Cargo.toml | 1 + contrib/lib/src/json.rs | 72 +++++++++++++++++++++++--------------- core/lib/src/lib.rs | 1 + examples/json/src/tests.rs | 8 ++--- 4 files changed, 49 insertions(+), 33 deletions(-) diff --git a/contrib/lib/Cargo.toml b/contrib/lib/Cargo.toml index df957bdbc5..84b5cd5519 100644 --- a/contrib/lib/Cargo.toml +++ b/contrib/lib/Cargo.toml @@ -42,6 +42,7 @@ memcache_pool = ["databases", "memcache", "r2d2-memcache"] [dependencies] # Global dependencies. +futures-preview = { version = "0.3.0-alpha.17" } rocket_contrib_codegen = { version = "0.5.0-dev", path = "../codegen", optional = true } rocket = { version = "0.5.0-dev", path = "../../core/lib/", default-features = false } log = "0.4" diff --git a/contrib/lib/src/json.rs b/contrib/lib/src/json.rs index 2a1be3825e..5481995155 100644 --- a/contrib/lib/src/json.rs +++ b/contrib/lib/src/json.rs @@ -15,14 +15,17 @@ //! ``` use std::ops::{Deref, DerefMut}; -use std::io::{self, Read}; +use std::io; use std::iter::FromIterator; +use futures::io::AsyncReadExt; + use rocket::request::Request; use rocket::outcome::Outcome::*; -use rocket::data::{Outcome, Transform, Transform::*, Transformed, Data, FromData}; +use rocket::data::{Transform::*, Transformed, Data, FromData, TransformFuture, FromDataFuture}; use rocket::response::{self, Responder, content}; use rocket::http::Status; +use rocket::AsyncReadExt as _; use serde::{Serialize, Serializer}; use serde::de::{Deserialize, Deserializer}; @@ -133,42 +136,53 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for Json { type Owned = String; type Borrowed = str; - fn transform(r: &Request<'_>, d: Data) -> Transform> { + fn transform(r: &Request<'_>, d: Data) -> TransformFuture<'a, Self::Owned, Self::Error> { let size_limit = r.limits().get("json").unwrap_or(LIMIT); - let mut s = String::with_capacity(512); - match d.open().take(size_limit).read_to_string(&mut s) { - Ok(_) => Borrowed(Success(s)), - Err(e) => Borrowed(Failure((Status::BadRequest, JsonError::Io(e)))) - } + Box::pin(async move { + let mut v = Vec::with_capacity(512); + let mut reader = d.open().take(size_limit); + match reader.read_to_end(&mut v).await { + Ok(_) => { + 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)))) + } + }) } - fn from_data(_: &Request<'_>, o: Transformed<'a, Self>) -> Outcome { - let string = o.borrowed()?; - match serde_json::from_str(&string) { - Ok(v) => Success(Json(v)), - Err(e) => { - error_!("Couldn't parse JSON body: {:?}", e); - if e.is_data() { - Failure((Status::UnprocessableEntity, JsonError::Parse(string, e))) - } else { - Failure((Status::BadRequest, JsonError::Parse(string, e))) + fn from_data(_: &Request<'_>, o: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error> { + Box::pin(async move { + let string = o.borrowed()?; + match serde_json::from_str(&string) { + Ok(v) => Success(Json(v)), + Err(e) => { + error_!("Couldn't parse JSON body: {:?}", e); + if e.is_data() { + Failure((Status::UnprocessableEntity, JsonError::Parse(string, e))) + } else { + Failure((Status::BadRequest, JsonError::Parse(string, e))) + } } } - } + }) } } /// Serializes the wrapped value into JSON. Returns a response with Content-Type /// JSON and a fixed-size body with the serialized value. If serialization /// fails, an `Err` of `Status::InternalServerError` is returned. -impl<'a, T: Serialize> Responder<'a> for Json { - fn respond_to(self, req: &Request<'_>) -> response::Result<'a> { - serde_json::to_string(&self.0).map(|string| { - content::Json(string).respond_to(req).unwrap() - }).map_err(|e| { - error_!("JSON failed to serialize: {:?}", e); - Status::InternalServerError - }) +impl<'r, T: Serialize> Responder<'r> for Json { + 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 { + error_!("JSON failed to serialize: {:?}", e); + Err(Status::InternalServerError) + }) + } } } @@ -283,9 +297,9 @@ impl FromIterator for JsonValue where serde_json::Value: FromIterator { /// Serializes the value into JSON. Returns a response with Content-Type JSON /// and a fixed-size body with the serialized value. -impl<'a> Responder<'a> for JsonValue { +impl<'r> Responder<'r> for JsonValue { #[inline] - fn respond_to(self, req: &Request<'_>) -> response::Result<'a> { + fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { content::Json(self.0.to_string()).respond_to(req) } } diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index b857d81891..e534b8e73c 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -141,6 +141,7 @@ pub use crate::router::Route; pub use crate::request::{Request, State}; pub use crate::catcher::Catcher; pub use crate::rocket::Rocket; +pub use ext::AsyncReadExt; /// Alias to [`Rocket::ignite()`] Creates a new instance of `Rocket`. pub fn ignite() -> Rocket { diff --git a/examples/json/src/tests.rs b/examples/json/src/tests.rs index 8b6909373f..21ecf3cdbe 100644 --- a/examples/json/src/tests.rs +++ b/examples/json/src/tests.rs @@ -10,13 +10,13 @@ fn bad_get_put() { let mut res = client.get("/message/99").header(ContentType::JSON).dispatch(); assert_eq!(res.status(), Status::NotFound); - let body = res.body_string().unwrap(); + let body = res.body_string_wait().unwrap(); assert!(body.contains("error")); assert!(body.contains("Resource was not found.")); // Try to get a message with an invalid ID. let mut res = client.get("/message/hi").header(ContentType::JSON).dispatch(); - let body = res.body_string().unwrap(); + let body = res.body_string_wait().unwrap(); assert_eq!(res.status(), Status::NotFound); assert!(body.contains("error")); @@ -52,7 +52,7 @@ fn post_get_put_get() { // Check that the message exists with the correct contents. let mut res = client.get("/message/1").header(ContentType::JSON).dispatch(); assert_eq!(res.status(), Status::Ok); - let body = res.body().unwrap().into_string().unwrap(); + let body = res.body_string_wait().unwrap(); assert!(body.contains("Hello, world!")); // Change the message contents. @@ -66,7 +66,7 @@ fn post_get_put_get() { // Check that the message exists with the updated contents. let mut res = client.get("/message/1").header(ContentType::JSON).dispatch(); assert_eq!(res.status(), Status::Ok); - let body = res.body().unwrap().into_string().unwrap(); + let body = res.body_string_wait().unwrap(); assert!(!body.contains("Hello, world!")); assert!(body.contains("Bye bye, world!")); } From e33078f4dd9035b92d6fea0fbcc98adb9d97e41a Mon Sep 17 00:00:00 2001 From: Redrield Date: Fri, 2 Aug 2019 09:31:30 -0400 Subject: [PATCH 12/25] Upgrade 'msgpack' for async in contrib and examples. --- contrib/lib/src/msgpack.rs | 69 ++++++++++++++++++++--------------- examples/msgpack/src/tests.rs | 4 +- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/contrib/lib/src/msgpack.rs b/contrib/lib/src/msgpack.rs index 354cd1dad5..c312f515c0 100644 --- a/contrib/lib/src/msgpack.rs +++ b/contrib/lib/src/msgpack.rs @@ -14,14 +14,16 @@ //! features = ["msgpack"] //! ``` -use std::io::Read; use std::ops::{Deref, DerefMut}; +use futures::io::AsyncReadExt; + use rocket::request::Request; use rocket::outcome::Outcome::*; -use rocket::data::{Outcome, Transform, Transform::*, Transformed, Data, FromData}; -use rocket::response::{self, Responder, content}; +use rocket::data::{Data, FromData, FromDataFuture, Transform::*, TransformFuture, Transformed}; use rocket::http::Status; +use rocket::response::{self, content, Responder}; +use rocket::AsyncReadExt as _; use serde::Serialize; use serde::de::Deserialize; @@ -40,7 +42,7 @@ pub use rmp_serde::decode::Error; /// request body. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; /// # type User = usize; @@ -64,7 +66,7 @@ pub use rmp_serde::decode::Error; /// response is set to `application/msgpack` automatically. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; /// # type User = usize; @@ -119,45 +121,52 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for MsgPack { type Owned = Vec; type Borrowed = [u8]; - fn transform(r: &Request<'_>, d: Data) -> Transform> { - let mut buf = Vec::new(); + fn transform(r: &Request<'_>, d: Data) -> TransformFuture<'a, Self::Owned, Self::Error> { let size_limit = r.limits().get("msgpack").unwrap_or(LIMIT); - match d.open().take(size_limit).read_to_end(&mut buf) { - Ok(_) => Borrowed(Success(buf)), - Err(e) => Borrowed(Failure((Status::BadRequest, Error::InvalidDataRead(e)))) - } + + Box::pin(async move { + let mut buf = Vec::new(); + let mut reader = d.open().take(size_limit); + match reader.read_to_end(&mut buf).await { + Ok(_) => Borrowed(Success(buf)), + Err(e) => Borrowed(Failure((Status::BadRequest, Error::InvalidDataRead(e)))), + } + }) } - fn from_data(_: &Request<'_>, o: Transformed<'a, Self>) -> Outcome { + fn from_data(_: &Request<'_>, o: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error> { use self::Error::*; - let buf = o.borrowed()?; - match rmp_serde::from_slice(&buf) { - Ok(val) => Success(MsgPack(val)), - Err(e) => { - error_!("Couldn't parse MessagePack body: {:?}", e); - match e { - TypeMismatch(_) | OutOfRange | LengthMismatch(_) => { - Failure((Status::UnprocessableEntity, e)) + Box::pin(async move { + let buf = o.borrowed()?; + match rmp_serde::from_slice(&buf) { + Ok(val) => Success(MsgPack(val)), + Err(e) => { + error_!("Couldn't parse MessagePack body: {:?}", e); + match e { + TypeMismatch(_) | OutOfRange | LengthMismatch(_) => { + Failure((Status::UnprocessableEntity, e)) + } + _ => Failure((Status::BadRequest, e)), } - _ => Failure((Status::BadRequest, e)) } } - } + }) } } /// Serializes the wrapped value into MessagePack. Returns a response with /// Content-Type `MsgPack` and a fixed-size body with the serialization. If /// serialization fails, an `Err` of `Status::InternalServerError` is returned. -impl Responder<'static> for MsgPack { - fn respond_to(self, req: &Request<'_>) -> response::Result<'static> { - rmp_serde::to_vec(&self.0).map_err(|e| { - error_!("MsgPack failed to serialize: {:?}", e); - Status::InternalServerError - }).and_then(|buf| { - content::MsgPack(buf).respond_to(req) - }) +impl<'r, T: Serialize> Responder<'r> for MsgPack { + fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { + match rmp_serde::to_vec(&self.0) { + Ok(buf) => content::MsgPack(buf).respond_to(req), + Err(e) => Box::pin(async move { + error_!("MsgPack failed to serialize: {:?}", e); + Err(Status::InternalServerError) + }), + } } } diff --git a/examples/msgpack/src/tests.rs b/examples/msgpack/src/tests.rs index 0c7e346113..5aa35c7bfd 100644 --- a/examples/msgpack/src/tests.rs +++ b/examples/msgpack/src/tests.rs @@ -16,7 +16,7 @@ fn msgpack_get() { assert_eq!(res.content_type(), Some(ContentType::MsgPack)); // Check that the message is `[1, "Hello, world!"]` - assert_eq!(&res.body_bytes().unwrap(), + assert_eq!(&res.body_bytes_wait().unwrap(), &[146, 1, 173, 72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33]); } @@ -30,5 +30,5 @@ fn msgpack_post() { .dispatch(); assert_eq!(res.status(), Status::Ok); - assert_eq!(res.body_string(), Some("Goodbye, world!".into())); + assert_eq!(res.body_string_wait(), Some("Goodbye, world!".into())); } From 1d863f57c32b057447ff8cb1a51602085a4edd41 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 4 Aug 2019 15:19:37 -0700 Subject: [PATCH 13/25] Update 'rocket_contrib::templates' and examples that use it for async. --- contrib/lib/src/templates/metadata.rs | 6 ++--- contrib/lib/src/templates/mod.rs | 29 +++++++++++++--------- contrib/lib/tests/templates.rs | 2 +- examples/cookies/src/tests.rs | 2 +- examples/handlebars_templates/src/tests.rs | 6 ++--- examples/session/src/tests.rs | 4 +-- examples/tera_templates/src/tests.rs | 6 ++--- 7 files changed, 30 insertions(+), 25 deletions(-) diff --git a/contrib/lib/src/templates/metadata.rs b/contrib/lib/src/templates/metadata.rs index b60bb6739f..4503fa3e8d 100644 --- a/contrib/lib/src/templates/metadata.rs +++ b/contrib/lib/src/templates/metadata.rs @@ -12,7 +12,7 @@ use crate::templates::ContextManager; /// used as a request guard in any request handler. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket_contrib; /// use rocket_contrib::templates::{Template, Metadata}; @@ -46,7 +46,7 @@ impl Metadata<'_> { /// # Example /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; /// # @@ -67,7 +67,7 @@ impl Metadata<'_> { /// # Example /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; /// # diff --git a/contrib/lib/src/templates/mod.rs b/contrib/lib/src/templates/mod.rs index 216a3c1aae..5fd7011377 100644 --- a/contrib/lib/src/templates/mod.rs +++ b/contrib/lib/src/templates/mod.rs @@ -37,7 +37,7 @@ //! of the template file minus the last two extensions, from a handler. //! //! ```rust -//! # #![feature(proc_macro_hygiene)] +//! # #![feature(proc_macro_hygiene, async_await)] //! # #[macro_use] extern crate rocket; //! # #[macro_use] extern crate rocket_contrib; //! # fn context() { } @@ -184,7 +184,7 @@ const DEFAULT_TEMPLATE_DIR: &str = "templates"; /// returned from a request handler directly: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket_contrib; /// # fn context() { } @@ -387,16 +387,21 @@ 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. -impl Responder<'static> for Template { - fn respond_to(self, req: &Request<'_>) -> response::Result<'static> { - let ctxt = req.guard::>().succeeded().ok_or_else(|| { - error_!("Uninitialized template context: missing fairing."); - info_!("To use templates, you must attach `Template::fairing()`."); - info_!("See the `Template` documentation for more information."); - Status::InternalServerError - })?.inner().context(); +impl<'r> Responder<'r> for Template { + fn respond_to(self, req: &'r Request<'_>) -> response::ResultFuture<'r> { + Box::pin(async move { + let (render, content_type) = { + let ctxt = req.guard::>().succeeded().ok_or_else(|| { + error_!("Uninitialized template context: missing fairing."); + info_!("To use templates, you must attach `Template::fairing()`."); + info_!("See the `Template` documentation for more information."); + Status::InternalServerError + })?.inner().context(); + + self.finalize(&ctxt)? + }; - let (render, content_type) = self.finalize(&ctxt)?; - Content(content_type, render).respond_to(req) + Content(content_type, render).respond_to(req).await + }) } } diff --git a/contrib/lib/tests/templates.rs b/contrib/lib/tests/templates.rs index 25c62f90cc..371adfd587 100644 --- a/contrib/lib/tests/templates.rs +++ b/contrib/lib/tests/templates.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[cfg(feature = "templates")] #[macro_use] extern crate rocket; diff --git a/examples/cookies/src/tests.rs b/examples/cookies/src/tests.rs index 4f62c28ed9..34071efb40 100644 --- a/examples/cookies/src/tests.rs +++ b/examples/cookies/src/tests.rs @@ -30,7 +30,7 @@ fn test_body(optional_cookie: Option>, expected_body: String) { }; assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some(expected_body)); + assert_eq!(response.body_string_wait(), Some(expected_body)); } #[test] diff --git a/examples/handlebars_templates/src/tests.rs b/examples/handlebars_templates/src/tests.rs index 89d159f977..e653d36107 100644 --- a/examples/handlebars_templates/src/tests.rs +++ b/examples/handlebars_templates/src/tests.rs @@ -33,7 +33,7 @@ fn test_root() { let expected = Template::show(client.rocket(), "error/404", &map).unwrap(); assert_eq!(response.status(), Status::NotFound); - assert_eq!(response.body_string(), Some(expected)); + assert_eq!(response.body_string_wait(), Some(expected)); }); } } @@ -51,7 +51,7 @@ fn test_name() { let expected = Template::show(client.rocket(), "index", &context).unwrap(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some(expected)); + assert_eq!(response.body_string_wait(), Some(expected)); }); } @@ -64,6 +64,6 @@ fn test_404() { let expected = Template::show(client.rocket(), "error/404", &map).unwrap(); assert_eq!(response.status(), Status::NotFound); - assert_eq!(response.body_string(), Some(expected)); + assert_eq!(response.body_string_wait(), Some(expected)); }); } diff --git a/examples/session/src/tests.rs b/examples/session/src/tests.rs index d6ab7771ad..5de8fb7f55 100644 --- a/examples/session/src/tests.rs +++ b/examples/session/src/tests.rs @@ -35,7 +35,7 @@ fn can_login() { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/login").dispatch(); - let body = response.body_string().unwrap(); + let body = response.body_string_wait().unwrap(); assert_eq!(response.status(), Status::Ok); assert!(body.contains("Please login to continue.")); } @@ -54,7 +54,7 @@ fn login_logout_succeeds() { // Ensure we're logged in. let mut response = client.get("/").cookie(login_cookie.clone()).dispatch(); - let body = response.body_string().unwrap(); + let body = response.body_string_wait().unwrap(); assert_eq!(response.status(), Status::Ok); assert!(body.contains("Logged in with user ID 1")); diff --git a/examples/tera_templates/src/tests.rs b/examples/tera_templates/src/tests.rs index 9fc00270a6..d35029571a 100644 --- a/examples/tera_templates/src/tests.rs +++ b/examples/tera_templates/src/tests.rs @@ -32,7 +32,7 @@ fn test_root() { let expected = Template::show(client.rocket(), "error/404", &map).unwrap(); assert_eq!(response.status(), Status::NotFound); - assert_eq!(response.body_string(), Some(expected)); + assert_eq!(response.body_string_wait(), Some(expected)); }); } } @@ -48,7 +48,7 @@ fn test_name() { let expected = Template::show(client.rocket(), "index", &context).unwrap(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some(expected)); + assert_eq!(response.body_string_wait(), Some(expected)); }); } @@ -61,6 +61,6 @@ fn test_404() { let expected = Template::show(client.rocket(), "error/404", &map).unwrap(); assert_eq!(response.status(), Status::NotFound); - assert_eq!(response.body_string(), Some(expected)); + assert_eq!(response.body_string_wait(), Some(expected)); }); } From 2edee386bb94efa42700fd825302518161d9abae Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 4 Aug 2019 15:22:13 -0700 Subject: [PATCH 14/25] Fix 'rocket_contrib::json' tests for async. --- contrib/lib/src/json.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/contrib/lib/src/json.rs b/contrib/lib/src/json.rs index 5481995155..481054bd2c 100644 --- a/contrib/lib/src/json.rs +++ b/contrib/lib/src/json.rs @@ -44,7 +44,7 @@ pub use serde_json::{json_internal, json_internal_vec}; /// or from [`serde`]. The data is parsed from the HTTP request body. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; /// # type User = usize; @@ -68,7 +68,7 @@ pub use serde_json::{json_internal, json_internal_vec}; /// set to `application/json` automatically. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # extern crate rocket_contrib; /// # type User = usize; @@ -224,7 +224,7 @@ impl DerefMut for Json { /// fashion during request handling. This looks something like: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket_contrib; /// use rocket_contrib::json::JsonValue; @@ -319,7 +319,7 @@ impl<'r> Responder<'r> for JsonValue { /// value created with this macro can be returned from a handler as follows: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket_contrib; /// use rocket_contrib::json::JsonValue; From d278d48715611094213321e0e02b617fbdd1bb89 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 4 Aug 2019 15:23:36 -0700 Subject: [PATCH 15/25] Fix 'rocket_contrib::helmet' tests for async. --- contrib/lib/tests/helmet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/lib/tests/helmet.rs b/contrib/lib/tests/helmet.rs index bd67537ba9..50ac77c603 100644 --- a/contrib/lib/tests/helmet.rs +++ b/contrib/lib/tests/helmet.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] #[cfg(feature = "helmet")] From 6675bb5d64736ff4756ed1cfacc1031460167338 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 4 Aug 2019 15:29:47 -0700 Subject: [PATCH 16/25] Use 'body_string_wait' in all example tests. --- examples/content_types/src/tests.rs | 2 +- examples/errors/src/tests.rs | 2 +- examples/fairings/src/tests.rs | 8 ++++---- examples/form_kitchen_sink/src/tests.rs | 2 +- examples/form_validation/src/tests.rs | 2 +- examples/hello_2015/src/tests.rs | 6 +++--- examples/hello_2018/src/tests.rs | 6 +++--- examples/hello_person/src/tests.rs | 2 +- examples/hello_world/src/tests.rs | 2 +- examples/managed_queue/src/tests.rs | 2 +- examples/manual_routes/src/tests.rs | 4 ++-- examples/optional_redirect/src/tests.rs | 2 +- examples/pastebin/src/tests.rs | 6 +++--- examples/query_params/src/tests.rs | 18 +++++++++--------- examples/ranking/src/tests.rs | 2 +- examples/raw_sqlite/src/tests.rs | 2 +- examples/raw_upload/src/tests.rs | 4 ++-- examples/redirect/src/tests.rs | 2 +- examples/request_guard/src/main.rs | 2 +- examples/state/src/tests.rs | 2 +- examples/stream/src/tests.rs | 4 ++-- examples/testing/src/main.rs | 2 +- examples/tls/src/tests.rs | 2 +- examples/uuid/src/tests.rs | 2 +- 24 files changed, 44 insertions(+), 44 deletions(-) diff --git a/examples/content_types/src/tests.rs b/examples/content_types/src/tests.rs index afe31228fc..5bae9a7fcc 100644 --- a/examples/content_types/src/tests.rs +++ b/examples/content_types/src/tests.rs @@ -12,7 +12,7 @@ fn test(method: Method, uri: &str, header: H, status: Status, body: String) let client = Client::new(rocket).unwrap(); let mut response = client.req(method, uri).header(header).dispatch(); assert_eq!(response.status(), status); - assert_eq!(response.body_string(), Some(body)); + assert_eq!(response.body_string_wait(), Some(body)); } #[test] diff --git a/examples/errors/src/tests.rs b/examples/errors/src/tests.rs index 78f4142229..e93ab2a81d 100644 --- a/examples/errors/src/tests.rs +++ b/examples/errors/src/tests.rs @@ -9,7 +9,7 @@ fn test(uri: &str, status: Status, body: String) { let client = Client::new(rocket).unwrap(); let mut response = client.get(uri).dispatch(); assert_eq!(response.status(), status); - assert_eq!(response.body_string(), Some(body)); + assert_eq!(response.body_string_wait(), Some(body)); } #[test] diff --git a/examples/fairings/src/tests.rs b/examples/fairings/src/tests.rs index 37622e50bb..421e2aa6cf 100644 --- a/examples/fairings/src/tests.rs +++ b/examples/fairings/src/tests.rs @@ -5,7 +5,7 @@ use rocket::local::Client; fn rewrite_get_put() { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("Hello, fairings!".into())); + assert_eq!(response.body_string_wait(), Some("Hello, fairings!".into())); } #[test] @@ -17,7 +17,7 @@ fn counts() { // Check the GET count, taking into account _this_ GET request. let mut response = client.get("/counts").dispatch(); - assert_eq!(response.body_string(), Some("Get: 2\nPost: 0".into())); + assert_eq!(response.body_string_wait(), Some("Get: 2\nPost: 0".into())); // Issue 1 more GET request and a POST. client.get("/").dispatch(); @@ -25,7 +25,7 @@ fn counts() { // Check the counts. let mut response = client.get("/counts").dispatch(); - assert_eq!(response.body_string(), Some("Get: 4\nPost: 1".into())); + assert_eq!(response.body_string_wait(), Some("Get: 4\nPost: 1".into())); } #[test] @@ -34,5 +34,5 @@ fn token() { // Ensure the token is '123', which is what we have in `Rocket.toml`. let mut res = client.get("/token").dispatch(); - assert_eq!(res.body_string(), Some("123".into())); + assert_eq!(res.body_string_wait(), Some("123".into())); } diff --git a/examples/form_kitchen_sink/src/tests.rs b/examples/form_kitchen_sink/src/tests.rs index cafbe685b7..55f15cf5bb 100644 --- a/examples/form_kitchen_sink/src/tests.rs +++ b/examples/form_kitchen_sink/src/tests.rs @@ -20,7 +20,7 @@ fn assert_form_eq(client: &Client, form_str: &str, expected: String) { .body(form_str) .dispatch(); - assert_eq!(res.body_string(), Some(expected)); + assert_eq!(res.body_string_wait(), Some(expected)); } fn assert_valid_form(client: &Client, input: &FormInput<'_>) { diff --git a/examples/form_validation/src/tests.rs b/examples/form_validation/src/tests.rs index 5a927eaa62..4a7e939a4c 100644 --- a/examples/form_validation/src/tests.rs +++ b/examples/form_validation/src/tests.rs @@ -14,7 +14,7 @@ fn test_login(user: &str, pass: &str, age: &str, status: Status, body: T) assert_eq!(response.status(), status); if let Some(expected_str) = body.into() { - let body_str = response.body_string(); + let body_str = response.body_string_wait(); assert!(body_str.map_or(false, |s| s.contains(expected_str))); } } diff --git a/examples/hello_2015/src/tests.rs b/examples/hello_2015/src/tests.rs index ab69295793..685d0727cd 100644 --- a/examples/hello_2015/src/tests.rs +++ b/examples/hello_2015/src/tests.rs @@ -5,7 +5,7 @@ fn hello_world() { let rocket = rocket::ignite().mount("/", routes![super::hello]); let client = Client::new(rocket).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("Hello, Rust 2015!".into())); + assert_eq!(response.body_string_wait(), Some("Hello, Rust 2015!".into())); } // Tests unrelated to the example. @@ -38,13 +38,13 @@ mod scoped_uri_tests { fn test_inner_hello() { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("Hello! Try /Rust%202015.".into())); + assert_eq!(response.body_string_wait(), Some("Hello! Try /Rust%202015.".into())); } #[test] fn test_hello_name() { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/Rust%202015").dispatch(); - assert_eq!(response.body_string().unwrap(), "Hello, Rust 2015! This is /Rust%202015."); + assert_eq!(response.body_string_wait().unwrap(), "Hello, Rust 2015! This is /Rust%202015."); } } diff --git a/examples/hello_2018/src/tests.rs b/examples/hello_2018/src/tests.rs index 804136c34e..9fa2575d02 100644 --- a/examples/hello_2018/src/tests.rs +++ b/examples/hello_2018/src/tests.rs @@ -5,7 +5,7 @@ fn hello_world() { let rocket = rocket::ignite().mount("/", routes![super::hello]); let client = Client::new(rocket).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("Hello, Rust 2018!".into())); + assert_eq!(response.body_string_wait(), Some("Hello, Rust 2018!".into())); } // Tests unrelated to the example. @@ -38,13 +38,13 @@ mod scoped_uri_tests { fn test_inner_hello() { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("Hello! Try /Rust%202018.".into())); + assert_eq!(response.body_string_wait(), Some("Hello! Try /Rust%202018.".into())); } #[test] fn test_hello_name() { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/Rust%202018").dispatch(); - assert_eq!(response.body_string().unwrap(), "Hello, Rust 2018! This is /Rust%202018."); + assert_eq!(response.body_string_wait().unwrap(), "Hello, Rust 2018! This is /Rust%202018."); } } diff --git a/examples/hello_person/src/tests.rs b/examples/hello_person/src/tests.rs index 35fd399912..c3dbdff250 100644 --- a/examples/hello_person/src/tests.rs +++ b/examples/hello_person/src/tests.rs @@ -7,7 +7,7 @@ fn client() -> Client { fn test(uri: &str, expected: String) { let client = client(); - assert_eq!(client.get(uri).dispatch().body_string(), Some(expected)); + assert_eq!(client.get(uri).dispatch().body_string_wait(), Some(expected)); } fn test_404(uri: &str) { diff --git a/examples/hello_world/src/tests.rs b/examples/hello_world/src/tests.rs index 80bf4aeb8d..069157dadd 100644 --- a/examples/hello_world/src/tests.rs +++ b/examples/hello_world/src/tests.rs @@ -5,5 +5,5 @@ fn hello_world() { let rocket = rocket::ignite().mount("/", routes![super::hello]); let client = Client::new(rocket).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("Hello, world!".into())); + assert_eq!(response.body_string_wait(), Some("Hello, world!".into())); } diff --git a/examples/managed_queue/src/tests.rs b/examples/managed_queue/src/tests.rs index e5a0fcd368..101267ef51 100644 --- a/examples/managed_queue/src/tests.rs +++ b/examples/managed_queue/src/tests.rs @@ -9,5 +9,5 @@ fn test_push_pop() { assert_eq!(response.status(), Status::Ok); let mut response = client.get("/pop").dispatch(); - assert_eq!(response.body_string(), Some("test1".to_string())); + assert_eq!(response.body_string_wait(), Some("test1".to_string())); } diff --git a/examples/manual_routes/src/tests.rs b/examples/manual_routes/src/tests.rs index e07709fd94..530e4fbc6b 100644 --- a/examples/manual_routes/src/tests.rs +++ b/examples/manual_routes/src/tests.rs @@ -6,7 +6,7 @@ fn test(uri: &str, content_type: ContentType, status: Status, body: String) { let client = Client::new(rocket()).unwrap();; let mut response = client.get(uri).header(content_type).dispatch(); assert_eq!(response.status(), status); - assert_eq!(response.body_string(), Some(body)); + assert_eq!(response.body_string_wait(), Some(body)); } #[test] @@ -46,7 +46,7 @@ fn test_upload() { // Ensure we get back the same body. let mut response = client.get("/upload").dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some(expected_body)); + assert_eq!(response.body_string_wait(), Some(expected_body)); } #[test] diff --git a/examples/optional_redirect/src/tests.rs b/examples/optional_redirect/src/tests.rs index 2e2875ee11..5459100373 100644 --- a/examples/optional_redirect/src/tests.rs +++ b/examples/optional_redirect/src/tests.rs @@ -12,7 +12,7 @@ fn test_200(uri: &str, expected_body: &str) { let client = client(); let mut response = client.get(uri).dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some(expected_body.to_string())); + assert_eq!(response.body_string_wait(), Some(expected_body.to_string())); } fn test_303(uri: &str, expected_location: &str) { diff --git a/examples/pastebin/src/tests.rs b/examples/pastebin/src/tests.rs index fa153294f5..063f4f0227 100644 --- a/examples/pastebin/src/tests.rs +++ b/examples/pastebin/src/tests.rs @@ -14,20 +14,20 @@ fn check_index() { let mut response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::Plain)); - assert_eq!(response.body_string(), Some(index().into())) + assert_eq!(response.body_string_wait(), Some(index().into())) } fn upload_paste(client: &Client, body: &str) -> String { let mut response = client.post("/").body(body).dispatch(); assert_eq!(response.status(), Status::Ok); assert_eq!(response.content_type(), Some(ContentType::Plain)); - extract_id(&response.body_string().unwrap()).unwrap() + extract_id(&response.body_string_wait().unwrap()).unwrap() } fn download_paste(client: &Client, id: &str) -> String { let mut response = client.get(format!("/{}", id)).dispatch(); assert_eq!(response.status(), Status::Ok); - response.body_string().unwrap() + response.body_string_wait().unwrap() } #[test] diff --git a/examples/query_params/src/tests.rs b/examples/query_params/src/tests.rs index 137a497f29..061e46b9f4 100644 --- a/examples/query_params/src/tests.rs +++ b/examples/query_params/src/tests.rs @@ -12,12 +12,12 @@ macro_rules! run_test { #[test] fn age_and_name_params() { run_test!("?age=10&name=john", |mut response: Response<'_>| { - assert_eq!(response.body_string(), + assert_eq!(response.body_string_wait(), Some("Hello, 10 year old named john!".into())); }); run_test!("?age=20&name=john", |mut response: Response<'_>| { - assert_eq!(response.body_string(), + assert_eq!(response.body_string_wait(), Some("20 years old? Hi, john!".into())); }); } @@ -25,12 +25,12 @@ fn age_and_name_params() { #[test] fn age_param_only() { run_test!("?age=10", |mut response: Response<'_>| { - assert_eq!(response.body_string(), + assert_eq!(response.body_string_wait(), Some("We're gonna need a name, and only a name.".into())); }); run_test!("?age=20", |mut response: Response<'_>| { - assert_eq!(response.body_string(), + assert_eq!(response.body_string_wait(), Some("We're gonna need a name, and only a name.".into())); }); } @@ -38,19 +38,19 @@ fn age_param_only() { #[test] fn name_param_only() { run_test!("?name=John", |mut response: Response<'_>| { - assert_eq!(response.body_string(), Some("Hello John!".into())); + assert_eq!(response.body_string_wait(), Some("Hello John!".into())); }); } #[test] fn no_params() { run_test!("", |mut response: Response<'_>| { - assert_eq!(response.body_string(), + assert_eq!(response.body_string_wait(), Some("We're gonna need a name, and only a name.".into())); }); run_test!("?", |mut response: Response<'_>| { - assert_eq!(response.body_string(), + assert_eq!(response.body_string_wait(), Some("We're gonna need a name, and only a name.".into())); }); } @@ -58,12 +58,12 @@ fn no_params() { #[test] fn extra_params() { run_test!("?age=20&name=Bob&extra", |mut response: Response<'_>| { - assert_eq!(response.body_string(), + assert_eq!(response.body_string_wait(), Some("20 years old? Hi, Bob!".into())); }); run_test!("?age=30&name=Bob&extra", |mut response: Response<'_>| { - assert_eq!(response.body_string(), + assert_eq!(response.body_string_wait(), Some("We're gonna need a name, and only a name.".into())); }); } diff --git a/examples/ranking/src/tests.rs b/examples/ranking/src/tests.rs index e638150a00..3573dcb6e7 100644 --- a/examples/ranking/src/tests.rs +++ b/examples/ranking/src/tests.rs @@ -4,7 +4,7 @@ fn test(uri: &str, expected: String) { let rocket = rocket::ignite().mount("/", routes![super::hello, super::hi]); let client = Client::new(rocket).unwrap(); let mut response = client.get(uri).dispatch(); - assert_eq!(response.body_string(), Some(expected)); + assert_eq!(response.body_string_wait(), Some(expected)); } #[test] diff --git a/examples/raw_sqlite/src/tests.rs b/examples/raw_sqlite/src/tests.rs index 3fbb8062a8..6819a6771d 100644 --- a/examples/raw_sqlite/src/tests.rs +++ b/examples/raw_sqlite/src/tests.rs @@ -5,5 +5,5 @@ use rocket::local::Client; fn hello() { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("Rocketeer".into())); + assert_eq!(response.body_string_wait(), Some("Rocketeer".into())); } diff --git a/examples/raw_upload/src/tests.rs b/examples/raw_upload/src/tests.rs index 8e9a7b379f..7ae2d33219 100644 --- a/examples/raw_upload/src/tests.rs +++ b/examples/raw_upload/src/tests.rs @@ -11,7 +11,7 @@ const UPLOAD_CONTENTS: &str = "Hey! I'm going to be uploaded. :D Yay!"; fn test_index() { let client = Client::new(super::rocket()).unwrap(); let mut res = client.get("/").dispatch(); - assert_eq!(res.body_string(), Some(super::index().to_string())); + assert_eq!(res.body_string_wait(), Some(super::index().to_string())); } #[test] @@ -28,7 +28,7 @@ fn test_raw_upload() { .dispatch(); assert_eq!(res.status(), Status::Ok); - assert_eq!(res.body_string(), Some(UPLOAD_CONTENTS.len().to_string())); + assert_eq!(res.body_string_wait(), Some(UPLOAD_CONTENTS.len().to_string())); // Ensure we find the body in the /tmp/upload.txt file. let mut file_contents = String::new(); diff --git a/examples/redirect/src/tests.rs b/examples/redirect/src/tests.rs index 51b6deb65e..d0c213d1ef 100644 --- a/examples/redirect/src/tests.rs +++ b/examples/redirect/src/tests.rs @@ -26,5 +26,5 @@ fn test_root() { fn test_login() { let client = client(); let mut r = client.get("/login").dispatch(); - assert_eq!(r.body_string(), Some("Hi! Please log in before continuing.".into())); + assert_eq!(r.body_string_wait(), Some("Hi! Please log in before continuing.".into())); } diff --git a/examples/request_guard/src/main.rs b/examples/request_guard/src/main.rs index 6ff46d76cf..d1085c8483 100644 --- a/examples/request_guard/src/main.rs +++ b/examples/request_guard/src/main.rs @@ -43,7 +43,7 @@ mod test { let mut response = req.dispatch(); let expect = format!("Your request contained {} headers!", headers.len()); - assert_eq!(response.body_string(), Some(expect)); + assert_eq!(response.body_string_wait(), Some(expect)); } #[test] diff --git a/examples/state/src/tests.rs b/examples/state/src/tests.rs index d27faf6a73..9ea0263d54 100644 --- a/examples/state/src/tests.rs +++ b/examples/state/src/tests.rs @@ -8,7 +8,7 @@ fn register_hit(client: &Client) { fn get_count(client: &Client) -> usize { let mut response = client.get("/count").dispatch(); - response.body_string().and_then(|s| s.parse().ok()).unwrap() + response.body_string_wait().and_then(|s| s.parse().ok()).unwrap() } #[test] diff --git a/examples/stream/src/tests.rs b/examples/stream/src/tests.rs index 50b29762a2..28c26c6c37 100644 --- a/examples/stream/src/tests.rs +++ b/examples/stream/src/tests.rs @@ -9,7 +9,7 @@ fn test_root() { let mut res = client.get("/").dispatch(); // Check that we have exactly 25,000 'a'. - let res_str = res.body_string().unwrap(); + let res_str = res.body_string_wait().unwrap(); assert_eq!(res_str.len(), 25000); for byte in res_str.as_bytes() { assert_eq!(*byte, b'a'); @@ -26,7 +26,7 @@ fn test_file() { // Get the big file contents, hopefully. let client = Client::new(super::rocket()).unwrap(); let mut res = client.get("/big_file").dispatch(); - assert_eq!(res.body_string(), Some(CONTENTS.into())); + assert_eq!(res.body_string_wait(), Some(CONTENTS.into())); // Delete the 'big_file'. fs::remove_file(super::FILENAME).expect("remove big_file"); diff --git a/examples/testing/src/main.rs b/examples/testing/src/main.rs index 9027cda38a..cfcaa2b187 100644 --- a/examples/testing/src/main.rs +++ b/examples/testing/src/main.rs @@ -26,6 +26,6 @@ mod test { let client = Client::new(rocket()).unwrap(); let mut response = client.get("/").dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string(), Some("Hello, world!".into())); + assert_eq!(response.body_string_wait(), Some("Hello, world!".into())); } } diff --git a/examples/tls/src/tests.rs b/examples/tls/src/tests.rs index 80bf4aeb8d..069157dadd 100644 --- a/examples/tls/src/tests.rs +++ b/examples/tls/src/tests.rs @@ -5,5 +5,5 @@ fn hello_world() { let rocket = rocket::ignite().mount("/", routes![super::hello]); let client = Client::new(rocket).unwrap(); let mut response = client.get("/").dispatch(); - assert_eq!(response.body_string(), Some("Hello, world!".into())); + assert_eq!(response.body_string_wait(), Some("Hello, world!".into())); } diff --git a/examples/uuid/src/tests.rs b/examples/uuid/src/tests.rs index fa31e31df8..adbe231b78 100644 --- a/examples/uuid/src/tests.rs +++ b/examples/uuid/src/tests.rs @@ -5,7 +5,7 @@ use rocket::http::Status; fn test(uri: &str, expected: &str) { let client = Client::new(rocket()).unwrap(); let mut res = client.get(uri).dispatch(); - assert_eq!(res.body_string(), Some(expected.into())); + assert_eq!(res.body_string_wait(), Some(expected.into())); } fn test_404(uri: &str) { From c82e24dbe60e1fa4a068726758f068b7d050815e Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 4 Aug 2019 15:36:55 -0700 Subject: [PATCH 17/25] Update 'fairings' example for async. --- examples/fairings/src/main.rs | 38 ++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/examples/fairings/src/main.rs b/examples/fairings/src/main.rs index 7b7ca500e1..da86f1eb86 100644 --- a/examples/fairings/src/main.rs +++ b/examples/fairings/src/main.rs @@ -35,20 +35,24 @@ impl Fairing for Counter { } } - fn on_response(&self, request: &Request<'_>, response: &mut Response<'_>) { - if response.status() != Status::NotFound { - return - } + fn on_response<'a, 'r>(&'a self, request: &'a Request<'r>, response: &'a mut Response<'r>) + -> std::pin::Pin + Send + 'a>> + { + Box::pin(async move { + if response.status() != Status::NotFound { + return + } - if request.method() == Method::Get && request.uri().path() == "/counts" { - let get_count = self.get.load(Ordering::Relaxed); - let post_count = self.post.load(Ordering::Relaxed); + if request.method() == Method::Get && request.uri().path() == "/counts" { + let get_count = self.get.load(Ordering::Relaxed); + let post_count = self.post.load(Ordering::Relaxed); - 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)); - } + 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)); + } + }) } } @@ -82,10 +86,12 @@ fn rocket() -> rocket::Rocket { } })) .attach(AdHoc::on_response("Response Rewriter", |req, res| { - if req.uri().path() == "/" { - println!(" => Rewriting response body."); - res.set_sized_body(Cursor::new("Hello, fairings!")); - } + Box::pin(async move { + if req.uri().path() == "/" { + println!(" => Rewriting response body."); + res.set_sized_body(Cursor::new("Hello, fairings!")); + } + }) })) } From e85c733c7d16930f19b27f70b0ee3e7ce2a50ef0 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 4 Aug 2019 15:42:38 -0700 Subject: [PATCH 18/25] Fix 'rocket_contrib::databases' tests for async. --- contrib/lib/src/databases.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/lib/src/databases.rs b/contrib/lib/src/databases.rs index 4afde63555..64a9e3f5ac 100644 --- a/contrib/lib/src/databases.rs +++ b/contrib/lib/src/databases.rs @@ -73,7 +73,7 @@ //! Whenever a connection to the database is needed: //! //! ```rust -//! # #![feature(proc_macro_hygiene)] +//! # #![feature(proc_macro_hygiene, async_await)] //! # //! # #[macro_use] extern crate rocket; //! # #[macro_use] extern crate rocket_contrib; @@ -289,7 +289,7 @@ //! connection to a given database: //! //! ```rust -//! # #![feature(proc_macro_hygiene)] +//! # #![feature(proc_macro_hygiene, async_await)] //! # //! # #[macro_use] extern crate rocket; //! # #[macro_use] extern crate rocket_contrib; @@ -311,7 +311,7 @@ //! connection type: //! //! ```rust -//! # #![feature(proc_macro_hygiene)] +//! # #![feature(proc_macro_hygiene, async_await)] //! # //! # #[macro_use] extern crate rocket; //! # #[macro_use] extern crate rocket_contrib; From 676ea96d4d848c8cb7514553f8b58a208b38ee20 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Sun, 4 Aug 2019 16:19:44 -0700 Subject: [PATCH 19/25] Fix a few 'unused use' warnings. --- core/lib/src/response/content.rs | 2 +- core/lib/src/response/flash.rs | 2 +- core/lib/src/response/stream.rs | 1 - core/lib/src/router/mod.rs | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/lib/src/response/content.rs b/core/lib/src/response/content.rs index d44bf23a87..846e20c99b 100644 --- a/core/lib/src/response/content.rs +++ b/core/lib/src/response/content.rs @@ -24,7 +24,7 @@ use crate::request::Request; use crate::response::{Response, Responder, ResultFuture}; -use crate::http::{Status, ContentType}; +use crate::http::ContentType; /// Sets the Content-Type of a `Responder` to a chosen value. /// diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index b1f8b2124d..2251156a90 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -3,7 +3,7 @@ use std::convert::AsRef; use time::Duration; use crate::outcome::IntoOutcome; -use crate::response::{Response, Responder, ResultFuture}; +use crate::response::{Responder, ResultFuture}; use crate::request::{self, Request, FromRequest}; use crate::http::{Status, Cookie}; use std::sync::atomic::{AtomicBool, Ordering}; diff --git a/core/lib/src/response/stream.rs b/core/lib/src/response/stream.rs index b5b49d0b72..9f2a030448 100644 --- a/core/lib/src/response/stream.rs +++ b/core/lib/src/response/stream.rs @@ -4,7 +4,6 @@ use futures::io::AsyncRead; use crate::request::Request; use crate::response::{Response, Responder, ResultFuture, DEFAULT_CHUNK_SIZE}; -use crate::http::Status; /// Streams a response to a client from an arbitrary `AsyncRead`er type. /// diff --git a/core/lib/src/router/mod.rs b/core/lib/src/router/mod.rs index 2357728964..03e2c5d248 100644 --- a/core/lib/src/router/mod.rs +++ b/core/lib/src/router/mod.rs @@ -3,7 +3,7 @@ mod route; use std::collections::hash_map::HashMap; -use futures::future::{Future, FutureExt}; +use futures::future::Future; pub use self::route::Route; From 5b1125bb4012e352c90ca98ae71c3e6f4441da12 Mon Sep 17 00:00:00 2001 From: Redrield Date: Sat, 3 Aug 2019 00:05:38 -0400 Subject: [PATCH 20/25] Update 'rocket_contrib::serve' for async. Also update 'static_files' example. --- contrib/lib/src/serve.rs | 10 +++++----- contrib/lib/tests/static_files.rs | 8 ++++---- examples/static_files/src/tests.rs | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/contrib/lib/src/serve.rs b/contrib/lib/src/serve.rs index 8e1c12c650..0fa1029ef3 100644 --- a/contrib/lib/src/serve.rs +++ b/contrib/lib/src/serve.rs @@ -18,7 +18,7 @@ use std::path::{PathBuf, Path}; use rocket::{Request, Data, Route}; use rocket::http::{Method, uri::Segments}; -use rocket::handler::{Handler, Outcome}; +use rocket::handler::{Handler, HandlerFuture, Outcome}; use rocket::response::NamedFile; /// A bitset representing configurable options for the [`StaticFiles`] handler. @@ -273,10 +273,10 @@ impl Into> for StaticFiles { } impl Handler for StaticFiles { - fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> Outcome<'r> { - fn handle_dir<'r>(opt: Options, r: &'r Request<'_>, d: Data, path: &Path) -> Outcome<'r> { + fn handle<'r>(&self, req: &'r Request<'_>, data: Data) -> HandlerFuture<'r> { + fn handle_dir<'r>(opt: Options, r: &'r Request<'_>, d: Data, path: &Path) -> HandlerFuture<'r> { if !opt.contains(Options::Index) { - return Outcome::forward(d); + return Box::pin(async move { Outcome::forward(d) }); } let file = NamedFile::open(path.join("index.html")).ok(); @@ -302,7 +302,7 @@ impl Handler for StaticFiles { match &path { Some(path) if path.is_dir() => handle_dir(self.options, req, data, path), Some(path) => Outcome::from_or_forward(req, data, NamedFile::open(path).ok()), - None => Outcome::forward(data) + None => Box::pin(async move { Outcome::forward(data) }), } } } diff --git a/contrib/lib/tests/static_files.rs b/contrib/lib/tests/static_files.rs index 18b4b1f65a..962cb74bc3 100644 --- a/contrib/lib/tests/static_files.rs +++ b/contrib/lib/tests/static_files.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[cfg(feature = "serve")] mod static_tests { @@ -57,7 +57,7 @@ mod static_tests { let mut file = File::open(path).expect("open file"); let mut expected_contents = String::new(); file.read_to_string(&mut expected_contents).expect("read file"); - assert_eq!(response.body_string(), Some(expected_contents)); + assert_eq!(response.body_string_wait(), Some(expected_contents)); } else { assert_eq!(response.status(), Status::NotFound); } @@ -133,11 +133,11 @@ mod static_tests { let mut response = client.get("/default/ireallydontexist").dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string().unwrap(), "ireallydontexist"); + assert_eq!(response.body_string_wait().unwrap(), "ireallydontexist"); let mut response = client.get("/default/idont/exist").dispatch(); assert_eq!(response.status(), Status::Ok); - assert_eq!(response.body_string().unwrap(), "idont/exist"); + assert_eq!(response.body_string_wait().unwrap(), "idont/exist"); assert_all(&client, "both", REGULAR_FILES, true); assert_all(&client, "both", HIDDEN_FILES, true); diff --git a/examples/static_files/src/tests.rs b/examples/static_files/src/tests.rs index c7b5d44344..e61c3785e4 100644 --- a/examples/static_files/src/tests.rs +++ b/examples/static_files/src/tests.rs @@ -13,7 +13,7 @@ fn test_query_file (path: &str, file: T, status: Status) let mut response = client.get(path).dispatch(); assert_eq!(response.status(), status); - let body_data = response.body().and_then(|body| body.into_bytes()); + let body_data = response.body_bytes_wait(); if let Some(filename) = file.into() { let expected_data = read_file_content(filename); assert!(body_data.map_or(false, |s| s == expected_data)); From 22622d1f09a4ebe2d9eaa3c5a533b88fef93c871 Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Tue, 6 Aug 2019 17:08:00 -0700 Subject: [PATCH 21/25] Update many doc tests in 'core' for async. --- contrib/lib/src/databases.rs | 2 +- contrib/lib/src/uuid.rs | 4 +- contrib/lib/tests/compress_responder.rs | 2 +- contrib/lib/tests/compression_fairing.rs | 2 +- core/codegen/src/lib.rs | 24 ++++---- core/codegen/tests/expansion.rs | 2 +- core/codegen/tests/responder.rs | 2 +- core/codegen/tests/route-data.rs | 2 +- core/codegen/tests/route-format.rs | 2 +- core/codegen/tests/route-ranking.rs | 2 +- core/codegen/tests/typed-uris.rs | 2 +- core/codegen/tests/ui-fail/catchers.rs | 2 +- .../ui-fail/route-attribute-general-syntax.rs | 2 +- .../tests/ui-fail/route-path-bad-syntax.rs | 2 +- .../tests/ui-fail/route-type-errors.rs | 2 +- core/codegen/tests/ui-fail/route-warnings.rs | 2 +- core/codegen/tests/ui-fail/routes.rs | 2 +- .../tests/ui-fail/typed-uri-bad-type.rs | 2 +- .../tests/ui-fail/typed-uris-bad-params.rs | 2 +- .../ui-fail/typed-uris-invalid-syntax.rs | 2 +- core/codegen/tests/uri_display.rs | 2 +- core/http/src/cookies.rs | 4 +- core/http/src/lib.rs | 2 +- core/http/src/uri/from_uri_param.rs | 2 +- core/http/src/uri/uri_display.rs | 8 +-- core/lib/benches/format-routing.rs | 2 +- core/lib/benches/ranked-routing.rs | 2 +- core/lib/benches/simple-routing.rs | 2 +- core/lib/src/catcher.rs | 11 ++-- core/lib/src/data/data.rs | 2 +- core/lib/src/fairing/ad_hoc.rs | 7 ++- core/lib/src/fairing/mod.rs | 55 +++++++++++-------- core/lib/src/handler.rs | 35 +++++++----- core/lib/src/lib.rs | 4 +- core/lib/src/local/mod.rs | 2 +- core/lib/src/request/form/error.rs | 2 +- core/lib/src/request/form/form.rs | 6 +- core/lib/src/request/form/from_form.rs | 4 +- core/lib/src/request/form/from_form_value.rs | 2 +- core/lib/src/request/form/lenient.rs | 4 +- core/lib/src/request/from_request.rs | 8 +-- core/lib/src/request/param.rs | 6 +- core/lib/src/request/query.rs | 4 +- core/lib/src/request/state.rs | 4 +- core/lib/src/response/flash.rs | 2 +- core/lib/src/response/redirect.rs | 2 +- core/lib/src/response/responder.rs | 22 ++++---- core/lib/src/rocket.rs | 16 +++--- core/lib/src/router/route.rs | 16 +++--- 49 files changed, 164 insertions(+), 138 deletions(-) diff --git a/contrib/lib/src/databases.rs b/contrib/lib/src/databases.rs index 64a9e3f5ac..1d8000d2ca 100644 --- a/contrib/lib/src/databases.rs +++ b/contrib/lib/src/databases.rs @@ -50,7 +50,7 @@ //! In your application's source code, one-time: //! //! ```rust -//! #![feature(proc_macro_hygiene)] +//! #![feature(proc_macro_hygiene, async_await)] //! //! #[macro_use] extern crate rocket; //! #[macro_use] extern crate rocket_contrib; diff --git a/contrib/lib/src/uuid.rs b/contrib/lib/src/uuid.rs index 5fab5efd51..dd86a7ff00 100644 --- a/contrib/lib/src/uuid.rs +++ b/contrib/lib/src/uuid.rs @@ -42,7 +42,7 @@ pub use self::uuid_crate::parser::ParseError; /// You can use the `Uuid` type directly as a target of a dynamic parameter: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket_contrib; /// use rocket_contrib::uuid::Uuid; @@ -56,7 +56,7 @@ pub use self::uuid_crate::parser::ParseError; /// You can also use the `Uuid` as a form value, including in query strings: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[macro_use] extern crate rocket_contrib; /// use rocket_contrib::uuid::Uuid; diff --git a/contrib/lib/tests/compress_responder.rs b/contrib/lib/tests/compress_responder.rs index 2d317ddfdb..fdef99c0a6 100644 --- a/contrib/lib/tests/compress_responder.rs +++ b/contrib/lib/tests/compress_responder.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] #[cfg(all(feature = "brotli_compression", feature = "gzip_compression"))] diff --git a/contrib/lib/tests/compression_fairing.rs b/contrib/lib/tests/compression_fairing.rs index fa09188d79..491957a9eb 100644 --- a/contrib/lib/tests/compression_fairing.rs +++ b/contrib/lib/tests/compression_fairing.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] #[cfg(all(feature = "brotli_compression", feature = "gzip_compression"))] diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index 643969bfe4..a9e408e5ef 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -31,7 +31,7 @@ //! crate root: //! //! ```rust -//! #![feature(proc_macro_hygiene)] +//! #![feature(proc_macro_hygiene, async_await)] //! //! #[macro_use] extern crate rocket; //! # #[get("/")] fn hello() { } @@ -41,7 +41,7 @@ //! Or, alternatively, selectively import from the top-level scope: //! //! ```rust -//! #![feature(proc_macro_hygiene)] +//! #![feature(proc_macro_hygiene, async_await)] //! # extern crate rocket; //! //! use rocket::{get, routes}; @@ -134,7 +134,7 @@ macro_rules! route_attribute { /// functions: /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// #[get("/")] @@ -157,7 +157,7 @@ macro_rules! route_attribute { /// explicitly specified: /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// #[route(GET, path = "/")] @@ -218,7 +218,7 @@ macro_rules! route_attribute { /// the arguments `foo`, `baz`, `msg`, `rest`, and `form`: /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # use rocket::request::Form; /// # use std::path::PathBuf; @@ -330,7 +330,7 @@ route_attribute!(options => Method::Options); /// This attribute can only be applied to free functions: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// use rocket::Request; @@ -742,7 +742,7 @@ pub fn derive_uri_display_path(input: TokenStream) -> TokenStream { /// corresponding [`Route`] structures. For example, given the following routes: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// #[get("/")] @@ -757,7 +757,7 @@ pub fn derive_uri_display_path(input: TokenStream) -> TokenStream { /// The `routes!` macro can be used as: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// # use rocket::http::Method; @@ -801,7 +801,7 @@ pub fn routes(input: TokenStream) -> TokenStream { /// catchers: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// #[catch(404)] @@ -816,7 +816,7 @@ pub fn routes(input: TokenStream) -> TokenStream { /// The `catchers!` macro can be used as: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// # #[catch(404)] fn not_found() { /* .. */ } @@ -858,7 +858,7 @@ pub fn catchers(input: TokenStream) -> TokenStream { /// For example, for the following route: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// #[get("/person/?")] @@ -872,7 +872,7 @@ pub fn catchers(input: TokenStream) -> TokenStream { /// A URI can be created as follows: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// # #[get("/person/?")] diff --git a/core/codegen/tests/expansion.rs b/core/codegen/tests/expansion.rs index aaaf2fcd61..d5d4f00e72 100644 --- a/core/codegen/tests/expansion.rs +++ b/core/codegen/tests/expansion.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/responder.rs b/core/codegen/tests/responder.rs index 0df09bc6f5..9608faf0d3 100644 --- a/core/codegen/tests/responder.rs +++ b/core/codegen/tests/responder.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] use rocket::local::Client; use rocket::response::Responder; diff --git a/core/codegen/tests/route-data.rs b/core/codegen/tests/route-data.rs index e15f4bb575..8a9cbc6fdd 100644 --- a/core/codegen/tests/route-data.rs +++ b/core/codegen/tests/route-data.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/route-format.rs b/core/codegen/tests/route-format.rs index 32bb935c42..d29e77cb4d 100644 --- a/core/codegen/tests/route-format.rs +++ b/core/codegen/tests/route-format.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/route-ranking.rs b/core/codegen/tests/route-ranking.rs index a85ee24cfd..5fcc364b15 100644 --- a/core/codegen/tests/route-ranking.rs +++ b/core/codegen/tests/route-ranking.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/typed-uris.rs b/core/codegen/tests/typed-uris.rs index 7f366e7e70..47d313bce6 100644 --- a/core/codegen/tests/typed-uris.rs +++ b/core/codegen/tests/typed-uris.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #![allow(dead_code, unused_variables)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/catchers.rs b/core/codegen/tests/ui-fail/catchers.rs index 627bf0c296..0168f3cdb1 100644 --- a/core/codegen/tests/ui-fail/catchers.rs +++ b/core/codegen/tests/ui-fail/catchers.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/route-attribute-general-syntax.rs b/core/codegen/tests/ui-fail/route-attribute-general-syntax.rs index 8457f96b7d..6f3dfda1a6 100644 --- a/core/codegen/tests/ui-fail/route-attribute-general-syntax.rs +++ b/core/codegen/tests/ui-fail/route-attribute-general-syntax.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/route-path-bad-syntax.rs b/core/codegen/tests/ui-fail/route-path-bad-syntax.rs index ab6a8a4a7b..1f79e386ed 100644 --- a/core/codegen/tests/ui-fail/route-path-bad-syntax.rs +++ b/core/codegen/tests/ui-fail/route-path-bad-syntax.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/route-type-errors.rs b/core/codegen/tests/ui-fail/route-type-errors.rs index f97645ae39..6cedb32fe4 100644 --- a/core/codegen/tests/ui-fail/route-type-errors.rs +++ b/core/codegen/tests/ui-fail/route-type-errors.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/route-warnings.rs b/core/codegen/tests/ui-fail/route-warnings.rs index 9111a2162f..57e3c93d51 100644 --- a/core/codegen/tests/ui-fail/route-warnings.rs +++ b/core/codegen/tests/ui-fail/route-warnings.rs @@ -1,6 +1,6 @@ // must-compile-successfully -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/routes.rs b/core/codegen/tests/ui-fail/routes.rs index 954293a50b..75952d25d2 100644 --- a/core/codegen/tests/ui-fail/routes.rs +++ b/core/codegen/tests/ui-fail/routes.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/typed-uri-bad-type.rs b/core/codegen/tests/ui-fail/typed-uri-bad-type.rs index 05b24fb346..82f93101ff 100644 --- a/core/codegen/tests/ui-fail/typed-uri-bad-type.rs +++ b/core/codegen/tests/ui-fail/typed-uri-bad-type.rs @@ -1,7 +1,7 @@ // normalize-stderr-test: "<(.*) as (.*)>" -> "$1 as $$TRAIT" // normalize-stderr-test: "and \d+ others" -> "and $$N others" -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/typed-uris-bad-params.rs b/core/codegen/tests/ui-fail/typed-uris-bad-params.rs index 2baf5be9a9..2bde677cda 100644 --- a/core/codegen/tests/ui-fail/typed-uris-bad-params.rs +++ b/core/codegen/tests/ui-fail/typed-uris-bad-params.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/ui-fail/typed-uris-invalid-syntax.rs b/core/codegen/tests/ui-fail/typed-uris-invalid-syntax.rs index 0c22cc1798..34c7181141 100644 --- a/core/codegen/tests/ui-fail/typed-uris-invalid-syntax.rs +++ b/core/codegen/tests/ui-fail/typed-uris-invalid-syntax.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/codegen/tests/uri_display.rs b/core/codegen/tests/uri_display.rs index b51d716a58..bcd112d4ad 100644 --- a/core/codegen/tests/uri_display.rs +++ b/core/codegen/tests/uri_display.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/http/src/cookies.rs b/core/http/src/cookies.rs index 8240c5c017..37c115d9ee 100644 --- a/core/http/src/cookies.rs +++ b/core/http/src/cookies.rs @@ -53,7 +53,7 @@ mod key { /// a handler to retrieve the value of a "message" cookie. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::http::Cookies; /// @@ -73,7 +73,7 @@ mod key { /// [private cookie]: Cookies::add_private() /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// use rocket::http::Status; diff --git a/core/http/src/lib.rs b/core/http/src/lib.rs index 51845708bd..1d0cdc086f 100644 --- a/core/http/src/lib.rs +++ b/core/http/src/lib.rs @@ -1,5 +1,5 @@ #![feature(specialization)] -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #![feature(crate_visibility_modifier)] #![feature(doc_cfg)] #![recursion_limit="512"] diff --git a/core/http/src/uri/from_uri_param.rs b/core/http/src/uri/from_uri_param.rs index 4aa374f872..992f899bb6 100644 --- a/core/http/src/uri/from_uri_param.rs +++ b/core/http/src/uri/from_uri_param.rs @@ -155,7 +155,7 @@ use crate::uri::{self, UriPart, UriDisplay}; /// With these implementations, the following typechecks: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # use std::fmt; /// use rocket::http::RawStr; diff --git a/core/http/src/uri/uri_display.rs b/core/http/src/uri/uri_display.rs index 153778fb19..b41f399e45 100644 --- a/core/http/src/uri/uri_display.rs +++ b/core/http/src/uri/uri_display.rs @@ -61,7 +61,7 @@ use crate::uri::{Uri, UriPart, Path, Query, Formatter}; /// the following route: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// #[get("/item/?")] /// fn get_item(id: i32, track: Option) { /* .. */ } @@ -70,7 +70,7 @@ use crate::uri::{Uri, UriPart, Path, Query, Formatter}; /// A URI for this route can be generated as follows: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # type T = (); /// # #[get("/item/?")] @@ -234,7 +234,7 @@ use crate::uri::{Uri, UriPart, Path, Query, Formatter}; /// `UriDisplay` implementation is required. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::http::RawStr; /// use rocket::request::FromParam; @@ -432,7 +432,7 @@ impl, E> UriDisplay for Result { /// trait for the corresponding `UriPart`. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// #[get("/item/?")] /// fn get_item(id: i32, track: Option) { /* .. */ } diff --git a/core/lib/benches/format-routing.rs b/core/lib/benches/format-routing.rs index f71eca05b9..d20eb2ab60 100644 --- a/core/lib/benches/format-routing.rs +++ b/core/lib/benches/format-routing.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/lib/benches/ranked-routing.rs b/core/lib/benches/ranked-routing.rs index d782ca79d9..df5716d279 100644 --- a/core/lib/benches/ranked-routing.rs +++ b/core/lib/benches/ranked-routing.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] #[macro_use] extern crate rocket; diff --git a/core/lib/benches/simple-routing.rs b/core/lib/benches/simple-routing.rs index 27fef79d0e..8a1da0a696 100644 --- a/core/lib/benches/simple-routing.rs +++ b/core/lib/benches/simple-routing.rs @@ -1,4 +1,4 @@ -#![feature(proc_macro_hygiene)] +#![feature(proc_macro_hygiene, async_await)] // #![feature(alloc_system)] // extern crate alloc_system; diff --git a/core/lib/src/catcher.rs b/core/lib/src/catcher.rs index afe059532d..f455edb29f 100644 --- a/core/lib/src/catcher.rs +++ b/core/lib/src/catcher.rs @@ -36,7 +36,7 @@ use yansi::Color::*; /// declared using the `catch` decorator, as follows: /// /// ```rust -/// #![feature(proc_macro_hygiene)] +/// #![feature(proc_macro_hygiene, async_await)] /// /// #[macro_use] extern crate rocket; /// @@ -78,16 +78,17 @@ impl Catcher { /// ```rust /// # #![allow(unused_variables)] /// use rocket::{Catcher, Request}; + /// use rocket::handler::ErrorHandlerFuture; /// use rocket::response::{Result, Responder}; /// use rocket::response::status::Custom; /// use rocket::http::Status; /// - /// fn handle_404<'r>(req: &'r Request) -> Result<'r> { - /// let res = Custom(Status::NotFound, format!("404: {}", req.uri())); - /// res.respond_to(req) + /// fn handle_404<'r>(req: &'r Request) -> ErrorHandlerFuture<'r> { + /// let res = Custom(Status::NotFound, format!("404: {}", req.uri())); + /// res.respond_to(req) /// } /// - /// fn handle_500<'r>(req: &'r Request) -> Result<'r> { + /// fn handle_500<'r>(req: &'r Request) -> ErrorHandlerFuture<'r> { /// "Whoops, we messed up!".respond_to(req) /// } /// diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 66fd295b7b..7540845210 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -23,7 +23,7 @@ const PEEK_BYTES: usize = 512; /// specifying the `data = ""` route parameter as follows: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # type DataGuard = rocket::data::Data; /// #[post("/submit", data = "")] diff --git a/core/lib/src/fairing/ad_hoc.rs b/core/lib/src/fairing/ad_hoc.rs index dc16049167..a2b9f1e284 100644 --- a/core/lib/src/fairing/ad_hoc.rs +++ b/core/lib/src/fairing/ad_hoc.rs @@ -117,12 +117,15 @@ impl AdHoc { /// # Example /// /// ```rust + /// # #![feature(async_await)] /// use rocket::fairing::AdHoc; /// /// // The no-op response fairing. /// let fairing = AdHoc::on_response("Dummy", |req, resp| { - /// // do something with the request and pending response... - /// # let (_, _) = (req, resp); + /// Box::pin(async move { + /// // do something with the request and pending response... + /// # let (_, _) = (req, resp); + /// }) /// }); /// ``` pub fn on_response(name: &'static str, f: F) -> AdHoc diff --git a/core/lib/src/fairing/mod.rs b/core/lib/src/fairing/mod.rs index 538e192c32..8ee0254bc2 100644 --- a/core/lib/src/fairing/mod.rs +++ b/core/lib/src/fairing/mod.rs @@ -20,9 +20,10 @@ //! write: //! //! ```rust +//! # #![feature(async_await)] //! # use rocket::fairing::AdHoc; //! # let req_fairing = AdHoc::on_request("Request", |_, _| ()); -//! # let res_fairing = AdHoc::on_response("Response", |_, _| ()); +//! # let res_fairing = AdHoc::on_response("Response", |_, _| Box::pin(async move {})); //! let rocket = rocket::ignite() //! .attach(req_fairing) //! .attach(res_fairing); @@ -206,7 +207,10 @@ pub use self::info_kind::{Info, Kind}; /// path. /// /// ```rust +/// # #![feature(async_await)] +/// use std::future::Future; /// use std::io::Cursor; +/// use std::pin::Pin; /// use std::sync::atomic::{AtomicUsize, Ordering}; /// /// use rocket::{Request, Data, Response}; @@ -235,21 +239,23 @@ pub use self::info_kind::{Info, Kind}; /// } /// } /// -/// fn on_response(&self, request: &Request, response: &mut Response) { -/// // Don't change a successful user's response, ever. -/// if response.status() != Status::NotFound { -/// return -/// } -/// -/// if request.method() == Method::Get && request.uri().path() == "/counts" { -/// let get_count = self.get.load(Ordering::Relaxed); -/// let post_count = self.post.load(Ordering::Relaxed); -/// -/// 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)); -/// } +/// fn on_response<'a, 'r>(&'a self, request: &'a Request<'r>, response: &'a mut Response<'r>) -> Pin + Send + 'a>> { +/// Box::pin(async move { +/// // Don't change a successful user's response, ever. +/// if response.status() != Status::NotFound { +/// return +/// } +/// +/// if request.method() == Method::Get && request.uri().path() == "/counts" { +/// let get_count = self.get.load(Ordering::Relaxed); +/// let post_count = self.post.load(Ordering::Relaxed); +/// +/// 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)); +/// } +/// }) /// } /// } /// ``` @@ -265,6 +271,9 @@ pub use self::info_kind::{Info, Kind}; /// request guard. /// /// ```rust +/// # #![feature(async_await)] +/// # use std::future::Future; +/// # use std::pin::Pin; /// # use std::time::{Duration, SystemTime}; /// # use rocket::Outcome; /// # use rocket::{Request, Data, Response}; @@ -297,12 +306,14 @@ pub use self::info_kind::{Info, Kind}; /// /// /// Adds a header to the response indicating how long the server took to /// /// process the request. -/// fn on_response(&self, request: &Request, response: &mut Response) { -/// let start_time = request.local_cache(|| TimerStart(None)); -/// if let Some(Ok(duration)) = start_time.0.map(|st| st.elapsed()) { -/// let ms = duration.as_secs() * 1000 + duration.subsec_millis() as u64; -/// response.set_raw_header("X-Response-Time", format!("{} ms", ms)); -/// } +/// fn on_response<'a, 'r>(&'a self, request: &'a Request<'r>, response: &'a mut Response<'r>) -> Pin + Send + 'a>> { +/// Box::pin(async move { +/// let start_time = request.local_cache(|| TimerStart(None)); +/// if let Some(Ok(duration)) = start_time.0.map(|st| st.elapsed()) { +/// let ms = duration.as_secs() * 1000 + duration.subsec_millis() as u64; +/// response.set_raw_header("X-Response-Time", format!("{} ms", ms)); +/// } +/// }) /// } /// } /// diff --git a/core/lib/src/handler.rs b/core/lib/src/handler.rs index 607f24fe09..0b7919778d 100644 --- a/core/lib/src/handler.rs +++ b/core/lib/src/handler.rs @@ -42,15 +42,16 @@ pub type HandlerFuture<'r> = std::pin::Pin> /// Such a handler might be written and used as follows: /// /// ```rust +/// # #![feature(async_await)] /// # #[derive(Copy, Clone)] enum Kind { Simple, Intermediate, Complex, } /// use rocket::{Request, Data, Route, http::Method}; -/// use rocket::handler::{self, Handler, Outcome}; +/// use rocket::handler::{self, Handler, Outcome, HandlerFuture}; /// /// #[derive(Clone)] /// struct CustomHandler(Kind); /// /// impl Handler for CustomHandler { -/// fn handle<'r>(&self, req: &'r Request, data: Data) -> Outcome<'r> { +/// fn handle<'r>(&self, req: &'r Request, data: Data) -> HandlerFuture<'r> { /// match self.0 { /// Kind::Simple => Outcome::from(req, "simple"), /// Kind::Intermediate => Outcome::from(req, "intermediate"), @@ -91,7 +92,7 @@ pub type HandlerFuture<'r> = std::pin::Pin> /// managed state and a static route, as follows: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// # #[derive(Copy, Clone)] @@ -198,10 +199,11 @@ impl<'r> Outcome<'r> { /// # Example /// /// ```rust + /// # #![feature(async_await)] /// use rocket::{Request, Data}; - /// use rocket::handler::Outcome; + /// use rocket::handler::{Outcome, HandlerFuture}; /// - /// fn str_responder(req: &Request, _: Data) -> Outcome<'static> { + /// fn str_responder<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { /// Outcome::from(req, "Hello, world!") /// } /// ``` @@ -224,10 +226,11 @@ impl<'r> Outcome<'r> { /// # Example /// /// ```rust + /// # #![feature(async_await)] /// use rocket::{Request, Data}; - /// use rocket::handler::Outcome; + /// use rocket::handler::{Outcome, HandlerFuture}; /// - /// fn str_responder(req: &Request, data: Data) -> Outcome<'static> { + /// fn str_responder<'r>(req: &'r Request, data: Data) -> HandlerFuture<'r> { /// Outcome::from_or_forward(req, data, "Hello, world!") /// } /// ``` @@ -252,12 +255,15 @@ impl<'r> Outcome<'r> { /// # Example /// /// ```rust + /// # #![feature(async_await)] /// use rocket::{Request, Data}; - /// use rocket::handler::Outcome; + /// use rocket::handler::{Outcome, HandlerFuture}; /// use rocket::http::Status; /// - /// fn bad_req_route(_: &Request, _: Data) -> Outcome<'static> { - /// Outcome::failure(Status::BadRequest) + /// fn bad_req_route<'r>(_: &'r Request, _: Data) -> HandlerFuture<'r> { + /// Box::pin(async move { + /// Outcome::failure(Status::BadRequest) + /// }) /// } /// ``` #[inline(always)] @@ -274,11 +280,14 @@ impl<'r> Outcome<'r> { /// # Example /// /// ```rust + /// # #![feature(async_await)] /// use rocket::{Request, Data}; - /// use rocket::handler::Outcome; + /// use rocket::handler::{Outcome, HandlerFuture}; /// - /// fn always_forward(_: &Request, data: Data) -> Outcome<'static> { - /// Outcome::forward(data) + /// fn always_forward<'r>(_: &'r Request, data: Data) -> HandlerFuture<'r> { + /// Box::pin(async move { + /// Outcome::forward(data) + /// }) /// } /// ``` #[inline(always)] diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index e534b8e73c..c3f03eb4ab 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -52,7 +52,7 @@ //! Then, add the following to the top of your `main.rs` file: //! //! ```rust -//! #![feature(proc_macro_hygiene)] +//! #![feature(proc_macro_hygiene, async_await)] //! //! #[macro_use] extern crate rocket; //! # #[get("/")] fn hello() { } @@ -63,7 +63,7 @@ //! write Rocket applications. Here's a simple example to get you started: //! //! ```rust -//! #![feature(proc_macro_hygiene)] +//! #![feature(proc_macro_hygiene, async_await)] //! //! #[macro_use] extern crate rocket; //! diff --git a/core/lib/src/local/mod.rs b/core/lib/src/local/mod.rs index 6265718a77..0c66f90bab 100644 --- a/core/lib/src/local/mod.rs +++ b/core/lib/src/local/mod.rs @@ -67,7 +67,7 @@ //! consider the following complete "Hello, world!" application, with testing. //! //! ```rust -//! #![feature(proc_macro_hygiene)] +//! #![feature(proc_macro_hygiene, async_await)] //! //! #[macro_use] extern crate rocket; //! diff --git a/core/lib/src/request/form/error.rs b/core/lib/src/request/form/error.rs index 086be805ff..fdd1b3fcb7 100644 --- a/core/lib/src/request/form/error.rs +++ b/core/lib/src/request/form/error.rs @@ -50,7 +50,7 @@ pub enum FormDataError<'f, E> { /// # Example /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::request::{Form, FormError, FormDataError}; /// diff --git a/core/lib/src/request/form/form.rs b/core/lib/src/request/form/form.rs index dd63caa2d0..c0290f23a5 100644 --- a/core/lib/src/request/form/form.rs +++ b/core/lib/src/request/form/form.rs @@ -33,7 +33,7 @@ use crate::ext::AsyncReadExt as _; /// implements the `FromForm` trait: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::request::Form; /// use rocket::http::RawStr; @@ -69,7 +69,7 @@ use crate::ext::AsyncReadExt as _; /// A handler that handles a form of this type can similarly by written: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #![allow(deprecated, unused_attributes)] /// # #[macro_use] extern crate rocket; /// # use rocket::request::Form; @@ -122,7 +122,7 @@ impl Form { /// # Example /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::request::Form; /// diff --git a/core/lib/src/request/form/from_form.rs b/core/lib/src/request/form/from_form.rs index 08c5598518..406f7d465e 100644 --- a/core/lib/src/request/form/from_form.rs +++ b/core/lib/src/request/form/from_form.rs @@ -13,7 +13,7 @@ use crate::request::FormItems; /// validation. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #![allow(deprecated, dead_code, unused_attributes)] /// # #[macro_use] extern crate rocket; /// #[derive(FromForm)] @@ -30,7 +30,7 @@ use crate::request::FormItems; /// data via the `data` parameter and `Form` type. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #![allow(deprecated, dead_code, unused_attributes)] /// # #[macro_use] extern crate rocket; /// # use rocket::request::Form; diff --git a/core/lib/src/request/form/from_form_value.rs b/core/lib/src/request/form/from_form_value.rs index 477639d211..2e083d9b2d 100644 --- a/core/lib/src/request/form/from_form_value.rs +++ b/core/lib/src/request/form/from_form_value.rs @@ -43,7 +43,7 @@ use crate::http::RawStr; /// according to its target type: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # type Size = String; /// #[get("/item?&")] diff --git a/core/lib/src/request/form/lenient.rs b/core/lib/src/request/form/lenient.rs index d25b3f1c33..a70dcce296 100644 --- a/core/lib/src/request/form/lenient.rs +++ b/core/lib/src/request/form/lenient.rs @@ -31,7 +31,7 @@ use crate::http::uri::{Query, FromUriParam}; /// handler: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::request::LenientForm; /// @@ -67,7 +67,7 @@ impl LenientForm { /// # Example /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::request::LenientForm; /// diff --git a/core/lib/src/request/from_request.rs b/core/lib/src/request/from_request.rs index dfcfe7ca98..cee9c6860e 100644 --- a/core/lib/src/request/from_request.rs +++ b/core/lib/src/request/from_request.rs @@ -55,7 +55,7 @@ impl IntoOutcome for Result { /// guard. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # use rocket::http::Method; /// # type A = Method; type B = Method; type C = Method; type T = (); @@ -165,7 +165,7 @@ impl IntoOutcome for Result { /// `sensitive` handler. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// use rocket::Outcome; @@ -220,7 +220,7 @@ impl IntoOutcome for Result { /// routes (`admin_dashboard` and `user_dashboard`): /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[cfg(feature = "private-cookies")] mod inner { /// # use rocket::outcome::{IntoOutcome, Outcome}; @@ -283,7 +283,7 @@ impl IntoOutcome for Result { /// used, as illustrated below: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[cfg(feature = "private-cookies")] mod inner { /// # use rocket::outcome::{IntoOutcome, Outcome}; diff --git a/core/lib/src/request/param.rs b/core/lib/src/request/param.rs index bcbdd9a5d1..931f5c2fd5 100644 --- a/core/lib/src/request/param.rs +++ b/core/lib/src/request/param.rs @@ -19,7 +19,7 @@ use crate::http::{RawStr, uri::{Segments, SegmentError}}; /// handler for the dynamic `"/"` path: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// #[get("/")] /// fn hello(id: usize) -> String { @@ -54,7 +54,7 @@ use crate::http::{RawStr, uri::{Segments, SegmentError}}; /// parameter as follows: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # use rocket::http::RawStr; /// #[get("/")] @@ -172,7 +172,7 @@ use crate::http::{RawStr, uri::{Segments, SegmentError}}; /// dynamic path segment: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # use rocket::request::FromParam; /// # use rocket::http::RawStr; diff --git a/core/lib/src/request/query.rs b/core/lib/src/request/query.rs index d6a4d55b0b..af25bf693c 100644 --- a/core/lib/src/request/query.rs +++ b/core/lib/src/request/query.rs @@ -8,7 +8,7 @@ use crate::request::{FormItems, FormItem, Form, LenientForm, FromForm}; /// generation for every trailing query parameter, `` below: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// # use rocket::request::Form; @@ -82,7 +82,7 @@ impl<'q> Iterator for Query<'q> { /// route: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::request::Form; /// diff --git a/core/lib/src/request/state.rs b/core/lib/src/request/state.rs index 54370a3d06..f45cf55998 100644 --- a/core/lib/src/request/state.rs +++ b/core/lib/src/request/state.rs @@ -22,7 +22,7 @@ use crate::http::Status; /// following example does just this: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::State; /// @@ -87,7 +87,7 @@ use crate::http::Status; /// [`State::from()`] static method: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::State; /// diff --git a/core/lib/src/response/flash.rs b/core/lib/src/response/flash.rs index 2251156a90..35a5689cd3 100644 --- a/core/lib/src/response/flash.rs +++ b/core/lib/src/response/flash.rs @@ -45,7 +45,7 @@ const FLASH_COOKIE_NAME: &str = "_flash"; /// message on both the request and response sides. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::response::{Flash, Redirect}; /// use rocket::request::FlashMessage; diff --git a/core/lib/src/response/redirect.rs b/core/lib/src/response/redirect.rs index d891fdacbc..8d5017a1a6 100644 --- a/core/lib/src/response/redirect.rs +++ b/core/lib/src/response/redirect.rs @@ -26,7 +26,7 @@ use crate::http::Status; /// a route, _always_ use [`uri!`] to construct a valid [`Origin`]: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::response::Redirect; /// diff --git a/core/lib/src/response/responder.rs b/core/lib/src/response/responder.rs index c003b5032f..6c19cd2fd8 100644 --- a/core/lib/src/response/responder.rs +++ b/core/lib/src/response/responder.rs @@ -14,7 +14,7 @@ use crate::request::Request; /// as illustrated below with `T`: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # type T = (); /// # @@ -155,7 +155,7 @@ use crate::request::Request; /// following `Responder` implementation accomplishes this: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// # #[derive(Debug)] @@ -167,14 +167,16 @@ use crate::request::Request; /// use rocket::response::{self, Response, Responder}; /// use rocket::http::ContentType; /// -/// impl Responder<'_> for Person { -/// fn respond_to(self, _: &Request) -> response::Result<'static> { -/// Response::build() -/// .sized_body(Cursor::new(format!("{}:{}", self.name, self.age))) -/// .raw_header("X-Person-Name", self.name) -/// .raw_header("X-Person-Age", self.age.to_string()) -/// .header(ContentType::new("application", "x-person")) -/// .ok() +/// impl Responder<'r> for Person { +/// fn respond_to(self, _: &'r Request) -> response::ResultFuture<'r> { +/// Box::pin(async move { +/// Response::build() +/// .sized_body(Cursor::new(format!("{}:{}", self.name, self.age))) +/// .raw_header("X-Person-Name", self.name) +/// .raw_header("X-Person-Age", self.age.to_string()) +/// .header(ContentType::new("application", "x-person")) +/// .ok() +/// }) /// } /// } /// # diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index ccc3ae2a46..fdc6804134 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -513,7 +513,7 @@ impl Rocket { /// dispatched to the `hi` route. /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// #[get("/world")] @@ -535,10 +535,10 @@ impl Rocket { /// /// ```rust /// use rocket::{Request, Route, Data}; - /// use rocket::handler::Outcome; + /// use rocket::handler::{HandlerFuture, Outcome}; /// use rocket::http::Method::*; /// - /// fn hi<'r>(req: &'r Request, _: Data) -> Outcome<'r> { + /// fn hi<'r>(req: &'r Request, _: Data) -> HandlerFuture<'r> { /// Outcome::from(req, "Hello!") /// } /// @@ -585,7 +585,7 @@ impl Rocket { /// # Examples /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::Request; /// @@ -641,7 +641,7 @@ impl Rocket { /// # Example /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::State; /// @@ -678,7 +678,7 @@ impl Rocket { /// # Example /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::Rocket; /// use rocket::fairing::AdHoc; @@ -819,7 +819,7 @@ impl Rocket { /// # Example /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::Rocket; /// use rocket::fairing::AdHoc; @@ -875,7 +875,7 @@ impl Rocket { /// # Example /// /// ```rust - /// # #![feature(proc_macro_hygiene)] + /// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// use rocket::Rocket; /// use rocket::fairing::AdHoc; diff --git a/core/lib/src/router/route.rs b/core/lib/src/router/route.rs index 0c90848fbc..cd32743d75 100644 --- a/core/lib/src/router/route.rs +++ b/core/lib/src/router/route.rs @@ -108,8 +108,8 @@ impl Route { /// use rocket::Route; /// use rocket::http::Method; /// # use rocket::{Request, Data}; - /// # use rocket::handler::Outcome; - /// # fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> { + /// # use rocket::handler::{Outcome, HandlerFuture}; + /// # fn handler<'r>(request: &'r Request, _data: Data) -> HandlerFuture<'r> { /// # Outcome::from(request, "Hello, world!") /// # } /// @@ -158,8 +158,8 @@ impl Route { /// use rocket::Route; /// use rocket::http::Method; /// # use rocket::{Request, Data}; - /// # use rocket::handler::Outcome; - /// # fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> { + /// # use rocket::handler::{Outcome, HandlerFuture}; + /// # fn handler<'r>(request: &'r Request, _data: Data) -> HandlerFuture<'r> { /// # Outcome::from(request, "Hello, world!") /// # } /// @@ -208,9 +208,9 @@ impl Route { /// use rocket::Route; /// use rocket::http::Method; /// # use rocket::{Request, Data}; - /// # use rocket::handler::Outcome; + /// # use rocket::handler::{Outcome, HandlerFuture}; /// # - /// # fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> { + /// # fn handler<'r>(request: &'r Request, _data: Data) -> HandlerFuture<'r> { /// # Outcome::from(request, "Hello, world!") /// # } /// @@ -242,9 +242,9 @@ impl Route { /// use rocket::Route; /// use rocket::http::{Method, uri::Origin}; /// # use rocket::{Request, Data}; - /// # use rocket::handler::Outcome; + /// # use rocket::handler::{Outcome, HandlerFuture}; /// # - /// # fn handler<'r>(request: &'r Request, _data: Data) -> Outcome<'r> { + /// # fn handler<'r>(request: &'r Request, _data: Data) -> HandlerFuture<'r> { /// # Outcome::from(request, "Hello, world!") /// # } /// From 215a4e186e8a173e50a0a9b85c1ce9e559c63bec Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Tue, 6 Aug 2019 18:20:30 -0700 Subject: [PATCH 22/25] Disable some known-failing tests for now. --- scripts/test.sh | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/scripts/test.sh b/scripts/test.sh index 88f16b96b9..7cfc09328b 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -67,7 +67,8 @@ if [ "$1" = "--contrib" ]; then msgpack tera_templates handlebars_templates - serve +# TODO.async: serve needs tests to use tokio runtime, blocked on #1071 +# serve helmet diesel_postgres_pool diesel_sqlite_pool @@ -79,14 +80,16 @@ if [ "$1" = "--contrib" ]; then redis_pool mongodb_pool memcache_pool - brotli_compression - gzip_compression +# TODO.async: compression not yet ported to async +# brotli_compression +# gzip_compression ) pushd "${CONTRIB_LIB_ROOT}" > /dev/null 2>&1 - echo ":: Building and testing contrib [default]..." - CARGO_INCREMENTAL=0 cargo test +# TODO.async: default_features includes `serve` +# echo ":: Building and testing contrib [default]..." +# CARGO_INCREMENTAL=0 cargo test for feature in "${FEATURES[@]}"; do echo ":: Building and testing contrib [${feature}]..." @@ -103,15 +106,21 @@ elif [ "$1" = "--core" ]; then pushd "${CORE_LIB_ROOT}" > /dev/null 2>&1 echo ":: Building and testing core [no features]..." - CARGO_INCREMENTAL=0 cargo test --no-default-features +# TODO.async: --lib because doc tests are not complete + CARGO_INCREMENTAL=0 cargo test --no-default-features --lib +# CARGO_INCREMENTAL=0 cargo test --no-default-features for feature in "${FEATURES[@]}"; do echo ":: Building and testing core [${feature}]..." - CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}" + +# TODO.async: --lib because doc tests are not complete + CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}" --lib +# CARGO_INCREMENTAL=0 cargo test --no-default-features --features "${feature}" done popd > /dev/null 2>&1 else echo ":: Building and testing libraries..." - CARGO_INCREMENTAL=0 cargo test --all-features --all $@ +# TODO.async: see other failures above +# CARGO_INCREMENTAL=0 cargo test --all-features --all $@ fi From f2aaab065638b10ac5f35f541af572614251888f Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Fri, 9 Aug 2019 23:21:41 -0400 Subject: [PATCH 23/25] Convert two internal functions returning `Pin>` to `async fn`. --- core/lib/src/data/data.rs | 57 +++++++++++++++----------------- core/lib/src/fairing/fairings.rs | 13 +++----- 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index 7540845210..d5cb5f7f4f 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -1,5 +1,4 @@ use std::path::Path; -use std::pin::Pin; use futures::compat::{Future01CompatExt, Stream01CompatExt, AsyncWrite01CompatExt}; use futures::io::{self, AsyncRead, AsyncReadExt as _, AsyncWrite}; @@ -181,41 +180,39 @@ impl Data { // bytes `vec[pos..cap]` are buffered and unread. The remainder of the data // bytes can be read from `stream`. #[inline(always)] - crate fn new(body: hyper::Body) -> Pin + Send>> { + crate async fn new(body: hyper::Body) -> Data { trace_!("Data::new({:?})", body); let mut stream = body.compat().map_err(|e| { io::Error::new(io::ErrorKind::Other, e) }).into_async_read(); - Box::pin(async { - let mut peek_buf = vec![0; PEEK_BYTES]; - - let eof = match stream.read_max(&mut peek_buf[..]).await { - Ok(n) => { - trace_!("Filled peek buf with {} bytes.", n); - - // TODO.async: This has not gone away, and I don't entirely - // understand what's happening here - - // We can use `set_len` here instead of `truncate`, but we'll - // take the performance hit to avoid `unsafe`. All of this code - // should go away when we migrate away from hyper 0.10.x. - - peek_buf.truncate(n); - n < PEEK_BYTES - } - Err(e) => { - error_!("Failed to read into peek buffer: {:?}.", e); - // Likewise here as above. - peek_buf.truncate(0); - false - } - }; - - trace_!("Peek bytes: {}/{} bytes.", peek_buf.len(), PEEK_BYTES); - Data { buffer: peek_buf, stream: Box::new(stream), is_complete: eof } - }) + let mut peek_buf = vec![0; PEEK_BYTES]; + + let eof = match stream.read_max(&mut peek_buf[..]).await { + Ok(n) => { + trace_!("Filled peek buf with {} bytes.", n); + + // TODO.async: This has not gone away, and I don't entirely + // understand what's happening here + + // We can use `set_len` here instead of `truncate`, but we'll + // take the performance hit to avoid `unsafe`. All of this code + // should go away when we migrate away from hyper 0.10.x. + + peek_buf.truncate(n); + n < PEEK_BYTES + } + Err(e) => { + error_!("Failed to read into peek buffer: {:?}.", e); + // Likewise here as above. + peek_buf.truncate(0); + false + } + }; + + trace_!("Peek bytes: {}/{} bytes.", peek_buf.len(), PEEK_BYTES); + Data { buffer: peek_buf, stream: Box::new(stream), is_complete: eof } } /// This creates a `data` object from a local data source `data`. diff --git a/core/lib/src/fairing/fairings.rs b/core/lib/src/fairing/fairings.rs index 7933ac434f..a0347cdeb5 100644 --- a/core/lib/src/fairing/fairings.rs +++ b/core/lib/src/fairing/fairings.rs @@ -1,6 +1,3 @@ -use std::pin::Pin; -use std::future::Future; - use crate::{Rocket, Request, Response, Data}; use crate::fairing::{Fairing, Kind}; @@ -68,12 +65,10 @@ impl Fairings { } #[inline(always)] - pub fn handle_response<'a, 'r>(&'a self, request: &'a Request<'r>, response: &'a mut Response<'r>) -> Pin + Send + 'a>> { - Box::pin(async move { - for &i in &self.response { - self.all_fairings[i].on_response(request, response).await; - } - }) + pub async fn handle_response<'r>(&self, request: &Request<'r>, response: &mut Response<'r>) { + for &i in &self.response { + self.all_fairings[i].on_response(request, response).await; + } } pub fn failures(&self) -> Option<&[&'static str]> { From 72ccbbdbf3be329fe9955c58538cd4577ddb597e Mon Sep 17 00:00:00 2001 From: Jeb Rosen Date: Mon, 12 Aug 2019 18:38:25 -0700 Subject: [PATCH 24/25] Use read_to_string (from futures-preview 0.3.0-alpha.18) to more closely match the pre-async code. --- contrib/lib/Cargo.toml | 2 +- contrib/lib/src/json.rs | 11 +-- core/lib/Cargo.toml | 2 +- core/lib/src/data/from_data.rs | 132 ++++++++++++++++-------------- core/lib/src/request/form/form.rs | 11 ++- 5 files changed, 82 insertions(+), 76 deletions(-) diff --git a/contrib/lib/Cargo.toml b/contrib/lib/Cargo.toml index 84b5cd5519..98b115d4c7 100644 --- a/contrib/lib/Cargo.toml +++ b/contrib/lib/Cargo.toml @@ -42,7 +42,7 @@ memcache_pool = ["databases", "memcache", "r2d2-memcache"] [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 = { version = "0.5.0-dev", path = "../../core/lib/", default-features = false } log = "0.4" diff --git a/contrib/lib/src/json.rs b/contrib/lib/src/json.rs index 481054bd2c..4dfca26abf 100644 --- a/contrib/lib/src/json.rs +++ b/contrib/lib/src/json.rs @@ -139,15 +139,10 @@ impl<'a, T: Deserialize<'a>> FromData<'a> for Json { fn transform(r: &Request<'_>, d: Data) -> TransformFuture<'a, Self::Owned, Self::Error> { let size_limit = r.limits().get("json").unwrap_or(LIMIT); 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); - match reader.read_to_end(&mut v).await { - Ok(_) => { - 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))))), - } - }, + match reader.read_to_string(&mut s).await { + Ok(_) => Borrowed(Success(s)), Err(e) => Borrowed(Failure((Status::BadRequest, JsonError::Io(e)))) } }) diff --git a/core/lib/Cargo.toml b/core/lib/Cargo.toml index 16bdb95cfa..414721dfe3 100644 --- a/core/lib/Cargo.toml +++ b/core/lib/Cargo.toml @@ -26,7 +26,7 @@ private-cookies = ["rocket_http/private-cookies"] [dependencies] rocket_codegen = { version = "0.5.0-dev", path = "../codegen" } 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" yansi = "0.5" log = { version = "0.4", features = ["std"] } diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index 60b34d3bf2..9d81bf1da1 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -142,7 +142,7 @@ pub type FromDataFuture<'a, T, E> = Pin> + /// if the guard returns successfully. /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # type DataGuard = rocket::data::Data; /// #[post("/submit", data = "")] @@ -188,16 +188,20 @@ pub type FromDataFuture<'a, T, E> = Pin> + /// `String` (an `&str`). /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # #[derive(Debug)] /// # struct Name<'a> { first: &'a str, last: &'a str, } /// use std::io::{self, Read}; /// +/// use futures::io::AsyncReadExt; +/// /// 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::AsyncReadExt as _; +/// /// const NAME_LIMIT: u64 = 256; /// /// enum NameError { @@ -210,32 +214,36 @@ pub type FromDataFuture<'a, T, E> = Pin> + /// type Owned = String; /// type Borrowed = str; /// -/// fn transform(_: &Request, data: Data) -> Transform> { -/// let mut stream = data.open().take(NAME_LIMIT); -/// let mut string = String::with_capacity((NAME_LIMIT / 2) as usize); -/// let outcome = match stream.read_to_string(&mut string) { -/// Ok(_) => Success(string), -/// Err(e) => Failure((Status::InternalServerError, NameError::Io(e))) -/// }; -/// -/// // Returning `Borrowed` here means we get `Borrowed` in `from_data`. -/// Transform::Borrowed(outcome) +/// fn transform(_: &Request, data: Data) -> TransformFuture<'a, Self::Owned, Self::Error> { +/// Box::pin(async move { +/// let mut stream = data.open().take(NAME_LIMIT); +/// let mut string = String::with_capacity((NAME_LIMIT / 2) as usize); +/// let outcome = match stream.read_to_string(&mut string).await { +/// Ok(_) => Success(string), +/// Err(e) => Failure((Status::InternalServerError, NameError::Io(e))) +/// }; +/// +/// // Returning `Borrowed` here means we get `Borrowed` in `from_data`. +/// Transform::Borrowed(outcome) +/// }) /// } /// -/// fn from_data(_: &Request, outcome: Transformed<'a, Self>) -> Outcome { -/// // Retrieve a borrow to the now transformed `String` (an &str). This -/// // is only correct because we know we _always_ return a `Borrowed` from -/// // `transform` above. -/// let string = outcome.borrowed()?; -/// -/// // Perform a crude, inefficient parse. -/// let splits: Vec<&str> = string.split(" ").collect(); -/// if splits.len() != 2 || splits.iter().any(|s| s.is_empty()) { -/// return Failure((Status::UnprocessableEntity, NameError::Parse)); -/// } -/// -/// // Return successfully. -/// Success(Name { first: splits[0], last: splits[1] }) +/// fn from_data(_: &Request, outcome: Transformed<'a, Self>) -> FromDataFuture<'a, Self, Self::Error> { +/// Box::pin(async move { +/// // Retrieve a borrow to the now transformed `String` (an &str). This +/// // is only correct because we know we _always_ return a `Borrowed` from +/// // `transform` above. +/// let string = outcome.borrowed()?; +/// +/// // Perform a crude, inefficient parse. +/// let splits: Vec<&str> = string.split(" ").collect(); +/// if splits.len() != 2 || splits.iter().any(|s| s.is_empty()) { +/// return Failure((Status::UnprocessableEntity, NameError::Parse)); +/// } +/// +/// // Return successfully. +/// Success(Name { first: splits[0], last: splits[1] }) +/// }) /// } /// } /// # #[post("/person", data = "")] @@ -434,7 +442,7 @@ impl<'a> FromData<'a> for Data { /// that you can retrieve it directly from a client's request body: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # type Person = rocket::data::Data; /// #[post("/person", data = "")] @@ -446,7 +454,7 @@ impl<'a> FromData<'a> for Data { /// A `FromDataSimple` implementation allowing this looks like: /// /// ```rust -/// # #![feature(proc_macro_hygiene)] +/// # #![feature(proc_macro_hygiene, async_await)] /// # #[macro_use] extern crate rocket; /// # /// # #[derive(Debug)] @@ -454,43 +462,50 @@ impl<'a> FromData<'a> for Data { /// # /// use std::io::Read; /// +/// use futures::io::AsyncReadExt; +/// /// use rocket::{Request, Data, Outcome, Outcome::*}; -/// use rocket::data::{self, FromDataSimple}; +/// use rocket::data::{self, FromDataSimple, FromDataFuture}; /// use rocket::http::{Status, ContentType}; /// +/// use rocket::AsyncReadExt as _; +/// /// // Always use a limit to prevent DoS attacks. /// const LIMIT: u64 = 256; /// /// impl FromDataSimple for Person { /// type Error = String; /// -/// fn from_data(req: &Request, data: Data) -> data::Outcome { +/// fn from_data(req: &Request, data: Data) -> FromDataFuture<'static, Self, String> { /// // Ensure the content type is correct before opening the data. /// let person_ct = ContentType::new("application", "x-person"); /// 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. -/// let mut string = String::new(); -/// if let Err(e) = data.open().take(LIMIT).read_to_string(&mut string) { -/// return Failure((Status::InternalServerError, format!("{:?}", e))); -/// } -/// -/// // Split the string into two pieces at ':'. -/// let (name, age) = match string.find(':') { -/// Some(i) => (string[..i].to_string(), &string[(i + 1)..]), -/// None => return Failure((Status::UnprocessableEntity, "':'".into())) -/// }; -/// -/// // Parse the age. -/// let age: u16 = match age.parse() { -/// Ok(age) => age, -/// Err(_) => return Failure((Status::UnprocessableEntity, "Age".into())) -/// }; -/// -/// // Return successfully. -/// Success(Person { name, age }) +/// Box::pin(async move { +/// // Read the data into a String. +/// let mut string = String::new(); +/// 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 ':'. +/// let (name, age) = match string.find(':') { +/// Some(i) => (string[..i].to_string(), &string[(i + 1)..]), +/// None => return Failure((Status::UnprocessableEntity, "':'".into())) +/// }; +/// +/// // Parse the age. +/// let age: u16 = match age.parse() { +/// Ok(age) => age, +/// Err(_) => return Failure((Status::UnprocessableEntity, "Age".into())) +/// }; +/// +/// // Return successfully. +/// Success(Person { name, age }) +/// }) /// } /// } /// # #[post("/person", data = "")] @@ -578,14 +593,11 @@ impl FromDataSimple for String { #[inline(always)] fn from_data(_: &Request<'_>, data: Data) -> FromDataFuture<'static, Self, Self::Error> { Box::pin(async { - let mut stream = data.open(); - let mut buf = Vec::new(); - if let Err(e) = stream.read_to_end(&mut buf).await { - return 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))), + let mut string = String::new(); + let mut reader = data.open(); + match reader.read_to_string(&mut string).await { + Ok(_) => Success(string), + Err(e) => Failure((Status::BadRequest, e)), } }) } diff --git a/core/lib/src/request/form/form.rs b/core/lib/src/request/form/form.rs index c0290f23a5..282f46c642 100644 --- a/core/lib/src/request/form/form.rs +++ b/core/lib/src/request/form/form.rs @@ -196,6 +196,8 @@ impl<'f, T: FromForm<'f> + Send + 'f> FromData<'f> for Form { request: &Request<'_>, data: Data ) -> TransformFuture<'f, Self::Owned, Self::Error> { + use std::cmp::min; + if !request.content_type().map_or(false, |ct| ct.is_form()) { warn_!("Form data does not have form content type."); 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 { let limit = request.limits().forms; let mut stream = data.open().take(limit); Box::pin(async move { - let mut buf = Vec::new(); - if let Err(e) = stream.read_to_end(&mut buf).await { + let mut form_string = String::with_capacity(min(4096, limit) as usize); + if let Err(e) = stream.read_to_string(&mut form_string).await { return Transform::Borrowed(Failure((Status::InternalServerError, FormDataError::Io(e)))); } - Transform::Borrowed(match String::from_utf8(buf) { - Ok(s) => Success(s), - Err(e) => Failure((Status::BadRequest, FormDataError::Io(std::io::Error::new(std::io::ErrorKind::Other, e)))), - }) + Transform::Borrowed(Success(form_string)) }) } From d1215ff4ffa392e0f828b606f495b2889dbc591c Mon Sep 17 00:00:00 2001 From: Jacob Pratt Date: Sun, 18 Aug 2019 20:47:08 -0400 Subject: [PATCH 25/25] Remove `crate_visibility_modifier`, among others Other features are not used in places, namely `doc_cfg`, `proc_macro_span`, and `label_break_value`. --- contrib/codegen/src/lib.rs | 5 ++-- contrib/lib/src/lib.rs | 2 -- core/codegen/src/attribute/segments.rs | 18 +++++++------- core/codegen/src/bang/mod.rs | 2 +- core/codegen/src/bang/uri.rs | 4 +-- core/codegen/src/derive/from_form.rs | 10 ++++---- core/codegen/src/http_codegen.rs | 22 ++++++++--------- core/codegen/src/lib.rs | 15 ++++++------ core/codegen/src/proc_macro_ext.rs | 2 +- core/http/src/accept.rs | 2 +- core/http/src/lib.rs | 4 +-- core/http/src/parse/uri/error.rs | 2 +- core/http/src/parse/uri/mod.rs | 2 +- core/http/src/parse/uri/parser.rs | 10 ++++---- core/http/src/uri/absolute.rs | 4 +-- core/http/src/uri/authority.rs | 6 ++--- core/http/src/uri/encoding.rs | 10 ++++---- core/http/src/uri/formatter.rs | 2 +- core/http/src/uri/mod.rs | 2 +- core/http/src/uri/origin.rs | 10 ++++---- core/http/src/uri/uri.rs | 4 +-- core/lib/src/catcher.rs | 4 +-- core/lib/src/config/config.rs | 18 +++++++------- core/lib/src/config/custom_values.rs | 6 ++--- core/lib/src/config/environment.rs | 4 +-- core/lib/src/config/mod.rs | 4 +-- core/lib/src/config/toml_ext.rs | 2 +- core/lib/src/data/data.rs | 6 ++--- core/lib/src/data/data_stream.rs | 2 +- core/lib/src/error.rs | 2 +- core/lib/src/fairing/mod.rs | 2 +- core/lib/src/lib.rs | 2 -- core/lib/src/local/client.rs | 2 +- core/lib/src/local/request.rs | 2 +- core/lib/src/logger.rs | 8 +++--- core/lib/src/request/form/form.rs | 2 +- core/lib/src/request/request.rs | 34 +++++++++++++------------- core/lib/src/response/mod.rs | 2 +- core/lib/src/response/response.rs | 2 +- core/lib/src/rocket.rs | 12 ++++----- core/lib/src/router/mod.rs | 4 +-- core/lib/src/router/route.rs | 10 ++++---- 42 files changed, 130 insertions(+), 138 deletions(-) diff --git a/contrib/codegen/src/lib.rs b/contrib/codegen/src/lib.rs index 7f61fc22aa..8ac640fdf5 100644 --- a/contrib/codegen/src/lib.rs +++ b/contrib/codegen/src/lib.rs @@ -1,5 +1,4 @@ -#![feature(proc_macro_span, proc_macro_diagnostic)] -#![feature(crate_visibility_modifier)] +#![feature(proc_macro_diagnostic)] #![recursion_limit="256"] #![warn(rust_2018_idioms)] @@ -32,7 +31,7 @@ extern crate proc_macro; #[macro_use] extern crate quote; #[allow(unused_imports)] -crate use devise::{syn, proc_macro2}; +pub(crate) use devise::{syn, proc_macro2}; #[cfg(feature = "database_attribute")] mod database; diff --git a/contrib/lib/src/lib.rs b/contrib/lib/src/lib.rs index 7b50f8dbf0..c55986a5d0 100644 --- a/contrib/lib/src/lib.rs +++ b/contrib/lib/src/lib.rs @@ -1,5 +1,3 @@ -#![feature(crate_visibility_modifier)] -#![feature(doc_cfg)] #![feature(async_await)] #![doc(html_root_url = "https://api.rocket.rs/v0.5")] diff --git a/core/codegen/src/attribute/segments.rs b/core/codegen/src/attribute/segments.rs index 716ea0949e..7d827fa26d 100644 --- a/core/codegen/src/attribute/segments.rs +++ b/core/codegen/src/attribute/segments.rs @@ -7,15 +7,15 @@ use crate::http::uri::{UriPart, Path}; use crate::http::route::RouteSegment; use crate::proc_macro_ext::{Diagnostics, StringLit, PResult, DResult}; -crate use crate::http::route::{Error, Kind, Source}; +pub(crate) use crate::http::route::{Error, Kind, Source}; #[derive(Debug, Clone)] -crate struct Segment { - crate span: Span, - crate kind: Kind, - crate source: Source, - crate name: String, - crate index: Option, +pub(crate) struct Segment { + pub(crate) span: Span, + pub(crate) kind: Kind, + pub(crate) source: Source, + pub(crate) name: String, + pub(crate) index: Option, } impl Segment { @@ -115,7 +115,7 @@ fn into_diagnostic( } } -crate fn parse_data_segment(segment: &str, span: Span) -> PResult { +pub(crate) fn parse_data_segment(segment: &str, span: Span) -> PResult { >::parse_one(segment) .map(|segment| { let mut seg = Segment::from(segment, span); @@ -126,7 +126,7 @@ crate fn parse_data_segment(segment: &str, span: Span) -> PResult { .map_err(|e| into_diagnostic(segment, segment, span, &e)) } -crate fn parse_segments( +pub(crate) fn parse_segments( string: &str, span: Span ) -> DResult> { diff --git a/core/codegen/src/bang/mod.rs b/core/codegen/src/bang/mod.rs index a93785ce77..1726e48595 100644 --- a/core/codegen/src/bang/mod.rs +++ b/core/codegen/src/bang/mod.rs @@ -9,7 +9,7 @@ use crate::{ROUTE_STRUCT_PREFIX, CATCH_STRUCT_PREFIX}; mod uri; mod uri_parsing; -crate fn prefix_last_segment(path: &mut Path, prefix: &str) { +pub(crate) fn prefix_last_segment(path: &mut Path, prefix: &str) { let mut last_seg = path.segments.last_mut().expect("syn::Path has segments"); last_seg.value_mut().ident = last_seg.value().ident.prepend(prefix); } diff --git a/core/codegen/src/bang/uri.rs b/core/codegen/src/bang/uri.rs index 8b0c7fc6c1..944f9b5f1d 100644 --- a/core/codegen/src/bang/uri.rs +++ b/core/codegen/src/bang/uri.rs @@ -23,7 +23,7 @@ macro_rules! p { ($n:expr, "parameter") => (p!(@go $n, "1 parameter", format!("{} parameters", $n))); } -crate fn _uri_macro(input: TokenStream) -> Result { +pub(crate) fn _uri_macro(input: TokenStream) -> Result { let input2: TokenStream2 = input.clone().into(); let mut params = syn::parse::(input).map_err(syn_to_diag)?; prefix_last_segment(&mut params.route_path, URI_MACRO_PREFIX); @@ -212,7 +212,7 @@ fn build_origin(internal: &InternalUriParams) -> Origin<'static> { Origin::new(path, query).to_normalized().into_owned() } -crate fn _uri_internal_macro(input: TokenStream) -> Result { +pub(crate) fn _uri_internal_macro(input: TokenStream) -> Result { // Parse the internal invocation and the user's URI param expressions. let internal = syn::parse::(input).map_err(syn_to_diag)?; let (path_params, query_params) = extract_exprs(&internal)?; diff --git a/core/codegen/src/derive/from_form.rs b/core/codegen/src/derive/from_form.rs index 5a6835323e..636d486a8f 100644 --- a/core/codegen/src/derive/from_form.rs +++ b/core/codegen/src/derive/from_form.rs @@ -2,13 +2,13 @@ use proc_macro::{Span, TokenStream}; use devise::{*, ext::{TypeExt, Split3}}; #[derive(FromMeta)] -crate struct Form { - crate field: FormField, +pub(crate) struct Form { + pub(crate) field: FormField, } -crate struct FormField { - crate span: Span, - crate name: String +pub(crate) struct FormField { + pub(crate) span: Span, + pub(crate) name: String } fn is_valid_field_name(s: &str) -> bool { diff --git a/core/codegen/src/http_codegen.rs b/core/codegen/src/http_codegen.rs index 782394282c..66dca82e98 100644 --- a/core/codegen/src/http_codegen.rs +++ b/core/codegen/src/http_codegen.rs @@ -8,25 +8,25 @@ use crate::attribute::segments::{parse_segments, parse_data_segment, Segment, Ki use crate::proc_macro_ext::StringLit; #[derive(Debug)] -crate struct ContentType(crate http::ContentType); +pub(crate) struct ContentType(pub(crate) http::ContentType); #[derive(Debug)] -crate struct Status(crate http::Status); +pub(crate) struct Status(pub(crate) http::Status); #[derive(Debug)] -crate struct MediaType(crate http::MediaType); +pub(crate) struct MediaType(pub(crate) http::MediaType); #[derive(Debug)] -crate struct Method(crate http::Method); +pub(crate) struct Method(pub(crate) http::Method); #[derive(Debug)] -crate struct Origin(crate http::uri::Origin<'static>); +pub(crate) struct Origin(pub(crate) http::uri::Origin<'static>); #[derive(Clone, Debug)] -crate struct DataSegment(crate Segment); +pub(crate) struct DataSegment(pub(crate) Segment); #[derive(Clone, Debug)] -crate struct Optional(crate Option); +pub(crate) struct Optional(pub(crate) Option); impl FromMeta for StringLit { fn from_meta(meta: MetaItem<'_>) -> Result { @@ -35,10 +35,10 @@ impl FromMeta for StringLit { } #[derive(Debug)] -crate struct RoutePath { - crate origin: Origin, - crate path: Vec, - crate query: Option>, +pub(crate) struct RoutePath { + pub(crate) origin: Origin, + pub(crate) path: Vec, + pub(crate) query: Option>, } impl FromMeta for Status { diff --git a/core/codegen/src/lib.rs b/core/codegen/src/lib.rs index a9e408e5ef..7ab937ce50 100644 --- a/core/codegen/src/lib.rs +++ b/core/codegen/src/lib.rs @@ -1,5 +1,4 @@ #![feature(proc_macro_diagnostic, proc_macro_span)] -#![feature(crate_visibility_modifier)] #![feature(async_await)] #![recursion_limit="128"] @@ -103,14 +102,14 @@ mod syn_ext; use crate::http::Method; use proc_macro::TokenStream; -crate use devise::proc_macro2; +pub(crate) use devise::proc_macro2; -crate static ROUTE_STRUCT_PREFIX: &str = "static_rocket_route_info_for_"; -crate static CATCH_STRUCT_PREFIX: &str = "static_rocket_catch_info_for_"; -crate static CATCH_FN_PREFIX: &str = "rocket_catch_fn_"; -crate static ROUTE_FN_PREFIX: &str = "rocket_route_fn_"; -crate static URI_MACRO_PREFIX: &str = "rocket_uri_macro_"; -crate static ROCKET_PARAM_PREFIX: &str = "__rocket_param_"; +pub(crate) static ROUTE_STRUCT_PREFIX: &str = "static_rocket_route_info_for_"; +pub(crate) static CATCH_STRUCT_PREFIX: &str = "static_rocket_catch_info_for_"; +pub(crate) static CATCH_FN_PREFIX: &str = "rocket_catch_fn_"; +pub(crate) static ROUTE_FN_PREFIX: &str = "rocket_route_fn_"; +pub(crate) static URI_MACRO_PREFIX: &str = "rocket_uri_macro_"; +pub(crate) static ROCKET_PARAM_PREFIX: &str = "__rocket_param_"; macro_rules! emit { ($tokens:expr) => ({ diff --git a/core/codegen/src/proc_macro_ext.rs b/core/codegen/src/proc_macro_ext.rs index e084962eed..5cfadbdd44 100644 --- a/core/codegen/src/proc_macro_ext.rs +++ b/core/codegen/src/proc_macro_ext.rs @@ -63,7 +63,7 @@ impl From> for Diagnostics { use std::ops::Deref; -pub struct StringLit(crate String, crate Literal); +pub struct StringLit(pub(crate) String, pub(crate) Literal); impl Deref for StringLit { type Target = str; diff --git a/core/http/src/accept.rs b/core/http/src/accept.rs index 545f51735e..ff3762ac9e 100644 --- a/core/http/src/accept.rs +++ b/core/http/src/accept.rs @@ -162,7 +162,7 @@ impl PartialEq for AcceptParams { /// let response = Response::build().header(Accept::JSON).finalize(); /// ``` #[derive(Debug, Clone, PartialEq)] -pub struct Accept(crate AcceptParams); +pub struct Accept(pub(crate) AcceptParams); macro_rules! accept_constructor { ($($name:ident ($check:ident): $str:expr, $t:expr, diff --git a/core/http/src/lib.rs b/core/http/src/lib.rs index 1d0cdc086f..4433bf7da5 100644 --- a/core/http/src/lib.rs +++ b/core/http/src/lib.rs @@ -1,7 +1,5 @@ #![feature(specialization)] #![feature(proc_macro_hygiene, async_await)] -#![feature(crate_visibility_modifier)] -#![feature(doc_cfg)] #![recursion_limit="512"] #![warn(rust_2018_idioms)] @@ -40,7 +38,7 @@ mod header; mod accept; mod raw_str; -crate mod parse; +pub(crate) mod parse; pub mod uncased; diff --git a/core/http/src/parse/uri/error.rs b/core/http/src/parse/uri/error.rs index 7ca02b70cd..6582d0fd32 100644 --- a/core/http/src/parse/uri/error.rs +++ b/core/http/src/parse/uri/error.rs @@ -25,7 +25,7 @@ enum Or { } impl<'a> Error<'a> { - crate fn from(src: &'a str, pear_error: ParseErr>) -> Error<'a> { + pub(crate) fn from(src: &'a str, pear_error: ParseErr>) -> Error<'a> { let new_expected = pear_error.expected.map(|token| { if token.is_ascii() && !token.is_ascii_control() { Or::A(token as char) diff --git a/core/http/src/parse/uri/mod.rs b/core/http/src/parse/uri/mod.rs index 33f0066143..178805e845 100644 --- a/core/http/src/parse/uri/mod.rs +++ b/core/http/src/parse/uri/mod.rs @@ -8,7 +8,7 @@ use crate::uri::{Uri, Origin, Absolute, Authority}; use crate::parse::indexed::IndexedInput; use self::parser::{uri, origin, authority_only, absolute_only, rocket_route_origin}; -crate use self::tables::is_pchar; +pub(crate) use self::tables::is_pchar; pub use self::error::Error; type RawInput<'a> = IndexedInput<'a, [u8]>; diff --git a/core/http/src/parse/uri/parser.rs b/core/http/src/parse/uri/parser.rs index dbcc5d8034..14831d41fd 100644 --- a/core/http/src/parse/uri/parser.rs +++ b/core/http/src/parse/uri/parser.rs @@ -9,7 +9,7 @@ use crate::parse::IndexedBytes; type Result<'a, T> = pear::Result>; #[parser] -crate fn uri<'a>(input: &mut RawInput<'a>) -> Result<'a, Uri<'a>> { +pub(crate) fn uri<'a>(input: &mut RawInput<'a>) -> Result<'a, Uri<'a>> { match input.len() { 0 => return Err(pear_error!("empty URI")), 1 => switch! { @@ -29,12 +29,12 @@ crate fn uri<'a>(input: &mut RawInput<'a>) -> Result<'a, Uri<'a>> { } #[parser] -crate fn origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a>> { +pub(crate) fn origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a>> { (peek(b'/')?, path_and_query(is_pchar)?).1 } #[parser] -crate fn rocket_route_origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a>> { +pub(crate) fn rocket_route_origin<'a>(input: &mut RawInput<'a>) -> Result<'a, Origin<'a>> { (peek(b'/')?, path_and_query(is_pchar_or_rchar)?).1 } @@ -128,7 +128,7 @@ fn absolute<'a>( } #[parser] -crate fn authority_only<'a>(input: &mut RawInput<'a>) -> Result<'a, Authority<'a>> { +pub(crate) fn authority_only<'a>(input: &mut RawInput<'a>) -> Result<'a, Authority<'a>> { if let Uri::Authority(authority) = absolute_or_authority()? { Ok(authority) } else { @@ -137,7 +137,7 @@ crate fn authority_only<'a>(input: &mut RawInput<'a>) -> Result<'a, Authority<'a } #[parser] -crate fn absolute_only<'a>(input: &mut RawInput<'a>) -> Result<'a, Absolute<'a>> { +pub(crate) fn absolute_only<'a>(input: &mut RawInput<'a>) -> Result<'a, Absolute<'a>> { if let Uri::Absolute(absolute) = absolute_or_authority()? { Ok(absolute) } else { diff --git a/core/http/src/uri/absolute.rs b/core/http/src/uri/absolute.rs index b059a280c7..10e0135352 100644 --- a/core/http/src/uri/absolute.rs +++ b/core/http/src/uri/absolute.rs @@ -44,7 +44,7 @@ impl IntoOwned for Absolute<'_> { impl<'a> Absolute<'a> { #[inline] - crate unsafe fn raw( + pub(crate) unsafe fn raw( source: Cow<'a, [u8]>, scheme: Indexed<'a, [u8]>, authority: Option>, @@ -59,7 +59,7 @@ impl<'a> Absolute<'a> { } #[cfg(test)] - crate fn new( + pub(crate) fn new( scheme: &'a str, authority: Option>, origin: Option> diff --git a/core/http/src/uri/authority.rs b/core/http/src/uri/authority.rs index 7bdcc8dd6a..2c3acb6ed9 100644 --- a/core/http/src/uri/authority.rs +++ b/core/http/src/uri/authority.rs @@ -28,7 +28,7 @@ pub struct Authority<'a> { } #[derive(Debug, Clone)] -crate enum Host { +pub(crate) enum Host { Bracketed(T), Raw(T) } @@ -55,7 +55,7 @@ impl IntoOwned for Authority<'_> { } impl<'a> Authority<'a> { - crate unsafe fn raw( + pub(crate) unsafe fn raw( source: Cow<'a, [u8]>, user_info: Option>, host: Host>, @@ -70,7 +70,7 @@ impl<'a> Authority<'a> { } #[cfg(test)] - crate fn new( + pub(crate) fn new( user_info: Option<&'a str>, host: Host<&'a str>, port: Option diff --git a/core/http/src/uri/encoding.rs b/core/http/src/uri/encoding.rs index c08d96cf06..11736d5168 100644 --- a/core/http/src/uri/encoding.rs +++ b/core/http/src/uri/encoding.rs @@ -8,7 +8,7 @@ use crate::parse::uri::is_pchar; #[derive(Clone, Copy)] #[allow(non_camel_case_types)] -crate struct UNSAFE_ENCODE_SET(PhantomData

); +pub(crate) struct UNSAFE_ENCODE_SET(PhantomData

); impl Default for UNSAFE_ENCODE_SET

{ #[inline(always)] @@ -31,7 +31,7 @@ impl EncodeSet for UNSAFE_ENCODE_SET { #[derive(Clone, Copy)] #[allow(non_camel_case_types)] -crate struct ENCODE_SET(PhantomData

); +pub(crate) struct ENCODE_SET(PhantomData

); impl EncodeSet for ENCODE_SET { #[inline(always)] @@ -52,7 +52,7 @@ impl EncodeSet for ENCODE_SET { #[derive(Default, Clone, Copy)] #[allow(non_camel_case_types)] -crate struct DEFAULT_ENCODE_SET; +pub(crate) struct DEFAULT_ENCODE_SET; impl EncodeSet for DEFAULT_ENCODE_SET { #[inline(always)] @@ -62,7 +62,7 @@ impl EncodeSet for DEFAULT_ENCODE_SET { } } -crate fn unsafe_percent_encode(string: &str) -> Cow<'_, str> { +pub(crate) fn unsafe_percent_encode(string: &str) -> Cow<'_, str> { match P::DELIMITER { '/' => percent_encode::>(string), '&' => percent_encode::>(string), @@ -70,6 +70,6 @@ crate fn unsafe_percent_encode(string: &str) -> Cow<'_, str> { } } -crate fn percent_encode(string: &str) -> Cow<'_, str> { +pub(crate) fn percent_encode(string: &str) -> Cow<'_, str> { utf8_percent_encode(string, S::default()).into() } diff --git a/core/http/src/uri/formatter.rs b/core/http/src/uri/formatter.rs index bdf4244e46..77f1a20fa7 100644 --- a/core/http/src/uri/formatter.rs +++ b/core/http/src/uri/formatter.rs @@ -158,7 +158,7 @@ pub struct Formatter<'i, P: UriPart> { impl<'i, P: UriPart> Formatter<'i, P> { #[inline(always)] - crate fn new(inner: &'i mut (dyn Write + 'i)) -> Self { + pub(crate) fn new(inner: &'i mut (dyn Write + 'i)) -> Self { Formatter { inner, prefixes: SmallVec::new(), diff --git a/core/http/src/uri/mod.rs b/core/http/src/uri/mod.rs index 029317994a..f13ff2e50b 100644 --- a/core/http/src/uri/mod.rs +++ b/core/http/src/uri/mod.rs @@ -9,7 +9,7 @@ mod authority; mod absolute; mod segments; -crate mod encoding; +pub(crate) mod encoding; pub use crate::parse::uri::Error; diff --git a/core/http/src/uri/origin.rs b/core/http/src/uri/origin.rs index 688151a5f0..00479a4086 100644 --- a/core/http/src/uri/origin.rs +++ b/core/http/src/uri/origin.rs @@ -85,10 +85,10 @@ use state::Storage; /// ``` #[derive(Clone, Debug)] pub struct Origin<'a> { - crate source: Option>, - crate path: IndexedStr<'a>, - crate query: Option>, - crate segment_count: Storage, + pub(crate) source: Option>, + pub(crate) path: IndexedStr<'a>, + pub(crate) query: Option>, + pub(crate) segment_count: Storage, } impl<'b> PartialEq> for Origin<'_> { @@ -112,7 +112,7 @@ impl IntoOwned for Origin<'_> { impl<'a> Origin<'a> { #[inline] - crate unsafe fn raw( + pub(crate) unsafe fn raw( source: Cow<'a, [u8]>, path: Indexed<'a, [u8]>, query: Option> diff --git a/core/http/src/uri/uri.rs b/core/http/src/uri/uri.rs index 325dede806..3f1a48655a 100644 --- a/core/http/src/uri/uri.rs +++ b/core/http/src/uri/uri.rs @@ -62,7 +62,7 @@ pub enum Uri<'a> { impl<'a> Uri<'a> { #[inline] - crate unsafe fn raw_absolute( + pub(crate) unsafe fn raw_absolute( source: Cow<'a, [u8]>, scheme: Indexed<'a, [u8]>, path: Indexed<'a, [u8]>, @@ -226,7 +226,7 @@ impl<'a> Uri<'a> { } } -crate unsafe fn as_utf8_unchecked(input: Cow<'_, [u8]>) -> Cow<'_, str> { +pub(crate) unsafe fn as_utf8_unchecked(input: Cow<'_, [u8]>) -> Cow<'_, str> { match input { Cow::Borrowed(bytes) => Cow::Borrowed(std::str::from_utf8_unchecked(bytes)), Cow::Owned(bytes) => Cow::Owned(String::from_utf8_unchecked(bytes)) diff --git a/core/lib/src/catcher.rs b/core/lib/src/catcher.rs index f455edb29f..bfc811017d 100644 --- a/core/lib/src/catcher.rs +++ b/core/lib/src/catcher.rs @@ -66,7 +66,7 @@ pub struct Catcher { pub code: u16, /// The catcher's associated handler. pub handler: ErrorHandler, - crate is_default: bool, + pub(crate) is_default: bool, } impl Catcher { @@ -101,7 +101,7 @@ impl Catcher { } #[inline(always)] - crate fn handle<'r>(&self, req: &'r Request<'_>) -> impl Future> { + pub(crate) fn handle<'r>(&self, req: &'r Request<'_>) -> impl Future> { (self.handler)(req) } diff --git a/core/lib/src/config/config.rs b/core/lib/src/config/config.rs index b237aa3ea5..4bdb0cb51f 100644 --- a/core/lib/src/config/config.rs +++ b/core/lib/src/config/config.rs @@ -49,17 +49,17 @@ pub struct Config { /// How much information to log. pub log_level: LoggingLevel, /// The secret key. - crate secret_key: SecretKey, + pub(crate) secret_key: SecretKey, /// TLS configuration. - crate tls: Option, + pub(crate) tls: Option, /// Streaming read size limits. pub limits: Limits, /// Extra parameters that aren't part of Rocket's core config. pub extras: HashMap, /// The path to the configuration file this config was loaded from, if any. - crate config_file_path: Option, + pub(crate) config_file_path: Option, /// The path root-relative files will be rooted from. - crate root_path: Option, + pub(crate) root_path: Option, } macro_rules! config_from_raw { @@ -192,7 +192,7 @@ impl Config { /// # Panics /// /// Panics if randomness cannot be retrieved from the OS. - crate fn default_from

(env: Environment, path: P) -> Result + pub(crate) fn default_from

(env: Environment, path: P) -> Result where P: AsRef { let mut config = Config::default(env); @@ -214,7 +214,7 @@ impl Config { /// # Panics /// /// Panics if randomness cannot be retrieved from the OS. - crate fn default(env: Environment) -> Config { + pub(crate) fn default(env: Environment) -> Config { // Note: This may truncate if num_cpus::get() / 2 > u16::max. That's okay. let default_workers = (num_cpus::get() * 2) as u16; @@ -276,7 +276,7 @@ impl Config { /// Constructs a `BadType` error given the entry `name`, the invalid `val` /// at that entry, and the `expect`ed type name. #[inline(always)] - crate fn bad_type(&self, + pub(crate) fn bad_type(&self, name: &str, actual: &'static str, expect: &'static str) -> ConfigError { @@ -300,7 +300,7 @@ impl Config { /// * **log**: String /// * **secret_key**: String (256-bit base64) /// * **tls**: Table (`certs` (path as String), `key` (path as String)) - crate fn set_raw(&mut self, name: &str, val: &Value) -> Result<()> { + pub(crate) fn set_raw(&mut self, name: &str, val: &Value) -> Result<()> { let (id, ok) = (|val| val, |_| Ok(())); config_from_raw!(self, name, val, address => (str, set_address, id), @@ -626,7 +626,7 @@ impl Config { /// Retrieves the secret key from `self`. #[inline] - crate fn secret_key(&self) -> &Key { + pub(crate) fn secret_key(&self) -> &Key { self.secret_key.inner() } diff --git a/core/lib/src/config/custom_values.rs b/core/lib/src/config/custom_values.rs index 2b50cb1cea..e477281373 100644 --- a/core/lib/src/config/custom_values.rs +++ b/core/lib/src/config/custom_values.rs @@ -13,14 +13,14 @@ pub enum SecretKey { impl SecretKey { #[inline] - crate fn inner(&self) -> &Key { + pub(crate) fn inner(&self) -> &Key { match *self { SecretKey::Generated(ref key) | SecretKey::Provided(ref key) => key } } #[inline] - crate fn is_generated(&self) -> bool { + pub(crate) fn is_generated(&self) -> bool { match *self { #[cfg(feature = "private-cookies")] SecretKey::Generated(_) => true, @@ -82,7 +82,7 @@ pub struct TlsConfig; #[derive(Debug, Clone)] pub struct Limits { // We cache this internally but don't share that fact in the API. - crate forms: u64, + pub(crate) forms: u64, extra: Vec<(String, u64)> } diff --git a/core/lib/src/config/environment.rs b/core/lib/src/config/environment.rs index e32e09be36..7296d9542b 100644 --- a/core/lib/src/config/environment.rs +++ b/core/lib/src/config/environment.rs @@ -21,10 +21,10 @@ pub enum Environment { impl Environment { /// List of all of the possible environments. - crate const ALL: [Environment; 3] = [Development, Staging, Production]; + pub(crate) const ALL: [Environment; 3] = [Development, Staging, Production]; /// String of all valid environments. - crate const VALID: &'static str = "development, staging, production"; + pub(crate) const VALID: &'static str = "development, staging, production"; /// Retrieves the "active" environment as determined by the `ROCKET_ENV` /// environment variable. If `ROCKET_ENV` is not set, returns `Development` diff --git a/core/lib/src/config/mod.rs b/core/lib/src/config/mod.rs index 6f48d18e60..16164e4f17 100644 --- a/core/lib/src/config/mod.rs +++ b/core/lib/src/config/mod.rs @@ -204,7 +204,7 @@ pub use self::environment::Environment; pub use self::config::Config; pub use self::builder::ConfigBuilder; pub use crate::logger::LoggingLevel; -crate use self::toml_ext::LoggedValue; +pub(crate) use self::toml_ext::LoggedValue; use crate::logger; use self::Environment::*; @@ -443,7 +443,7 @@ impl RocketConfig { /// # Panics /// /// If there is a problem, prints a nice error message and bails. -crate fn init() -> Config { +pub(crate) fn init() -> Config { let bail = |e: ConfigError| -> ! { logger::init(LoggingLevel::Debug); e.pretty_print(); diff --git a/core/lib/src/config/toml_ext.rs b/core/lib/src/config/toml_ext.rs index d7a9385969..3db30e89d3 100644 --- a/core/lib/src/config/toml_ext.rs +++ b/core/lib/src/config/toml_ext.rs @@ -81,7 +81,7 @@ pub fn parse_simple_toml_value(mut input: &str) -> StdResult { /// A simple wrapper over a `Value` reference with a custom implementation of /// `Display`. This is used to log config values at initialization. -crate struct LoggedValue<'a>(pub &'a Value); +pub(crate) struct LoggedValue<'a>(pub &'a Value); impl fmt::Display for LoggedValue<'_> { #[inline] diff --git a/core/lib/src/data/data.rs b/core/lib/src/data/data.rs index d5cb5f7f4f..928e766d5d 100644 --- a/core/lib/src/data/data.rs +++ b/core/lib/src/data/data.rs @@ -75,7 +75,7 @@ impl Data { DataStream(buffer, stream) } - crate fn from_hyp(body: hyper::Body) -> impl Future { + pub(crate) fn from_hyp(body: hyper::Body) -> impl Future { // TODO.async: This used to also set the read timeout to 5 seconds. Data::new(body) @@ -180,7 +180,7 @@ impl Data { // bytes `vec[pos..cap]` are buffered and unread. The remainder of the data // bytes can be read from `stream`. #[inline(always)] - crate async fn new(body: hyper::Body) -> Data { + pub(crate) async fn new(body: hyper::Body) -> Data { trace_!("Data::new({:?})", body); let mut stream = body.compat().map_err(|e| { @@ -217,7 +217,7 @@ impl Data { /// This creates a `data` object from a local data source `data`. #[inline] - crate fn local(data: Vec) -> Data { + pub(crate) fn local(data: Vec) -> Data { Data { buffer: data, stream: Box::new(&[][..]), diff --git a/core/lib/src/data/data_stream.rs b/core/lib/src/data/data_stream.rs index f2e79e2d8f..721079bdb3 100644 --- a/core/lib/src/data/data_stream.rs +++ b/core/lib/src/data/data_stream.rs @@ -11,7 +11,7 @@ use futures::task::{Poll, Context}; /// [`Data::open()`](crate::data::Data::open()). The stream contains all of the data /// in the body of the request. It exposes no methods directly. Instead, it must /// be used as an opaque [`Read`] structure. -pub struct DataStream(crate Vec, crate Box); +pub struct DataStream(pub(crate) Vec, pub(crate) Box); // TODO.async: Consider implementing `AsyncBufRead` diff --git a/core/lib/src/error.rs b/core/lib/src/error.rs index 422dee33fa..3c0fa94198 100644 --- a/core/lib/src/error.rs +++ b/core/lib/src/error.rs @@ -87,7 +87,7 @@ pub struct LaunchError { impl LaunchError { #[inline(always)] - crate fn new(kind: LaunchErrorKind) -> LaunchError { + pub(crate) fn new(kind: LaunchErrorKind) -> LaunchError { LaunchError { handled: AtomicBool::new(false), kind } } diff --git a/core/lib/src/fairing/mod.rs b/core/lib/src/fairing/mod.rs index 8ee0254bc2..7ee7e14fd8 100644 --- a/core/lib/src/fairing/mod.rs +++ b/core/lib/src/fairing/mod.rs @@ -57,7 +57,7 @@ mod fairings; mod ad_hoc; mod info_kind; -crate use self::fairings::Fairings; +pub(crate) use self::fairings::Fairings; pub use self::ad_hoc::AdHoc; pub use self::info_kind::{Info, Kind}; diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index c3f03eb4ab..d60f26c996 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -1,8 +1,6 @@ #![feature(specialization)] #![feature(try_trait)] #![feature(proc_macro_hygiene)] -#![feature(crate_visibility_modifier)] -#![feature(label_break_value)] #![feature(async_await)] #![recursion_limit="256"] diff --git a/core/lib/src/local/client.rs b/core/lib/src/local/client.rs index 8e7900dd61..a409ac03fb 100644 --- a/core/lib/src/local/client.rs +++ b/core/lib/src/local/client.rs @@ -69,7 +69,7 @@ use crate::error::LaunchError; /// [`post()`]: #method.post pub struct Client { rocket: Rocket, - crate cookies: Option>, + pub(crate) cookies: Option>, } impl Client { diff --git a/core/lib/src/local/request.rs b/core/lib/src/local/request.rs index 1a7076b2cf..d9d2c6a9db 100644 --- a/core/lib/src/local/request.rs +++ b/core/lib/src/local/request.rs @@ -101,7 +101,7 @@ pub struct LocalRequest<'c> { impl<'c> LocalRequest<'c> { #[inline(always)] - crate fn new( + pub(crate) fn new( client: &'c Client, method: Method, uri: Cow<'c, str> diff --git a/core/lib/src/logger.rs b/core/lib/src/logger.rs index 81e62750e5..a6a63562ad 100644 --- a/core/lib/src/logger.rs +++ b/core/lib/src/logger.rs @@ -6,7 +6,7 @@ use std::str::FromStr; use log; use yansi::Paint; -crate const COLORS_ENV: &str = "ROCKET_CLI_COLORS"; +pub(crate) const COLORS_ENV: &str = "ROCKET_CLI_COLORS"; struct RocketLogger(LoggingLevel); @@ -145,7 +145,7 @@ impl log::Log for RocketLogger { } } -crate fn try_init(level: LoggingLevel, verbose: bool) -> bool { +pub(crate) fn try_init(level: LoggingLevel, verbose: bool) -> bool { if level == LoggingLevel::Off { return false; } @@ -198,13 +198,13 @@ fn usize_to_filter(num: usize) -> log::LevelFilter { } } -crate fn push_max_level(level: LoggingLevel) { +pub(crate) fn push_max_level(level: LoggingLevel) { LAST_LOG_FILTER.store(filter_to_usize(log::max_level()), Ordering::Release); PUSHED.store(true, Ordering::Release); log::set_max_level(level.to_level_filter()); } -crate fn pop_max_level() { +pub(crate) fn pop_max_level() { if PUSHED.load(Ordering::Acquire) { log::set_max_level(usize_to_filter(LAST_LOG_FILTER.load(Ordering::Acquire))); } diff --git a/core/lib/src/request/form/form.rs b/core/lib/src/request/form/form.rs index 282f46c642..25ca26c1cf 100644 --- a/core/lib/src/request/form/form.rs +++ b/core/lib/src/request/form/form.rs @@ -151,7 +151,7 @@ impl Deref for Form { } impl<'f, T: FromForm<'f>> Form { - crate fn from_data( + pub(crate) fn from_data( form_str: &'f str, strict: bool ) -> Outcome> { diff --git a/core/lib/src/request/request.rs b/core/lib/src/request/request.rs index c1d55e73be..862fe2749b 100644 --- a/core/lib/src/request/request.rs +++ b/core/lib/src/request/request.rs @@ -31,24 +31,24 @@ pub struct Request<'r> { uri: Origin<'r>, headers: HeaderMap<'r>, remote: Option, - crate state: RequestState<'r>, + pub(crate) state: RequestState<'r>, } //#[derive(Clone)] -crate struct RequestState<'r> { - crate config: &'r Config, - crate managed: &'r Container, - crate path_segments: SmallVec<[Indices; 12]>, - crate query_items: Option>, - crate route: RwLock>, - crate cookies: Mutex>, - crate accept: Storage>, - crate content_type: Storage>, - crate cache: Arc, +pub(crate) struct RequestState<'r> { + pub(crate) config: &'r Config, + pub(crate) managed: &'r Container, + pub(crate) path_segments: SmallVec<[Indices; 12]>, + pub(crate) query_items: Option>, + pub(crate) route: RwLock>, + pub(crate) cookies: Mutex>, + pub(crate) accept: Storage>, + pub(crate) content_type: Storage>, + pub(crate) cache: Arc, } #[derive(Clone)] -crate struct IndexedFormItem { +pub(crate) struct IndexedFormItem { raw: Indices, key: Indices, value: Indices @@ -57,7 +57,7 @@ crate struct IndexedFormItem { impl<'r> Request<'r> { /// Create a new `Request` with the given `method` and `uri`. #[inline(always)] - crate fn new<'s: 'r>( + pub(crate) fn new<'s: 'r>( rocket: &'r Rocket, method: Method, uri: Origin<'s> @@ -743,7 +743,7 @@ impl<'r> Request<'r> { // Returns an iterator over the raw segments of the path URI. Does not take // into account the current route. This is used during routing. #[inline] - crate fn raw_path_segments(&self) -> impl Iterator { + pub(crate) fn raw_path_segments(&self) -> impl Iterator { let path = self.uri.path(); self.state.path_segments.iter().cloned() .map(move |(i, j)| path[i..j].into()) @@ -772,19 +772,19 @@ impl<'r> Request<'r> { /// Set `self`'s parameters given that the route used to reach this request /// was `route`. Use during routing when attempting a given route. #[inline(always)] - crate fn set_route(&self, route: &'r Route) { + pub(crate) fn set_route(&self, route: &'r Route) { * self.state.route.write().unwrap() = Some(route); } /// Set the method of `self`, even when `self` is a shared reference. Used /// during routing to override methods for re-routing. #[inline(always)] - crate fn _set_method(&self, method: Method) { + pub(crate) fn _set_method(&self, method: Method) { *self.method.write().unwrap() = method; } /// Convert from Hyper types into a Rocket Request. - crate fn from_hyp( + pub(crate) fn from_hyp( rocket: &'r Rocket, h_method: hyper::Method, h_headers: hyper::HeaderMap, diff --git a/core/lib/src/response/mod.rs b/core/lib/src/response/mod.rs index bb0bb5e1fa..3a5b5b6ea6 100644 --- a/core/lib/src/response/mod.rs +++ b/core/lib/src/response/mod.rs @@ -26,7 +26,7 @@ mod named_file; mod stream; mod response; -crate mod flash; +pub(crate) mod flash; pub mod content; pub mod status; diff --git a/core/lib/src/response/response.rs b/core/lib/src/response/response.rs index dd57b5180b..1ceaee1c10 100644 --- a/core/lib/src/response/response.rs +++ b/core/lib/src/response/response.rs @@ -993,7 +993,7 @@ impl<'r> Response<'r> { // Makes the `AsyncRead`er in the body empty but leaves the size of the body if // it exists. Only meant to be used to handle HEAD requests automatically. #[inline(always)] - crate fn strip_body(&mut self) { + pub(crate) fn strip_body(&mut self) { if let Some(body) = self.take_body() { self.body = match body { Body::Sized(_, n) => Some(Body::Sized(Box::pin(io::empty()), n)), diff --git a/core/lib/src/rocket.rs b/core/lib/src/rocket.rs index fdc6804134..9538a9d908 100644 --- a/core/lib/src/rocket.rs +++ b/core/lib/src/rocket.rs @@ -38,11 +38,11 @@ use crate::http::uri::Origin; /// The main `Rocket` type: used to mount routes and catchers and launch the /// application. pub struct Rocket { - crate config: Config, + pub(crate) config: Config, router: Router, default_catchers: HashMap, catchers: HashMap, - crate state: Container, + pub(crate) state: Container, fairings: Fairings, } @@ -239,7 +239,7 @@ impl Rocket { } #[inline] - crate fn dispatch<'s, 'r: 's>( + pub(crate) fn dispatch<'s, 'r: 's>( &'s self, request: &'r mut Request<'s>, data: Data @@ -324,7 +324,7 @@ impl Rocket { // (ensuring `handler` takes an immutable borrow), any caller to `route` // should be able to supply an `&mut` and retain an `&` after the call. #[inline] - crate fn route<'s, 'r: 's>( + pub(crate) fn route<'s, 'r: 's>( &'s self, request: &'r Request<'s>, mut data: Data, @@ -359,7 +359,7 @@ impl Rocket { // catcher is called. If the catcher fails to return a good response, the // 500 catcher is executed. If there is no registered catcher for `status`, // the default catcher is used. - crate fn handle_error<'s, 'r: 's>( + pub(crate) fn handle_error<'s, 'r: 's>( &'s self, status: Status, req: &'r Request<'s> @@ -705,7 +705,7 @@ impl Rocket { self } - crate fn prelaunch_check(mut self) -> Result { + pub(crate) fn prelaunch_check(mut self) -> Result { self.router = match self.router.collisions() { Ok(router) => router, Err(e) => return Err(LaunchError::new(LaunchErrorKind::Collision(e))) diff --git a/core/lib/src/router/mod.rs b/core/lib/src/router/mod.rs index 03e2c5d248..edf25b7020 100644 --- a/core/lib/src/router/mod.rs +++ b/core/lib/src/router/mod.rs @@ -14,7 +14,7 @@ use crate::http::Method; type Selector = Method; // A handler to use when one is needed temporarily. -crate fn dummy_handler<'r>(r: &'r Request<'_>, _: crate::Data) -> std::pin::Pin> + Send + 'r>> { +pub(crate) fn dummy_handler<'r>(r: &'r Request<'_>, _: crate::Data) -> std::pin::Pin> + Send + 'r>> { crate::Outcome::from(r, ()) } @@ -50,7 +50,7 @@ impl Router { matches } - crate fn collisions(mut self) -> Result> { + pub(crate) fn collisions(mut self) -> Result> { let mut collisions = vec![]; for routes in self.routes.values_mut() { for i in 0..routes.len() { diff --git a/core/lib/src/router/route.rs b/core/lib/src/router/route.rs index cd32743d75..017653adc6 100644 --- a/core/lib/src/router/route.rs +++ b/core/lib/src/router/route.rs @@ -30,14 +30,14 @@ pub struct Route { /// The media type this route matches against, if any. pub format: Option, /// Cached metadata that aids in routing later. - crate metadata: Metadata + pub(crate) metadata: Metadata } #[derive(Debug, Default, Clone)] -crate struct Metadata { - crate path_segments: Vec>, - crate query_segments: Option>>, - crate fully_dynamic_query: bool, +pub(crate) struct Metadata { + pub(crate) path_segments: Vec>, + pub(crate) query_segments: Option>>, + pub(crate) fully_dynamic_query: bool, } impl Metadata {