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:
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));
|
||||
}
|
||||
Reference in New Issue
Block a user