Update to Hyper 0.10. Use cookie crate directly.

A few interesting notes on this breakage:

  * `Cookie` how has a lifetime. It should be `'static'` everywhere.
  * The `SetCookie` header is no longer reexported.
  * Instead, `Cookie` implements `Into<Header>` for Set-Cookie.
This commit is contained in:
Sergio Benitez 2017-01-26 23:08:15 -08:00
parent 8fd19cce4f
commit 06a7317fd9
14 changed files with 144 additions and 77 deletions

View File

@ -6,7 +6,6 @@ workspace = "../../"
[dependencies] [dependencies]
rocket = { path = "../../lib" } rocket = { path = "../../lib" }
rocket_codegen = { path = "../../codegen" } rocket_codegen = { path = "../../codegen" }
lazy_static = "*"
[dependencies.rocket_contrib] [dependencies.rocket_contrib]
path = "../../contrib" path = "../../contrib"

View File

@ -1,8 +1,6 @@
#![feature(plugin, custom_derive, custom_attribute)] #![feature(plugin, custom_derive, custom_attribute)]
#![plugin(rocket_codegen)] #![plugin(rocket_codegen)]
#[macro_use]
extern crate lazy_static;
extern crate rocket_contrib; extern crate rocket_contrib;
extern crate rocket; extern crate rocket;
@ -23,15 +21,16 @@ struct Message {
#[post("/submit", data = "<message>")] #[post("/submit", data = "<message>")]
fn submit(cookies: &Cookies, message: Form<Message>) -> Redirect { fn submit(cookies: &Cookies, message: Form<Message>) -> Redirect {
cookies.add(Cookie::new("message".into(), message.into_inner().message)); cookies.add(Cookie::new("message", message.into_inner().message));
Redirect::to("/") Redirect::to("/")
} }
#[get("/")] #[get("/")]
fn index(cookies: &Cookies) -> Template { fn index(cookies: &Cookies) -> Template {
let cookie = cookies.find("message");
let mut context = HashMap::new(); let mut context = HashMap::new();
if let Some(msg) = cookies.find("message").map(|msg| msg.value) { if let Some(ref cookie) = cookie {
context.insert("message", msg); context.insert("message", cookie.value());
} }
Template::render("index", &context) Template::render("index", &context)

View File

@ -20,7 +20,7 @@ fn test_submit() {
assert_eq!(location_headers, vec!["/".to_string()]); assert_eq!(location_headers, vec!["/".to_string()]);
} }
fn test_body(optional_cookie: Option<Cookie>, expected_body: String) { fn test_body(optional_cookie: Option<Cookie<'static>>, expected_body: String) {
let rocket = rocket::ignite().mount("/", routes![super::index]); let rocket = rocket::ignite().mount("/", routes![super::index]);
let mut request = MockRequest::new(Method::Get, "/"); let mut request = MockRequest::new(Method::Get, "/");
@ -47,7 +47,7 @@ fn test_index() {
context.insert("message", "Hello from Rocket!"); context.insert("message", "Hello from Rocket!");
// Test the route with the "message" cookie. // 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); let template = Template::render("index", &context);
test_body(Some(cookie), template.to_string()); test_body(Some(cookie), template.to_string());
} }

View File

@ -18,11 +18,16 @@ build = "build.rs"
term-painter = "^0.2" term-painter = "^0.2"
log = "^0.3" log = "^0.3"
url = "^1" url = "^1"
hyper = { version = "=0.9.14", default-features = false } hyper = { version = "0.10", default-features = false }
toml = { version = "^0.2", default-features = false } toml = { version = "^0.2", default-features = false }
num_cpus = "1" num_cpus = "1"
state = "^0.2" state = "^0.2"
# cookie = "^0.3" time = "^0.1"
[dependencies.cookie]
version = "^0.6"
default-features = false
features = ["percent-encode"]
[dev-dependencies] [dev-dependencies]
lazy_static = "0.2" lazy_static = "0.2"

View File

@ -36,7 +36,7 @@
//! * **port**: _[integer]_ a port number to listen on //! * **port**: _[integer]_ a port number to listen on
//! * examples: `"8000"`, `"80"`, `"4242"` //! * examples: `"8000"`, `"80"`, `"4242"`
//! * **workers**: _[integer]_ the number of concurrent workers to use //! * **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"`, //! * **log**: _[string]_ how much information to log; one of `"normal"`,
//! `"debug"`, or `"critical"` //! `"debug"`, or `"critical"`
//! * **session_key**: _[string]_ a 192-bit base64 encoded string (32 //! * **session_key**: _[string]_ a 192-bit base64 encoded string (32

View File

@ -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<Cookie<'c>> for Header<'static> {
fn from(cookie: Cookie) -> Header<'static> {
Header::new("Set-Cookie", cookie.encoded().to_string())
}
}

View File

@ -2,7 +2,6 @@ use std::collections::HashMap;
use std::borrow::{Borrow, Cow}; use std::borrow::{Borrow, Cow};
use std::fmt; use std::fmt;
use http::hyper::header as hyper;
use http::ascii::{UncasedAscii, UncasedAsciiRef}; use http::ascii::{UncasedAscii, UncasedAsciiRef};
/// Simple representation of an HTTP header. /// Simple representation of an HTTP header.
@ -107,13 +106,6 @@ impl<'h> fmt::Display for Header<'h> {
} }
} }
impl<T> From<T> 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. /// A collection of headers, mapping a header name to its many ordered values.
#[derive(Clone, Debug, PartialEq, Default)] #[derive(Clone, Debug, PartialEq, Default)]
pub struct HeaderMap<'h> { pub struct HeaderMap<'h> {
@ -345,16 +337,13 @@ impl<'h> HeaderMap<'h> {
/// ///
/// ```rust /// ```rust
/// use rocket::http::{Cookie, HeaderMap}; /// use rocket::http::{Cookie, HeaderMap};
/// use rocket::http::hyper::header;
/// ///
/// let mut map = HeaderMap::new(); /// let mut map = HeaderMap::new();
/// ///
/// let cookie = vec![Cookie::new("a".to_string(), "b".to_string())]; /// map.add(Cookie::new("a", "b"));
/// map.add(header::SetCookie(cookie));
/// assert_eq!(map.get("Set-Cookie").count(), 1); /// assert_eq!(map.get("Set-Cookie").count(), 1);
/// ///
/// let cookie = vec![Cookie::new("c".to_string(), "d".to_string())]; /// map.add(Cookie::new("c", "d"));
/// map.add(header::SetCookie(cookie));
/// assert_eq!(map.get("Set-Cookie").count(), 2); /// assert_eq!(map.get("Set-Cookie").count(), 2);
/// ``` /// ```
#[inline(always)] #[inline(always)]

View File

@ -1,25 +1,79 @@
//! Re-exported hyper HTTP library types. //! Re-exported hyper HTTP library types.
//! //!
//! ## Hyper //! All types that are re-exported from Hyper reside inside of this module.
//!
//! All types that are re-exported from Hyper resides inside of this module.
//! These types will, with certainty, be removed with time, but they reside here //! These types will, with certainty, be removed with time, but they reside here
//! while necessary. //! while necessary.
pub use hyper::server::Request as Request; #[doc(hidden)] pub use hyper::server::Request as Request;
pub use hyper::server::Response as Response; #[doc(hidden)] pub use hyper::server::Response as Response;
pub use hyper::server::Server as Server; #[doc(hidden)] pub use hyper::server::Server as Server;
pub use hyper::server::Handler as Handler; #[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::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>`. /// 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<self::$name> 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
}
}

View File

@ -23,4 +23,4 @@ pub use self::content_type::ContentType;
pub use self::status::{Status, StatusClass}; pub use self::status::{Status, StatusClass};
pub use self::header::{Header, HeaderMap}; pub use self::header::{Header, HeaderMap};
pub use self::cookies::{Cookie, Cookies}; pub use self::cookies::{Cookie, Cookies, CookieJar, CookieBuilder};

View File

@ -97,6 +97,8 @@ extern crate url;
extern crate toml; extern crate toml;
extern crate num_cpus; extern crate num_cpus;
extern crate state; extern crate state;
extern crate cookie;
extern crate time;
#[cfg(test)] #[macro_use] extern crate lazy_static; #[cfg(test)] #[macro_use] extern crate lazy_static;

View File

@ -12,7 +12,7 @@ use super::{FromParam, FromSegments};
use router::Route; use router::Route;
use http::uri::{URI, Segments}; use http::uri::{URI, Segments};
use http::{Method, ContentType, Header, HeaderMap, Cookies}; use http::{Method, ContentType, Header, HeaderMap, Cookie, Cookies};
use http::hyper; use http::hyper;
@ -247,7 +247,8 @@ impl<'r> Request<'r> {
/// use rocket::http::{Cookie, Method, ContentType}; /// use rocket::http::{Cookie, Method, ContentType};
/// ///
/// let mut request = Request::new(Method::Get, "/uri"); /// 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)] #[inline(always)]
pub fn cookies(&self) -> &Cookies { pub fn cookies(&self) -> &Cookies {
@ -255,18 +256,6 @@ impl<'r> Request<'r> {
} }
/// Replace all of the cookies in `self` with `cookies`. /// 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)] #[doc(hidden)]
#[inline(always)] #[inline(always)]
pub fn set_cookies(&mut self, cookies: Cookies) { pub fn set_cookies(&mut self, cookies: Cookies) {
@ -430,8 +419,25 @@ impl<'r> Request<'r> {
let mut request = Request::new(method, uri); let mut request = Request::new(method, uri);
// Set the request cookies, if they exist. TODO: Use session key. // Set the request cookies, if they exist. TODO: Use session key.
if let Some(cookies) = h_headers.get::<hyper::header::Cookie>() { if let Some(cookie_headers) = h_headers.get_raw("Cookie") {
request.set_cookies(cookies.to_cookie_jar(&[])); 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. // Set the rest of the headers.

View File

@ -1,10 +1,11 @@
use std::convert::AsRef; use std::convert::AsRef;
use time::Duration;
use outcome::IntoOutcome; use outcome::IntoOutcome;
use response::{Response, Responder}; use response::{Response, Responder};
use request::{self, Request, FromRequest}; use request::{self, Request, FromRequest};
use http::hyper::header; use http::{Status, Cookie};
use http::Status;
// The name of the actual flash cookie. // The name of the actual flash cookie.
const FLASH_COOKIE_NAME: &'static str = "_flash"; const FLASH_COOKIE_NAME: &'static str = "_flash";
@ -161,12 +162,12 @@ impl<'r, R: Responder<'r>> Flash<R> {
Flash::new(responder, "error", msg) 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 content = format!("{}{}{}", self.name.len(), self.name, self.message);
let mut pair = header::CookiePair::new(FLASH_COOKIE_NAME.to_string(), content); Cookie::build(FLASH_COOKIE_NAME, content)
pair.path = Some("/".to_string()); .max_age(Duration::minutes(5))
pair.max_age = Some(300); .path("/")
pair .finish()
} }
} }
@ -177,9 +178,9 @@ impl<'r, R: Responder<'r>> Flash<R> {
impl<'r, R: Responder<'r>> Responder<'r> for Flash<R> { impl<'r, R: Responder<'r>> Responder<'r> for Flash<R> {
fn respond(self) -> Result<Response<'r>, Status> { fn respond(self) -> Result<Response<'r>, Status> {
trace_!("Flash: setting message: {}:{}", self.name, self.message); trace_!("Flash: setting message: {}:{}", self.name, self.message);
let cookie = vec![self.cookie_pair()]; let cookie = self.cookie();
Response::build_from(self.responder.respond()?) Response::build_from(self.responder.respond()?)
.header_adjoin(header::SetCookie(cookie)) .header_adjoin(cookie)
.ok() .ok()
} }
} }
@ -221,7 +222,7 @@ impl<'a, 'r> FromRequest<'a, 'r> for Flash<()> {
request.cookies().remove(FLASH_COOKIE_NAME); request.cookies().remove(FLASH_COOKIE_NAME);
// Parse the flash. // 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)) { let (len_str, rest) = match content.find(|c: char| !c.is_digit(10)) {
Some(i) => (&content[..i], &content[i..]), Some(i) => (&content[..i], &content[i..]),
None => (content, ""), None => (content, ""),

View File

@ -20,7 +20,7 @@ use catcher::{self, Catcher};
use outcome::Outcome; use outcome::Outcome;
use error::Error; use error::Error;
use http::{Method, Status}; use http::{Method, Status, Header};
use http::hyper::{self, header}; use http::hyper::{self, header};
use http::uri::URI; use http::uri::URI;
@ -79,7 +79,7 @@ impl Rocket {
fn issue_response(&self, mut response: Response, hyp_res: hyper::FreshResponse) { fn issue_response(&self, mut response: Response, hyp_res: hyper::FreshResponse) {
// Add the 'rocket' server header, and write out the response. // Add the 'rocket' server header, and write out the response.
// TODO: If removing Hyper, write out `Date` header too. // 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) { match self.write_response(response, hyp_res) {
Ok(_) => info_!("{}", Green.paint("Response succeeded.")), Ok(_) => info_!("{}", Green.paint("Response succeeded.")),
@ -192,9 +192,8 @@ impl Rocket {
match self.route(request, data) { match self.route(request, data) {
Outcome::Success(mut response) => { Outcome::Success(mut response) => {
// A user's route responded! // A user's route responded!
let cookie_delta = request.cookies().delta(); for cookie in request.cookies().delta() {
if !cookie_delta.is_empty() { response.adjoin_header(cookie);
response.adjoin_header(header::SetCookie(cookie_delta));
} }
response response

View File

@ -195,10 +195,11 @@ impl<'r> MockRequest<'r> {
/// use rocket::http::Cookie; /// use rocket::http::Cookie;
/// ///
/// let req = MockRequest::new(Get, "/") /// 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] #[inline]
pub fn cookie(self, cookie: Cookie) -> Self { pub fn cookie(self, cookie: Cookie<'static>) -> Self {
self.request.cookies().add(cookie); self.request.cookies().add(cookie);
self self
} }