RPM build fix (reverted CI changes which will need to be un-reverted or made conditional) and vendor Rust dependencies to make builds much faster in any CI system.
This commit is contained in:
85
zeroidc/vendor/http/src/byte_str.rs
vendored
Normal file
85
zeroidc/vendor/http/src/byte_str.rs
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
use bytes::Bytes;
|
||||
|
||||
use std::{ops, str};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub(crate) struct ByteStr {
|
||||
// Invariant: bytes contains valid UTF-8
|
||||
bytes: Bytes,
|
||||
}
|
||||
|
||||
impl ByteStr {
|
||||
#[inline]
|
||||
pub fn new() -> ByteStr {
|
||||
ByteStr {
|
||||
// Invariant: the empty slice is trivially valid UTF-8.
|
||||
bytes: Bytes::new(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub const fn from_static(val: &'static str) -> ByteStr {
|
||||
ByteStr {
|
||||
// Invariant: val is a str so contains vaid UTF-8.
|
||||
bytes: Bytes::from_static(val.as_bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// ## Panics
|
||||
/// In a debug build this will panic if `bytes` is not valid UTF-8.
|
||||
///
|
||||
/// ## Safety
|
||||
/// `bytes` must contain valid UTF-8. In a release build it is undefined
|
||||
/// behaviour to call this with `bytes` that is not valid UTF-8.
|
||||
pub unsafe fn from_utf8_unchecked(bytes: Bytes) -> ByteStr {
|
||||
if cfg!(debug_assertions) {
|
||||
match str::from_utf8(&bytes) {
|
||||
Ok(_) => (),
|
||||
Err(err) => panic!(
|
||||
"ByteStr::from_utf8_unchecked() with invalid bytes; error = {}, bytes = {:?}",
|
||||
err, bytes
|
||||
),
|
||||
}
|
||||
}
|
||||
// Invariant: assumed by the safety requirements of this function.
|
||||
ByteStr { bytes: bytes }
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for ByteStr {
|
||||
type Target = str;
|
||||
|
||||
#[inline]
|
||||
fn deref(&self) -> &str {
|
||||
let b: &[u8] = self.bytes.as_ref();
|
||||
// Safety: the invariant of `bytes` is that it contains valid UTF-8.
|
||||
unsafe { str::from_utf8_unchecked(b) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for ByteStr {
|
||||
#[inline]
|
||||
fn from(src: String) -> ByteStr {
|
||||
ByteStr {
|
||||
// Invariant: src is a String so contains valid UTF-8.
|
||||
bytes: Bytes::from(src),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a str> for ByteStr {
|
||||
#[inline]
|
||||
fn from(src: &'a str) -> ByteStr {
|
||||
ByteStr {
|
||||
// Invariant: src is a str so contains valid UTF-8.
|
||||
bytes: Bytes::copy_from_slice(src.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ByteStr> for Bytes {
|
||||
fn from(src: ByteStr) -> Self {
|
||||
src.bytes
|
||||
}
|
||||
}
|
||||
17
zeroidc/vendor/http/src/convert.rs
vendored
Normal file
17
zeroidc/vendor/http/src/convert.rs
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
macro_rules! if_downcast_into {
|
||||
($in_ty:ty, $out_ty:ty, $val:ident, $body:expr) => ({
|
||||
if std::any::TypeId::of::<$in_ty>() == std::any::TypeId::of::<$out_ty>() {
|
||||
// Store the value in an `Option` so we can `take`
|
||||
// it after casting to `&mut dyn Any`.
|
||||
let mut slot = Some($val);
|
||||
// Re-write the `$val` ident with the downcasted value.
|
||||
let $val = (&mut slot as &mut dyn std::any::Any)
|
||||
.downcast_mut::<Option<$out_ty>>()
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap();
|
||||
// Run the $body in scope of the replaced val.
|
||||
$body
|
||||
}
|
||||
})
|
||||
}
|
||||
149
zeroidc/vendor/http/src/error.rs
vendored
Normal file
149
zeroidc/vendor/http/src/error.rs
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::result;
|
||||
|
||||
use crate::header;
|
||||
use crate::method;
|
||||
use crate::status;
|
||||
use crate::uri;
|
||||
|
||||
/// A generic "error" for HTTP connections
|
||||
///
|
||||
/// This error type is less specific than the error returned from other
|
||||
/// functions in this crate, but all other errors can be converted to this
|
||||
/// error. Consumers of this crate can typically consume and work with this form
|
||||
/// of error for conversions with the `?` operator.
|
||||
pub struct Error {
|
||||
inner: ErrorKind,
|
||||
}
|
||||
|
||||
/// A `Result` typedef to use with the `http::Error` type
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
enum ErrorKind {
|
||||
StatusCode(status::InvalidStatusCode),
|
||||
Method(method::InvalidMethod),
|
||||
Uri(uri::InvalidUri),
|
||||
UriParts(uri::InvalidUriParts),
|
||||
HeaderName(header::InvalidHeaderName),
|
||||
HeaderValue(header::InvalidHeaderValue),
|
||||
}
|
||||
|
||||
impl fmt::Debug for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_tuple("http::Error")
|
||||
// Skip the noise of the ErrorKind enum
|
||||
.field(&self.get_ref())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self.get_ref(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Return true if the underlying error has the same type as T.
|
||||
pub fn is<T: error::Error + 'static>(&self) -> bool {
|
||||
self.get_ref().is::<T>()
|
||||
}
|
||||
|
||||
/// Return a reference to the lower level, inner error.
|
||||
pub fn get_ref(&self) -> &(dyn error::Error + 'static) {
|
||||
use self::ErrorKind::*;
|
||||
|
||||
match self.inner {
|
||||
StatusCode(ref e) => e,
|
||||
Method(ref e) => e,
|
||||
Uri(ref e) => e,
|
||||
UriParts(ref e) => e,
|
||||
HeaderName(ref e) => e,
|
||||
HeaderValue(ref e) => e,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for Error {
|
||||
// Return any available cause from the inner error. Note the inner error is
|
||||
// not itself the cause.
|
||||
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
|
||||
self.get_ref().source()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<status::InvalidStatusCode> for Error {
|
||||
fn from(err: status::InvalidStatusCode) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::StatusCode(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<method::InvalidMethod> for Error {
|
||||
fn from(err: method::InvalidMethod) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::Method(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uri::InvalidUri> for Error {
|
||||
fn from(err: uri::InvalidUri) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::Uri(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uri::InvalidUriParts> for Error {
|
||||
fn from(err: uri::InvalidUriParts) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::UriParts(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<header::InvalidHeaderName> for Error {
|
||||
fn from(err: header::InvalidHeaderName) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::HeaderName(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<header::InvalidHeaderValue> for Error {
|
||||
fn from(err: header::InvalidHeaderValue) -> Error {
|
||||
Error {
|
||||
inner: ErrorKind::HeaderValue(err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<std::convert::Infallible> for Error {
|
||||
fn from(err: std::convert::Infallible) -> Error {
|
||||
match err {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn inner_error_is_invalid_status_code() {
|
||||
if let Err(e) = status::StatusCode::from_u16(6666) {
|
||||
let err: Error = e.into();
|
||||
let ie = err.get_ref();
|
||||
assert!(!ie.is::<header::InvalidHeaderValue>());
|
||||
assert!(ie.is::<status::InvalidStatusCode>());
|
||||
ie.downcast_ref::<status::InvalidStatusCode>().unwrap();
|
||||
|
||||
assert!(!err.is::<header::InvalidHeaderValue>());
|
||||
assert!(err.is::<status::InvalidStatusCode>());
|
||||
} else {
|
||||
panic!("Bad status allowed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
250
zeroidc/vendor/http/src/extensions.rs
vendored
Normal file
250
zeroidc/vendor/http/src/extensions.rs
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
use std::any::{Any, TypeId};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt;
|
||||
use std::hash::{BuildHasherDefault, Hasher};
|
||||
|
||||
type AnyMap = HashMap<TypeId, Box<dyn Any + Send + Sync>, BuildHasherDefault<IdHasher>>;
|
||||
|
||||
// With TypeIds as keys, there's no need to hash them. They are already hashes
|
||||
// themselves, coming from the compiler. The IdHasher just holds the u64 of
|
||||
// the TypeId, and then returns it, instead of doing any bit fiddling.
|
||||
#[derive(Default)]
|
||||
struct IdHasher(u64);
|
||||
|
||||
impl Hasher for IdHasher {
|
||||
fn write(&mut self, _: &[u8]) {
|
||||
unreachable!("TypeId calls write_u64");
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_u64(&mut self, id: u64) {
|
||||
self.0 = id;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn finish(&self) -> u64 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// A type map of protocol extensions.
|
||||
///
|
||||
/// `Extensions` can be used by `Request` and `Response` to store
|
||||
/// extra data derived from the underlying protocol.
|
||||
#[derive(Default)]
|
||||
pub struct Extensions {
|
||||
// If extensions are never used, no need to carry around an empty HashMap.
|
||||
// That's 3 words. Instead, this is only 1 word.
|
||||
map: Option<Box<AnyMap>>,
|
||||
}
|
||||
|
||||
impl Extensions {
|
||||
/// Create an empty `Extensions`.
|
||||
#[inline]
|
||||
pub fn new() -> Extensions {
|
||||
Extensions { map: None }
|
||||
}
|
||||
|
||||
/// Insert a type into this `Extensions`.
|
||||
///
|
||||
/// If a extension of this type already existed, it will
|
||||
/// be returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// assert!(ext.insert(5i32).is_none());
|
||||
/// assert!(ext.insert(4u8).is_none());
|
||||
/// assert_eq!(ext.insert(9i32), Some(5i32));
|
||||
/// ```
|
||||
pub fn insert<T: Send + Sync + 'static>(&mut self, val: T) -> Option<T> {
|
||||
self.map
|
||||
.get_or_insert_with(|| Box::new(HashMap::default()))
|
||||
.insert(TypeId::of::<T>(), Box::new(val))
|
||||
.and_then(|boxed| {
|
||||
(boxed as Box<dyn Any + 'static>)
|
||||
.downcast()
|
||||
.ok()
|
||||
.map(|boxed| *boxed)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a reference to a type previously inserted on this `Extensions`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// assert!(ext.get::<i32>().is_none());
|
||||
/// ext.insert(5i32);
|
||||
///
|
||||
/// assert_eq!(ext.get::<i32>(), Some(&5i32));
|
||||
/// ```
|
||||
pub fn get<T: Send + Sync + 'static>(&self) -> Option<&T> {
|
||||
self.map
|
||||
.as_ref()
|
||||
.and_then(|map| map.get(&TypeId::of::<T>()))
|
||||
.and_then(|boxed| (&**boxed as &(dyn Any + 'static)).downcast_ref())
|
||||
}
|
||||
|
||||
/// Get a mutable reference to a type previously inserted on this `Extensions`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// ext.insert(String::from("Hello"));
|
||||
/// ext.get_mut::<String>().unwrap().push_str(" World");
|
||||
///
|
||||
/// assert_eq!(ext.get::<String>().unwrap(), "Hello World");
|
||||
/// ```
|
||||
pub fn get_mut<T: Send + Sync + 'static>(&mut self) -> Option<&mut T> {
|
||||
self.map
|
||||
.as_mut()
|
||||
.and_then(|map| map.get_mut(&TypeId::of::<T>()))
|
||||
.and_then(|boxed| (&mut **boxed as &mut (dyn Any + 'static)).downcast_mut())
|
||||
}
|
||||
|
||||
/// Remove a type from this `Extensions`.
|
||||
///
|
||||
/// If a extension of this type existed, it will be returned.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// ext.insert(5i32);
|
||||
/// assert_eq!(ext.remove::<i32>(), Some(5i32));
|
||||
/// assert!(ext.get::<i32>().is_none());
|
||||
/// ```
|
||||
pub fn remove<T: Send + Sync + 'static>(&mut self) -> Option<T> {
|
||||
self.map
|
||||
.as_mut()
|
||||
.and_then(|map| map.remove(&TypeId::of::<T>()))
|
||||
.and_then(|boxed| {
|
||||
(boxed as Box<dyn Any + 'static>)
|
||||
.downcast()
|
||||
.ok()
|
||||
.map(|boxed| *boxed)
|
||||
})
|
||||
}
|
||||
|
||||
/// Clear the `Extensions` of all inserted extensions.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// ext.insert(5i32);
|
||||
/// ext.clear();
|
||||
///
|
||||
/// assert!(ext.get::<i32>().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn clear(&mut self) {
|
||||
if let Some(ref mut map) = self.map {
|
||||
map.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// Check whether the extension set is empty or not.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// assert!(ext.is_empty());
|
||||
/// ext.insert(5i32);
|
||||
/// assert!(!ext.is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.map
|
||||
.as_ref()
|
||||
.map_or(true, |map| map.is_empty())
|
||||
}
|
||||
|
||||
/// Get the numer of extensions available.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext = Extensions::new();
|
||||
/// assert_eq!(ext.len(), 0);
|
||||
/// ext.insert(5i32);
|
||||
/// assert_eq!(ext.len(), 1);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.map
|
||||
.as_ref()
|
||||
.map_or(0, |map| map.len())
|
||||
}
|
||||
|
||||
/// Extends `self` with another `Extensions`.
|
||||
///
|
||||
/// If an instance of a specific type exists in both, the one in `self` is overwritten with the
|
||||
/// one from `other`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Extensions;
|
||||
/// let mut ext_a = Extensions::new();
|
||||
/// ext_a.insert(8u8);
|
||||
/// ext_a.insert(16u16);
|
||||
///
|
||||
/// let mut ext_b = Extensions::new();
|
||||
/// ext_b.insert(4u8);
|
||||
/// ext_b.insert("hello");
|
||||
///
|
||||
/// ext_a.extend(ext_b);
|
||||
/// assert_eq!(ext_a.len(), 3);
|
||||
/// assert_eq!(ext_a.get::<u8>(), Some(&4u8));
|
||||
/// assert_eq!(ext_a.get::<u16>(), Some(&16u16));
|
||||
/// assert_eq!(ext_a.get::<&'static str>().copied(), Some("hello"));
|
||||
/// ```
|
||||
pub fn extend(&mut self, other: Self) {
|
||||
if let Some(other) = other.map {
|
||||
if let Some(map) = &mut self.map {
|
||||
map.extend(*other);
|
||||
} else {
|
||||
self.map = Some(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Extensions {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Extensions").finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extensions() {
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct MyType(i32);
|
||||
|
||||
let mut extensions = Extensions::new();
|
||||
|
||||
extensions.insert(5i32);
|
||||
extensions.insert(MyType(10));
|
||||
|
||||
assert_eq!(extensions.get(), Some(&5i32));
|
||||
assert_eq!(extensions.get_mut(), Some(&mut 5i32));
|
||||
|
||||
assert_eq!(extensions.remove::<i32>(), Some(5i32));
|
||||
assert!(extensions.get::<i32>().is_none());
|
||||
|
||||
assert_eq!(extensions.get::<bool>(), None);
|
||||
assert_eq!(extensions.get(), Some(&MyType(10)));
|
||||
}
|
||||
3496
zeroidc/vendor/http/src/header/map.rs
vendored
Normal file
3496
zeroidc/vendor/http/src/header/map.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
172
zeroidc/vendor/http/src/header/mod.rs
vendored
Normal file
172
zeroidc/vendor/http/src/header/mod.rs
vendored
Normal file
@@ -0,0 +1,172 @@
|
||||
//! HTTP header types
|
||||
//!
|
||||
//! The module provides [`HeaderName`], [`HeaderMap`], and a number of types
|
||||
//! used for interacting with `HeaderMap`. These types allow representing both
|
||||
//! HTTP/1 and HTTP/2 headers.
|
||||
//!
|
||||
//! # `HeaderName`
|
||||
//!
|
||||
//! The `HeaderName` type represents both standard header names as well as
|
||||
//! custom header names. The type handles the case insensitive nature of header
|
||||
//! names and is used as the key portion of `HeaderMap`. Header names are
|
||||
//! normalized to lower case. In other words, when creating a `HeaderName` with
|
||||
//! a string, even if upper case characters are included, when getting a string
|
||||
//! representation of the `HeaderName`, it will be all lower case. This allows
|
||||
//! for faster `HeaderMap` comparison operations.
|
||||
//!
|
||||
//! The internal representation is optimized to efficiently handle the cases
|
||||
//! most commonly encountered when working with HTTP. Standard header names are
|
||||
//! special cased and are represented internally as an enum. Short custom
|
||||
//! headers will be stored directly in the `HeaderName` struct and will not
|
||||
//! incur any allocation overhead, however longer strings will require an
|
||||
//! allocation for storage.
|
||||
//!
|
||||
//! ## Limitations
|
||||
//!
|
||||
//! `HeaderName` has a max length of 32,768 for header names. Attempting to
|
||||
//! parse longer names will result in a panic.
|
||||
//!
|
||||
//! # `HeaderMap`
|
||||
//!
|
||||
//! `HeaderMap` is a map structure of header names highly optimized for use
|
||||
//! cases common with HTTP. It is a [multimap] structure, where each header name
|
||||
//! may have multiple associated header values. Given this, some of the APIs
|
||||
//! diverge from [`HashMap`].
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! Just like `HashMap` in Rust's stdlib, `HeaderMap` is based on [Robin Hood
|
||||
//! hashing]. This algorithm tends to reduce the worst case search times in the
|
||||
//! table and enables high load factors without seriously affecting performance.
|
||||
//! Internally, keys and values are stored in vectors. As such, each insertion
|
||||
//! will not incur allocation overhead. However, once the underlying vector
|
||||
//! storage is full, a larger vector must be allocated and all values copied.
|
||||
//!
|
||||
//! ## Deterministic ordering
|
||||
//!
|
||||
//! Unlike Rust's `HashMap`, values in `HeaderMap` are deterministically
|
||||
//! ordered. Roughly, values are ordered by insertion. This means that a
|
||||
//! function that deterministically operates on a header map can rely on the
|
||||
//! iteration order to remain consistent across processes and platforms.
|
||||
//!
|
||||
//! ## Adaptive hashing
|
||||
//!
|
||||
//! `HeaderMap` uses an adaptive hashing strategy in order to efficiently handle
|
||||
//! most common cases. All standard headers have statically computed hash values
|
||||
//! which removes the need to perform any hashing of these headers at runtime.
|
||||
//! The default hash function emphasizes performance over robustness. However,
|
||||
//! `HeaderMap` detects high collision rates and switches to a secure hash
|
||||
//! function in those events. The threshold is set such that only denial of
|
||||
//! service attacks should trigger it.
|
||||
//!
|
||||
//! ## Limitations
|
||||
//!
|
||||
//! `HeaderMap` can store a maximum of 32,768 headers (header name / value
|
||||
//! pairs). Attempting to insert more will result in a panic.
|
||||
//!
|
||||
//! [`HeaderName`]: struct.HeaderName.html
|
||||
//! [`HeaderMap`]: struct.HeaderMap.html
|
||||
//! [multimap]: https://en.wikipedia.org/wiki/Multimap
|
||||
//! [`HashMap`]: https://doc.rust-lang.org/std/collections/struct.HashMap.html
|
||||
//! [Robin Hood hashing]: https://en.wikipedia.org/wiki/Hash_table#Robin_Hood_hashing
|
||||
|
||||
mod map;
|
||||
mod name;
|
||||
mod value;
|
||||
|
||||
pub use self::map::{
|
||||
AsHeaderName, Drain, Entry, GetAll, HeaderMap, IntoHeaderName, IntoIter, Iter, IterMut, Keys,
|
||||
OccupiedEntry, VacantEntry, ValueDrain, ValueIter, ValueIterMut, Values, ValuesMut,
|
||||
};
|
||||
pub use self::name::{HeaderName, InvalidHeaderName};
|
||||
pub use self::value::{HeaderValue, InvalidHeaderValue, ToStrError};
|
||||
|
||||
// Use header name constants
|
||||
pub use self::name::{
|
||||
ACCEPT,
|
||||
ACCEPT_CHARSET,
|
||||
ACCEPT_ENCODING,
|
||||
ACCEPT_LANGUAGE,
|
||||
ACCEPT_RANGES,
|
||||
ACCESS_CONTROL_ALLOW_CREDENTIALS,
|
||||
ACCESS_CONTROL_ALLOW_HEADERS,
|
||||
ACCESS_CONTROL_ALLOW_METHODS,
|
||||
ACCESS_CONTROL_ALLOW_ORIGIN,
|
||||
ACCESS_CONTROL_EXPOSE_HEADERS,
|
||||
ACCESS_CONTROL_MAX_AGE,
|
||||
ACCESS_CONTROL_REQUEST_HEADERS,
|
||||
ACCESS_CONTROL_REQUEST_METHOD,
|
||||
AGE,
|
||||
ALLOW,
|
||||
ALT_SVC,
|
||||
AUTHORIZATION,
|
||||
CACHE_CONTROL,
|
||||
CONNECTION,
|
||||
CONTENT_DISPOSITION,
|
||||
CONTENT_ENCODING,
|
||||
CONTENT_LANGUAGE,
|
||||
CONTENT_LENGTH,
|
||||
CONTENT_LOCATION,
|
||||
CONTENT_RANGE,
|
||||
CONTENT_SECURITY_POLICY,
|
||||
CONTENT_SECURITY_POLICY_REPORT_ONLY,
|
||||
CONTENT_TYPE,
|
||||
COOKIE,
|
||||
DNT,
|
||||
DATE,
|
||||
ETAG,
|
||||
EXPECT,
|
||||
EXPIRES,
|
||||
FORWARDED,
|
||||
FROM,
|
||||
HOST,
|
||||
IF_MATCH,
|
||||
IF_MODIFIED_SINCE,
|
||||
IF_NONE_MATCH,
|
||||
IF_RANGE,
|
||||
IF_UNMODIFIED_SINCE,
|
||||
LAST_MODIFIED,
|
||||
LINK,
|
||||
LOCATION,
|
||||
MAX_FORWARDS,
|
||||
ORIGIN,
|
||||
PRAGMA,
|
||||
PROXY_AUTHENTICATE,
|
||||
PROXY_AUTHORIZATION,
|
||||
PUBLIC_KEY_PINS,
|
||||
PUBLIC_KEY_PINS_REPORT_ONLY,
|
||||
RANGE,
|
||||
REFERER,
|
||||
REFERRER_POLICY,
|
||||
REFRESH,
|
||||
RETRY_AFTER,
|
||||
SEC_WEBSOCKET_ACCEPT,
|
||||
SEC_WEBSOCKET_EXTENSIONS,
|
||||
SEC_WEBSOCKET_KEY,
|
||||
SEC_WEBSOCKET_PROTOCOL,
|
||||
SEC_WEBSOCKET_VERSION,
|
||||
SERVER,
|
||||
SET_COOKIE,
|
||||
STRICT_TRANSPORT_SECURITY,
|
||||
TE,
|
||||
TRAILER,
|
||||
TRANSFER_ENCODING,
|
||||
UPGRADE,
|
||||
UPGRADE_INSECURE_REQUESTS,
|
||||
USER_AGENT,
|
||||
VARY,
|
||||
VIA,
|
||||
WARNING,
|
||||
WWW_AUTHENTICATE,
|
||||
X_CONTENT_TYPE_OPTIONS,
|
||||
X_DNS_PREFETCH_CONTROL,
|
||||
X_FRAME_OPTIONS,
|
||||
X_XSS_PROTECTION,
|
||||
};
|
||||
|
||||
/// Maximum length of a header name
|
||||
///
|
||||
/// Generally, 64kb for a header name is WAY too much than would ever be needed
|
||||
/// in practice. Restricting it to this size enables using `u16` values to
|
||||
/// represent offsets when dealing with header names.
|
||||
const MAX_HEADER_NAME_LEN: usize = (1 << 16) - 1;
|
||||
1856
zeroidc/vendor/http/src/header/name.rs
vendored
Normal file
1856
zeroidc/vendor/http/src/header/name.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
795
zeroidc/vendor/http/src/header/value.rs
vendored
Normal file
795
zeroidc/vendor/http/src/header/value.rs
vendored
Normal file
@@ -0,0 +1,795 @@
|
||||
use bytes::{Bytes, BytesMut};
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::error::Error;
|
||||
use std::fmt::Write;
|
||||
use std::str::FromStr;
|
||||
use std::{cmp, fmt, mem, str};
|
||||
|
||||
use crate::header::name::HeaderName;
|
||||
|
||||
/// Represents an HTTP header field value.
|
||||
///
|
||||
/// In practice, HTTP header field values are usually valid ASCII. However, the
|
||||
/// HTTP spec allows for a header value to contain opaque bytes as well. In this
|
||||
/// case, the header field value is not able to be represented as a string.
|
||||
///
|
||||
/// To handle this, the `HeaderValue` is useable as a type and can be compared
|
||||
/// with strings and implements `Debug`. A `to_str` fn is provided that returns
|
||||
/// an `Err` if the header value contains non visible ascii characters.
|
||||
#[derive(Clone, Hash)]
|
||||
pub struct HeaderValue {
|
||||
inner: Bytes,
|
||||
is_sensitive: bool,
|
||||
}
|
||||
|
||||
/// A possible error when converting a `HeaderValue` from a string or byte
|
||||
/// slice.
|
||||
pub struct InvalidHeaderValue {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
/// A possible error when converting a `HeaderValue` to a string representation.
|
||||
///
|
||||
/// Header field values may contain opaque bytes, in which case it is not
|
||||
/// possible to represent the value as a string.
|
||||
#[derive(Debug)]
|
||||
pub struct ToStrError {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl HeaderValue {
|
||||
/// Convert a static string to a `HeaderValue`.
|
||||
///
|
||||
/// This function will not perform any copying, however the string is
|
||||
/// checked to ensure that no invalid characters are present. Only visible
|
||||
/// ASCII characters (32-127) are permitted.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the argument contains invalid header value
|
||||
/// characters.
|
||||
///
|
||||
/// Until [Allow panicking in constants](https://github.com/rust-lang/rfcs/pull/2345)
|
||||
/// makes its way into stable, the panic message at compile-time is
|
||||
/// going to look cryptic, but should at least point at your header value:
|
||||
///
|
||||
/// ```text
|
||||
/// error: any use of this value will cause an error
|
||||
/// --> http/src/header/value.rs:67:17
|
||||
/// |
|
||||
/// 67 | ([] as [u8; 0])[0]; // Invalid header value
|
||||
/// | ^^^^^^^^^^^^^^^^^^
|
||||
/// | |
|
||||
/// | index out of bounds: the length is 0 but the index is 0
|
||||
/// | inside `HeaderValue::from_static` at http/src/header/value.rs:67:17
|
||||
/// | inside `INVALID_HEADER` at src/main.rs:73:33
|
||||
/// |
|
||||
/// ::: src/main.rs:73:1
|
||||
/// |
|
||||
/// 73 | const INVALID_HEADER: HeaderValue = HeaderValue::from_static("жsome value");
|
||||
/// | ----------------------------------------------------------------------------
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert_eq!(val, "hello");
|
||||
/// ```
|
||||
#[inline]
|
||||
#[allow(unconditional_panic)] // required for the panic circumvention
|
||||
pub const fn from_static(src: &'static str) -> HeaderValue {
|
||||
let bytes = src.as_bytes();
|
||||
let mut i = 0;
|
||||
while i < bytes.len() {
|
||||
if !is_visible_ascii(bytes[i]) {
|
||||
([] as [u8; 0])[0]; // Invalid header value
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
|
||||
HeaderValue {
|
||||
inner: Bytes::from_static(bytes),
|
||||
is_sensitive: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to convert a string to a `HeaderValue`.
|
||||
///
|
||||
/// If the argument contains invalid header value characters, an error is
|
||||
/// returned. Only visible ASCII characters (32-127) are permitted. Use
|
||||
/// `from_bytes` to create a `HeaderValue` that includes opaque octets
|
||||
/// (128-255).
|
||||
///
|
||||
/// This function is intended to be replaced in the future by a `TryFrom`
|
||||
/// implementation once the trait is stabilized in std.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_str("hello").unwrap();
|
||||
/// assert_eq!(val, "hello");
|
||||
/// ```
|
||||
///
|
||||
/// An invalid value
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_str("\n");
|
||||
/// assert!(val.is_err());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> {
|
||||
HeaderValue::try_from_generic(src, |s| Bytes::copy_from_slice(s.as_bytes()))
|
||||
}
|
||||
|
||||
/// Converts a HeaderName into a HeaderValue
|
||||
///
|
||||
/// Since every valid HeaderName is a valid HeaderValue this is done infallibly.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::{HeaderValue, HeaderName};
|
||||
/// # use http::header::ACCEPT;
|
||||
/// let val = HeaderValue::from_name(ACCEPT);
|
||||
/// assert_eq!(val, HeaderValue::from_bytes(b"accept").unwrap());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_name(name: HeaderName) -> HeaderValue {
|
||||
name.into()
|
||||
}
|
||||
|
||||
/// Attempt to convert a byte slice to a `HeaderValue`.
|
||||
///
|
||||
/// If the argument contains invalid header value bytes, an error is
|
||||
/// returned. Only byte values between 32 and 255 (inclusive) are permitted,
|
||||
/// excluding byte 127 (DEL).
|
||||
///
|
||||
/// This function is intended to be replaced in the future by a `TryFrom`
|
||||
/// implementation once the trait is stabilized in std.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_bytes(b"hello\xfa").unwrap();
|
||||
/// assert_eq!(val, &b"hello\xfa"[..]);
|
||||
/// ```
|
||||
///
|
||||
/// An invalid value
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_bytes(b"\n");
|
||||
/// assert!(val.is_err());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> {
|
||||
HeaderValue::try_from_generic(src, Bytes::copy_from_slice)
|
||||
}
|
||||
|
||||
/// Attempt to convert a `Bytes` buffer to a `HeaderValue`.
|
||||
///
|
||||
/// This will try to prevent a copy if the type passed is the type used
|
||||
/// internally, and will copy the data if it is not.
|
||||
pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
|
||||
where
|
||||
T: AsRef<[u8]> + 'static,
|
||||
{
|
||||
if_downcast_into!(T, Bytes, src, {
|
||||
return HeaderValue::from_shared(src);
|
||||
});
|
||||
|
||||
HeaderValue::from_bytes(src.as_ref())
|
||||
}
|
||||
|
||||
/// Convert a `Bytes` directly into a `HeaderValue` without validating.
|
||||
///
|
||||
/// This function does NOT validate that illegal bytes are not contained
|
||||
/// within the buffer.
|
||||
pub unsafe fn from_maybe_shared_unchecked<T>(src: T) -> HeaderValue
|
||||
where
|
||||
T: AsRef<[u8]> + 'static,
|
||||
{
|
||||
if cfg!(debug_assertions) {
|
||||
match HeaderValue::from_maybe_shared(src) {
|
||||
Ok(val) => val,
|
||||
Err(_err) => {
|
||||
panic!("HeaderValue::from_maybe_shared_unchecked() with invalid bytes");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
if_downcast_into!(T, Bytes, src, {
|
||||
return HeaderValue {
|
||||
inner: src,
|
||||
is_sensitive: false,
|
||||
};
|
||||
});
|
||||
|
||||
let src = Bytes::copy_from_slice(src.as_ref());
|
||||
HeaderValue {
|
||||
inner: src,
|
||||
is_sensitive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_shared(src: Bytes) -> Result<HeaderValue, InvalidHeaderValue> {
|
||||
HeaderValue::try_from_generic(src, std::convert::identity)
|
||||
}
|
||||
|
||||
fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(src: T, into: F) -> Result<HeaderValue, InvalidHeaderValue> {
|
||||
for &b in src.as_ref() {
|
||||
if !is_valid(b) {
|
||||
return Err(InvalidHeaderValue { _priv: () });
|
||||
}
|
||||
}
|
||||
Ok(HeaderValue {
|
||||
inner: into(src),
|
||||
is_sensitive: false,
|
||||
})
|
||||
}
|
||||
|
||||
/// Yields a `&str` slice if the `HeaderValue` only contains visible ASCII
|
||||
/// chars.
|
||||
///
|
||||
/// This function will perform a scan of the header value, checking all the
|
||||
/// characters.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert_eq!(val.to_str().unwrap(), "hello");
|
||||
/// ```
|
||||
pub fn to_str(&self) -> Result<&str, ToStrError> {
|
||||
let bytes = self.as_ref();
|
||||
|
||||
for &b in bytes {
|
||||
if !is_visible_ascii(b) {
|
||||
return Err(ToStrError { _priv: () });
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { Ok(str::from_utf8_unchecked(bytes)) }
|
||||
}
|
||||
|
||||
/// Returns the length of `self`.
|
||||
///
|
||||
/// This length is in bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert_eq!(val.len(), 5);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn len(&self) -> usize {
|
||||
self.as_ref().len()
|
||||
}
|
||||
|
||||
/// Returns true if the `HeaderValue` has a length of zero bytes.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("");
|
||||
/// assert!(val.is_empty());
|
||||
///
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert!(!val.is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Converts a `HeaderValue` to a byte slice.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let val = HeaderValue::from_static("hello");
|
||||
/// assert_eq!(val.as_bytes(), b"hello");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.as_ref()
|
||||
}
|
||||
|
||||
/// Mark that the header value represents sensitive information.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let mut val = HeaderValue::from_static("my secret");
|
||||
///
|
||||
/// val.set_sensitive(true);
|
||||
/// assert!(val.is_sensitive());
|
||||
///
|
||||
/// val.set_sensitive(false);
|
||||
/// assert!(!val.is_sensitive());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn set_sensitive(&mut self, val: bool) {
|
||||
self.is_sensitive = val;
|
||||
}
|
||||
|
||||
/// Returns `true` if the value represents sensitive data.
|
||||
///
|
||||
/// Sensitive data could represent passwords or other data that should not
|
||||
/// be stored on disk or in memory. By marking header values as sensitive,
|
||||
/// components using this crate can be instructed to treat them with special
|
||||
/// care for security reasons. For example, caches can avoid storing
|
||||
/// sensitive values, and HPACK encoders used by HTTP/2.0 implementations
|
||||
/// can choose not to compress them.
|
||||
///
|
||||
/// Additionally, sensitive values will be masked by the `Debug`
|
||||
/// implementation of `HeaderValue`.
|
||||
///
|
||||
/// Note that sensitivity is not factored into equality or ordering.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let mut val = HeaderValue::from_static("my secret");
|
||||
///
|
||||
/// val.set_sensitive(true);
|
||||
/// assert!(val.is_sensitive());
|
||||
///
|
||||
/// val.set_sensitive(false);
|
||||
/// assert!(!val.is_sensitive());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn is_sensitive(&self) -> bool {
|
||||
self.is_sensitive
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for HeaderValue {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.inner.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for HeaderValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.is_sensitive {
|
||||
f.write_str("Sensitive")
|
||||
} else {
|
||||
f.write_str("\"")?;
|
||||
let mut from = 0;
|
||||
let bytes = self.as_bytes();
|
||||
for (i, &b) in bytes.iter().enumerate() {
|
||||
if !is_visible_ascii(b) || b == b'"' {
|
||||
if from != i {
|
||||
f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
|
||||
}
|
||||
if b == b'"' {
|
||||
f.write_str("\\\"")?;
|
||||
} else {
|
||||
write!(f, "\\x{:x}", b)?;
|
||||
}
|
||||
from = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
|
||||
f.write_str("\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HeaderName> for HeaderValue {
|
||||
#[inline]
|
||||
fn from(h: HeaderName) -> HeaderValue {
|
||||
HeaderValue {
|
||||
inner: h.into_bytes(),
|
||||
is_sensitive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! from_integers {
|
||||
($($name:ident: $t:ident => $max_len:expr),*) => {$(
|
||||
impl From<$t> for HeaderValue {
|
||||
fn from(num: $t) -> HeaderValue {
|
||||
let mut buf = if mem::size_of::<BytesMut>() - 1 < $max_len {
|
||||
// On 32bit platforms, BytesMut max inline size
|
||||
// is 15 bytes, but the $max_len could be bigger.
|
||||
//
|
||||
// The likelihood of the number *actually* being
|
||||
// that big is very small, so only allocate
|
||||
// if the number needs that space.
|
||||
//
|
||||
// The largest decimal number in 15 digits:
|
||||
// It wold be 10.pow(15) - 1, but this is a constant
|
||||
// version.
|
||||
if num as u64 > 999_999_999_999_999_999 {
|
||||
BytesMut::with_capacity($max_len)
|
||||
} else {
|
||||
// fits inline...
|
||||
BytesMut::new()
|
||||
}
|
||||
} else {
|
||||
// full value fits inline, so don't allocate!
|
||||
BytesMut::new()
|
||||
};
|
||||
let _ = buf.write_str(::itoa::Buffer::new().format(num));
|
||||
HeaderValue {
|
||||
inner: buf.freeze(),
|
||||
is_sensitive: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn $name() {
|
||||
let n: $t = 55;
|
||||
let val = HeaderValue::from(n);
|
||||
assert_eq!(val, &n.to_string());
|
||||
|
||||
let n = ::std::$t::MAX;
|
||||
let val = HeaderValue::from(n);
|
||||
assert_eq!(val, &n.to_string());
|
||||
}
|
||||
)*};
|
||||
}
|
||||
|
||||
from_integers! {
|
||||
// integer type => maximum decimal length
|
||||
|
||||
// u8 purposely left off... HeaderValue::from(b'3') could be confusing
|
||||
from_u16: u16 => 5,
|
||||
from_i16: i16 => 6,
|
||||
from_u32: u32 => 10,
|
||||
from_i32: i32 => 11,
|
||||
from_u64: u64 => 20,
|
||||
from_i64: i64 => 20
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "16")]
|
||||
from_integers! {
|
||||
from_usize: usize => 5,
|
||||
from_isize: isize => 6
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
from_integers! {
|
||||
from_usize: usize => 10,
|
||||
from_isize: isize => 11
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
from_integers! {
|
||||
from_usize: usize => 20,
|
||||
from_isize: isize => 20
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod from_header_name_tests {
|
||||
use super::*;
|
||||
use crate::header::map::HeaderMap;
|
||||
use crate::header::name;
|
||||
|
||||
#[test]
|
||||
fn it_can_insert_header_name_as_header_value() {
|
||||
let mut map = HeaderMap::new();
|
||||
map.insert(name::UPGRADE, name::SEC_WEBSOCKET_PROTOCOL.into());
|
||||
map.insert(
|
||||
name::ACCEPT,
|
||||
name::HeaderName::from_bytes(b"hello-world").unwrap().into(),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
map.get(name::UPGRADE).unwrap(),
|
||||
HeaderValue::from_bytes(b"sec-websocket-protocol").unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
map.get(name::ACCEPT).unwrap(),
|
||||
HeaderValue::from_bytes(b"hello-world").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for HeaderValue {
|
||||
type Err = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
|
||||
HeaderValue::from_str(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a HeaderValue> for HeaderValue {
|
||||
#[inline]
|
||||
fn from(t: &'a HeaderValue) -> Self {
|
||||
t.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
|
||||
t.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a String> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
#[inline]
|
||||
fn try_from(s: &'a String) -> Result<Self, Self::Error> {
|
||||
Self::from_bytes(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
HeaderValue::from_bytes(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: String) -> Result<Self, Self::Error> {
|
||||
HeaderValue::from_shared(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for HeaderValue {
|
||||
type Error = InvalidHeaderValue;
|
||||
|
||||
#[inline]
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
HeaderValue::from_shared(vec.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod try_from_header_name_tests {
|
||||
use super::*;
|
||||
use crate::header::name;
|
||||
|
||||
#[test]
|
||||
fn it_converts_using_try_from() {
|
||||
assert_eq!(
|
||||
HeaderValue::try_from(name::UPGRADE).unwrap(),
|
||||
HeaderValue::from_bytes(b"upgrade").unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const fn is_visible_ascii(b: u8) -> bool {
|
||||
b >= 32 && b < 127 || b == b'\t'
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_valid(b: u8) -> bool {
|
||||
b >= 32 && b != 127 || b == b'\t'
|
||||
}
|
||||
|
||||
impl fmt::Debug for InvalidHeaderValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InvalidHeaderValue")
|
||||
// skip _priv noise
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidHeaderValue {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("failed to parse header value")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for InvalidHeaderValue {}
|
||||
|
||||
impl fmt::Display for ToStrError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("failed to convert header to a str")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for ToStrError {}
|
||||
|
||||
// ===== PartialEq / PartialOrd =====
|
||||
|
||||
impl PartialEq for HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
self.inner == other.inner
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for HeaderValue {}
|
||||
|
||||
impl PartialOrd for HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.inner.partial_cmp(&other.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for HeaderValue {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> cmp::Ordering {
|
||||
self.inner.cmp(&other.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.inner == other.as_bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<[u8]> for HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &[u8]) -> bool {
|
||||
self.inner == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<str> for HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
|
||||
(*self.inner).partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<[u8]> for HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
|
||||
(*self.inner).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<HeaderValue> for str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<HeaderValue> for [u8] {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<HeaderValue> for str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<HeaderValue> for [u8] {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
*self == &other[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<String> for HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
self.inner.partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<HeaderValue> for String {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<HeaderValue> for String {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<HeaderValue> for &'a HeaderValue {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
**self == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<HeaderValue> for &'a HeaderValue {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
(**self).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
|
||||
where
|
||||
HeaderValue: PartialEq<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a T) -> bool {
|
||||
*self == **other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
|
||||
where
|
||||
HeaderValue: PartialOrd<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<HeaderValue> for &'a str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &HeaderValue) -> bool {
|
||||
*other == *self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<HeaderValue> for &'a str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
|
||||
self.as_bytes().partial_cmp(other.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_from() {
|
||||
HeaderValue::try_from(vec![127]).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_debug() {
|
||||
let cases = &[
|
||||
("hello", "\"hello\""),
|
||||
("hello \"world\"", "\"hello \\\"world\\\"\""),
|
||||
("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
|
||||
];
|
||||
|
||||
for &(value, expected) in cases {
|
||||
let val = HeaderValue::from_bytes(value.as_bytes()).unwrap();
|
||||
let actual = format!("{:?}", val);
|
||||
assert_eq!(expected, actual);
|
||||
}
|
||||
|
||||
let mut sensitive = HeaderValue::from_static("password");
|
||||
sensitive.set_sensitive(true);
|
||||
assert_eq!("Sensitive", format!("{:?}", sensitive));
|
||||
}
|
||||
211
zeroidc/vendor/http/src/lib.rs
vendored
Normal file
211
zeroidc/vendor/http/src/lib.rs
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
#![doc(html_root_url = "https://docs.rs/http/0.2.8")]
|
||||
|
||||
//! A general purpose library of common HTTP types
|
||||
//!
|
||||
//! This crate is a general purpose library for common types found when working
|
||||
//! with the HTTP protocol. You'll find `Request` and `Response` types for
|
||||
//! working as either a client or a server as well as all of their components.
|
||||
//! Notably you'll find `Uri` for what a `Request` is requesting, a `Method`
|
||||
//! for how it's being requested, a `StatusCode` for what sort of response came
|
||||
//! back, a `Version` for how this was communicated, and
|
||||
//! `HeaderName`/`HeaderValue` definitions to get grouped in a `HeaderMap` to
|
||||
//! work with request/response headers.
|
||||
//!
|
||||
//! You will notably *not* find an implementation of sending requests or
|
||||
//! spinning up a server in this crate. It's intended that this crate is the
|
||||
//! "standard library" for HTTP clients and servers without dictating any
|
||||
//! particular implementation. Note that this crate is still early on in its
|
||||
//! lifecycle so the support libraries that integrate with the `http` crate are
|
||||
//! a work in progress! Stay tuned and we'll be sure to highlight crates here
|
||||
//! in the future.
|
||||
//!
|
||||
//! ## Requests and Responses
|
||||
//!
|
||||
//! Perhaps the main two types in this crate are the `Request` and `Response`
|
||||
//! types. A `Request` could either be constructed to get sent off as a client
|
||||
//! or it can also be received to generate a `Response` for a server. Similarly
|
||||
//! as a client a `Response` is what you get after sending a `Request`, whereas
|
||||
//! on a server you'll be manufacturing a `Response` to send back to the client.
|
||||
//!
|
||||
//! Each type has a number of accessors for the component fields. For as a
|
||||
//! server you might want to inspect a requests URI to dispatch it:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::{Request, Response};
|
||||
//!
|
||||
//! fn response(req: Request<()>) -> http::Result<Response<()>> {
|
||||
//! match req.uri().path() {
|
||||
//! "/" => index(req),
|
||||
//! "/foo" => foo(req),
|
||||
//! "/bar" => bar(req),
|
||||
//! _ => not_found(req),
|
||||
//! }
|
||||
//! }
|
||||
//! # fn index(_req: Request<()>) -> http::Result<Response<()>> { panic!() }
|
||||
//! # fn foo(_req: Request<()>) -> http::Result<Response<()>> { panic!() }
|
||||
//! # fn bar(_req: Request<()>) -> http::Result<Response<()>> { panic!() }
|
||||
//! # fn not_found(_req: Request<()>) -> http::Result<Response<()>> { panic!() }
|
||||
//! ```
|
||||
//!
|
||||
//! On a `Request` you'll also find accessors like `method` to return a
|
||||
//! `Method` and `headers` to inspect the various headers. A `Response`
|
||||
//! has similar methods for headers, the status code, etc.
|
||||
//!
|
||||
//! In addition to getters, request/response types also have mutable accessors
|
||||
//! to edit the request/response:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::{HeaderValue, Response, StatusCode};
|
||||
//! use http::header::CONTENT_TYPE;
|
||||
//!
|
||||
//! fn add_server_headers<T>(response: &mut Response<T>) {
|
||||
//! response.headers_mut()
|
||||
//! .insert(CONTENT_TYPE, HeaderValue::from_static("text/html"));
|
||||
//! *response.status_mut() = StatusCode::OK;
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! And finally, one of the most important aspects of requests/responses, the
|
||||
//! body! The `Request` and `Response` types in this crate are *generic* in
|
||||
//! what their body is. This allows downstream libraries to use different
|
||||
//! representations such as `Request<Vec<u8>>`, `Response<impl Read>`,
|
||||
//! `Request<impl Stream<Item = Vec<u8>, Error = _>>`, or even
|
||||
//! `Response<MyCustomType>` where the custom type was deserialized from JSON.
|
||||
//!
|
||||
//! The body representation is intentionally flexible to give downstream
|
||||
//! libraries maximal flexibility in implementing the body as appropriate.
|
||||
//!
|
||||
//! ## HTTP Headers
|
||||
//!
|
||||
//! Another major piece of functionality in this library is HTTP header
|
||||
//! interpretation and generation. The `HeaderName` type serves as a way to
|
||||
//! define header *names*, or what's to the left of the colon. A `HeaderValue`
|
||||
//! conversely is the header *value*, or what's to the right of a colon.
|
||||
//!
|
||||
//! For example, if you have an HTTP request that looks like:
|
||||
//!
|
||||
//! ```http
|
||||
//! GET /foo HTTP/1.1
|
||||
//! Accept: text/html
|
||||
//! ```
|
||||
//!
|
||||
//! Then `"Accept"` is a `HeaderName` while `"text/html"` is a `HeaderValue`.
|
||||
//! Each of these is a dedicated type to allow for a number of interesting
|
||||
//! optimizations and to also encode the static guarantees of each type. For
|
||||
//! example a `HeaderName` is always a valid `&str`, but a `HeaderValue` may
|
||||
//! not be valid UTF-8.
|
||||
//!
|
||||
//! The most common header names are already defined for you as constant values
|
||||
//! in the `header` module of this crate. For example:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::header::{self, HeaderName};
|
||||
//!
|
||||
//! let name: HeaderName = header::ACCEPT;
|
||||
//! assert_eq!(name.as_str(), "accept");
|
||||
//! ```
|
||||
//!
|
||||
//! You can, however, also parse header names from strings:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::header::{self, HeaderName};
|
||||
//!
|
||||
//! let name = "Accept".parse::<HeaderName>().unwrap();
|
||||
//! assert_eq!(name, header::ACCEPT);
|
||||
//! ```
|
||||
//!
|
||||
//! Header values can be created from string literals through the `from_static`
|
||||
//! function:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::HeaderValue;
|
||||
//!
|
||||
//! let value = HeaderValue::from_static("text/html");
|
||||
//! assert_eq!(value.as_bytes(), b"text/html");
|
||||
//! ```
|
||||
//!
|
||||
//! And header values can also be parsed like names:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::HeaderValue;
|
||||
//!
|
||||
//! let value = "text/html";
|
||||
//! let value = value.parse::<HeaderValue>().unwrap();
|
||||
//! ```
|
||||
//!
|
||||
//! Most HTTP requests and responses tend to come with more than one header, so
|
||||
//! it's not too useful to just work with names and values only! This crate also
|
||||
//! provides a `HeaderMap` type which is a specialized hash map for keys as
|
||||
//! `HeaderName` and generic values. This type, like header names, is optimized
|
||||
//! for common usage but should continue to scale with your needs over time.
|
||||
//!
|
||||
//! # URIs
|
||||
//!
|
||||
//! Each HTTP `Request` has an associated URI with it. This may just be a path
|
||||
//! like `/index.html` but it could also be an absolute URL such as
|
||||
//! `https://www.rust-lang.org/index.html`. A `URI` has a number of accessors to
|
||||
//! interpret it:
|
||||
//!
|
||||
//! ```
|
||||
//! use http::Uri;
|
||||
//! use http::uri::Scheme;
|
||||
//!
|
||||
//! let uri = "https://www.rust-lang.org/index.html".parse::<Uri>().unwrap();
|
||||
//!
|
||||
//! assert_eq!(uri.scheme(), Some(&Scheme::HTTPS));
|
||||
//! assert_eq!(uri.host(), Some("www.rust-lang.org"));
|
||||
//! assert_eq!(uri.path(), "/index.html");
|
||||
//! assert_eq!(uri.query(), None);
|
||||
//! ```
|
||||
|
||||
#![deny(warnings, missing_docs, missing_debug_implementations)]
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
extern crate doc_comment;
|
||||
|
||||
#[cfg(test)]
|
||||
doctest!("../README.md");
|
||||
|
||||
#[macro_use]
|
||||
mod convert;
|
||||
|
||||
pub mod header;
|
||||
pub mod method;
|
||||
pub mod request;
|
||||
pub mod response;
|
||||
pub mod status;
|
||||
pub mod uri;
|
||||
pub mod version;
|
||||
|
||||
mod byte_str;
|
||||
mod error;
|
||||
mod extensions;
|
||||
|
||||
pub use crate::error::{Error, Result};
|
||||
pub use crate::extensions::Extensions;
|
||||
#[doc(no_inline)]
|
||||
pub use crate::header::{HeaderMap, HeaderValue};
|
||||
pub use crate::method::Method;
|
||||
pub use crate::request::Request;
|
||||
pub use crate::response::Response;
|
||||
pub use crate::status::StatusCode;
|
||||
pub use crate::uri::Uri;
|
||||
pub use crate::version::Version;
|
||||
|
||||
fn _assert_types() {
|
||||
fn assert_send<T: Send>() {}
|
||||
fn assert_sync<T: Sync>() {}
|
||||
|
||||
assert_send::<Request<()>>();
|
||||
assert_send::<Response<()>>();
|
||||
|
||||
assert_sync::<Request<()>>();
|
||||
assert_sync::<Response<()>>();
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
/// Private trait to this crate to prevent traits from being implemented in
|
||||
/// downstream crates.
|
||||
pub trait Sealed {}
|
||||
}
|
||||
473
zeroidc/vendor/http/src/method.rs
vendored
Normal file
473
zeroidc/vendor/http/src/method.rs
vendored
Normal file
@@ -0,0 +1,473 @@
|
||||
//! The HTTP request method
|
||||
//!
|
||||
//! This module contains HTTP-method related structs and errors and such. The
|
||||
//! main type of this module, `Method`, is also reexported at the root of the
|
||||
//! crate as `http::Method` and is intended for import through that location
|
||||
//! primarily.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use http::Method;
|
||||
//!
|
||||
//! assert_eq!(Method::GET, Method::from_bytes(b"GET").unwrap());
|
||||
//! assert!(Method::GET.is_idempotent());
|
||||
//! assert_eq!(Method::POST.as_str(), "POST");
|
||||
//! ```
|
||||
|
||||
use self::Inner::*;
|
||||
use self::extension::{InlineExtension, AllocatedExtension};
|
||||
|
||||
use std::convert::AsRef;
|
||||
use std::error::Error;
|
||||
use std::str::FromStr;
|
||||
use std::convert::TryFrom;
|
||||
use std::{fmt, str};
|
||||
|
||||
/// The Request Method (VERB)
|
||||
///
|
||||
/// This type also contains constants for a number of common HTTP methods such
|
||||
/// as GET, POST, etc.
|
||||
///
|
||||
/// Currently includes 8 variants representing the 8 methods defined in
|
||||
/// [RFC 7230](https://tools.ietf.org/html/rfc7231#section-4.1), plus PATCH,
|
||||
/// and an Extension variant for all extensions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use http::Method;
|
||||
///
|
||||
/// assert_eq!(Method::GET, Method::from_bytes(b"GET").unwrap());
|
||||
/// assert!(Method::GET.is_idempotent());
|
||||
/// assert_eq!(Method::POST.as_str(), "POST");
|
||||
/// ```
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Method(Inner);
|
||||
|
||||
/// A possible error value when converting `Method` from bytes.
|
||||
pub struct InvalidMethod {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
enum Inner {
|
||||
Options,
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Delete,
|
||||
Head,
|
||||
Trace,
|
||||
Connect,
|
||||
Patch,
|
||||
// If the extension is short enough, store it inline
|
||||
ExtensionInline(InlineExtension),
|
||||
// Otherwise, allocate it
|
||||
ExtensionAllocated(AllocatedExtension),
|
||||
}
|
||||
|
||||
|
||||
impl Method {
|
||||
/// GET
|
||||
pub const GET: Method = Method(Get);
|
||||
|
||||
/// POST
|
||||
pub const POST: Method = Method(Post);
|
||||
|
||||
/// PUT
|
||||
pub const PUT: Method = Method(Put);
|
||||
|
||||
/// DELETE
|
||||
pub const DELETE: Method = Method(Delete);
|
||||
|
||||
/// HEAD
|
||||
pub const HEAD: Method = Method(Head);
|
||||
|
||||
/// OPTIONS
|
||||
pub const OPTIONS: Method = Method(Options);
|
||||
|
||||
/// CONNECT
|
||||
pub const CONNECT: Method = Method(Connect);
|
||||
|
||||
/// PATCH
|
||||
pub const PATCH: Method = Method(Patch);
|
||||
|
||||
/// TRACE
|
||||
pub const TRACE: Method = Method(Trace);
|
||||
|
||||
/// Converts a slice of bytes to an HTTP method.
|
||||
pub fn from_bytes(src: &[u8]) -> Result<Method, InvalidMethod> {
|
||||
match src.len() {
|
||||
0 => Err(InvalidMethod::new()),
|
||||
3 => match src {
|
||||
b"GET" => Ok(Method(Get)),
|
||||
b"PUT" => Ok(Method(Put)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
4 => match src {
|
||||
b"POST" => Ok(Method(Post)),
|
||||
b"HEAD" => Ok(Method(Head)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
5 => match src {
|
||||
b"PATCH" => Ok(Method(Patch)),
|
||||
b"TRACE" => Ok(Method(Trace)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
6 => match src {
|
||||
b"DELETE" => Ok(Method(Delete)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
7 => match src {
|
||||
b"OPTIONS" => Ok(Method(Options)),
|
||||
b"CONNECT" => Ok(Method(Connect)),
|
||||
_ => Method::extension_inline(src),
|
||||
},
|
||||
_ => {
|
||||
if src.len() < InlineExtension::MAX {
|
||||
Method::extension_inline(src)
|
||||
} else {
|
||||
let allocated = AllocatedExtension::new(src)?;
|
||||
|
||||
Ok(Method(ExtensionAllocated(allocated)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn extension_inline(src: &[u8]) -> Result<Method, InvalidMethod> {
|
||||
let inline = InlineExtension::new(src)?;
|
||||
|
||||
Ok(Method(ExtensionInline(inline)))
|
||||
}
|
||||
|
||||
/// Whether a method is considered "safe", meaning the request is
|
||||
/// essentially read-only.
|
||||
///
|
||||
/// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.1)
|
||||
/// for more words.
|
||||
pub fn is_safe(&self) -> bool {
|
||||
match self.0 {
|
||||
Get | Head | Options | Trace => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether a method is considered "idempotent", meaning the request has
|
||||
/// the same result if executed multiple times.
|
||||
///
|
||||
/// See [the spec](https://tools.ietf.org/html/rfc7231#section-4.2.2) for
|
||||
/// more words.
|
||||
pub fn is_idempotent(&self) -> bool {
|
||||
match self.0 {
|
||||
Put | Delete => true,
|
||||
_ => self.is_safe(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a &str representation of the HTTP method
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self.0 {
|
||||
Options => "OPTIONS",
|
||||
Get => "GET",
|
||||
Post => "POST",
|
||||
Put => "PUT",
|
||||
Delete => "DELETE",
|
||||
Head => "HEAD",
|
||||
Trace => "TRACE",
|
||||
Connect => "CONNECT",
|
||||
Patch => "PATCH",
|
||||
ExtensionInline(ref inline) => inline.as_str(),
|
||||
ExtensionAllocated(ref allocated) => allocated.as_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Method {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a Method> for Method {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a Method) -> bool {
|
||||
self == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Method> for &'a Method {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Method) -> bool {
|
||||
*self == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<str> for Method {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_ref() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Method> for str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Method) -> bool {
|
||||
self == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for Method {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
self.as_ref() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Method> for &'a str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Method) -> bool {
|
||||
*self == other.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Method {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Method {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt.write_str(self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Method {
|
||||
#[inline]
|
||||
fn default() -> Method {
|
||||
Method::GET
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Method> for Method {
|
||||
#[inline]
|
||||
fn from(t: &'a Method) -> Self {
|
||||
t.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Method {
|
||||
type Error = InvalidMethod;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
Method::from_bytes(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Method {
|
||||
type Error = InvalidMethod;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(t.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Method {
|
||||
type Err = InvalidMethod;
|
||||
|
||||
#[inline]
|
||||
fn from_str(t: &str) -> Result<Self, Self::Err> {
|
||||
TryFrom::try_from(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl InvalidMethod {
|
||||
fn new() -> InvalidMethod {
|
||||
InvalidMethod { _priv: () }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for InvalidMethod {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InvalidMethod")
|
||||
// skip _priv noise
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidMethod {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("invalid HTTP method")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for InvalidMethod {}
|
||||
|
||||
mod extension {
|
||||
use super::InvalidMethod;
|
||||
use std::str;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
// Invariant: the first self.1 bytes of self.0 are valid UTF-8.
|
||||
pub struct InlineExtension([u8; InlineExtension::MAX], u8);
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
// Invariant: self.0 contains valid UTF-8.
|
||||
pub struct AllocatedExtension(Box<[u8]>);
|
||||
|
||||
impl InlineExtension {
|
||||
// Method::from_bytes() assumes this is at least 7
|
||||
pub const MAX: usize = 15;
|
||||
|
||||
pub fn new(src: &[u8]) -> Result<InlineExtension, InvalidMethod> {
|
||||
let mut data: [u8; InlineExtension::MAX] = Default::default();
|
||||
|
||||
write_checked(src, &mut data)?;
|
||||
|
||||
// Invariant: write_checked ensures that the first src.len() bytes
|
||||
// of data are valid UTF-8.
|
||||
Ok(InlineExtension(data, src.len() as u8))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
let InlineExtension(ref data, len) = self;
|
||||
// Safety: the invariant of InlineExtension ensures that the first
|
||||
// len bytes of data contain valid UTF-8.
|
||||
unsafe {str::from_utf8_unchecked(&data[..*len as usize])}
|
||||
}
|
||||
}
|
||||
|
||||
impl AllocatedExtension {
|
||||
pub fn new(src: &[u8]) -> Result<AllocatedExtension, InvalidMethod> {
|
||||
let mut data: Vec<u8> = vec![0; src.len()];
|
||||
|
||||
write_checked(src, &mut data)?;
|
||||
|
||||
// Invariant: data is exactly src.len() long and write_checked
|
||||
// ensures that the first src.len() bytes of data are valid UTF-8.
|
||||
Ok(AllocatedExtension(data.into_boxed_slice()))
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
// Safety: the invariant of AllocatedExtension ensures that self.0
|
||||
// contains valid UTF-8.
|
||||
unsafe {str::from_utf8_unchecked(&self.0)}
|
||||
}
|
||||
}
|
||||
|
||||
// From the HTTP spec section 5.1.1, the HTTP method is case-sensitive and can
|
||||
// contain the following characters:
|
||||
//
|
||||
// ```
|
||||
// method = token
|
||||
// token = 1*tchar
|
||||
// tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." /
|
||||
// "^" / "_" / "`" / "|" / "~" / DIGIT / ALPHA
|
||||
// ```
|
||||
//
|
||||
// https://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01#Method
|
||||
//
|
||||
// Note that this definition means that any &[u8] that consists solely of valid
|
||||
// characters is also valid UTF-8 because the valid method characters are a
|
||||
// subset of the valid 1 byte UTF-8 encoding.
|
||||
const METHOD_CHARS: [u8; 256] = [
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 1x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 2x
|
||||
b'\0', b'\0', b'\0', b'!', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 3x
|
||||
b'\0', b'\0', b'*', b'+', b'\0', b'-', b'.', b'\0', b'0', b'1', // 4x
|
||||
b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'\0', b'\0', // 5x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'A', b'B', b'C', b'D', b'E', // 6x
|
||||
b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', // 7x
|
||||
b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', // 8x
|
||||
b'Z', b'\0', b'\0', b'\0', b'^', b'_', b'`', b'a', b'b', b'c', // 9x
|
||||
b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x
|
||||
b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x
|
||||
b'x', b'y', b'z', b'\0', b'|', b'\0', b'~', b'\0', b'\0', b'\0', // 12x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 13x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 14x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 15x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 16x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 17x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 18x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 19x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 20x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 21x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 22x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 23x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', b'\0', // 24x
|
||||
b'\0', b'\0', b'\0', b'\0', b'\0', b'\0' // 25x
|
||||
];
|
||||
|
||||
// write_checked ensures (among other things) that the first src.len() bytes
|
||||
// of dst are valid UTF-8
|
||||
fn write_checked(src: &[u8], dst: &mut [u8]) -> Result<(), InvalidMethod> {
|
||||
for (i, &b) in src.iter().enumerate() {
|
||||
let b = METHOD_CHARS[b as usize];
|
||||
|
||||
if b == 0 {
|
||||
return Err(InvalidMethod::new());
|
||||
}
|
||||
|
||||
dst[i] = b;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_method_eq() {
|
||||
assert_eq!(Method::GET, Method::GET);
|
||||
assert_eq!(Method::GET, "GET");
|
||||
assert_eq!(&Method::GET, "GET");
|
||||
|
||||
assert_eq!("GET", Method::GET);
|
||||
assert_eq!("GET", &Method::GET);
|
||||
|
||||
assert_eq!(&Method::GET, Method::GET);
|
||||
assert_eq!(Method::GET, &Method::GET);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_invalid_method() {
|
||||
assert!(Method::from_str("").is_err());
|
||||
assert!(Method::from_bytes(b"").is_err());
|
||||
assert!(Method::from_bytes(&[0xC0]).is_err()); // invalid utf-8
|
||||
assert!(Method::from_bytes(&[0x10]).is_err()); // invalid method characters
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_idempotent() {
|
||||
assert!(Method::OPTIONS.is_idempotent());
|
||||
assert!(Method::GET.is_idempotent());
|
||||
assert!(Method::PUT.is_idempotent());
|
||||
assert!(Method::DELETE.is_idempotent());
|
||||
assert!(Method::HEAD.is_idempotent());
|
||||
assert!(Method::TRACE.is_idempotent());
|
||||
|
||||
assert!(!Method::POST.is_idempotent());
|
||||
assert!(!Method::CONNECT.is_idempotent());
|
||||
assert!(!Method::PATCH.is_idempotent());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_extention_method() {
|
||||
assert_eq!(Method::from_str("WOW").unwrap(), "WOW");
|
||||
assert_eq!(Method::from_str("wOw!!").unwrap(), "wOw!!");
|
||||
|
||||
let long_method = "This_is_a_very_long_method.It_is_valid_but_unlikely.";
|
||||
assert_eq!(Method::from_str(&long_method).unwrap(), long_method);
|
||||
}
|
||||
}
|
||||
1096
zeroidc/vendor/http/src/request.rs
vendored
Normal file
1096
zeroidc/vendor/http/src/request.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
799
zeroidc/vendor/http/src/response.rs
vendored
Normal file
799
zeroidc/vendor/http/src/response.rs
vendored
Normal file
@@ -0,0 +1,799 @@
|
||||
//! HTTP response types.
|
||||
//!
|
||||
//! This module contains structs related to HTTP responses, notably the
|
||||
//! `Response` type itself as well as a builder to create responses. Typically
|
||||
//! you'll import the `http::Response` type rather than reaching into this
|
||||
//! module itself.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! Creating a `Response` to return
|
||||
//!
|
||||
//! ```
|
||||
//! use http::{Request, Response, StatusCode};
|
||||
//!
|
||||
//! fn respond_to(req: Request<()>) -> http::Result<Response<()>> {
|
||||
//! let mut builder = Response::builder()
|
||||
//! .header("Foo", "Bar")
|
||||
//! .status(StatusCode::OK);
|
||||
//!
|
||||
//! if req.headers().contains_key("Another-Header") {
|
||||
//! builder = builder.header("Another-Header", "Ack");
|
||||
//! }
|
||||
//!
|
||||
//! builder.body(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! A simple 404 handler
|
||||
//!
|
||||
//! ```
|
||||
//! use http::{Request, Response, StatusCode};
|
||||
//!
|
||||
//! fn not_found(_req: Request<()>) -> http::Result<Response<()>> {
|
||||
//! Response::builder()
|
||||
//! .status(StatusCode::NOT_FOUND)
|
||||
//! .body(())
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Or otherwise inspecting the result of a request:
|
||||
//!
|
||||
//! ```no_run
|
||||
//! use http::{Request, Response};
|
||||
//!
|
||||
//! fn get(url: &str) -> http::Result<Response<()>> {
|
||||
//! // ...
|
||||
//! # panic!()
|
||||
//! }
|
||||
//!
|
||||
//! let response = get("https://www.rust-lang.org/").unwrap();
|
||||
//!
|
||||
//! if !response.status().is_success() {
|
||||
//! panic!("failed to get a successful response status!");
|
||||
//! }
|
||||
//!
|
||||
//! if let Some(date) = response.headers().get("Date") {
|
||||
//! // we've got a `Date` header!
|
||||
//! }
|
||||
//!
|
||||
//! let body = response.body();
|
||||
//! // ...
|
||||
//! ```
|
||||
|
||||
use std::any::Any;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
|
||||
use crate::header::{HeaderMap, HeaderName, HeaderValue};
|
||||
use crate::status::StatusCode;
|
||||
use crate::version::Version;
|
||||
use crate::{Extensions, Result};
|
||||
|
||||
/// Represents an HTTP response
|
||||
///
|
||||
/// An HTTP response consists of a head and a potentially optional body. The body
|
||||
/// component is generic, enabling arbitrary types to represent the HTTP body.
|
||||
/// For example, the body could be `Vec<u8>`, a `Stream` of byte chunks, or a
|
||||
/// value that has been deserialized.
|
||||
///
|
||||
/// Typically you'll work with responses on the client side as the result of
|
||||
/// sending a `Request` and on the server you'll be generating a `Response` to
|
||||
/// send back to the client.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Creating a `Response` to return
|
||||
///
|
||||
/// ```
|
||||
/// use http::{Request, Response, StatusCode};
|
||||
///
|
||||
/// fn respond_to(req: Request<()>) -> http::Result<Response<()>> {
|
||||
/// let mut builder = Response::builder()
|
||||
/// .header("Foo", "Bar")
|
||||
/// .status(StatusCode::OK);
|
||||
///
|
||||
/// if req.headers().contains_key("Another-Header") {
|
||||
/// builder = builder.header("Another-Header", "Ack");
|
||||
/// }
|
||||
///
|
||||
/// builder.body(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// A simple 404 handler
|
||||
///
|
||||
/// ```
|
||||
/// use http::{Request, Response, StatusCode};
|
||||
///
|
||||
/// fn not_found(_req: Request<()>) -> http::Result<Response<()>> {
|
||||
/// Response::builder()
|
||||
/// .status(StatusCode::NOT_FOUND)
|
||||
/// .body(())
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// Or otherwise inspecting the result of a request:
|
||||
///
|
||||
/// ```no_run
|
||||
/// use http::{Request, Response};
|
||||
///
|
||||
/// fn get(url: &str) -> http::Result<Response<()>> {
|
||||
/// // ...
|
||||
/// # panic!()
|
||||
/// }
|
||||
///
|
||||
/// let response = get("https://www.rust-lang.org/").unwrap();
|
||||
///
|
||||
/// if !response.status().is_success() {
|
||||
/// panic!("failed to get a successful response status!");
|
||||
/// }
|
||||
///
|
||||
/// if let Some(date) = response.headers().get("Date") {
|
||||
/// // we've got a `Date` header!
|
||||
/// }
|
||||
///
|
||||
/// let body = response.body();
|
||||
/// // ...
|
||||
/// ```
|
||||
///
|
||||
/// Deserialize a response of bytes via json:
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate serde;
|
||||
/// # extern crate serde_json;
|
||||
/// # extern crate http;
|
||||
/// use http::Response;
|
||||
/// use serde::de;
|
||||
///
|
||||
/// fn deserialize<T>(res: Response<Vec<u8>>) -> serde_json::Result<Response<T>>
|
||||
/// where for<'de> T: de::Deserialize<'de>,
|
||||
/// {
|
||||
/// let (parts, body) = res.into_parts();
|
||||
/// let body = serde_json::from_slice(&body)?;
|
||||
/// Ok(Response::from_parts(parts, body))
|
||||
/// }
|
||||
/// #
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
///
|
||||
/// Or alternatively, serialize the body of a response to json
|
||||
///
|
||||
/// ```
|
||||
/// # extern crate serde;
|
||||
/// # extern crate serde_json;
|
||||
/// # extern crate http;
|
||||
/// use http::Response;
|
||||
/// use serde::ser;
|
||||
///
|
||||
/// fn serialize<T>(res: Response<T>) -> serde_json::Result<Response<Vec<u8>>>
|
||||
/// where T: ser::Serialize,
|
||||
/// {
|
||||
/// let (parts, body) = res.into_parts();
|
||||
/// let body = serde_json::to_vec(&body)?;
|
||||
/// Ok(Response::from_parts(parts, body))
|
||||
/// }
|
||||
/// #
|
||||
/// # fn main() {}
|
||||
/// ```
|
||||
pub struct Response<T> {
|
||||
head: Parts,
|
||||
body: T,
|
||||
}
|
||||
|
||||
/// Component parts of an HTTP `Response`
|
||||
///
|
||||
/// The HTTP response head consists of a status, version, and a set of
|
||||
/// header fields.
|
||||
pub struct Parts {
|
||||
/// The response's status
|
||||
pub status: StatusCode,
|
||||
|
||||
/// The response's version
|
||||
pub version: Version,
|
||||
|
||||
/// The response's headers
|
||||
pub headers: HeaderMap<HeaderValue>,
|
||||
|
||||
/// The response's extensions
|
||||
pub extensions: Extensions,
|
||||
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
/// An HTTP response builder
|
||||
///
|
||||
/// This type can be used to construct an instance of `Response` through a
|
||||
/// builder-like pattern.
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
inner: Result<Parts>,
|
||||
}
|
||||
|
||||
impl Response<()> {
|
||||
/// Creates a new builder-style object to manufacture a `Response`
|
||||
///
|
||||
/// This method returns an instance of `Builder` which can be used to
|
||||
/// create a `Response`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response = Response::builder()
|
||||
/// .status(200)
|
||||
/// .header("X-Custom-Foo", "Bar")
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn builder() -> Builder {
|
||||
Builder::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Response<T> {
|
||||
/// Creates a new blank `Response` with the body
|
||||
///
|
||||
/// The component ports of this response will be set to their default, e.g.
|
||||
/// the ok status, no headers, etc.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response = Response::new("hello world");
|
||||
///
|
||||
/// assert_eq!(response.status(), StatusCode::OK);
|
||||
/// assert_eq!(*response.body(), "hello world");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new(body: T) -> Response<T> {
|
||||
Response {
|
||||
head: Parts::new(),
|
||||
body: body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new `Response` with the given head and body
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response = Response::new("hello world");
|
||||
/// let (mut parts, body) = response.into_parts();
|
||||
///
|
||||
/// parts.status = StatusCode::BAD_REQUEST;
|
||||
/// let response = Response::from_parts(parts, body);
|
||||
///
|
||||
/// assert_eq!(response.status(), StatusCode::BAD_REQUEST);
|
||||
/// assert_eq!(*response.body(), "hello world");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_parts(parts: Parts, body: T) -> Response<T> {
|
||||
Response {
|
||||
head: parts,
|
||||
body: body,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the `StatusCode`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// assert_eq!(response.status(), StatusCode::OK);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn status(&self) -> StatusCode {
|
||||
self.head.status
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated `StatusCode`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let mut response: Response<()> = Response::default();
|
||||
/// *response.status_mut() = StatusCode::CREATED;
|
||||
/// assert_eq!(response.status(), StatusCode::CREATED);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn status_mut(&mut self) -> &mut StatusCode {
|
||||
&mut self.head.status
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// assert_eq!(response.version(), Version::HTTP_11);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn version(&self) -> Version {
|
||||
self.head.version
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated version.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let mut response: Response<()> = Response::default();
|
||||
/// *response.version_mut() = Version::HTTP_2;
|
||||
/// assert_eq!(response.version(), Version::HTTP_2);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn version_mut(&mut self) -> &mut Version {
|
||||
&mut self.head.version
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated header field map.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// assert!(response.headers().is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn headers(&self) -> &HeaderMap<HeaderValue> {
|
||||
&self.head.headers
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated header field map.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// # use http::header::*;
|
||||
/// let mut response: Response<()> = Response::default();
|
||||
/// response.headers_mut().insert(HOST, HeaderValue::from_static("world"));
|
||||
/// assert!(!response.headers().is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn headers_mut(&mut self) -> &mut HeaderMap<HeaderValue> {
|
||||
&mut self.head.headers
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated extensions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// assert!(response.extensions().get::<i32>().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn extensions(&self) -> &Extensions {
|
||||
&self.head.extensions
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated extensions.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// # use http::header::*;
|
||||
/// let mut response: Response<()> = Response::default();
|
||||
/// response.extensions_mut().insert("hello");
|
||||
/// assert_eq!(response.extensions().get(), Some(&"hello"));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn extensions_mut(&mut self) -> &mut Extensions {
|
||||
&mut self.head.extensions
|
||||
}
|
||||
|
||||
/// Returns a reference to the associated HTTP body.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<String> = Response::default();
|
||||
/// assert!(response.body().is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn body(&self) -> &T {
|
||||
&self.body
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the associated HTTP body.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let mut response: Response<String> = Response::default();
|
||||
/// response.body_mut().push_str("hello world");
|
||||
/// assert!(!response.body().is_empty());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn body_mut(&mut self) -> &mut T {
|
||||
&mut self.body
|
||||
}
|
||||
|
||||
/// Consumes the response, returning just the body.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Response;
|
||||
/// let response = Response::new(10);
|
||||
/// let body = response.into_body();
|
||||
/// assert_eq!(body, 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn into_body(self) -> T {
|
||||
self.body
|
||||
}
|
||||
|
||||
/// Consumes the response returning the head and body parts.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response: Response<()> = Response::default();
|
||||
/// let (parts, body) = response.into_parts();
|
||||
/// assert_eq!(parts.status, StatusCode::OK);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn into_parts(self) -> (Parts, T) {
|
||||
(self.head, self.body)
|
||||
}
|
||||
|
||||
/// Consumes the response returning a new response with body mapped to the
|
||||
/// return type of the passed in function.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// let response = Response::builder().body("some string").unwrap();
|
||||
/// let mapped_response: Response<&[u8]> = response.map(|b| {
|
||||
/// assert_eq!(b, "some string");
|
||||
/// b.as_bytes()
|
||||
/// });
|
||||
/// assert_eq!(mapped_response.body(), &"some string".as_bytes());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn map<F, U>(self, f: F) -> Response<U>
|
||||
where
|
||||
F: FnOnce(T) -> U,
|
||||
{
|
||||
Response {
|
||||
body: f(self.body),
|
||||
head: self.head,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default> Default for Response<T> {
|
||||
#[inline]
|
||||
fn default() -> Response<T> {
|
||||
Response::new(T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for Response<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Response")
|
||||
.field("status", &self.status())
|
||||
.field("version", &self.version())
|
||||
.field("headers", self.headers())
|
||||
// omits Extensions because not useful
|
||||
.field("body", self.body())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Parts {
|
||||
/// Creates a new default instance of `Parts`
|
||||
fn new() -> Parts {
|
||||
Parts {
|
||||
status: StatusCode::default(),
|
||||
version: Version::default(),
|
||||
headers: HeaderMap::default(),
|
||||
extensions: Extensions::default(),
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Parts {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Parts")
|
||||
.field("status", &self.status)
|
||||
.field("version", &self.version)
|
||||
.field("headers", &self.headers)
|
||||
// omits Extensions because not useful
|
||||
// omits _priv because not useful
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new default instance of `Builder` to construct either a
|
||||
/// `Head` or a `Response`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = response::Builder::new()
|
||||
/// .status(200)
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new() -> Builder {
|
||||
Builder::default()
|
||||
}
|
||||
|
||||
/// Set the HTTP status for this response.
|
||||
///
|
||||
/// This function will configure the HTTP status code of the `Response` that
|
||||
/// will be returned from `Builder::build`.
|
||||
///
|
||||
/// By default this is `200`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .status(200)
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn status<T>(self, status: T) -> Builder
|
||||
where
|
||||
StatusCode: TryFrom<T>,
|
||||
<StatusCode as TryFrom<T>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.and_then(move |mut head| {
|
||||
head.status = TryFrom::try_from(status).map_err(Into::into)?;
|
||||
Ok(head)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the HTTP version for this response.
|
||||
///
|
||||
/// This function will configure the HTTP version of the `Response` that
|
||||
/// will be returned from `Builder::build`.
|
||||
///
|
||||
/// By default this is HTTP/1.1
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .version(Version::HTTP_2)
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn version(self, version: Version) -> Builder {
|
||||
self.and_then(move |mut head| {
|
||||
head.version = version;
|
||||
Ok(head)
|
||||
})
|
||||
}
|
||||
|
||||
/// Appends a header to this response builder.
|
||||
///
|
||||
/// This function will append the provided key/value as a header to the
|
||||
/// internal `HeaderMap` being constructed. Essentially this is equivalent
|
||||
/// to calling `HeaderMap::append`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// # use http::header::HeaderValue;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .header("Content-Type", "text/html")
|
||||
/// .header("X-Custom-Foo", "bar")
|
||||
/// .header("content-length", 0)
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn header<K, V>(self, key: K, value: V) -> Builder
|
||||
where
|
||||
HeaderName: TryFrom<K>,
|
||||
<HeaderName as TryFrom<K>>::Error: Into<crate::Error>,
|
||||
HeaderValue: TryFrom<V>,
|
||||
<HeaderValue as TryFrom<V>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.and_then(move |mut head| {
|
||||
let name = <HeaderName as TryFrom<K>>::try_from(key).map_err(Into::into)?;
|
||||
let value = <HeaderValue as TryFrom<V>>::try_from(value).map_err(Into::into)?;
|
||||
head.headers.append(name, value);
|
||||
Ok(head)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get header on this response builder.
|
||||
///
|
||||
/// When builder has error returns None.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Response;
|
||||
/// # use http::header::HeaderValue;
|
||||
/// let res = Response::builder()
|
||||
/// .header("Accept", "text/html")
|
||||
/// .header("X-Custom-Foo", "bar");
|
||||
/// let headers = res.headers_ref().unwrap();
|
||||
/// assert_eq!( headers["Accept"], "text/html" );
|
||||
/// assert_eq!( headers["X-Custom-Foo"], "bar" );
|
||||
/// ```
|
||||
pub fn headers_ref(&self) -> Option<&HeaderMap<HeaderValue>> {
|
||||
self.inner.as_ref().ok().map(|h| &h.headers)
|
||||
}
|
||||
|
||||
/// Get header on this response builder.
|
||||
/// when builder has error returns None
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
/// # use http::header::HeaderValue;
|
||||
/// # use http::response::Builder;
|
||||
/// let mut res = Response::builder();
|
||||
/// {
|
||||
/// let headers = res.headers_mut().unwrap();
|
||||
/// headers.insert("Accept", HeaderValue::from_static("text/html"));
|
||||
/// headers.insert("X-Custom-Foo", HeaderValue::from_static("bar"));
|
||||
/// }
|
||||
/// let headers = res.headers_ref().unwrap();
|
||||
/// assert_eq!( headers["Accept"], "text/html" );
|
||||
/// assert_eq!( headers["X-Custom-Foo"], "bar" );
|
||||
/// ```
|
||||
pub fn headers_mut(&mut self) -> Option<&mut HeaderMap<HeaderValue>> {
|
||||
self.inner.as_mut().ok().map(|h| &mut h.headers)
|
||||
}
|
||||
|
||||
/// Adds an extension to this builder
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .extension("My Extension")
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
///
|
||||
/// assert_eq!(response.extensions().get::<&'static str>(),
|
||||
/// Some(&"My Extension"));
|
||||
/// ```
|
||||
pub fn extension<T>(self, extension: T) -> Builder
|
||||
where
|
||||
T: Any + Send + Sync + 'static,
|
||||
{
|
||||
self.and_then(move |mut head| {
|
||||
head.extensions.insert(extension);
|
||||
Ok(head)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a reference to the extensions for this response builder.
|
||||
///
|
||||
/// If the builder has an error, this returns `None`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Response;
|
||||
/// let res = Response::builder().extension("My Extension").extension(5u32);
|
||||
/// let extensions = res.extensions_ref().unwrap();
|
||||
/// assert_eq!(extensions.get::<&'static str>(), Some(&"My Extension"));
|
||||
/// assert_eq!(extensions.get::<u32>(), Some(&5u32));
|
||||
/// ```
|
||||
pub fn extensions_ref(&self) -> Option<&Extensions> {
|
||||
self.inner.as_ref().ok().map(|h| &h.extensions)
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the extensions for this response builder.
|
||||
///
|
||||
/// If the builder has an error, this returns `None`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::Response;
|
||||
/// let mut res = Response::builder().extension("My Extension");
|
||||
/// let mut extensions = res.extensions_mut().unwrap();
|
||||
/// assert_eq!(extensions.get::<&'static str>(), Some(&"My Extension"));
|
||||
/// extensions.insert(5u32);
|
||||
/// assert_eq!(extensions.get::<u32>(), Some(&5u32));
|
||||
/// ```
|
||||
pub fn extensions_mut(&mut self) -> Option<&mut Extensions> {
|
||||
self.inner.as_mut().ok().map(|h| &mut h.extensions)
|
||||
}
|
||||
|
||||
/// "Consumes" this builder, using the provided `body` to return a
|
||||
/// constructed `Response`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may return an error if any previously configured argument
|
||||
/// failed to parse or get converted to the internal representation. For
|
||||
/// example if an invalid `head` was specified via `header("Foo",
|
||||
/// "Bar\r\n")` the error will be returned when this function is called
|
||||
/// rather than when `header` was called.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let response = Response::builder()
|
||||
/// .body(())
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn body<T>(self, body: T) -> Result<Response<T>> {
|
||||
self.inner.map(move |head| {
|
||||
Response {
|
||||
head,
|
||||
body,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
fn and_then<F>(self, func: F) -> Self
|
||||
where
|
||||
F: FnOnce(Parts) -> Result<Parts>
|
||||
{
|
||||
Builder {
|
||||
inner: self.inner.and_then(func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
#[inline]
|
||||
fn default() -> Builder {
|
||||
Builder {
|
||||
inner: Ok(Parts::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn it_can_map_a_body_from_one_type_to_another() {
|
||||
let response = Response::builder().body("some string").unwrap();
|
||||
let mapped_response = response.map(|s| {
|
||||
assert_eq!(s, "some string");
|
||||
123u32
|
||||
});
|
||||
assert_eq!(mapped_response.body(), &123u32);
|
||||
}
|
||||
}
|
||||
588
zeroidc/vendor/http/src/status.rs
vendored
Normal file
588
zeroidc/vendor/http/src/status.rs
vendored
Normal file
@@ -0,0 +1,588 @@
|
||||
//! HTTP status codes
|
||||
//!
|
||||
//! This module contains HTTP-status code related structs an errors. The main
|
||||
//! type in this module is `StatusCode` which is not intended to be used through
|
||||
//! this module but rather the `http::StatusCode` type.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use http::StatusCode;
|
||||
//!
|
||||
//! assert_eq!(StatusCode::from_u16(200).unwrap(), StatusCode::OK);
|
||||
//! assert_eq!(StatusCode::NOT_FOUND, 404);
|
||||
//! assert!(StatusCode::OK.is_success());
|
||||
//! ```
|
||||
|
||||
use std::convert::TryFrom;
|
||||
use std::num::NonZeroU16;
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// An HTTP status code (`status-code` in RFC 7230 et al.).
|
||||
///
|
||||
/// Constants are provided for known status codes, including those in the IANA
|
||||
/// [HTTP Status Code Registry](
|
||||
/// https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml).
|
||||
///
|
||||
/// Status code values in the range 100-999 (inclusive) are supported by this
|
||||
/// type. Values in the range 100-599 are semantically classified by the most
|
||||
/// significant digit. See [`StatusCode::is_success`], etc. Values above 599
|
||||
/// are unclassified but allowed for legacy compatibility, though their use is
|
||||
/// discouraged. Applications may interpret such values as protocol errors.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use http::StatusCode;
|
||||
///
|
||||
/// assert_eq!(StatusCode::from_u16(200).unwrap(), StatusCode::OK);
|
||||
/// assert_eq!(StatusCode::NOT_FOUND.as_u16(), 404);
|
||||
/// assert!(StatusCode::OK.is_success());
|
||||
/// ```
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct StatusCode(NonZeroU16);
|
||||
|
||||
/// A possible error value when converting a `StatusCode` from a `u16` or `&str`
|
||||
///
|
||||
/// This error indicates that the supplied input was not a valid number, was less
|
||||
/// than 100, or was greater than 999.
|
||||
pub struct InvalidStatusCode {
|
||||
_priv: (),
|
||||
}
|
||||
|
||||
impl StatusCode {
|
||||
/// Converts a u16 to a status code.
|
||||
///
|
||||
/// The function validates the correctness of the supplied u16. It must be
|
||||
/// greater or equal to 100 and less than 1000.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use http::StatusCode;
|
||||
///
|
||||
/// let ok = StatusCode::from_u16(200).unwrap();
|
||||
/// assert_eq!(ok, StatusCode::OK);
|
||||
///
|
||||
/// let err = StatusCode::from_u16(99);
|
||||
/// assert!(err.is_err());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_u16(src: u16) -> Result<StatusCode, InvalidStatusCode> {
|
||||
if src < 100 || src >= 1000 {
|
||||
return Err(InvalidStatusCode::new());
|
||||
}
|
||||
|
||||
NonZeroU16::new(src)
|
||||
.map(StatusCode)
|
||||
.ok_or_else(InvalidStatusCode::new)
|
||||
}
|
||||
|
||||
/// Converts a &[u8] to a status code
|
||||
pub fn from_bytes(src: &[u8]) -> Result<StatusCode, InvalidStatusCode> {
|
||||
if src.len() != 3 {
|
||||
return Err(InvalidStatusCode::new());
|
||||
}
|
||||
|
||||
let a = src[0].wrapping_sub(b'0') as u16;
|
||||
let b = src[1].wrapping_sub(b'0') as u16;
|
||||
let c = src[2].wrapping_sub(b'0') as u16;
|
||||
|
||||
if a == 0 || a > 9 || b > 9 || c > 9 {
|
||||
return Err(InvalidStatusCode::new());
|
||||
}
|
||||
|
||||
let status = (a * 100) + (b * 10) + c;
|
||||
NonZeroU16::new(status)
|
||||
.map(StatusCode)
|
||||
.ok_or_else(InvalidStatusCode::new)
|
||||
}
|
||||
|
||||
/// Returns the `u16` corresponding to this `StatusCode`.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// This is the same as the `From<StatusCode>` implementation, but
|
||||
/// included as an inherent method because that implementation doesn't
|
||||
/// appear in rustdocs, as well as a way to force the type instead of
|
||||
/// relying on inference.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let status = http::StatusCode::OK;
|
||||
/// assert_eq!(status.as_u16(), 200);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_u16(&self) -> u16 {
|
||||
(*self).into()
|
||||
}
|
||||
|
||||
/// Returns a &str representation of the `StatusCode`
|
||||
///
|
||||
/// The return value only includes a numerical representation of the
|
||||
/// status code. The canonical reason is not included.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let status = http::StatusCode::OK;
|
||||
/// assert_eq!(status.as_str(), "200");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
let offset = (self.0.get() - 100) as usize;
|
||||
let offset = offset * 3;
|
||||
|
||||
// Invariant: self has checked range [100, 999] and CODE_DIGITS is
|
||||
// ASCII-only, of length 900 * 3 = 2700 bytes
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
{ &CODE_DIGITS[offset..offset+3] }
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
unsafe { CODE_DIGITS.get_unchecked(offset..offset+3) }
|
||||
}
|
||||
|
||||
/// Get the standardised `reason-phrase` for this status code.
|
||||
///
|
||||
/// This is mostly here for servers writing responses, but could potentially have application
|
||||
/// at other times.
|
||||
///
|
||||
/// The reason phrase is defined as being exclusively for human readers. You should avoid
|
||||
/// deriving any meaning from it at all costs.
|
||||
///
|
||||
/// Bear in mind also that in HTTP/2.0 and HTTP/3.0 the reason phrase is abolished from
|
||||
/// transmission, and so this canonical reason phrase really is the only reason phrase you’ll
|
||||
/// find.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// let status = http::StatusCode::OK;
|
||||
/// assert_eq!(status.canonical_reason(), Some("OK"));
|
||||
/// ```
|
||||
pub fn canonical_reason(&self) -> Option<&'static str> {
|
||||
canonical_reason(self.0.get())
|
||||
}
|
||||
|
||||
/// Check if status is within 100-199.
|
||||
#[inline]
|
||||
pub fn is_informational(&self) -> bool {
|
||||
200 > self.0.get() && self.0.get() >= 100
|
||||
}
|
||||
|
||||
/// Check if status is within 200-299.
|
||||
#[inline]
|
||||
pub fn is_success(&self) -> bool {
|
||||
300 > self.0.get() && self.0.get() >= 200
|
||||
}
|
||||
|
||||
/// Check if status is within 300-399.
|
||||
#[inline]
|
||||
pub fn is_redirection(&self) -> bool {
|
||||
400 > self.0.get() && self.0.get() >= 300
|
||||
}
|
||||
|
||||
/// Check if status is within 400-499.
|
||||
#[inline]
|
||||
pub fn is_client_error(&self) -> bool {
|
||||
500 > self.0.get() && self.0.get() >= 400
|
||||
}
|
||||
|
||||
/// Check if status is within 500-599.
|
||||
#[inline]
|
||||
pub fn is_server_error(&self) -> bool {
|
||||
600 > self.0.get() && self.0.get() >= 500
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for StatusCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Formats the status code, *including* the canonical reason.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::StatusCode;
|
||||
/// assert_eq!(format!("{}", StatusCode::OK), "200 OK");
|
||||
/// ```
|
||||
impl fmt::Display for StatusCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{} {}",
|
||||
u16::from(*self),
|
||||
self.canonical_reason().unwrap_or("<unknown status code>")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for StatusCode {
|
||||
#[inline]
|
||||
fn default() -> StatusCode {
|
||||
StatusCode::OK
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<u16> for StatusCode {
|
||||
#[inline]
|
||||
fn eq(&self, other: &u16) -> bool {
|
||||
self.as_u16() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<StatusCode> for u16 {
|
||||
#[inline]
|
||||
fn eq(&self, other: &StatusCode) -> bool {
|
||||
*self == other.as_u16()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StatusCode> for u16 {
|
||||
#[inline]
|
||||
fn from(status: StatusCode) -> u16 {
|
||||
status.0.get()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for StatusCode {
|
||||
type Err = InvalidStatusCode;
|
||||
|
||||
fn from_str(s: &str) -> Result<StatusCode, InvalidStatusCode> {
|
||||
StatusCode::from_bytes(s.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a StatusCode> for StatusCode {
|
||||
#[inline]
|
||||
fn from(t: &'a StatusCode) -> Self {
|
||||
t.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for StatusCode {
|
||||
type Error = InvalidStatusCode;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
StatusCode::from_bytes(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for StatusCode {
|
||||
type Error = InvalidStatusCode;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
|
||||
t.parse()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u16> for StatusCode {
|
||||
type Error = InvalidStatusCode;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: u16) -> Result<Self, Self::Error> {
|
||||
StatusCode::from_u16(t)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! status_codes {
|
||||
(
|
||||
$(
|
||||
$(#[$docs:meta])*
|
||||
($num:expr, $konst:ident, $phrase:expr);
|
||||
)+
|
||||
) => {
|
||||
impl StatusCode {
|
||||
$(
|
||||
$(#[$docs])*
|
||||
pub const $konst: StatusCode = StatusCode(unsafe { NonZeroU16::new_unchecked($num) });
|
||||
)+
|
||||
|
||||
}
|
||||
|
||||
fn canonical_reason(num: u16) -> Option<&'static str> {
|
||||
match num {
|
||||
$(
|
||||
$num => Some($phrase),
|
||||
)+
|
||||
_ => None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
status_codes! {
|
||||
/// 100 Continue
|
||||
/// [[RFC7231, Section 6.2.1](https://tools.ietf.org/html/rfc7231#section-6.2.1)]
|
||||
(100, CONTINUE, "Continue");
|
||||
/// 101 Switching Protocols
|
||||
/// [[RFC7231, Section 6.2.2](https://tools.ietf.org/html/rfc7231#section-6.2.2)]
|
||||
(101, SWITCHING_PROTOCOLS, "Switching Protocols");
|
||||
/// 102 Processing
|
||||
/// [[RFC2518](https://tools.ietf.org/html/rfc2518)]
|
||||
(102, PROCESSING, "Processing");
|
||||
|
||||
/// 200 OK
|
||||
/// [[RFC7231, Section 6.3.1](https://tools.ietf.org/html/rfc7231#section-6.3.1)]
|
||||
(200, OK, "OK");
|
||||
/// 201 Created
|
||||
/// [[RFC7231, Section 6.3.2](https://tools.ietf.org/html/rfc7231#section-6.3.2)]
|
||||
(201, CREATED, "Created");
|
||||
/// 202 Accepted
|
||||
/// [[RFC7231, Section 6.3.3](https://tools.ietf.org/html/rfc7231#section-6.3.3)]
|
||||
(202, ACCEPTED, "Accepted");
|
||||
/// 203 Non-Authoritative Information
|
||||
/// [[RFC7231, Section 6.3.4](https://tools.ietf.org/html/rfc7231#section-6.3.4)]
|
||||
(203, NON_AUTHORITATIVE_INFORMATION, "Non Authoritative Information");
|
||||
/// 204 No Content
|
||||
/// [[RFC7231, Section 6.3.5](https://tools.ietf.org/html/rfc7231#section-6.3.5)]
|
||||
(204, NO_CONTENT, "No Content");
|
||||
/// 205 Reset Content
|
||||
/// [[RFC7231, Section 6.3.6](https://tools.ietf.org/html/rfc7231#section-6.3.6)]
|
||||
(205, RESET_CONTENT, "Reset Content");
|
||||
/// 206 Partial Content
|
||||
/// [[RFC7233, Section 4.1](https://tools.ietf.org/html/rfc7233#section-4.1)]
|
||||
(206, PARTIAL_CONTENT, "Partial Content");
|
||||
/// 207 Multi-Status
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(207, MULTI_STATUS, "Multi-Status");
|
||||
/// 208 Already Reported
|
||||
/// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
|
||||
(208, ALREADY_REPORTED, "Already Reported");
|
||||
|
||||
/// 226 IM Used
|
||||
/// [[RFC3229](https://tools.ietf.org/html/rfc3229)]
|
||||
(226, IM_USED, "IM Used");
|
||||
|
||||
/// 300 Multiple Choices
|
||||
/// [[RFC7231, Section 6.4.1](https://tools.ietf.org/html/rfc7231#section-6.4.1)]
|
||||
(300, MULTIPLE_CHOICES, "Multiple Choices");
|
||||
/// 301 Moved Permanently
|
||||
/// [[RFC7231, Section 6.4.2](https://tools.ietf.org/html/rfc7231#section-6.4.2)]
|
||||
(301, MOVED_PERMANENTLY, "Moved Permanently");
|
||||
/// 302 Found
|
||||
/// [[RFC7231, Section 6.4.3](https://tools.ietf.org/html/rfc7231#section-6.4.3)]
|
||||
(302, FOUND, "Found");
|
||||
/// 303 See Other
|
||||
/// [[RFC7231, Section 6.4.4](https://tools.ietf.org/html/rfc7231#section-6.4.4)]
|
||||
(303, SEE_OTHER, "See Other");
|
||||
/// 304 Not Modified
|
||||
/// [[RFC7232, Section 4.1](https://tools.ietf.org/html/rfc7232#section-4.1)]
|
||||
(304, NOT_MODIFIED, "Not Modified");
|
||||
/// 305 Use Proxy
|
||||
/// [[RFC7231, Section 6.4.5](https://tools.ietf.org/html/rfc7231#section-6.4.5)]
|
||||
(305, USE_PROXY, "Use Proxy");
|
||||
/// 307 Temporary Redirect
|
||||
/// [[RFC7231, Section 6.4.7](https://tools.ietf.org/html/rfc7231#section-6.4.7)]
|
||||
(307, TEMPORARY_REDIRECT, "Temporary Redirect");
|
||||
/// 308 Permanent Redirect
|
||||
/// [[RFC7238](https://tools.ietf.org/html/rfc7238)]
|
||||
(308, PERMANENT_REDIRECT, "Permanent Redirect");
|
||||
|
||||
/// 400 Bad Request
|
||||
/// [[RFC7231, Section 6.5.1](https://tools.ietf.org/html/rfc7231#section-6.5.1)]
|
||||
(400, BAD_REQUEST, "Bad Request");
|
||||
/// 401 Unauthorized
|
||||
/// [[RFC7235, Section 3.1](https://tools.ietf.org/html/rfc7235#section-3.1)]
|
||||
(401, UNAUTHORIZED, "Unauthorized");
|
||||
/// 402 Payment Required
|
||||
/// [[RFC7231, Section 6.5.2](https://tools.ietf.org/html/rfc7231#section-6.5.2)]
|
||||
(402, PAYMENT_REQUIRED, "Payment Required");
|
||||
/// 403 Forbidden
|
||||
/// [[RFC7231, Section 6.5.3](https://tools.ietf.org/html/rfc7231#section-6.5.3)]
|
||||
(403, FORBIDDEN, "Forbidden");
|
||||
/// 404 Not Found
|
||||
/// [[RFC7231, Section 6.5.4](https://tools.ietf.org/html/rfc7231#section-6.5.4)]
|
||||
(404, NOT_FOUND, "Not Found");
|
||||
/// 405 Method Not Allowed
|
||||
/// [[RFC7231, Section 6.5.5](https://tools.ietf.org/html/rfc7231#section-6.5.5)]
|
||||
(405, METHOD_NOT_ALLOWED, "Method Not Allowed");
|
||||
/// 406 Not Acceptable
|
||||
/// [[RFC7231, Section 6.5.6](https://tools.ietf.org/html/rfc7231#section-6.5.6)]
|
||||
(406, NOT_ACCEPTABLE, "Not Acceptable");
|
||||
/// 407 Proxy Authentication Required
|
||||
/// [[RFC7235, Section 3.2](https://tools.ietf.org/html/rfc7235#section-3.2)]
|
||||
(407, PROXY_AUTHENTICATION_REQUIRED, "Proxy Authentication Required");
|
||||
/// 408 Request Timeout
|
||||
/// [[RFC7231, Section 6.5.7](https://tools.ietf.org/html/rfc7231#section-6.5.7)]
|
||||
(408, REQUEST_TIMEOUT, "Request Timeout");
|
||||
/// 409 Conflict
|
||||
/// [[RFC7231, Section 6.5.8](https://tools.ietf.org/html/rfc7231#section-6.5.8)]
|
||||
(409, CONFLICT, "Conflict");
|
||||
/// 410 Gone
|
||||
/// [[RFC7231, Section 6.5.9](https://tools.ietf.org/html/rfc7231#section-6.5.9)]
|
||||
(410, GONE, "Gone");
|
||||
/// 411 Length Required
|
||||
/// [[RFC7231, Section 6.5.10](https://tools.ietf.org/html/rfc7231#section-6.5.10)]
|
||||
(411, LENGTH_REQUIRED, "Length Required");
|
||||
/// 412 Precondition Failed
|
||||
/// [[RFC7232, Section 4.2](https://tools.ietf.org/html/rfc7232#section-4.2)]
|
||||
(412, PRECONDITION_FAILED, "Precondition Failed");
|
||||
/// 413 Payload Too Large
|
||||
/// [[RFC7231, Section 6.5.11](https://tools.ietf.org/html/rfc7231#section-6.5.11)]
|
||||
(413, PAYLOAD_TOO_LARGE, "Payload Too Large");
|
||||
/// 414 URI Too Long
|
||||
/// [[RFC7231, Section 6.5.12](https://tools.ietf.org/html/rfc7231#section-6.5.12)]
|
||||
(414, URI_TOO_LONG, "URI Too Long");
|
||||
/// 415 Unsupported Media Type
|
||||
/// [[RFC7231, Section 6.5.13](https://tools.ietf.org/html/rfc7231#section-6.5.13)]
|
||||
(415, UNSUPPORTED_MEDIA_TYPE, "Unsupported Media Type");
|
||||
/// 416 Range Not Satisfiable
|
||||
/// [[RFC7233, Section 4.4](https://tools.ietf.org/html/rfc7233#section-4.4)]
|
||||
(416, RANGE_NOT_SATISFIABLE, "Range Not Satisfiable");
|
||||
/// 417 Expectation Failed
|
||||
/// [[RFC7231, Section 6.5.14](https://tools.ietf.org/html/rfc7231#section-6.5.14)]
|
||||
(417, EXPECTATION_FAILED, "Expectation Failed");
|
||||
/// 418 I'm a teapot
|
||||
/// [curiously not registered by IANA but [RFC2324](https://tools.ietf.org/html/rfc2324)]
|
||||
(418, IM_A_TEAPOT, "I'm a teapot");
|
||||
|
||||
/// 421 Misdirected Request
|
||||
/// [RFC7540, Section 9.1.2](http://tools.ietf.org/html/rfc7540#section-9.1.2)
|
||||
(421, MISDIRECTED_REQUEST, "Misdirected Request");
|
||||
/// 422 Unprocessable Entity
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(422, UNPROCESSABLE_ENTITY, "Unprocessable Entity");
|
||||
/// 423 Locked
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(423, LOCKED, "Locked");
|
||||
/// 424 Failed Dependency
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(424, FAILED_DEPENDENCY, "Failed Dependency");
|
||||
|
||||
/// 426 Upgrade Required
|
||||
/// [[RFC7231, Section 6.5.15](https://tools.ietf.org/html/rfc7231#section-6.5.15)]
|
||||
(426, UPGRADE_REQUIRED, "Upgrade Required");
|
||||
|
||||
/// 428 Precondition Required
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
(428, PRECONDITION_REQUIRED, "Precondition Required");
|
||||
/// 429 Too Many Requests
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
(429, TOO_MANY_REQUESTS, "Too Many Requests");
|
||||
|
||||
/// 431 Request Header Fields Too Large
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
(431, REQUEST_HEADER_FIELDS_TOO_LARGE, "Request Header Fields Too Large");
|
||||
|
||||
/// 451 Unavailable For Legal Reasons
|
||||
/// [[RFC7725](http://tools.ietf.org/html/rfc7725)]
|
||||
(451, UNAVAILABLE_FOR_LEGAL_REASONS, "Unavailable For Legal Reasons");
|
||||
|
||||
/// 500 Internal Server Error
|
||||
/// [[RFC7231, Section 6.6.1](https://tools.ietf.org/html/rfc7231#section-6.6.1)]
|
||||
(500, INTERNAL_SERVER_ERROR, "Internal Server Error");
|
||||
/// 501 Not Implemented
|
||||
/// [[RFC7231, Section 6.6.2](https://tools.ietf.org/html/rfc7231#section-6.6.2)]
|
||||
(501, NOT_IMPLEMENTED, "Not Implemented");
|
||||
/// 502 Bad Gateway
|
||||
/// [[RFC7231, Section 6.6.3](https://tools.ietf.org/html/rfc7231#section-6.6.3)]
|
||||
(502, BAD_GATEWAY, "Bad Gateway");
|
||||
/// 503 Service Unavailable
|
||||
/// [[RFC7231, Section 6.6.4](https://tools.ietf.org/html/rfc7231#section-6.6.4)]
|
||||
(503, SERVICE_UNAVAILABLE, "Service Unavailable");
|
||||
/// 504 Gateway Timeout
|
||||
/// [[RFC7231, Section 6.6.5](https://tools.ietf.org/html/rfc7231#section-6.6.5)]
|
||||
(504, GATEWAY_TIMEOUT, "Gateway Timeout");
|
||||
/// 505 HTTP Version Not Supported
|
||||
/// [[RFC7231, Section 6.6.6](https://tools.ietf.org/html/rfc7231#section-6.6.6)]
|
||||
(505, HTTP_VERSION_NOT_SUPPORTED, "HTTP Version Not Supported");
|
||||
/// 506 Variant Also Negotiates
|
||||
/// [[RFC2295](https://tools.ietf.org/html/rfc2295)]
|
||||
(506, VARIANT_ALSO_NEGOTIATES, "Variant Also Negotiates");
|
||||
/// 507 Insufficient Storage
|
||||
/// [[RFC4918](https://tools.ietf.org/html/rfc4918)]
|
||||
(507, INSUFFICIENT_STORAGE, "Insufficient Storage");
|
||||
/// 508 Loop Detected
|
||||
/// [[RFC5842](https://tools.ietf.org/html/rfc5842)]
|
||||
(508, LOOP_DETECTED, "Loop Detected");
|
||||
|
||||
/// 510 Not Extended
|
||||
/// [[RFC2774](https://tools.ietf.org/html/rfc2774)]
|
||||
(510, NOT_EXTENDED, "Not Extended");
|
||||
/// 511 Network Authentication Required
|
||||
/// [[RFC6585](https://tools.ietf.org/html/rfc6585)]
|
||||
(511, NETWORK_AUTHENTICATION_REQUIRED, "Network Authentication Required");
|
||||
}
|
||||
|
||||
impl InvalidStatusCode {
|
||||
fn new() -> InvalidStatusCode {
|
||||
InvalidStatusCode {
|
||||
_priv: (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for InvalidStatusCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("InvalidStatusCode")
|
||||
// skip _priv noise
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for InvalidStatusCode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("invalid status code")
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for InvalidStatusCode {}
|
||||
|
||||
// A string of packed 3-ASCII-digit status code values for the supported range
|
||||
// of [100, 999] (900 codes, 2700 bytes).
|
||||
const CODE_DIGITS: &'static str = "\
|
||||
100101102103104105106107108109110111112113114115116117118119\
|
||||
120121122123124125126127128129130131132133134135136137138139\
|
||||
140141142143144145146147148149150151152153154155156157158159\
|
||||
160161162163164165166167168169170171172173174175176177178179\
|
||||
180181182183184185186187188189190191192193194195196197198199\
|
||||
200201202203204205206207208209210211212213214215216217218219\
|
||||
220221222223224225226227228229230231232233234235236237238239\
|
||||
240241242243244245246247248249250251252253254255256257258259\
|
||||
260261262263264265266267268269270271272273274275276277278279\
|
||||
280281282283284285286287288289290291292293294295296297298299\
|
||||
300301302303304305306307308309310311312313314315316317318319\
|
||||
320321322323324325326327328329330331332333334335336337338339\
|
||||
340341342343344345346347348349350351352353354355356357358359\
|
||||
360361362363364365366367368369370371372373374375376377378379\
|
||||
380381382383384385386387388389390391392393394395396397398399\
|
||||
400401402403404405406407408409410411412413414415416417418419\
|
||||
420421422423424425426427428429430431432433434435436437438439\
|
||||
440441442443444445446447448449450451452453454455456457458459\
|
||||
460461462463464465466467468469470471472473474475476477478479\
|
||||
480481482483484485486487488489490491492493494495496497498499\
|
||||
500501502503504505506507508509510511512513514515516517518519\
|
||||
520521522523524525526527528529530531532533534535536537538539\
|
||||
540541542543544545546547548549550551552553554555556557558559\
|
||||
560561562563564565566567568569570571572573574575576577578579\
|
||||
580581582583584585586587588589590591592593594595596597598599\
|
||||
600601602603604605606607608609610611612613614615616617618619\
|
||||
620621622623624625626627628629630631632633634635636637638639\
|
||||
640641642643644645646647648649650651652653654655656657658659\
|
||||
660661662663664665666667668669670671672673674675676677678679\
|
||||
680681682683684685686687688689690691692693694695696697698699\
|
||||
700701702703704705706707708709710711712713714715716717718719\
|
||||
720721722723724725726727728729730731732733734735736737738739\
|
||||
740741742743744745746747748749750751752753754755756757758759\
|
||||
760761762763764765766767768769770771772773774775776777778779\
|
||||
780781782783784785786787788789790791792793794795796797798799\
|
||||
800801802803804805806807808809810811812813814815816817818819\
|
||||
820821822823824825826827828829830831832833834835836837838839\
|
||||
840841842843844845846847848849850851852853854855856857858859\
|
||||
860861862863864865866867868869870871872873874875876877878879\
|
||||
880881882883884885886887888889890891892893894895896897898899\
|
||||
900901902903904905906907908909910911912913914915916917918919\
|
||||
920921922923924925926927928929930931932933934935936937938939\
|
||||
940941942943944945946947948949950951952953954955956957958959\
|
||||
960961962963964965966967968969970971972973974975976977978979\
|
||||
980981982983984985986987988989990991992993994995996997998999";
|
||||
671
zeroidc/vendor/http/src/uri/authority.rs
vendored
Normal file
671
zeroidc/vendor/http/src/uri/authority.rs
vendored
Normal file
@@ -0,0 +1,671 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::str::FromStr;
|
||||
use std::{cmp, fmt, str};
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use super::{ErrorKind, InvalidUri, Port, URI_CHARS};
|
||||
use crate::byte_str::ByteStr;
|
||||
|
||||
/// Represents the authority component of a URI.
|
||||
#[derive(Clone)]
|
||||
pub struct Authority {
|
||||
pub(super) data: ByteStr,
|
||||
}
|
||||
|
||||
impl Authority {
|
||||
pub(super) fn empty() -> Self {
|
||||
Authority {
|
||||
data: ByteStr::new(),
|
||||
}
|
||||
}
|
||||
|
||||
// Not public while `bytes` is unstable.
|
||||
pub(super) fn from_shared(s: Bytes) -> Result<Self, InvalidUri> {
|
||||
// Precondition on create_authority: trivially satisfied by the
|
||||
// identity clousre
|
||||
create_authority(s, |s| s)
|
||||
}
|
||||
|
||||
/// Attempt to convert an `Authority` from a static string.
|
||||
///
|
||||
/// This function will not perform any copying, and the string will be
|
||||
/// checked if it is empty or contains an invalid character.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the argument contains invalid characters or
|
||||
/// is empty.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority = Authority::from_static("example.com");
|
||||
/// assert_eq!(authority.host(), "example.com");
|
||||
/// ```
|
||||
pub fn from_static(src: &'static str) -> Self {
|
||||
Authority::from_shared(Bytes::from_static(src.as_bytes()))
|
||||
.expect("static str is not valid authority")
|
||||
}
|
||||
|
||||
/// Attempt to convert a `Bytes` buffer to a `Authority`.
|
||||
///
|
||||
/// This will try to prevent a copy if the type passed is the type used
|
||||
/// internally, and will copy the data if it is not.
|
||||
pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
|
||||
where
|
||||
T: AsRef<[u8]> + 'static,
|
||||
{
|
||||
if_downcast_into!(T, Bytes, src, {
|
||||
return Authority::from_shared(src);
|
||||
});
|
||||
|
||||
Authority::try_from(src.as_ref())
|
||||
}
|
||||
|
||||
// Note: this may return an *empty* Authority. You might want `parse_non_empty`.
|
||||
// Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
|
||||
// ret is the return value.
|
||||
pub(super) fn parse(s: &[u8]) -> Result<usize, InvalidUri> {
|
||||
let mut colon_cnt = 0;
|
||||
let mut start_bracket = false;
|
||||
let mut end_bracket = false;
|
||||
let mut has_percent = false;
|
||||
let mut end = s.len();
|
||||
let mut at_sign_pos = None;
|
||||
|
||||
// Among other things, this loop checks that every byte in s up to the
|
||||
// first '/', '?', or '#' is a valid URI character (or in some contexts,
|
||||
// a '%'). This means that each such byte is a valid single-byte UTF-8
|
||||
// code point.
|
||||
for (i, &b) in s.iter().enumerate() {
|
||||
match URI_CHARS[b as usize] {
|
||||
b'/' | b'?' | b'#' => {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
b':' => {
|
||||
colon_cnt += 1;
|
||||
}
|
||||
b'[' => {
|
||||
if has_percent || start_bracket {
|
||||
// Something other than the userinfo has a `%`, so reject it.
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
start_bracket = true;
|
||||
}
|
||||
b']' => {
|
||||
if end_bracket {
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
end_bracket = true;
|
||||
|
||||
// Those were part of an IPv6 hostname, so forget them...
|
||||
colon_cnt = 0;
|
||||
has_percent = false;
|
||||
}
|
||||
b'@' => {
|
||||
at_sign_pos = Some(i);
|
||||
|
||||
// Those weren't a port colon, but part of the
|
||||
// userinfo, so it needs to be forgotten.
|
||||
colon_cnt = 0;
|
||||
has_percent = false;
|
||||
}
|
||||
0 if b == b'%' => {
|
||||
// Per https://tools.ietf.org/html/rfc3986#section-3.2.1 and
|
||||
// https://url.spec.whatwg.org/#authority-state
|
||||
// the userinfo can have a percent-encoded username and password,
|
||||
// so record that a `%` was found. If this turns out to be
|
||||
// part of the userinfo, this flag will be cleared.
|
||||
// Also per https://tools.ietf.org/html/rfc6874, percent-encoding can
|
||||
// be used to indicate a zone identifier.
|
||||
// If the flag hasn't been cleared at the end, that means this
|
||||
// was part of the hostname (and not part of an IPv6 address), and
|
||||
// will fail with an error.
|
||||
has_percent = true;
|
||||
}
|
||||
0 => {
|
||||
return Err(ErrorKind::InvalidUriChar.into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if start_bracket ^ end_bracket {
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
|
||||
if colon_cnt > 1 {
|
||||
// Things like 'localhost:8080:3030' are rejected.
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
|
||||
if end > 0 && at_sign_pos == Some(end - 1) {
|
||||
// If there's nothing after an `@`, this is bonkers.
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
|
||||
if has_percent {
|
||||
// Something after the userinfo has a `%`, so reject it.
|
||||
return Err(ErrorKind::InvalidAuthority.into());
|
||||
}
|
||||
|
||||
Ok(end)
|
||||
}
|
||||
|
||||
// Parse bytes as an Authority, not allowing an empty string.
|
||||
//
|
||||
// This should be used by functions that allow a user to parse
|
||||
// an `Authority` by itself.
|
||||
//
|
||||
// Postcondition: for all Ok() returns, s[..ret.unwrap()] is valid UTF-8 where
|
||||
// ret is the return value.
|
||||
fn parse_non_empty(s: &[u8]) -> Result<usize, InvalidUri> {
|
||||
if s.is_empty() {
|
||||
return Err(ErrorKind::Empty.into());
|
||||
}
|
||||
Authority::parse(s)
|
||||
}
|
||||
|
||||
/// Get the host of this `Authority`.
|
||||
///
|
||||
/// The host subcomponent of authority is identified by an IP literal
|
||||
/// encapsulated within square brackets, an IPv4 address in dotted- decimal
|
||||
/// form, or a registered name. The host subcomponent is **case-insensitive**.
|
||||
///
|
||||
/// ```notrust
|
||||
/// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|
||||
/// |---------|
|
||||
/// |
|
||||
/// host
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(authority.host(), "example.org");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn host(&self) -> &str {
|
||||
host(self.as_str())
|
||||
}
|
||||
|
||||
/// Get the port part of this `Authority`.
|
||||
///
|
||||
/// The port subcomponent of authority is designated by an optional port
|
||||
/// number following the host and delimited from it by a single colon (":")
|
||||
/// character. It can be turned into a decimal port number with the `as_u16`
|
||||
/// method or as a `str` with the `as_str` method.
|
||||
///
|
||||
/// ```notrust
|
||||
/// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|
||||
/// |-|
|
||||
/// |
|
||||
/// port
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Authority with port
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// let port = authority.port().unwrap();
|
||||
/// assert_eq!(port.as_u16(), 80);
|
||||
/// assert_eq!(port.as_str(), "80");
|
||||
/// ```
|
||||
///
|
||||
/// Authority without port
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org".parse().unwrap();
|
||||
///
|
||||
/// assert!(authority.port().is_none());
|
||||
/// ```
|
||||
pub fn port(&self) -> Option<Port<&str>> {
|
||||
let bytes = self.as_str();
|
||||
bytes
|
||||
.rfind(":")
|
||||
.and_then(|i| Port::from_str(&bytes[i + 1..]).ok())
|
||||
}
|
||||
|
||||
/// Get the port of this `Authority` as a `u16`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(authority.port_u16(), Some(80));
|
||||
/// ```
|
||||
pub fn port_u16(&self) -> Option<u16> {
|
||||
self.port().and_then(|p| Some(p.as_u16()))
|
||||
}
|
||||
|
||||
/// Return a str representation of the authority
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
&self.data[..]
|
||||
}
|
||||
}
|
||||
|
||||
// Purposefully not public while `bytes` is unstable.
|
||||
// impl TryFrom<Bytes> for Authority
|
||||
|
||||
impl AsRef<str> for Authority {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Authority {
|
||||
fn eq(&self, other: &Authority) -> bool {
|
||||
self.data.eq_ignore_ascii_case(&other.data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Authority {}
|
||||
|
||||
/// Case-insensitive equality
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "HELLO.com".parse().unwrap();
|
||||
/// assert_eq!(authority, "hello.coM");
|
||||
/// assert_eq!("hello.com", authority);
|
||||
/// ```
|
||||
impl PartialEq<str> for Authority {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.data.eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Authority> for str {
|
||||
fn eq(&self, other: &Authority) -> bool {
|
||||
self.eq_ignore_ascii_case(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<Authority> for &'a str {
|
||||
fn eq(&self, other: &Authority) -> bool {
|
||||
self.eq_ignore_ascii_case(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for Authority {
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
self.data.eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for Authority {
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.data.eq_ignore_ascii_case(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<Authority> for String {
|
||||
fn eq(&self, other: &Authority) -> bool {
|
||||
self.as_str().eq_ignore_ascii_case(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
/// Case-insensitive ordering
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "DEF.com".parse().unwrap();
|
||||
/// assert!(authority < "ghi.com");
|
||||
/// assert!(authority > "abc.com");
|
||||
/// ```
|
||||
impl PartialOrd for Authority {
|
||||
fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
|
||||
let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<str> for Authority {
|
||||
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
|
||||
let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Authority> for str {
|
||||
fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
|
||||
let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<Authority> for &'a str {
|
||||
fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
|
||||
let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<&'a str> for Authority {
|
||||
fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
|
||||
let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<String> for Authority {
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
let left = self.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<Authority> for String {
|
||||
fn partial_cmp(&self, other: &Authority) -> Option<cmp::Ordering> {
|
||||
let left = self.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
let right = other.data.as_bytes().iter().map(|b| b.to_ascii_lowercase());
|
||||
left.partial_cmp(right)
|
||||
}
|
||||
}
|
||||
|
||||
/// Case-insensitive hashing
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// # use std::hash::{Hash, Hasher};
|
||||
/// # use std::collections::hash_map::DefaultHasher;
|
||||
///
|
||||
/// let a: Authority = "HELLO.com".parse().unwrap();
|
||||
/// let b: Authority = "hello.coM".parse().unwrap();
|
||||
///
|
||||
/// let mut s = DefaultHasher::new();
|
||||
/// a.hash(&mut s);
|
||||
/// let a = s.finish();
|
||||
///
|
||||
/// let mut s = DefaultHasher::new();
|
||||
/// b.hash(&mut s);
|
||||
/// let b = s.finish();
|
||||
///
|
||||
/// assert_eq!(a, b);
|
||||
/// ```
|
||||
impl Hash for Authority {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
self.data.len().hash(state);
|
||||
for &b in self.data.as_bytes() {
|
||||
state.write_u8(b.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Authority {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
// parse first, and only turn into Bytes if valid
|
||||
|
||||
// Preconditon on create_authority: copy_from_slice() copies all of
|
||||
// bytes from the [u8] parameter into a new Bytes
|
||||
create_authority(s, |s| Bytes::copy_from_slice(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Authority {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for Authority {
|
||||
type Error = InvalidUri;
|
||||
|
||||
#[inline]
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Authority::from_shared(vec.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for Authority {
|
||||
type Error = InvalidUri;
|
||||
|
||||
#[inline]
|
||||
fn try_from(t: String) -> Result<Self, Self::Error> {
|
||||
Authority::from_shared(t.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Authority {
|
||||
type Err = InvalidUri;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, InvalidUri> {
|
||||
TryFrom::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Authority {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Authority {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
fn host(auth: &str) -> &str {
|
||||
let host_port = auth
|
||||
.rsplitn(2, '@')
|
||||
.next()
|
||||
.expect("split always has at least 1 item");
|
||||
|
||||
if host_port.as_bytes()[0] == b'[' {
|
||||
let i = host_port
|
||||
.find(']')
|
||||
.expect("parsing should validate brackets");
|
||||
// ..= ranges aren't available in 1.20, our minimum Rust version...
|
||||
&host_port[0..i + 1]
|
||||
} else {
|
||||
host_port
|
||||
.split(':')
|
||||
.next()
|
||||
.expect("split always has at least 1 item")
|
||||
}
|
||||
}
|
||||
|
||||
// Precondition: f converts all of the bytes in the passed in B into the
|
||||
// returned Bytes.
|
||||
fn create_authority<B, F>(b: B, f: F) -> Result<Authority, InvalidUri>
|
||||
where
|
||||
B: AsRef<[u8]>,
|
||||
F: FnOnce(B) -> Bytes,
|
||||
{
|
||||
let s = b.as_ref();
|
||||
let authority_end = Authority::parse_non_empty(s)?;
|
||||
|
||||
if authority_end != s.len() {
|
||||
return Err(ErrorKind::InvalidUriChar.into());
|
||||
}
|
||||
|
||||
let bytes = f(b);
|
||||
|
||||
Ok(Authority {
|
||||
// Safety: the postcondition on parse_non_empty() and the check against
|
||||
// s.len() ensure that b is valid UTF-8. The precondition on f ensures
|
||||
// that this is carried through to bytes.
|
||||
data: unsafe { ByteStr::from_utf8_unchecked(bytes) },
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_empty_string_is_error() {
|
||||
let err = Authority::parse_non_empty(b"").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::Empty);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equal_to_self_of_same_authority() {
|
||||
let authority1: Authority = "example.com".parse().unwrap();
|
||||
let authority2: Authority = "EXAMPLE.COM".parse().unwrap();
|
||||
assert_eq!(authority1, authority2);
|
||||
assert_eq!(authority2, authority1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_to_self_of_different_authority() {
|
||||
let authority1: Authority = "example.com".parse().unwrap();
|
||||
let authority2: Authority = "test.com".parse().unwrap();
|
||||
assert_ne!(authority1, authority2);
|
||||
assert_ne!(authority2, authority1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_str() {
|
||||
let authority: Authority = "example.com".parse().unwrap();
|
||||
assert_eq!(&authority, "EXAMPLE.com");
|
||||
assert_eq!("EXAMPLE.com", &authority);
|
||||
assert_eq!(authority, "EXAMPLE.com");
|
||||
assert_eq!("EXAMPLE.com", authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_static_equates_with_a_str() {
|
||||
let authority = Authority::from_static("example.com");
|
||||
assert_eq!(authority, "example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_with_a_str_of_a_different_authority() {
|
||||
let authority: Authority = "example.com".parse().unwrap();
|
||||
assert_ne!(&authority, "test.com");
|
||||
assert_ne!("test.com", &authority);
|
||||
assert_ne!(authority, "test.com");
|
||||
assert_ne!("test.com", authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_string() {
|
||||
let authority: Authority = "example.com".parse().unwrap();
|
||||
assert_eq!(authority, "EXAMPLE.com".to_string());
|
||||
assert_eq!("EXAMPLE.com".to_string(), authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_string_of_a_different_authority() {
|
||||
let authority: Authority = "example.com".parse().unwrap();
|
||||
assert_ne!(authority, "test.com".to_string());
|
||||
assert_ne!("test.com".to_string(), authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_to_self() {
|
||||
let authority1: Authority = "abc.com".parse().unwrap();
|
||||
let authority2: Authority = "def.com".parse().unwrap();
|
||||
assert!(authority1 < authority2);
|
||||
assert!(authority2 > authority1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_with_a_str() {
|
||||
let authority: Authority = "def.com".parse().unwrap();
|
||||
// with ref
|
||||
assert!(&authority < "ghi.com");
|
||||
assert!("ghi.com" > &authority);
|
||||
assert!(&authority > "abc.com");
|
||||
assert!("abc.com" < &authority);
|
||||
|
||||
// no ref
|
||||
assert!(authority < "ghi.com");
|
||||
assert!("ghi.com" > authority);
|
||||
assert!(authority > "abc.com");
|
||||
assert!("abc.com" < authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_with_a_string() {
|
||||
let authority: Authority = "def.com".parse().unwrap();
|
||||
assert!(authority < "ghi.com".to_string());
|
||||
assert!("ghi.com".to_string() > authority);
|
||||
assert!(authority > "abc.com".to_string());
|
||||
assert!("abc.com".to_string() < authority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allows_percent_in_userinfo() {
|
||||
let authority_str = "a%2f:b%2f@example.com";
|
||||
let authority: Authority = authority_str.parse().unwrap();
|
||||
assert_eq!(authority, authority_str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_percent_in_hostname() {
|
||||
let err = Authority::parse_non_empty(b"example%2f.com").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
|
||||
let err = Authority::parse_non_empty(b"a%2f:b%2f@example%2f.com").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allows_percent_in_ipv6_address() {
|
||||
let authority_str = "[fe80::1:2:3:4%25eth0]";
|
||||
let result: Authority = authority_str.parse().unwrap();
|
||||
assert_eq!(result, authority_str);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_percent_outside_ipv6_address() {
|
||||
let err = Authority::parse_non_empty(b"1234%20[fe80::1:2:3:4]").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
|
||||
let err = Authority::parse_non_empty(b"[fe80::1:2:3:4]%20").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_utf8() {
|
||||
let err = Authority::try_from([0xc0u8].as_ref()).unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidUriChar);
|
||||
|
||||
let err = Authority::from_shared(Bytes::from_static([0xc0u8].as_ref()))
|
||||
.unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidUriChar);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_use_of_brackets() {
|
||||
let err = Authority::parse_non_empty(b"[]@[").unwrap_err();
|
||||
assert_eq!(err.0, ErrorKind::InvalidAuthority);
|
||||
}
|
||||
}
|
||||
197
zeroidc/vendor/http/src/uri/builder.rs
vendored
Normal file
197
zeroidc/vendor/http/src/uri/builder.rs
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
|
||||
use super::{Authority, Parts, PathAndQuery, Scheme};
|
||||
use crate::Uri;
|
||||
|
||||
/// A builder for `Uri`s.
|
||||
///
|
||||
/// This type can be used to construct an instance of `Uri`
|
||||
/// through a builder pattern.
|
||||
#[derive(Debug)]
|
||||
pub struct Builder {
|
||||
parts: Result<Parts, crate::Error>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
/// Creates a new default instance of `Builder` to construct a `Uri`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let uri = uri::Builder::new()
|
||||
/// .scheme("https")
|
||||
/// .authority("hyper.rs")
|
||||
/// .path_and_query("/")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn new() -> Builder {
|
||||
Builder::default()
|
||||
}
|
||||
|
||||
/// Set the `Scheme` for this URI.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let mut builder = uri::Builder::new();
|
||||
/// builder.scheme("https");
|
||||
/// ```
|
||||
pub fn scheme<T>(self, scheme: T) -> Self
|
||||
where
|
||||
Scheme: TryFrom<T>,
|
||||
<Scheme as TryFrom<T>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.map(move |mut parts| {
|
||||
let scheme = scheme.try_into().map_err(Into::into)?;
|
||||
parts.scheme = Some(scheme);
|
||||
Ok(parts)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the `Authority` for this URI.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let uri = uri::Builder::new()
|
||||
/// .authority("tokio.rs")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn authority<T>(self, auth: T) -> Self
|
||||
where
|
||||
Authority: TryFrom<T>,
|
||||
<Authority as TryFrom<T>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.map(move |mut parts| {
|
||||
let auth = auth.try_into().map_err(Into::into)?;
|
||||
parts.authority = Some(auth);
|
||||
Ok(parts)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the `PathAndQuery` for this URI.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let uri = uri::Builder::new()
|
||||
/// .path_and_query("/hello?foo=bar")
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn path_and_query<T>(self, p_and_q: T) -> Self
|
||||
where
|
||||
PathAndQuery: TryFrom<T>,
|
||||
<PathAndQuery as TryFrom<T>>::Error: Into<crate::Error>,
|
||||
{
|
||||
self.map(move |mut parts| {
|
||||
let p_and_q = p_and_q.try_into().map_err(Into::into)?;
|
||||
parts.path_and_query = Some(p_and_q);
|
||||
Ok(parts)
|
||||
})
|
||||
}
|
||||
|
||||
/// Consumes this builder, and tries to construct a valid `Uri` from
|
||||
/// the configured pieces.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function may return an error if any previously configured argument
|
||||
/// failed to parse or get converted to the internal representation. For
|
||||
/// example if an invalid `scheme` was specified via `scheme("!@#%/^")`
|
||||
/// the error will be returned when this function is called rather than
|
||||
/// when `scheme` was called.
|
||||
///
|
||||
/// Additionally, the various forms of URI require certain combinations of
|
||||
/// parts to be set to be valid. If the parts don't fit into any of the
|
||||
/// valid forms of URI, a new error is returned.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::*;
|
||||
///
|
||||
/// let uri = Uri::builder()
|
||||
/// .build()
|
||||
/// .unwrap();
|
||||
/// ```
|
||||
pub fn build(self) -> Result<Uri, crate::Error> {
|
||||
let parts = self.parts?;
|
||||
Uri::from_parts(parts).map_err(Into::into)
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
fn map<F>(self, func: F) -> Self
|
||||
where
|
||||
F: FnOnce(Parts) -> Result<Parts, crate::Error>,
|
||||
{
|
||||
|
||||
Builder {
|
||||
parts: self.parts.and_then(func),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Builder {
|
||||
#[inline]
|
||||
fn default() -> Builder {
|
||||
Builder {
|
||||
parts: Ok(Parts::default()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn build_from_str() {
|
||||
let uri = Builder::new()
|
||||
.scheme(Scheme::HTTP)
|
||||
.authority("hyper.rs")
|
||||
.path_and_query("/foo?a=1")
|
||||
.build()
|
||||
.unwrap();
|
||||
assert_eq!(uri.scheme_str(), Some("http"));
|
||||
assert_eq!(uri.authority().unwrap().host(), "hyper.rs");
|
||||
assert_eq!(uri.path(), "/foo");
|
||||
assert_eq!(uri.query(), Some("a=1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_from_string() {
|
||||
for i in 1..10 {
|
||||
let uri = Builder::new()
|
||||
.path_and_query(format!("/foo?a={}", i))
|
||||
.build()
|
||||
.unwrap();
|
||||
let expected_query = format!("a={}", i);
|
||||
assert_eq!(uri.path(), "/foo");
|
||||
assert_eq!(uri.query(), Some(expected_query.as_str()));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_from_string_ref() {
|
||||
for i in 1..10 {
|
||||
let p_a_q = format!("/foo?a={}", i);
|
||||
let uri = Builder::new().path_and_query(&p_a_q).build().unwrap();
|
||||
let expected_query = format!("a={}", i);
|
||||
assert_eq!(uri.path(), "/foo");
|
||||
assert_eq!(uri.query(), Some(expected_query.as_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
1118
zeroidc/vendor/http/src/uri/mod.rs
vendored
Normal file
1118
zeroidc/vendor/http/src/uri/mod.rs
vendored
Normal file
File diff suppressed because it is too large
Load Diff
558
zeroidc/vendor/http/src/uri/path.rs
vendored
Normal file
558
zeroidc/vendor/http/src/uri/path.rs
vendored
Normal file
@@ -0,0 +1,558 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
use std::{cmp, fmt, str};
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use super::{ErrorKind, InvalidUri};
|
||||
use crate::byte_str::ByteStr;
|
||||
|
||||
/// Represents the path component of a URI
|
||||
#[derive(Clone)]
|
||||
pub struct PathAndQuery {
|
||||
pub(super) data: ByteStr,
|
||||
pub(super) query: u16,
|
||||
}
|
||||
|
||||
const NONE: u16 = ::std::u16::MAX;
|
||||
|
||||
impl PathAndQuery {
|
||||
// Not public while `bytes` is unstable.
|
||||
pub(super) fn from_shared(mut src: Bytes) -> Result<Self, InvalidUri> {
|
||||
let mut query = NONE;
|
||||
let mut fragment = None;
|
||||
|
||||
// block for iterator borrow
|
||||
{
|
||||
let mut iter = src.as_ref().iter().enumerate();
|
||||
|
||||
// path ...
|
||||
for (i, &b) in &mut iter {
|
||||
// See https://url.spec.whatwg.org/#path-state
|
||||
match b {
|
||||
b'?' => {
|
||||
debug_assert_eq!(query, NONE);
|
||||
query = i as u16;
|
||||
break;
|
||||
}
|
||||
b'#' => {
|
||||
fragment = Some(i);
|
||||
break;
|
||||
}
|
||||
|
||||
// This is the range of bytes that don't need to be
|
||||
// percent-encoded in the path. If it should have been
|
||||
// percent-encoded, then error.
|
||||
0x21 |
|
||||
0x24..=0x3B |
|
||||
0x3D |
|
||||
0x40..=0x5F |
|
||||
0x61..=0x7A |
|
||||
0x7C |
|
||||
0x7E => {},
|
||||
|
||||
// These are code points that are supposed to be
|
||||
// percent-encoded in the path but there are clients
|
||||
// out there sending them as is and httparse accepts
|
||||
// to parse those requests, so they are allowed here
|
||||
// for parity.
|
||||
//
|
||||
// For reference, those are code points that are used
|
||||
// to send requests with JSON directly embedded in
|
||||
// the URI path. Yes, those things happen for real.
|
||||
b'"' |
|
||||
b'{' | b'}' => {},
|
||||
|
||||
_ => return Err(ErrorKind::InvalidUriChar.into()),
|
||||
}
|
||||
}
|
||||
|
||||
// query ...
|
||||
if query != NONE {
|
||||
for (i, &b) in iter {
|
||||
match b {
|
||||
// While queries *should* be percent-encoded, most
|
||||
// bytes are actually allowed...
|
||||
// See https://url.spec.whatwg.org/#query-state
|
||||
//
|
||||
// Allowed: 0x21 / 0x24 - 0x3B / 0x3D / 0x3F - 0x7E
|
||||
0x21 |
|
||||
0x24..=0x3B |
|
||||
0x3D |
|
||||
0x3F..=0x7E => {},
|
||||
|
||||
b'#' => {
|
||||
fragment = Some(i);
|
||||
break;
|
||||
}
|
||||
|
||||
_ => return Err(ErrorKind::InvalidUriChar.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(i) = fragment {
|
||||
src.truncate(i);
|
||||
}
|
||||
|
||||
Ok(PathAndQuery {
|
||||
data: unsafe { ByteStr::from_utf8_unchecked(src) },
|
||||
query: query,
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert a `PathAndQuery` from a static string.
|
||||
///
|
||||
/// This function will not perform any copying, however the string is
|
||||
/// checked to ensure that it is valid.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function panics if the argument is an invalid path and query.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let v = PathAndQuery::from_static("/hello?world");
|
||||
///
|
||||
/// assert_eq!(v.path(), "/hello");
|
||||
/// assert_eq!(v.query(), Some("world"));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn from_static(src: &'static str) -> Self {
|
||||
let src = Bytes::from_static(src.as_bytes());
|
||||
|
||||
PathAndQuery::from_shared(src).unwrap()
|
||||
}
|
||||
|
||||
/// Attempt to convert a `Bytes` buffer to a `PathAndQuery`.
|
||||
///
|
||||
/// This will try to prevent a copy if the type passed is the type used
|
||||
/// internally, and will copy the data if it is not.
|
||||
pub fn from_maybe_shared<T>(src: T) -> Result<Self, InvalidUri>
|
||||
where
|
||||
T: AsRef<[u8]> + 'static,
|
||||
{
|
||||
if_downcast_into!(T, Bytes, src, {
|
||||
return PathAndQuery::from_shared(src);
|
||||
});
|
||||
|
||||
PathAndQuery::try_from(src.as_ref())
|
||||
}
|
||||
|
||||
pub(super) fn empty() -> Self {
|
||||
PathAndQuery {
|
||||
data: ByteStr::new(),
|
||||
query: NONE,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn slash() -> Self {
|
||||
PathAndQuery {
|
||||
data: ByteStr::from_static("/"),
|
||||
query: NONE,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn star() -> Self {
|
||||
PathAndQuery {
|
||||
data: ByteStr::from_static("*"),
|
||||
query: NONE,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path component
|
||||
///
|
||||
/// The path component is **case sensitive**.
|
||||
///
|
||||
/// ```notrust
|
||||
/// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|
||||
/// |--------|
|
||||
/// |
|
||||
/// path
|
||||
/// ```
|
||||
///
|
||||
/// If the URI is `*` then the path component is equal to `*`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
///
|
||||
/// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(path_and_query.path(), "/hello/world");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn path(&self) -> &str {
|
||||
let ret = if self.query == NONE {
|
||||
&self.data[..]
|
||||
} else {
|
||||
&self.data[..self.query as usize]
|
||||
};
|
||||
|
||||
if ret.is_empty() {
|
||||
return "/";
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
/// Returns the query string component
|
||||
///
|
||||
/// The query component contains non-hierarchical data that, along with data
|
||||
/// in the path component, serves to identify a resource within the scope of
|
||||
/// the URI's scheme and naming authority (if any). The query component is
|
||||
/// indicated by the first question mark ("?") character and terminated by a
|
||||
/// number sign ("#") character or by the end of the URI.
|
||||
///
|
||||
/// ```notrust
|
||||
/// abc://username:password@example.com:123/path/data?key=value&key2=value2#fragid1
|
||||
/// |-------------------|
|
||||
/// |
|
||||
/// query
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// With a query string component
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(path_and_query.query(), Some("key=value&foo=bar"));
|
||||
/// ```
|
||||
///
|
||||
/// Without a query string component
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
|
||||
///
|
||||
/// assert!(path_and_query.query().is_none());
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn query(&self) -> Option<&str> {
|
||||
if self.query == NONE {
|
||||
None
|
||||
} else {
|
||||
let i = self.query + 1;
|
||||
Some(&self.data[i as usize..])
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the path and query as a string component.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// With a query string component
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let path_and_query: PathAndQuery = "/hello/world?key=value&foo=bar".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(path_and_query.as_str(), "/hello/world?key=value&foo=bar");
|
||||
/// ```
|
||||
///
|
||||
/// Without a query string component
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let path_and_query: PathAndQuery = "/hello/world".parse().unwrap();
|
||||
///
|
||||
/// assert_eq!(path_and_query.as_str(), "/hello/world");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
let ret = &self.data[..];
|
||||
if ret.is_empty() {
|
||||
return "/";
|
||||
}
|
||||
ret
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
PathAndQuery::from_shared(Bytes::copy_from_slice(s))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<Vec<u8>> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
PathAndQuery::from_shared(vec.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<String> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: String) -> Result<Self, Self::Error> {
|
||||
PathAndQuery::from_shared(s.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&String> for PathAndQuery {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &String) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for PathAndQuery {
|
||||
type Err = InvalidUri;
|
||||
#[inline]
|
||||
fn from_str(s: &str) -> Result<Self, InvalidUri> {
|
||||
TryFrom::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for PathAndQuery {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PathAndQuery {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if !self.data.is_empty() {
|
||||
match self.data.as_bytes()[0] {
|
||||
b'/' | b'*' => write!(fmt, "{}", &self.data[..]),
|
||||
_ => write!(fmt, "/{}", &self.data[..]),
|
||||
}
|
||||
} else {
|
||||
write!(fmt, "/")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== PartialEq / PartialOrd =====
|
||||
|
||||
impl PartialEq for PathAndQuery {
|
||||
#[inline]
|
||||
fn eq(&self, other: &PathAndQuery) -> bool {
|
||||
self.data == other.data
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for PathAndQuery {}
|
||||
|
||||
impl PartialEq<str> for PathAndQuery {
|
||||
#[inline]
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str() == other
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<PathAndQuery> for &'a str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &PathAndQuery) -> bool {
|
||||
self == &other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialEq<&'a str> for PathAndQuery {
|
||||
#[inline]
|
||||
fn eq(&self, other: &&'a str) -> bool {
|
||||
self.as_str() == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<PathAndQuery> for str {
|
||||
#[inline]
|
||||
fn eq(&self, other: &PathAndQuery) -> bool {
|
||||
self == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<String> for PathAndQuery {
|
||||
#[inline]
|
||||
fn eq(&self, other: &String) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq<PathAndQuery> for String {
|
||||
#[inline]
|
||||
fn eq(&self, other: &PathAndQuery) -> bool {
|
||||
self.as_str() == other.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PathAndQuery {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<str> for PathAndQuery {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<PathAndQuery> for str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<&'a str> for PathAndQuery {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &&'a str) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> PartialOrd<PathAndQuery> for &'a str {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
|
||||
self.partial_cmp(&other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<String> for PathAndQuery {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd<PathAndQuery> for String {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &PathAndQuery) -> Option<cmp::Ordering> {
|
||||
self.as_str().partial_cmp(other.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn equal_to_self_of_same_path() {
|
||||
let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
let p2: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
assert_eq!(p1, p2);
|
||||
assert_eq!(p2, p1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_to_self_of_different_path() {
|
||||
let p1: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
let p2: PathAndQuery = "/world&foo=bar".parse().unwrap();
|
||||
assert_ne!(p1, p2);
|
||||
assert_ne!(p2, p1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_str() {
|
||||
let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
assert_eq!(&path_and_query, "/hello/world&foo=bar");
|
||||
assert_eq!("/hello/world&foo=bar", &path_and_query);
|
||||
assert_eq!(path_and_query, "/hello/world&foo=bar");
|
||||
assert_eq!("/hello/world&foo=bar", path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_with_a_str_of_a_different_path() {
|
||||
let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
// as a reference
|
||||
assert_ne!(&path_and_query, "/hello&foo=bar");
|
||||
assert_ne!("/hello&foo=bar", &path_and_query);
|
||||
// without reference
|
||||
assert_ne!(path_and_query, "/hello&foo=bar");
|
||||
assert_ne!("/hello&foo=bar", path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equates_with_a_string() {
|
||||
let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
assert_eq!(path_and_query, "/hello/world&foo=bar".to_string());
|
||||
assert_eq!("/hello/world&foo=bar".to_string(), path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn not_equal_with_a_string_of_a_different_path() {
|
||||
let path_and_query: PathAndQuery = "/hello/world&foo=bar".parse().unwrap();
|
||||
assert_ne!(path_and_query, "/hello&foo=bar".to_string());
|
||||
assert_ne!("/hello&foo=bar".to_string(), path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_to_self() {
|
||||
let p1: PathAndQuery = "/a/world&foo=bar".parse().unwrap();
|
||||
let p2: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
|
||||
assert!(p1 < p2);
|
||||
assert!(p2 > p1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_with_a_str() {
|
||||
let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
|
||||
// by ref
|
||||
assert!(&path_and_query < "/c/world&foo=bar");
|
||||
assert!("/c/world&foo=bar" > &path_and_query);
|
||||
assert!(&path_and_query > "/a/world&foo=bar");
|
||||
assert!("/a/world&foo=bar" < &path_and_query);
|
||||
|
||||
// by val
|
||||
assert!(path_and_query < "/c/world&foo=bar");
|
||||
assert!("/c/world&foo=bar" > path_and_query);
|
||||
assert!(path_and_query > "/a/world&foo=bar");
|
||||
assert!("/a/world&foo=bar" < path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compares_with_a_string() {
|
||||
let path_and_query: PathAndQuery = "/b/world&foo=bar".parse().unwrap();
|
||||
assert!(path_and_query < "/c/world&foo=bar".to_string());
|
||||
assert!("/c/world&foo=bar".to_string() > path_and_query);
|
||||
assert!(path_and_query > "/a/world&foo=bar".to_string());
|
||||
assert!("/a/world&foo=bar".to_string() < path_and_query);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_valid_percent_encodings() {
|
||||
assert_eq!("/a%20b", pq("/a%20b?r=1").path());
|
||||
assert_eq!("qr=%31", pq("/a/b?qr=%31").query().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ignores_invalid_percent_encodings() {
|
||||
assert_eq!("/a%%b", pq("/a%%b?r=1").path());
|
||||
assert_eq!("/aaa%", pq("/aaa%").path());
|
||||
assert_eq!("/aaa%", pq("/aaa%?r=1").path());
|
||||
assert_eq!("/aa%2", pq("/aa%2").path());
|
||||
assert_eq!("/aa%2", pq("/aa%2?r=1").path());
|
||||
assert_eq!("qr=%3", pq("/a/b?qr=%3").query().unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn json_is_fine() {
|
||||
assert_eq!(r#"/{"bread":"baguette"}"#, pq(r#"/{"bread":"baguette"}"#).path());
|
||||
}
|
||||
|
||||
fn pq(s: &str) -> PathAndQuery {
|
||||
s.parse().expect(&format!("parsing {}", s))
|
||||
}
|
||||
}
|
||||
151
zeroidc/vendor/http/src/uri/port.rs
vendored
Normal file
151
zeroidc/vendor/http/src/uri/port.rs
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
use std::fmt;
|
||||
|
||||
use super::{ErrorKind, InvalidUri};
|
||||
|
||||
/// The port component of a URI.
|
||||
pub struct Port<T> {
|
||||
port: u16,
|
||||
repr: T,
|
||||
}
|
||||
|
||||
impl<T> Port<T> {
|
||||
/// Returns the port number as a `u16`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Port as `u16`.
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// let port = authority.port().unwrap();
|
||||
/// assert_eq!(port.as_u16(), 80);
|
||||
/// ```
|
||||
pub fn as_u16(&self) -> u16 {
|
||||
self.port
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Port<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
/// Converts a `str` to a port number.
|
||||
///
|
||||
/// The supplied `str` must be a valid u16.
|
||||
pub(crate) fn from_str(bytes: T) -> Result<Self, InvalidUri> {
|
||||
bytes
|
||||
.as_ref()
|
||||
.parse::<u16>()
|
||||
.map(|port| Port { port, repr: bytes })
|
||||
.map_err(|_| ErrorKind::InvalidPort.into())
|
||||
}
|
||||
|
||||
/// Returns the port number as a `str`.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Port as `str`.
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Authority;
|
||||
/// let authority: Authority = "example.org:80".parse().unwrap();
|
||||
///
|
||||
/// let port = authority.port().unwrap();
|
||||
/// assert_eq!(port.as_str(), "80");
|
||||
/// ```
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.repr.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Debug for Port<T>
|
||||
where
|
||||
T: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_tuple("Port").field(&self.port).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> fmt::Display for Port<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// Use `u16::fmt` so that it respects any formatting flags that
|
||||
// may have been set (like padding, align, etc).
|
||||
fmt::Display::fmt(&self.port, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Port<T>> for u16 {
|
||||
fn from(port: Port<T>) -> Self {
|
||||
port.as_u16()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<str> for Port<T>
|
||||
where
|
||||
T: AsRef<str>,
|
||||
{
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U> PartialEq<Port<U>> for Port<T> {
|
||||
fn eq(&self, other: &Port<U>) -> bool {
|
||||
self.port == other.port
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<u16> for Port<T> {
|
||||
fn eq(&self, other: &u16) -> bool {
|
||||
self.port == *other
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> PartialEq<Port<T>> for u16 {
|
||||
fn eq(&self, other: &Port<T>) -> bool {
|
||||
other.port == *self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn partialeq_port() {
|
||||
let port_a = Port::from_str("8080").unwrap();
|
||||
let port_b = Port::from_str("8080").unwrap();
|
||||
assert_eq!(port_a, port_b);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partialeq_port_different_reprs() {
|
||||
let port_a = Port {
|
||||
repr: "8081",
|
||||
port: 8081,
|
||||
};
|
||||
let port_b = Port {
|
||||
repr: String::from("8081"),
|
||||
port: 8081,
|
||||
};
|
||||
assert_eq!(port_a, port_b);
|
||||
assert_eq!(port_b, port_a);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn partialeq_u16() {
|
||||
let port = Port::from_str("8080").unwrap();
|
||||
// test equals in both directions
|
||||
assert_eq!(port, 8080);
|
||||
assert_eq!(8080, port);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn u16_from_port() {
|
||||
let port = Port::from_str("8080").unwrap();
|
||||
assert_eq!(8080, u16::from(port));
|
||||
}
|
||||
}
|
||||
363
zeroidc/vendor/http/src/uri/scheme.rs
vendored
Normal file
363
zeroidc/vendor/http/src/uri/scheme.rs
vendored
Normal file
@@ -0,0 +1,363 @@
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::str::FromStr;
|
||||
|
||||
use bytes::Bytes;
|
||||
|
||||
use super::{ErrorKind, InvalidUri};
|
||||
use crate::byte_str::ByteStr;
|
||||
|
||||
/// Represents the scheme component of a URI
|
||||
#[derive(Clone)]
|
||||
pub struct Scheme {
|
||||
pub(super) inner: Scheme2,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(super) enum Scheme2<T = Box<ByteStr>> {
|
||||
None,
|
||||
Standard(Protocol),
|
||||
Other(T),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(super) enum Protocol {
|
||||
Http,
|
||||
Https,
|
||||
}
|
||||
|
||||
impl Scheme {
|
||||
/// HTTP protocol scheme
|
||||
pub const HTTP: Scheme = Scheme {
|
||||
inner: Scheme2::Standard(Protocol::Http),
|
||||
};
|
||||
|
||||
/// HTTP protocol over TLS.
|
||||
pub const HTTPS: Scheme = Scheme {
|
||||
inner: Scheme2::Standard(Protocol::Https),
|
||||
};
|
||||
|
||||
pub(super) fn empty() -> Self {
|
||||
Scheme {
|
||||
inner: Scheme2::None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a str representation of the scheme
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::*;
|
||||
/// let scheme: Scheme = "http".parse().unwrap();
|
||||
/// assert_eq!(scheme.as_str(), "http");
|
||||
/// ```
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
use self::Protocol::*;
|
||||
use self::Scheme2::*;
|
||||
|
||||
match self.inner {
|
||||
Standard(Http) => "http",
|
||||
Standard(Https) => "https",
|
||||
Other(ref v) => &v[..],
|
||||
None => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a [u8]> for Scheme {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a [u8]) -> Result<Self, Self::Error> {
|
||||
use self::Scheme2::*;
|
||||
|
||||
match Scheme2::parse_exact(s)? {
|
||||
None => Err(ErrorKind::InvalidScheme.into()),
|
||||
Standard(p) => Ok(Standard(p).into()),
|
||||
Other(_) => {
|
||||
let bytes = Bytes::copy_from_slice(s);
|
||||
|
||||
// Safety: postcondition on parse_exact() means that s and
|
||||
// hence bytes are valid UTF-8.
|
||||
let string = unsafe { ByteStr::from_utf8_unchecked(bytes) };
|
||||
|
||||
Ok(Other(Box::new(string)).into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a str> for Scheme {
|
||||
type Error = InvalidUri;
|
||||
#[inline]
|
||||
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
|
||||
TryFrom::try_from(s.as_bytes())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Scheme {
|
||||
type Err = InvalidUri;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
TryFrom::try_from(s)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Scheme {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(self.as_str(), f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Scheme {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Scheme {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Scheme {
|
||||
fn eq(&self, other: &Scheme) -> bool {
|
||||
use self::Protocol::*;
|
||||
use self::Scheme2::*;
|
||||
|
||||
match (&self.inner, &other.inner) {
|
||||
(&Standard(Http), &Standard(Http)) => true,
|
||||
(&Standard(Https), &Standard(Https)) => true,
|
||||
(&Other(ref a), &Other(ref b)) => a.eq_ignore_ascii_case(b),
|
||||
(&None, _) | (_, &None) => unreachable!(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Scheme {}
|
||||
|
||||
/// Case-insensitive equality
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use http::uri::Scheme;
|
||||
/// let scheme: Scheme = "HTTP".parse().unwrap();
|
||||
/// assert_eq!(scheme, *"http");
|
||||
/// ```
|
||||
impl PartialEq<str> for Scheme {
|
||||
fn eq(&self, other: &str) -> bool {
|
||||
self.as_str().eq_ignore_ascii_case(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Case-insensitive equality
|
||||
impl PartialEq<Scheme> for str {
|
||||
fn eq(&self, other: &Scheme) -> bool {
|
||||
other == self
|
||||
}
|
||||
}
|
||||
|
||||
/// Case-insensitive hashing
|
||||
impl Hash for Scheme {
|
||||
fn hash<H>(&self, state: &mut H)
|
||||
where
|
||||
H: Hasher,
|
||||
{
|
||||
match self.inner {
|
||||
Scheme2::None => (),
|
||||
Scheme2::Standard(Protocol::Http) => state.write_u8(1),
|
||||
Scheme2::Standard(Protocol::Https) => state.write_u8(2),
|
||||
Scheme2::Other(ref other) => {
|
||||
other.len().hash(state);
|
||||
for &b in other.as_bytes() {
|
||||
state.write_u8(b.to_ascii_lowercase());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Scheme2<T> {
|
||||
pub(super) fn is_none(&self) -> bool {
|
||||
match *self {
|
||||
Scheme2::None => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Require the scheme to not be too long in order to enable further
|
||||
// optimizations later.
|
||||
const MAX_SCHEME_LEN: usize = 64;
|
||||
|
||||
// scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
|
||||
//
|
||||
// SCHEME_CHARS is a table of valid characters in the scheme part of a URI. An
|
||||
// entry in the table is 0 for invalid characters. For valid characters the
|
||||
// entry is itself (i.e. the entry for 43 is b'+' because b'+' == 43u8). An
|
||||
// important characteristic of this table is that all entries above 127 are
|
||||
// invalid. This makes all of the valid entries a valid single-byte UTF-8 code
|
||||
// point. This means that a slice of such valid entries is valid UTF-8.
|
||||
const SCHEME_CHARS: [u8; 256] = [
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3x
|
||||
0, 0, 0, b'+', 0, b'-', b'.', 0, b'0', b'1', // 4x
|
||||
b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b':', 0, // 5x
|
||||
0, 0, 0, 0, 0, b'A', b'B', b'C', b'D', b'E', // 6x
|
||||
b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', // 7x
|
||||
b'P', b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', // 8x
|
||||
b'Z', 0, 0, 0, 0, 0, 0, b'a', b'b', b'c', // 9x
|
||||
b'd', b'e', b'f', b'g', b'h', b'i', b'j', b'k', b'l', b'm', // 10x
|
||||
b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v', b'w', // 11x
|
||||
b'x', b'y', b'z', 0, 0, 0, b'~', 0, 0, 0, // 12x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 13x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 14x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 15x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 17x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 18x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 19x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 21x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 22x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 23x
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 24x
|
||||
0, 0, 0, 0, 0, 0 // 25x
|
||||
];
|
||||
|
||||
impl Scheme2<usize> {
|
||||
// Postcondition: On all Ok() returns, s is valid UTF-8
|
||||
fn parse_exact(s: &[u8]) -> Result<Scheme2<()>, InvalidUri> {
|
||||
match s {
|
||||
b"http" => Ok(Protocol::Http.into()),
|
||||
b"https" => Ok(Protocol::Https.into()),
|
||||
_ => {
|
||||
if s.len() > MAX_SCHEME_LEN {
|
||||
return Err(ErrorKind::SchemeTooLong.into());
|
||||
}
|
||||
|
||||
// check that each byte in s is a SCHEME_CHARS which implies
|
||||
// that it is a valid single byte UTF-8 code point.
|
||||
for &b in s {
|
||||
match SCHEME_CHARS[b as usize] {
|
||||
b':' => {
|
||||
// Don't want :// here
|
||||
return Err(ErrorKind::InvalidScheme.into());
|
||||
}
|
||||
0 => {
|
||||
return Err(ErrorKind::InvalidScheme.into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Scheme2::Other(()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse(s: &[u8]) -> Result<Scheme2<usize>, InvalidUri> {
|
||||
if s.len() >= 7 {
|
||||
// Check for HTTP
|
||||
if s[..7].eq_ignore_ascii_case(b"http://") {
|
||||
// Prefix will be striped
|
||||
return Ok(Protocol::Http.into());
|
||||
}
|
||||
}
|
||||
|
||||
if s.len() >= 8 {
|
||||
// Check for HTTPs
|
||||
if s[..8].eq_ignore_ascii_case(b"https://") {
|
||||
return Ok(Protocol::Https.into());
|
||||
}
|
||||
}
|
||||
|
||||
if s.len() > 3 {
|
||||
for i in 0..s.len() {
|
||||
let b = s[i];
|
||||
|
||||
match SCHEME_CHARS[b as usize] {
|
||||
b':' => {
|
||||
// Not enough data remaining
|
||||
if s.len() < i + 3 {
|
||||
break;
|
||||
}
|
||||
|
||||
// Not a scheme
|
||||
if &s[i + 1..i + 3] != b"//" {
|
||||
break;
|
||||
}
|
||||
|
||||
if i > MAX_SCHEME_LEN {
|
||||
return Err(ErrorKind::SchemeTooLong.into());
|
||||
}
|
||||
|
||||
// Return scheme
|
||||
return Ok(Scheme2::Other(i));
|
||||
}
|
||||
// Invald scheme character, abort
|
||||
0 => break,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Scheme2::None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Protocol {
|
||||
pub(super) fn len(&self) -> usize {
|
||||
match *self {
|
||||
Protocol::Http => 4,
|
||||
Protocol::Https => 5,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<Protocol> for Scheme2<T> {
|
||||
fn from(src: Protocol) -> Self {
|
||||
Scheme2::Standard(src)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<Scheme2> for Scheme {
|
||||
fn from(src: Scheme2) -> Self {
|
||||
Scheme { inner: src }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn scheme_eq_to_str() {
|
||||
assert_eq!(&scheme("http"), "http");
|
||||
assert_eq!(&scheme("https"), "https");
|
||||
assert_eq!(&scheme("ftp"), "ftp");
|
||||
assert_eq!(&scheme("my+funky+scheme"), "my+funky+scheme");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn invalid_scheme_is_error() {
|
||||
Scheme::try_from("my_funky_scheme").expect_err("Unexpectly valid Scheme");
|
||||
|
||||
// Invalid UTF-8
|
||||
Scheme::try_from([0xC0].as_ref()).expect_err("Unexpectly valid Scheme");
|
||||
}
|
||||
|
||||
fn scheme(s: &str) -> Scheme {
|
||||
s.parse().expect(&format!("Invalid scheme: {}", s))
|
||||
}
|
||||
}
|
||||
519
zeroidc/vendor/http/src/uri/tests.rs
vendored
Normal file
519
zeroidc/vendor/http/src/uri/tests.rs
vendored
Normal file
@@ -0,0 +1,519 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::{ErrorKind, InvalidUri, Port, Uri, URI_CHARS};
|
||||
|
||||
#[test]
|
||||
fn test_char_table() {
|
||||
for (i, &v) in URI_CHARS.iter().enumerate() {
|
||||
if v != 0 {
|
||||
assert_eq!(i, v as usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! part {
|
||||
($s:expr) => {
|
||||
Some(&$s.parse().unwrap())
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! test_parse {
|
||||
(
|
||||
$test_name:ident,
|
||||
$str:expr,
|
||||
$alt:expr,
|
||||
$($method:ident = $value:expr,)*
|
||||
) => (
|
||||
#[test]
|
||||
fn $test_name() {
|
||||
let orig_str = $str;
|
||||
let uri = match Uri::from_str(orig_str) {
|
||||
Ok(uri) => uri,
|
||||
Err(err) => {
|
||||
panic!("parse error {:?} from {:?}", err, orig_str);
|
||||
},
|
||||
};
|
||||
$(
|
||||
assert_eq!(uri.$method(), $value, "{}: uri = {:?}", stringify!($method), uri);
|
||||
)+
|
||||
assert_eq!(uri, orig_str, "partial eq to original str");
|
||||
assert_eq!(uri, uri.clone(), "clones are equal");
|
||||
|
||||
let new_str = uri.to_string();
|
||||
let new_uri = Uri::from_str(&new_str).expect("to_string output parses again as a Uri");
|
||||
assert_eq!(new_uri, orig_str, "round trip still equals original str");
|
||||
|
||||
const ALT: &'static [&'static str] = &$alt;
|
||||
|
||||
for &alt in ALT.iter() {
|
||||
let other: Uri = alt.parse().unwrap();
|
||||
assert_eq!(uri, *alt);
|
||||
assert_eq!(uri, other);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_path_and_query,
|
||||
"/some/path/here?and=then&hello#and-bye",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = None,
|
||||
path = "/some/path/here",
|
||||
query = Some("and=then&hello"),
|
||||
host = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form,
|
||||
"http://127.0.0.1:61761/chunks",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1:61761"),
|
||||
path = "/chunks",
|
||||
query = None,
|
||||
host = Some("127.0.0.1"),
|
||||
port = Port::from_str("61761").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form_without_path,
|
||||
"https://127.0.0.1:61761",
|
||||
["https://127.0.0.1:61761/"],
|
||||
|
||||
scheme = part!("https"),
|
||||
authority = part!("127.0.0.1:61761"),
|
||||
path = "/",
|
||||
query = None,
|
||||
host = Some("127.0.0.1"),
|
||||
port = Port::from_str("61761").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_asterisk_form,
|
||||
"*",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = None,
|
||||
path = "*",
|
||||
query = None,
|
||||
host = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_authority_no_port,
|
||||
"localhost",
|
||||
["LOCALHOST", "LocaLHOSt"],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("localhost"),
|
||||
path = "",
|
||||
query = None,
|
||||
port = None,
|
||||
host = Some("localhost"),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_authority_only_one_character_issue_197,
|
||||
"S",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("S"),
|
||||
path = "",
|
||||
query = None,
|
||||
port = None,
|
||||
host = Some("S"),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_authority_form,
|
||||
"localhost:3000",
|
||||
["localhosT:3000"],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("localhost:3000"),
|
||||
path = "",
|
||||
query = None,
|
||||
host = Some("localhost"),
|
||||
port = Port::from_str("3000").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_with_default_port_http,
|
||||
"http://127.0.0.1:80",
|
||||
["http://127.0.0.1:80/"],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1:80"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = Port::from_str("80").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_with_default_port_https,
|
||||
"https://127.0.0.1:443",
|
||||
["https://127.0.0.1:443/"],
|
||||
|
||||
scheme = part!("https"),
|
||||
authority = part!("127.0.0.1:443"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = Port::from_str("443").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_fragment_questionmark,
|
||||
"http://127.0.0.1/#?",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_path_with_terminating_questionmark,
|
||||
"http://127.0.0.1/path?",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
path = "/path",
|
||||
query = Some(""),
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form_with_empty_path_and_nonempty_query,
|
||||
"http://127.0.0.1?foo=bar",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
path = "/",
|
||||
query = Some("foo=bar"),
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form_with_empty_path_and_fragment_with_slash,
|
||||
"http://127.0.0.1#foo/bar",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_absolute_form_with_empty_path_and_fragment_with_questionmark,
|
||||
"http://127.0.0.1#foo?bar",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_long_host_with_no_scheme,
|
||||
"thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost"),
|
||||
path = "",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_uri_parse_long_host_with_port_and_no_scheme,
|
||||
"thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost:1234",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("thequickbrownfoxjumpedoverthelazydogtofindthelargedangerousdragon.localhost:1234"),
|
||||
path = "",
|
||||
query = None,
|
||||
port = Port::from_str("1234").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo1,
|
||||
"http://a:b@127.0.0.1:1234/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("a:b@127.0.0.1:1234"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = Port::from_str("1234").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo2,
|
||||
"http://a:b@127.0.0.1/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("a:b@127.0.0.1"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo3,
|
||||
"http://a@127.0.0.1/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("a@127.0.0.1"),
|
||||
host = Some("127.0.0.1"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo_with_port,
|
||||
"user@localhost:3000",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("user@localhost:3000"),
|
||||
path = "",
|
||||
query = None,
|
||||
host = Some("localhost"),
|
||||
port = Port::from_str("3000").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_userinfo_pass_with_port,
|
||||
"user:pass@localhost:3000",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = part!("user:pass@localhost:3000"),
|
||||
path = "",
|
||||
query = None,
|
||||
host = Some("localhost"),
|
||||
port = Port::from_str("3000").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6,
|
||||
"http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
|
||||
host = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6_shorthand,
|
||||
"http://[::1]/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[::1]"),
|
||||
host = Some("[::1]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6_shorthand2,
|
||||
"http://[::]/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[::]"),
|
||||
host = Some("[::]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6_shorthand3,
|
||||
"http://[2001:db8::2:1]/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[2001:db8::2:1]"),
|
||||
host = Some("[2001:db8::2:1]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_ipv6_with_port,
|
||||
"http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008/",
|
||||
[],
|
||||
|
||||
scheme = part!("http"),
|
||||
authority = part!("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8008"),
|
||||
host = Some("[2001:0db8:85a3:0000:0000:8a2e:0370:7334]"),
|
||||
path = "/",
|
||||
query = None,
|
||||
port = Port::from_str("8008").ok(),
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_percentage_encoded_path,
|
||||
"/echo/abcdefgh_i-j%20/abcdefg_i-j%20478",
|
||||
[],
|
||||
|
||||
scheme = None,
|
||||
authority = None,
|
||||
host = None,
|
||||
path = "/echo/abcdefgh_i-j%20/abcdefg_i-j%20478",
|
||||
query = None,
|
||||
port = None,
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_path_permissive,
|
||||
"/foo=bar|baz\\^~%",
|
||||
[],
|
||||
|
||||
path = "/foo=bar|baz\\^~%",
|
||||
}
|
||||
|
||||
test_parse! {
|
||||
test_query_permissive,
|
||||
"/?foo={bar|baz}\\^`",
|
||||
[],
|
||||
|
||||
query = Some("foo={bar|baz}\\^`"),
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uri_parse_error() {
|
||||
fn err(s: &str) {
|
||||
Uri::from_str(s).unwrap_err();
|
||||
}
|
||||
|
||||
err("http://");
|
||||
err("htt:p//host");
|
||||
err("hyper.rs/");
|
||||
err("hyper.rs?key=val");
|
||||
err("?key=val");
|
||||
err("localhost/");
|
||||
err("localhost?key=val");
|
||||
err("\0");
|
||||
err("http://[::1");
|
||||
err("http://::1]");
|
||||
err("localhost:8080:3030");
|
||||
err("@");
|
||||
err("http://username:password@/wut");
|
||||
|
||||
// illegal queries
|
||||
err("/?foo\rbar");
|
||||
err("/?foo\nbar");
|
||||
err("/?<");
|
||||
err("/?>");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_uri_len() {
|
||||
let mut uri = vec![];
|
||||
uri.extend(b"http://localhost/");
|
||||
uri.extend(vec![b'a'; 70 * 1024]);
|
||||
|
||||
let uri = String::from_utf8(uri).unwrap();
|
||||
let res: Result<Uri, InvalidUri> = uri.parse();
|
||||
|
||||
assert_eq!(res.unwrap_err().0, ErrorKind::TooLong);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_overflowing_scheme() {
|
||||
let mut uri = vec![];
|
||||
uri.extend(vec![b'a'; 256]);
|
||||
uri.extend(b"://localhost/");
|
||||
|
||||
let uri = String::from_utf8(uri).unwrap();
|
||||
let res: Result<Uri, InvalidUri> = uri.parse();
|
||||
|
||||
assert_eq!(res.unwrap_err().0, ErrorKind::SchemeTooLong);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_max_length_scheme() {
|
||||
let mut uri = vec![];
|
||||
uri.extend(vec![b'a'; 64]);
|
||||
uri.extend(b"://localhost/");
|
||||
|
||||
let uri = String::from_utf8(uri).unwrap();
|
||||
let uri: Uri = uri.parse().unwrap();
|
||||
|
||||
assert_eq!(uri.scheme_str().unwrap().len(), 64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_uri_to_path_and_query() {
|
||||
let cases = vec![
|
||||
("/", "/"),
|
||||
("/foo?bar", "/foo?bar"),
|
||||
("/foo?bar#nope", "/foo?bar"),
|
||||
("http://hyper.rs", "/"),
|
||||
("http://hyper.rs/", "/"),
|
||||
("http://hyper.rs/path", "/path"),
|
||||
("http://hyper.rs?query", "/?query"),
|
||||
("*", "*"),
|
||||
];
|
||||
|
||||
for case in cases {
|
||||
let uri = Uri::from_str(case.0).unwrap();
|
||||
let s = uri.path_and_query().unwrap().to_string();
|
||||
|
||||
assert_eq!(s, case.1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_authority_uri_parts_round_trip() {
|
||||
let s = "hyper.rs";
|
||||
let uri = Uri::from_str(s).expect("first parse");
|
||||
assert_eq!(uri, s);
|
||||
assert_eq!(uri.to_string(), s);
|
||||
|
||||
let parts = uri.into_parts();
|
||||
let uri2 = Uri::from_parts(parts).expect("from_parts");
|
||||
assert_eq!(uri2, s);
|
||||
assert_eq!(uri2.to_string(), s);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_partial_eq_path_with_terminating_questionmark() {
|
||||
let a = "/path";
|
||||
let uri = Uri::from_str("/path?").expect("first parse");
|
||||
|
||||
assert_eq!(uri, a);
|
||||
}
|
||||
75
zeroidc/vendor/http/src/version.rs
vendored
Normal file
75
zeroidc/vendor/http/src/version.rs
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
//! HTTP version
|
||||
//!
|
||||
//! This module contains a definition of the `Version` type. The `Version`
|
||||
//! type is intended to be accessed through the root of the crate
|
||||
//! (`http::Version`) rather than this module.
|
||||
//!
|
||||
//! The `Version` type contains constants that represent the various versions
|
||||
//! of the HTTP protocol.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! use http::Version;
|
||||
//!
|
||||
//! let http11 = Version::HTTP_11;
|
||||
//! let http2 = Version::HTTP_2;
|
||||
//! assert!(http11 != http2);
|
||||
//!
|
||||
//! println!("{:?}", http2);
|
||||
//! ```
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Represents a version of the HTTP spec.
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone, Eq, Ord, Hash)]
|
||||
pub struct Version(Http);
|
||||
|
||||
impl Version {
|
||||
/// `HTTP/0.9`
|
||||
pub const HTTP_09: Version = Version(Http::Http09);
|
||||
|
||||
/// `HTTP/1.0`
|
||||
pub const HTTP_10: Version = Version(Http::Http10);
|
||||
|
||||
/// `HTTP/1.1`
|
||||
pub const HTTP_11: Version = Version(Http::Http11);
|
||||
|
||||
/// `HTTP/2.0`
|
||||
pub const HTTP_2: Version = Version(Http::H2);
|
||||
|
||||
/// `HTTP/3.0`
|
||||
pub const HTTP_3: Version = Version(Http::H3);
|
||||
}
|
||||
|
||||
#[derive(PartialEq, PartialOrd, Copy, Clone, Eq, Ord, Hash)]
|
||||
enum Http {
|
||||
Http09,
|
||||
Http10,
|
||||
Http11,
|
||||
H2,
|
||||
H3,
|
||||
__NonExhaustive,
|
||||
}
|
||||
|
||||
impl Default for Version {
|
||||
#[inline]
|
||||
fn default() -> Version {
|
||||
Version::HTTP_11
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Version {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
use self::Http::*;
|
||||
|
||||
f.write_str(match self.0 {
|
||||
Http09 => "HTTP/0.9",
|
||||
Http10 => "HTTP/1.0",
|
||||
Http11 => "HTTP/1.1",
|
||||
H2 => "HTTP/2.0",
|
||||
H3 => "HTTP/3.0",
|
||||
__NonExhaustive => unreachable!(),
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user