1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
use std::fmt;
use std::cell::RefMut;

use Header;
use cookie::Delta;

#[doc(hidden)] pub use self::key::*;
pub use cookie::{Cookie, CookieJar, SameSite};

/// Types and methods to manage a `Key` when private cookies are enabled.
#[cfg(feature = "private-cookies")]
mod key {
    pub use cookie::Key;
}

/// Types and methods to manage a `Key` when private cookies are disabled.
#[cfg(not(feature = "private-cookies"))]
mod key {
    #[derive(Copy, Clone)]
    pub struct Key;

    impl Key {
        pub fn generate() -> Self { Key }
        pub fn from_master(_bytes: &[u8]) -> Self { Key }
    }
}

/// Collection of one or more HTTP cookies.
///
/// The `Cookies` type allows for retrieval of cookies from an incoming request
/// as well as modifications to cookies to be reflected by Rocket on outgoing
/// responses. `Cookies` is a smart-pointer; it internally borrows and refers to
/// the collection of cookies active during a request's life-cycle.
///
/// # Usage
///
/// A type of `Cookies` can be retrieved via its `FromRequest` implementation as
/// a request guard or via the [`Request::cookies()`] method. Individual cookies
/// can be retrieved via the [`get()`] and [`get_private()`] methods. Cookies
/// can be added or removed via the [`add()`], [`add_private()`], [`remove()`],
/// and [`remove_private()`] methods.
///
/// [`Request::cookies()`]: ::rocket::Request::cookies()
/// [`get()`]: #method.get
/// [`get_private()`]: #method.get_private
/// [`add()`]: #method.add
/// [`add_private()`]: #method.add_private
/// [`remove()`]: #method.remove
/// [`remove_private()`]: #method.remove_private
///
/// ## Examples
///
/// The following short snippet shows `Cookies` being used as a request guard in
/// a handler to retrieve the value of a "message" cookie.
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro)]
/// # #[macro_use] extern crate rocket;
/// use rocket::http::Cookies;
///
/// #[get("/message")]
/// fn message(cookies: Cookies) -> Option<String> {
///     cookies.get("message").map(|c| format!("Message: {}", c.value()))
/// }
/// # fn main() {  }
/// ```
///
/// The following snippet shows `Cookies` being retrieved from a `Request` in a
/// custom request guard implementation for `User`. A [private cookie]
/// containing a user's ID is retrieved. If the cookie exists and the ID parses
/// as an integer, a `User` structure is validated. Otherwise, the guard
/// forwards.
///
/// [private cookie]: Cookies::add_private()
///
/// ```rust
/// # #![feature(proc_macro_hygiene, decl_macro, never_type)]
/// # #[macro_use] extern crate rocket;
/// #
/// use rocket::http::Status;
/// use rocket::outcome::IntoOutcome;
/// use rocket::request::{self, Request, FromRequest};
///
/// // In practice, we'd probably fetch the user from the database.
/// struct User(usize);
///
/// impl<'a, 'r> FromRequest<'a, 'r> for User {
///     type Error = !;
///
///     fn from_request(request: &'a Request<'r>) -> request::Outcome<User, !> {
///         request.cookies()
///             .get_private("user_id")
///             .and_then(|cookie| cookie.value().parse().ok())
///             .map(|id| User(id))
///             .or_forward(())
///     }
/// }
/// # fn main() { }
/// ```
///
/// # Private Cookies
///
/// _Private_ cookies are just like regular cookies except that they are
/// encrypted using authenticated encryption, a form of encryption which
/// simultaneously provides confidentiality, integrity, and authenticity. This
/// means that private cookies cannot be inspected, tampered with, or
/// manufactured by clients. If you prefer, you can think of private cookies as
/// being signed and encrypted.
///
/// Private cookies can be retrieved, added, and removed from a `Cookies`
/// collection via the [`get_private()`], [`add_private()`], and
/// [`remove_private()`] methods.
///
/// ## Encryption Key
///
/// To encrypt private cookies, Rocket uses the 256-bit key specified in the
/// `secret_key` configuration parameter. If one is not specified, Rocket will
/// automatically generate a fresh key. Note, however, that a private cookie can
/// only be decrypted with the same key with which it was encrypted. As such, it
/// is important to set a `secret_key` configuration parameter when using
/// private cookies so that cookies decrypt properly after an application
/// restart. Rocket will emit a warning if an application is run in production
/// mode without a configured `secret_key`.
///
/// Generating a string suitable for use as a `secret_key` configuration value
/// is usually done through tools like `openssl`. Using `openssl`, for instance,
/// a 256-bit base64 key can be generated with the command `openssl rand -base64
/// 32`.
pub enum Cookies<'a> {
    #[doc(hidden)]
    Jarred(RefMut<'a, CookieJar>, &'a Key),
    #[doc(hidden)]
    Empty(CookieJar)
}

impl<'a> Cookies<'a> {
    /// WARNING: This is unstable! Do not use this method outside of Rocket!
    #[inline]
    #[doc(hidden)]
    pub fn new(jar: RefMut<'a, CookieJar>, key: &'a Key) -> Cookies<'a> {
        Cookies::Jarred(jar, key)
    }

    /// WARNING: This is unstable! Do not use this method outside of Rocket!
    #[doc(hidden)]
    #[inline(always)]
    pub fn empty() -> Cookies<'static> {
        Cookies::Empty(CookieJar::new())
    }

    /// WARNING: This is unstable! Do not use this method outside of Rocket!
    #[doc(hidden)]
    #[inline(always)]
    pub fn parse_cookie(cookie_str: &str) -> Option<Cookie<'static>> {
        Cookie::parse_encoded(cookie_str).map(|c| c.into_owned()).ok()
    }

    /// Adds an original `cookie` to this collection.
    /// WARNING: This is unstable! Do not use this method outside of Rocket!
    #[inline]
    #[doc(hidden)]
    pub fn add_original(&mut self, cookie: Cookie<'static>) {
        if let Cookies::Jarred(ref mut jar, _) = *self {
            jar.add_original(cookie)
        }
    }

    /// Returns a reference to the `Cookie` inside this container with the name
    /// `name`. If no such cookie exists, returns `None`.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate rocket;
    /// use rocket::http::Cookies;
    ///
    /// fn handler(cookies: Cookies) {
    ///     let cookie = cookies.get("name");
    /// }
    /// ```
    pub fn get(&self, name: &str) -> Option<&Cookie<'static>> {
        match *self {
            Cookies::Jarred(ref jar, _) => jar.get(name),
            Cookies::Empty(_) => None
        }
    }

    /// Adds `cookie` to this collection.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate rocket;
    /// use rocket::http::{Cookie, Cookies};
    ///
    /// fn handler(mut cookies: Cookies) {
    ///     cookies.add(Cookie::new("name", "value"));
    ///
    ///     let cookie = Cookie::build("name", "value")
    ///         .path("/")
    ///         .secure(true)
    ///         .finish();
    ///
    ///     cookies.add(cookie);
    /// }
    /// ```
    pub fn add(&mut self, cookie: Cookie<'static>) {
        if let Cookies::Jarred(ref mut jar, _) = *self {
            jar.add(cookie)
        }
    }

    /// Removes `cookie` from this collection and generates a "removal" cookies
    /// to send to the client on response. For correctness, `cookie` must
    /// contain the same `path` and `domain` as the cookie that was initially
    /// set. Failure to provide the initial `path` and `domain` will result in
    /// cookies that are not properly removed.
    ///
    /// A "removal" cookie is a cookie that has the same name as the original
    /// cookie but has an empty value, a max-age of 0, and an expiration date
    /// far in the past.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate rocket;
    /// use rocket::http::{Cookie, Cookies};
    ///
    /// fn handler(mut cookies: Cookies) {
    ///     cookies.remove(Cookie::named("name"));
    /// }
    /// ```
    pub fn remove(&mut self, cookie: Cookie<'static>) {
        if let Cookies::Jarred(ref mut jar, _) = *self {
            jar.remove(cookie)
        }
    }

    /// Removes all delta cookies.
    /// WARNING: This is unstable! Do not use this method outside of Rocket!
    #[inline]
    #[doc(hidden)]
    pub fn reset_delta(&mut self) {
        match *self {
            Cookies::Jarred(ref mut jar, _) => jar.reset_delta(),
            Cookies::Empty(ref mut jar) => jar.reset_delta()
        }
    }

    /// Returns an iterator over all of the cookies present in this collection.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate rocket;
    /// use rocket::http::Cookies;
    ///
    /// fn handler(cookies: Cookies) {
    ///     for c in cookies.iter() {
    ///         println!("Name: '{}', Value: '{}'", c.name(), c.value());
    ///     }
    /// }
    /// ```
    pub fn iter(&self) -> impl Iterator<Item=&Cookie<'static>> {
        match *self {
            Cookies::Jarred(ref jar, _) => jar.iter(),
            Cookies::Empty(ref jar) => jar.iter()
        }
    }

    /// WARNING: This is unstable! Do not use this method outside of Rocket!
    #[inline]
    #[doc(hidden)]
    pub fn delta(&self) -> Delta {
        match *self {
            Cookies::Jarred(ref jar, _) => jar.delta(),
            Cookies::Empty(ref jar) => jar.delta()
        }
    }
}

#[cfg(feature = "private-cookies")]
impl<'a> Cookies<'a> {
    /// Returns a reference to the `Cookie` inside this collection 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.
    ///
    /// This method is only available when the `private-cookies` feature is
    /// enabled.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate rocket;
    /// use rocket::http::Cookies;
    ///
    /// fn handler(mut cookies: Cookies) {
    ///     let cookie = cookies.get_private("name");
    /// }
    /// ```
    pub fn get_private(&mut self, name: &str) -> Option<Cookie<'static>> {
        match *self {
            Cookies::Jarred(ref mut jar, key) => jar.private(key).get(name),
            Cookies::Empty(_) => None
        }
    }

    /// Adds `cookie` to the collection. The cookie's value is encrypted with
    /// authenticated encryption assuring confidentiality, integrity, and
    /// authenticity. The cookie can later be retrieved using
    /// [`get_private`](#method.get_private) and removed using
    /// [`remove_private`](#method.remove_private).
    ///
    /// Unless a value is supplied for the given key, the following defaults are
    /// set on `cookie` before being added to `self`:
    ///
    ///    * `path`: `"/"`
    ///    * `SameSite`: `Strict`
    ///    * `HttpOnly`: `true`
    ///    * `Expires`: 1 week from now
    ///
    /// These defaults ensure maximum usability and security. For additional
    /// security, you may wish to set the `secure` flag.
    ///
    /// This method is only available when the `private-cookies` feature is
    /// enabled.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate rocket;
    /// use rocket::http::{Cookie, Cookies};
    ///
    /// fn handler(mut cookies: Cookies) {
    ///     cookies.add_private(Cookie::new("name", "value"));
    /// }
    /// ```
    pub fn add_private(&mut self, mut cookie: Cookie<'static>) {
        if let Cookies::Jarred(ref mut jar, key) = *self {
            Cookies::set_private_defaults(&mut cookie);
            jar.private(key).add(cookie)
        }
    }

    /// Adds an original, private `cookie` to the collection.
    /// WARNING: This is unstable! Do not use this method outside of Rocket!
    #[doc(hidden)]
    pub fn add_original_private(&mut self, mut cookie: Cookie<'static>) {
        if let Cookies::Jarred(ref mut jar, key) = *self {
            Cookies::set_private_defaults(&mut cookie);
            jar.private(key).add_original(cookie)
        }
    }

    /// For each property mentioned below, this method checks
    /// if there is a provided value and if there is none, sets
    /// a default value.
    /// Default values are:
    ///
    ///    * `path`: `"/"`
    ///    * `SameSite`: `Strict`
    ///    * `HttpOnly`: `true`
    ///    * `Expires`: 1 week from now
    ///
    fn set_private_defaults(cookie: &mut Cookie<'static>) {
        if cookie.path().is_none() {
            cookie.set_path("/");
        }

        if cookie.http_only().is_none() {
            cookie.set_http_only(true);
        }

        if cookie.expires().is_none() {
            cookie.set_expires(::time::now() + ::time::Duration::weeks(1));
        }

        if cookie.same_site().is_none() {
            cookie.set_same_site(SameSite::Strict);
        }
    }

    /// Removes the private `cookie` from the collection.
    ///
    /// For correct removal, the passed in `cookie` must contain the same `path`
    /// and `domain` as the cookie that was initially set. If a path is not set
    /// on `cookie`, the `"/"` path will automatically be set.
    ///
    /// This method is only available when the `private-cookies` feature is
    /// enabled.
    ///
    /// # Example
    ///
    /// ```rust
    /// # extern crate rocket;
    /// use rocket::http::{Cookie, Cookies};
    ///
    /// fn handler(mut cookies: Cookies) {
    ///     cookies.remove_private(Cookie::named("name"));
    /// }
    /// ```
    pub fn remove_private(&mut self, mut cookie: Cookie<'static>) {
        if let Cookies::Jarred(ref mut jar, key) = *self {
            if cookie.path().is_none() {
                cookie.set_path("/");
            }

            jar.private(key).remove(cookie)
        }
    }
}

impl<'a> fmt::Debug for Cookies<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Cookies::Jarred(ref jar, _) => jar.fmt(f),
            Cookies::Empty(ref jar) => jar.fmt(f)
        }
    }
}

impl<'c> From<Cookie<'c>> for Header<'static> {
    fn from(cookie: Cookie) -> Header<'static> {
        Header::new("Set-Cookie", cookie.encoded().to_string())
    }
}

impl<'a, 'c> From<&'a Cookie<'c>> for Header<'static> {
    fn from(cookie: &Cookie) -> Header<'static> {
        Header::new("Set-Cookie", cookie.encoded().to_string())
    }
}