Skip to content

Commit

Permalink
Merge branch 'master' into feat/allow-utf8-in-path-and-query
Browse files Browse the repository at this point in the history
  • Loading branch information
seanmonstar authored Dec 20, 2024
2 parents ee7266d + a912445 commit 2080183
Show file tree
Hide file tree
Showing 15 changed files with 101 additions and 75 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
# 1.2.0 (December 3, 2024)

* Add `StatusCode::TOO_EARLY` constant for 425 status.
* Loosen `TryFrom<HashMap>` for `HeaderMap` to work with any state generic.
* Change `Builder` methods to use `TryInto` instead of `TryFrom` arguments.
* Make `StatusCode::as_u16` a `const` function.
* Fix `Method` parsing to allow `#$%&'` characters.
* Fix `HeaderName` parsing to reject `"` characters.
* Fix off by 1 error in `Method::from_bytes` that could cause extra allocations.

# 1.1.0 (March 4, 2024)

* Add methods to allow trying to allocate in the `HeaderMap`, returning an error if oversize instead of panicking.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ name = "http"
# - Update html_root_url in lib.rs.
# - Update CHANGELOG.md.
# - Create git tag
version = "1.1.0"
version = "1.2.0"
readme = "README.md"
documentation = "https://docs.rs/http"
repository = "https://github.com/hyperium/http"
Expand Down
2 changes: 1 addition & 1 deletion src/byte_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ impl ByteStr {
///
/// ## 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.
/// behavior 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) {
Expand Down
4 changes: 2 additions & 2 deletions src/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Extensions {
/// Insert a type into this `Extensions`.
///
/// If a extension of this type already existed, it will
/// be returned.
/// be returned and replaced with the new one.
///
/// # Example
///
Expand Down Expand Up @@ -215,7 +215,7 @@ impl Extensions {
self.map.as_ref().map_or(true, |map| map.is_empty())
}

/// Get the numer of extensions available.
/// Get the number of extensions available.
///
/// # Example
///
Expand Down
6 changes: 3 additions & 3 deletions src/header/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ impl<T> HeaderMap<T> {
Ok(HeaderMap {
mask: (raw_cap - 1) as Size,
indices: vec![Pos::none(); raw_cap].into_boxed_slice(),
entries: Vec::with_capacity(raw_cap),
entries: Vec::with_capacity(usable_capacity(raw_cap)),
extra_values: Vec::new(),
danger: Danger::Green,
})
Expand Down Expand Up @@ -2020,7 +2020,7 @@ impl<T> FromIterator<(HeaderName, T)> for HeaderMap<T> {
/// let headers: HeaderMap = (&map).try_into().expect("valid headers");
/// assert_eq!(headers["X-Custom-Header"], "my value");
/// ```
impl<'a, K, V, T> TryFrom<&'a HashMap<K, V>> for HeaderMap<T>
impl<'a, K, V, S, T> TryFrom<&'a HashMap<K, V, S>> for HeaderMap<T>
where
K: Eq + Hash,
HeaderName: TryFrom<&'a K>,
Expand All @@ -2030,7 +2030,7 @@ where
{
type Error = Error;

fn try_from(c: &'a HashMap<K, V>) -> Result<Self, Self::Error> {
fn try_from(c: &'a HashMap<K, V, S>) -> Result<Self, Self::Error> {
c.iter()
.map(|(k, v)| -> crate::Result<(HeaderName, T)> {
let name = TryFrom::try_from(k).map_err(Into::into)?;
Expand Down
8 changes: 4 additions & 4 deletions src/header/name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1011,7 +1011,7 @@ const HEADER_CHARS: [u8; 256] = [
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, b'!', b'"', b'#', b'$', b'%', b'&', b'\'', // 3x
0, 0, 0, b'!', 0, b'#', b'$', b'%', b'&', b'\'', // 3x
0, 0, b'*', 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', 0, 0, // 5x
0, 0, 0, 0, 0, b'a', b'b', b'c', b'd', b'e', // 6x
Expand Down Expand Up @@ -1659,13 +1659,13 @@ const SCRATCH_BUF_OVERFLOW: usize = SCRATCH_BUF_SIZE + 1;
fn uninit_u8_array() -> [MaybeUninit<u8>; SCRATCH_BUF_SIZE] {
let arr = MaybeUninit::<[MaybeUninit<u8>; SCRATCH_BUF_SIZE]>::uninit();
// Safety: assume_init() is claiming that an array of MaybeUninit<>
// has been initilized, but MaybeUninit<>'s do not require initilizaton.
// has been initialized, but MaybeUninit<>'s do not require initialization.
unsafe { arr.assume_init() }
}

// Assuming all the elements are initilized, get a slice of them.
// Assuming all the elements are initialized, get a slice of them.
//
// Safety: All elements of `slice` must be initilized to prevent
// Safety: All elements of `slice` must be initialized to prevent
// undefined behavior.
unsafe fn slice_assume_init<T>(slice: &[MaybeUninit<T>]) -> &[T] {
&*(slice as *const [MaybeUninit<T>] as *const [T])
Expand Down
13 changes: 5 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,13 @@
//! 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`][header::HeaderName]/[`HeaderValue`][header::HeaderName] definitions to get grouped in a [`HeaderMap`] to
//! [`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.
//! particular implementation.
//!
//! ## Requests and Responses
//!
Expand Down Expand Up @@ -87,10 +84,10 @@
//! Accept: text/html
//! ```
//!
//! Then `"Accept"` is a [`HeaderName`][header::HeaderName] while `"text/html"` is a [`HeaderValue`][header::HeaderValue].
//! 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`][header::HeaderName] is always a valid `&str`, but a [`HeaderValue`] may
//! 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
Expand Down Expand Up @@ -134,7 +131,7 @@
//! 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`][header::HeaderName] and generic values. This type, like header names, is optimized
//! [`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
Expand Down
17 changes: 16 additions & 1 deletion src/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ impl Method {
_ => Method::extension_inline(src),
},
_ => {
if src.len() < InlineExtension::MAX {
if src.len() <= InlineExtension::MAX {
Method::extension_inline(src)
} else {
let allocated = AllocatedExtension::new(src)?;
Expand Down Expand Up @@ -465,6 +465,21 @@ mod test {

let long_method = "This_is_a_very_long_method.It_is_valid_but_unlikely.";
assert_eq!(Method::from_str(long_method).unwrap(), long_method);

let longest_inline_method = [b'A'; InlineExtension::MAX];
assert_eq!(
Method::from_bytes(&longest_inline_method).unwrap(),
Method(ExtensionInline(
InlineExtension::new(&longest_inline_method).unwrap()
))
);
let shortest_allocated_method = [b'A'; InlineExtension::MAX + 1];
assert_eq!(
Method::from_bytes(&shortest_allocated_method).unwrap(),
Method(ExtensionAllocated(
AllocatedExtension::new(&shortest_allocated_method).unwrap()
))
);
}

#[test]
Expand Down
62 changes: 31 additions & 31 deletions src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
//! ```
use std::any::Any;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::fmt;

use crate::header::{HeaderMap, HeaderName, HeaderValue};
Expand Down Expand Up @@ -231,8 +231,8 @@ impl Request<()> {
/// ```
pub fn get<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::GET).uri(uri)
}
Expand All @@ -253,8 +253,8 @@ impl Request<()> {
/// ```
pub fn put<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::PUT).uri(uri)
}
Expand All @@ -275,8 +275,8 @@ impl Request<()> {
/// ```
pub fn post<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::POST).uri(uri)
}
Expand All @@ -297,8 +297,8 @@ impl Request<()> {
/// ```
pub fn delete<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::DELETE).uri(uri)
}
Expand All @@ -320,8 +320,8 @@ impl Request<()> {
/// ```
pub fn options<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::OPTIONS).uri(uri)
}
Expand All @@ -342,8 +342,8 @@ impl Request<()> {
/// ```
pub fn head<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::HEAD).uri(uri)
}
Expand All @@ -364,8 +364,8 @@ impl Request<()> {
/// ```
pub fn connect<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::CONNECT).uri(uri)
}
Expand All @@ -386,8 +386,8 @@ impl Request<()> {
/// ```
pub fn patch<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::PATCH).uri(uri)
}
Expand All @@ -408,8 +408,8 @@ impl Request<()> {
/// ```
pub fn trace<T>(uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
Builder::new().method(Method::TRACE).uri(uri)
}
Expand Down Expand Up @@ -767,11 +767,11 @@ impl Builder {
/// ```
pub fn method<T>(self, method: T) -> Builder
where
Method: TryFrom<T>,
<Method as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Method>,
<T as TryInto<Method>>::Error: Into<crate::Error>,
{
self.and_then(move |mut head| {
let method = TryFrom::try_from(method).map_err(Into::into)?;
let method = method.try_into().map_err(Into::into)?;
head.method = method;
Ok(head)
})
Expand Down Expand Up @@ -812,11 +812,11 @@ impl Builder {
/// ```
pub fn uri<T>(self, uri: T) -> Builder
where
Uri: TryFrom<T>,
<Uri as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<Uri>,
<T as TryInto<Uri>>::Error: Into<crate::Error>,
{
self.and_then(move |mut head| {
head.uri = TryFrom::try_from(uri).map_err(Into::into)?;
head.uri = uri.try_into().map_err(Into::into)?;
Ok(head)
})
}
Expand Down Expand Up @@ -900,14 +900,14 @@ impl Builder {
/// ```
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>,
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: Into<crate::Error>,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::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)?;
let name = key.try_into().map_err(Into::into)?;
let value = value.try_into().map_err(Into::into)?;
head.headers.try_append(name, value)?;
Ok(head)
})
Expand Down
20 changes: 10 additions & 10 deletions src/response.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
//! ```
use std::any::Any;
use std::convert::TryFrom;
use std::convert::TryInto;
use std::fmt;

use crate::header::{HeaderMap, HeaderName, HeaderValue};
Expand Down Expand Up @@ -559,11 +559,11 @@ impl Builder {
/// ```
pub fn status<T>(self, status: T) -> Builder
where
StatusCode: TryFrom<T>,
<StatusCode as TryFrom<T>>::Error: Into<crate::Error>,
T: TryInto<StatusCode>,
<T as TryInto<StatusCode>>::Error: Into<crate::Error>,
{
self.and_then(move |mut head| {
head.status = TryFrom::try_from(status).map_err(Into::into)?;
head.status = status.try_into().map_err(Into::into)?;
Ok(head)
})
}
Expand Down Expand Up @@ -610,14 +610,14 @@ impl Builder {
/// ```
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>,
K: TryInto<HeaderName>,
<K as TryInto<HeaderName>>::Error: Into<crate::Error>,
V: TryInto<HeaderValue>,
<V as TryInto<HeaderValue>>::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)?;
let name = key.try_into().map_err(Into::into)?;
let value = value.try_into().map_err(Into::into)?;
head.headers.try_append(name, value)?;
Ok(head)
})
Expand Down
Loading

0 comments on commit 2080183

Please sign in to comment.