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`.
This commit is contained in:
Sean McArthur
2020-11-16 15:39:10 -08:00
parent 2f2ceb2426
commit 2a19ab74ed
31 changed files with 459 additions and 239 deletions

View File

@@ -21,6 +21,7 @@ impl<T: Buf> BufList<T> {
}
#[inline]
#[cfg(feature = "http1")]
pub(crate) fn bufs_cnt(&self) -> usize {
self.bufs.len()
}

124
src/common/date.rs Normal file
View File

@@ -0,0 +1,124 @@
use std::cell::RefCell;
use std::fmt::{self, Write};
use std::str;
use std::time::{Duration, SystemTime};
#[cfg(feature = "http2")]
use http::header::HeaderValue;
use httpdate::HttpDate;
// "Sun, 06 Nov 1994 08:49:37 GMT".len()
pub const DATE_VALUE_LENGTH: usize = 29;
#[cfg(feature = "http1")]
pub fn extend(dst: &mut Vec<u8>) {
CACHED.with(|cache| {
dst.extend_from_slice(cache.borrow().buffer());
})
}
#[cfg(feature = "http1")]
pub fn update() {
CACHED.with(|cache| {
cache.borrow_mut().check();
})
}
#[cfg(feature = "http2")]
pub(crate) fn update_and_header_value() -> HeaderValue {
CACHED.with(|cache| {
let mut cache = cache.borrow_mut();
cache.check();
HeaderValue::from_bytes(cache.buffer()).expect("Date format should be valid HeaderValue")
})
}
struct CachedDate {
bytes: [u8; DATE_VALUE_LENGTH],
pos: usize,
next_update: SystemTime,
}
thread_local!(static CACHED: RefCell<CachedDate> = RefCell::new(CachedDate::new()));
impl CachedDate {
fn new() -> Self {
let mut cache = CachedDate {
bytes: [0; DATE_VALUE_LENGTH],
pos: 0,
next_update: SystemTime::now(),
};
cache.update(cache.next_update);
cache
}
fn buffer(&self) -> &[u8] {
&self.bytes[..]
}
fn check(&mut self) {
let now = SystemTime::now();
if now > self.next_update {
self.update(now);
}
}
fn update(&mut self, now: SystemTime) {
self.render(now);
self.next_update = now + Duration::new(1, 0);
}
fn render(&mut self, now: SystemTime) {
self.pos = 0;
let _ = write!(self, "{}", HttpDate::from(now));
debug_assert!(self.pos == DATE_VALUE_LENGTH);
}
}
impl fmt::Write for CachedDate {
fn write_str(&mut self, s: &str) -> fmt::Result {
let len = s.len();
self.bytes[self.pos..self.pos + len].copy_from_slice(s.as_bytes());
self.pos += len;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "nightly")]
use test::Bencher;
#[test]
fn test_date_len() {
assert_eq!(DATE_VALUE_LENGTH, "Sun, 06 Nov 1994 08:49:37 GMT".len());
}
#[cfg(feature = "nightly")]
#[bench]
fn bench_date_check(b: &mut Bencher) {
let mut date = CachedDate::new();
// cache the first update
date.check();
b.iter(|| {
date.check();
});
}
#[cfg(feature = "nightly")]
#[bench]
fn bench_date_render(b: &mut Bencher) {
let mut date = CachedDate::new();
let now = SystemTime::now();
date.render(now);
b.bytes = date.buffer().len() as u64;
b.iter(|| {
date.render(now);
test::black_box(&date);
});
}
}

View File

@@ -6,15 +6,10 @@ use std::sync::Arc;
use crate::body::{Body, HttpBody};
#[cfg(feature = "http2")]
use crate::proto::h2::server::H2Stream;
use crate::rt::Executor;
use crate::server::conn::spawn_all::{NewSvcTask, Watcher};
use crate::service::HttpService;
/// An executor of futures.
pub trait Executor<Fut> {
/// Place the future into the executor to be run.
fn execute(&self, fut: Fut);
}
pub trait ConnStreamExec<F, B: HttpBody>: Clone {
fn execute_h2stream(&mut self, fut: H2Stream<F, B>);
}

View File

@@ -29,7 +29,7 @@ impl<T> Rewind<T> {
}
}
#[cfg(any(feature = "http2", test))]
#[cfg(any(all(feature = "http1", feature = "http2"), test))]
pub(crate) fn rewind(&mut self, bs: Bytes) {
debug_assert!(self.pre.is_none());
self.pre = Some(bs);

View File

@@ -8,9 +8,14 @@ macro_rules! ready {
}
pub(crate) mod buf;
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) mod date;
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) mod drain;
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) mod exec;
pub(crate) mod io;
#[cfg(any(feature = "http1", feature = "http2"))]
mod lazy;
mod never;
#[cfg(feature = "stream")]
@@ -18,11 +23,14 @@ pub(crate) mod sync_wrapper;
pub(crate) mod task;
pub(crate) mod watch;
pub use self::exec::Executor;
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) use self::exec::{BoxSendFuture, Exec};
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) use self::lazy::{lazy, Started as Lazy};
pub use self::never::Never;
pub(crate) use self::task::Poll;
// group up types normally needed for `Future`
pub(crate) use std::{future::Future, marker::Unpin, pin::Pin};
#[cfg(any(feature = "http1", feature = "http2"))]
pub(crate) use std::marker::Unpin;
pub(crate) use std::{future::Future, pin::Pin};

View File

@@ -1,9 +1,11 @@
#[cfg(feature = "http1")]
use super::Never;
pub(crate) use std::task::{Context, Poll};
/// A function to help "yield" a future, such that it is re-scheduled immediately.
///
/// Useful for spin counts, so a future doesn't hog too much time.
#[cfg(feature = "http1")]
pub(crate) fn yield_now(cx: &mut Context<'_>) -> Poll<Never> {
cx.waker().wake_by_ref();
Poll::Pending