From a2e99985b0ab620170a4ff0983950757ca28c63a Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Mon, 21 Nov 2016 00:45:44 -0800 Subject: [PATCH] Fix data buffer indexing bug. Add from_request example. --- lib/src/data/data.rs | 47 +++++++++++++++++----------- lib/src/request/from_request.rs | 54 ++++++++++++++++++++++++++++++++- 2 files changed, 83 insertions(+), 18 deletions(-) diff --git a/lib/src/data/data.rs b/lib/src/data/data.rs index a9c83b53..3785ebd8 100644 --- a/lib/src/data/data.rs +++ b/lib/src/data/data.rs @@ -138,6 +138,9 @@ impl Data { io::copy(&mut self.open(), &mut File::create(path)?) } + // 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`. #[doc(hidden)] pub fn new(mut buf: Vec, pos: usize, @@ -151,28 +154,38 @@ impl Data { buf.resize(PEEK_BYTES, 0); } + // We want to fill the buffer with as many bytes as possible. We also + // want to record if we reach the EOF while filling the buffer. The + // buffer already has `cap` bytes. We read up to buf.len() - 1 bytes, + // and then we try reading 1 more to see if we've reached the EOF. trace!("Init buffer cap: {}", cap); let buf_len = buf.len(); - let eof = match stream.read(&mut buf[cap..(buf_len - 1)]) { - Ok(n) if n == 0 => true, - Ok(n) => { - trace!("Filled peek buf with {} bytes.", n); - cap += n; - match stream.read(&mut buf[cap..(cap + 1)]) { - Ok(n) => { - cap += n; - n == 0 - } - Err(e) => { - error_!("Failed to check stream EOF status: {:?}", e); - false + let eof = if cap < buf_len { + // We have room to read into the buffer. Let's do it. + match stream.read(&mut buf[cap..(buf_len - 1)]) { + Ok(0) => true, + Ok(n) => { + trace!("Filled peek buf with {} bytes.", n); + cap += n; + match stream.read(&mut buf[cap..(cap + 1)]) { + Ok(n) => { + cap += n; + n == 0 + } + Err(e) => { + error_!("Failed to check stream EOF status: {:?}", e); + false + } } } + Err(e) => { + error_!("Failed to read into peek buffer: {:?}", e); + false + } } - Err(e) => { - error_!("Failed to read into peek buffer: {:?}", e); - false - } + } else { + // There's no more room in the buffer. Assume there are still bytes. + false }; trace!("Peek buffer size: {}, remaining: {}", buf_len, buf_len - cap); diff --git a/lib/src/request/from_request.rs b/lib/src/request/from_request.rs index a26d746c..bf058cd3 100644 --- a/lib/src/request/from_request.rs +++ b/lib/src/request/from_request.rs @@ -19,7 +19,7 @@ impl IntoOutcome for Result { /// Trait used to derive an object from incoming request metadata. /// -/// An arbitrary number of types that implement this trait can appears as +/// An arbitrary number of types that implement this trait can appear as /// parameters in a route handler, as illustrated below: /// /// ```rust,ignore @@ -62,6 +62,58 @@ impl IntoOutcome for Result { /// If the `Outcome` is `Forward`, the request will be forwarded to the next /// matching request. Note that users can request an `Option` to catch /// `Forward`s. +/// +/// # Example +/// +/// Imagine you're running an authenticated API service that requires that some +/// requests be sent along with a valid API key in a header field. You want to +/// ensure that the handlers corresponding to these requests don't get called +/// unless there is an API key in the request and the key is valid. The +/// following example implements this using an `APIKey` type and a `FromRequest` +/// implementation for that type in the `senstive` handler: +/// +/// ```rust +/// # #![feature(plugin)] +/// # #![plugin(rocket_codegen)] +/// # extern crate rocket; +/// # +/// use rocket::Outcome; +/// use rocket::http::StatusCode; +/// use rocket::request::{self, Request, FromRequest}; +/// +/// struct APIKey(String); +/// +/// /// Returns true if `key` is a valid API key string. +/// fn is_valid(key: &str) -> bool { +/// key == "valid_api_key" +/// } +/// +/// impl<'r> FromRequest<'r> for APIKey { +/// type Error = (); +/// fn from_request(request: &'r Request) -> request::Outcome { +/// if let Some(keys) = request.headers().get_raw("x-api-key") { +/// if keys.len() != 1 { +/// return Outcome::Failure((StatusCode::BadRequest, ())); +/// } +/// +/// if let Ok(key) = String::from_utf8(keys[0].clone()) { +/// if is_valid(&key) { +/// return Outcome::Success(APIKey(key)); +/// } +/// } +/// } +/// +/// Outcome::Forward(()) +/// } +/// } +/// +/// #[get("/sensitive")] +/// fn sensitive(key: APIKey) -> &'static str { +/// "Sensitive data." +/// } +/// +/// # fn main() { } +/// ``` pub trait FromRequest<'r>: Sized { /// The associated error to be returned when derivation fails. type Error: Debug;