diff --git a/examples/cookies/Cargo.toml b/examples/cookies/Cargo.toml index c83d5a0e..cf32faba 100644 --- a/examples/cookies/Cargo.toml +++ b/examples/cookies/Cargo.toml @@ -6,7 +6,6 @@ workspace = "../../" [dependencies] rocket = { path = "../../lib" } rocket_codegen = { path = "../../codegen" } -lazy_static = "*" [dependencies.rocket_contrib] path = "../../contrib" diff --git a/examples/cookies/src/main.rs b/examples/cookies/src/main.rs index d11a5af4..b45fe625 100644 --- a/examples/cookies/src/main.rs +++ b/examples/cookies/src/main.rs @@ -1,8 +1,6 @@ #![feature(plugin, custom_derive, custom_attribute)] #![plugin(rocket_codegen)] -#[macro_use] -extern crate lazy_static; extern crate rocket_contrib; extern crate rocket; @@ -23,15 +21,16 @@ struct Message { #[post("/submit", data = "")] fn submit(cookies: &Cookies, message: Form) -> Redirect { - cookies.add(Cookie::new("message".into(), message.into_inner().message)); + cookies.add(Cookie::new("message", message.into_inner().message)); Redirect::to("/") } #[get("/")] fn index(cookies: &Cookies) -> Template { + let cookie = cookies.find("message"); let mut context = HashMap::new(); - if let Some(msg) = cookies.find("message").map(|msg| msg.value) { - context.insert("message", msg); + if let Some(ref cookie) = cookie { + context.insert("message", cookie.value()); } Template::render("index", &context) diff --git a/examples/cookies/src/tests.rs b/examples/cookies/src/tests.rs index 4cce2748..78012606 100644 --- a/examples/cookies/src/tests.rs +++ b/examples/cookies/src/tests.rs @@ -20,7 +20,7 @@ fn test_submit() { assert_eq!(location_headers, vec!["/".to_string()]); } -fn test_body(optional_cookie: Option, expected_body: String) { +fn test_body(optional_cookie: Option>, expected_body: String) { let rocket = rocket::ignite().mount("/", routes![super::index]); let mut request = MockRequest::new(Method::Get, "/"); @@ -47,7 +47,7 @@ fn test_index() { context.insert("message", "Hello from Rocket!"); // Test the route with the "message" cookie. - let cookie = Cookie::new("message".into(), "Hello from Rocket!".into()); + let cookie = Cookie::new("message", "Hello from Rocket!"); let template = Template::render("index", &context); test_body(Some(cookie), template.to_string()); } diff --git a/lib/Cargo.toml b/lib/Cargo.toml index e9d2067f..6d94b0f9 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -18,11 +18,16 @@ build = "build.rs" term-painter = "^0.2" log = "^0.3" url = "^1" -hyper = { version = "=0.9.14", default-features = false } +hyper = { version = "0.10", default-features = false } toml = { version = "^0.2", default-features = false } num_cpus = "1" state = "^0.2" -# cookie = "^0.3" +time = "^0.1" + +[dependencies.cookie] +version = "^0.6" +default-features = false +features = ["percent-encode"] [dev-dependencies] lazy_static = "0.2" diff --git a/lib/src/config/mod.rs b/lib/src/config/mod.rs index 516d3261..404d81a4 100644 --- a/lib/src/config/mod.rs +++ b/lib/src/config/mod.rs @@ -36,7 +36,7 @@ //! * **port**: _[integer]_ a port number to listen on //! * examples: `"8000"`, `"80"`, `"4242"` //! * **workers**: _[integer]_ the number of concurrent workers to use -//! * examples: `"12"`, `"1"`, `"4"` +//! * examples: `12`, `1`, `4` //! * **log**: _[string]_ how much information to log; one of `"normal"`, //! `"debug"`, or `"critical"` //! * **session_key**: _[string]_ a 192-bit base64 encoded string (32 diff --git a/lib/src/http/cookies.rs b/lib/src/http/cookies.rs index c917d2fb..02c08162 100644 --- a/lib/src/http/cookies.rs +++ b/lib/src/http/cookies.rs @@ -1,5 +1,17 @@ -use http; +use http::Header; -pub use http::hyper::header::CookiePair as Cookie; +pub use cookie::Cookie; +pub use cookie::CookieJar; +pub use cookie::CookieBuilder; -pub type Cookies = http::hyper::header::CookieJar<'static>; +/// Type alias to a `'static` CookieJar. +/// +/// A `CookieJar` should never be used without a `'static` lifetime. As a +/// result, you should always use this alias. +pub type Cookies = self::CookieJar<'static>; + +impl<'c> From> for Header<'static> { + fn from(cookie: Cookie) -> Header<'static> { + Header::new("Set-Cookie", cookie.encoded().to_string()) + } +} diff --git a/lib/src/http/header.rs b/lib/src/http/header.rs index ae93c21f..b6c138e9 100644 --- a/lib/src/http/header.rs +++ b/lib/src/http/header.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::borrow::{Borrow, Cow}; use std::fmt; -use http::hyper::header as hyper; use http::ascii::{UncasedAscii, UncasedAsciiRef}; /// Simple representation of an HTTP header. @@ -107,13 +106,6 @@ impl<'h> fmt::Display for Header<'h> { } } -impl From for Header<'static> where T: hyper::Header + hyper::HeaderFormat { - fn from(hyper_header: T) -> Header<'static> { - let formatter = hyper::HeaderFormatter(&hyper_header); - Header::new(T::header_name(), format!("{}", formatter)) - } -} - /// A collection of headers, mapping a header name to its many ordered values. #[derive(Clone, Debug, PartialEq, Default)] pub struct HeaderMap<'h> { @@ -345,16 +337,13 @@ impl<'h> HeaderMap<'h> { /// /// ```rust /// use rocket::http::{Cookie, HeaderMap}; - /// use rocket::http::hyper::header; /// /// let mut map = HeaderMap::new(); /// - /// let cookie = vec![Cookie::new("a".to_string(), "b".to_string())]; - /// map.add(header::SetCookie(cookie)); + /// map.add(Cookie::new("a", "b")); /// assert_eq!(map.get("Set-Cookie").count(), 1); /// - /// let cookie = vec![Cookie::new("c".to_string(), "d".to_string())]; - /// map.add(header::SetCookie(cookie)); + /// map.add(Cookie::new("c", "d")); /// assert_eq!(map.get("Set-Cookie").count(), 2); /// ``` #[inline(always)] diff --git a/lib/src/http/hyper.rs b/lib/src/http/hyper.rs index 61a904c0..62ada00d 100644 --- a/lib/src/http/hyper.rs +++ b/lib/src/http/hyper.rs @@ -1,25 +1,79 @@ //! Re-exported hyper HTTP library types. //! -//! ## Hyper -//! -//! All types that are re-exported from Hyper resides inside of this module. +//! All types that are re-exported from Hyper reside inside of this module. //! These types will, with certainty, be removed with time, but they reside here //! while necessary. -pub use hyper::server::Request as Request; -pub use hyper::server::Response as Response; -pub use hyper::server::Server as Server; -pub use hyper::server::Handler as Handler; +#[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::uri::RequestUri; +#[doc(hidden)] pub use hyper::http::h1; +#[doc(hidden)] pub use hyper::buffer; -pub use hyper::header; pub use hyper::mime; -pub use hyper::net; - -pub use hyper::method::Method; -pub use hyper::status::StatusCode; -pub use hyper::uri::RequestUri; -pub use hyper::http::h1; -pub use hyper::buffer; /// Type alias to `hyper::Response<'a, hyper::net::Fresh>`. -pub type FreshResponse<'a> = self::Response<'a, self::net::Fresh>; +#[doc(hidden)] pub type FreshResponse<'a> = self::Response<'a, self::net::Fresh>; + +/// Reexported Hyper header types. +pub mod header { + use http::Header; + + use hyper::header::HeaderFormatter; + 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> { + let formatter = HeaderFormatter(&header); + Header::new($name::header_name(), format!("{}", formatter)) + } + } + )*) + } + + 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 + } +} diff --git a/lib/src/http/mod.rs b/lib/src/http/mod.rs index 4ccafd0c..7e5f3471 100644 --- a/lib/src/http/mod.rs +++ b/lib/src/http/mod.rs @@ -23,4 +23,4 @@ pub use self::content_type::ContentType; pub use self::status::{Status, StatusClass}; pub use self::header::{Header, HeaderMap}; -pub use self::cookies::{Cookie, Cookies}; +pub use self::cookies::{Cookie, Cookies, CookieJar, CookieBuilder}; diff --git a/lib/src/lib.rs b/lib/src/lib.rs index 78fc8d04..8d22dd78 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -97,6 +97,8 @@ extern crate url; extern crate toml; extern crate num_cpus; extern crate state; +extern crate cookie; +extern crate time; #[cfg(test)] #[macro_use] extern crate lazy_static; diff --git a/lib/src/request/request.rs b/lib/src/request/request.rs index 786f4e58..a1e21671 100644 --- a/lib/src/request/request.rs +++ b/lib/src/request/request.rs @@ -12,7 +12,7 @@ use super::{FromParam, FromSegments}; use router::Route; use http::uri::{URI, Segments}; -use http::{Method, ContentType, Header, HeaderMap, Cookies}; +use http::{Method, ContentType, Header, HeaderMap, Cookie, Cookies}; use http::hyper; @@ -247,7 +247,8 @@ impl<'r> Request<'r> { /// use rocket::http::{Cookie, Method, ContentType}; /// /// let mut request = Request::new(Method::Get, "/uri"); - /// request.cookies().add(Cookie::new("key".into(), "val".into())) + /// request.cookies().add(Cookie::new("key", "val")); + /// request.cookies().add(Cookie::new("ans", format!("life: {}", 38 + 4))); /// ``` #[inline(always)] pub fn cookies(&self) -> &Cookies { @@ -255,18 +256,6 @@ impl<'r> Request<'r> { } /// Replace all of the cookies in `self` with `cookies`. - /// - /// # Example - /// - /// Add a new cookie to a request's cookies: - /// - /// ```rust - /// use rocket::Request; - /// use rocket::http::{Cookie, Method, ContentType}; - /// - /// let mut request = Request::new(Method::Get, "/uri"); - /// request.cookies().add(Cookie::new("key".into(), "val".into())) - /// ``` #[doc(hidden)] #[inline(always)] pub fn set_cookies(&mut self, cookies: Cookies) { @@ -430,8 +419,25 @@ impl<'r> Request<'r> { let mut request = Request::new(method, uri); // Set the request cookies, if they exist. TODO: Use session key. - if let Some(cookies) = h_headers.get::() { - request.set_cookies(cookies.to_cookie_jar(&[])); + if let Some(cookie_headers) = h_headers.get_raw("Cookie") { + let mut cookies = Cookies::new(&[]); + for header in cookie_headers { + let raw_str = match ::std::str::from_utf8(header) { + Ok(string) => string, + Err(_) => continue + }; + + for cookie_str in raw_str.split(";") { + let cookie = match Cookie::parse_encoded(cookie_str.to_string()) { + Ok(cookie) => cookie, + Err(_) => continue + }; + + cookies.add_original(cookie); + } + } + + request.set_cookies(cookies); } // Set the rest of the headers. diff --git a/lib/src/response/flash.rs b/lib/src/response/flash.rs index 89966947..da3b641f 100644 --- a/lib/src/response/flash.rs +++ b/lib/src/response/flash.rs @@ -1,10 +1,11 @@ use std::convert::AsRef; +use time::Duration; + use outcome::IntoOutcome; use response::{Response, Responder}; use request::{self, Request, FromRequest}; -use http::hyper::header; -use http::Status; +use http::{Status, Cookie}; // The name of the actual flash cookie. const FLASH_COOKIE_NAME: &'static str = "_flash"; @@ -161,12 +162,12 @@ impl<'r, R: Responder<'r>> Flash { Flash::new(responder, "error", msg) } - fn cookie_pair(&self) -> header::CookiePair { + fn cookie(&self) -> Cookie<'static> { let content = format!("{}{}{}", self.name.len(), self.name, self.message); - let mut pair = header::CookiePair::new(FLASH_COOKIE_NAME.to_string(), content); - pair.path = Some("/".to_string()); - pair.max_age = Some(300); - pair + Cookie::build(FLASH_COOKIE_NAME, content) + .max_age(Duration::minutes(5)) + .path("/") + .finish() } } @@ -177,9 +178,9 @@ impl<'r, R: Responder<'r>> Flash { impl<'r, R: Responder<'r>> Responder<'r> for Flash { fn respond(self) -> Result, Status> { trace_!("Flash: setting message: {}:{}", self.name, self.message); - let cookie = vec![self.cookie_pair()]; + let cookie = self.cookie(); Response::build_from(self.responder.respond()?) - .header_adjoin(header::SetCookie(cookie)) + .header_adjoin(cookie) .ok() } } @@ -221,7 +222,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Flash<()> { request.cookies().remove(FLASH_COOKIE_NAME); // Parse the flash. - let content = cookie.pair().1; + let content = cookie.value(); let (len_str, rest) = match content.find(|c: char| !c.is_digit(10)) { Some(i) => (&content[..i], &content[i..]), None => (content, ""), diff --git a/lib/src/rocket.rs b/lib/src/rocket.rs index 9d593121..85d014a1 100644 --- a/lib/src/rocket.rs +++ b/lib/src/rocket.rs @@ -20,7 +20,7 @@ use catcher::{self, Catcher}; use outcome::Outcome; use error::Error; -use http::{Method, Status}; +use http::{Method, Status, Header}; use http::hyper::{self, header}; use http::uri::URI; @@ -79,7 +79,7 @@ impl Rocket { fn issue_response(&self, mut response: Response, hyp_res: hyper::FreshResponse) { // Add the 'rocket' server header, and write out the response. // TODO: If removing Hyper, write out `Date` header too. - response.set_header(header::Server("rocket".to_string())); + response.set_header(Header::new("Server", "rocket")); match self.write_response(response, hyp_res) { Ok(_) => info_!("{}", Green.paint("Response succeeded.")), @@ -192,9 +192,8 @@ impl Rocket { match self.route(request, data) { Outcome::Success(mut response) => { // A user's route responded! - let cookie_delta = request.cookies().delta(); - if !cookie_delta.is_empty() { - response.adjoin_header(header::SetCookie(cookie_delta)); + for cookie in request.cookies().delta() { + response.adjoin_header(cookie); } response diff --git a/lib/src/testing.rs b/lib/src/testing.rs index cb5a6f83..87e52bd9 100644 --- a/lib/src/testing.rs +++ b/lib/src/testing.rs @@ -195,10 +195,11 @@ impl<'r> MockRequest<'r> { /// use rocket::http::Cookie; /// /// let req = MockRequest::new(Get, "/") - /// .cookie(Cookie::new("user_id".into(), "12".into())); + /// .cookie(Cookie::new("username", "sb")) + /// .cookie(Cookie::new("user_id", format!("{}", 12))); /// ``` #[inline] - pub fn cookie(self, cookie: Cookie) -> Self { + pub fn cookie(self, cookie: Cookie<'static>) -> Self { self.request.cookies().add(cookie); self }