summaryrefslogtreecommitdiffstats
path: root/third_party/rust/h2/src/hpack/header.rs
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/h2/src/hpack/header.rs')
-rw-r--r--third_party/rust/h2/src/hpack/header.rs308
1 files changed, 308 insertions, 0 deletions
diff --git a/third_party/rust/h2/src/hpack/header.rs b/third_party/rust/h2/src/hpack/header.rs
new file mode 100644
index 0000000000..0b5d1fdedd
--- /dev/null
+++ b/third_party/rust/h2/src/hpack/header.rs
@@ -0,0 +1,308 @@
+use super::{DecoderError, NeedMore};
+use crate::ext::Protocol;
+
+use bytes::Bytes;
+use http::header::{HeaderName, HeaderValue};
+use http::{Method, StatusCode};
+use std::fmt;
+
+/// HTTP/2 Header
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub enum Header<T = HeaderName> {
+ Field { name: T, value: HeaderValue },
+ // TODO: Change these types to `http::uri` types.
+ Authority(BytesStr),
+ Method(Method),
+ Scheme(BytesStr),
+ Path(BytesStr),
+ Protocol(Protocol),
+ Status(StatusCode),
+}
+
+/// The header field name
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub enum Name<'a> {
+ Field(&'a HeaderName),
+ Authority,
+ Method,
+ Scheme,
+ Path,
+ Protocol,
+ Status,
+}
+
+#[doc(hidden)]
+#[derive(Clone, Eq, PartialEq, Default)]
+pub struct BytesStr(Bytes);
+
+pub fn len(name: &HeaderName, value: &HeaderValue) -> usize {
+ let n: &str = name.as_ref();
+ 32 + n.len() + value.len()
+}
+
+impl Header<Option<HeaderName>> {
+ pub fn reify(self) -> Result<Header, HeaderValue> {
+ use self::Header::*;
+
+ Ok(match self {
+ Field {
+ name: Some(n),
+ value,
+ } => Field { name: n, value },
+ Field { name: None, value } => return Err(value),
+ Authority(v) => Authority(v),
+ Method(v) => Method(v),
+ Scheme(v) => Scheme(v),
+ Path(v) => Path(v),
+ Protocol(v) => Protocol(v),
+ Status(v) => Status(v),
+ })
+ }
+}
+
+impl Header {
+ pub fn new(name: Bytes, value: Bytes) -> Result<Header, DecoderError> {
+ if name.is_empty() {
+ return Err(DecoderError::NeedMore(NeedMore::UnexpectedEndOfStream));
+ }
+ if name[0] == b':' {
+ match &name[1..] {
+ b"authority" => {
+ let value = BytesStr::try_from(value)?;
+ Ok(Header::Authority(value))
+ }
+ b"method" => {
+ let method = Method::from_bytes(&value)?;
+ Ok(Header::Method(method))
+ }
+ b"scheme" => {
+ let value = BytesStr::try_from(value)?;
+ Ok(Header::Scheme(value))
+ }
+ b"path" => {
+ let value = BytesStr::try_from(value)?;
+ Ok(Header::Path(value))
+ }
+ b"protocol" => {
+ let value = Protocol::try_from(value)?;
+ Ok(Header::Protocol(value))
+ }
+ b"status" => {
+ let status = StatusCode::from_bytes(&value)?;
+ Ok(Header::Status(status))
+ }
+ _ => Err(DecoderError::InvalidPseudoheader),
+ }
+ } else {
+ // HTTP/2 requires lower case header names
+ let name = HeaderName::from_lowercase(&name)?;
+ let value = HeaderValue::from_bytes(&value)?;
+
+ Ok(Header::Field { name, value })
+ }
+ }
+
+ pub fn len(&self) -> usize {
+ match *self {
+ Header::Field {
+ ref name,
+ ref value,
+ } => len(name, value),
+ Header::Authority(ref v) => 32 + 10 + v.len(),
+ Header::Method(ref v) => 32 + 7 + v.as_ref().len(),
+ Header::Scheme(ref v) => 32 + 7 + v.len(),
+ Header::Path(ref v) => 32 + 5 + v.len(),
+ Header::Protocol(ref v) => 32 + 9 + v.as_str().len(),
+ Header::Status(_) => 32 + 7 + 3,
+ }
+ }
+
+ /// Returns the header name
+ pub fn name(&self) -> Name {
+ match *self {
+ Header::Field { ref name, .. } => Name::Field(name),
+ Header::Authority(..) => Name::Authority,
+ Header::Method(..) => Name::Method,
+ Header::Scheme(..) => Name::Scheme,
+ Header::Path(..) => Name::Path,
+ Header::Protocol(..) => Name::Protocol,
+ Header::Status(..) => Name::Status,
+ }
+ }
+
+ pub fn value_slice(&self) -> &[u8] {
+ match *self {
+ Header::Field { ref value, .. } => value.as_ref(),
+ Header::Authority(ref v) => v.as_ref(),
+ Header::Method(ref v) => v.as_ref().as_ref(),
+ Header::Scheme(ref v) => v.as_ref(),
+ Header::Path(ref v) => v.as_ref(),
+ Header::Protocol(ref v) => v.as_ref(),
+ Header::Status(ref v) => v.as_str().as_ref(),
+ }
+ }
+
+ pub fn value_eq(&self, other: &Header) -> bool {
+ match *self {
+ Header::Field { ref value, .. } => {
+ let a = value;
+ match *other {
+ Header::Field { ref value, .. } => a == value,
+ _ => false,
+ }
+ }
+ Header::Authority(ref a) => match *other {
+ Header::Authority(ref b) => a == b,
+ _ => false,
+ },
+ Header::Method(ref a) => match *other {
+ Header::Method(ref b) => a == b,
+ _ => false,
+ },
+ Header::Scheme(ref a) => match *other {
+ Header::Scheme(ref b) => a == b,
+ _ => false,
+ },
+ Header::Path(ref a) => match *other {
+ Header::Path(ref b) => a == b,
+ _ => false,
+ },
+ Header::Protocol(ref a) => match *other {
+ Header::Protocol(ref b) => a == b,
+ _ => false,
+ },
+ Header::Status(ref a) => match *other {
+ Header::Status(ref b) => a == b,
+ _ => false,
+ },
+ }
+ }
+
+ pub fn is_sensitive(&self) -> bool {
+ match *self {
+ Header::Field { ref value, .. } => value.is_sensitive(),
+ // TODO: Technically these other header values can be sensitive too.
+ _ => false,
+ }
+ }
+
+ pub fn skip_value_index(&self) -> bool {
+ use http::header;
+
+ match *self {
+ Header::Field { ref name, .. } => matches!(
+ *name,
+ header::AGE
+ | header::AUTHORIZATION
+ | header::CONTENT_LENGTH
+ | header::ETAG
+ | header::IF_MODIFIED_SINCE
+ | header::IF_NONE_MATCH
+ | header::LOCATION
+ | header::COOKIE
+ | header::SET_COOKIE
+ ),
+ Header::Path(..) => true,
+ _ => false,
+ }
+ }
+}
+
+// Mostly for tests
+impl From<Header> for Header<Option<HeaderName>> {
+ fn from(src: Header) -> Self {
+ match src {
+ Header::Field { name, value } => Header::Field {
+ name: Some(name),
+ value,
+ },
+ Header::Authority(v) => Header::Authority(v),
+ Header::Method(v) => Header::Method(v),
+ Header::Scheme(v) => Header::Scheme(v),
+ Header::Path(v) => Header::Path(v),
+ Header::Protocol(v) => Header::Protocol(v),
+ Header::Status(v) => Header::Status(v),
+ }
+ }
+}
+
+impl<'a> Name<'a> {
+ pub fn into_entry(self, value: Bytes) -> Result<Header, DecoderError> {
+ match self {
+ Name::Field(name) => Ok(Header::Field {
+ name: name.clone(),
+ value: HeaderValue::from_bytes(&value)?,
+ }),
+ Name::Authority => Ok(Header::Authority(BytesStr::try_from(value)?)),
+ Name::Method => Ok(Header::Method(Method::from_bytes(&value)?)),
+ Name::Scheme => Ok(Header::Scheme(BytesStr::try_from(value)?)),
+ Name::Path => Ok(Header::Path(BytesStr::try_from(value)?)),
+ Name::Protocol => Ok(Header::Protocol(Protocol::try_from(value)?)),
+ Name::Status => {
+ match StatusCode::from_bytes(&value) {
+ Ok(status) => Ok(Header::Status(status)),
+ // TODO: better error handling
+ Err(_) => Err(DecoderError::InvalidStatusCode),
+ }
+ }
+ }
+ }
+
+ pub fn as_slice(&self) -> &[u8] {
+ match *self {
+ Name::Field(ref name) => name.as_ref(),
+ Name::Authority => b":authority",
+ Name::Method => b":method",
+ Name::Scheme => b":scheme",
+ Name::Path => b":path",
+ Name::Protocol => b":protocol",
+ Name::Status => b":status",
+ }
+ }
+}
+
+// ===== impl BytesStr =====
+
+impl BytesStr {
+ pub(crate) const fn from_static(value: &'static str) -> Self {
+ BytesStr(Bytes::from_static(value.as_bytes()))
+ }
+
+ pub(crate) fn from(value: &str) -> Self {
+ BytesStr(Bytes::copy_from_slice(value.as_bytes()))
+ }
+
+ #[doc(hidden)]
+ pub fn try_from(bytes: Bytes) -> Result<Self, std::str::Utf8Error> {
+ std::str::from_utf8(bytes.as_ref())?;
+ Ok(BytesStr(bytes))
+ }
+
+ pub(crate) fn as_str(&self) -> &str {
+ // Safety: check valid utf-8 in constructor
+ unsafe { std::str::from_utf8_unchecked(self.0.as_ref()) }
+ }
+
+ pub(crate) fn into_inner(self) -> Bytes {
+ self.0
+ }
+}
+
+impl std::ops::Deref for BytesStr {
+ type Target = str;
+ fn deref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<[u8]> for BytesStr {
+ fn as_ref(&self) -> &[u8] {
+ self.0.as_ref()
+ }
+}
+
+impl fmt::Debug for BytesStr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.0.fmt(f)
+ }
+}