Skip to content

Commit 2a19ab7

Browse files
committed
feat(http1): Make HTTP/1 support an optional feature
cc #2251 BREAKING CHANGE: This puts all HTTP/1 methods and support behind an `http1` cargo feature, which will not be enabled by default. To use HTTP/1, add `features = ["http1"]` to the hyper dependency in your `Cargo.toml`.
1 parent 2f2ceb2 commit 2a19ab7

File tree

31 files changed

+459
-239
lines changed

31 files changed

+459
-239
lines changed

Cargo.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
[package]
32
name = "hyper"
43
version = "0.14.0-dev" # don't forget to update html_root_url
@@ -76,11 +75,11 @@ default = [
7675
"runtime",
7776
"stream",
7877

79-
#"http1",
78+
"http1",
8079
"http2",
8180
]
8281
full = [
83-
#"http1",
82+
"http1",
8483
"http2",
8584
"stream",
8685
"runtime",
@@ -97,7 +96,7 @@ tcp = [
9796
]
9897

9998
# HTTP versions
100-
#http1 = []
99+
http1 = []
101100
http2 = ["h2"]
102101

103102
# `impl Stream` for things

src/body/body.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,23 @@ use std::error::Error as StdError;
44
use std::fmt;
55

66
use bytes::Bytes;
7-
use futures_channel::{mpsc, oneshot};
7+
use futures_channel::mpsc;
8+
#[cfg(any(feature = "http1", feature = "http2"))]
9+
use futures_channel::oneshot;
810
use futures_core::Stream; // for mpsc::Receiver
911
#[cfg(feature = "stream")]
1012
use futures_util::TryStreamExt;
1113
use http::HeaderMap;
1214
use http_body::{Body as HttpBody, SizeHint};
1315

16+
use super::DecodedLength;
1417
#[cfg(feature = "stream")]
1518
use crate::common::sync_wrapper::SyncWrapper;
16-
use crate::common::{task, watch, Future, Never, Pin, Poll};
19+
use crate::common::{task, watch, Pin, Poll};
20+
#[cfg(any(feature = "http1", feature = "http2"))]
21+
use crate::common::{Future, Never};
1722
#[cfg(feature = "http2")]
1823
use crate::proto::h2::ping;
19-
use crate::proto::DecodedLength;
2024
use crate::upgrade::OnUpgrade;
2125

2226
type BodySender = mpsc::Sender<Result<Bytes, crate::Error>>;
@@ -67,14 +71,17 @@ struct Extra {
6771
on_upgrade: OnUpgrade,
6872
}
6973

74+
#[cfg(any(feature = "http1", feature = "http2"))]
7075
type DelayEofUntil = oneshot::Receiver<Never>;
7176

7277
enum DelayEof {
7378
/// Initial state, stream hasn't seen EOF yet.
79+
#[cfg(any(feature = "http1", feature = "http2"))]
7480
NotEof(DelayEofUntil),
7581
/// Transitions to this state once we've seen `poll` try to
7682
/// return EOF (`None`). This future is then polled, and
7783
/// when it completes, the Body finally returns EOF (`None`).
84+
#[cfg(any(feature = "http1", feature = "http2"))]
7885
Eof(DelayEofUntil),
7986
}
8087

@@ -203,13 +210,15 @@ impl Body {
203210
body
204211
}
205212

213+
#[cfg(feature = "http1")]
206214
pub(crate) fn set_on_upgrade(&mut self, upgrade: OnUpgrade) {
207215
debug_assert!(!upgrade.is_none(), "set_on_upgrade with empty upgrade");
208216
let extra = self.extra_mut();
209217
debug_assert!(extra.on_upgrade.is_none(), "set_on_upgrade twice");
210218
extra.on_upgrade = upgrade;
211219
}
212220

221+
#[cfg(any(feature = "http1", feature = "http2"))]
213222
pub(crate) fn delayed_eof(&mut self, fut: DelayEofUntil) {
214223
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(fut));
215224
}
@@ -220,6 +229,7 @@ impl Body {
220229
.and_then(|extra| extra.delayed_eof.take())
221230
}
222231

232+
#[cfg(any(feature = "http1", feature = "http2"))]
223233
fn extra_mut(&mut self) -> &mut Extra {
224234
self.extra.get_or_insert_with(|| {
225235
Box::new(Extra {
@@ -231,6 +241,7 @@ impl Body {
231241

232242
fn poll_eof(&mut self, cx: &mut task::Context<'_>) -> Poll<Option<crate::Result<Bytes>>> {
233243
match self.take_delayed_eof() {
244+
#[cfg(any(feature = "http1", feature = "http2"))]
234245
Some(DelayEof::NotEof(mut delay)) => match self.poll_inner(cx) {
235246
ok @ Poll::Ready(Some(Ok(..))) | ok @ Poll::Pending => {
236247
self.extra_mut().delayed_eof = Some(DelayEof::NotEof(delay));
@@ -246,6 +257,7 @@ impl Body {
246257
},
247258
Poll::Ready(Some(Err(e))) => Poll::Ready(Some(Err(e))),
248259
},
260+
#[cfg(any(feature = "http1", feature = "http2"))]
249261
Some(DelayEof::Eof(mut delay)) => match Pin::new(&mut delay).poll(cx) {
250262
Poll::Ready(Ok(never)) => match never {},
251263
Poll::Pending => {
@@ -254,6 +266,8 @@ impl Body {
254266
}
255267
Poll::Ready(Err(_done)) => Poll::Ready(None),
256268
},
269+
#[cfg(not(any(feature = "http1", feature = "http2")))]
270+
Some(delay_eof) => match delay_eof {},
257271
None => self.poll_inner(cx),
258272
}
259273
}
@@ -300,6 +314,7 @@ impl Body {
300314
}
301315
}
302316

317+
#[cfg(feature = "http1")]
303318
pub(super) fn take_full_data(&mut self) -> Option<Bytes> {
304319
if let Kind::Once(ref mut chunk) = self.kind {
305320
chunk.take()
@@ -549,6 +564,7 @@ impl Sender {
549564
.try_send(Err(crate::Error::new_body_write_aborted()));
550565
}
551566

567+
#[cfg(feature = "http1")]
552568
pub(crate) fn send_error(&mut self, err: crate::Error) {
553569
let _ = self.tx.try_send(Err(err));
554570
}

src/body/length.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use std::fmt;
2+
3+
#[derive(Clone, Copy, PartialEq, Eq)]
4+
pub(crate) struct DecodedLength(u64);
5+
6+
#[cfg(any(feature = "http1", feature = "http2", test))]
7+
const MAX_LEN: u64 = std::u64::MAX - 2;
8+
9+
impl DecodedLength {
10+
pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(::std::u64::MAX);
11+
pub(crate) const CHUNKED: DecodedLength = DecodedLength(::std::u64::MAX - 1);
12+
pub(crate) const ZERO: DecodedLength = DecodedLength(0);
13+
14+
#[cfg(test)]
15+
pub(crate) fn new(len: u64) -> Self {
16+
debug_assert!(len <= MAX_LEN);
17+
DecodedLength(len)
18+
}
19+
20+
/// Takes the length as a content-length without other checks.
21+
///
22+
/// Should only be called if previously confirmed this isn't
23+
/// CLOSE_DELIMITED or CHUNKED.
24+
#[inline]
25+
#[cfg(feature = "http1")]
26+
pub(crate) fn danger_len(self) -> u64 {
27+
debug_assert!(self.0 < Self::CHUNKED.0);
28+
self.0
29+
}
30+
31+
/// Converts to an Option<u64> representing a Known or Unknown length.
32+
pub(crate) fn into_opt(self) -> Option<u64> {
33+
match self {
34+
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
35+
DecodedLength(known) => Some(known),
36+
}
37+
}
38+
39+
/// Checks the `u64` is within the maximum allowed for content-length.
40+
#[cfg(any(feature = "http1", feature = "http2"))]
41+
pub(crate) fn checked_new(len: u64) -> Result<Self, crate::error::Parse> {
42+
if len <= MAX_LEN {
43+
Ok(DecodedLength(len))
44+
} else {
45+
warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN);
46+
Err(crate::error::Parse::TooLarge)
47+
}
48+
}
49+
50+
pub(crate) fn sub_if(&mut self, amt: u64) {
51+
match *self {
52+
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (),
53+
DecodedLength(ref mut known) => {
54+
*known -= amt;
55+
}
56+
}
57+
}
58+
}
59+
60+
impl fmt::Debug for DecodedLength {
61+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62+
match *self {
63+
DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"),
64+
DecodedLength::CHUNKED => f.write_str("CHUNKED"),
65+
DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(),
66+
}
67+
}
68+
}
69+
70+
impl fmt::Display for DecodedLength {
71+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72+
match *self {
73+
DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"),
74+
DecodedLength::CHUNKED => f.write_str("chunked encoding"),
75+
DecodedLength::ZERO => f.write_str("empty"),
76+
DecodedLength(n) => write!(f, "content-length ({} bytes)", n),
77+
}
78+
}
79+
}
80+
81+
#[cfg(test)]
82+
mod tests {
83+
use super::*;
84+
85+
#[test]
86+
fn sub_if_known() {
87+
let mut len = DecodedLength::new(30);
88+
len.sub_if(20);
89+
90+
assert_eq!(len.0, 10);
91+
}
92+
93+
#[test]
94+
fn sub_if_chunked() {
95+
let mut len = DecodedLength::CHUNKED;
96+
len.sub_if(20);
97+
98+
assert_eq!(len, DecodedLength::CHUNKED);
99+
}
100+
}

src/body/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@ pub use http_body::Body as HttpBody;
2020

2121
pub use self::aggregate::aggregate;
2222
pub use self::body::{Body, Sender};
23+
pub(crate) use self::length::DecodedLength;
2324
pub use self::to_bytes::to_bytes;
2425

2526
mod aggregate;
2627
mod body;
28+
mod length;
2729
mod to_bytes;
2830

2931
/// An optimization to try to take a full body if immediately available.
3032
///
3133
/// This is currently limited to *only* `hyper::Body`s.
34+
#[cfg(feature = "http1")]
3235
pub(crate) fn take_full_data<T: HttpBody + 'static>(body: &mut T) -> Option<T::Data> {
3336
use std::any::{Any, TypeId};
3437

src/cfg.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,37 @@
1-
macro_rules! cfg_http2 {
1+
macro_rules! cfg_any_http {
22
($($item:item)*) => {
33
$(
4-
#[cfg(feature = "http2")]
5-
//#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
4+
#[cfg(any(
5+
feature = "http1",
6+
feature = "http2",
7+
))]
8+
#[cfg_attr(docsrs, doc(cfg(any(
9+
feature = "http1",
10+
feature = "http2",
11+
))))]
612
$item
713
)*
814
}
915
}
16+
17+
cfg_any_http! {
18+
macro_rules! cfg_http1 {
19+
($($item:item)*) => {
20+
$(
21+
#[cfg(feature = "http1")]
22+
#[cfg_attr(docsrs, doc(cfg(feature = "http1")))]
23+
$item
24+
)*
25+
}
26+
}
27+
28+
macro_rules! cfg_http2 {
29+
($($item:item)*) => {
30+
$(
31+
#[cfg(feature = "http2")]
32+
#[cfg_attr(docsrs, doc(cfg(feature = "http2")))]
33+
$item
34+
)*
35+
}
36+
}
37+
}

0 commit comments

Comments
 (0)