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
// Copyright 2016 The rust-url developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#[cfg(feature = "heapsize")] use heapsize::HeapSizeOf;
use host::Host;
use idna::domain_to_unicode;
use parser::default_port;
use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use Url;

pub fn url_origin(url: &Url) -> Origin {
    let scheme = url.scheme();
    match scheme {
        "blob" => {
            let result = Url::parse(url.path());
            match result {
                Ok(ref url) => url_origin(url),
                Err(_)  => Origin::new_opaque()
            }
        },
        "ftp" | "gopher" | "http" | "https" | "ws" | "wss" => {
            Origin::Tuple(scheme.to_owned(), url.host().unwrap().to_owned(),
                url.port_or_known_default().unwrap())
        },
        // TODO: Figure out what to do if the scheme is a file
        "file" => Origin::new_opaque(),
        _ => Origin::new_opaque()
    }
}

/// The origin of an URL
///
/// Two URLs with the same origin are considered
/// to originate from the same entity and can therefore trust
/// each other.
///
/// The origin is determined based on the scheme as follows:
///
/// - If the scheme is "blob" the origin is the origin of the
///   URL contained in the path component. If parsing fails,
///   it is an opaque origin.
/// - If the scheme is "ftp", "gopher", "http", "https", "ws", or "wss",
///   then the origin is a tuple of the scheme, host, and port.
/// - If the scheme is anything else, the origin is opaque, meaning
///   the URL does not have the same origin as any other URL.
///
/// For more information see <https://url.spec.whatwg.org/#origin>
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub enum Origin {
    /// A globally unique identifier
    Opaque(OpaqueOrigin),

    /// Consists of the URL's scheme, host and port
    Tuple(String, Host<String>, u16)
}

#[cfg(feature = "heapsize")]
impl HeapSizeOf for Origin {
    fn heap_size_of_children(&self) -> usize {
        match *self {
            Origin::Tuple(ref scheme, ref host, _) => {
                scheme.heap_size_of_children() +
                host.heap_size_of_children()
            },
            _ => 0,
        }
    }
}


impl Origin {
    /// Creates a new opaque origin that is only equal to itself.
    pub fn new_opaque() -> Origin {
        static COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
        Origin::Opaque(OpaqueOrigin(COUNTER.fetch_add(1, Ordering::SeqCst)))
    }

    /// Return whether this origin is a (scheme, host, port) tuple
    /// (as opposed to an opaque origin).
    pub fn is_tuple(&self) -> bool {
        matches!(*self, Origin::Tuple(..))
    }

    /// <https://html.spec.whatwg.org/multipage/#ascii-serialisation-of-an-origin>
    pub fn ascii_serialization(&self) -> String {
        match *self {
            Origin::Opaque(_) => "null".to_owned(),
            Origin::Tuple(ref scheme, ref host, port) => {
                if default_port(scheme) == Some(port) {
                    format!("{}://{}", scheme, host)
                } else {
                    format!("{}://{}:{}", scheme, host, port)
                }
            }
        }
    }

    /// <https://html.spec.whatwg.org/multipage/#unicode-serialisation-of-an-origin>
    pub fn unicode_serialization(&self) -> String {
        match *self {
            Origin::Opaque(_) => "null".to_owned(),
            Origin::Tuple(ref scheme, ref host, port) => {
                let host = match *host {
                    Host::Domain(ref domain) => {
                        let (domain, _errors) = domain_to_unicode(domain);
                        Host::Domain(domain)
                    }
                    _ => host.clone()
                };
                if default_port(scheme) == Some(port) {
                    format!("{}://{}", scheme, host)
                } else {
                    format!("{}://{}:{}", scheme, host, port)
                }
            }
        }
    }
}

/// Opaque identifier for URLs that have file or other schemes
#[derive(Eq, PartialEq, Hash, Clone, Debug)]
pub struct OpaqueOrigin(usize);

#[cfg(feature = "heapsize")]
known_heap_size!(0, OpaqueOrigin);