From 1e5a1b89409feb827b0d1ee20525191d17ad5c13 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Thu, 20 Apr 2017 20:30:41 -0700 Subject: [PATCH] Remove 'testing' feature. Close stream on network error. This is a breaking change. The `testing` feature no longer exists. Testing structures can now be accessed without any features enabled. Prior to this change, Rocket would panic when draining from a network stream failed. With this change, Rocket force closes the stream on any error. This change also ensures that the `Fairings` launch output only prints if at least one fairing has been attached. --- examples/config/Cargo.toml | 3 - examples/content_types/Cargo.toml | 3 - examples/cookies/Cargo.toml | 3 - examples/errors/Cargo.toml | 3 - examples/extended_validation/Cargo.toml | 3 - examples/fairings/Cargo.toml | 3 - examples/forms/Cargo.toml | 3 - examples/from_request/Cargo.toml | 3 - examples/handlebars_templates/Cargo.toml | 3 - examples/hello_alt_methods/Cargo.toml | 3 - examples/hello_person/Cargo.toml | 3 - examples/hello_ranks/Cargo.toml | 3 - examples/hello_tls/Cargo.toml | 3 - examples/hello_world/Cargo.toml | 3 - examples/json/Cargo.toml | 3 - examples/managed_queue/Cargo.toml | 3 - examples/manual_routes/Cargo.toml | 3 - examples/msgpack/Cargo.toml | 3 - examples/optional_redirect/Cargo.toml | 3 - examples/optional_result/Cargo.toml | 3 - examples/pastebin/Cargo.toml | 3 - examples/query_params/Cargo.toml | 3 - examples/raw_sqlite/Cargo.toml | 3 - examples/redirect/Cargo.toml | 3 - examples/session/Cargo.toml | 3 - examples/state/Cargo.toml | 3 - examples/static_files/Cargo.toml | 3 - examples/testing/Cargo.toml | 3 - examples/uuid/Cargo.toml | 3 - lib/Cargo.toml | 1 - lib/benches/format-routing.rs | 1 - lib/benches/ranked-routing.rs | 1 - lib/benches/simple-routing.rs | 1 - lib/src/data/data.rs | 43 +++++++-- lib/src/data/data_stream.rs | 91 +++---------------- lib/src/data/mod.rs | 27 +----- lib/src/data/net_stream.rs | 87 ++++++++++++++++++ lib/src/data/test_data.rs | 75 --------------- lib/src/fairing/mod.rs | 10 +- lib/src/lib.rs | 2 +- lib/src/rocket.rs | 1 - lib/src/testing.rs | 41 ++------- lib/tests/form_method-issue-45.rs | 1 - lib/tests/form_value_decoding-issue-82.rs | 1 - lib/tests/head_handling.rs | 1 - lib/tests/limits.rs | 1 - lib/tests/precise-content-type-matching.rs | 1 - lib/tests/query-and-non-query-dont-collide.rs | 1 - lib/tests/redirect_from_catcher-issue-113.rs | 1 - lib/tests/remote-rewrite.rs | 1 - lib/tests/segments-issues-41-86.rs | 1 - 51 files changed, 155 insertions(+), 322 deletions(-) create mode 100644 lib/src/data/net_stream.rs delete mode 100644 lib/src/data/test_data.rs diff --git a/examples/config/Cargo.toml b/examples/config/Cargo.toml index 884c1fa8..dc5343ce 100644 --- a/examples/config/Cargo.toml +++ b/examples/config/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/content_types/Cargo.toml b/examples/content_types/Cargo.toml index e1455c7e..d999bedd 100644 --- a/examples/content_types/Cargo.toml +++ b/examples/content_types/Cargo.toml @@ -9,6 +9,3 @@ rocket_codegen = { path = "../../codegen" } serde = "0.9" serde_json = "0.9" serde_derive = "0.9" - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/cookies/Cargo.toml b/examples/cookies/Cargo.toml index ee0da5be..d10f4b89 100644 --- a/examples/cookies/Cargo.toml +++ b/examples/cookies/Cargo.toml @@ -11,6 +11,3 @@ rocket_codegen = { path = "../../codegen" } path = "../../contrib" default-features = false features = ["handlebars_templates"] - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/errors/Cargo.toml b/examples/errors/Cargo.toml index 9ef31a5c..2bb7eec2 100644 --- a/examples/errors/Cargo.toml +++ b/examples/errors/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/extended_validation/Cargo.toml b/examples/extended_validation/Cargo.toml index c2c1ffa3..dd719fa7 100644 --- a/examples/extended_validation/Cargo.toml +++ b/examples/extended_validation/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/fairings/Cargo.toml b/examples/fairings/Cargo.toml index c45bc723..40368ab6 100644 --- a/examples/fairings/Cargo.toml +++ b/examples/fairings/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/forms/Cargo.toml b/examples/forms/Cargo.toml index 6c73b2df..1c4cc4ee 100644 --- a/examples/forms/Cargo.toml +++ b/examples/forms/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/from_request/Cargo.toml b/examples/from_request/Cargo.toml index 31b232a0..f6551ac2 100644 --- a/examples/from_request/Cargo.toml +++ b/examples/from_request/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/handlebars_templates/Cargo.toml b/examples/handlebars_templates/Cargo.toml index 3a5b4266..5384c9e4 100644 --- a/examples/handlebars_templates/Cargo.toml +++ b/examples/handlebars_templates/Cargo.toml @@ -14,6 +14,3 @@ serde_json = "0.9" path = "../../contrib" default-features = false features = ["handlebars_templates"] - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/hello_alt_methods/Cargo.toml b/examples/hello_alt_methods/Cargo.toml index d3f67872..d0731a51 100644 --- a/examples/hello_alt_methods/Cargo.toml +++ b/examples/hello_alt_methods/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/hello_person/Cargo.toml b/examples/hello_person/Cargo.toml index 93ba8a66..6628c1af 100644 --- a/examples/hello_person/Cargo.toml +++ b/examples/hello_person/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/hello_ranks/Cargo.toml b/examples/hello_ranks/Cargo.toml index f3b6dc95..99fd4aae 100644 --- a/examples/hello_ranks/Cargo.toml +++ b/examples/hello_ranks/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/hello_tls/Cargo.toml b/examples/hello_tls/Cargo.toml index d691440e..1bfa6f1a 100644 --- a/examples/hello_tls/Cargo.toml +++ b/examples/hello_tls/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib", features = ["tls"] } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/hello_world/Cargo.toml b/examples/hello_world/Cargo.toml index 9e726476..ccd454ee 100644 --- a/examples/hello_world/Cargo.toml +++ b/examples/hello_world/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/json/Cargo.toml b/examples/json/Cargo.toml index 3bee1177..5e96c61a 100644 --- a/examples/json/Cargo.toml +++ b/examples/json/Cargo.toml @@ -14,6 +14,3 @@ serde_derive = "0.9" path = "../../contrib" default-features = false features = ["json"] - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/managed_queue/Cargo.toml b/examples/managed_queue/Cargo.toml index da95be2f..24f7fe59 100644 --- a/examples/managed_queue/Cargo.toml +++ b/examples/managed_queue/Cargo.toml @@ -7,6 +7,3 @@ workspace = "../.." crossbeam = "*" rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/manual_routes/Cargo.toml b/examples/manual_routes/Cargo.toml index 3b20079f..af75e891 100644 --- a/examples/manual_routes/Cargo.toml +++ b/examples/manual_routes/Cargo.toml @@ -5,6 +5,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/msgpack/Cargo.toml b/examples/msgpack/Cargo.toml index 4d0bd76b..e2c904dd 100644 --- a/examples/msgpack/Cargo.toml +++ b/examples/msgpack/Cargo.toml @@ -13,6 +13,3 @@ serde_derive = "0.9" path = "../../contrib" default-features = false features = ["msgpack"] - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/optional_redirect/Cargo.toml b/examples/optional_redirect/Cargo.toml index 90ee8ab0..e945bd34 100644 --- a/examples/optional_redirect/Cargo.toml +++ b/examples/optional_redirect/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/optional_result/Cargo.toml b/examples/optional_result/Cargo.toml index b5ecb5b9..f0bd8b4e 100644 --- a/examples/optional_result/Cargo.toml +++ b/examples/optional_result/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/pastebin/Cargo.toml b/examples/pastebin/Cargo.toml index de14c65e..907ff59a 100644 --- a/examples/pastebin/Cargo.toml +++ b/examples/pastebin/Cargo.toml @@ -7,6 +7,3 @@ workspace = "../../" rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } rand = "0.3" - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/query_params/Cargo.toml b/examples/query_params/Cargo.toml index 45dd3c2c..0b8c86f6 100644 --- a/examples/query_params/Cargo.toml +++ b/examples/query_params/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/raw_sqlite/Cargo.toml b/examples/raw_sqlite/Cargo.toml index 9df1c4b5..9252323e 100644 --- a/examples/raw_sqlite/Cargo.toml +++ b/examples/raw_sqlite/Cargo.toml @@ -7,6 +7,3 @@ workspace = "../../" rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } rusqlite = "0.10" - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/redirect/Cargo.toml b/examples/redirect/Cargo.toml index 2e4693be..0f38fae7 100644 --- a/examples/redirect/Cargo.toml +++ b/examples/redirect/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/session/Cargo.toml b/examples/session/Cargo.toml index 64c480e8..390ef1f6 100644 --- a/examples/session/Cargo.toml +++ b/examples/session/Cargo.toml @@ -11,6 +11,3 @@ rocket_codegen = { path = "../../codegen" } path = "../../contrib" default-features = false features = ["handlebars_templates"] - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/state/Cargo.toml b/examples/state/Cargo.toml index c41a40eb..008d3ae7 100644 --- a/examples/state/Cargo.toml +++ b/examples/state/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/static_files/Cargo.toml b/examples/static_files/Cargo.toml index 09159834..4c3355eb 100644 --- a/examples/static_files/Cargo.toml +++ b/examples/static_files/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/testing/Cargo.toml b/examples/testing/Cargo.toml index 98770f56..7a728c3d 100644 --- a/examples/testing/Cargo.toml +++ b/examples/testing/Cargo.toml @@ -6,6 +6,3 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/uuid/Cargo.toml b/examples/uuid/Cargo.toml index 06ab2b76..28841e1e 100644 --- a/examples/uuid/Cargo.toml +++ b/examples/uuid/Cargo.toml @@ -13,6 +13,3 @@ lazy_static = "^0.2" default-features = false path = "../../contrib" features = ["uuid"] - -[dev-dependencies] -rocket = { path = "../../lib", features = ["testing"] } diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 8dcecad5..8cda74a2 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -15,7 +15,6 @@ build = "build.rs" categories = ["web-programming::http-server"] [features] -testing = [] tls = ["rustls", "hyper-rustls"] [dependencies] diff --git a/lib/benches/format-routing.rs b/lib/benches/format-routing.rs index a10d0880..362b829d 100644 --- a/lib/benches/format-routing.rs +++ b/lib/benches/format-routing.rs @@ -18,7 +18,6 @@ fn rocket() -> rocket::Rocket { .mount("/", routes![get, post]) } -#[cfg(feature = "testing")] mod benches { extern crate test; diff --git a/lib/benches/ranked-routing.rs b/lib/benches/ranked-routing.rs index d1d44503..1caef283 100644 --- a/lib/benches/ranked-routing.rs +++ b/lib/benches/ranked-routing.rs @@ -31,7 +31,6 @@ fn rocket() -> rocket::Rocket { .mount("/", routes![post, post2, post3]) } -#[cfg(feature = "testing")] mod benches { extern crate test; diff --git a/lib/benches/simple-routing.rs b/lib/benches/simple-routing.rs index 6363a7b7..be4d9cce 100644 --- a/lib/benches/simple-routing.rs +++ b/lib/benches/simple-routing.rs @@ -34,7 +34,6 @@ fn rocket() -> rocket::Rocket { index_b, index_c, index_dyn_a]) } -#[cfg(feature = "testing")] mod benches { extern crate test; diff --git a/lib/src/data/data.rs b/lib/src/data/data.rs index d6e92e1d..74d2fe79 100644 --- a/lib/src/data/data.rs +++ b/lib/src/data/data.rs @@ -6,8 +6,9 @@ use std::mem::transmute; #[cfg(feature = "tls")] use hyper_rustls::WrappedStream; +use super::data_stream::{DataStream, StreamReader, kill_stream}; +use super::net_stream::NetStream; use ext::ReadExt; -use super::data_stream::{DataStream, HyperNetStream, StreamReader, kill_stream}; use http::hyper::h1::HttpReader; use http::hyper::buffer; @@ -17,6 +18,9 @@ use http::hyper::net::{HttpStream, NetworkStream}; pub type BodyReader<'a, 'b> = self::HttpReader<&'a mut self::buffer::BufReader<&'b mut NetworkStream>>; +/// The number of bytes to read into the "peek" buffer. +const PEEK_BYTES: usize = 4096; + /// Type representing the data in the body of an incoming request. /// /// This type is the only means by which the body of a request can be retrieved. @@ -90,19 +94,19 @@ impl Data { let net_stream = h_body.get_ref().get_ref(); #[cfg(feature = "tls")] - fn concrete_stream(stream: &&mut NetworkStream) -> Option { + fn concrete_stream(stream: &&mut NetworkStream) -> Option { stream.downcast_ref::() - .map(|s| HyperNetStream::Http(s.clone())) + .map(|s| NetStream::Http(s.clone())) .or_else(|| { stream.downcast_ref::() - .map(|s| HyperNetStream::Https(s.clone())) + .map(|s| NetStream::Https(s.clone())) }) } #[cfg(not(feature = "tls"))] - fn concrete_stream(stream: &&mut NetworkStream) -> Option { + fn concrete_stream(stream: &&mut NetworkStream) -> Option { stream.downcast_ref::() - .map(|s| HyperNetStream::Http(s.clone())) + .map(|s| NetStream::Http(s.clone())) } // Retrieve the underlying HTTPStream from Hyper. @@ -164,15 +168,15 @@ impl Data { } // Creates a new data object with an internal buffer `buf`, where the cursor - // in the buffer is at `pos` and the buffer has `cap` valid bytes. The - // remainder of the data bytes can be read from `stream`. + // in the buffer is at `pos` and the buffer has `cap` valid bytes. Thus, the + // bytes `vec[pos..cap]` are buffered and unread. The remainder of the data + // bytes can be read from `stream`. pub(crate) fn new(mut buf: Vec, pos: usize, mut cap: usize, mut stream: StreamReader ) -> Data { // Make sure the buffer is large enough for the bytes we want to peek. - const PEEK_BYTES: usize = 4096; if buf.len() < PEEK_BYTES { trace_!("Resizing peek buffer from {} to {}.", buf.len(), PEEK_BYTES); buf.resize(PEEK_BYTES, 0); @@ -203,6 +207,27 @@ impl Data { capacity: cap, } } + + /// This creates a `data` object from a local data source `data`. + pub(crate) fn local(mut data: Vec) -> Data { + // Emulate peek buffering. + let (buf, rest) = if data.len() <= PEEK_BYTES { + (data, vec![]) + } else { + let rest = data.split_off(PEEK_BYTES); + (data, rest) + }; + + let (buf_len, stream_len) = (buf.len(), rest.len() as u64); + let stream = NetStream::Local(Cursor::new(rest)); + Data { + buffer: buf, + stream: HttpReader::SizedReader(stream, stream_len), + is_done: stream_len == 0, + position: 0, + capacity: buf_len, + } + } } impl Drop for Data { diff --git a/lib/src/data/data_stream.rs b/lib/src/data/data_stream.rs index a63371d4..c98b8293 100644 --- a/lib/src/data/data_stream.rs +++ b/lib/src/data/data_stream.rs @@ -1,80 +1,14 @@ use std::io::{self, BufRead, Read, Cursor, BufReader, Chain, Take}; -use std::net::{SocketAddr, Shutdown}; -use std::time::Duration; +use std::net::Shutdown; -#[cfg(feature = "tls")] use hyper_rustls::WrappedStream as RustlsStream; +use super::net_stream::NetStream; -use http::hyper::net::{HttpStream, NetworkStream}; +use http::hyper::net::NetworkStream; use http::hyper::h1::HttpReader; -pub type StreamReader = HttpReader; +pub type StreamReader = HttpReader; pub type InnerStream = Chain>>, BufReader>; -#[derive(Clone)] -pub enum HyperNetStream { - Http(HttpStream), - #[cfg(feature = "tls")] - Https(RustlsStream) -} - -macro_rules! with_inner { - ($net:expr, |$stream:ident| $body:expr) => ({ - trace!("{}:{}", file!(), line!()); - match *$net { - HyperNetStream::Http(ref $stream) => $body, - #[cfg(feature = "tls")] HyperNetStream::Https(ref $stream) => $body - } - }); - ($net:expr, |mut $stream:ident| $body:expr) => ({ - trace!("{}:{}", file!(), line!()); - match *$net { - HyperNetStream::Http(ref mut $stream) => $body, - #[cfg(feature = "tls")] HyperNetStream::Https(ref mut $stream) => $body - } - }) -} - -impl io::Read for HyperNetStream { - #[inline(always)] - fn read(&mut self, buf: &mut [u8]) -> io::Result { - with_inner!(self, |mut stream| io::Read::read(stream, buf)) - } -} - -impl io::Write for HyperNetStream { - #[inline(always)] - fn write(&mut self, buf: &[u8]) -> io::Result { - with_inner!(self, |mut stream| io::Write::write(stream, buf)) - } - - #[inline(always)] - fn flush(&mut self) -> io::Result<()> { - with_inner!(self, |mut stream| io::Write::flush(stream)) - } -} - -impl NetworkStream for HyperNetStream { - #[inline(always)] - fn peer_addr(&mut self) -> io::Result { - with_inner!(self, |mut stream| NetworkStream::peer_addr(stream)) - } - - #[inline(always)] - fn set_read_timeout(&self, dur: Option) -> io::Result<()> { - with_inner!(self, |stream| NetworkStream::set_read_timeout(stream, dur)) - } - - #[inline(always)] - fn set_write_timeout(&self, dur: Option) -> io::Result<()> { - with_inner!(self, |stream| NetworkStream::set_write_timeout(stream, dur)) - } - - #[inline(always)] - fn close(&mut self, how: Shutdown) -> io::Result<()> { - with_inner!(self, |mut stream| NetworkStream::close(stream, how)) - } -} - /// Raw data stream of a request body. /// /// This stream can only be obtained by calling @@ -83,12 +17,12 @@ impl NetworkStream for HyperNetStream { /// Instead, it must be used as an opaque `Read` or `BufRead` structure. pub struct DataStream { stream: InnerStream, - network: HyperNetStream, + network: NetStream, } impl DataStream { #[inline(always)] - pub(crate) fn new(stream: InnerStream, network: HyperNetStream) -> DataStream { + pub(crate) fn new(stream: InnerStream, network: NetStream) -> DataStream { DataStream { stream, network } } } @@ -112,19 +46,18 @@ impl BufRead for DataStream { } } -// pub fn kill_stream(stream: &mut S, network: &mut HyperNetStream) { -pub fn kill_stream(stream: &mut S, network: &mut N) { - io::copy(&mut stream.take(1024), &mut io::sink()).expect("kill_stream: sink"); - // If there are any more bytes, kill it. - let mut buf = [0]; - if let Ok(n) = stream.read(&mut buf) { - if n > 0 { +pub fn kill_stream(stream: &mut S, network: &mut N) { + // 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."); if let Err(e) = network.close(Shutdown::Both) { error_!("Failed to close network stream: {:?}", e); } } + Ok(n) => debug!("flushed {} unread bytes", n) } } diff --git a/lib/src/data/mod.rs b/lib/src/data/mod.rs index af868a66..5f1bb391 100644 --- a/lib/src/data/mod.rs +++ b/lib/src/data/mod.rs @@ -1,25 +1,8 @@ -//! Types and traits for reading and parsing request body data. - -#[cfg(any(test, feature = "testing"))] -#[path = "."] -mod items { - mod test_data; - - pub use self::test_data::Data; - pub use self::test_data::DataStream; -} - -#[cfg(not(any(test, feature = "testing")))] -#[path = "."] -mod items { - mod data; - mod data_stream; - - pub use self::data::Data; - pub use self::data_stream::DataStream; -} - +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, Outcome}; -pub use self::items::{Data, DataStream}; diff --git a/lib/src/data/net_stream.rs b/lib/src/data/net_stream.rs new file mode 100644 index 00000000..28c3d766 --- /dev/null +++ b/lib/src/data/net_stream.rs @@ -0,0 +1,87 @@ +use std::io::{self, Cursor}; +use std::net::{SocketAddr, Shutdown}; +use std::time::Duration; + +#[cfg(feature = "tls")] use hyper_rustls::WrappedStream as RustlsStream; +use http::hyper::net::{HttpStream, NetworkStream}; + +use self::NetStream::*; + +// 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(HttpStream), + #[cfg(feature = "tls")] + Https(RustlsStream), + Local(Cursor>) +} + +impl io::Read for NetStream { + #[inline(always)] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + match *self { + Http(ref mut stream) => stream.read(buf), + Local(ref mut stream) => stream.read(buf), + #[cfg(feature = "tls")] Https(ref mut stream) => stream.read(buf) + } + } +} + +impl io::Write for NetStream { + #[inline(always)] + fn write(&mut self, buf: &[u8]) -> io::Result { + match *self { + Http(ref mut stream) => stream.write(buf), + Local(ref mut stream) => stream.write(buf), + #[cfg(feature = "tls")] Https(ref mut stream) => stream.write(buf) + } + } + + #[inline(always)] + fn flush(&mut self) -> io::Result<()> { + match *self { + Http(ref mut stream) => stream.flush(), + Local(ref mut stream) => stream.flush(), + #[cfg(feature = "tls")] Https(ref mut stream) => stream.flush() + } + } +} + +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(), + Local(_) => 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), + Local(_) => 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), + Local(_) => 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), + Local(_) => Ok(()), + } + } +} diff --git a/lib/src/data/test_data.rs b/lib/src/data/test_data.rs deleted file mode 100644 index 5970108b..00000000 --- a/lib/src/data/test_data.rs +++ /dev/null @@ -1,75 +0,0 @@ -use std::io::{self, Read, BufRead, Write, Cursor, BufReader}; -use std::path::Path; -use std::fs::File; - -use http::hyper::h1::HttpReader; -use http::hyper::net::NetworkStream; -use http::hyper::buffer; - -pub type BodyReader<'a, 'b> = - self::HttpReader<&'a mut self::buffer::BufReader<&'b mut NetworkStream>>; - -const PEEK_BYTES: usize = 4096; - -pub struct DataStream { - stream: BufReader>>, -} - -impl Read for DataStream { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - self.stream.read(buf) - } -} - -impl BufRead for DataStream { - fn fill_buf(&mut self) -> io::Result<&[u8]> { - self.stream.fill_buf() - } - - fn consume(&mut self, amt: usize) { - self.stream.consume(amt) - } -} - -pub struct Data { - data: Vec, -} - -impl Data { - pub fn open(self) -> DataStream { - DataStream { stream: BufReader::new(Cursor::new(self.data)) } - } - - #[inline] - pub fn peek(&self) -> &[u8] { - &self.data[..::std::cmp::min(PEEK_BYTES, self.data.len())] - } - - #[inline] - pub fn peek_complete(&self) -> bool { - self.data.len() <= PEEK_BYTES - } - - #[inline] - pub fn stream_to(self, writer: &mut W) -> io::Result { - io::copy(&mut self.open(), writer) - } - - #[inline] - pub fn stream_to_file>(self, path: P) -> io::Result { - io::copy(&mut self.open(), &mut File::create(path)?) - } - - pub(crate) fn from_hyp(mut h_body: BodyReader) -> Result { - let mut vec = Vec::new(); - if let Err(_) = io::copy(&mut h_body, &mut vec) { - return Err("Reading from body failed."); - }; - - Ok(Data::new(vec)) - } - - pub(crate) fn new(data: Vec) -> Data { - Data { data: data } - } -} diff --git a/lib/src/fairing/mod.rs b/lib/src/fairing/mod.rs index e2069a65..dd176d49 100644 --- a/lib/src/fairing/mod.rs +++ b/lib/src/fairing/mod.rs @@ -173,9 +173,17 @@ impl Fairings { } } + fn num_attached(&self) -> usize { + self.launch.len() + self.request.len() + self.response.len() + } + pub fn pretty_print_counts(&self) { use term_painter::ToStyle; - use term_painter::Color::White; + use term_painter::Color::{White, Magenta}; + + if self.num_attached() > 0 { + info!("📡 {}:", Magenta.paint("Fairings")); + } if !self.launch.is_empty() { info_!("{} launch", White.paint(self.launch.len())); diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 318d1ad6..9221c04e 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -117,7 +117,7 @@ extern crate smallvec; #[cfg(test)] #[macro_use] extern crate lazy_static; #[doc(hidden)] #[macro_use] pub mod logger; -#[cfg(any(test, feature = "testing"))] pub mod testing; +pub mod testing; pub mod http; pub mod request; pub mod response; diff --git a/lib/src/rocket.rs b/lib/src/rocket.rs index 4940e665..754cf213 100644 --- a/lib/src/rocket.rs +++ b/lib/src/rocket.rs @@ -645,7 +645,6 @@ impl Rocket { return LaunchError::from(LaunchErrorKind::Collision); } - info!("📡 {}:", Magenta.paint("Fairings")); self.fairings.pretty_print_counts(); let full_addr = format!("{}:{}", self.config.address, self.config.port); diff --git a/lib/src/testing.rs b/lib/src/testing.rs index 733d00ff..46445aa4 100644 --- a/lib/src/testing.rs +++ b/lib/src/testing.rs @@ -1,33 +1,5 @@ //! A tiny module for testing Rocket applications. //! -//! # Enabling -//! -//! The `testing` module is only available when Rocket is compiled with the -//! `testing` feature flag. The suggested way to enable the `testing` module is -//! through Cargo's `[dev-dependencies]` feature which allows features (and -//! other dependencies) to be enabled exclusively when testing/benchmarking your -//! application. -//! -//! To compile Rocket with the `testing` feature for testing/benchmarking, add -//! the following to your `Cargo.toml`: -//! -//! ```toml -//! [dev-dependencies] -//! rocket = { version = "*", features = ["testing"] } -//! ``` -//! -//! Then, in your testing module, `use` the testing types. This typically looks -//! as follows: -//! -//! ```rust,ignore -//! #[cfg(test)] -//! mod test { -//! use super::rocket; -//! use rocket::testing::MockRequest; -//! use rocket::http::Method::*; -//! } -//! ``` -//! //! # Usage //! //! The testing methadology is simple: @@ -51,9 +23,10 @@ //! builds a request for submitting a login form with three fields: //! //! ```rust -//! # use rocket::http::Method::*; -//! # use rocket::testing::MockRequest; -//! # use rocket::http::ContentType; +//! use rocket::http::Method::*; +//! use rocket::http::ContentType; +//! use rocket::testing::MockRequest; +//! //! let (username, password, age) = ("user", "password", 32); //! MockRequest::new(Post, "/login") //! .header(ContentType::Form) @@ -119,7 +92,7 @@ impl<'r> MockRequest<'r> { pub fn new>(method: Method, uri: S) -> Self { MockRequest { request: Request::new(method, uri.as_ref().to_string()), - data: Data::new(vec![]) + data: Data::local(vec![]) } } @@ -222,7 +195,7 @@ impl<'r> MockRequest<'r> { /// ``` #[inline] pub fn body>(mut self, body: S) -> Self { - self.data = Data::new(body.as_ref().into()); + self.data = Data::local(body.as_ref().into()); self } @@ -261,7 +234,7 @@ impl<'r> MockRequest<'r> { /// # } /// ``` pub fn dispatch_with<'s>(&'s mut self, rocket: &'r Rocket) -> Response<'s> { - let data = ::std::mem::replace(&mut self.data, Data::new(vec![])); + let data = ::std::mem::replace(&mut self.data, Data::local(vec![])); rocket.dispatch(&mut self.request, data) } } diff --git a/lib/tests/form_method-issue-45.rs b/lib/tests/form_method-issue-45.rs index 0100dfae..a271842a 100644 --- a/lib/tests/form_method-issue-45.rs +++ b/lib/tests/form_method-issue-45.rs @@ -16,7 +16,6 @@ fn bug(form_data: Form) -> &'static str { "OK" } -#[cfg(feature = "testing")] mod tests { use super::*; use rocket::testing::MockRequest; diff --git a/lib/tests/form_value_decoding-issue-82.rs b/lib/tests/form_value_decoding-issue-82.rs index 32e5260b..904d4cca 100644 --- a/lib/tests/form_value_decoding-issue-82.rs +++ b/lib/tests/form_value_decoding-issue-82.rs @@ -15,7 +15,6 @@ fn bug(form_data: Form) -> String { form_data.into_inner().form_data } -#[cfg(feature = "testing")] mod tests { use super::*; use rocket::testing::MockRequest; diff --git a/lib/tests/head_handling.rs b/lib/tests/head_handling.rs index 0c86c067..c4cc52aa 100644 --- a/lib/tests/head_handling.rs +++ b/lib/tests/head_handling.rs @@ -20,7 +20,6 @@ fn other() -> content::JSON<()> { content::JSON(()) } -#[cfg(feature = "testing")] mod tests { use super::*; diff --git a/lib/tests/limits.rs b/lib/tests/limits.rs index 9070c42f..c42bf6f0 100644 --- a/lib/tests/limits.rs +++ b/lib/tests/limits.rs @@ -15,7 +15,6 @@ fn index(form: Form) -> String { form.into_inner().value } -#[cfg(feature = "testing")] mod tests { use rocket; use rocket::config::{Environment, Config, Limits}; diff --git a/lib/tests/precise-content-type-matching.rs b/lib/tests/precise-content-type-matching.rs index 8a717555..18e4538c 100644 --- a/lib/tests/precise-content-type-matching.rs +++ b/lib/tests/precise-content-type-matching.rs @@ -23,7 +23,6 @@ fn specified_html() -> &'static str { "specified_html" } -#[cfg(feature = "testing")] mod tests { use super::*; diff --git a/lib/tests/query-and-non-query-dont-collide.rs b/lib/tests/query-and-non-query-dont-collide.rs index 45105251..3e1c14a2 100644 --- a/lib/tests/query-and-non-query-dont-collide.rs +++ b/lib/tests/query-and-non-query-dont-collide.rs @@ -18,7 +18,6 @@ fn second() -> &'static str { "no query" } -#[cfg(feature = "testing")] mod tests { use super::*; diff --git a/lib/tests/redirect_from_catcher-issue-113.rs b/lib/tests/redirect_from_catcher-issue-113.rs index c68152a6..03dc5d81 100644 --- a/lib/tests/redirect_from_catcher-issue-113.rs +++ b/lib/tests/redirect_from_catcher-issue-113.rs @@ -10,7 +10,6 @@ fn not_found() -> Redirect { Redirect::to("/") } -#[cfg(feature = "testing")] mod tests { use super::*; use rocket::testing::MockRequest; diff --git a/lib/tests/remote-rewrite.rs b/lib/tests/remote-rewrite.rs index 4ef8b494..c7befbc5 100644 --- a/lib/tests/remote-rewrite.rs +++ b/lib/tests/remote-rewrite.rs @@ -10,7 +10,6 @@ fn get_ip(remote: SocketAddr) -> String { remote.to_string() } -#[cfg(feature = "testing")] mod remote_rewrite_tests { use super::*; use rocket::testing::MockRequest; diff --git a/lib/tests/segments-issues-41-86.rs b/lib/tests/segments-issues-41-86.rs index 834b92e1..34d55ad4 100644 --- a/lib/tests/segments-issues-41-86.rs +++ b/lib/tests/segments-issues-41-86.rs @@ -30,7 +30,6 @@ fn dual(user: String, path: Segments) -> String { user + "/is/" + &path.collect::>().join("/") } -#[cfg(feature = "testing")] mod tests { use super::*; use rocket::testing::MockRequest;