1 // Deprecated in 1.26, needed until our minimum version is >=1.23. 2 #[allow(unused, deprecated)] 3 use std::ascii::AsciiExt; 4 use std::fmt; 5 use std::hash::{Hash, Hasher}; 6 use std::str::FromStr; 7 8 use bytes::Bytes; 9 10 use byte_str::ByteStr; 11 use convert::HttpTryFrom; 12 use super::{ErrorKind, InvalidUri, InvalidUriBytes}; 13 14 /// Represents the scheme component of a URI 15 #[derive(Clone)] 16 pub struct Scheme { 17 pub(super) inner: Scheme2, 18 } 19 20 #[derive(Clone, Debug)] 21 pub(super) enum Scheme2<T = Box<ByteStr>> { 22 None, 23 Standard(Protocol), 24 Other(T), 25 } 26 27 #[derive(Copy, Clone, Debug)] 28 pub(super) enum Protocol { 29 Http, 30 Https, 31 } 32 33 impl Scheme { 34 /// HTTP protocol scheme 35 pub const HTTP: Scheme = Scheme { 36 inner: Scheme2::Standard(Protocol::Http), 37 }; 38 39 /// HTTP protocol over TLS. 40 pub const HTTPS: Scheme = Scheme { 41 inner: Scheme2::Standard(Protocol::Https), 42 }; 43 44 /// Attempt to convert a `Scheme` from `Bytes` 45 /// 46 /// This function will be replaced by a `TryFrom` implementation once the 47 /// trait lands in stable. 48 /// 49 /// # Examples 50 /// 51 /// ``` 52 /// # extern crate http; 53 /// # use http::uri::*; 54 /// extern crate bytes; 55 /// 56 /// use bytes::Bytes; 57 /// 58 /// # pub fn main() { 59 /// let bytes = Bytes::from("http"); 60 /// let scheme = Scheme::from_shared(bytes).unwrap(); 61 /// 62 /// assert_eq!(scheme.as_str(), "http"); 63 /// # } 64 /// ``` from_shared(s: Bytes) -> Result<Self, InvalidUriBytes>65 pub fn from_shared(s: Bytes) -> Result<Self, InvalidUriBytes> { 66 use self::Scheme2::*; 67 68 match Scheme2::parse_exact(&s[..]).map_err(InvalidUriBytes)? { 69 None => Err(ErrorKind::InvalidScheme.into()), 70 Standard(p) => Ok(Standard(p).into()), 71 Other(_) => { 72 let b = unsafe { ByteStr::from_utf8_unchecked(s) }; 73 Ok(Other(Box::new(b)).into()) 74 } 75 } 76 } 77 empty() -> Self78 pub(super) fn empty() -> Self { 79 Scheme { 80 inner: Scheme2::None, 81 } 82 } 83 84 /// Return a str representation of the scheme 85 /// 86 /// # Examples 87 /// 88 /// ``` 89 /// # use http::uri::*; 90 /// let scheme: Scheme = "http".parse().unwrap(); 91 /// assert_eq!(scheme.as_str(), "http"); 92 /// ``` 93 #[inline] as_str(&self) -> &str94 pub fn as_str(&self) -> &str { 95 use self::Scheme2::*; 96 use self::Protocol::*; 97 98 match self.inner { 99 Standard(Http) => "http", 100 Standard(Https) => "https", 101 Other(ref v) => &v[..], 102 None => unreachable!(), 103 } 104 } 105 106 /// Converts this `Scheme` back to a sequence of bytes 107 #[inline] into_bytes(self) -> Bytes108 pub fn into_bytes(self) -> Bytes { 109 self.into() 110 } 111 } 112 113 impl HttpTryFrom<Bytes> for Scheme { 114 type Error = InvalidUriBytes; 115 #[inline] try_from(bytes: Bytes) -> Result<Self, Self::Error>116 fn try_from(bytes: Bytes) -> Result<Self, Self::Error> { 117 Scheme::from_shared(bytes) 118 } 119 } 120 121 impl<'a> HttpTryFrom<&'a [u8]> for Scheme { 122 type Error = InvalidUri; 123 #[inline] try_from(s: &'a [u8]) -> Result<Self, Self::Error>124 fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> { 125 use self::Scheme2::*; 126 127 match Scheme2::parse_exact(s)? { 128 None => Err(ErrorKind::InvalidScheme.into()), 129 Standard(p) => Ok(Standard(p).into()), 130 Other(_) => { 131 // Unsafe: parse_exact already checks for a strict subset of UTF-8 132 Ok(Other(Box::new(unsafe { 133 ByteStr::from_utf8_unchecked(s.into()) 134 })).into()) 135 } 136 } 137 } 138 } 139 140 impl<'a> HttpTryFrom<&'a str> for Scheme { 141 type Error = InvalidUri; 142 #[inline] try_from(s: &'a str) -> Result<Self, Self::Error>143 fn try_from(s: &'a str) -> Result<Self, Self::Error> { 144 HttpTryFrom::try_from(s.as_bytes()) 145 } 146 } 147 148 impl FromStr for Scheme { 149 type Err = InvalidUri; 150 from_str(s: &str) -> Result<Self, Self::Err>151 fn from_str(s: &str) -> Result<Self, Self::Err> { 152 HttpTryFrom::try_from(s) 153 } 154 } 155 156 impl From<Scheme> for Bytes { 157 #[inline] from(src: Scheme) -> Self158 fn from(src: Scheme) -> Self { 159 use self::Scheme2::*; 160 use self::Protocol::*; 161 162 match src.inner { 163 None => Bytes::new(), 164 Standard(Http) => Bytes::from_static(b"http"), 165 Standard(Https) => Bytes::from_static(b"https"), 166 Other(v) => (*v).into(), 167 } 168 } 169 } 170 171 impl fmt::Debug for Scheme { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result172 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 173 fmt::Debug::fmt(self.as_str(), f) 174 } 175 } 176 177 impl fmt::Display for Scheme { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result178 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 179 f.write_str(self.as_str()) 180 } 181 } 182 183 impl AsRef<str> for Scheme { 184 #[inline] as_ref(&self) -> &str185 fn as_ref(&self) -> &str { 186 self.as_str() 187 } 188 } 189 190 impl PartialEq for Scheme { eq(&self, other: &Scheme) -> bool191 fn eq(&self, other: &Scheme) -> bool { 192 use self::Protocol::*; 193 use self::Scheme2::*; 194 195 match (&self.inner, &other.inner) { 196 (&Standard(Http), &Standard(Http)) => true, 197 (&Standard(Https), &Standard(Https)) => true, 198 (&Other(ref a), &Other(ref b)) => a.eq_ignore_ascii_case(b), 199 (&None, _) | (_, &None) => unreachable!(), 200 _ => false, 201 } 202 } 203 } 204 205 impl Eq for Scheme {} 206 207 /// Case-insensitive equality 208 /// 209 /// # Examples 210 /// 211 /// ``` 212 /// # use http::uri::Scheme; 213 /// let scheme: Scheme = "HTTP".parse().unwrap(); 214 /// assert_eq!(scheme, *"http"); 215 /// ``` 216 impl PartialEq<str> for Scheme { eq(&self, other: &str) -> bool217 fn eq(&self, other: &str) -> bool { 218 self.as_str().eq_ignore_ascii_case(other) 219 } 220 } 221 222 /// Case-insensitive equality 223 impl PartialEq<Scheme> for str { eq(&self, other: &Scheme) -> bool224 fn eq(&self, other: &Scheme) -> bool { 225 other == self 226 } 227 } 228 229 /// Case-insensitive hashing 230 impl Hash for Scheme { hash<H>(&self, state: &mut H) where H: Hasher231 fn hash<H>(&self, state: &mut H) where H: Hasher { 232 match self.inner { 233 Scheme2::None => (), 234 Scheme2::Standard(Protocol::Http) => state.write_u8(1), 235 Scheme2::Standard(Protocol::Https) => state.write_u8(2), 236 Scheme2::Other(ref other) => { 237 other.len().hash(state); 238 for &b in other.as_bytes() { 239 state.write_u8(b.to_ascii_lowercase()); 240 } 241 } 242 } 243 } 244 } 245 246 impl<T> Scheme2<T> { is_none(&self) -> bool247 pub(super) fn is_none(&self) -> bool { 248 match *self { 249 Scheme2::None => true, 250 _ => false, 251 } 252 } 253 } 254 255 // Require the scheme to not be too long in order to enable further 256 // optimizations later. 257 const MAX_SCHEME_LEN: usize = 64; 258 259 // scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) 260 // 261 const SCHEME_CHARS: [u8; 256] = [ 262 // 0 1 2 3 4 5 6 7 8 9 263 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x 264 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x 265 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x 266 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3x 267 0, 0, 0, b'+', 0, b'-', b'.', 0, b'0', b'1', // 4x 268 b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', 0, // 5x 269 0, 0, 0, 0, 0, b'A', b'B', b'C', b'D', b'E', // 6x 270 b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', // 7x 271 b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', // 8x 272 b'Z', 0, 0, 0, 0, 0, 0, b'a', b'b', b'c', // 9x 273 b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x 274 b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x 275 b'x', b'y', b'z', 0, 0, 0, b'~', 0, 0, 0, // 12x 276 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x 277 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x 278 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x 279 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x 280 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x 281 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x 282 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x 283 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x 284 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x 285 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x 286 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x 287 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x 288 0, 0, 0, 0, 0, 0 // 25x 289 ]; 290 291 impl Scheme2<usize> { parse_exact(s: &[u8]) -> Result<Scheme2<()>, InvalidUri>292 fn parse_exact(s: &[u8]) -> Result<Scheme2<()>, InvalidUri> { 293 match s { 294 b"http" => Ok(Protocol::Http.into()), 295 b"https" => Ok(Protocol::Https.into()), 296 _ => { 297 if s.len() > MAX_SCHEME_LEN { 298 return Err(ErrorKind::SchemeTooLong.into()); 299 } 300 301 for &b in s { 302 match SCHEME_CHARS[b as usize] { 303 b':' => { 304 // Don't want :// here 305 return Err(ErrorKind::InvalidScheme.into()); 306 } 307 0 => { 308 return Err(ErrorKind::InvalidScheme.into()); 309 } 310 _ => {} 311 } 312 } 313 314 Ok(Scheme2::Other(())) 315 } 316 } 317 } 318 parse(s: &[u8]) -> Result<Scheme2<usize>, InvalidUri>319 pub(super) fn parse(s: &[u8]) -> Result<Scheme2<usize>, InvalidUri> { 320 if s.len() >= 7 { 321 // Check for HTTP 322 if s[..7].eq_ignore_ascii_case(b"http://") { 323 // Prefix will be striped 324 return Ok(Protocol::Http.into()); 325 } 326 } 327 328 if s.len() >= 8 { 329 // Check for HTTPs 330 if s[..8].eq_ignore_ascii_case(b"https://") { 331 return Ok(Protocol::Https.into()); 332 } 333 } 334 335 if s.len() > 3 { 336 for i in 0..s.len() { 337 let b = s[i]; 338 339 if i == MAX_SCHEME_LEN { 340 return Err(ErrorKind::SchemeTooLong.into()); 341 } 342 343 match SCHEME_CHARS[b as usize] { 344 b':' => { 345 // Not enough data remaining 346 if s.len() < i + 3 { 347 break; 348 } 349 350 // Not a scheme 351 if &s[i+1..i+3] != b"//" { 352 break; 353 } 354 355 // Return scheme 356 return Ok(Scheme2::Other(i)); 357 } 358 // Invald scheme character, abort 359 0 => break, 360 _ => {} 361 } 362 } 363 } 364 365 Ok(Scheme2::None) 366 } 367 } 368 369 impl Protocol { len(&self) -> usize370 pub(super) fn len(&self) -> usize { 371 match *self { 372 Protocol::Http => 4, 373 Protocol::Https => 5, 374 } 375 } 376 } 377 378 impl<T> From<Protocol> for Scheme2<T> { from(src: Protocol) -> Self379 fn from(src: Protocol) -> Self { 380 Scheme2::Standard(src) 381 } 382 } 383 384 #[doc(hidden)] 385 impl From<Scheme2> for Scheme { from(src: Scheme2) -> Self386 fn from(src: Scheme2) -> Self { 387 Scheme { inner: src } 388 } 389 } 390