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
extern crate url; extern crate iron; use self::url::Url; use std::ascii::AsciiExt; /// A struct which implements the concept 'Web Origin' as defined in /// https://tools.ietf.org/html/rfc6454. /// /// This implementation only considers hierarchical URLs and null. /// /// The rationale behind skipping random id:s is that any such random origin should /// never be equal to another random origin. /// This has the implication that it's unneccesary to compare them to /// each other and we might as well return parse error and handle that /// case separately. /// #[derive(PartialEq, Eq, Hash, Debug, Clone)] pub enum Origin { /// The `Null` origin, indicating that a resource lacks a proper origin. /// This value is commonly used in the Origin header to indicate that an origin couldn't be /// deduced or was deliberitely left out. This value is set instead of omitting the Origin // header, since ommiting the header could just signal a client unaware of the origin concept. Null, /// The common origin, formed from a `(schem, host, port)` triple. Triple { /// Lower-case scheme scheme: String, /// Host with all ascii chars lowercased and punycoded host: String, /// The explicit port or scheme default port if not explicity set port: u16, }, } impl Origin { /// Parses the given string as an origin. /// #Errors /// Errors are returned if /// /// * The argument cannot be parsed as an URL /// * There's no host in the URL /// * The URL scheme is not supported by the URL parser (rust-url) /// * If there is no known default port for the scheme /// /// #Examples /// ``` /// use corsware::Origin; /// let o1 = Origin::parse("http://exämple.com"); /// let o2 = Origin::parse("hTtP://user:password@eXämpLe.cOm:80/a/path.html"); /// assert_eq!(o1, o2); /// ``` pub fn parse(s: &str) -> Result<Origin, String> { match Url::parse(s) { Err(_) => Err(format!("Could not be parsed as URL: '{}'", s)), Ok(url) => { // - 1. If the URI does not use a hierarchical element as a naming // - authority (see [RFC3986], Section 3.2) or if the URI is not an // - absolute URI, then generate a fresh globally unique identifier // - and return that value. // // From https://hyper.rs/hyper/0.8.0/hyper/struct.Url.html#method.host: // host(): If the URL is in a relative scheme, return its structured host. match url.host_str() { None => Err(format!("No host in URL '{}'", url)), Some(host_str) => { // - 2. Let uri-scheme be the scheme component of the URI, converted to // - lowercase. let uri_scheme = url.scheme().to_owned().to_lowercase(); // 4. If uri-scheme is "file", the implementation MAY return an // - implementation-defined value... // NOTE: file scheme not supported, would have bailed already // - 5. Let uri-host be the host component of the URI, converted to lower // - case (using the i;ascii-casemap collation defined in [RFC4790]). // // regarding i;ascii-casemap: // - Its equality, ordering, and substring operations are as for i;octet, // - except that at first, the lower-case letters (octet values 97-122) in // - each input string are changed to upper case (octet values 65-90). let uri_host = host_str.to_ascii_lowercase(); // 6. If there is no port component of the URI: // 1. Let uri-port be the default port for the protocol given by // uri-scheme. // Otherwise: // 2. Let uri-port be the port component of the URI. let uri_port = url.port_or_known_default(); // - 3. If the implementation doesn't support the protocol given by uri- // - scheme, then generate a fresh globally unique identifier and // - return that value. // // We support all schemes wich have a default port known by hyper match uri_port { None => Err(format!("Unsupported URL scheme '{}'", uri_scheme)), Some(port) => { // 7. Return the triple (uri-scheme, uri-host, uri-port). Ok(Origin::Triple { scheme: uri_scheme, host: uri_host, port, }) } } } } } } } /// Parses the given string as an origin. Allows for /// "null" to be parsed as Origin::Null and should only /// be used in cases where getting Null origin is not a /// security problem. Remember that Null origin should be treated /// as "Origin could not be deduced" /// /// #Examples /// ``` /// use corsware::Origin; /// let o1 = Origin::parse_allow_null("null"); /// assert_eq!(o1, Ok(Origin::Null)); /// let o2 = Origin::parse_allow_null("http://www.a.com"); /// assert_eq!(o2, Ok(Origin::Triple { /// scheme: "http".to_owned(), /// host: "www.a.com".to_owned(), /// port: 80u16 /// })); /// ``` pub fn parse_allow_null(s: &str) -> Result<Origin, String> { match s { "null" => Ok(Origin::Null), _ => Origin::parse(s), } } /// Returns the scheme of the origin in lower case. /// #Example /// ``` /// use corsware::Origin; /// assert_eq!(Origin::parse("hTtP://a.com").unwrap().scheme(), &"http".to_owned()); /// ``` pub fn scheme(&self) -> &String { match *self { Origin::Null => panic!("Null Origin has no scheme"), Origin::Triple { ref scheme, .. } => scheme, } } /// Returns the host of the origin in ascii lower case. /// #Example /// ``` /// use corsware::Origin; /// assert_eq!(Origin::parse("ftp://Aö.coM").unwrap().host(), &"xn--a-1ga.com".to_owned()); /// ``` pub fn host(&self) -> &String { match *self { Origin::Null => panic!("Null Origin has no host"), Origin::Triple { ref host, .. } => host, } } /// Returns the port of the origin. Will return the default /// port if not set explicitly /// #Example /// ``` /// use corsware::Origin; /// assert_eq!(Origin::parse("ftp://a.com").unwrap().port(), 21); /// ``` pub fn port(&self) -> u16 { match *self { Origin::Null => panic!("Null Origin has no port"), Origin::Triple { ref port, .. } => *port, } } } #[cfg(test)] mod tests;