mirror of
https://github.com/Noratrieb/icefun.git
synced 2026-01-15 21:25:02 +01:00
loop
This commit is contained in:
parent
7af1274587
commit
189f24e53b
58 changed files with 1489 additions and 12529 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1,18 +1,13 @@
|
|||
use std::error::Error as StdError;
|
||||
|
||||
use bytes::{Buf, Bytes};
|
||||
use http::Request;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
use super::{Http1Transaction, Wants};
|
||||
use crate::body::{Body, DecodedLength, HttpBody};
|
||||
use crate::common::{task, Future, Pin, Poll, Unpin};
|
||||
use crate::proto::{
|
||||
BodyLength, Conn, Dispatched, MessageHead, RequestHead,
|
||||
};
|
||||
use crate::proto::{BodyLength, Conn, Dispatched, MessageHead, RequestHead};
|
||||
use crate::upgrade::OnUpgrade;
|
||||
|
||||
pub(crate) struct Dispatcher<D, Bs: HttpBody, I, T> {
|
||||
conn: Conn<I, Bs::Data, T>,
|
||||
dispatch: D,
|
||||
|
|
@ -20,7 +15,6 @@ pub(crate) struct Dispatcher<D, Bs: HttpBody, I, T> {
|
|||
body_rx: Pin<Box<Option<Bs>>>,
|
||||
is_closing: bool,
|
||||
}
|
||||
|
||||
pub(crate) trait Dispatch {
|
||||
type PollItem;
|
||||
type PollBody;
|
||||
|
|
@ -30,40 +24,30 @@ pub(crate) trait Dispatch {
|
|||
self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>>;
|
||||
fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, Body)>) -> crate::Result<()>;
|
||||
fn recv_msg(
|
||||
&mut self,
|
||||
msg: crate::Result<(Self::RecvItem, Body)>,
|
||||
) -> crate::Result<()>;
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), ()>>;
|
||||
fn should_poll(&self) -> bool;
|
||||
}
|
||||
|
||||
cfg_server! {
|
||||
use crate::service::HttpService;
|
||||
|
||||
pub(crate) struct Server<S: HttpService<B>, B> {
|
||||
in_flight: Pin<Box<Option<S::Future>>>,
|
||||
pub(crate) service: S,
|
||||
}
|
||||
use crate ::service::HttpService; pub (crate) struct Server < S : HttpService < B >,
|
||||
B > { in_flight : Pin < Box < Option < S::Future >>>, pub (crate) service : S, }
|
||||
}
|
||||
|
||||
cfg_client! {
|
||||
pin_project_lite::pin_project! {
|
||||
pub(crate) struct Client<B> {
|
||||
callback: Option<crate::client::dispatch::Callback<Request<B>, http::Response<Body>>>,
|
||||
#[pin]
|
||||
rx: ClientRx<B>,
|
||||
rx_closed: bool,
|
||||
}
|
||||
}
|
||||
|
||||
type ClientRx<B> = crate::client::dispatch::Receiver<Request<B>, http::Response<Body>>;
|
||||
pin_project_lite::pin_project! { pub (crate) struct Client < B > { callback : Option
|
||||
< crate ::client::dispatch::Callback < Request < B >, http::Response < Body >>>,
|
||||
#[pin] rx : ClientRx < B >, rx_closed : bool, } } type ClientRx < B > = crate
|
||||
::client::dispatch::Receiver < Request < B >, http::Response < Body >>;
|
||||
}
|
||||
|
||||
impl<D, Bs, I, T> Dispatcher<D, Bs, I, T>
|
||||
where
|
||||
D: Dispatch<
|
||||
PollItem = MessageHead<T::Outgoing>,
|
||||
PollBody = Bs,
|
||||
RecvItem = MessageHead<T::Incoming>,
|
||||
> + Unpin,
|
||||
PollItem = MessageHead<T::Outgoing>,
|
||||
PollBody = Bs,
|
||||
RecvItem = MessageHead<T::Incoming>,
|
||||
> + Unpin,
|
||||
D::PollError: Into<Box<dyn StdError + Send + Sync>>,
|
||||
I: AsyncRead + AsyncWrite + Unpin,
|
||||
T: Http1Transaction + Unpin,
|
||||
|
|
@ -71,28 +55,15 @@ where
|
|||
Bs::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
pub(crate) fn new(dispatch: D, conn: Conn<I, Bs::Data, T>) -> Self {
|
||||
Dispatcher {
|
||||
conn,
|
||||
dispatch,
|
||||
body_tx: None,
|
||||
body_rx: Box::pin(None),
|
||||
is_closing: false,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) fn disable_keep_alive(&mut self) {
|
||||
self.conn.disable_keep_alive();
|
||||
if self.conn.is_write_closed() {
|
||||
self.close();
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn into_inner(self) -> (I, Bytes, D) {
|
||||
let (io, buf) = self.conn.into_inner();
|
||||
(io, buf, self.dispatch)
|
||||
loop {}
|
||||
}
|
||||
|
||||
/// Run this dispatcher until HTTP says this connection is done,
|
||||
/// but don't call `AsyncWrite::shutdown` on the underlying IO.
|
||||
///
|
||||
|
|
@ -105,312 +76,51 @@ where
|
|||
where
|
||||
Self: Unpin,
|
||||
{
|
||||
Pin::new(self).poll_catch(cx, false).map_ok(|ds| {
|
||||
if let Dispatched::Upgrade(pending) = ds {
|
||||
pending.manual();
|
||||
}
|
||||
})
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_catch(
|
||||
&mut self,
|
||||
cx: &mut task::Context<'_>,
|
||||
should_shutdown: bool,
|
||||
) -> Poll<crate::Result<Dispatched>> {
|
||||
Poll::Ready(ready!(self.poll_inner(cx, should_shutdown)).or_else(|e| {
|
||||
// An error means we're shutting down either way.
|
||||
// We just try to give the error to the user,
|
||||
// and close the connection with an Ok. If we
|
||||
// cannot give it to the user, then return the Err.
|
||||
self.dispatch.recv_msg(Err(e))?;
|
||||
Ok(Dispatched::Shutdown)
|
||||
}))
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_inner(
|
||||
&mut self,
|
||||
cx: &mut task::Context<'_>,
|
||||
should_shutdown: bool,
|
||||
) -> Poll<crate::Result<Dispatched>> {
|
||||
T::update_date();
|
||||
|
||||
ready!(self.poll_loop(cx))?;
|
||||
|
||||
if self.is_done() {
|
||||
if let Some(pending) = self.conn.pending_upgrade() {
|
||||
self.conn.take_error()?;
|
||||
return Poll::Ready(Ok(Dispatched::Upgrade(pending)));
|
||||
} else if should_shutdown {
|
||||
ready!(self.conn.poll_shutdown(cx)).map_err(crate::Error::new_shutdown)?;
|
||||
}
|
||||
self.conn.take_error()?;
|
||||
Poll::Ready(Ok(Dispatched::Shutdown))
|
||||
} else {
|
||||
Poll::Pending
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_loop(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
||||
// Limit the looping on this connection, in case it is ready far too
|
||||
// often, so that other futures don't starve.
|
||||
//
|
||||
// 16 was chosen arbitrarily, as that is number of pipelined requests
|
||||
// benchmarks often use. Perhaps it should be a config option instead.
|
||||
for _ in 0..16 {
|
||||
let _ = self.poll_read(cx)?;
|
||||
let _ = self.poll_write(cx)?;
|
||||
let _ = self.poll_flush(cx)?;
|
||||
|
||||
// This could happen if reading paused before blocking on IO,
|
||||
// such as getting to the end of a framed message, but then
|
||||
// writing/flushing set the state back to Init. In that case,
|
||||
// if the read buffer still had bytes, we'd want to try poll_read
|
||||
// again, or else we wouldn't ever be woken up again.
|
||||
//
|
||||
// Using this instead of task::current() and notify() inside
|
||||
// the Conn is noticeably faster in pipelined benchmarks.
|
||||
if !self.conn.wants_read_again() {
|
||||
//break;
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
trace!("poll_loop yielding (self = {:p})", self);
|
||||
|
||||
task::yield_now(cx).map(|never| match never {})
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_read(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
||||
loop {
|
||||
if self.is_closing {
|
||||
return Poll::Ready(Ok(()));
|
||||
} else if self.conn.can_read_head() {
|
||||
ready!(self.poll_read_head(cx))?;
|
||||
} else if let Some(mut body) = self.body_tx.take() {
|
||||
if self.conn.can_read_body() {
|
||||
match body.poll_ready(cx) {
|
||||
Poll::Ready(Ok(())) => (),
|
||||
Poll::Pending => {
|
||||
self.body_tx = Some(body);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Poll::Ready(Err(_canceled)) => {
|
||||
// user doesn't care about the body
|
||||
// so we should stop reading
|
||||
trace!("body receiver dropped before eof, draining or closing");
|
||||
self.conn.poll_drain_or_close_read(cx);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
match self.conn.poll_read_body(cx) {
|
||||
Poll::Ready(Some(Ok(chunk))) => match body.try_send_data(chunk) {
|
||||
Ok(()) => {
|
||||
self.body_tx = Some(body);
|
||||
}
|
||||
Err(_canceled) => {
|
||||
if self.conn.can_read_body() {
|
||||
trace!("body receiver dropped before eof, closing");
|
||||
self.conn.close_read();
|
||||
}
|
||||
}
|
||||
},
|
||||
Poll::Ready(None) => {
|
||||
// just drop, the body will close automatically
|
||||
}
|
||||
Poll::Pending => {
|
||||
self.body_tx = Some(body);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Poll::Ready(Some(Err(e))) => {
|
||||
body.send_error(crate::Error::new_body(e));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// just drop, the body will close automatically
|
||||
}
|
||||
} else {
|
||||
return self.conn.poll_read_keep_alive(cx);
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_read_head(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
||||
// can dispatch receive, or does it still care about, an incoming message?
|
||||
match ready!(self.dispatch.poll_ready(cx)) {
|
||||
Ok(()) => (),
|
||||
Err(()) => {
|
||||
trace!("dispatch no longer receiving messages");
|
||||
self.close();
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
// dispatch is ready for a message, try to read one
|
||||
match ready!(self.conn.poll_read_head(cx)) {
|
||||
Some(Ok((mut head, body_len, wants))) => {
|
||||
let body = match body_len {
|
||||
DecodedLength::ZERO => Body::empty(),
|
||||
other => {
|
||||
let (tx, rx) = Body::new_channel(other, wants.contains(Wants::EXPECT));
|
||||
self.body_tx = Some(tx);
|
||||
rx
|
||||
}
|
||||
};
|
||||
if wants.contains(Wants::UPGRADE) {
|
||||
let upgrade = self.conn.on_upgrade();
|
||||
debug_assert!(!upgrade.is_none(), "empty upgrade");
|
||||
debug_assert!(head.extensions.get::<OnUpgrade>().is_none(), "OnUpgrade already set");
|
||||
head.extensions.insert(upgrade);
|
||||
}
|
||||
self.dispatch.recv_msg(Ok((head, body)))?;
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
Some(Err(err)) => {
|
||||
debug!("read_head error: {}", err);
|
||||
self.dispatch.recv_msg(Err(err))?;
|
||||
// if here, the dispatcher gave the user the error
|
||||
// somewhere else. we still need to shutdown, but
|
||||
// not as a second error.
|
||||
self.close();
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
None => {
|
||||
// read eof, the write side will have been closed too unless
|
||||
// allow_read_close was set to true, in which case just do
|
||||
// nothing...
|
||||
debug_assert!(self.conn.is_read_closed());
|
||||
if self.conn.is_write_closed() {
|
||||
self.close();
|
||||
}
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_write(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
||||
loop {
|
||||
if self.is_closing {
|
||||
return Poll::Ready(Ok(()));
|
||||
} else if self.body_rx.is_none()
|
||||
&& self.conn.can_write_head()
|
||||
&& self.dispatch.should_poll()
|
||||
{
|
||||
if let Some(msg) = ready!(Pin::new(&mut self.dispatch).poll_msg(cx)) {
|
||||
let (head, mut body) = msg.map_err(crate::Error::new_user_service)?;
|
||||
|
||||
// Check if the body knows its full data immediately.
|
||||
//
|
||||
// If so, we can skip a bit of bookkeeping that streaming
|
||||
// bodies need to do.
|
||||
if let Some(full) = crate::body::take_full_data(&mut body) {
|
||||
self.conn.write_full_msg(head, full);
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
|
||||
let body_type = if body.is_end_stream() {
|
||||
self.body_rx.set(None);
|
||||
None
|
||||
} else {
|
||||
let btype = body
|
||||
.size_hint()
|
||||
.exact()
|
||||
.map(BodyLength::Known)
|
||||
.or_else(|| Some(BodyLength::Unknown));
|
||||
self.body_rx.set(Some(body));
|
||||
btype
|
||||
};
|
||||
self.conn.write_head(head, body_type);
|
||||
} else {
|
||||
self.close();
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
} else if !self.conn.can_buffer_body() {
|
||||
ready!(self.poll_flush(cx))?;
|
||||
} else {
|
||||
// A new scope is needed :(
|
||||
if let (Some(mut body), clear_body) =
|
||||
OptGuard::new(self.body_rx.as_mut()).guard_mut()
|
||||
{
|
||||
debug_assert!(!*clear_body, "opt guard defaults to keeping body");
|
||||
if !self.conn.can_write_body() {
|
||||
trace!(
|
||||
"no more write body allowed, user body is_end_stream = {}",
|
||||
body.is_end_stream(),
|
||||
);
|
||||
*clear_body = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
let item = ready!(body.as_mut().poll_data(cx));
|
||||
if let Some(item) = item {
|
||||
let chunk = item.map_err(|e| {
|
||||
*clear_body = true;
|
||||
crate::Error::new_user_body(e)
|
||||
})?;
|
||||
let eos = body.is_end_stream();
|
||||
if eos {
|
||||
*clear_body = true;
|
||||
if chunk.remaining() == 0 {
|
||||
trace!("discarding empty chunk");
|
||||
self.conn.end_body()?;
|
||||
} else {
|
||||
self.conn.write_body_and_end(chunk);
|
||||
}
|
||||
} else {
|
||||
if chunk.remaining() == 0 {
|
||||
trace!("discarding empty chunk");
|
||||
continue;
|
||||
}
|
||||
self.conn.write_body(chunk);
|
||||
}
|
||||
} else {
|
||||
*clear_body = true;
|
||||
self.conn.end_body()?;
|
||||
}
|
||||
} else {
|
||||
return Poll::Pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_flush(&mut self, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
||||
self.conn.poll_flush(cx).map_err(|err| {
|
||||
debug!("error writing: {}", err);
|
||||
crate::Error::new_body_write(err)
|
||||
})
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn close(&mut self) {
|
||||
self.is_closing = true;
|
||||
self.conn.close_read();
|
||||
self.conn.close_write();
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn is_done(&self) -> bool {
|
||||
if self.is_closing {
|
||||
return true;
|
||||
}
|
||||
|
||||
let read_done = self.conn.is_read_closed();
|
||||
|
||||
if !T::should_read_first() && read_done {
|
||||
// a client that cannot read may was well be done.
|
||||
true
|
||||
} else {
|
||||
let write_done = self.conn.is_write_closed()
|
||||
|| (!self.dispatch.should_poll() && self.body_rx.is_none());
|
||||
read_done && write_done
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D, Bs, I, T> Future for Dispatcher<D, Bs, I, T>
|
||||
where
|
||||
D: Dispatch<
|
||||
PollItem = MessageHead<T::Outgoing>,
|
||||
PollBody = Bs,
|
||||
RecvItem = MessageHead<T::Incoming>,
|
||||
> + Unpin,
|
||||
PollItem = MessageHead<T::Outgoing>,
|
||||
PollBody = Bs,
|
||||
RecvItem = MessageHead<T::Incoming>,
|
||||
> + Unpin,
|
||||
D::PollError: Into<Box<dyn StdError + Send + Sync>>,
|
||||
I: AsyncRead + AsyncWrite + Unpin,
|
||||
T: Http1Transaction + Unpin,
|
||||
|
|
@ -418,333 +128,98 @@ where
|
|||
Bs::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
type Output = crate::Result<Dispatched>;
|
||||
|
||||
#[inline]
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
self.poll_catch(cx, true)
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl OptGuard =====
|
||||
|
||||
/// A drop guard to allow a mutable borrow of an Option while being able to
|
||||
/// set whether the `Option` should be cleared on drop.
|
||||
struct OptGuard<'a, T>(Pin<&'a mut Option<T>>, bool);
|
||||
|
||||
impl<'a, T> OptGuard<'a, T> {
|
||||
fn new(pin: Pin<&'a mut Option<T>>) -> Self {
|
||||
OptGuard(pin, false)
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn guard_mut(&mut self) -> (Option<Pin<&mut T>>, &mut bool) {
|
||||
(self.0.as_mut().as_pin_mut(), &mut self.1)
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> Drop for OptGuard<'a, T> {
|
||||
fn drop(&mut self) {
|
||||
if self.1 {
|
||||
self.0.set(None);
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Server =====
|
||||
|
||||
cfg_server! {
|
||||
impl<S, B> Server<S, B>
|
||||
where
|
||||
S: HttpService<B>,
|
||||
{
|
||||
pub(crate) fn new(service: S) -> Server<S, B> {
|
||||
Server {
|
||||
in_flight: Box::pin(None),
|
||||
service,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn into_service(self) -> S {
|
||||
self.service
|
||||
}
|
||||
}
|
||||
|
||||
// Service is never pinned
|
||||
impl<S: HttpService<B>, B> Unpin for Server<S, B> {}
|
||||
|
||||
impl<S, Bs> Dispatch for Server<S, Body>
|
||||
where
|
||||
S: HttpService<Body, ResBody = Bs>,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
Bs: HttpBody,
|
||||
{
|
||||
type PollItem = MessageHead<http::StatusCode>;
|
||||
type PollBody = Bs;
|
||||
type PollError = S::Error;
|
||||
type RecvItem = RequestHead;
|
||||
|
||||
fn poll_msg(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), Self::PollError>>> {
|
||||
let mut this = self.as_mut();
|
||||
let ret = if let Some(ref mut fut) = this.in_flight.as_mut().as_pin_mut() {
|
||||
let resp = ready!(fut.as_mut().poll(cx)?);
|
||||
let (parts, body) = resp.into_parts();
|
||||
let head = MessageHead {
|
||||
version: parts.version,
|
||||
subject: parts.status,
|
||||
headers: parts.headers,
|
||||
extensions: parts.extensions,
|
||||
};
|
||||
Poll::Ready(Some(Ok((head, body))))
|
||||
} else {
|
||||
unreachable!("poll_msg shouldn't be called if no inflight");
|
||||
};
|
||||
|
||||
// Since in_flight finished, remove it
|
||||
this.in_flight.set(None);
|
||||
ret
|
||||
}
|
||||
|
||||
fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, Body)>) -> crate::Result<()> {
|
||||
let (msg, body) = msg?;
|
||||
let mut req = Request::new(body);
|
||||
*req.method_mut() = msg.subject.0;
|
||||
*req.uri_mut() = msg.subject.1;
|
||||
*req.headers_mut() = msg.headers;
|
||||
*req.version_mut() = msg.version;
|
||||
*req.extensions_mut() = msg.extensions;
|
||||
let fut = self.service.call(req);
|
||||
self.in_flight.set(Some(fut));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), ()>> {
|
||||
if self.in_flight.is_some() {
|
||||
Poll::Pending
|
||||
} else {
|
||||
self.service.poll_ready(cx).map_err(|_e| {
|
||||
// FIXME: return error value.
|
||||
trace!("service closed");
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn should_poll(&self) -> bool {
|
||||
self.in_flight.is_some()
|
||||
}
|
||||
}
|
||||
impl < S, B > Server < S, B > where S : HttpService < B >, { pub (crate) fn
|
||||
new(service : S) -> Server < S, B > { Server { in_flight : Box::pin(None), service, }
|
||||
} pub (crate) fn into_service(self) -> S { self.service } } impl < S : HttpService <
|
||||
B >, B > Unpin for Server < S, B > {} impl < S, Bs > Dispatch for Server < S, Body >
|
||||
where S : HttpService < Body, ResBody = Bs >, S::Error : Into < Box < dyn StdError +
|
||||
Send + Sync >>, Bs : HttpBody, { type PollItem = MessageHead < http::StatusCode >;
|
||||
type PollBody = Bs; type PollError = S::Error; type RecvItem = RequestHead; fn
|
||||
poll_msg(mut self : Pin <& mut Self >, cx : & mut task::Context <'_ >,) -> Poll <
|
||||
Option < Result < (Self::PollItem, Self::PollBody), Self::PollError >>> { let mut
|
||||
this = self.as_mut(); let ret = if let Some(ref mut fut) = this.in_flight.as_mut()
|
||||
.as_pin_mut() { let resp = ready!(fut.as_mut().poll(cx) ?); let (parts, body) = resp
|
||||
.into_parts(); let head = MessageHead { version : parts.version, subject : parts
|
||||
.status, headers : parts.headers, extensions : parts.extensions, };
|
||||
Poll::Ready(Some(Ok((head, body)))) } else {
|
||||
unreachable!("poll_msg shouldn't be called if no inflight"); }; this.in_flight
|
||||
.set(None); ret } fn recv_msg(& mut self, msg : crate ::Result < (Self::RecvItem,
|
||||
Body) >) -> crate ::Result < () > { let (msg, body) = msg ?; let mut req =
|
||||
Request::new(body); * req.method_mut() = msg.subject.0; * req.uri_mut() = msg.subject
|
||||
.1; * req.headers_mut() = msg.headers; * req.version_mut() = msg.version; * req
|
||||
.extensions_mut() = msg.extensions; let fut = self.service.call(req); self.in_flight
|
||||
.set(Some(fut)); Ok(()) } fn poll_ready(& mut self, cx : & mut task::Context <'_ >)
|
||||
-> Poll < Result < (), () >> { if self.in_flight.is_some() { Poll::Pending } else {
|
||||
self.service.poll_ready(cx).map_err(| _e | { trace!("service closed"); }) } } fn
|
||||
should_poll(& self) -> bool { self.in_flight.is_some() } }
|
||||
}
|
||||
|
||||
// ===== impl Client =====
|
||||
|
||||
cfg_client! {
|
||||
impl<B> Client<B> {
|
||||
pub(crate) fn new(rx: ClientRx<B>) -> Client<B> {
|
||||
Client {
|
||||
callback: None,
|
||||
rx,
|
||||
rx_closed: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Dispatch for Client<B>
|
||||
where
|
||||
B: HttpBody,
|
||||
{
|
||||
type PollItem = RequestHead;
|
||||
type PollBody = B;
|
||||
type PollError = crate::common::Never;
|
||||
type RecvItem = crate::proto::ResponseHead;
|
||||
|
||||
fn poll_msg(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Poll<Option<Result<(Self::PollItem, Self::PollBody), crate::common::Never>>> {
|
||||
let mut this = self.as_mut();
|
||||
debug_assert!(!this.rx_closed);
|
||||
match this.rx.poll_recv(cx) {
|
||||
Poll::Ready(Some((req, mut cb))) => {
|
||||
// check that future hasn't been canceled already
|
||||
match cb.poll_canceled(cx) {
|
||||
Poll::Ready(()) => {
|
||||
trace!("request canceled");
|
||||
Poll::Ready(None)
|
||||
}
|
||||
Poll::Pending => {
|
||||
let (parts, body) = req.into_parts();
|
||||
let head = RequestHead {
|
||||
version: parts.version,
|
||||
subject: crate::proto::RequestLine(parts.method, parts.uri),
|
||||
headers: parts.headers,
|
||||
extensions: parts.extensions,
|
||||
};
|
||||
this.callback = Some(cb);
|
||||
Poll::Ready(Some(Ok((head, body))))
|
||||
}
|
||||
}
|
||||
}
|
||||
Poll::Ready(None) => {
|
||||
// user has dropped sender handle
|
||||
trace!("client tx closed");
|
||||
this.rx_closed = true;
|
||||
Poll::Ready(None)
|
||||
}
|
||||
Poll::Pending => Poll::Pending,
|
||||
}
|
||||
}
|
||||
|
||||
fn recv_msg(&mut self, msg: crate::Result<(Self::RecvItem, Body)>) -> crate::Result<()> {
|
||||
match msg {
|
||||
Ok((msg, body)) => {
|
||||
if let Some(cb) = self.callback.take() {
|
||||
let res = msg.into_response(body);
|
||||
cb.send(Ok(res));
|
||||
Ok(())
|
||||
} else {
|
||||
// Getting here is likely a bug! An error should have happened
|
||||
// in Conn::require_empty_read() before ever parsing a
|
||||
// full message!
|
||||
Err(crate::Error::new_unexpected_message())
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if let Some(cb) = self.callback.take() {
|
||||
cb.send(Err((err, None)));
|
||||
Ok(())
|
||||
} else if !self.rx_closed {
|
||||
self.rx.close();
|
||||
if let Some((req, cb)) = self.rx.try_recv() {
|
||||
trace!("canceling queued request with connection error: {}", err);
|
||||
// in this case, the message was never even started, so it's safe to tell
|
||||
// the user that the request was completely canceled
|
||||
cb.send(Err((crate::Error::new_canceled().with(err), Some(req))));
|
||||
Ok(())
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll<Result<(), ()>> {
|
||||
match self.callback {
|
||||
Some(ref mut cb) => match cb.poll_canceled(cx) {
|
||||
Poll::Ready(()) => {
|
||||
trace!("callback receiver has dropped");
|
||||
Poll::Ready(Err(()))
|
||||
}
|
||||
Poll::Pending => Poll::Ready(Ok(())),
|
||||
},
|
||||
None => Poll::Ready(Err(())),
|
||||
}
|
||||
}
|
||||
|
||||
fn should_poll(&self) -> bool {
|
||||
self.callback.is_none()
|
||||
}
|
||||
impl < B > Client < B > { pub (crate) fn new(rx : ClientRx < B >) -> Client < B > {
|
||||
Client { callback : None, rx, rx_closed : false, } } } impl < B > Dispatch for Client
|
||||
< B > where B : HttpBody, { type PollItem = RequestHead; type PollBody = B; type
|
||||
PollError = crate ::common::Never; type RecvItem = crate ::proto::ResponseHead; fn
|
||||
poll_msg(mut self : Pin <& mut Self >, cx : & mut task::Context <'_ >,) -> Poll <
|
||||
Option < Result < (Self::PollItem, Self::PollBody), crate ::common::Never >>> { let
|
||||
mut this = self.as_mut(); debug_assert!(! this.rx_closed); match this.rx
|
||||
.poll_recv(cx) { Poll::Ready(Some((req, mut cb))) => { match cb.poll_canceled(cx) {
|
||||
Poll::Ready(()) => { trace!("request canceled"); Poll::Ready(None) } Poll::Pending =>
|
||||
{ let (parts, body) = req.into_parts(); let head = RequestHead { version : parts
|
||||
.version, subject : crate ::proto::RequestLine(parts.method, parts.uri), headers :
|
||||
parts.headers, extensions : parts.extensions, }; this.callback = Some(cb);
|
||||
Poll::Ready(Some(Ok((head, body)))) } } } Poll::Ready(None) => {
|
||||
trace!("client tx closed"); this.rx_closed = true; Poll::Ready(None) } Poll::Pending
|
||||
=> Poll::Pending, } } fn recv_msg(& mut self, msg : crate ::Result < (Self::RecvItem,
|
||||
Body) >) -> crate ::Result < () > { match msg { Ok((msg, body)) => { if let Some(cb)
|
||||
= self.callback.take() { let res = msg.into_response(body); cb.send(Ok(res)); Ok(())
|
||||
} else { Err(crate ::Error::new_unexpected_message()) } } Err(err) => { if let
|
||||
Some(cb) = self.callback.take() { cb.send(Err((err, None))); Ok(()) } else if ! self
|
||||
.rx_closed { self.rx.close(); if let Some((req, cb)) = self.rx.try_recv() {
|
||||
trace!("canceling queued request with connection error: {}", err); cb.send(Err((crate
|
||||
::Error::new_canceled().with(err), Some(req)))); Ok(()) } else { Err(err) } } else {
|
||||
Err(err) } } } } fn poll_ready(& mut self, cx : & mut task::Context <'_ >) -> Poll <
|
||||
Result < (), () >> { match self.callback { Some(ref mut cb) => match cb
|
||||
.poll_canceled(cx) { Poll::Ready(()) => { trace!("callback receiver has dropped");
|
||||
Poll::Ready(Err(())) } Poll::Pending => Poll::Ready(Ok(())), }, None =>
|
||||
Poll::Ready(Err(())), } } fn should_poll(& self) -> bool { self.callback.is_none() }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::proto::h1::ClientTransaction;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn client_read_bytes_before_writing_request() {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
tokio_test::task::spawn(()).enter(|cx, _| {
|
||||
let (io, mut handle) = tokio_test::io::Builder::new().build_with_handle();
|
||||
|
||||
// Block at 0 for now, but we will release this response before
|
||||
// the request is ready to write later...
|
||||
let (mut tx, rx) = crate::client::dispatch::channel();
|
||||
let conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(io);
|
||||
let mut dispatcher = Dispatcher::new(Client::new(rx), conn);
|
||||
|
||||
// First poll is needed to allow tx to send...
|
||||
assert!(Pin::new(&mut dispatcher).poll(cx).is_pending());
|
||||
|
||||
// Unblock our IO, which has a response before we've sent request!
|
||||
//
|
||||
handle.read(b"HTTP/1.1 200 OK\r\n\r\n");
|
||||
|
||||
let mut res_rx = tx
|
||||
.try_send(crate::Request::new(crate::Body::empty()))
|
||||
.unwrap();
|
||||
|
||||
tokio_test::assert_ready_ok!(Pin::new(&mut dispatcher).poll(cx));
|
||||
let err = tokio_test::assert_ready_ok!(Pin::new(&mut res_rx).poll(cx))
|
||||
.expect_err("callback should send error");
|
||||
|
||||
match (err.0.kind(), err.1) {
|
||||
(&crate::error::Kind::Canceled, Some(_)) => (),
|
||||
other => panic!("expected Canceled, got {:?}", other),
|
||||
}
|
||||
});
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn client_flushing_is_not_ready_for_next_request() {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
let (io, _handle) = tokio_test::io::Builder::new()
|
||||
.write(b"POST / HTTP/1.1\r\ncontent-length: 4\r\n\r\n")
|
||||
.read(b"HTTP/1.1 200 OK\r\ncontent-length: 0\r\n\r\n")
|
||||
.wait(std::time::Duration::from_secs(2))
|
||||
.build_with_handle();
|
||||
|
||||
let (mut tx, rx) = crate::client::dispatch::channel();
|
||||
let mut conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(io);
|
||||
conn.set_write_strategy_queue();
|
||||
|
||||
let dispatcher = Dispatcher::new(Client::new(rx), conn);
|
||||
let _dispatcher = tokio::spawn(async move { dispatcher.await });
|
||||
|
||||
let req = crate::Request::builder()
|
||||
.method("POST")
|
||||
.body(crate::Body::from("reee"))
|
||||
.unwrap();
|
||||
|
||||
let res = tx.try_send(req).unwrap().await.expect("response");
|
||||
drop(res);
|
||||
|
||||
assert!(!tx.is_ready());
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn body_empty_chunks_ignored() {
|
||||
let _ = pretty_env_logger::try_init();
|
||||
|
||||
let io = tokio_test::io::Builder::new()
|
||||
// no reading or writing, just be blocked for the test...
|
||||
.wait(Duration::from_secs(5))
|
||||
.build();
|
||||
|
||||
let (mut tx, rx) = crate::client::dispatch::channel();
|
||||
let conn = Conn::<_, bytes::Bytes, ClientTransaction>::new(io);
|
||||
let mut dispatcher = tokio_test::task::spawn(Dispatcher::new(Client::new(rx), conn));
|
||||
|
||||
// First poll is needed to allow tx to send...
|
||||
assert!(dispatcher.poll().is_pending());
|
||||
|
||||
let body = {
|
||||
let (mut tx, body) = crate::Body::channel();
|
||||
tx.try_send_data("".into()).unwrap();
|
||||
body
|
||||
};
|
||||
|
||||
let _res_rx = tx.try_send(crate::Request::new(body)).unwrap();
|
||||
|
||||
// Ensure conn.write_body wasn't called with the empty chunk.
|
||||
// If it is, it will trigger an assertion.
|
||||
assert!(dispatcher.poll().is_pending());
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,22 @@
|
|||
use std::fmt;
|
||||
use std::io::IoSlice;
|
||||
|
||||
use bytes::buf::{Chain, Take};
|
||||
use bytes::Buf;
|
||||
use tracing::trace;
|
||||
|
||||
use super::io::WriteBuf;
|
||||
|
||||
type StaticBuf = &'static [u8];
|
||||
|
||||
/// Encoders to handle different Transfer-Encodings.
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) struct Encoder {
|
||||
kind: Kind,
|
||||
is_last: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct EncodedBuf<B> {
|
||||
kind: BufKind<B>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct NotEof(u64);
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Kind {
|
||||
/// An Encoder for when Transfer-Encoding includes `chunked`.
|
||||
|
|
@ -39,7 +32,6 @@ enum Kind {
|
|||
#[cfg(feature = "server")]
|
||||
CloseDelimited,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum BufKind<B> {
|
||||
Exact(B),
|
||||
|
|
@ -47,140 +39,52 @@ enum BufKind<B> {
|
|||
Chunked(Chain<Chain<ChunkSize, B>, StaticBuf>),
|
||||
ChunkedEnd(StaticBuf),
|
||||
}
|
||||
|
||||
impl Encoder {
|
||||
fn new(kind: Kind) -> Encoder {
|
||||
Encoder {
|
||||
kind,
|
||||
is_last: false,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
pub(crate) fn chunked() -> Encoder {
|
||||
Encoder::new(Kind::Chunked)
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn length(len: u64) -> Encoder {
|
||||
Encoder::new(Kind::Length(len))
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) fn close_delimited() -> Encoder {
|
||||
Encoder::new(Kind::CloseDelimited)
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn is_eof(&self) -> bool {
|
||||
matches!(self.kind, Kind::Length(0))
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) fn set_last(mut self, is_last: bool) -> Self {
|
||||
self.is_last = is_last;
|
||||
self
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn is_last(&self) -> bool {
|
||||
self.is_last
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn is_close_delimited(&self) -> bool {
|
||||
match self.kind {
|
||||
#[cfg(feature = "server")]
|
||||
Kind::CloseDelimited => true,
|
||||
_ => false,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn end<B>(&self) -> Result<Option<EncodedBuf<B>>, NotEof> {
|
||||
match self.kind {
|
||||
Kind::Length(0) => Ok(None),
|
||||
Kind::Chunked => Ok(Some(EncodedBuf {
|
||||
kind: BufKind::ChunkedEnd(b"0\r\n\r\n"),
|
||||
})),
|
||||
#[cfg(feature = "server")]
|
||||
Kind::CloseDelimited => Ok(None),
|
||||
Kind::Length(n) => Err(NotEof(n)),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn encode<B>(&mut self, msg: B) -> EncodedBuf<B>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
let len = msg.remaining();
|
||||
debug_assert!(len > 0, "encode() called with empty buf");
|
||||
|
||||
let kind = match self.kind {
|
||||
Kind::Chunked => {
|
||||
trace!("encoding chunked {}B", len);
|
||||
let buf = ChunkSize::new(len)
|
||||
.chain(msg)
|
||||
.chain(b"\r\n" as &'static [u8]);
|
||||
BufKind::Chunked(buf)
|
||||
}
|
||||
Kind::Length(ref mut remaining) => {
|
||||
trace!("sized write, len = {}", len);
|
||||
if len as u64 > *remaining {
|
||||
let limit = *remaining as usize;
|
||||
*remaining = 0;
|
||||
BufKind::Limited(msg.take(limit))
|
||||
} else {
|
||||
*remaining -= len as u64;
|
||||
BufKind::Exact(msg)
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "server")]
|
||||
Kind::CloseDelimited => {
|
||||
trace!("close delimited write {}B", len);
|
||||
BufKind::Exact(msg)
|
||||
}
|
||||
};
|
||||
EncodedBuf { kind }
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(super) fn encode_and_end<B>(&self, msg: B, dst: &mut WriteBuf<EncodedBuf<B>>) -> bool
|
||||
pub(super) fn encode_and_end<B>(
|
||||
&self,
|
||||
msg: B,
|
||||
dst: &mut WriteBuf<EncodedBuf<B>>,
|
||||
) -> bool
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
let len = msg.remaining();
|
||||
debug_assert!(len > 0, "encode() called with empty buf");
|
||||
|
||||
match self.kind {
|
||||
Kind::Chunked => {
|
||||
trace!("encoding chunked {}B", len);
|
||||
let buf = ChunkSize::new(len)
|
||||
.chain(msg)
|
||||
.chain(b"\r\n0\r\n\r\n" as &'static [u8]);
|
||||
dst.buffer(buf);
|
||||
!self.is_last
|
||||
}
|
||||
Kind::Length(remaining) => {
|
||||
use std::cmp::Ordering;
|
||||
|
||||
trace!("sized write, len = {}", len);
|
||||
match (len as u64).cmp(&remaining) {
|
||||
Ordering::Equal => {
|
||||
dst.buffer(msg);
|
||||
!self.is_last
|
||||
}
|
||||
Ordering::Greater => {
|
||||
dst.buffer(msg.take(remaining as usize));
|
||||
!self.is_last
|
||||
}
|
||||
Ordering::Less => {
|
||||
dst.buffer(msg);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "server")]
|
||||
Kind::CloseDelimited => {
|
||||
trace!("close delimited write {}B", len);
|
||||
dst.buffer(msg);
|
||||
false
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
/// Encodes the full body, without verifying the remaining length matches.
|
||||
///
|
||||
/// This is used in conjunction with HttpBody::__hyper_full_data(), which
|
||||
|
|
@ -190,250 +94,106 @@ impl Encoder {
|
|||
where
|
||||
B: Buf,
|
||||
{
|
||||
debug_assert!(msg.remaining() > 0, "encode() called with empty buf");
|
||||
debug_assert!(
|
||||
match self.kind {
|
||||
Kind::Length(len) => len == msg.remaining() as u64,
|
||||
_ => true,
|
||||
},
|
||||
"danger_full_buf length mismatches"
|
||||
);
|
||||
|
||||
match self.kind {
|
||||
Kind::Chunked => {
|
||||
let len = msg.remaining();
|
||||
trace!("encoding chunked {}B", len);
|
||||
let buf = ChunkSize::new(len)
|
||||
.chain(msg)
|
||||
.chain(b"\r\n0\r\n\r\n" as &'static [u8]);
|
||||
dst.buffer(buf);
|
||||
}
|
||||
_ => {
|
||||
dst.buffer(msg);
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Buf for EncodedBuf<B>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
#[inline]
|
||||
fn remaining(&self) -> usize {
|
||||
match self.kind {
|
||||
BufKind::Exact(ref b) => b.remaining(),
|
||||
BufKind::Limited(ref b) => b.remaining(),
|
||||
BufKind::Chunked(ref b) => b.remaining(),
|
||||
BufKind::ChunkedEnd(ref b) => b.remaining(),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn chunk(&self) -> &[u8] {
|
||||
match self.kind {
|
||||
BufKind::Exact(ref b) => b.chunk(),
|
||||
BufKind::Limited(ref b) => b.chunk(),
|
||||
BufKind::Chunked(ref b) => b.chunk(),
|
||||
BufKind::ChunkedEnd(ref b) => b.chunk(),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn advance(&mut self, cnt: usize) {
|
||||
match self.kind {
|
||||
BufKind::Exact(ref mut b) => b.advance(cnt),
|
||||
BufKind::Limited(ref mut b) => b.advance(cnt),
|
||||
BufKind::Chunked(ref mut b) => b.advance(cnt),
|
||||
BufKind::ChunkedEnd(ref mut b) => b.advance(cnt),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn chunks_vectored<'t>(&'t self, dst: &mut [IoSlice<'t>]) -> usize {
|
||||
match self.kind {
|
||||
BufKind::Exact(ref b) => b.chunks_vectored(dst),
|
||||
BufKind::Limited(ref b) => b.chunks_vectored(dst),
|
||||
BufKind::Chunked(ref b) => b.chunks_vectored(dst),
|
||||
BufKind::ChunkedEnd(ref b) => b.chunks_vectored(dst),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
const USIZE_BYTES: usize = 4;
|
||||
|
||||
#[cfg(target_pointer_width = "64")]
|
||||
const USIZE_BYTES: usize = 8;
|
||||
|
||||
// each byte will become 2 hex
|
||||
const CHUNK_SIZE_MAX_BYTES: usize = USIZE_BYTES * 2;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct ChunkSize {
|
||||
bytes: [u8; CHUNK_SIZE_MAX_BYTES + 2],
|
||||
pos: u8,
|
||||
len: u8,
|
||||
}
|
||||
|
||||
impl ChunkSize {
|
||||
fn new(len: usize) -> ChunkSize {
|
||||
use std::fmt::Write;
|
||||
let mut size = ChunkSize {
|
||||
bytes: [0; CHUNK_SIZE_MAX_BYTES + 2],
|
||||
pos: 0,
|
||||
len: 0,
|
||||
};
|
||||
write!(&mut size, "{:X}\r\n", len).expect("CHUNK_SIZE_MAX_BYTES should fit any usize");
|
||||
size
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Buf for ChunkSize {
|
||||
#[inline]
|
||||
fn remaining(&self) -> usize {
|
||||
(self.len - self.pos).into()
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn chunk(&self) -> &[u8] {
|
||||
&self.bytes[self.pos.into()..self.len.into()]
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn advance(&mut self, cnt: usize) {
|
||||
assert!(cnt <= self.remaining());
|
||||
self.pos += cnt as u8; // just asserted cnt fits in u8
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ChunkSize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ChunkSize")
|
||||
.field("bytes", &&self.bytes[..self.len.into()])
|
||||
.field("pos", &self.pos)
|
||||
.finish()
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for ChunkSize {
|
||||
fn write_str(&mut self, num: &str) -> fmt::Result {
|
||||
use std::io::Write;
|
||||
(&mut self.bytes[self.len.into()..])
|
||||
.write_all(num.as_bytes())
|
||||
.expect("&mut [u8].write() cannot error");
|
||||
self.len += num.len() as u8; // safe because bytes is never bigger than 256
|
||||
Ok(())
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Buf> From<B> for EncodedBuf<B> {
|
||||
fn from(buf: B) -> Self {
|
||||
EncodedBuf {
|
||||
kind: BufKind::Exact(buf),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Buf> From<Take<B>> for EncodedBuf<B> {
|
||||
fn from(buf: Take<B>) -> Self {
|
||||
EncodedBuf {
|
||||
kind: BufKind::Limited(buf),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Buf> From<Chain<Chain<ChunkSize, B>, StaticBuf>> for EncodedBuf<B> {
|
||||
fn from(buf: Chain<Chain<ChunkSize, B>, StaticBuf>) -> Self {
|
||||
EncodedBuf {
|
||||
kind: BufKind::Chunked(buf),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for NotEof {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "early end, expected {} more bytes", self.0)
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for NotEof {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use bytes::BufMut;
|
||||
|
||||
use super::super::io::Cursor;
|
||||
use super::Encoder;
|
||||
|
||||
#[test]
|
||||
fn chunked() {
|
||||
let mut encoder = Encoder::chunked();
|
||||
let mut dst = Vec::new();
|
||||
|
||||
let msg1 = b"foo bar".as_ref();
|
||||
let buf1 = encoder.encode(msg1);
|
||||
dst.put(buf1);
|
||||
assert_eq!(dst, b"7\r\nfoo bar\r\n");
|
||||
|
||||
let msg2 = b"baz quux herp".as_ref();
|
||||
let buf2 = encoder.encode(msg2);
|
||||
dst.put(buf2);
|
||||
|
||||
assert_eq!(dst, b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n");
|
||||
|
||||
let end = encoder.end::<Cursor<Vec<u8>>>().unwrap().unwrap();
|
||||
dst.put(end);
|
||||
|
||||
assert_eq!(
|
||||
dst,
|
||||
b"7\r\nfoo bar\r\nD\r\nbaz quux herp\r\n0\r\n\r\n".as_ref()
|
||||
);
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn length() {
|
||||
let max_len = 8;
|
||||
let mut encoder = Encoder::length(max_len as u64);
|
||||
let mut dst = Vec::new();
|
||||
|
||||
let msg1 = b"foo bar".as_ref();
|
||||
let buf1 = encoder.encode(msg1);
|
||||
dst.put(buf1);
|
||||
|
||||
assert_eq!(dst, b"foo bar");
|
||||
assert!(!encoder.is_eof());
|
||||
encoder.end::<()>().unwrap_err();
|
||||
|
||||
let msg2 = b"baz".as_ref();
|
||||
let buf2 = encoder.encode(msg2);
|
||||
dst.put(buf2);
|
||||
|
||||
assert_eq!(dst.len(), max_len);
|
||||
assert_eq!(dst, b"foo barb");
|
||||
assert!(encoder.is_eof());
|
||||
assert!(encoder.end::<()>().unwrap().is_none());
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eof() {
|
||||
let mut encoder = Encoder::close_delimited();
|
||||
let mut dst = Vec::new();
|
||||
|
||||
let msg1 = b"foo bar".as_ref();
|
||||
let buf1 = encoder.encode(msg1);
|
||||
dst.put(buf1);
|
||||
|
||||
assert_eq!(dst, b"foo bar");
|
||||
assert!(!encoder.is_eof());
|
||||
encoder.end::<()>().unwrap();
|
||||
|
||||
let msg2 = b"baz".as_ref();
|
||||
let buf2 = encoder.encode(msg2);
|
||||
dst.put(buf2);
|
||||
|
||||
assert_eq!(dst, b"foo barbaz");
|
||||
assert!(!encoder.is_eof());
|
||||
encoder.end::<()>().unwrap();
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,68 +1,55 @@
|
|||
#[cfg(all(feature = "server", feature = "runtime"))]
|
||||
use std::{pin::Pin, time::Duration};
|
||||
|
||||
use bytes::BytesMut;
|
||||
use http::{HeaderMap, Method};
|
||||
use httparse::ParserConfig;
|
||||
#[cfg(all(feature = "server", feature = "runtime"))]
|
||||
use tokio::time::Sleep;
|
||||
|
||||
use crate::body::DecodedLength;
|
||||
use crate::proto::{BodyLength, MessageHead};
|
||||
|
||||
pub(crate) use self::conn::Conn;
|
||||
pub(crate) use self::decode::Decoder;
|
||||
pub(crate) use self::dispatch::Dispatcher;
|
||||
pub(crate) use self::encode::{EncodedBuf, Encoder};
|
||||
//TODO: move out of h1::io
|
||||
pub(crate) use self::io::MINIMUM_MAX_BUFFER_SIZE;
|
||||
|
||||
mod conn;
|
||||
mod decode;
|
||||
pub(crate) mod dispatch;
|
||||
mod encode;
|
||||
mod io;
|
||||
mod role;
|
||||
|
||||
cfg_client! {
|
||||
pub(crate) type ClientTransaction = role::Client;
|
||||
pub (crate) type ClientTransaction = role::Client;
|
||||
}
|
||||
|
||||
cfg_server! {
|
||||
pub(crate) type ServerTransaction = role::Server;
|
||||
pub (crate) type ServerTransaction = role::Server;
|
||||
}
|
||||
|
||||
pub(crate) trait Http1Transaction {
|
||||
type Incoming;
|
||||
type Outgoing: Default;
|
||||
const LOG: &'static str;
|
||||
fn parse(bytes: &mut BytesMut, ctx: ParseContext<'_>) -> ParseResult<Self::Incoming>;
|
||||
fn encode(enc: Encode<'_, Self::Outgoing>, dst: &mut Vec<u8>) -> crate::Result<Encoder>;
|
||||
|
||||
fn encode(
|
||||
enc: Encode<'_, Self::Outgoing>,
|
||||
dst: &mut Vec<u8>,
|
||||
) -> crate::Result<Encoder>;
|
||||
fn on_error(err: &crate::Error) -> Option<MessageHead<Self::Outgoing>>;
|
||||
|
||||
fn is_client() -> bool {
|
||||
!Self::is_server()
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn is_server() -> bool {
|
||||
!Self::is_client()
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn should_error_on_parse_eof() -> bool {
|
||||
Self::is_client()
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn should_read_first() -> bool {
|
||||
Self::is_server()
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn update_date() {}
|
||||
}
|
||||
|
||||
/// Result newtype for Http1Transaction::parse.
|
||||
pub(crate) type ParseResult<T> = Result<Option<ParsedMessage<T>>, crate::error::Parse>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct ParsedMessage<T> {
|
||||
head: MessageHead<T>,
|
||||
|
|
@ -71,7 +58,6 @@ pub(crate) struct ParsedMessage<T> {
|
|||
keep_alive: bool,
|
||||
wants_upgrade: bool,
|
||||
}
|
||||
|
||||
pub(crate) struct ParseContext<'a> {
|
||||
cached_headers: &'a mut Option<HeaderMap>,
|
||||
req_method: &'a mut Option<Method>,
|
||||
|
|
@ -91,7 +77,6 @@ pub(crate) struct ParseContext<'a> {
|
|||
#[cfg(feature = "ffi")]
|
||||
raw_headers: bool,
|
||||
}
|
||||
|
||||
/// Passed to Http1Transaction::encode
|
||||
pub(crate) struct Encode<'a, T> {
|
||||
head: &'a mut MessageHead<T>,
|
||||
|
|
@ -101,22 +86,18 @@ pub(crate) struct Encode<'a, T> {
|
|||
req_method: &'a mut Option<Method>,
|
||||
title_case_headers: bool,
|
||||
}
|
||||
|
||||
/// Extra flags that a request "wants", like expect-continue or upgrades.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Wants(u8);
|
||||
|
||||
impl Wants {
|
||||
const EMPTY: Wants = Wants(0b00);
|
||||
const EXPECT: Wants = Wants(0b01);
|
||||
const UPGRADE: Wants = Wants(0b10);
|
||||
|
||||
#[must_use]
|
||||
fn add(self, other: Wants) -> Wants {
|
||||
Wants(self.0 | other.0)
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn contains(&self, other: Wants) -> bool {
|
||||
(self.0 & other.0) == other.0
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,6 @@
|
|||
use std::error::Error as StdError;
|
||||
#[cfg(feature = "runtime")]
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::Bytes;
|
||||
use futures_channel::{mpsc, oneshot};
|
||||
use futures_util::future::{self, Either, FutureExt as _, TryFutureExt as _};
|
||||
|
|
@ -11,7 +10,6 @@ use h2::SendStream;
|
|||
use http::{Method, StatusCode};
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use super::{ping, H2Upgraded, PipeToSendStream, SendBuf};
|
||||
use crate::body::HttpBody;
|
||||
use crate::client::dispatch::Callback;
|
||||
|
|
@ -23,25 +21,13 @@ use crate::proto::Dispatched;
|
|||
use crate::upgrade::Upgraded;
|
||||
use crate::{Body, Request, Response};
|
||||
use h2::client::ResponseFuture;
|
||||
|
||||
type ClientRx<B> = crate::client::dispatch::Receiver<Request<B>, Response<Body>>;
|
||||
|
||||
///// An mpsc channel is used to help notify the `Connection` task when *all*
|
||||
///// other handles to it have been dropped, so that it can shutdown.
|
||||
type ConnDropRef = mpsc::Sender<Never>;
|
||||
|
||||
///// A oneshot channel watches the `Connection` task, and when it completes,
|
||||
///// the "dispatch" task will be notified and can shutdown sooner.
|
||||
type ConnEof = oneshot::Receiver<Never>;
|
||||
|
||||
// Our defaults are chosen for the "majority" case, which usually are not
|
||||
// resource constrained, and so the spec default of 64kb can be too limiting
|
||||
// for performance.
|
||||
const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024 * 5; // 5mb
|
||||
const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024 * 2; // 2mb
|
||||
const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; // 16kb
|
||||
const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 1024; // 1mb
|
||||
|
||||
const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024 * 5;
|
||||
const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024 * 2;
|
||||
const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16;
|
||||
const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 1024;
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Config {
|
||||
pub(crate) adaptive_window: bool,
|
||||
|
|
@ -57,56 +43,17 @@ pub(crate) struct Config {
|
|||
pub(crate) max_concurrent_reset_streams: Option<usize>,
|
||||
pub(crate) max_send_buffer_size: usize,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
adaptive_window: false,
|
||||
initial_conn_window_size: DEFAULT_CONN_WINDOW,
|
||||
initial_stream_window_size: DEFAULT_STREAM_WINDOW,
|
||||
max_frame_size: DEFAULT_MAX_FRAME_SIZE,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_interval: None,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_timeout: Duration::from_secs(20),
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_while_idle: false,
|
||||
max_concurrent_reset_streams: None,
|
||||
max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
fn new_builder(config: &Config) -> Builder {
|
||||
let mut builder = Builder::default();
|
||||
builder
|
||||
.initial_window_size(config.initial_stream_window_size)
|
||||
.initial_connection_window_size(config.initial_conn_window_size)
|
||||
.max_frame_size(config.max_frame_size)
|
||||
.max_send_buffer_size(config.max_send_buffer_size)
|
||||
.enable_push(false);
|
||||
if let Some(max) = config.max_concurrent_reset_streams {
|
||||
builder.max_concurrent_reset_streams(max);
|
||||
}
|
||||
builder
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn new_ping_config(config: &Config) -> ping::Config {
|
||||
ping::Config {
|
||||
bdp_initial_window: if config.adaptive_window {
|
||||
Some(config.initial_stream_window_size)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_interval: config.keep_alive_interval,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_timeout: config.keep_alive_timeout,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_while_idle: config.keep_alive_while_idle,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) async fn handshake<T, B>(
|
||||
io: T,
|
||||
req_rx: ClientRx<B>,
|
||||
|
|
@ -118,85 +65,15 @@ where
|
|||
B: HttpBody,
|
||||
B::Data: Send + 'static,
|
||||
{
|
||||
let (h2_tx, mut conn) = new_builder(config)
|
||||
.handshake::<_, SendBuf<B::Data>>(io)
|
||||
.await
|
||||
.map_err(crate::Error::new_h2)?;
|
||||
|
||||
// An mpsc channel is used entirely to detect when the
|
||||
// 'Client' has been dropped. This is to get around a bug
|
||||
// in h2 where dropping all SendRequests won't notify a
|
||||
// parked Connection.
|
||||
let (conn_drop_ref, rx) = mpsc::channel(1);
|
||||
let (cancel_tx, conn_eof) = oneshot::channel();
|
||||
|
||||
let conn_drop_rx = rx.into_future().map(|(item, _rx)| {
|
||||
if let Some(never) = item {
|
||||
match never {}
|
||||
}
|
||||
});
|
||||
|
||||
let ping_config = new_ping_config(&config);
|
||||
|
||||
let (conn, ping) = if ping_config.is_enabled() {
|
||||
let pp = conn.ping_pong().expect("conn.ping_pong");
|
||||
let (recorder, mut ponger) = ping::channel(pp, ping_config);
|
||||
|
||||
let conn = future::poll_fn(move |cx| {
|
||||
match ponger.poll(cx) {
|
||||
Poll::Ready(ping::Ponged::SizeUpdate(wnd)) => {
|
||||
conn.set_target_window_size(wnd);
|
||||
conn.set_initial_window_size(wnd)?;
|
||||
}
|
||||
#[cfg(feature = "runtime")]
|
||||
Poll::Ready(ping::Ponged::KeepAliveTimedOut) => {
|
||||
debug!("connection keep-alive timed out");
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
Poll::Pending => {}
|
||||
}
|
||||
|
||||
Pin::new(&mut conn).poll(cx)
|
||||
});
|
||||
(Either::Left(conn), recorder)
|
||||
} else {
|
||||
(Either::Right(conn), ping::disabled())
|
||||
};
|
||||
let conn = conn.map_err(|e| debug!("connection error: {}", e));
|
||||
|
||||
exec.execute(conn_task(conn, conn_drop_rx, cancel_tx));
|
||||
|
||||
Ok(ClientTask {
|
||||
ping,
|
||||
conn_drop_ref,
|
||||
conn_eof,
|
||||
executor: exec,
|
||||
h2_tx,
|
||||
req_rx,
|
||||
fut_ctx: None,
|
||||
})
|
||||
loop {}
|
||||
}
|
||||
|
||||
async fn conn_task<C, D>(conn: C, drop_rx: D, cancel_tx: oneshot::Sender<Never>)
|
||||
where
|
||||
C: Future + Unpin,
|
||||
D: Future<Output = ()> + Unpin,
|
||||
{
|
||||
match future::select(conn, drop_rx).await {
|
||||
Either::Left(_) => {
|
||||
// ok or err, the `conn` has finished
|
||||
}
|
||||
Either::Right(((), conn)) => {
|
||||
// mpsc has been dropped, hopefully polling
|
||||
// the connection some more should start shutdown
|
||||
// and then close
|
||||
trace!("send_request dropped, starting conn shutdown");
|
||||
drop(cancel_tx);
|
||||
let _ = conn.await;
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
struct FutCtx<B>
|
||||
where
|
||||
B: HttpBody,
|
||||
|
|
@ -208,9 +85,7 @@ where
|
|||
body: B,
|
||||
cb: Callback<Request<B>, Response<Body>>,
|
||||
}
|
||||
|
||||
impl<B: HttpBody> Unpin for FutCtx<B> {}
|
||||
|
||||
pub(crate) struct ClientTask<B>
|
||||
where
|
||||
B: HttpBody,
|
||||
|
|
@ -223,16 +98,14 @@ where
|
|||
req_rx: ClientRx<B>,
|
||||
fut_ctx: Option<FutCtx<B>>,
|
||||
}
|
||||
|
||||
impl<B> ClientTask<B>
|
||||
where
|
||||
B: HttpBody + 'static,
|
||||
{
|
||||
pub(crate) fn is_extended_connect_protocol_enabled(&self) -> bool {
|
||||
self.h2_tx.is_extended_connect_protocol_enabled()
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> ClientTask<B>
|
||||
where
|
||||
B: HttpBody + Send + 'static,
|
||||
|
|
@ -240,92 +113,9 @@ where
|
|||
B::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
fn poll_pipe(&mut self, f: FutCtx<B>, cx: &mut task::Context<'_>) {
|
||||
let ping = self.ping.clone();
|
||||
let send_stream = if !f.is_connect {
|
||||
if !f.eos {
|
||||
let mut pipe = Box::pin(PipeToSendStream::new(f.body, f.body_tx)).map(|res| {
|
||||
if let Err(e) = res {
|
||||
debug!("client request body error: {}", e);
|
||||
}
|
||||
});
|
||||
|
||||
// eagerly see if the body pipe is ready and
|
||||
// can thus skip allocating in the executor
|
||||
match Pin::new(&mut pipe).poll(cx) {
|
||||
Poll::Ready(_) => (),
|
||||
Poll::Pending => {
|
||||
let conn_drop_ref = self.conn_drop_ref.clone();
|
||||
// keep the ping recorder's knowledge of an
|
||||
// "open stream" alive while this body is
|
||||
// still sending...
|
||||
let ping = ping.clone();
|
||||
let pipe = pipe.map(move |x| {
|
||||
drop(conn_drop_ref);
|
||||
drop(ping);
|
||||
x
|
||||
});
|
||||
// Clear send task
|
||||
self.executor.execute(pipe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
} else {
|
||||
Some(f.body_tx)
|
||||
};
|
||||
|
||||
let fut = f.fut.map(move |result| match result {
|
||||
Ok(res) => {
|
||||
// record that we got the response headers
|
||||
ping.record_non_data();
|
||||
|
||||
let content_length = headers::content_length_parse_all(res.headers());
|
||||
if let (Some(mut send_stream), StatusCode::OK) = (send_stream, res.status()) {
|
||||
if content_length.map_or(false, |len| len != 0) {
|
||||
warn!("h2 connect response with non-zero body not supported");
|
||||
|
||||
send_stream.send_reset(h2::Reason::INTERNAL_ERROR);
|
||||
return Err((
|
||||
crate::Error::new_h2(h2::Reason::INTERNAL_ERROR.into()),
|
||||
None,
|
||||
));
|
||||
}
|
||||
let (parts, recv_stream) = res.into_parts();
|
||||
let mut res = Response::from_parts(parts, Body::empty());
|
||||
|
||||
let (pending, on_upgrade) = crate::upgrade::pending();
|
||||
let io = H2Upgraded {
|
||||
ping,
|
||||
send_stream: unsafe { UpgradedSendStream::new(send_stream) },
|
||||
recv_stream,
|
||||
buf: Bytes::new(),
|
||||
};
|
||||
let upgraded = Upgraded::new(io, Bytes::new());
|
||||
|
||||
pending.fulfill(upgraded);
|
||||
res.extensions_mut().insert(on_upgrade);
|
||||
|
||||
Ok(res)
|
||||
} else {
|
||||
let res = res.map(|stream| {
|
||||
let ping = ping.for_stream(&stream);
|
||||
crate::Body::h2(stream, content_length.into(), ping)
|
||||
});
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
ping.ensure_not_timed_out().map_err(|e| (e, None))?;
|
||||
|
||||
debug!("client response error: {}", err);
|
||||
Err((crate::Error::new_h2(err), None))
|
||||
}
|
||||
});
|
||||
self.executor.execute(f.cb.send_when(fut));
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Future for ClientTask<B>
|
||||
where
|
||||
B: HttpBody + Send + 'static,
|
||||
|
|
@ -333,118 +123,7 @@ where
|
|||
B::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
type Output = crate::Result<Dispatched>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
loop {
|
||||
match ready!(self.h2_tx.poll_ready(cx)) {
|
||||
Ok(()) => (),
|
||||
Err(err) => {
|
||||
self.ping.ensure_not_timed_out()?;
|
||||
return if err.reason() == Some(::h2::Reason::NO_ERROR) {
|
||||
trace!("connection gracefully shutdown");
|
||||
Poll::Ready(Ok(Dispatched::Shutdown))
|
||||
} else {
|
||||
Poll::Ready(Err(crate::Error::new_h2(err)))
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
match self.fut_ctx.take() {
|
||||
// If we were waiting on pending open
|
||||
// continue where we left off.
|
||||
Some(f) => {
|
||||
self.poll_pipe(f, cx);
|
||||
continue;
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
|
||||
match self.req_rx.poll_recv(cx) {
|
||||
Poll::Ready(Some((req, cb))) => {
|
||||
// check that future hasn't been canceled already
|
||||
if cb.is_canceled() {
|
||||
trace!("request callback is canceled");
|
||||
continue;
|
||||
}
|
||||
let (head, body) = req.into_parts();
|
||||
let mut req = ::http::Request::from_parts(head, ());
|
||||
super::strip_connection_headers(req.headers_mut(), true);
|
||||
if let Some(len) = body.size_hint().exact() {
|
||||
if len != 0 || headers::method_has_defined_payload_semantics(req.method()) {
|
||||
headers::set_content_length_if_missing(req.headers_mut(), len);
|
||||
}
|
||||
}
|
||||
|
||||
let is_connect = req.method() == Method::CONNECT;
|
||||
let eos = body.is_end_stream();
|
||||
|
||||
if is_connect {
|
||||
if headers::content_length_parse_all(req.headers())
|
||||
.map_or(false, |len| len != 0)
|
||||
{
|
||||
warn!("h2 connect request with non-zero body not supported");
|
||||
cb.send(Err((
|
||||
crate::Error::new_h2(h2::Reason::INTERNAL_ERROR.into()),
|
||||
None,
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(protocol) = req.extensions_mut().remove::<Protocol>() {
|
||||
req.extensions_mut().insert(protocol.into_inner());
|
||||
}
|
||||
|
||||
let (fut, body_tx) = match self.h2_tx.send_request(req, !is_connect && eos) {
|
||||
Ok(ok) => ok,
|
||||
Err(err) => {
|
||||
debug!("client send request error: {}", err);
|
||||
cb.send(Err((crate::Error::new_h2(err), None)));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let f = FutCtx {
|
||||
is_connect,
|
||||
eos,
|
||||
fut,
|
||||
body_tx,
|
||||
body,
|
||||
cb,
|
||||
};
|
||||
|
||||
// Check poll_ready() again.
|
||||
// If the call to send_request() resulted in the new stream being pending open
|
||||
// we have to wait for the open to complete before accepting new requests.
|
||||
match self.h2_tx.poll_ready(cx) {
|
||||
Poll::Pending => {
|
||||
// Save Context
|
||||
self.fut_ctx = Some(f);
|
||||
return Poll::Pending;
|
||||
}
|
||||
Poll::Ready(Ok(())) => (),
|
||||
Poll::Ready(Err(err)) => {
|
||||
f.cb.send(Err((crate::Error::new_h2(err), None)));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
self.poll_pipe(f, cx);
|
||||
continue;
|
||||
}
|
||||
|
||||
Poll::Ready(None) => {
|
||||
trace!("client::dispatch::Sender dropped");
|
||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||
}
|
||||
|
||||
Poll::Pending => match ready!(Pin::new(&mut self.conn_eof).poll(cx)) {
|
||||
Ok(never) => match never {},
|
||||
Err(_conn_is_eof) => {
|
||||
trace!("connection task is closed, closing dispatch task");
|
||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,280 +9,83 @@ use std::mem;
|
|||
use std::task::Context;
|
||||
use tokio::io::{AsyncRead, AsyncWrite, ReadBuf};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use crate::body::HttpBody;
|
||||
use crate::common::{task, Future, Pin, Poll};
|
||||
use crate::proto::h2::ping::Recorder;
|
||||
|
||||
pub(crate) mod ping;
|
||||
|
||||
cfg_client! {
|
||||
pub(crate) mod client;
|
||||
pub(crate) use self::client::ClientTask;
|
||||
pub (crate) mod client; pub (crate) use self::client::ClientTask;
|
||||
}
|
||||
|
||||
cfg_server! {
|
||||
pub(crate) mod server;
|
||||
pub(crate) use self::server::Server;
|
||||
pub (crate) mod server; pub (crate) use self::server::Server;
|
||||
}
|
||||
|
||||
/// Default initial stream window size defined in HTTP2 spec.
|
||||
pub(crate) const SPEC_WINDOW_SIZE: u32 = 65_535;
|
||||
|
||||
fn strip_connection_headers(headers: &mut HeaderMap, is_request: bool) {
|
||||
// List of connection headers from:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection
|
||||
//
|
||||
// TE headers are allowed in HTTP/2 requests as long as the value is "trailers", so they're
|
||||
// tested separately.
|
||||
let connection_headers = [
|
||||
HeaderName::from_lowercase(b"keep-alive").unwrap(),
|
||||
HeaderName::from_lowercase(b"proxy-connection").unwrap(),
|
||||
TRAILER,
|
||||
TRANSFER_ENCODING,
|
||||
UPGRADE,
|
||||
];
|
||||
|
||||
for header in connection_headers.iter() {
|
||||
if headers.remove(header).is_some() {
|
||||
warn!("Connection header illegal in HTTP/2: {}", header.as_str());
|
||||
}
|
||||
}
|
||||
|
||||
if is_request {
|
||||
if headers
|
||||
.get(TE)
|
||||
.map(|te_header| te_header != "trailers")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
warn!("TE headers not set to \"trailers\" are illegal in HTTP/2 requests");
|
||||
headers.remove(TE);
|
||||
}
|
||||
} else if headers.remove(TE).is_some() {
|
||||
warn!("TE headers illegal in HTTP/2 responses");
|
||||
}
|
||||
|
||||
if let Some(header) = headers.remove(CONNECTION) {
|
||||
warn!(
|
||||
"Connection header illegal in HTTP/2: {}",
|
||||
CONNECTION.as_str()
|
||||
);
|
||||
let header_contents = header.to_str().unwrap();
|
||||
|
||||
// A `Connection` header may have a comma-separated list of names of other headers that
|
||||
// are meant for only this specific connection.
|
||||
//
|
||||
// Iterate these names and remove them as headers. Connection-specific headers are
|
||||
// forbidden in HTTP2, as that information has been moved into frame types of the h2
|
||||
// protocol.
|
||||
for name in header_contents.split(',') {
|
||||
let name = name.trim();
|
||||
headers.remove(name);
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
// body adapters used by both Client and Server
|
||||
|
||||
pin_project! {
|
||||
struct PipeToSendStream<S>
|
||||
where
|
||||
S: HttpBody,
|
||||
{
|
||||
body_tx: SendStream<SendBuf<S::Data>>,
|
||||
data_done: bool,
|
||||
#[pin]
|
||||
stream: S,
|
||||
}
|
||||
struct PipeToSendStream < S > where S : HttpBody, { body_tx : SendStream < SendBuf <
|
||||
S::Data >>, data_done : bool, #[pin] stream : S, }
|
||||
}
|
||||
|
||||
impl<S> PipeToSendStream<S>
|
||||
where
|
||||
S: HttpBody,
|
||||
{
|
||||
fn new(stream: S, tx: SendStream<SendBuf<S::Data>>) -> PipeToSendStream<S> {
|
||||
PipeToSendStream {
|
||||
body_tx: tx,
|
||||
data_done: false,
|
||||
stream,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Future for PipeToSendStream<S>
|
||||
where
|
||||
S: HttpBody,
|
||||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
type Output = crate::Result<()>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
let mut me = self.project();
|
||||
loop {
|
||||
if !*me.data_done {
|
||||
// we don't have the next chunk of data yet, so just reserve 1 byte to make
|
||||
// sure there's some capacity available. h2 will handle the capacity management
|
||||
// for the actual body chunk.
|
||||
me.body_tx.reserve_capacity(1);
|
||||
|
||||
if me.body_tx.capacity() == 0 {
|
||||
loop {
|
||||
match ready!(me.body_tx.poll_capacity(cx)) {
|
||||
Some(Ok(0)) => {}
|
||||
Some(Ok(_)) => break,
|
||||
Some(Err(e)) => {
|
||||
return Poll::Ready(Err(crate::Error::new_body_write(e)))
|
||||
}
|
||||
None => {
|
||||
// None means the stream is no longer in a
|
||||
// streaming state, we either finished it
|
||||
// somehow, or the remote reset us.
|
||||
return Poll::Ready(Err(crate::Error::new_body_write(
|
||||
"send stream capacity unexpectedly closed",
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Poll::Ready(reason) = me
|
||||
.body_tx
|
||||
.poll_reset(cx)
|
||||
.map_err(crate::Error::new_body_write)?
|
||||
{
|
||||
debug!("stream received RST_STREAM: {:?}", reason);
|
||||
return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
|
||||
reason,
|
||||
))));
|
||||
}
|
||||
|
||||
match ready!(me.stream.as_mut().poll_data(cx)) {
|
||||
Some(Ok(chunk)) => {
|
||||
let is_eos = me.stream.is_end_stream();
|
||||
trace!(
|
||||
"send body chunk: {} bytes, eos={}",
|
||||
chunk.remaining(),
|
||||
is_eos,
|
||||
);
|
||||
|
||||
let buf = SendBuf::Buf(chunk);
|
||||
me.body_tx
|
||||
.send_data(buf, is_eos)
|
||||
.map_err(crate::Error::new_body_write)?;
|
||||
|
||||
if is_eos {
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
Some(Err(e)) => return Poll::Ready(Err(me.body_tx.on_user_err(e))),
|
||||
None => {
|
||||
me.body_tx.reserve_capacity(0);
|
||||
let is_eos = me.stream.is_end_stream();
|
||||
if is_eos {
|
||||
return Poll::Ready(me.body_tx.send_eos_frame());
|
||||
} else {
|
||||
*me.data_done = true;
|
||||
// loop again to poll_trailers
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let Poll::Ready(reason) = me
|
||||
.body_tx
|
||||
.poll_reset(cx)
|
||||
.map_err(crate::Error::new_body_write)?
|
||||
{
|
||||
debug!("stream received RST_STREAM: {:?}", reason);
|
||||
return Poll::Ready(Err(crate::Error::new_body_write(::h2::Error::from(
|
||||
reason,
|
||||
))));
|
||||
}
|
||||
|
||||
match ready!(me.stream.poll_trailers(cx)) {
|
||||
Ok(Some(trailers)) => {
|
||||
me.body_tx
|
||||
.send_trailers(trailers)
|
||||
.map_err(crate::Error::new_body_write)?;
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
Ok(None) => {
|
||||
// There were no trailers, so send an empty DATA frame...
|
||||
return Poll::Ready(me.body_tx.send_eos_frame());
|
||||
}
|
||||
Err(e) => return Poll::Ready(Err(me.body_tx.on_user_err(e))),
|
||||
}
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
trait SendStreamExt {
|
||||
fn on_user_err<E>(&mut self, err: E) -> crate::Error
|
||||
where
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync>>;
|
||||
fn send_eos_frame(&mut self) -> crate::Result<()>;
|
||||
}
|
||||
|
||||
impl<B: Buf> SendStreamExt for SendStream<SendBuf<B>> {
|
||||
fn on_user_err<E>(&mut self, err: E) -> crate::Error
|
||||
where
|
||||
E: Into<Box<dyn std::error::Error + Send + Sync>>,
|
||||
{
|
||||
let err = crate::Error::new_user_body(err);
|
||||
debug!("send body user stream error: {}", err);
|
||||
self.send_reset(err.h2_reason());
|
||||
err
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn send_eos_frame(&mut self) -> crate::Result<()> {
|
||||
trace!("send body eos");
|
||||
self.send_data(SendBuf::None, true)
|
||||
.map_err(crate::Error::new_body_write)
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(usize)]
|
||||
enum SendBuf<B> {
|
||||
Buf(B),
|
||||
Cursor(Cursor<Box<[u8]>>),
|
||||
None,
|
||||
}
|
||||
|
||||
impl<B: Buf> Buf for SendBuf<B> {
|
||||
#[inline]
|
||||
fn remaining(&self) -> usize {
|
||||
match *self {
|
||||
Self::Buf(ref b) => b.remaining(),
|
||||
Self::Cursor(ref c) => Buf::remaining(c),
|
||||
Self::None => 0,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn chunk(&self) -> &[u8] {
|
||||
match *self {
|
||||
Self::Buf(ref b) => b.chunk(),
|
||||
Self::Cursor(ref c) => c.chunk(),
|
||||
Self::None => &[],
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn advance(&mut self, cnt: usize) {
|
||||
match *self {
|
||||
Self::Buf(ref mut b) => b.advance(cnt),
|
||||
Self::Cursor(ref mut c) => c.advance(cnt),
|
||||
Self::None => {}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn chunks_vectored<'a>(&'a self, dst: &mut [IoSlice<'a>]) -> usize {
|
||||
match *self {
|
||||
Self::Buf(ref b) => b.chunks_vectored(dst),
|
||||
Self::Cursor(ref c) => c.chunks_vectored(dst),
|
||||
Self::None => 0,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
struct H2Upgraded<B>
|
||||
where
|
||||
B: Buf,
|
||||
|
|
@ -292,7 +95,6 @@ where
|
|||
recv_stream: RecvStream,
|
||||
buf: Bytes,
|
||||
}
|
||||
|
||||
impl<B> AsyncRead for H2Upgraded<B>
|
||||
where
|
||||
B: Buf,
|
||||
|
|
@ -302,37 +104,9 @@ where
|
|||
cx: &mut Context<'_>,
|
||||
read_buf: &mut ReadBuf<'_>,
|
||||
) -> Poll<Result<(), io::Error>> {
|
||||
if self.buf.is_empty() {
|
||||
self.buf = loop {
|
||||
match ready!(self.recv_stream.poll_data(cx)) {
|
||||
None => return Poll::Ready(Ok(())),
|
||||
Some(Ok(buf)) if buf.is_empty() && !self.recv_stream.is_end_stream() => {
|
||||
continue
|
||||
}
|
||||
Some(Ok(buf)) => {
|
||||
self.ping.record_data(buf.len());
|
||||
break buf;
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
return Poll::Ready(match e.reason() {
|
||||
Some(Reason::NO_ERROR) | Some(Reason::CANCEL) => Ok(()),
|
||||
Some(Reason::STREAM_CLOSED) => {
|
||||
Err(io::Error::new(io::ErrorKind::BrokenPipe, e))
|
||||
}
|
||||
_ => Err(h2_to_io_error(e)),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
let cnt = std::cmp::min(self.buf.len(), read_buf.remaining());
|
||||
read_buf.put_slice(&self.buf[..cnt]);
|
||||
self.buf.advance(cnt);
|
||||
let _ = self.recv_stream.flow_control().release_capacity(cnt);
|
||||
Poll::Ready(Ok(()))
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> AsyncWrite for H2Upgraded<B>
|
||||
where
|
||||
B: Buf,
|
||||
|
|
@ -342,130 +116,69 @@ where
|
|||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<Result<usize, io::Error>> {
|
||||
if buf.is_empty() {
|
||||
return Poll::Ready(Ok(0));
|
||||
}
|
||||
self.send_stream.reserve_capacity(buf.len());
|
||||
|
||||
// We ignore all errors returned by `poll_capacity` and `write`, as we
|
||||
// will get the correct from `poll_reset` anyway.
|
||||
let cnt = match ready!(self.send_stream.poll_capacity(cx)) {
|
||||
None => Some(0),
|
||||
Some(Ok(cnt)) => self
|
||||
.send_stream
|
||||
.write(&buf[..cnt], false)
|
||||
.ok()
|
||||
.map(|()| cnt),
|
||||
Some(Err(_)) => None,
|
||||
};
|
||||
|
||||
if let Some(cnt) = cnt {
|
||||
return Poll::Ready(Ok(cnt));
|
||||
}
|
||||
|
||||
Poll::Ready(Err(h2_to_io_error(
|
||||
match ready!(self.send_stream.poll_reset(cx)) {
|
||||
Ok(Reason::NO_ERROR) | Ok(Reason::CANCEL) | Ok(Reason::STREAM_CLOSED) => {
|
||||
return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()))
|
||||
}
|
||||
Ok(reason) => reason.into(),
|
||||
Err(e) => e,
|
||||
},
|
||||
)))
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Result<(), io::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
fn poll_flush(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), io::Error>> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), io::Error>> {
|
||||
if self.send_stream.write(&[], true).is_ok() {
|
||||
return Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
Poll::Ready(Err(h2_to_io_error(
|
||||
match ready!(self.send_stream.poll_reset(cx)) {
|
||||
Ok(Reason::NO_ERROR) => {
|
||||
return Poll::Ready(Ok(()))
|
||||
}
|
||||
Ok(Reason::CANCEL) | Ok(Reason::STREAM_CLOSED) => {
|
||||
return Poll::Ready(Err(io::ErrorKind::BrokenPipe.into()))
|
||||
}
|
||||
Ok(reason) => reason.into(),
|
||||
Err(e) => e,
|
||||
},
|
||||
)))
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
fn h2_to_io_error(e: h2::Error) -> io::Error {
|
||||
if e.is_io() {
|
||||
e.into_io().unwrap()
|
||||
} else {
|
||||
io::Error::new(io::ErrorKind::Other, e)
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
struct UpgradedSendStream<B>(SendStream<SendBuf<Neutered<B>>>);
|
||||
|
||||
impl<B> UpgradedSendStream<B>
|
||||
where
|
||||
B: Buf,
|
||||
{
|
||||
unsafe fn new(inner: SendStream<SendBuf<B>>) -> Self {
|
||||
assert_eq!(mem::size_of::<B>(), mem::size_of::<Neutered<B>>());
|
||||
Self(mem::transmute(inner))
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn reserve_capacity(&mut self, cnt: usize) {
|
||||
unsafe { self.as_inner_unchecked().reserve_capacity(cnt) }
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_capacity(&mut self, cx: &mut Context<'_>) -> Poll<Option<Result<usize, h2::Error>>> {
|
||||
unsafe { self.as_inner_unchecked().poll_capacity(cx) }
|
||||
fn poll_capacity(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Option<Result<usize, h2::Error>>> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_reset(&mut self, cx: &mut Context<'_>) -> Poll<Result<h2::Reason, h2::Error>> {
|
||||
unsafe { self.as_inner_unchecked().poll_reset(cx) }
|
||||
fn poll_reset(
|
||||
&mut self,
|
||||
cx: &mut Context<'_>,
|
||||
) -> Poll<Result<h2::Reason, h2::Error>> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn write(&mut self, buf: &[u8], end_of_stream: bool) -> Result<(), io::Error> {
|
||||
let send_buf = SendBuf::Cursor(Cursor::new(buf.into()));
|
||||
unsafe {
|
||||
self.as_inner_unchecked()
|
||||
.send_data(send_buf, end_of_stream)
|
||||
.map_err(h2_to_io_error)
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
unsafe fn as_inner_unchecked(&mut self) -> &mut SendStream<SendBuf<B>> {
|
||||
&mut *(&mut self.0 as *mut _ as *mut _)
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
struct Neutered<B> {
|
||||
_inner: B,
|
||||
impossible: Impossible,
|
||||
}
|
||||
|
||||
enum Impossible {}
|
||||
|
||||
unsafe impl<B> Send for Neutered<B> {}
|
||||
|
||||
impl<B> Buf for Neutered<B> {
|
||||
fn remaining(&self) -> usize {
|
||||
match self.impossible {}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn chunk(&self) -> &[u8] {
|
||||
match self.impossible {}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn advance(&mut self, _cnt: usize) {
|
||||
match self.impossible {}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
/// 3b. Merge RTT with a running average.
|
||||
/// 3c. Calculate bdp as bytes/rtt.
|
||||
/// 3d. If bdp is over 2/3 max, set new max to bdp and update windows.
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
use std::fmt;
|
||||
#[cfg(feature = "runtime")]
|
||||
|
|
@ -30,74 +29,17 @@ use std::task::{self, Poll};
|
|||
use std::time::Duration;
|
||||
#[cfg(not(feature = "runtime"))]
|
||||
use std::time::Instant;
|
||||
|
||||
use h2::{Ping, PingPong};
|
||||
#[cfg(feature = "runtime")]
|
||||
use tokio::time::{Instant, Sleep};
|
||||
use tracing::{debug, trace};
|
||||
|
||||
type WindowSize = u32;
|
||||
|
||||
pub(super) fn disabled() -> Recorder {
|
||||
Recorder { shared: None }
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(super) fn channel(ping_pong: PingPong, config: Config) -> (Recorder, Ponger) {
|
||||
debug_assert!(
|
||||
config.is_enabled(),
|
||||
"ping channel requires bdp or keep-alive config",
|
||||
);
|
||||
|
||||
let bdp = config.bdp_initial_window.map(|wnd| Bdp {
|
||||
bdp: wnd,
|
||||
max_bandwidth: 0.0,
|
||||
rtt: 0.0,
|
||||
ping_delay: Duration::from_millis(100),
|
||||
stable_count: 0,
|
||||
});
|
||||
|
||||
let (bytes, next_bdp_at) = if bdp.is_some() {
|
||||
(Some(0), Some(Instant::now()))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
let keep_alive = config.keep_alive_interval.map(|interval| KeepAlive {
|
||||
interval,
|
||||
timeout: config.keep_alive_timeout,
|
||||
while_idle: config.keep_alive_while_idle,
|
||||
timer: Box::pin(tokio::time::sleep(interval)),
|
||||
state: KeepAliveState::Init,
|
||||
});
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
let last_read_at = keep_alive.as_ref().map(|_| Instant::now());
|
||||
|
||||
let shared = Arc::new(Mutex::new(Shared {
|
||||
bytes,
|
||||
#[cfg(feature = "runtime")]
|
||||
last_read_at,
|
||||
#[cfg(feature = "runtime")]
|
||||
is_keep_alive_timed_out: false,
|
||||
ping_pong,
|
||||
ping_sent_at: None,
|
||||
next_bdp_at,
|
||||
}));
|
||||
|
||||
(
|
||||
Recorder {
|
||||
shared: Some(shared.clone()),
|
||||
},
|
||||
Ponger {
|
||||
bdp,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive,
|
||||
shared,
|
||||
},
|
||||
)
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(super) struct Config {
|
||||
pub(super) bdp_initial_window: Option<WindowSize>,
|
||||
|
|
@ -112,41 +54,32 @@ pub(super) struct Config {
|
|||
#[cfg(feature = "runtime")]
|
||||
pub(super) keep_alive_while_idle: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct Recorder {
|
||||
shared: Option<Arc<Mutex<Shared>>>,
|
||||
}
|
||||
|
||||
pub(super) struct Ponger {
|
||||
bdp: Option<Bdp>,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive: Option<KeepAlive>,
|
||||
shared: Arc<Mutex<Shared>>,
|
||||
}
|
||||
|
||||
struct Shared {
|
||||
ping_pong: PingPong,
|
||||
ping_sent_at: Option<Instant>,
|
||||
|
||||
// bdp
|
||||
/// If `Some`, bdp is enabled, and this tracks how many bytes have been
|
||||
/// read during the current sample.
|
||||
bytes: Option<usize>,
|
||||
/// We delay a variable amount of time between BDP pings. This allows us
|
||||
/// to send less pings as the bandwidth stabilizes.
|
||||
next_bdp_at: Option<Instant>,
|
||||
|
||||
// keep-alive
|
||||
/// If `Some`, keep-alive is enabled, and the Instant is how long ago
|
||||
/// the connection read the last frame.
|
||||
#[cfg(feature = "runtime")]
|
||||
last_read_at: Option<Instant>,
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
is_keep_alive_timed_out: bool,
|
||||
}
|
||||
|
||||
struct Bdp {
|
||||
/// Current BDP in bytes
|
||||
bdp: u32,
|
||||
|
|
@ -161,7 +94,6 @@ struct Bdp {
|
|||
/// The count of ping round trips where BDP has stayed the same.
|
||||
stable_count: u32,
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
struct KeepAlive {
|
||||
/// If no frames are received in this amount of time, a PING frame is sent.
|
||||
|
|
@ -171,385 +103,113 @@ struct KeepAlive {
|
|||
timeout: Duration,
|
||||
/// If true, sends pings even when there are no active streams.
|
||||
while_idle: bool,
|
||||
|
||||
state: KeepAliveState,
|
||||
timer: Pin<Box<Sleep>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
enum KeepAliveState {
|
||||
Init,
|
||||
Scheduled,
|
||||
PingSent,
|
||||
}
|
||||
|
||||
pub(super) enum Ponged {
|
||||
SizeUpdate(WindowSize),
|
||||
#[cfg(feature = "runtime")]
|
||||
KeepAliveTimedOut,
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
#[derive(Debug)]
|
||||
pub(super) struct KeepAliveTimedOut;
|
||||
|
||||
// ===== impl Config =====
|
||||
|
||||
impl Config {
|
||||
pub(super) fn is_enabled(&self) -> bool {
|
||||
#[cfg(feature = "runtime")]
|
||||
{
|
||||
self.bdp_initial_window.is_some() || self.keep_alive_interval.is_some()
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "runtime"))]
|
||||
{
|
||||
self.bdp_initial_window.is_some()
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Recorder =====
|
||||
|
||||
impl Recorder {
|
||||
pub(crate) fn record_data(&self, len: usize) {
|
||||
let shared = if let Some(ref shared) = self.shared {
|
||||
shared
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut locked = shared.lock().unwrap();
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
locked.update_last_read_at();
|
||||
|
||||
// are we ready to send another bdp ping?
|
||||
// if not, we don't need to record bytes either
|
||||
|
||||
if let Some(ref next_bdp_at) = locked.next_bdp_at {
|
||||
if Instant::now() < *next_bdp_at {
|
||||
return;
|
||||
} else {
|
||||
locked.next_bdp_at = None;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut bytes) = locked.bytes {
|
||||
*bytes += len;
|
||||
} else {
|
||||
// no need to send bdp ping if bdp is disabled
|
||||
return;
|
||||
}
|
||||
|
||||
if !locked.is_ping_sent() {
|
||||
locked.send_ping();
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn record_non_data(&self) {
|
||||
#[cfg(feature = "runtime")]
|
||||
{
|
||||
let shared = if let Some(ref shared) = self.shared {
|
||||
shared
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut locked = shared.lock().unwrap();
|
||||
|
||||
locked.update_last_read_at();
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
/// If the incoming stream is already closed, convert self into
|
||||
/// a disabled reporter.
|
||||
#[cfg(feature = "client")]
|
||||
pub(super) fn for_stream(self, stream: &h2::RecvStream) -> Self {
|
||||
if stream.is_end_stream() {
|
||||
disabled()
|
||||
} else {
|
||||
self
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(super) fn ensure_not_timed_out(&self) -> crate::Result<()> {
|
||||
#[cfg(feature = "runtime")]
|
||||
{
|
||||
if let Some(ref shared) = self.shared {
|
||||
let locked = shared.lock().unwrap();
|
||||
if locked.is_keep_alive_timed_out {
|
||||
return Err(KeepAliveTimedOut.crate_error());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// else
|
||||
Ok(())
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Ponger =====
|
||||
|
||||
impl Ponger {
|
||||
pub(super) fn poll(&mut self, cx: &mut task::Context<'_>) -> Poll<Ponged> {
|
||||
let now = Instant::now();
|
||||
let mut locked = self.shared.lock().unwrap();
|
||||
#[cfg(feature = "runtime")]
|
||||
let is_idle = self.is_idle();
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
{
|
||||
if let Some(ref mut ka) = self.keep_alive {
|
||||
ka.schedule(is_idle, &locked);
|
||||
ka.maybe_ping(cx, &mut locked);
|
||||
}
|
||||
}
|
||||
|
||||
if !locked.is_ping_sent() {
|
||||
// XXX: this doesn't register a waker...?
|
||||
return Poll::Pending;
|
||||
}
|
||||
|
||||
match locked.ping_pong.poll_pong(cx) {
|
||||
Poll::Ready(Ok(_pong)) => {
|
||||
let start = locked
|
||||
.ping_sent_at
|
||||
.expect("pong received implies ping_sent_at");
|
||||
locked.ping_sent_at = None;
|
||||
let rtt = now - start;
|
||||
trace!("recv pong");
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
{
|
||||
if let Some(ref mut ka) = self.keep_alive {
|
||||
locked.update_last_read_at();
|
||||
ka.schedule(is_idle, &locked);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref mut bdp) = self.bdp {
|
||||
let bytes = locked.bytes.expect("bdp enabled implies bytes");
|
||||
locked.bytes = Some(0); // reset
|
||||
trace!("received BDP ack; bytes = {}, rtt = {:?}", bytes, rtt);
|
||||
|
||||
let update = bdp.calculate(bytes, rtt);
|
||||
locked.next_bdp_at = Some(now + bdp.ping_delay);
|
||||
if let Some(update) = update {
|
||||
return Poll::Ready(Ponged::SizeUpdate(update))
|
||||
}
|
||||
}
|
||||
}
|
||||
Poll::Ready(Err(e)) => {
|
||||
debug!("pong error: {}", e);
|
||||
}
|
||||
Poll::Pending => {
|
||||
#[cfg(feature = "runtime")]
|
||||
{
|
||||
if let Some(ref mut ka) = self.keep_alive {
|
||||
if let Err(KeepAliveTimedOut) = ka.maybe_timeout(cx) {
|
||||
self.keep_alive = None;
|
||||
locked.is_keep_alive_timed_out = true;
|
||||
return Poll::Ready(Ponged::KeepAliveTimedOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: this doesn't register a waker...?
|
||||
Poll::Pending
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
fn is_idle(&self) -> bool {
|
||||
Arc::strong_count(&self.shared) <= 2
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Shared =====
|
||||
|
||||
impl Shared {
|
||||
fn send_ping(&mut self) {
|
||||
match self.ping_pong.send_ping(Ping::opaque()) {
|
||||
Ok(()) => {
|
||||
self.ping_sent_at = Some(Instant::now());
|
||||
trace!("sent ping");
|
||||
}
|
||||
Err(err) => {
|
||||
debug!("error sending ping: {}", err);
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn is_ping_sent(&self) -> bool {
|
||||
self.ping_sent_at.is_some()
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
fn update_last_read_at(&mut self) {
|
||||
if self.last_read_at.is_some() {
|
||||
self.last_read_at = Some(Instant::now());
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
fn last_read_at(&self) -> Instant {
|
||||
self.last_read_at.expect("keep_alive expects last_read_at")
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl Bdp =====
|
||||
|
||||
/// Any higher than this likely will be hitting the TCP flow control.
|
||||
const BDP_LIMIT: usize = 1024 * 1024 * 16;
|
||||
|
||||
impl Bdp {
|
||||
fn calculate(&mut self, bytes: usize, rtt: Duration) -> Option<WindowSize> {
|
||||
// No need to do any math if we're at the limit.
|
||||
if self.bdp as usize == BDP_LIMIT {
|
||||
self.stabilize_delay();
|
||||
return None;
|
||||
}
|
||||
|
||||
// average the rtt
|
||||
let rtt = seconds(rtt);
|
||||
if self.rtt == 0.0 {
|
||||
// First sample means rtt is first rtt.
|
||||
self.rtt = rtt;
|
||||
} else {
|
||||
// Weigh this rtt as 1/8 for a moving average.
|
||||
self.rtt += (rtt - self.rtt) * 0.125;
|
||||
}
|
||||
|
||||
// calculate the current bandwidth
|
||||
let bw = (bytes as f64) / (self.rtt * 1.5);
|
||||
trace!("current bandwidth = {:.1}B/s", bw);
|
||||
|
||||
if bw < self.max_bandwidth {
|
||||
// not a faster bandwidth, so don't update
|
||||
self.stabilize_delay();
|
||||
return None;
|
||||
} else {
|
||||
self.max_bandwidth = bw;
|
||||
}
|
||||
|
||||
// if the current `bytes` sample is at least 2/3 the previous
|
||||
// bdp, increase to double the current sample.
|
||||
if bytes >= self.bdp as usize * 2 / 3 {
|
||||
self.bdp = (bytes * 2).min(BDP_LIMIT) as WindowSize;
|
||||
trace!("BDP increased to {}", self.bdp);
|
||||
|
||||
self.stable_count = 0;
|
||||
self.ping_delay /= 2;
|
||||
Some(self.bdp)
|
||||
} else {
|
||||
self.stabilize_delay();
|
||||
None
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn stabilize_delay(&mut self) {
|
||||
if self.ping_delay < Duration::from_secs(10) {
|
||||
self.stable_count += 1;
|
||||
|
||||
if self.stable_count >= 2 {
|
||||
self.ping_delay *= 4;
|
||||
self.stable_count = 0;
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
fn seconds(dur: Duration) -> f64 {
|
||||
const NANOS_PER_SEC: f64 = 1_000_000_000.0;
|
||||
let secs = dur.as_secs() as f64;
|
||||
secs + (dur.subsec_nanos() as f64) / NANOS_PER_SEC
|
||||
loop {}
|
||||
}
|
||||
|
||||
// ===== impl KeepAlive =====
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
impl KeepAlive {
|
||||
fn schedule(&mut self, is_idle: bool, shared: &Shared) {
|
||||
match self.state {
|
||||
KeepAliveState::Init => {
|
||||
if !self.while_idle && is_idle {
|
||||
return;
|
||||
}
|
||||
|
||||
self.state = KeepAliveState::Scheduled;
|
||||
let interval = shared.last_read_at() + self.interval;
|
||||
self.timer.as_mut().reset(interval);
|
||||
}
|
||||
KeepAliveState::PingSent => {
|
||||
if shared.is_ping_sent() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.state = KeepAliveState::Scheduled;
|
||||
let interval = shared.last_read_at() + self.interval;
|
||||
self.timer.as_mut().reset(interval);
|
||||
}
|
||||
KeepAliveState::Scheduled => (),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn maybe_ping(&mut self, cx: &mut task::Context<'_>, shared: &mut Shared) {
|
||||
match self.state {
|
||||
KeepAliveState::Scheduled => {
|
||||
if Pin::new(&mut self.timer).poll(cx).is_pending() {
|
||||
return;
|
||||
}
|
||||
// check if we've received a frame while we were scheduled
|
||||
if shared.last_read_at() + self.interval > self.timer.deadline() {
|
||||
self.state = KeepAliveState::Init;
|
||||
cx.waker().wake_by_ref(); // schedule us again
|
||||
return;
|
||||
}
|
||||
trace!("keep-alive interval ({:?}) reached", self.interval);
|
||||
shared.send_ping();
|
||||
self.state = KeepAliveState::PingSent;
|
||||
let timeout = Instant::now() + self.timeout;
|
||||
self.timer.as_mut().reset(timeout);
|
||||
}
|
||||
KeepAliveState::Init | KeepAliveState::PingSent => (),
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn maybe_timeout(&mut self, cx: &mut task::Context<'_>) -> Result<(), KeepAliveTimedOut> {
|
||||
match self.state {
|
||||
KeepAliveState::PingSent => {
|
||||
if Pin::new(&mut self.timer).poll(cx).is_pending() {
|
||||
return Ok(());
|
||||
}
|
||||
trace!("keep-alive timeout ({:?}) reached", self.timeout);
|
||||
Err(KeepAliveTimedOut)
|
||||
}
|
||||
KeepAliveState::Init | KeepAliveState::Scheduled => Ok(()),
|
||||
}
|
||||
fn maybe_timeout(
|
||||
&mut self,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Result<(), KeepAliveTimedOut> {
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
// ===== impl KeepAliveTimedOut =====
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
impl KeepAliveTimedOut {
|
||||
pub(super) fn crate_error(self) -> crate::Error {
|
||||
crate::Error::new(crate::error::Kind::Http2).with(self)
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
impl fmt::Display for KeepAliveTimedOut {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("keep-alive timed out")
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "runtime")]
|
||||
impl std::error::Error for KeepAliveTimedOut {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
Some(&crate::error::TimedOut)
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use std::error::Error as StdError;
|
|||
use std::marker::Unpin;
|
||||
#[cfg(feature = "runtime")]
|
||||
use std::time::Duration;
|
||||
|
||||
use bytes::Bytes;
|
||||
use h2::server::{Connection, Handshake, SendResponse};
|
||||
use h2::{Reason, RecvStream};
|
||||
|
|
@ -10,7 +9,6 @@ use http::{Method, Request};
|
|||
use pin_project_lite::pin_project;
|
||||
use tokio::io::{AsyncRead, AsyncWrite};
|
||||
use tracing::{debug, trace, warn};
|
||||
|
||||
use super::{ping, PipeToSendStream, SendBuf};
|
||||
use crate::body::HttpBody;
|
||||
use crate::common::exec::ConnStreamExec;
|
||||
|
|
@ -21,23 +19,13 @@ use crate::proto::h2::ping::Recorder;
|
|||
use crate::proto::h2::{H2Upgraded, UpgradedSendStream};
|
||||
use crate::proto::Dispatched;
|
||||
use crate::service::HttpService;
|
||||
|
||||
use crate::upgrade::{OnUpgrade, Pending, Upgraded};
|
||||
use crate::{Body, Response};
|
||||
|
||||
// Our defaults are chosen for the "majority" case, which usually are not
|
||||
// resource constrained, and so the spec default of 64kb can be too limiting
|
||||
// for performance.
|
||||
//
|
||||
// At the same time, a server more often has multiple clients connected, and
|
||||
// so is more likely to use more resources than a client would.
|
||||
const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024; // 1mb
|
||||
const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024; // 1mb
|
||||
const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16; // 16kb
|
||||
const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 400; // 400kb
|
||||
// 16 MB "sane default" taken from golang http2
|
||||
const DEFAULT_CONN_WINDOW: u32 = 1024 * 1024;
|
||||
const DEFAULT_STREAM_WINDOW: u32 = 1024 * 1024;
|
||||
const DEFAULT_MAX_FRAME_SIZE: u32 = 1024 * 16;
|
||||
const DEFAULT_MAX_SEND_BUF_SIZE: usize = 1024 * 400;
|
||||
const DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE: u32 = 16 << 20;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Config {
|
||||
pub(crate) adaptive_window: bool,
|
||||
|
|
@ -53,50 +41,23 @@ pub(crate) struct Config {
|
|||
pub(crate) max_send_buffer_size: usize,
|
||||
pub(crate) max_header_list_size: u32,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Config {
|
||||
Config {
|
||||
adaptive_window: false,
|
||||
initial_conn_window_size: DEFAULT_CONN_WINDOW,
|
||||
initial_stream_window_size: DEFAULT_STREAM_WINDOW,
|
||||
max_frame_size: DEFAULT_MAX_FRAME_SIZE,
|
||||
enable_connect_protocol: false,
|
||||
max_concurrent_streams: None,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_interval: None,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_timeout: Duration::from_secs(20),
|
||||
max_send_buffer_size: DEFAULT_MAX_SEND_BUF_SIZE,
|
||||
max_header_list_size: DEFAULT_SETTINGS_MAX_HEADER_LIST_SIZE,
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
pub(crate) struct Server<T, S, B, E>
|
||||
where
|
||||
S: HttpService<Body>,
|
||||
B: HttpBody,
|
||||
{
|
||||
exec: E,
|
||||
service: S,
|
||||
state: State<T, B>,
|
||||
}
|
||||
pub (crate) struct Server < T, S, B, E > where S : HttpService < Body >, B :
|
||||
HttpBody, { exec : E, service : S, state : State < T, B >, }
|
||||
}
|
||||
|
||||
enum State<T, B>
|
||||
where
|
||||
B: HttpBody,
|
||||
{
|
||||
Handshaking {
|
||||
ping_config: ping::Config,
|
||||
hs: Handshake<T, SendBuf<B::Data>>,
|
||||
},
|
||||
Handshaking { ping_config: ping::Config, hs: Handshake<T, SendBuf<B::Data>> },
|
||||
Serving(Serving<T, B>),
|
||||
Closed,
|
||||
}
|
||||
|
||||
struct Serving<T, B>
|
||||
where
|
||||
B: HttpBody,
|
||||
|
|
@ -105,7 +66,6 @@ where
|
|||
conn: Connection<T, SendBuf<B::Data>>,
|
||||
closing: Option<crate::Error>,
|
||||
}
|
||||
|
||||
impl<T, S, B, E> Server<T, S, B, E>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
|
|
@ -114,70 +74,18 @@ where
|
|||
B: HttpBody + 'static,
|
||||
E: ConnStreamExec<S::Future, B>,
|
||||
{
|
||||
pub(crate) fn new(io: T, service: S, config: &Config, exec: E) -> Server<T, S, B, E> {
|
||||
let mut builder = h2::server::Builder::default();
|
||||
builder
|
||||
.initial_window_size(config.initial_stream_window_size)
|
||||
.initial_connection_window_size(config.initial_conn_window_size)
|
||||
.max_frame_size(config.max_frame_size)
|
||||
.max_header_list_size(config.max_header_list_size)
|
||||
.max_send_buffer_size(config.max_send_buffer_size);
|
||||
if let Some(max) = config.max_concurrent_streams {
|
||||
builder.max_concurrent_streams(max);
|
||||
}
|
||||
if config.enable_connect_protocol {
|
||||
builder.enable_connect_protocol();
|
||||
}
|
||||
let handshake = builder.handshake(io);
|
||||
|
||||
let bdp = if config.adaptive_window {
|
||||
Some(config.initial_stream_window_size)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let ping_config = ping::Config {
|
||||
bdp_initial_window: bdp,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_interval: config.keep_alive_interval,
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_timeout: config.keep_alive_timeout,
|
||||
// If keep-alive is enabled for servers, always enabled while
|
||||
// idle, so it can more aggressively close dead connections.
|
||||
#[cfg(feature = "runtime")]
|
||||
keep_alive_while_idle: true,
|
||||
};
|
||||
|
||||
Server {
|
||||
exec,
|
||||
state: State::Handshaking {
|
||||
ping_config,
|
||||
hs: handshake,
|
||||
},
|
||||
service,
|
||||
}
|
||||
pub(crate) fn new(
|
||||
io: T,
|
||||
service: S,
|
||||
config: &Config,
|
||||
exec: E,
|
||||
) -> Server<T, S, B, E> {
|
||||
loop {}
|
||||
}
|
||||
|
||||
pub(crate) fn graceful_shutdown(&mut self) {
|
||||
trace!("graceful_shutdown");
|
||||
match self.state {
|
||||
State::Handshaking { .. } => {
|
||||
// fall-through, to replace state with Closed
|
||||
}
|
||||
State::Serving(ref mut srv) => {
|
||||
if srv.closing.is_none() {
|
||||
srv.conn.graceful_shutdown();
|
||||
}
|
||||
return;
|
||||
}
|
||||
State::Closed => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.state = State::Closed;
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, S, B, E> Future for Server<T, S, B, E>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
|
|
@ -187,43 +95,10 @@ where
|
|||
E: ConnStreamExec<S::Future, B>,
|
||||
{
|
||||
type Output = crate::Result<Dispatched>;
|
||||
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
let me = &mut *self;
|
||||
loop {
|
||||
let next = match me.state {
|
||||
State::Handshaking {
|
||||
ref mut hs,
|
||||
ref ping_config,
|
||||
} => {
|
||||
let mut conn = ready!(Pin::new(hs).poll(cx).map_err(crate::Error::new_h2))?;
|
||||
let ping = if ping_config.is_enabled() {
|
||||
let pp = conn.ping_pong().expect("conn.ping_pong");
|
||||
Some(ping::channel(pp, ping_config.clone()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
State::Serving(Serving {
|
||||
ping,
|
||||
conn,
|
||||
closing: None,
|
||||
})
|
||||
}
|
||||
State::Serving(ref mut srv) => {
|
||||
ready!(srv.poll_server(cx, &mut me.service, &mut me.exec))?;
|
||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||
}
|
||||
State::Closed => {
|
||||
// graceful_shutdown was called before handshaking finished,
|
||||
// nothing to do here...
|
||||
return Poll::Ready(Ok(Dispatched::Shutdown));
|
||||
}
|
||||
};
|
||||
me.state = next;
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, B> Serving<T, B>
|
||||
where
|
||||
T: AsyncRead + AsyncWrite + Unpin,
|
||||
|
|
@ -240,171 +115,27 @@ where
|
|||
S::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
E: ConnStreamExec<S::Future, B>,
|
||||
{
|
||||
if self.closing.is_none() {
|
||||
loop {
|
||||
self.poll_ping(cx);
|
||||
|
||||
// Check that the service is ready to accept a new request.
|
||||
//
|
||||
// - If not, just drive the connection some.
|
||||
// - If ready, try to accept a new request from the connection.
|
||||
match service.poll_ready(cx) {
|
||||
Poll::Ready(Ok(())) => (),
|
||||
Poll::Pending => {
|
||||
// use `poll_closed` instead of `poll_accept`,
|
||||
// in order to avoid accepting a request.
|
||||
ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;
|
||||
trace!("incoming connection complete");
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
Poll::Ready(Err(err)) => {
|
||||
let err = crate::Error::new_user_service(err);
|
||||
debug!("service closed: {}", err);
|
||||
|
||||
let reason = err.h2_reason();
|
||||
if reason == Reason::NO_ERROR {
|
||||
// NO_ERROR is only used for graceful shutdowns...
|
||||
trace!("interpreting NO_ERROR user error as graceful_shutdown");
|
||||
self.conn.graceful_shutdown();
|
||||
} else {
|
||||
trace!("abruptly shutting down with {:?}", reason);
|
||||
self.conn.abrupt_shutdown(reason);
|
||||
}
|
||||
self.closing = Some(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// When the service is ready, accepts an incoming request.
|
||||
match ready!(self.conn.poll_accept(cx)) {
|
||||
Some(Ok((req, mut respond))) => {
|
||||
trace!("incoming request");
|
||||
let content_length = headers::content_length_parse_all(req.headers());
|
||||
let ping = self
|
||||
.ping
|
||||
.as_ref()
|
||||
.map(|ping| ping.0.clone())
|
||||
.unwrap_or_else(ping::disabled);
|
||||
|
||||
// Record the headers received
|
||||
ping.record_non_data();
|
||||
|
||||
let is_connect = req.method() == Method::CONNECT;
|
||||
let (mut parts, stream) = req.into_parts();
|
||||
let (mut req, connect_parts) = if !is_connect {
|
||||
(
|
||||
Request::from_parts(
|
||||
parts,
|
||||
crate::Body::h2(stream, content_length.into(), ping),
|
||||
),
|
||||
None,
|
||||
)
|
||||
} else {
|
||||
if content_length.map_or(false, |len| len != 0) {
|
||||
warn!("h2 connect request with non-zero body not supported");
|
||||
respond.send_reset(h2::Reason::INTERNAL_ERROR);
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
let (pending, upgrade) = crate::upgrade::pending();
|
||||
debug_assert!(parts.extensions.get::<OnUpgrade>().is_none());
|
||||
parts.extensions.insert(upgrade);
|
||||
(
|
||||
Request::from_parts(parts, crate::Body::empty()),
|
||||
Some(ConnectParts {
|
||||
pending,
|
||||
ping,
|
||||
recv_stream: stream,
|
||||
}),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(protocol) = req.extensions_mut().remove::<h2::ext::Protocol>() {
|
||||
req.extensions_mut().insert(Protocol::from_inner(protocol));
|
||||
}
|
||||
|
||||
let fut = H2Stream::new(service.call(req), connect_parts, respond);
|
||||
exec.execute_h2stream(fut);
|
||||
}
|
||||
Some(Err(e)) => {
|
||||
return Poll::Ready(Err(crate::Error::new_h2(e)));
|
||||
}
|
||||
None => {
|
||||
// no more incoming streams...
|
||||
if let Some((ref ping, _)) = self.ping {
|
||||
ping.ensure_not_timed_out()?;
|
||||
}
|
||||
|
||||
trace!("incoming connection complete");
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug_assert!(
|
||||
self.closing.is_some(),
|
||||
"poll_server broke loop without closing"
|
||||
);
|
||||
|
||||
ready!(self.conn.poll_closed(cx).map_err(crate::Error::new_h2))?;
|
||||
|
||||
Poll::Ready(Err(self.closing.take().expect("polled after error")))
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn poll_ping(&mut self, cx: &mut task::Context<'_>) {
|
||||
if let Some((_, ref mut estimator)) = self.ping {
|
||||
match estimator.poll(cx) {
|
||||
Poll::Ready(ping::Ponged::SizeUpdate(wnd)) => {
|
||||
self.conn.set_target_window_size(wnd);
|
||||
let _ = self.conn.set_initial_window_size(wnd);
|
||||
}
|
||||
#[cfg(feature = "runtime")]
|
||||
Poll::Ready(ping::Ponged::KeepAliveTimedOut) => {
|
||||
debug!("keep-alive timed out, closing connection");
|
||||
self.conn.abrupt_shutdown(h2::Reason::NO_ERROR);
|
||||
}
|
||||
Poll::Pending => {}
|
||||
}
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct H2Stream<F, B>
|
||||
where
|
||||
B: HttpBody,
|
||||
{
|
||||
reply: SendResponse<SendBuf<B::Data>>,
|
||||
#[pin]
|
||||
state: H2StreamState<F, B>,
|
||||
}
|
||||
#[allow(missing_debug_implementations)] pub struct H2Stream < F, B > where B :
|
||||
HttpBody, { reply : SendResponse < SendBuf < B::Data >>, #[pin] state : H2StreamState
|
||||
< F, B >, }
|
||||
}
|
||||
|
||||
pin_project! {
|
||||
#[project = H2StreamStateProj]
|
||||
enum H2StreamState<F, B>
|
||||
where
|
||||
B: HttpBody,
|
||||
{
|
||||
Service {
|
||||
#[pin]
|
||||
fut: F,
|
||||
connect_parts: Option<ConnectParts>,
|
||||
},
|
||||
Body {
|
||||
#[pin]
|
||||
pipe: PipeToSendStream<B>,
|
||||
},
|
||||
}
|
||||
#[project = H2StreamStateProj] enum H2StreamState < F, B > where B : HttpBody, {
|
||||
Service { #[pin] fut : F, connect_parts : Option < ConnectParts >, }, Body { #[pin]
|
||||
pipe : PipeToSendStream < B >, }, }
|
||||
}
|
||||
|
||||
struct ConnectParts {
|
||||
pending: Pending,
|
||||
ping: Recorder,
|
||||
recv_stream: RecvStream,
|
||||
}
|
||||
|
||||
impl<F, B> H2Stream<F, B>
|
||||
where
|
||||
B: HttpBody,
|
||||
|
|
@ -414,26 +145,17 @@ where
|
|||
connect_parts: Option<ConnectParts>,
|
||||
respond: SendResponse<SendBuf<B::Data>>,
|
||||
) -> H2Stream<F, B> {
|
||||
H2Stream {
|
||||
reply: respond,
|
||||
state: H2StreamState::Service { fut, connect_parts },
|
||||
}
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! reply {
|
||||
($me:expr, $res:expr, $eos:expr) => {{
|
||||
match $me.reply.send_response($res, $eos) {
|
||||
Ok(tx) => tx,
|
||||
Err(e) => {
|
||||
debug!("send response error: {}", e);
|
||||
$me.reply.send_reset(Reason::INTERNAL_ERROR);
|
||||
return Poll::Ready(Err(crate::Error::new_h2(e)));
|
||||
}
|
||||
}
|
||||
}};
|
||||
($me:expr, $res:expr, $eos:expr) => {
|
||||
{ match $me .reply.send_response($res, $eos) { Ok(tx) => tx, Err(e) => {
|
||||
debug!("send response error: {}", e); $me .reply
|
||||
.send_reset(Reason::INTERNAL_ERROR); return Poll::Ready(Err(crate
|
||||
::Error::new_h2(e))); } } }
|
||||
};
|
||||
}
|
||||
|
||||
impl<F, B, E> H2Stream<F, B>
|
||||
where
|
||||
F: Future<Output = Result<Response<B>, E>>,
|
||||
|
|
@ -442,92 +164,13 @@ where
|
|||
B::Error: Into<Box<dyn StdError + Send + Sync>>,
|
||||
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
fn poll2(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
|
||||
let mut me = self.project();
|
||||
loop {
|
||||
let next = match me.state.as_mut().project() {
|
||||
H2StreamStateProj::Service {
|
||||
fut: h,
|
||||
connect_parts,
|
||||
} => {
|
||||
let res = match h.poll(cx) {
|
||||
Poll::Ready(Ok(r)) => r,
|
||||
Poll::Pending => {
|
||||
// Response is not yet ready, so we want to check if the client has sent a
|
||||
// RST_STREAM frame which would cancel the current request.
|
||||
if let Poll::Ready(reason) =
|
||||
me.reply.poll_reset(cx).map_err(crate::Error::new_h2)?
|
||||
{
|
||||
debug!("stream received RST_STREAM: {:?}", reason);
|
||||
return Poll::Ready(Err(crate::Error::new_h2(reason.into())));
|
||||
}
|
||||
return Poll::Pending;
|
||||
}
|
||||
Poll::Ready(Err(e)) => {
|
||||
let err = crate::Error::new_user_service(e);
|
||||
warn!("http2 service errored: {}", err);
|
||||
me.reply.send_reset(err.h2_reason());
|
||||
return Poll::Ready(Err(err));
|
||||
}
|
||||
};
|
||||
|
||||
let (head, body) = res.into_parts();
|
||||
let mut res = ::http::Response::from_parts(head, ());
|
||||
super::strip_connection_headers(res.headers_mut(), false);
|
||||
|
||||
// set Date header if it isn't already set...
|
||||
res.headers_mut()
|
||||
.entry(::http::header::DATE)
|
||||
.or_insert_with(date::update_and_header_value);
|
||||
|
||||
if let Some(connect_parts) = connect_parts.take() {
|
||||
if res.status().is_success() {
|
||||
if headers::content_length_parse_all(res.headers())
|
||||
.map_or(false, |len| len != 0)
|
||||
{
|
||||
warn!("h2 successful response to CONNECT request with body not supported");
|
||||
me.reply.send_reset(h2::Reason::INTERNAL_ERROR);
|
||||
return Poll::Ready(Err(crate::Error::new_user_header()));
|
||||
}
|
||||
let send_stream = reply!(me, res, false);
|
||||
connect_parts.pending.fulfill(Upgraded::new(
|
||||
H2Upgraded {
|
||||
ping: connect_parts.ping,
|
||||
recv_stream: connect_parts.recv_stream,
|
||||
send_stream: unsafe { UpgradedSendStream::new(send_stream) },
|
||||
buf: Bytes::new(),
|
||||
},
|
||||
Bytes::new(),
|
||||
));
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if !body.is_end_stream() {
|
||||
// automatically set Content-Length from body...
|
||||
if let Some(len) = body.size_hint().exact() {
|
||||
headers::set_content_length_if_missing(res.headers_mut(), len);
|
||||
}
|
||||
|
||||
let body_tx = reply!(me, res, false);
|
||||
H2StreamState::Body {
|
||||
pipe: PipeToSendStream::new(body, body_tx),
|
||||
}
|
||||
} else {
|
||||
reply!(me, res, true);
|
||||
return Poll::Ready(Ok(()));
|
||||
}
|
||||
}
|
||||
H2StreamStateProj::Body { pipe } => {
|
||||
return pipe.poll(cx);
|
||||
}
|
||||
};
|
||||
me.state.set(next);
|
||||
}
|
||||
fn poll2(
|
||||
self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> Poll<crate::Result<()>> {
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F, B, E> Future for H2Stream<F, B>
|
||||
where
|
||||
F: Future<Output = Result<Response<B>, E>>,
|
||||
|
|
@ -537,12 +180,7 @@ where
|
|||
E: Into<Box<dyn StdError + Send + Sync>>,
|
||||
{
|
||||
type Output = ();
|
||||
|
||||
fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
|
||||
self.poll2(cx).map(|res| {
|
||||
if let Err(e) = res {
|
||||
debug!("stream error: {}", e);
|
||||
}
|
||||
})
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,11 @@
|
|||
//! Pieces pertaining to the HTTP message protocol.
|
||||
|
||||
cfg_feature! {
|
||||
#![feature = "http1"]
|
||||
|
||||
pub(crate) mod h1;
|
||||
|
||||
pub(crate) use self::h1::Conn;
|
||||
|
||||
#[cfg(feature = "client")]
|
||||
pub(crate) use self::h1::dispatch;
|
||||
#[cfg(feature = "server")]
|
||||
pub(crate) use self::h1::ServerTransaction;
|
||||
#![feature = "http1"] pub (crate) mod h1; pub (crate) use self::h1::Conn;
|
||||
#[cfg(feature = "client")] pub (crate) use self::h1::dispatch; #[cfg(feature =
|
||||
"server")] pub (crate) use self::h1::ServerTransaction;
|
||||
}
|
||||
|
||||
#[cfg(feature = "http2")]
|
||||
pub(crate) mod h2;
|
||||
|
||||
/// An Incoming Message head. Includes request/status line, and headers.
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct MessageHead<S> {
|
||||
|
|
@ -28,19 +18,15 @@ pub(crate) struct MessageHead<S> {
|
|||
/// Extensions.
|
||||
extensions: http::Extensions,
|
||||
}
|
||||
|
||||
/// An incoming request message.
|
||||
#[cfg(feature = "http1")]
|
||||
pub(crate) type RequestHead = MessageHead<RequestLine>;
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
#[cfg(feature = "http1")]
|
||||
pub(crate) struct RequestLine(pub(crate) http::Method, pub(crate) http::Uri);
|
||||
|
||||
/// An incoming response message.
|
||||
#[cfg(all(feature = "http1", feature = "client"))]
|
||||
pub(crate) type ResponseHead = MessageHead<http::StatusCode>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "http1")]
|
||||
pub(crate) enum BodyLength {
|
||||
|
|
@ -49,7 +35,6 @@ pub(crate) enum BodyLength {
|
|||
/// Transfer-Encoding: chunked (if h1)
|
||||
Unknown,
|
||||
}
|
||||
|
||||
/// Status of when a Disaptcher future completes.
|
||||
pub(crate) enum Dispatched {
|
||||
/// Dispatcher completely shutdown connection.
|
||||
|
|
@ -58,14 +43,8 @@ pub(crate) enum Dispatched {
|
|||
#[cfg(feature = "http1")]
|
||||
Upgrade(crate::upgrade::Pending),
|
||||
}
|
||||
|
||||
impl MessageHead<http::StatusCode> {
|
||||
fn into_response<B>(self, body: B) -> http::Response<B> {
|
||||
let mut res = http::Response::new(body);
|
||||
*res.status_mut() = self.subject;
|
||||
*res.headers_mut() = self.headers;
|
||||
*res.version_mut() = self.version;
|
||||
*res.extensions_mut() = self.extensions;
|
||||
res
|
||||
loop {}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue