Backed out changeset 010032851619 (bug 1371405)

This commit is contained in:
Sebastian Hengst 2017-06-20 21:23:05 +02:00
parent b106efd1e8
commit d4d94c196f
19 changed files with 741 additions and 1811 deletions

View File

@ -17,7 +17,7 @@ dependencies = [
"slog-stdlog 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"slog-stream 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)",
"webdriver 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)",
"webdriver 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
"zip 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -126,7 +126,7 @@ dependencies = [
[[package]]
name = "cookie"
version = "0.9.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"time 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)",
@ -636,11 +636,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "webdriver"
version = "0.27.0"
version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"cookie 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
"cookie 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"hyper 0.10.10 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
"regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
@ -696,7 +696,7 @@ dependencies = [
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
"checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f"
"checksum cookie 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa6d675d62b2f95b56b331b5222a520149a54f23a2d21974dfcc69caf0a9d"
"checksum cookie 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30b3493e12a550c2f96be785088d1da8d93189e7237c8a8d0d871bc9070334c3"
"checksum crossbeam 0.2.10 (registry+https://github.com/rust-lang/crates.io-index)" = "0c5ea215664ca264da8a9d9c3be80d2eaf30923c259d03e870388eb927508f97"
"checksum dbghelp-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "97590ba53bcb8ac28279161ca943a924d1fd4a8fb3fa63302591647c4fc5b850"
"checksum flate2 0.2.19 (registry+https://github.com/rust-lang/crates.io-index)" = "36df0166e856739905cd3d7e0b210fe818592211a008862599845e012d8d304c"
@ -763,7 +763,7 @@ dependencies = [
"checksum uuid 0.1.18 (registry+https://github.com/rust-lang/crates.io-index)" = "78c590b5bd79ed10aad8fb75f078a59d8db445af6c743e55c4a53227fc01c13f"
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum webdriver 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26cd8cf65699e3b8d1a21088ba2180cfe4f5d37a414c994976a34b289799e24d"
"checksum webdriver 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3099729d884692d690796454e8529edf3f0ebd87c87840f9c809df8eabb175ed"
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
"checksum winreg 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e63857fb213f619b4c4fff86b158285c76766aac7e7474967e92fb6dbbfeefe9"

View File

@ -27,7 +27,7 @@ slog-atomic = "0.4"
slog-stdlog = "1"
slog-stream = "1"
uuid = "0.1.18"
webdriver = "0.27.0"
webdriver = "0.26.0"
zip = "0.1"
[[bin]]

View File

@ -1 +1 @@
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"c1e953ee360e77de57f7b02f1b7880bd6a3dc22d1a69e953c2ac2c52cc52d247",".travis.yml":"d2a9bb7c029e8ed0acfb8dc8e786014cfa4f053b6f4c525303d69fd7e28704e9","Cargo.toml":"58c04bac67faab6be97b59de07794082d3939573035d206bfdd097943a5db79c","Cargo.toml.orig":"00a4a17717e88d551432a0fd757ed2461facc4dd63a4d7e2ec46301f29354283","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"d4e2bab975203277cab1daa6560bd918fdc35e50a7a41a97a55eeea06526a441","src/builder.rs":"528640f717f5769e522a9ac066a994c21973ff3a5e9359d087f410233887c83c","src/delta.rs":"510fc3dbf0a70d635d0488c5a5a32a2ba8e1490ce05bee39d944ea8c02189bbc","src/draft.rs":"bd11960db08f4e4368937845fc18b842e474391738e4457a3441df2789c9d320","src/jar.rs":"98237c4a37143e08bcb6e84c5ed69b799a8a08f89a1b83f02c425cc92b089252","src/lib.rs":"c9713205a51c98138cdcf422313fde959d8bcabf483c4803f2ed6c755eb9f7d5","src/parse.rs":"ee46cee7fa445e6545f29eac3eac81e76ec29e9c53e000195af427c7315ee11c","src/secure/key.rs":"734f35ef4b0d6b63174befdcb970f0304ac63f0895871b7c2f267fefdd43b648","src/secure/macros.rs":"83d770e5c4eb7fbd3c3d86973b69042e9e2bb9fafb72a4456598e2ae78638d5f","src/secure/mod.rs":"5d7fecb62295827d474ed1ce6b7628fe93d4a09eb14babfde036d64e8e4a04f8","src/secure/private.rs":"fbe9b8f79acaab9f9698298e7be57d3fcc33ca3fffbd13f576951b16f28cba60","src/secure/signed.rs":"8440c9ce5a0be4e162fb502cd1fbe24572ce00709f5554c45f8bece39637590d"},"package":"a54aa6d675d62b2f95b56b331b5222a520149a54f23a2d21974dfcc69caf0a9d"}
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"c1e953ee360e77de57f7b02f1b7880bd6a3dc22d1a69e953c2ac2c52cc52d247",".travis.yml":"d2a9bb7c029e8ed0acfb8dc8e786014cfa4f053b6f4c525303d69fd7e28704e9","Cargo.toml":"aa5f58e2a4ebafbfb9f46c6b5e725b5203272e28e6cc350fb67f3454d4aef7b1","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"378f5840b258e2779c39418f3f2d7b2ba96f1c7917dd6be0713f88305dbda397","README.md":"e480abd7b525f43f10248c0cb7740daac6832ef6614ccc5c76bc39d0f2b5cd9d","src/builder.rs":"0cb65ced41732bcb282c164309e15ef670fdf6a940614d58c1681db9844f4791","src/jar.rs":"6a1f8cb3600290f19711507185806ba17d13d956ef5b196afa8ee8108d276766","src/lib.rs":"95f3d945fac20870c4d894e4dcc7ec836c27bd7e269d9428d5713ba04979becb","src/parse.rs":"3a11cbd4ae2ba22d057910ce020c7e4db6ac9b2605f072ed7ab1d0c4ac46d803"},"package":"30b3493e12a550c2f96be785088d1da8d93189e7237c8a8d0d871bc9070334c3"}

View File

@ -1,38 +1,23 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g. crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "cookie"
version = "0.9.1"
authors = ["Alex Crichton <alex@alexcrichton.com>", "Sergio Benitez <sb@sergio.bz>"]
description = "Crate for parsing HTTP cookie headers and managing a cookie jar. Supports signed\nand private (encrypted + signed) jars.\n"
documentation = "https://docs.rs/cookie"
version = "0.6.2"
license = "MIT/Apache-2.0"
repository = "https://github.com/alexcrichton/cookie-rs"
[dependencies.url]
version = "1.0"
optional = true
[dependencies.ring]
version = "0.11.0"
optional = true
[dependencies.base64]
version = "0.6.0"
optional = true
[dependencies.time]
version = "0.1"
documentation = "https://docs.rs/cookie"
description = """
Crate for parsing HTTP cookie headers and managing a cookie jar. Supports
encrypted, signed, and permanent cookie chars composed together in a manner
similar to Rails' cookie jar.
"""
[features]
secure = ["openssl", "rustc-serialize"]
percent-encode = ["url"]
secure = ["ring", "base64"]
[dependencies]
time = "0.1"
url = { version = "1.0", optional = true }
openssl = { version = "0.9.0", optional = true }
rustc-serialize = { version = "0.3", optional = true }

View File

@ -1,26 +1,21 @@
# cookie-rs
[![Build Status](https://travis-ci.org/alexcrichton/cookie-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/cookie-rs)
[![Current Crates.io Version](https://img.shields.io/crates/v/cookie.svg)](https://crates.io/crates/cookie)
A library for parsing HTTP cookies and managing cookie jars.
[Documentation](http://docs.rs/cookie)
# Usage
Add the following to your `Cargo.toml`:
A library for parsing HTTP cookies and managing cookie jars
```toml
# Cargo.toml
[dependencies]
cookie = "0.9"
cookie = "0.6"
```
See the [documentation](http://docs.rs/cookie) for detailed usage information.
# License
`cookie-rs` is primarily distributed under the terms of both the MIT license and
the Apache License (Version 2.0), with portions covered by various BSD-like
licenses.
See [LICENSE-APACHE](LICENSE-APACHE), and [LICENSE-MIT](LICENSE-MIT) for
details.
See LICENSE-APACHE, and LICENSE-MIT for details.

View File

@ -2,13 +2,13 @@ use std::borrow::Cow;
use time::{Tm, Duration};
use ::{Cookie, SameSite};
use ::Cookie;
/// Structure that follows the builder pattern for building `Cookie` structs.
///
/// To construct a cookie:
///
/// 1. Call [`Cookie::build`](struct.Cookie.html#method.build) to start building.
/// 1. Call [Cookie::build](struct.Cookie.html#method.build) to start building.
/// 2. Use any of the builder methods to set fields in the cookie.
/// 3. Call [finish](#method.finish) to retrieve the built cookie.
///
@ -31,6 +31,7 @@ use ::{Cookie, SameSite};
/// .finish();
/// # }
/// ```
///
#[derive(Debug, Clone)]
pub struct CookieBuilder {
/// The cookie being built.
@ -181,52 +182,6 @@ impl CookieBuilder {
self
}
/// Sets the `same_site` field in the cookie being built.
///
/// # Example
///
/// ```rust
/// use cookie::{Cookie, SameSite};
///
/// let c = Cookie::build("foo", "bar")
/// .same_site(SameSite::Strict)
/// .finish();
///
/// assert_eq!(c.same_site(), Some(SameSite::Strict));
/// ```
#[inline]
pub fn same_site(mut self, value: SameSite) -> CookieBuilder {
self.cookie.set_same_site(value);
self
}
/// Makes the cookie being built 'permanent' by extending its expiration and
/// max age 20 years into the future.
///
/// # Example
///
/// ```rust
/// # extern crate cookie;
/// extern crate time;
///
/// use cookie::Cookie;
/// use time::Duration;
///
/// # fn main() {
/// let c = Cookie::build("foo", "bar")
/// .permanent()
/// .finish();
///
/// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
/// # assert!(c.expires().is_some());
/// # }
/// ```
#[inline]
pub fn permanent(mut self) -> CookieBuilder {
self.cookie.make_permanent();
self
}
/// Finishes building and returns the built `Cookie`.
///
/// # Example

View File

@ -1,71 +0,0 @@
use std::ops::{Deref, DerefMut};
use std::hash::{Hash, Hasher};
use std::borrow::Borrow;
use Cookie;
/// A `DeltaCookie` is a helper structure used in a cookie jar. It wraps a
/// `Cookie` so that it can be hashed and compared purely by name. It further
/// records whether the wrapped cookie is a "removal" cookie, that is, a cookie
/// that when sent to the client removes the named cookie on the client's
/// machine.
#[derive(Clone, Debug)]
pub struct DeltaCookie {
pub cookie: Cookie<'static>,
pub removed: bool,
}
impl DeltaCookie {
/// Create a new `DeltaCookie` that is being added to a jar.
#[inline]
pub fn added(cookie: Cookie<'static>) -> DeltaCookie {
DeltaCookie {
cookie: cookie,
removed: false,
}
}
/// Create a new `DeltaCookie` that is being removed from a jar. The
/// `cookie` should be a "removal" cookie.
#[inline]
pub fn removed(cookie: Cookie<'static>) -> DeltaCookie {
DeltaCookie {
cookie: cookie,
removed: true,
}
}
}
impl Deref for DeltaCookie {
type Target = Cookie<'static>;
fn deref(&self) -> &Cookie<'static> {
&self.cookie
}
}
impl DerefMut for DeltaCookie {
fn deref_mut(&mut self) -> &mut Cookie<'static> {
&mut self.cookie
}
}
impl PartialEq for DeltaCookie {
fn eq(&self, other: &DeltaCookie) -> bool {
self.name() == other.name()
}
}
impl Eq for DeltaCookie {}
impl Hash for DeltaCookie {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name().hash(state);
}
}
impl Borrow<str> for DeltaCookie {
fn borrow(&self) -> &str {
self.name()
}
}

View File

@ -1,71 +0,0 @@
//! This module contains types that represent cookie properties that are not yet
//! standardized. That is, _draft_ features.
use std::fmt;
/// The `SameSite` cookie attribute.
///
/// A cookie with a `SameSite` attribute is imposed restrictions on when it is
/// sent to the origin server in a cross-site request. If the `SameSite`
/// attribute is "Strict", then the cookie is never sent in cross-site requests.
/// If the `SameSite` attribute is "Lax", the cookie is only sent in cross-site
/// requests with "safe" HTTP methods, i.e, `GET`, `HEAD`, `OPTIONS`, `TRACE`.
///
/// **Note:** This cookie attribute is an HTTP draft! Its meaning and definition
/// are subject to change.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum SameSite {
/// The "Strict" `SameSite` attribute.
Strict,
/// The "Lax" `SameSite` attribute.
Lax
}
impl SameSite {
/// Returns `true` if `self` is `SameSite::Strict` and `false` otherwise.
///
/// # Example
///
/// ```rust
/// use cookie::SameSite;
///
/// let strict = SameSite::Strict;
/// assert!(strict.is_strict());
/// assert!(!strict.is_lax());
/// ```
#[inline]
pub fn is_strict(&self) -> bool {
match *self {
SameSite::Strict => true,
SameSite::Lax => false
}
}
/// Returns `true` if `self` is `SameSite::Lax` and `false` otherwise.
///
/// # Example
///
/// ```rust
/// use cookie::SameSite;
///
/// let lax = SameSite::Lax;
/// assert!(lax.is_lax());
/// assert!(!lax.is_strict());
/// ```
#[inline]
pub fn is_lax(&self) -> bool {
match *self {
SameSite::Strict => false,
SameSite::Lax => true
}
}
}
impl fmt::Display for SameSite {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
SameSite::Strict => write!(f, "Strict"),
SameSite::Lax => write!(f, "Lax")
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
//! HTTP cookie parsing and cookie jar management.
//! HTTP Cookie parsing and Cookie Jar management.
//!
//! This crates provides the [`Cookie`](struct.Cookie.html) type, which directly
//! maps to an HTTP cookie, and the [`CookieJar`](struct.CookieJar.html) type,
//! This crates provides the [Cookie](struct.Cookie.html) type, which directly
//! maps to an HTTP cookie, and the [CookieJar](struct.CookieJar.html) type,
//! which allows for simple management of many cookies as well as encryption and
//! signing of cookies for session management.
//!
@ -10,7 +10,7 @@
//! Add the following to the `[dependencies]` section of your `Cargo.toml`:
//!
//! ```ignore
//! cookie = "0.9"
//! cookie = "0.6"
//! ```
//!
//! Then add the following line to your crate root:
@ -25,18 +25,13 @@
//! features:
//!
//!
//! * **secure** (disabled by default)
//! * **secure** (enabled by default)
//!
//! Enables signed and private (signed + encrypted) cookie jars.
//! Enables signing and encryption of cookies.
//!
//! When this feature is enabled, the
//! [signed](struct.CookieJar.html#method.signed) and
//! [private](struct.CookieJar.html#method.private) method of `CookieJar` and
//! [`SignedJar`](struct.SignedJar.html) and
//! [`PrivateJar`](struct.PrivateJar.html) structures are available. The jars
//! act as "children jars", allowing for easy retrieval and addition of signed
//! and/or encrypted cookies to a cookie jar. When this feature is disabled,
//! none of the types are available.
//! When this feature is enabled, signed and encrypted cookies jars will
//! encrypt and/or sign any cookies added to them. When this feature is
//! disabled, those cookies will be added in plaintext.
//!
//! * **percent-encode** (disabled by default)
//!
@ -44,7 +39,7 @@
//!
//! When this feature is enabled, the
//! [encoded](struct.Cookie.html#method.encoded) and
//! [`parse_encoded`](struct.Cookie.html#method.parse_encoded) methods of
//! [parse_encoded](struct.Cookie.html#method.parse_encoded) methods of
//! `Cookie` become available. The `encoded` method returns a wrapper around a
//! `Cookie` whose `Display` implementation percent-encodes the name and value
//! of the cookie. The `parse_encoded` method percent-decodes the name and
@ -58,20 +53,18 @@
//! features = ["secure", "percent-encode"]
//! ```
#![doc(html_root_url = "https://docs.rs/cookie/0.9")]
#![doc(html_root_url = "https://docs.rs/cookie/0.6")]
#![allow(deprecated)]
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]
#[cfg(feature = "percent-encode")] extern crate url;
#[cfg(feature = "percent-encode")]
extern crate url;
extern crate time;
mod builder;
mod parse;
mod jar;
mod delta;
mod draft;
#[cfg(feature = "secure")] #[macro_use] mod secure;
#[cfg(feature = "secure")] pub use secure::*;
mod parse;
use std::borrow::Cow;
use std::ascii::AsciiExt;
@ -84,9 +77,9 @@ use time::{Tm, Duration};
use parse::parse_cookie;
pub use parse::ParseError;
pub use builder::CookieBuilder;
pub use jar::{CookieJar, Delta, Iter};
pub use draft::*;
pub use jar::CookieJar;
#[derive(Debug, Clone)]
enum CookieStr {
@ -97,6 +90,14 @@ enum CookieStr {
}
impl CookieStr {
/// Whether this string is derived from indexes or not.
fn is_indexed(&self) -> bool {
match *self {
CookieStr::Indexed(..) => true,
CookieStr::Concrete(..) => false,
}
}
/// Retrieves the string `self` corresponds to. If `self` is derived from
/// indexes, the corresponding subslice of `string` is returned. Otherwise,
/// the concrete string is returned.
@ -105,25 +106,13 @@ impl CookieStr {
///
/// Panics if `self` is an indexed string and `string` is None.
fn to_str<'s>(&'s self, string: Option<&'s Cow<str>>) -> &'s str {
match *self {
CookieStr::Indexed(i, j) => {
let s = string.expect("`Some` base string must exist when \
converting indexed str to str! (This is a module invariant.)");
&s[i..j]
},
CookieStr::Concrete(ref cstr) => &*cstr,
if self.is_indexed() && string.is_none() {
panic!("Cannot convert indexed str to str without base string!")
}
}
fn to_raw_str<'s, 'c: 's>(&'s self, string: &'s Cow<'c, str>) -> Option<&'c str> {
match *self {
CookieStr::Indexed(i, j) => {
match *string {
Cow::Borrowed(s) => Some(&s[i..j]),
Cow::Owned(_) => None,
}
},
CookieStr::Concrete(_) => None,
CookieStr::Indexed(i, j) => &string.unwrap()[i..j],
CookieStr::Concrete(ref cstr) => &*cstr,
}
}
}
@ -143,7 +132,7 @@ impl CookieStr {
/// ```
///
/// To construct more elaborate cookies, use the [build](#method.build) method
/// and [`CookieBuilder`](struct.CookieBuilder.html) methods:
/// and [CookieBuilder](struct.CookieBuilder.html) methods:
///
/// ```rust
/// use cookie::Cookie;
@ -176,8 +165,6 @@ pub struct Cookie<'c> {
secure: bool,
/// Whether this cookie was marked httponly.
http_only: bool,
/// The draft `SameSite` attribute.
same_site: Option<SameSite>,
}
impl Cookie<'static> {
@ -205,27 +192,9 @@ impl Cookie<'static> {
path: None,
secure: false,
http_only: false,
same_site: None,
}
}
/// Creates a new `Cookie` with the given name and an empty value.
///
/// # Example
///
/// ```rust
/// use cookie::Cookie;
///
/// let cookie = Cookie::named("name");
/// assert_eq!(cookie.name(), "name");
/// assert!(cookie.value().is_empty());
/// ```
pub fn named<N>(name: N) -> Cookie<'static>
where N: Into<Cow<'static, str>>
{
Cookie::new(name, "")
}
/// Creates a new `CookieBuilder` instance from the given key and value
/// strings.
///
@ -330,7 +299,6 @@ impl<'c> Cookie<'c> {
path: self.path,
secure: self.secure,
http_only: self.http_only,
same_site: self.same_site,
}
}
@ -409,21 +377,6 @@ impl<'c> Cookie<'c> {
self.secure
}
/// Returns the `SameSite` attribute of this cookie if one was specified.
///
/// # Example
///
/// ```
/// use cookie::{Cookie, SameSite};
///
/// let c = Cookie::parse("name=value; SameSite=Lax").unwrap();
/// assert_eq!(c.same_site(), Some(SameSite::Lax));
/// ```
#[inline]
pub fn same_site(&self) -> Option<SameSite> {
self.same_site
}
/// Returns the specified max-age of the cookie if one was specified.
///
/// # Example
@ -577,24 +530,6 @@ impl<'c> Cookie<'c> {
self.secure = value;
}
/// Sets the value of `same_site` in `self` to `value`.
///
/// # Example
///
/// ```
/// use cookie::{Cookie, SameSite};
///
/// let mut c = Cookie::new("name", "value");
/// assert!(c.same_site().is_none());
///
/// c.set_same_site(SameSite::Strict);
/// assert_eq!(c.same_site(), Some(SameSite::Strict));
/// ```
#[inline]
pub fn set_same_site(&mut self, value: SameSite) {
self.same_site = Some(value);
}
/// Sets the value of `max_age` in `self` to `value`.
///
/// # Example
@ -679,34 +614,6 @@ impl<'c> Cookie<'c> {
self.expires = Some(time);
}
/// Makes `self` a "permanent" cookie by extending its expiration and max
/// age 20 years into the future.
///
/// # Example
///
/// ```rust
/// # extern crate cookie;
/// extern crate time;
///
/// use cookie::Cookie;
/// use time::Duration;
///
/// # fn main() {
/// let mut c = Cookie::new("foo", "bar");
/// assert!(c.expires().is_none());
/// assert!(c.max_age().is_none());
///
/// c.make_permanent();
/// assert!(c.expires().is_some());
/// assert_eq!(c.max_age(), Some(Duration::days(365 * 20)));
/// # }
/// ```
pub fn make_permanent(&mut self) {
let twenty_years = Duration::days(365 * 20);
self.set_max_age(twenty_years);
self.set_expires(time::now() + twenty_years);
}
fn fmt_parameters(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.http_only() {
write!(f, "; HttpOnly")?;
@ -716,10 +623,6 @@ impl<'c> Cookie<'c> {
write!(f, "; Secure")?;
}
if let Some(same_site) = self.same_site() {
write!(f, "; SameSite={}", same_site)?;
}
if let Some(path) = self.path() {
write!(f, "; Path={}", path)?;
}
@ -738,133 +641,6 @@ impl<'c> Cookie<'c> {
Ok(())
}
/// Returns the name of `self` as a string slice of the raw string `self`
/// was originally parsed from. If `self` was not originally parsed from a
/// raw string, returns `None`.
///
/// This method differs from [name](#method.name) in that it returns a
/// string with the same lifetime as the originally parsed string. This
/// lifetime may outlive `self`. If a longer lifetime is not required, or
/// you're unsure if you need a longer lifetime, use [name](#method.name).
///
/// # Example
///
/// ```
/// use cookie::Cookie;
///
/// let cookie_string = format!("{}={}", "foo", "bar");
///
/// // `c` will be dropped at the end of the scope, but `name` will live on
/// let name = {
/// let c = Cookie::parse(cookie_string.as_str()).unwrap();
/// c.name_raw()
/// };
///
/// assert_eq!(name, Some("foo"));
/// ```
#[inline]
pub fn name_raw(&self) -> Option<&'c str> {
self.cookie_string.as_ref()
.and_then(|s| self.name.to_raw_str(s))
}
/// Returns the value of `self` as a string slice of the raw string `self`
/// was originally parsed from. If `self` was not originally parsed from a
/// raw string, returns `None`.
///
/// This method differs from [value](#method.value) in that it returns a
/// string with the same lifetime as the originally parsed string. This
/// lifetime may outlive `self`. If a longer lifetime is not required, or
/// you're unsure if you need a longer lifetime, use [value](#method.value).
///
/// # Example
///
/// ```
/// use cookie::Cookie;
///
/// let cookie_string = format!("{}={}", "foo", "bar");
///
/// // `c` will be dropped at the end of the scope, but `value` will live on
/// let value = {
/// let c = Cookie::parse(cookie_string.as_str()).unwrap();
/// c.value_raw()
/// };
///
/// assert_eq!(value, Some("bar"));
/// ```
#[inline]
pub fn value_raw(&self) -> Option<&'c str> {
self.cookie_string.as_ref()
.and_then(|s| self.value.to_raw_str(s))
}
/// Returns the `Path` of `self` as a string slice of the raw string `self`
/// was originally parsed from. If `self` was not originally parsed from a
/// raw string, or if `self` doesn't contain a `Path`, or if the `Path` has
/// changed since parsing, returns `None`.
///
/// This method differs from [path](#method.path) in that it returns a
/// string with the same lifetime as the originally parsed string. This
/// lifetime may outlive `self`. If a longer lifetime is not required, or
/// you're unsure if you need a longer lifetime, use [path](#method.path).
///
/// # Example
///
/// ```
/// use cookie::Cookie;
///
/// let cookie_string = format!("{}={}; Path=/", "foo", "bar");
///
/// // `c` will be dropped at the end of the scope, but `path` will live on
/// let path = {
/// let c = Cookie::parse(cookie_string.as_str()).unwrap();
/// c.path_raw()
/// };
///
/// assert_eq!(path, Some("/"));
/// ```
#[inline]
pub fn path_raw(&self) -> Option<&'c str> {
match (self.path.as_ref(), self.cookie_string.as_ref()) {
(Some(path), Some(string)) => path.to_raw_str(string),
_ => None,
}
}
/// Returns the `Domain` of `self` as a string slice of the raw string
/// `self` was originally parsed from. If `self` was not originally parsed
/// from a raw string, or if `self` doesn't contain a `Domain`, or if the
/// `Domain` has changed since parsing, returns `None`.
///
/// This method differs from [domain](#method.domain) in that it returns a
/// string with the same lifetime as the originally parsed string. This
/// lifetime may outlive `self` struct. If a longer lifetime is not
/// required, or you're unsure if you need a longer lifetime, use
/// [domain](#method.domain).
///
/// # Example
///
/// ```
/// use cookie::Cookie;
///
/// let cookie_string = format!("{}={}; Domain=crates.io", "foo", "bar");
///
/// //`c` will be dropped at the end of the scope, but `domain` will live on
/// let domain = {
/// let c = Cookie::parse(cookie_string.as_str()).unwrap();
/// c.domain_raw()
/// };
///
/// assert_eq!(domain, Some("crates.io"));
/// ```
#[inline]
pub fn domain_raw(&self) -> Option<&'c str> {
match (self.domain.as_ref(), self.cookie_string.as_ref()) {
(Some(domain), Some(string)) => domain.to_raw_str(string),
_ => None,
}
}
}
/// Wrapper around `Cookie` whose `Display` implementation percent-encodes the
@ -902,19 +678,6 @@ impl<'a, 'c: 'a> fmt::Display for EncodedCookie<'a, 'c> {
}
impl<'c> fmt::Display for Cookie<'c> {
/// Formats the cookie `self` as a `Set-Cookie` header value.
///
/// # Example
///
/// ```rust
/// use cookie::Cookie;
///
/// let mut cookie = Cookie::build("foo", "bar")
/// .path("/")
/// .finish();
///
/// assert_eq!(&cookie.to_string(), "foo=bar; Path=/");
/// ```
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}={}", self.name(), self.value())?;
self.fmt_parameters(f)
@ -960,7 +723,7 @@ impl<'a, 'b> PartialEq<Cookie<'b>> for Cookie<'a> {
#[cfg(test)]
mod tests {
use ::{Cookie, SameSite};
use ::Cookie;
use ::time::{strptime, Duration};
#[test]
@ -994,60 +757,6 @@ mod tests {
.expires(expires).finish();
assert_eq!(&cookie.to_string(),
"foo=bar; Expires=Wed, 21 Oct 2015 07:28:00 GMT");
let cookie = Cookie::build("foo", "bar")
.same_site(SameSite::Strict).finish();
assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Strict");
let cookie = Cookie::build("foo", "bar")
.same_site(SameSite::Lax).finish();
assert_eq!(&cookie.to_string(), "foo=bar; SameSite=Lax");
}
#[test]
fn cookie_string_long_lifetimes() {
let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
let (name, value, path, domain) = {
// Create a cookie passing a slice
let c = Cookie::parse(cookie_string.as_str()).unwrap();
(c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
};
assert_eq!(name, Some("bar"));
assert_eq!(value, Some("baz"));
assert_eq!(path, Some("/subdir"));
assert_eq!(domain, Some("crates.io"));
}
#[test]
fn owned_cookie_string() {
let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io".to_owned();
let (name, value, path, domain) = {
// Create a cookie passing an owned string
let c = Cookie::parse(cookie_string).unwrap();
(c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
};
assert_eq!(name, None);
assert_eq!(value, None);
assert_eq!(path, None);
assert_eq!(domain, None);
}
#[test]
fn owned_cookie_struct() {
let cookie_string = "bar=baz; Path=/subdir; HttpOnly; Domain=crates.io";
let (name, value, path, domain) = {
// Create an owned cookie
let c = Cookie::parse(cookie_string).unwrap().into_owned();
(c.name_raw(), c.value_raw(), c.path_raw(), c.domain_raw())
};
assert_eq!(name, None);
assert_eq!(value, None);
assert_eq!(path, None);
assert_eq!(domain, None);
}
#[test]

View File

@ -10,7 +10,7 @@ use std::convert::From;
use url::percent_encoding::percent_decode;
use time::{self, Duration};
use ::{Cookie, SameSite, CookieStr};
use ::{Cookie, CookieStr};
/// Enum corresponding to a parsing error.
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
@ -117,8 +117,8 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
let (name, value) = if decode {
name_val_decoded(name, value)?
} else {
let name_indexes = indexes_of(name, s).expect("name sub");
let value_indexes = indexes_of(value, s).expect("value sub");
let name_indexes = indexes_of(name, &s).expect("name sub");
let value_indexes = indexes_of(value, &s).expect("value sub");
let name = CookieStr::Indexed(name_indexes.0, name_indexes.1);
let value = CookieStr::Indexed(value_indexes.0, value_indexes.1);
@ -135,7 +135,6 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
path: None,
secure: false,
http_only: false,
same_site: None
};
for attr in attributes {
@ -162,31 +161,19 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
Err(_) => continue,
};
}
("domain", Some(mut domain)) if !domain.is_empty() => {
if domain.starts_with('.') {
domain = &domain[1..];
}
("domain", Some(v)) if !v.is_empty() => {
let domain = match v.starts_with('.') {
true => &v[1..],
false => v,
};
let (i, j) = indexes_of(domain, s).expect("domain sub");
let (i, j) = indexes_of(domain, &s).expect("domain sub");
cookie.domain = Some(CookieStr::Indexed(i, j));
}
("path", Some(v)) => {
let (i, j) = indexes_of(v, s).expect("path sub");
let (i, j) = indexes_of(v, &s).expect("path sub");
cookie.path = Some(CookieStr::Indexed(i, j));
}
("samesite", Some(v)) => {
if v.eq_ignore_ascii_case("strict") {
cookie.same_site = Some(SameSite::Strict);
} else if v.eq_ignore_ascii_case("lax") {
cookie.same_site = Some(SameSite::Lax);
} else {
// We do nothing here, for now. When/if the `SameSite`
// attribute becomes standard, the spec says that we should
// ignore this cookie, i.e, fail to parse it, when an
// invalid value is passed in. The draft is at
// http://httpwg.org/http-extensions/draft-ietf-httpbis-cookie-same-site.html.
}
}
("expires", Some(v)) => {
// Try strptime with three date formats according to
// http://tools.ietf.org/html/rfc2616#section-3.3.1. Try
@ -196,7 +183,7 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
.or_else(|_| time::strptime(v, "%a, %d-%b-%Y %H:%M:%S %Z"))
.or_else(|_| time::strptime(v, "%a %b %d %H:%M:%S %Y"));
if let Ok(time) = tm {
if let Some(time) = tm.ok() {
cookie.expires = Some(time)
}
}
@ -223,7 +210,7 @@ pub fn parse_cookie<'c, S>(cow: S, decode: bool) -> Result<Cookie<'c>, ParseErro
#[cfg(test)]
mod tests {
use ::{Cookie, SameSite};
use ::Cookie;
use ::time::{strptime, Duration};
macro_rules! assert_eq_parse {
@ -248,29 +235,6 @@ mod tests {
)
}
#[test]
fn parse_same_site() {
let expected = Cookie::build("foo", "bar")
.same_site(SameSite::Lax)
.finish();
assert_eq_parse!("foo=bar; SameSite=Lax", expected);
assert_eq_parse!("foo=bar; SameSite=lax", expected);
assert_eq_parse!("foo=bar; SameSite=LAX", expected);
assert_eq_parse!("foo=bar; samesite=Lax", expected);
assert_eq_parse!("foo=bar; SAMESITE=Lax", expected);
let expected = Cookie::build("foo", "bar")
.same_site(SameSite::Strict)
.finish();
assert_eq_parse!("foo=bar; SameSite=Strict", expected);
assert_eq_parse!("foo=bar; SameSITE=Strict", expected);
assert_eq_parse!("foo=bar; SameSite=strict", expected);
assert_eq_parse!("foo=bar; SameSite=STrICT", expected);
assert_eq_parse!("foo=bar; SameSite=STRICT", expected);
}
#[test]
fn parse() {
assert!(Cookie::parse("bar").is_err());

View File

@ -1,174 +0,0 @@
use secure::ring::hkdf::expand;
use secure::ring::digest::{SHA256, Algorithm};
use secure::ring::hmac::SigningKey;
use secure::ring::rand::{SecureRandom, SystemRandom};
use secure::private::KEY_LEN as PRIVATE_KEY_LEN;
use secure::signed::KEY_LEN as SIGNED_KEY_LEN;
static HKDF_DIGEST: &'static Algorithm = &SHA256;
const KEYS_INFO: &'static str = "COOKIE;SIGNED:HMAC-SHA256;PRIVATE:AEAD-AES-256-GCM";
/// A cryptographic master key for use with `Signed` and/or `Private` jars.
///
/// This structure encapsulates secure, cryptographic keys for use with both
/// [PrivateJar](struct.PrivateJar.html) and [SignedJar](struct.SignedJar.html).
/// It can be derived from a single master key via
/// [from_master](#method.from_master) or generated from a secure random source
/// via [generate](#method.generate). A single instance of `Key` can be used for
/// both a `PrivateJar` and a `SignedJar`.
///
/// This type is only available when the `secure` feature is enabled.
#[derive(Clone)]
pub struct Key {
signing_key: [u8; SIGNED_KEY_LEN],
encryption_key: [u8; PRIVATE_KEY_LEN]
}
impl Key {
/// Derives new signing/encryption keys from a master key.
///
/// The master key must be at least 256-bits (32 bytes). For security, the
/// master key _must_ be cryptographically random. The keys are derived
/// deterministically from the master key.
///
/// # Panics
///
/// Panics if `key` is less than 32 bytes in length.
///
/// # Example
///
/// ```rust
/// use cookie::Key;
///
/// # /*
/// let master_key = { /* a cryptographically random key >= 32 bytes */ };
/// # */
/// # let master_key: &Vec<u8> = &(0..32).collect();
///
/// let key = Key::from_master(master_key);
/// ```
pub fn from_master(key: &[u8]) -> Key {
if key.len() < 32 {
panic!("bad master key length: expected at least 32 bytes, found {}", key.len());
}
// Expand the user's key into two.
let prk = SigningKey::new(HKDF_DIGEST, key);
let mut both_keys = [0; SIGNED_KEY_LEN + PRIVATE_KEY_LEN];
expand(&prk, KEYS_INFO.as_bytes(), &mut both_keys);
// Copy the keys into their respective arrays.
let mut signing_key = [0; SIGNED_KEY_LEN];
let mut encryption_key = [0; PRIVATE_KEY_LEN];
signing_key.copy_from_slice(&both_keys[..SIGNED_KEY_LEN]);
encryption_key.copy_from_slice(&both_keys[SIGNED_KEY_LEN..]);
Key {
signing_key: signing_key,
encryption_key: encryption_key
}
}
/// Generates signing/encryption keys from a secure, random source. Keys are
/// generated nondeterministically.
///
/// # Panics
///
/// Panics if randomness cannot be retrieved from the operating system. See
/// [try_generate](#method.try_generate) for a non-panicking version.
///
/// # Example
///
/// ```rust
/// use cookie::Key;
///
/// let key = Key::generate();
/// ```
pub fn generate() -> Key {
Self::try_generate().expect("failed to generate `Key` from randomness")
}
/// Attempts to generate signing/encryption keys from a secure, random
/// source. Keys are generated nondeterministically. If randomness cannot be
/// retrieved from the underlying operating system, returns `None`.
///
/// # Example
///
/// ```rust
/// use cookie::Key;
///
/// let key = Key::try_generate();
/// ```
pub fn try_generate() -> Option<Key> {
let mut sign_key = [0; SIGNED_KEY_LEN];
let mut enc_key = [0; PRIVATE_KEY_LEN];
let rng = SystemRandom::new();
if rng.fill(&mut sign_key).is_err() || rng.fill(&mut enc_key).is_err() {
return None
}
Some(Key { signing_key: sign_key, encryption_key: enc_key })
}
/// Returns the raw bytes of a key suitable for signing cookies.
///
/// # Example
///
/// ```rust
/// use cookie::Key;
///
/// let key = Key::generate();
/// let signing_key = key.signing();
/// ```
pub fn signing(&self) -> &[u8] {
&self.signing_key[..]
}
/// Returns the raw bytes of a key suitable for encrypting cookies.
///
/// # Example
///
/// ```rust
/// use cookie::Key;
///
/// let key = Key::generate();
/// let encryption_key = key.encryption();
/// ```
pub fn encryption(&self) -> &[u8] {
&self.encryption_key[..]
}
}
#[cfg(test)]
mod test {
use super::Key;
#[test]
fn deterministic_from_master() {
let master_key: Vec<u8> = (0..32).collect();
let key_a = Key::from_master(&master_key);
let key_b = Key::from_master(&master_key);
assert_eq!(key_a.signing(), key_b.signing());
assert_eq!(key_a.encryption(), key_b.encryption());
assert_ne!(key_a.encryption(), key_a.signing());
let master_key_2: Vec<u8> = (32..64).collect();
let key_2 = Key::from_master(&master_key_2);
assert_ne!(key_2.signing(), key_a.signing());
assert_ne!(key_2.encryption(), key_a.encryption());
}
#[test]
fn non_deterministic_generate() {
let key_a = Key::generate();
let key_b = Key::generate();
assert_ne!(key_a.signing(), key_b.signing());
assert_ne!(key_a.encryption(), key_b.encryption());
}
}

View File

@ -1,41 +0,0 @@
#[cfg(test)]
macro_rules! assert_simple_behaviour {
($clear:expr, $secure:expr) => ({
assert_eq!($clear.iter().count(), 0);
$secure.add(Cookie::new("name", "val"));
assert_eq!($clear.iter().count(), 1);
assert_eq!($secure.get("name").unwrap().value(), "val");
assert_ne!($clear.get("name").unwrap().value(), "val");
$secure.add(Cookie::new("another", "two"));
assert_eq!($clear.iter().count(), 2);
$clear.remove(Cookie::named("another"));
assert_eq!($clear.iter().count(), 1);
$secure.remove(Cookie::named("name"));
assert_eq!($clear.iter().count(), 0);
})
}
#[cfg(test)]
macro_rules! assert_secure_behaviour {
($clear:expr, $secure:expr) => ({
$secure.add(Cookie::new("secure", "secure"));
assert!($clear.get("secure").unwrap().value() != "secure");
assert!($secure.get("secure").unwrap().value() == "secure");
let mut cookie = $clear.get("secure").unwrap().clone();
let new_val = format!("{}l", cookie.value());
cookie.set_value(new_val);
$clear.add(cookie);
assert!($secure.get("secure").is_none());
let mut cookie = $clear.get("secure").unwrap().clone();
cookie.set_value("foobar");
$clear.add(cookie);
assert!($secure.get("secure").is_none());
})
}

View File

@ -1,12 +0,0 @@
extern crate ring;
extern crate base64;
#[macro_use]
mod macros;
mod private;
mod signed;
mod key;
pub use self::private::*;
pub use self::signed::*;
pub use self::key::*;

View File

@ -1,178 +0,0 @@
use secure::ring::aead::{seal_in_place, open_in_place, Algorithm, AES_256_GCM};
use secure::ring::aead::{OpeningKey, SealingKey};
use secure::ring::rand::{SecureRandom, SystemRandom};
use secure::{base64, Key};
use {Cookie, CookieJar};
// Keep these in sync, and keep the key len synced with the `private` docs as
// well as the `KEYS_INFO` const in secure::Key.
static ALGO: &'static Algorithm = &AES_256_GCM;
const NONCE_LEN: usize = 12;
pub const KEY_LEN: usize = 32;
/// A child cookie jar that provides authenticated encryption for its cookies.
///
/// A _private_ child jar signs and encrypts all the cookies added to it and
/// verifies and decrypts cookies retrieved from it. Any cookies stored in a
/// `PrivateJar` are simultaneously assured confidentiality, integrity, and
/// authenticity. In other words, clients cannot discover nor tamper with the
/// contents of a cookie, nor can they fabricate cookie data.
///
/// This type is only available when the `secure` feature is enabled.
pub struct PrivateJar<'a> {
parent: &'a mut CookieJar,
key: [u8; KEY_LEN]
}
impl<'a> PrivateJar<'a> {
/// Creates a new child `PrivateJar` with parent `parent` and key `key`.
/// This method is typically called indirectly via the `signed` method of
/// `CookieJar`.
#[doc(hidden)]
pub fn new(parent: &'a mut CookieJar, key: &Key) -> PrivateJar<'a> {
let mut key_array = [0u8; KEY_LEN];
key_array.copy_from_slice(key.encryption());
PrivateJar { parent: parent, key: key_array }
}
/// Given a sealed value `str` where the nonce is prepended to the original
/// value and then both are Base64 encoded, verifies and decrypts the sealed
/// value and returns it. If there's a problem, returns an `Err` with a
/// string describing the issue.
fn unseal(&self, value: &str) -> Result<String, &'static str> {
let mut data = base64::decode(value).map_err(|_| "bad base64 value")?;
if data.len() <= NONCE_LEN {
return Err("length of decoded data is <= NONCE_LEN");
}
let key = OpeningKey::new(ALGO, &self.key).expect("opening key");
let (nonce, sealed) = data.split_at_mut(NONCE_LEN);
let unsealed = open_in_place(&key, nonce, &[], 0, sealed)
.map_err(|_| "invalid key/nonce/value: bad seal")?;
::std::str::from_utf8(unsealed)
.map(|s| s.to_string())
.map_err(|_| "bad unsealed utf8")
}
/// Returns a reference to the `Cookie` inside this jar with the name `name`
/// and authenticates and decrypts the cookie's value, returning a `Cookie`
/// with the decrypted value. If the cookie cannot be found, or the cookie
/// fails to authenticate or decrypt, `None` is returned.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// let mut private_jar = jar.private(&key);
/// assert!(private_jar.get("name").is_none());
///
/// private_jar.add(Cookie::new("name", "value"));
/// assert_eq!(private_jar.get("name").unwrap().value(), "value");
/// ```
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
if let Some(cookie_ref) = self.parent.get(name) {
let mut cookie = cookie_ref.clone();
if let Ok(value) = self.unseal(cookie.value()) {
cookie.set_value(value);
return Some(cookie);
}
}
None
}
/// Adds `cookie` to the parent jar. The cookie's value is encrypted with
/// authenticated encryption assuring confidentiality, integrity, and
/// authenticity.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// jar.private(&key).add(Cookie::new("name", "value"));
///
/// assert_ne!(jar.get("name").unwrap().value(), "value");
/// assert_eq!(jar.private(&key).get("name").unwrap().value(), "value");
/// ```
pub fn add(&mut self, mut cookie: Cookie<'static>) {
let mut data;
let output_len = {
// Create the `SealingKey` structure.
let key = SealingKey::new(ALGO, &self.key).expect("sealing key creation");
// Create a vec to hold the [nonce | cookie value | overhead].
let overhead = ALGO.tag_len();
let cookie_val = cookie.value().as_bytes();
data = vec![0; NONCE_LEN + cookie_val.len() + overhead];
// Randomly generate the nonce, then copy the cookie value as input.
let (nonce, in_out) = data.split_at_mut(NONCE_LEN);
SystemRandom::new().fill(nonce).expect("couldn't random fill nonce");
in_out[..cookie_val.len()].copy_from_slice(cookie_val);
// Perform the actual sealing operation and get the output length.
seal_in_place(&key, nonce, &[], in_out, overhead).expect("in-place seal")
};
// Base64 encode the nonce and encrypted value.
let sealed_value = base64::encode(&data[..(NONCE_LEN + output_len)]);
cookie.set_value(sealed_value);
// Add the sealed cookie to the parent.
self.parent.add(cookie);
}
/// Removes `cookie` from the parent jar.
///
/// For correct removal, the passed in `cookie` must contain the same `path`
/// and `domain` as the cookie that was initially set.
///
/// See [CookieJar::remove](struct.CookieJar.html#method.remove) for more
/// details.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// let mut private_jar = jar.private(&key);
///
/// private_jar.add(Cookie::new("name", "value"));
/// assert!(private_jar.get("name").is_some());
///
/// private_jar.remove(Cookie::named("name"));
/// assert!(private_jar.get("name").is_none());
/// ```
pub fn remove(&mut self, cookie: Cookie<'static>) {
self.parent.remove(cookie);
}
}
#[cfg(test)]
mod test {
use {CookieJar, Cookie, Key};
#[test]
fn simple() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_simple_behaviour!(jar, jar.private(&key));
}
#[test]
fn private() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_secure_behaviour!(jar, jar.private(&key));
}
}

View File

@ -1,152 +0,0 @@
use secure::ring::digest::{SHA256, Algorithm};
use secure::ring::hmac::{SigningKey, sign, verify_with_own_key as verify};
use secure::{base64, Key};
use {Cookie, CookieJar};
// Keep these in sync, and keep the key len synced with the `signed` docs as
// well as the `KEYS_INFO` const in secure::Key.
static HMAC_DIGEST: &'static Algorithm = &SHA256;
const BASE64_DIGEST_LEN: usize = 44;
pub const KEY_LEN: usize = 32;
/// A child cookie jar that authenticates its cookies.
///
/// A _signed_ child jar signs all the cookies added to it and verifies cookies
/// retrieved from it. Any cookies stored in a `SignedJar` are assured integrity
/// and authenticity. In other words, clients cannot tamper with the contents of
/// a cookie nor can they fabricate cookie values, but the data is visible in
/// plaintext.
///
/// This type is only available when the `secure` feature is enabled.
pub struct SignedJar<'a> {
parent: &'a mut CookieJar,
key: SigningKey
}
impl<'a> SignedJar<'a> {
/// Creates a new child `SignedJar` with parent `parent` and key `key`. This
/// method is typically called indirectly via the `signed` method of
/// `CookieJar`.
#[doc(hidden)]
pub fn new(parent: &'a mut CookieJar, key: &Key) -> SignedJar<'a> {
SignedJar { parent: parent, key: SigningKey::new(HMAC_DIGEST, key.signing()) }
}
/// Given a signed value `str` where the signature is prepended to `value`,
/// verifies the signed value and returns it. If there's a problem, returns
/// an `Err` with a string describing the issue.
fn verify(&self, cookie_value: &str) -> Result<String, &'static str> {
if cookie_value.len() < BASE64_DIGEST_LEN {
return Err("length of value is <= BASE64_DIGEST_LEN");
}
let (digest_str, value) = cookie_value.split_at(BASE64_DIGEST_LEN);
let sig = base64::decode(digest_str).map_err(|_| "bad base64 digest")?;
verify(&self.key, value.as_bytes(), &sig)
.map(|_| value.to_string())
.map_err(|_| "value did not verify")
}
/// Returns a reference to the `Cookie` inside this jar with the name `name`
/// and verifies the authenticity and integrity of the cookie's value,
/// returning a `Cookie` with the authenticated value. If the cookie cannot
/// be found, or the cookie fails to verify, `None` is returned.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// let mut signed_jar = jar.signed(&key);
/// assert!(signed_jar.get("name").is_none());
///
/// signed_jar.add(Cookie::new("name", "value"));
/// assert_eq!(signed_jar.get("name").unwrap().value(), "value");
/// ```
pub fn get(&self, name: &str) -> Option<Cookie<'static>> {
if let Some(cookie_ref) = self.parent.get(name) {
let mut cookie = cookie_ref.clone();
if let Ok(value) = self.verify(cookie.value()) {
cookie.set_value(value);
return Some(cookie);
}
}
None
}
/// Adds `cookie` to the parent jar. The cookie's value is signed assuring
/// integrity and authenticity.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// jar.signed(&key).add(Cookie::new("name", "value"));
///
/// assert_ne!(jar.get("name").unwrap().value(), "value");
/// assert!(jar.get("name").unwrap().value().contains("value"));
/// assert_eq!(jar.signed(&key).get("name").unwrap().value(), "value");
/// ```
pub fn add(&mut self, mut cookie: Cookie<'static>) {
let digest = sign(&self.key, cookie.value().as_bytes());
let mut new_value = base64::encode(digest.as_ref());
new_value.push_str(cookie.value());
cookie.set_value(new_value);
self.parent.add(cookie);
}
/// Removes `cookie` from the parent jar.
///
/// For correct removal, the passed in `cookie` must contain the same `path`
/// and `domain` as the cookie that was initially set.
///
/// See [CookieJar::remove](struct.CookieJar.html#method.remove) for more
/// details.
///
/// # Example
///
/// ```rust
/// use cookie::{CookieJar, Cookie, Key};
///
/// let key = Key::generate();
/// let mut jar = CookieJar::new();
/// let mut signed_jar = jar.signed(&key);
///
/// signed_jar.add(Cookie::new("name", "value"));
/// assert!(signed_jar.get("name").is_some());
///
/// signed_jar.remove(Cookie::named("name"));
/// assert!(signed_jar.get("name").is_none());
/// ```
pub fn remove(&mut self, cookie: Cookie<'static>) {
self.parent.remove(cookie);
}
}
#[cfg(test)]
mod test {
use {CookieJar, Cookie, Key};
#[test]
fn simple() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_simple_behaviour!(jar, jar.signed(&key));
}
#[test]
fn private() {
let key = Key::generate();
let mut jar = CookieJar::new();
assert_secure_behaviour!(jar, jar.signed(&key));
}
}

View File

@ -1 +1 @@
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"6d7856aa51991bc6c15945046377a9992b8886cc8e4602d08f650f92803b71f6",".travis.yml":"78252ef89a407b1d76616b7afbf7afb8205530a7f7039f3a7ea140684e3aa8bc","Cargo.toml":"b5a8bd0fff8cc291a345626ccd8fb5bd958ec2f59cc58760870897b727631226","Cargo.toml.orig":"641af2d4d16c6003d80ce6bdf2fbee0ab9571c4deb27a8d1533e9ca5d4dcde16","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"bd0e99ce271903a9f67cf5f8fca2f67f2583e4336fbaf583fcd78ec238d4176e","src/capabilities.rs":"5039c1f80885ca2bab19f2d1c40b405c37c09901918625395141ac2e01600728","src/command.rs":"4bc0380b3e8916dd7514f751b18b47bf510c1fa16d9832617cf0ce5470d87797","src/common.rs":"d696aabe88061f8315578c42115d976123a8fc4276384e478e14d241dfc9acc0","src/error.rs":"b0acf64e052edbc26e7dbcd1a54e8b9a786f01e9d48e0e5b2f410266bfdb9da2","src/httpapi.rs":"44f1061123580ebb73ddd164a18cb223e16445c6a2eabd91f14c39d6a3d282e1","src/lib.rs":"336c146e934711dfe49f4b44bbcf278686b00be8d89abb9c7b7b045254994fbf","src/macros.rs":"93094c48e3880d925e684fba9678693eb8c0c39c7ed47b130b0751c4bca37ddc","src/response.rs":"63cabdc7f9136a0f24c10dc16b11c6991c95fd9fee1d1bc47d48c62c6f69eb33","src/server.rs":"f2110378cfaf7a4facb39d0e45c479a00c95a939536c85a6a105c858fffc2d70"},"package":"26cd8cf65699e3b8d1a21088ba2180cfe4f5d37a414c994976a34b289799e24d"}
{"files":{".cargo-ok":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",".gitignore":"6d7856aa51991bc6c15945046377a9992b8886cc8e4602d08f650f92803b71f6",".travis.yml":"78252ef89a407b1d76616b7afbf7afb8205530a7f7039f3a7ea140684e3aa8bc","Cargo.toml":"ed7a4c3380fbb78b99be805c20f20d590e5f377173284017ed86606cef3663e9","LICENSE":"fab3dd6bdab226f1c08630b1dd917e11fcb4ec5e1e020e2c16f83a0a13863e85","README.md":"bd0e99ce271903a9f67cf5f8fca2f67f2583e4336fbaf583fcd78ec238d4176e","src/capabilities.rs":"5039c1f80885ca2bab19f2d1c40b405c37c09901918625395141ac2e01600728","src/command.rs":"4bc0380b3e8916dd7514f751b18b47bf510c1fa16d9832617cf0ce5470d87797","src/common.rs":"d696aabe88061f8315578c42115d976123a8fc4276384e478e14d241dfc9acc0","src/error.rs":"b0acf64e052edbc26e7dbcd1a54e8b9a786f01e9d48e0e5b2f410266bfdb9da2","src/httpapi.rs":"44f1061123580ebb73ddd164a18cb223e16445c6a2eabd91f14c39d6a3d282e1","src/lib.rs":"336c146e934711dfe49f4b44bbcf278686b00be8d89abb9c7b7b045254994fbf","src/macros.rs":"93094c48e3880d925e684fba9678693eb8c0c39c7ed47b130b0751c4bca37ddc","src/response.rs":"c09d92fcf8177e3a0950db71f843123bab3dea78f136ba9c712afe1893f73961","src/server.rs":"f2110378cfaf7a4facb39d0e45c479a00c95a939536c85a6a105c858fffc2d70"},"package":"3099729d884692d690796454e8529edf3f0ebd87c87840f9c809df8eabb175ed"}

View File

@ -1,46 +1,20 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g. crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
name = "webdriver"
version = "0.27.0"
version = "0.26.0"
authors = ["Mozilla Tools and Automation <tools@lists.mozilla.com>"]
description = "Library implementing the wire protocol for the W3C WebDriver specification"
documentation = "https://docs.rs/webdriver"
repository = "https://github.com/mozilla/webdriver-rust"
readme = "README.md"
keywords = ["webdriver", "browser", "automation", "protocol", "w3c"]
license = "MPL-2.0"
repository = "https://github.com/mozilla/webdriver-rust"
[dependencies.log]
version = "0.3"
[dependencies.hyper]
version = "0.10"
[dependencies.url]
version = "1"
[dependencies.backtrace]
version = "0.3"
[dependencies.rustc-serialize]
version = "0.3"
[dependencies.time]
version = "0.1"
[dependencies.regex]
version = "0.2"
[dependencies.cookie]
version = "0.9"
default-features = false
[dependencies]
backtrace = "0.3"
cookie = {version = "0.6", default-features = false}
hyper = "0.10"
log = "0.3"
regex = "0.2"
rustc-serialize = "0.3"
time = "0.1"
url = "1"

View File

@ -8,35 +8,32 @@ use time;
pub enum WebDriverResponse {
CloseWindow(CloseWindowResponse),
Cookie(CookieResponse),
Cookies(CookiesResponse),
DeleteSession,
ElementRect(RectResponse),
ElementRect(ElementRectResponse),
Generic(ValueResponse),
NewSession(NewSessionResponse),
Timeouts(TimeoutsResponse),
Void,
WindowRect(RectResponse),
WindowRect(WindowRectResponse),
}
impl WebDriverResponse {
pub fn to_json_string(self) -> String {
use response::WebDriverResponse::*;
let obj = match self {
CloseWindow(ref x) => json::encode(&x.to_json()),
Cookie(ref x) => json::encode(x),
Cookies(ref x) => json::encode(x),
DeleteSession => Ok("{}".to_string()),
ElementRect(ref x) => json::encode(x),
Generic(ref x) => json::encode(x),
NewSession(ref x) => json::encode(x),
Timeouts(ref x) => json::encode(x),
Void => Ok("{}".to_string()),
WindowRect(ref x) => json::encode(x),
WebDriverResponse::CloseWindow(ref x) => json::encode(&x.to_json()),
WebDriverResponse::Cookie(ref x) => json::encode(x),
WebDriverResponse::DeleteSession => Ok("{}".to_string()),
WebDriverResponse::ElementRect(ref x) => json::encode(x),
WebDriverResponse::Generic(ref x) => json::encode(x),
WebDriverResponse::NewSession(ref x) => json::encode(x),
WebDriverResponse::Timeouts(ref x) => json::encode(x),
WebDriverResponse::Void => Ok("{}".to_string()),
WebDriverResponse::WindowRect(ref x) => json::encode(x),
}.unwrap();
match self {
Generic(_) | Cookie(_) | Cookies(_) => obj,
WebDriverResponse::Generic(_) |
WebDriverResponse::Cookie(_) => obj,
_ => {
let mut data = String::with_capacity(11 + obj.len());
data.push_str("{\"value\": ");
@ -114,16 +111,24 @@ impl ValueResponse {
}
#[derive(RustcEncodable, Debug)]
pub struct RectResponse {
pub struct WindowRectResponse {
pub x: i64,
pub y: i64,
pub width: u64,
pub height: u64,
}
#[derive(RustcEncodable, Debug)]
pub struct ElementRectResponse {
pub x: f64,
pub y: f64,
pub width: f64,
pub height: f64
}
impl RectResponse {
pub fn new(x: f64, y: f64, width: f64, height: f64) -> RectResponse {
RectResponse {
impl ElementRectResponse {
pub fn new(x: f64, y: f64, width: f64, height: f64) -> ElementRectResponse {
ElementRectResponse {
x: x,
y: y,
width: width,
@ -132,6 +137,7 @@ impl RectResponse {
}
}
//TODO: some of these fields are probably supposed to be optional
#[derive(RustcEncodable, PartialEq, Debug, Clone)]
pub struct Cookie {
pub name: String,
@ -140,7 +146,22 @@ pub struct Cookie {
pub domain: Nullable<String>,
pub expiry: Nullable<Date>,
pub secure: bool,
pub httpOnly: bool,
pub httpOnly: bool
}
impl Cookie {
pub fn new(name: String, value: String, path: Nullable<String>, domain: Nullable<String>,
expiry: Nullable<Date>, secure: bool, http_only: bool) -> Cookie {
Cookie {
name: name,
value: value,
path: path,
domain: domain,
expiry: expiry,
secure: secure,
httpOnly: http_only
}
}
}
impl Into<cookie::Cookie<'static>> for Cookie {
@ -159,7 +180,7 @@ impl Into<cookie::Cookie<'static>> for Cookie {
let cookie = match self.expiry {
Nullable::Value(Date(expiry)) => {
cookie.expires(time::at(time::Timespec::new(expiry as i64, 0)))
}
},
Nullable::Null => cookie,
};
cookie.finish()
@ -168,20 +189,32 @@ impl Into<cookie::Cookie<'static>> for Cookie {
#[derive(RustcEncodable, Debug)]
pub struct CookieResponse {
pub value: Cookie,
pub value: Vec<Cookie>
}
#[derive(RustcEncodable, Debug)]
pub struct CookiesResponse {
pub value: Vec<Cookie>,
impl CookieResponse {
pub fn new(value: Vec<Cookie>) -> CookieResponse {
CookieResponse {
value: value
}
}
}
#[cfg(test)]
mod tests {
use super::{CloseWindowResponse, Cookie, CookieResponse, CookiesResponse, NewSessionResponse,
Nullable, RectResponse, TimeoutsResponse, ValueResponse, WebDriverResponse};
use rustc_serialize::json::Json;
use std::collections::BTreeMap;
use rustc_serialize::json::Json;
use super::{WebDriverResponse,
CloseWindowResponse,
CookieResponse,
ElementRectResponse,
NewSessionResponse,
ValueResponse,
TimeoutsResponse,
WindowRectResponse,
Cookie,
Nullable};
fn test(resp: WebDriverResponse, expected_str: &str) {
let data = resp.to_json_string();
@ -200,43 +233,24 @@ mod tests {
#[test]
fn test_cookie() {
let cookie = Cookie {
name: "name".into(),
value: "value".into(),
path: Nullable::Value("/".into()),
domain: Nullable::Null,
expiry: Nullable::Null,
secure: true,
httpOnly: false,
};
let resp = WebDriverResponse::Cookie(CookieResponse { value: cookie });
let expected = r#"{"value": {"name": "name", "expiry": null, "value": "value",
"path": "/", "domain": null, "secure": true, "httpOnly": false}}"#;
test(resp, expected);
}
#[test]
fn test_cookies() {
let resp = WebDriverResponse::Cookies(CookiesResponse {
value: vec![
Cookie {
name: "name".into(),
value: "value".into(),
path: Nullable::Value("/".into()),
domain: Nullable::Null,
expiry: Nullable::Null,
secure: true,
httpOnly: false,
}
]});
let expected = r#"{"value": [{"name": "name", "value": "value", "path": "/",
let resp = WebDriverResponse::Cookie(CookieResponse::new(
vec![
Cookie::new("test".into(),
"test_value".into(),
Nullable::Value("/".into()),
Nullable::Null,
Nullable::Null,
true,
false)
]));
let expected = r#"{"value": [{"name": "test", "value": "test_value", "path": "/",
"domain": null, "expiry": null, "secure": true, "httpOnly": false}]}"#;
test(resp, expected);
}
#[test]
fn test_element_rect() {
let resp = WebDriverResponse::ElementRect(RectResponse::new(
let resp = WebDriverResponse::ElementRect(ElementRectResponse::new(
0f64, 1f64, 2f64, 3f64));
let expected = r#"{"value": {"x": 0.0, "y": 1.0, "width": 2.0, "height": 3.0}}"#;
test(resp, expected);
@ -244,13 +258,13 @@ mod tests {
#[test]
fn test_window_rect() {
let resp = WebDriverResponse::WindowRect(RectResponse {
x: 0f64,
y: 1f64,
width: 2f64,
height: 3f64,
let resp = WebDriverResponse::WindowRect(WindowRectResponse {
x: 0i64,
y: 1i64,
width: 2u64,
height: 3u64,
});
let expected = r#"{"value": {"x": 0.0, "y": 1.0, "width": 2.0, "height": 3.0}}"#;
let expected = r#"{"value": {"x": 0, "y": 1, "width": 2, "height": 3}}"#;
test(resp, expected);
}