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 _}; use futures_util::stream::StreamExt as _; use h2::client::{Builder, SendRequest}; use h2::SendStream; use http::{Method, StatusCode}; use tokio::io::{AsyncRead, AsyncWrite}; use tracing::{debug, trace, warn};
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
// 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)| { iflet 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);
asyncfn 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;
}
}
}
impl<B> ClientTask<B> where
B: HttpBody + Send + 'static,
B::Data: Send,
B::Error: Into<Box<dyn StdError + Send + Sync>>,
{ fn poll_pipe(&mutself, f: FutCtx<B>, cx: &mut task::Context<'_>) { let ping = self.ping.clone(); let send_stream = if !f.is_connect { if !f.eos { letmut pipe = Box::pin(PipeToSendStream::new(f.body, f.body_tx)).map(|res| { iflet 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()); iflet (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(); letmut 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());
matchself.fut_ctx.take() { // If we were waiting on pending open // continue where we left off.
Some(f) => { self.poll_pipe(f, cx); continue;
}
None => (),
}
matchself.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(); letmut req = ::http::Request::from_parts(head, ()); super::strip_connection_headers(req.headers_mut(), true); iflet 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;
}
}
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. matchself.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;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.