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.
This commit is contained in:
Sergio Benitez 2017-04-20 20:30:41 -07:00
parent ac0c78a0cd
commit 1e5a1b8940
51 changed files with 155 additions and 322 deletions

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -9,6 +9,3 @@ rocket_codegen = { path = "../../codegen" }
serde = "0.9" serde = "0.9"
serde_json = "0.9" serde_json = "0.9"
serde_derive = "0.9" serde_derive = "0.9"
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -11,6 +11,3 @@ rocket_codegen = { path = "../../codegen" }
path = "../../contrib" path = "../../contrib"
default-features = false default-features = false
features = ["handlebars_templates"] features = ["handlebars_templates"]
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -14,6 +14,3 @@ serde_json = "0.9"
path = "../../contrib" path = "../../contrib"
default-features = false default-features = false
features = ["handlebars_templates"] features = ["handlebars_templates"]
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib", features = ["tls"] } rocket = { path = "../../lib", features = ["tls"] }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -14,6 +14,3 @@ serde_derive = "0.9"
path = "../../contrib" path = "../../contrib"
default-features = false default-features = false
features = ["json"] features = ["json"]
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -7,6 +7,3 @@ workspace = "../.."
crossbeam = "*" crossbeam = "*"
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -5,6 +5,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -13,6 +13,3 @@ serde_derive = "0.9"
path = "../../contrib" path = "../../contrib"
default-features = false default-features = false
features = ["msgpack"] features = ["msgpack"]
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -7,6 +7,3 @@ workspace = "../../"
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
rand = "0.3" rand = "0.3"
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -7,6 +7,3 @@ workspace = "../../"
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
rusqlite = "0.10" rusqlite = "0.10"
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -11,6 +11,3 @@ rocket_codegen = { path = "../../codegen" }
path = "../../contrib" path = "../../contrib"
default-features = false default-features = false
features = ["handlebars_templates"] features = ["handlebars_templates"]
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -6,6 +6,3 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -13,6 +13,3 @@ lazy_static = "^0.2"
default-features = false default-features = false
path = "../../contrib" path = "../../contrib"
features = ["uuid"] features = ["uuid"]
[dev-dependencies]
rocket = { path = "../../lib", features = ["testing"] }

View File

@ -15,7 +15,6 @@ build = "build.rs"
categories = ["web-programming::http-server"] categories = ["web-programming::http-server"]
[features] [features]
testing = []
tls = ["rustls", "hyper-rustls"] tls = ["rustls", "hyper-rustls"]
[dependencies] [dependencies]

View File

@ -18,7 +18,6 @@ fn rocket() -> rocket::Rocket {
.mount("/", routes![get, post]) .mount("/", routes![get, post])
} }
#[cfg(feature = "testing")]
mod benches { mod benches {
extern crate test; extern crate test;

View File

@ -31,7 +31,6 @@ fn rocket() -> rocket::Rocket {
.mount("/", routes![post, post2, post3]) .mount("/", routes![post, post2, post3])
} }
#[cfg(feature = "testing")]
mod benches { mod benches {
extern crate test; extern crate test;

View File

@ -34,7 +34,6 @@ fn rocket() -> rocket::Rocket {
index_b, index_c, index_dyn_a]) index_b, index_c, index_dyn_a])
} }
#[cfg(feature = "testing")]
mod benches { mod benches {
extern crate test; extern crate test;

View File

@ -6,8 +6,9 @@ use std::mem::transmute;
#[cfg(feature = "tls")] use hyper_rustls::WrappedStream; #[cfg(feature = "tls")] use hyper_rustls::WrappedStream;
use super::data_stream::{DataStream, StreamReader, kill_stream};
use super::net_stream::NetStream;
use ext::ReadExt; use ext::ReadExt;
use super::data_stream::{DataStream, HyperNetStream, StreamReader, kill_stream};
use http::hyper::h1::HttpReader; use http::hyper::h1::HttpReader;
use http::hyper::buffer; use http::hyper::buffer;
@ -17,6 +18,9 @@ use http::hyper::net::{HttpStream, NetworkStream};
pub type BodyReader<'a, 'b> = pub type BodyReader<'a, 'b> =
self::HttpReader<&'a mut self::buffer::BufReader<&'b mut NetworkStream>>; 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. /// 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. /// 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(); let net_stream = h_body.get_ref().get_ref();
#[cfg(feature = "tls")] #[cfg(feature = "tls")]
fn concrete_stream(stream: &&mut NetworkStream) -> Option<HyperNetStream> { fn concrete_stream(stream: &&mut NetworkStream) -> Option<NetStream> {
stream.downcast_ref::<HttpStream>() stream.downcast_ref::<HttpStream>()
.map(|s| HyperNetStream::Http(s.clone())) .map(|s| NetStream::Http(s.clone()))
.or_else(|| { .or_else(|| {
stream.downcast_ref::<WrappedStream>() stream.downcast_ref::<WrappedStream>()
.map(|s| HyperNetStream::Https(s.clone())) .map(|s| NetStream::Https(s.clone()))
}) })
} }
#[cfg(not(feature = "tls"))] #[cfg(not(feature = "tls"))]
fn concrete_stream(stream: &&mut NetworkStream) -> Option<HyperNetStream> { fn concrete_stream(stream: &&mut NetworkStream) -> Option<NetStream> {
stream.downcast_ref::<HttpStream>() stream.downcast_ref::<HttpStream>()
.map(|s| HyperNetStream::Http(s.clone())) .map(|s| NetStream::Http(s.clone()))
} }
// Retrieve the underlying HTTPStream from Hyper. // 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 // 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 // in the buffer is at `pos` and the buffer has `cap` valid bytes. Thus, the
// remainder of the data bytes can be read from `stream`. // 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<u8>, pub(crate) fn new(mut buf: Vec<u8>,
pos: usize, pos: usize,
mut cap: usize, mut cap: usize,
mut stream: StreamReader mut stream: StreamReader
) -> Data { ) -> Data {
// Make sure the buffer is large enough for the bytes we want to peek. // Make sure the buffer is large enough for the bytes we want to peek.
const PEEK_BYTES: usize = 4096;
if buf.len() < PEEK_BYTES { if buf.len() < PEEK_BYTES {
trace_!("Resizing peek buffer from {} to {}.", buf.len(), PEEK_BYTES); trace_!("Resizing peek buffer from {} to {}.", buf.len(), PEEK_BYTES);
buf.resize(PEEK_BYTES, 0); buf.resize(PEEK_BYTES, 0);
@ -203,6 +207,27 @@ impl Data {
capacity: cap, capacity: cap,
} }
} }
/// This creates a `data` object from a local data source `data`.
pub(crate) fn local(mut data: Vec<u8>) -> 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 { impl Drop for Data {

View File

@ -1,80 +1,14 @@
use std::io::{self, BufRead, Read, Cursor, BufReader, Chain, Take}; use std::io::{self, BufRead, Read, Cursor, BufReader, Chain, Take};
use std::net::{SocketAddr, Shutdown}; use std::net::Shutdown;
use std::time::Duration;
#[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; use http::hyper::h1::HttpReader;
pub type StreamReader = HttpReader<HyperNetStream>; pub type StreamReader = HttpReader<NetStream>;
pub type InnerStream = Chain<Take<Cursor<Vec<u8>>>, BufReader<StreamReader>>; pub type InnerStream = Chain<Take<Cursor<Vec<u8>>>, BufReader<StreamReader>>;
#[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<usize> {
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<usize> {
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<SocketAddr> {
with_inner!(self, |mut stream| NetworkStream::peer_addr(stream))
}
#[inline(always)]
fn set_read_timeout(&self, dur: Option<Duration>) -> io::Result<()> {
with_inner!(self, |stream| NetworkStream::set_read_timeout(stream, dur))
}
#[inline(always)]
fn set_write_timeout(&self, dur: Option<Duration>) -> 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. /// Raw data stream of a request body.
/// ///
/// This stream can only be obtained by calling /// 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. /// Instead, it must be used as an opaque `Read` or `BufRead` structure.
pub struct DataStream { pub struct DataStream {
stream: InnerStream, stream: InnerStream,
network: HyperNetStream, network: NetStream,
} }
impl DataStream { impl DataStream {
#[inline(always)] #[inline(always)]
pub(crate) fn new(stream: InnerStream, network: HyperNetStream) -> DataStream { pub(crate) fn new(stream: InnerStream, network: NetStream) -> DataStream {
DataStream { stream, network } DataStream { stream, network }
} }
} }
@ -112,19 +46,18 @@ impl BufRead for DataStream {
} }
} }
// pub fn kill_stream<S: Read>(stream: &mut S, network: &mut HyperNetStream) {
pub fn kill_stream<S: Read, N: NetworkStream>(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. pub fn kill_stream<S: Read, N: NetworkStream>(stream: &mut S, network: &mut N) {
let mut buf = [0]; // Take <= 1k from the stream. If there might be more data, force close.
if let Ok(n) = stream.read(&mut buf) { const FLUSH_LEN: u64 = 1024;
if n > 0 { match io::copy(&mut stream.take(FLUSH_LEN), &mut io::sink()) {
Ok(FLUSH_LEN) | Err(_) => {
warn_!("Data left unread. Force closing network stream."); warn_!("Data left unread. Force closing network stream.");
if let Err(e) = network.close(Shutdown::Both) { if let Err(e) = network.close(Shutdown::Both) {
error_!("Failed to close network stream: {:?}", e); error_!("Failed to close network stream: {:?}", e);
} }
} }
Ok(n) => debug!("flushed {} unread bytes", n)
} }
} }

View File

@ -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;
mod data_stream; mod data_stream;
mod net_stream;
mod from_data;
pub use self::data::Data; pub use self::data::Data;
pub use self::data_stream::DataStream; pub use self::data_stream::DataStream;
}
mod from_data;
pub use self::from_data::{FromData, Outcome}; pub use self::from_data::{FromData, Outcome};
pub use self::items::{Data, DataStream};

View File

@ -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<Vec<u8>>)
}
impl io::Read for NetStream {
#[inline(always)]
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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<usize> {
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<SocketAddr> {
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<Duration>) -> 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<Duration>) -> 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(()),
}
}
}

View File

@ -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<Cursor<Vec<u8>>>,
}
impl Read for DataStream {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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<u8>,
}
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<W: Write>(self, writer: &mut W) -> io::Result<u64> {
io::copy(&mut self.open(), writer)
}
#[inline]
pub fn stream_to_file<P: AsRef<Path>>(self, path: P) -> io::Result<u64> {
io::copy(&mut self.open(), &mut File::create(path)?)
}
pub(crate) fn from_hyp(mut h_body: BodyReader) -> Result<Data, &'static str> {
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<u8>) -> Data {
Data { data: data }
}
}

View File

@ -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) { pub fn pretty_print_counts(&self) {
use term_painter::ToStyle; 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() { if !self.launch.is_empty() {
info_!("{} launch", White.paint(self.launch.len())); info_!("{} launch", White.paint(self.launch.len()));

View File

@ -117,7 +117,7 @@ extern crate smallvec;
#[cfg(test)] #[macro_use] extern crate lazy_static; #[cfg(test)] #[macro_use] extern crate lazy_static;
#[doc(hidden)] #[macro_use] pub mod logger; #[doc(hidden)] #[macro_use] pub mod logger;
#[cfg(any(test, feature = "testing"))] pub mod testing; pub mod testing;
pub mod http; pub mod http;
pub mod request; pub mod request;
pub mod response; pub mod response;

View File

@ -645,7 +645,6 @@ impl Rocket {
return LaunchError::from(LaunchErrorKind::Collision); return LaunchError::from(LaunchErrorKind::Collision);
} }
info!("📡 {}:", Magenta.paint("Fairings"));
self.fairings.pretty_print_counts(); self.fairings.pretty_print_counts();
let full_addr = format!("{}:{}", self.config.address, self.config.port); let full_addr = format!("{}:{}", self.config.address, self.config.port);

View File

@ -1,33 +1,5 @@
//! A tiny module for testing Rocket applications. //! 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 //! # Usage
//! //!
//! The testing methadology is simple: //! The testing methadology is simple:
@ -51,9 +23,10 @@
//! builds a request for submitting a login form with three fields: //! builds a request for submitting a login form with three fields:
//! //!
//! ```rust //! ```rust
//! # use rocket::http::Method::*; //! use rocket::http::Method::*;
//! # use rocket::testing::MockRequest; //! use rocket::http::ContentType;
//! # use rocket::http::ContentType; //! use rocket::testing::MockRequest;
//!
//! let (username, password, age) = ("user", "password", 32); //! let (username, password, age) = ("user", "password", 32);
//! MockRequest::new(Post, "/login") //! MockRequest::new(Post, "/login")
//! .header(ContentType::Form) //! .header(ContentType::Form)
@ -119,7 +92,7 @@ impl<'r> MockRequest<'r> {
pub fn new<S: AsRef<str>>(method: Method, uri: S) -> Self { pub fn new<S: AsRef<str>>(method: Method, uri: S) -> Self {
MockRequest { MockRequest {
request: Request::new(method, uri.as_ref().to_string()), 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] #[inline]
pub fn body<S: AsRef<[u8]>>(mut self, body: S) -> Self { pub fn body<S: AsRef<[u8]>>(mut self, body: S) -> Self {
self.data = Data::new(body.as_ref().into()); self.data = Data::local(body.as_ref().into());
self self
} }
@ -261,7 +234,7 @@ impl<'r> MockRequest<'r> {
/// # } /// # }
/// ``` /// ```
pub fn dispatch_with<'s>(&'s mut self, rocket: &'r Rocket) -> Response<'s> { 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) rocket.dispatch(&mut self.request, data)
} }
} }

View File

@ -16,7 +16,6 @@ fn bug(form_data: Form<FormData>) -> &'static str {
"OK" "OK"
} }
#[cfg(feature = "testing")]
mod tests { mod tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::testing::MockRequest;

View File

@ -15,7 +15,6 @@ fn bug(form_data: Form<FormData>) -> String {
form_data.into_inner().form_data form_data.into_inner().form_data
} }
#[cfg(feature = "testing")]
mod tests { mod tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::testing::MockRequest;

View File

@ -20,7 +20,6 @@ fn other() -> content::JSON<()> {
content::JSON(()) content::JSON(())
} }
#[cfg(feature = "testing")]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -15,7 +15,6 @@ fn index(form: Form<Simple>) -> String {
form.into_inner().value form.into_inner().value
} }
#[cfg(feature = "testing")]
mod tests { mod tests {
use rocket; use rocket;
use rocket::config::{Environment, Config, Limits}; use rocket::config::{Environment, Config, Limits};

View File

@ -23,7 +23,6 @@ fn specified_html() -> &'static str {
"specified_html" "specified_html"
} }
#[cfg(feature = "testing")]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -18,7 +18,6 @@ fn second() -> &'static str {
"no query" "no query"
} }
#[cfg(feature = "testing")]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -10,7 +10,6 @@ fn not_found() -> Redirect {
Redirect::to("/") Redirect::to("/")
} }
#[cfg(feature = "testing")]
mod tests { mod tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::testing::MockRequest;

View File

@ -10,7 +10,6 @@ fn get_ip(remote: SocketAddr) -> String {
remote.to_string() remote.to_string()
} }
#[cfg(feature = "testing")]
mod remote_rewrite_tests { mod remote_rewrite_tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::testing::MockRequest;

View File

@ -30,7 +30,6 @@ fn dual(user: String, path: Segments) -> String {
user + "/is/" + &path.collect::<Vec<_>>().join("/") user + "/is/" + &path.collect::<Vec<_>>().join("/")
} }
#[cfg(feature = "testing")]
mod tests { mod tests {
use super::*; use super::*;
use rocket::testing::MockRequest; use rocket::testing::MockRequest;