Skip to content

Commit 348b7d9

Browse files
authored
Merge branch 'main' into header-content-location
2 parents f4abf11 + 8aa4f52 commit 348b7d9

File tree

17 files changed

+554
-99
lines changed

17 files changed

+554
-99
lines changed

Cargo.toml

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "http-types"
3-
version = "2.5.0"
3+
version = "2.6.0"
44
license = "MIT OR Apache-2.0"
55
repository = "https://github.com/http-rs/http-types"
66
documentation = "https://docs.rs/http-types"
@@ -16,17 +16,19 @@ features = ["docs"]
1616
rustdoc-args = ["--cfg", "feature=\"docs\""]
1717

1818
[features]
19-
default = ["async_std", "cookie-secure"]
19+
default = ["fs", "cookie-secure"]
2020
docs = ["unstable"]
2121
unstable = []
2222
hyperium_http = ["http"]
23-
async_std = [] # "async-std" when it is not default
23+
async_std = ["fs"]
2424
cookie-secure = ["cookie/secure"]
25+
fs = ["async-std"]
2526

2627
[dependencies]
27-
# Note(yoshuawuyts): used for async_std's `channel` only; use "core" once possible.
2828
# features: async_std
29-
async-std = { version = "1.6.0", features = ["unstable"] }
29+
async-std = { version = "1.6.0", optional = true }
30+
futures-lite = "1.11.1"
31+
async-channel = "1.5.1"
3032

3133
# features: hyperium/http
3234
http = { version = "0.2.0", optional = true }
@@ -44,4 +46,4 @@ serde_qs = "0.7.0"
4446

4547
[dev-dependencies]
4648
http = "0.2.0"
47-
async-std = { version = "1.6.0", features = ["unstable", "attributes"] }
49+
async-std = { version = "1.6.0", features = ["attributes"] }

src/body.rs

Lines changed: 17 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
use async_std::io::prelude::*;
2-
use async_std::io::{self, Cursor};
1+
use futures_lite::{io, prelude::*};
32
use serde::{de::DeserializeOwned, Serialize};
43

54
use std::fmt::{self, Debug};
@@ -54,7 +53,7 @@ pin_project_lite::pin_project! {
5453
/// and not rely on the fallback mechanisms. However, they're still there if you need them.
5554
pub struct Body {
5655
#[pin]
57-
reader: Box<dyn BufRead + Unpin + Send + Sync + 'static>,
56+
reader: Box<dyn AsyncBufRead + Unpin + Send + Sync + 'static>,
5857
mime: Mime,
5958
length: Option<usize>,
6059
}
@@ -102,7 +101,7 @@ impl Body {
102101
/// req.set_body(Body::from_reader(cursor, Some(len)));
103102
/// ```
104103
pub fn from_reader(
105-
reader: impl BufRead + Unpin + Send + Sync + 'static,
104+
reader: impl AsyncBufRead + Unpin + Send + Sync + 'static,
106105
len: Option<usize>,
107106
) -> Self {
108107
Self {
@@ -125,7 +124,7 @@ impl Body {
125124
/// let body = Body::from_reader(cursor, None);
126125
/// let _ = body.into_reader();
127126
/// ```
128-
pub fn into_reader(self) -> Box<dyn BufRead + Unpin + Send + Sync + 'static> {
127+
pub fn into_reader(self) -> Box<dyn AsyncBufRead + Unpin + Send + Sync + 'static> {
129128
self.reader
130129
}
131130

@@ -160,7 +159,7 @@ impl Body {
160159
/// # Examples
161160
///
162161
/// ```
163-
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
162+
/// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
164163
/// use http_types::Body;
165164
///
166165
/// let bytes = vec![1, 2, 3];
@@ -209,9 +208,7 @@ impl Body {
209208
/// # Examples
210209
///
211210
/// ```
212-
/// # use std::io::prelude::*;
213-
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
214-
/// # async_std::task::block_on(async {
211+
/// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
215212
/// use http_types::Body;
216213
/// use async_std::io::Cursor;
217214
///
@@ -246,7 +243,7 @@ impl Body {
246243
let bytes = serde_json::to_vec(&json)?;
247244
let body = Self {
248245
length: Some(bytes.len()),
249-
reader: Box::new(Cursor::new(bytes)),
246+
reader: Box::new(io::Cursor::new(bytes)),
250247
mime: mime::JSON,
251248
};
252249
Ok(body)
@@ -257,7 +254,7 @@ impl Body {
257254
/// # Examples
258255
///
259256
/// ```
260-
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
257+
/// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
261258
/// use http_types::Body;
262259
/// use http_types::convert::{Serialize, Deserialize};
263260
///
@@ -290,7 +287,7 @@ impl Body {
290287
/// # Examples
291288
///
292289
/// ```
293-
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
290+
/// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
294291
/// use http_types::Body;
295292
/// use http_types::convert::{Serialize, Deserialize};
296293
///
@@ -310,7 +307,7 @@ impl Body {
310307

311308
let body = Self {
312309
length: Some(bytes.len()),
313-
reader: Box::new(Cursor::new(bytes)),
310+
reader: Box::new(io::Cursor::new(bytes)),
314311
mime: mime::FORM,
315312
};
316313
Ok(body)
@@ -326,7 +323,7 @@ impl Body {
326323
/// # Examples
327324
///
328325
/// ```
329-
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
326+
/// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
330327
/// use http_types::Body;
331328
/// use http_types::convert::{Serialize, Deserialize};
332329
///
@@ -353,14 +350,14 @@ impl Body {
353350
/// # Examples
354351
///
355352
/// ```no_run
356-
/// # fn main() -> Result<(), http_types::Error> { async_std::task::block_on(async {
353+
/// # fn main() -> http_types::Result<()> { async_std::task::block_on(async {
357354
/// use http_types::{Body, Response, StatusCode};
358355
///
359356
/// let mut res = Response::new(StatusCode::Ok);
360357
/// res.set_body(Body::from_file("/path/to/file").await?);
361358
/// # Ok(()) }) }
362359
/// ```
363-
#[cfg(all(feature = "async_std", not(target_os = "unknown")))]
360+
#[cfg(all(feature = "fs", not(target_os = "unknown")))]
364361
pub async fn from_file<P>(path: P) -> io::Result<Self>
365362
where
366363
P: AsRef<std::path::Path>,
@@ -455,7 +452,7 @@ impl<'a> From<&'a [u8]> for Body {
455452
}
456453
}
457454

458-
impl Read for Body {
455+
impl AsyncRead for Body {
459456
#[allow(missing_doc_code_examples)]
460457
fn poll_read(
461458
mut self: Pin<&mut Self>,
@@ -466,7 +463,7 @@ impl Read for Body {
466463
}
467464
}
468465

469-
impl BufRead for Body {
466+
impl AsyncBufRead for Body {
470467
#[allow(missing_doc_code_examples)]
471468
fn poll_fill_buf(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<&'_ [u8]>> {
472469
let this = self.project();
@@ -480,7 +477,7 @@ impl BufRead for Body {
480477

481478
/// Look at first few bytes of a file to determine the mime type.
482479
/// This is used for various binary formats such as images and videos.
483-
#[cfg(all(feature = "async_std", not(target_os = "unknown")))]
480+
#[cfg(all(feature = "fs", not(target_os = "unknown")))]
484481
async fn peek_mime(file: &mut async_std::fs::File) -> io::Result<Option<Mime>> {
485482
// We need to read the first 300 bytes to correctly infer formats such as tar.
486483
let mut buf = [0_u8; 300];
@@ -494,7 +491,7 @@ async fn peek_mime(file: &mut async_std::fs::File) -> io::Result<Option<Mime>> {
494491

495492
/// Look at the extension of a file to determine the mime type.
496493
/// This is useful for plain-text formats such as HTML and CSS.
497-
#[cfg(all(feature = "async_std", not(target_os = "unknown")))]
494+
#[cfg(all(feature = "fs", not(target_os = "unknown")))]
498495
fn guess_ext(path: &std::path::Path) -> Option<Mime> {
499496
let ext = path.extension().map(|p| p.to_str()).flatten();
500497
ext.and_then(Mime::from_extension)

src/content/content_length.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use crate::headers::{HeaderName, HeaderValue, Headers, CONTENT_LENGTH};
2+
use crate::Status;
3+
4+
/// The size of the entity-body, in bytes, sent to the recipient.
5+
///
6+
/// # Specifications
7+
///
8+
/// - [RFC 7230, section 3.3.2: Content-Length](https://tools.ietf.org/html/rfc7230#section-3.3.2)
9+
///
10+
/// # Examples
11+
///
12+
/// ```
13+
/// # fn main() -> http_types::Result<()> {
14+
/// #
15+
/// use http_types::Response;
16+
/// use http_types::content::{ContentLength};
17+
///
18+
/// let content_len = ContentLength::new(12);
19+
///
20+
/// let mut res = Response::new(200);
21+
/// content_len.apply(&mut res);
22+
///
23+
/// let content_len = ContentLength::from_headers(res)?.unwrap();
24+
/// assert_eq!(content_len.len(), 12);
25+
/// #
26+
/// # Ok(()) }
27+
/// ```
28+
#[derive(Debug)]
29+
pub struct ContentLength {
30+
length: u64,
31+
}
32+
33+
#[allow(clippy::len_without_is_empty)]
34+
impl ContentLength {
35+
/// Create a new instance.
36+
pub fn new(length: u64) -> Self {
37+
Self { length }
38+
}
39+
40+
/// Create a new instance from headers.
41+
pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
42+
let headers = match headers.as_ref().get(CONTENT_LENGTH) {
43+
Some(headers) => headers,
44+
None => return Ok(None),
45+
};
46+
47+
// If we successfully parsed the header then there's always at least one
48+
// entry. We want the last entry.
49+
let value = headers.iter().last().unwrap();
50+
let length = value.as_str().trim().parse().status(400)?;
51+
Ok(Some(Self { length }))
52+
}
53+
54+
/// Sets the header.
55+
pub fn apply(&self, mut headers: impl AsMut<Headers>) {
56+
headers.as_mut().insert(self.name(), self.value());
57+
}
58+
59+
/// Get the `HeaderName`.
60+
pub fn name(&self) -> HeaderName {
61+
CONTENT_LENGTH
62+
}
63+
64+
/// Get the `HeaderValue`.
65+
pub fn value(&self) -> HeaderValue {
66+
let output = format!("{}", self.length);
67+
68+
// SAFETY: the internal string is validated to be ASCII.
69+
unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
70+
}
71+
72+
/// Get the content length.
73+
pub fn len(&self) -> u64 {
74+
self.length
75+
}
76+
77+
/// Set the content length.
78+
pub fn set_len(&mut self, len: u64) {
79+
self.length = len;
80+
}
81+
}
82+
83+
#[cfg(test)]
84+
mod test {
85+
use super::*;
86+
use crate::headers::Headers;
87+
88+
#[test]
89+
fn smoke() -> crate::Result<()> {
90+
let content_len = ContentLength::new(12);
91+
92+
let mut headers = Headers::new();
93+
content_len.apply(&mut headers);
94+
95+
let content_len = ContentLength::from_headers(headers)?.unwrap();
96+
assert_eq!(content_len.len(), 12);
97+
Ok(())
98+
}
99+
100+
#[test]
101+
fn bad_request_on_parse_error() -> crate::Result<()> {
102+
let mut headers = Headers::new();
103+
headers.insert(CONTENT_LENGTH, "<nori ate the tag. yum.>");
104+
let err = ContentLength::from_headers(headers).unwrap_err();
105+
assert_eq!(err.status(), 400);
106+
Ok(())
107+
}
108+
}

src/content/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
pub mod accept_encoding;
99
pub mod content_encoding;
1010

11+
mod content_length;
1112
mod content_location;
1213
mod encoding;
1314
mod encoding_proposal;
@@ -16,6 +17,7 @@ mod encoding_proposal;
1617
pub use accept_encoding::AcceptEncoding;
1718
#[doc(inline)]
1819
pub use content_encoding::ContentEncoding;
20+
pub use content_length::ContentLength;
1921
pub use content_location::ContentLocation;
2022
pub use encoding::Encoding;
2123
pub use encoding_proposal::EncodingProposal;

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@ pub mod conditional;
122122
pub mod content;
123123
pub mod headers;
124124
pub mod mime;
125+
pub mod other;
125126
pub mod proxies;
127+
pub mod server;
126128

127129
mod body;
128130
mod error;

0 commit comments

Comments
 (0)