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