зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1371405 - Update vendored WebDriver crate; r=jgraham
This updates the vendored WebDriver crate to 0.27.0 MozReview-Commit-ID: HM6MIU9IDAq --HG-- extra : rebase_source : 080e3a35972079b86b03b731e83e5171c5918da4
This commit is contained in:
Родитель
8cc37ae802
Коммит
4e394ad086
|
@ -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.26.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webdriver 0.27.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.6.2"
|
||||
version = "0.9.1"
|
||||
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.26.0"
|
||||
version = "0.27.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.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"cookie 0.9.1 (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.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30b3493e12a550c2f96be785088d1da8d93189e7237c8a8d0d871bc9070334c3"
|
||||
"checksum cookie 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a54aa6d675d62b2f95b56b331b5222a520149a54f23a2d21974dfcc69caf0a9d"
|
||||
"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.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3099729d884692d690796454e8529edf3f0ebd87c87840f9c809df8eabb175ed"
|
||||
"checksum webdriver 0.27.0 (registry+https://github.com/rust-lang/crates.io-index)" = "26cd8cf65699e3b8d1a21088ba2180cfe4f5d37a414c994976a34b289799e24d"
|
||||
"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"
|
||||
|
|
|
@ -27,7 +27,7 @@ slog-atomic = "0.4"
|
|||
slog-stdlog = "1"
|
||||
slog-stream = "1"
|
||||
uuid = "0.1.18"
|
||||
webdriver = "0.26.0"
|
||||
webdriver = "0.27.0"
|
||||
zip = "0.1"
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"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"}
|
||||
{"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"}
|
|
@ -1,23 +1,38 @@
|
|||
[package]
|
||||
# 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>"]
|
||||
version = "0.6.2"
|
||||
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"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/alexcrichton/cookie-rs"
|
||||
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.
|
||||
"""
|
||||
[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"
|
||||
|
||||
[features]
|
||||
secure = ["openssl", "rustc-serialize"]
|
||||
percent-encode = ["url"]
|
||||
|
||||
[dependencies]
|
||||
time = "0.1"
|
||||
url = { version = "1.0", optional = true }
|
||||
openssl = { version = "0.9.0", optional = true }
|
||||
rustc-serialize = { version = "0.3", optional = true }
|
||||
secure = ["ring", "base64"]
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "cookie"
|
||||
authors = ["Alex Crichton <alex@alexcrichton.com>", "Sergio Benitez <sb@sergio.bz>"]
|
||||
version = "0.9.1"
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/alexcrichton/cookie-rs"
|
||||
documentation = "https://docs.rs/cookie"
|
||||
description = """
|
||||
Crate for parsing HTTP cookie headers and managing a cookie jar. Supports signed
|
||||
and private (encrypted + signed) jars.
|
||||
"""
|
||||
|
||||
[features]
|
||||
secure = ["ring", "base64"]
|
||||
percent-encode = ["url"]
|
||||
|
||||
[dependencies]
|
||||
time = "0.1"
|
||||
url = { version = "1.0", optional = true }
|
||||
ring = { version = "0.11.0", optional = true }
|
||||
base64 = { version = "0.6.0", optional = true }
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
|
@ -1,21 +1,26 @@
|
|||
# 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)
|
||||
|
||||
[Documentation](http://docs.rs/cookie)
|
||||
A library for parsing HTTP cookies and managing cookie jars.
|
||||
|
||||
A library for parsing HTTP cookies and managing cookie jars
|
||||
# Usage
|
||||
|
||||
Add the following to your `Cargo.toml`:
|
||||
|
||||
```toml
|
||||
# Cargo.toml
|
||||
[dependencies]
|
||||
cookie = "0.6"
|
||||
cookie = "0.9"
|
||||
```
|
||||
|
||||
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, and LICENSE-MIT for details.
|
||||
See [LICENSE-APACHE](LICENSE-APACHE), and [LICENSE-MIT](LICENSE-MIT) for
|
||||
details.
|
||||
|
|
|
@ -2,13 +2,13 @@ use std::borrow::Cow;
|
|||
|
||||
use time::{Tm, Duration};
|
||||
|
||||
use ::Cookie;
|
||||
use ::{Cookie, SameSite};
|
||||
|
||||
/// 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,7 +31,6 @@ use ::Cookie;
|
|||
/// .finish();
|
||||
/// # }
|
||||
/// ```
|
||||
///
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CookieBuilder {
|
||||
/// The cookie being built.
|
||||
|
@ -182,6 +181,52 @@ 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
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
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()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
//! 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")
|
||||
}
|
||||
}
|
||||
}
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -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.6"
|
||||
//! cookie = "0.9"
|
||||
//! ```
|
||||
//!
|
||||
//! Then add the following line to your crate root:
|
||||
|
@ -25,13 +25,18 @@
|
|||
//! features:
|
||||
//!
|
||||
//!
|
||||
//! * **secure** (enabled by default)
|
||||
//! * **secure** (disabled by default)
|
||||
//!
|
||||
//! Enables signing and encryption of cookies.
|
||||
//! Enables signed and private (signed + encrypted) cookie jars.
|
||||
//!
|
||||
//! 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.
|
||||
//! 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.
|
||||
//!
|
||||
//! * **percent-encode** (disabled by default)
|
||||
//!
|
||||
|
@ -39,7 +44,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
|
||||
|
@ -53,18 +58,20 @@
|
|||
//! features = ["secure", "percent-encode"]
|
||||
//! ```
|
||||
|
||||
#![doc(html_root_url = "https://docs.rs/cookie/0.6")]
|
||||
#![allow(deprecated)]
|
||||
#![doc(html_root_url = "https://docs.rs/cookie/0.9")]
|
||||
#![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 jar;
|
||||
mod parse;
|
||||
mod jar;
|
||||
mod delta;
|
||||
mod draft;
|
||||
|
||||
#[cfg(feature = "secure")] #[macro_use] mod secure;
|
||||
#[cfg(feature = "secure")] pub use secure::*;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ascii::AsciiExt;
|
||||
|
@ -77,9 +84,9 @@ use time::{Tm, Duration};
|
|||
|
||||
use parse::parse_cookie;
|
||||
pub use parse::ParseError;
|
||||
|
||||
pub use builder::CookieBuilder;
|
||||
pub use jar::CookieJar;
|
||||
pub use jar::{CookieJar, Delta, Iter};
|
||||
pub use draft::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum CookieStr {
|
||||
|
@ -90,14 +97,6 @@ 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.
|
||||
|
@ -106,15 +105,27 @@ 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 {
|
||||
if self.is_indexed() && string.is_none() {
|
||||
panic!("Cannot convert indexed str to str without base string!")
|
||||
}
|
||||
|
||||
match *self {
|
||||
CookieStr::Indexed(i, j) => &string.unwrap()[i..j],
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Representation of an HTTP cookie.
|
||||
|
@ -132,7 +143,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;
|
||||
|
@ -165,6 +176,8 @@ 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> {
|
||||
|
@ -192,9 +205,27 @@ 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.
|
||||
///
|
||||
|
@ -299,6 +330,7 @@ impl<'c> Cookie<'c> {
|
|||
path: self.path,
|
||||
secure: self.secure,
|
||||
http_only: self.http_only,
|
||||
same_site: self.same_site,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,6 +409,21 @@ 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
|
||||
|
@ -530,6 +577,24 @@ 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
|
||||
|
@ -614,6 +679,34 @@ 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")?;
|
||||
|
@ -623,6 +716,10 @@ 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)?;
|
||||
}
|
||||
|
@ -641,6 +738,133 @@ 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
|
||||
|
@ -678,6 +902,19 @@ 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)
|
||||
|
@ -723,7 +960,7 @@ impl<'a, 'b> PartialEq<Cookie<'b>> for Cookie<'a> {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::Cookie;
|
||||
use ::{Cookie, SameSite};
|
||||
use ::time::{strptime, Duration};
|
||||
|
||||
#[test]
|
||||
|
@ -757,6 +994,60 @@ 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]
|
||||
|
|
|
@ -10,7 +10,7 @@ use std::convert::From;
|
|||
use url::percent_encoding::percent_decode;
|
||||
use time::{self, Duration};
|
||||
|
||||
use ::{Cookie, CookieStr};
|
||||
use ::{Cookie, SameSite, 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,6 +135,7 @@ 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 {
|
||||
|
@ -161,19 +162,31 @@ fn parse_inner<'c>(s: &str, decode: bool) -> Result<Cookie<'c>, ParseError> {
|
|||
Err(_) => continue,
|
||||
};
|
||||
}
|
||||
("domain", Some(v)) if !v.is_empty() => {
|
||||
let domain = match v.starts_with('.') {
|
||||
true => &v[1..],
|
||||
false => v,
|
||||
};
|
||||
("domain", Some(mut domain)) if !domain.is_empty() => {
|
||||
if domain.starts_with('.') {
|
||||
domain = &domain[1..];
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -183,7 +196,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 Some(time) = tm.ok() {
|
||||
if let Ok(time) = tm {
|
||||
cookie.expires = Some(time)
|
||||
}
|
||||
}
|
||||
|
@ -210,7 +223,7 @@ pub fn parse_cookie<'c, S>(cow: S, decode: bool) -> Result<Cookie<'c>, ParseErro
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use ::Cookie;
|
||||
use ::{Cookie, SameSite};
|
||||
use ::time::{strptime, Duration};
|
||||
|
||||
macro_rules! assert_eq_parse {
|
||||
|
@ -235,6 +248,29 @@ 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());
|
||||
|
|
|
@ -0,0 +1,174 @@
|
|||
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());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
#[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());
|
||||
})
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
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::*;
|
|
@ -0,0 +1,178 @@
|
|||
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));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
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));
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
{"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"}
|
||||
{"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"}
|
|
@ -1,20 +1,46 @@
|
|||
# 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.26.0"
|
||||
version = "0.27.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]
|
||||
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"
|
||||
[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
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
[package]
|
||||
name = "webdriver"
|
||||
version = "0.27.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"
|
||||
|
||||
[dependencies]
|
||||
backtrace = "0.3"
|
||||
cookie = {version = "0.9", default-features = false}
|
||||
hyper = "0.10"
|
||||
log = "0.3"
|
||||
regex = "0.2"
|
||||
rustc-serialize = "0.3"
|
||||
time = "0.1"
|
||||
url = "1"
|
|
@ -8,32 +8,35 @@ use time;
|
|||
pub enum WebDriverResponse {
|
||||
CloseWindow(CloseWindowResponse),
|
||||
Cookie(CookieResponse),
|
||||
Cookies(CookiesResponse),
|
||||
DeleteSession,
|
||||
ElementRect(ElementRectResponse),
|
||||
ElementRect(RectResponse),
|
||||
Generic(ValueResponse),
|
||||
NewSession(NewSessionResponse),
|
||||
Timeouts(TimeoutsResponse),
|
||||
Void,
|
||||
WindowRect(WindowRectResponse),
|
||||
WindowRect(RectResponse),
|
||||
}
|
||||
|
||||
impl WebDriverResponse {
|
||||
pub fn to_json_string(self) -> String {
|
||||
use response::WebDriverResponse::*;
|
||||
|
||||
let obj = match self {
|
||||
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),
|
||||
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),
|
||||
}.unwrap();
|
||||
|
||||
match self {
|
||||
WebDriverResponse::Generic(_) |
|
||||
WebDriverResponse::Cookie(_) => obj,
|
||||
Generic(_) | Cookie(_) | Cookies(_) => obj,
|
||||
_ => {
|
||||
let mut data = String::with_capacity(11 + obj.len());
|
||||
data.push_str("{\"value\": ");
|
||||
|
@ -111,24 +114,16 @@ impl ValueResponse {
|
|||
}
|
||||
|
||||
#[derive(RustcEncodable, Debug)]
|
||||
pub struct WindowRectResponse {
|
||||
pub x: i64,
|
||||
pub y: i64,
|
||||
pub width: u64,
|
||||
pub height: u64,
|
||||
}
|
||||
|
||||
#[derive(RustcEncodable, Debug)]
|
||||
pub struct ElementRectResponse {
|
||||
pub struct RectResponse {
|
||||
pub x: f64,
|
||||
pub y: f64,
|
||||
pub width: f64,
|
||||
pub height: f64
|
||||
}
|
||||
|
||||
impl ElementRectResponse {
|
||||
pub fn new(x: f64, y: f64, width: f64, height: f64) -> ElementRectResponse {
|
||||
ElementRectResponse {
|
||||
impl RectResponse {
|
||||
pub fn new(x: f64, y: f64, width: f64, height: f64) -> RectResponse {
|
||||
RectResponse {
|
||||
x: x,
|
||||
y: y,
|
||||
width: width,
|
||||
|
@ -137,7 +132,6 @@ impl ElementRectResponse {
|
|||
}
|
||||
}
|
||||
|
||||
//TODO: some of these fields are probably supposed to be optional
|
||||
#[derive(RustcEncodable, PartialEq, Debug, Clone)]
|
||||
pub struct Cookie {
|
||||
pub name: String,
|
||||
|
@ -146,22 +140,7 @@ pub struct Cookie {
|
|||
pub domain: Nullable<String>,
|
||||
pub expiry: Nullable<Date>,
|
||||
pub secure: 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
|
||||
}
|
||||
}
|
||||
pub httpOnly: bool,
|
||||
}
|
||||
|
||||
impl Into<cookie::Cookie<'static>> for Cookie {
|
||||
|
@ -180,7 +159,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()
|
||||
|
@ -189,32 +168,20 @@ impl Into<cookie::Cookie<'static>> for Cookie {
|
|||
|
||||
#[derive(RustcEncodable, Debug)]
|
||||
pub struct CookieResponse {
|
||||
pub value: Vec<Cookie>
|
||||
pub value: Cookie,
|
||||
}
|
||||
|
||||
impl CookieResponse {
|
||||
pub fn new(value: Vec<Cookie>) -> CookieResponse {
|
||||
CookieResponse {
|
||||
value: value
|
||||
}
|
||||
}
|
||||
#[derive(RustcEncodable, Debug)]
|
||||
pub struct CookiesResponse {
|
||||
pub value: Vec<Cookie>,
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::collections::BTreeMap;
|
||||
use super::{CloseWindowResponse, Cookie, CookieResponse, CookiesResponse, NewSessionResponse,
|
||||
Nullable, RectResponse, TimeoutsResponse, ValueResponse, WebDriverResponse};
|
||||
use rustc_serialize::json::Json;
|
||||
use super::{WebDriverResponse,
|
||||
CloseWindowResponse,
|
||||
CookieResponse,
|
||||
ElementRectResponse,
|
||||
NewSessionResponse,
|
||||
ValueResponse,
|
||||
TimeoutsResponse,
|
||||
WindowRectResponse,
|
||||
Cookie,
|
||||
Nullable};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn test(resp: WebDriverResponse, expected_str: &str) {
|
||||
let data = resp.to_json_string();
|
||||
|
@ -233,24 +200,43 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_cookie() {
|
||||
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": "/",
|
||||
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": "/",
|
||||
"domain": null, "expiry": null, "secure": true, "httpOnly": false}]}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_element_rect() {
|
||||
let resp = WebDriverResponse::ElementRect(ElementRectResponse::new(
|
||||
let resp = WebDriverResponse::ElementRect(RectResponse::new(
|
||||
0f64, 1f64, 2f64, 3f64));
|
||||
let expected = r#"{"value": {"x": 0.0, "y": 1.0, "width": 2.0, "height": 3.0}}"#;
|
||||
test(resp, expected);
|
||||
|
@ -258,13 +244,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_window_rect() {
|
||||
let resp = WebDriverResponse::WindowRect(WindowRectResponse {
|
||||
x: 0i64,
|
||||
y: 1i64,
|
||||
width: 2u64,
|
||||
height: 3u64,
|
||||
let resp = WebDriverResponse::WindowRect(RectResponse {
|
||||
x: 0f64,
|
||||
y: 1f64,
|
||||
width: 2f64,
|
||||
height: 3f64,
|
||||
});
|
||||
let expected = r#"{"value": {"x": 0, "y": 1, "width": 2, "height": 3}}"#;
|
||||
let expected = r#"{"value": {"x": 0.0, "y": 1.0, "width": 2.0, "height": 3.0}}"#;
|
||||
test(resp, expected);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче