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:
@@ -9,10 +9,10 @@ use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::io::Buffered;
|
||||
use super::{Decoder, Encode, EncodedBuf, Encoder, Http1Transaction, ParseContext, Wants};
|
||||
use crate::body::DecodedLength;
|
||||
use crate::common::{task, Pin, Poll, Unpin};
|
||||
use crate::headers::connection_keep_alive;
|
||||
use crate::proto::{BodyLength, DecodedLength, MessageHead};
|
||||
use crate::Result;
|
||||
use crate::proto::{BodyLength, MessageHead};
|
||||
|
||||
const H2_PREFACE: &[u8] = b"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n";
|
||||
|
||||
@@ -589,7 +589,7 @@ where
|
||||
self.state.writing = state;
|
||||
}
|
||||
|
||||
pub fn end_body(&mut self) -> Result<()> {
|
||||
pub fn end_body(&mut self) -> crate::Result<()> {
|
||||
debug_assert!(self.can_write_body());
|
||||
|
||||
let mut res = Ok(());
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
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;
|
||||
|
||||
pub fn extend(dst: &mut Vec<u8>) {
|
||||
CACHED.with(|cache| {
|
||||
dst.extend_from_slice(cache.borrow().buffer());
|
||||
})
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,10 @@ use http::{Request, Response, StatusCode};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
|
||||
use super::{Http1Transaction, Wants};
|
||||
use crate::body::{Body, HttpBody};
|
||||
use crate::body::{Body, DecodedLength, HttpBody};
|
||||
use crate::common::{task, Future, Never, Pin, Poll, Unpin};
|
||||
use crate::proto::{
|
||||
BodyLength, Conn, DecodedLength, Dispatched, MessageHead, RequestHead, RequestLine,
|
||||
BodyLength, Conn, Dispatched, MessageHead, RequestHead, RequestLine,
|
||||
ResponseHead,
|
||||
};
|
||||
use crate::service::HttpService;
|
||||
|
||||
@@ -2,13 +2,14 @@ use std::cell::Cell;
|
||||
use std::cmp;
|
||||
use std::fmt;
|
||||
use std::io::{self, IoSlice};
|
||||
use std::marker::Unpin;
|
||||
|
||||
use bytes::{Buf, BufMut, Bytes, BytesMut};
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
|
||||
use super::{Http1Transaction, ParseContext, ParsedMessage};
|
||||
use crate::common::buf::BufList;
|
||||
use crate::common::{task, Pin, Poll, Unpin};
|
||||
use crate::common::{task, Pin, Poll};
|
||||
|
||||
/// The initial buffer size allocated before trying to read from IO.
|
||||
pub(crate) const INIT_BUFFER_SIZE: usize = 8192;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use bytes::BytesMut;
|
||||
use http::{HeaderMap, Method};
|
||||
|
||||
use crate::proto::{BodyLength, DecodedLength, MessageHead};
|
||||
use crate::body::DecodedLength;
|
||||
use crate::proto::{BodyLength, MessageHead};
|
||||
|
||||
pub(crate) use self::conn::Conn;
|
||||
pub use self::decode::Decoder;
|
||||
@@ -11,7 +12,6 @@ pub use self::io::Cursor; //TODO: move out of h1::io
|
||||
pub use self::io::MINIMUM_MAX_BUFFER_SIZE;
|
||||
|
||||
mod conn;
|
||||
pub(super) mod date;
|
||||
mod decode;
|
||||
pub(crate) mod dispatch;
|
||||
mod encode;
|
||||
|
||||
@@ -9,12 +9,14 @@ use bytes::BytesMut;
|
||||
use http::header::{self, Entry, HeaderName, HeaderValue};
|
||||
use http::{HeaderMap, Method, StatusCode, Version};
|
||||
|
||||
use crate::body::DecodedLength;
|
||||
use crate::common::date;
|
||||
use crate::error::Parse;
|
||||
use crate::headers;
|
||||
use crate::proto::h1::{
|
||||
date, Encode, Encoder, Http1Transaction, ParseContext, ParseResult, ParsedMessage,
|
||||
Encode, Encoder, Http1Transaction, ParseContext, ParseResult, ParsedMessage,
|
||||
};
|
||||
use crate::proto::{BodyLength, DecodedLength, MessageHead, RequestHead, RequestLine};
|
||||
use crate::proto::{BodyLength, MessageHead, RequestHead, RequestLine};
|
||||
|
||||
const MAX_HEADERS: usize = 100;
|
||||
const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific
|
||||
|
||||
@@ -9,8 +9,7 @@ use pin_project::pin_project;
|
||||
use std::error::Error as StdError;
|
||||
use std::io::IoSlice;
|
||||
|
||||
use super::DecodedLength;
|
||||
use crate::body::HttpBody;
|
||||
use crate::body::{DecodedLength, HttpBody};
|
||||
use crate::common::{task, Future, Pin, Poll};
|
||||
use crate::headers::content_length_parse_all;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use super::{decode_content_length, ping, PipeToSendStream, SendBuf};
|
||||
use crate::body::HttpBody;
|
||||
use crate::common::exec::ConnStreamExec;
|
||||
use crate::common::{task, Future, Pin, Poll};
|
||||
use crate::common::{date, task, Future, Pin, Poll};
|
||||
use crate::headers;
|
||||
use crate::proto::Dispatched;
|
||||
use crate::service::HttpService;
|
||||
@@ -400,7 +400,7 @@ where
|
||||
// set Date header if it isn't already set...
|
||||
res.headers_mut()
|
||||
.entry(::http::header::DATE)
|
||||
.or_insert_with(crate::proto::h1::date::update_and_header_value);
|
||||
.or_insert_with(date::update_and_header_value);
|
||||
|
||||
// automatically set Content-Length from body...
|
||||
if let Some(len) = body.size_hint().exact() {
|
||||
|
||||
123
src/proto/mod.rs
123
src/proto/mod.rs
@@ -1,10 +1,11 @@
|
||||
//! Pieces pertaining to the HTTP message protocol.
|
||||
use http::{HeaderMap, Method, StatusCode, Uri, Version};
|
||||
|
||||
pub(crate) use self::body_length::DecodedLength;
|
||||
pub(crate) use self::h1::{dispatch, Conn, ServerTransaction};
|
||||
cfg_http1! {
|
||||
pub(crate) mod h1;
|
||||
|
||||
pub(crate) use self::h1::{dispatch, Conn, ServerTransaction};
|
||||
}
|
||||
|
||||
pub(crate) mod h1;
|
||||
cfg_http2! {
|
||||
pub(crate) mod h2;
|
||||
}
|
||||
@@ -13,23 +14,27 @@ cfg_http2! {
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct MessageHead<S> {
|
||||
/// HTTP version of the message.
|
||||
pub version: Version,
|
||||
pub version: http::Version,
|
||||
/// Subject (request line or status line) of Incoming message.
|
||||
pub subject: S,
|
||||
/// Headers of the Incoming message.
|
||||
pub headers: HeaderMap,
|
||||
pub headers: http::HeaderMap,
|
||||
}
|
||||
|
||||
/// An incoming request message.
|
||||
#[cfg(feature = "http1")]
|
||||
pub type RequestHead = MessageHead<RequestLine>;
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct RequestLine(pub Method, pub Uri);
|
||||
#[cfg(feature = "http1")]
|
||||
pub struct RequestLine(pub http::Method, pub http::Uri);
|
||||
|
||||
/// An incoming response message.
|
||||
pub type ResponseHead = MessageHead<StatusCode>;
|
||||
#[cfg(feature = "http1")]
|
||||
pub type ResponseHead = MessageHead<http::StatusCode>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "http1")]
|
||||
pub enum BodyLength {
|
||||
/// Content-Length
|
||||
Known(u64),
|
||||
@@ -42,106 +47,6 @@ pub(crate) enum Dispatched {
|
||||
/// Dispatcher completely shutdown connection.
|
||||
Shutdown,
|
||||
/// Dispatcher has pending upgrade, and so did not shutdown.
|
||||
#[cfg(feature = "http1")]
|
||||
Upgrade(crate::upgrade::Pending),
|
||||
}
|
||||
|
||||
/// A separate module to encapsulate the invariants of the DecodedLength type.
|
||||
mod body_length {
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub(crate) struct DecodedLength(u64);
|
||||
|
||||
const MAX_LEN: u64 = std::u64::MAX - 2;
|
||||
|
||||
impl DecodedLength {
|
||||
pub(crate) const CLOSE_DELIMITED: DecodedLength = DecodedLength(::std::u64::MAX);
|
||||
pub(crate) const CHUNKED: DecodedLength = DecodedLength(::std::u64::MAX - 1);
|
||||
pub(crate) const ZERO: DecodedLength = DecodedLength(0);
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) fn new(len: u64) -> Self {
|
||||
debug_assert!(len <= MAX_LEN);
|
||||
DecodedLength(len)
|
||||
}
|
||||
|
||||
/// Takes the length as a content-length without other checks.
|
||||
///
|
||||
/// Should only be called if previously confirmed this isn't
|
||||
/// CLOSE_DELIMITED or CHUNKED.
|
||||
#[inline]
|
||||
pub(crate) fn danger_len(self) -> u64 {
|
||||
debug_assert!(self.0 < Self::CHUNKED.0);
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Converts to an Option<u64> representing a Known or Unknown length.
|
||||
pub(crate) fn into_opt(self) -> Option<u64> {
|
||||
match self {
|
||||
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => None,
|
||||
DecodedLength(known) => Some(known),
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks the `u64` is within the maximum allowed for content-length.
|
||||
pub(crate) fn checked_new(len: u64) -> Result<Self, crate::error::Parse> {
|
||||
if len <= MAX_LEN {
|
||||
Ok(DecodedLength(len))
|
||||
} else {
|
||||
warn!("content-length bigger than maximum: {} > {}", len, MAX_LEN);
|
||||
Err(crate::error::Parse::TooLarge)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sub_if(&mut self, amt: u64) {
|
||||
match *self {
|
||||
DecodedLength::CHUNKED | DecodedLength::CLOSE_DELIMITED => (),
|
||||
DecodedLength(ref mut known) => {
|
||||
*known -= amt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for DecodedLength {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
DecodedLength::CLOSE_DELIMITED => f.write_str("CLOSE_DELIMITED"),
|
||||
DecodedLength::CHUNKED => f.write_str("CHUNKED"),
|
||||
DecodedLength(n) => f.debug_tuple("DecodedLength").field(&n).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for DecodedLength {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
DecodedLength::CLOSE_DELIMITED => f.write_str("close-delimited"),
|
||||
DecodedLength::CHUNKED => f.write_str("chunked encoding"),
|
||||
DecodedLength::ZERO => f.write_str("empty"),
|
||||
DecodedLength(n) => write!(f, "content-length ({} bytes)", n),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn sub_if_known() {
|
||||
let mut len = DecodedLength::new(30);
|
||||
len.sub_if(20);
|
||||
|
||||
assert_eq!(len.0, 10);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sub_if_chunked() {
|
||||
let mut len = DecodedLength::CHUNKED;
|
||||
len.sub_if(20);
|
||||
|
||||
assert_eq!(len, DecodedLength::CHUNKED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user