use std::{
convert::Infallible,
future::Future,
marker::PhantomData,
pin::Pin,
task::{Context, Poll},
time::Duration,
};
usecrate::rt::{Read, Write}; use bytes::Bytes; use futures_channel::mpsc::{Receiver, Sender}; use futures_channel::{mpsc, oneshot}; use futures_core::{ready, FusedFuture, FusedStream, Stream}; use h2::client::{Builder, Connection, SendRequest}; use h2::SendStream; use http::{Method, StatusCode}; use pin_project_lite::pin_project;
type ClientRx<B> = crate::client::dispatch::Receiver<Request<B>, Response<IncomingBody>>;
///// 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<Infallible>;
///// 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<Infallible>;
// 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_MAX_HEADER_LIST_SIZE: u32 = 1024 * 16; // 16kb
// The maximum number of concurrent streams that the client is allowed to open // before it receives the initial SETTINGS frame from the server. // This default value is derived from what the HTTP/2 spec recommends as the // minimum value that endpoints advertise to their peers. It means that using // this value will minimize the chance of the failure where the local endpoint // attempts to open too many streams and gets rejected by the remote peer with // the `REFUSED_STREAM` error. const DEFAULT_INITIAL_MAX_SEND_STREAMS: usize = 100;
// 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, conn_drop_rx) = mpsc::channel(1); let (cancel_tx, conn_eof) = oneshot::channel();
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, ponger) = ping::channel(pp, ping_config, timer);
if !this.conn.is_terminated() && Pin::new(&mut this.conn).poll(cx).is_ready() { // ok or err, the `conn` has finished. return Poll::Ready(());
}
if !this.drop_rx.is_terminated() && Pin::new(&mut this.drop_rx).poll_next(cx).is_ready() { // mpsc has been dropped, hopefully polling // the connection some more should start shutdown // and then close.
trace!("send_request dropped, starting conn shutdown");
drop(this.cancel_tx.take().expect("ConnTask Future polled twice"));
}
// Check if the client cancelled the request (e.g. dropped the // response future due to a timeout). If so, reset the h2 stream // so that a RST_STREAM is sent and flow-control capacity is freed. let cancel_result = this.cancel_rx.as_mut().map(|rx| Pin::new(rx).poll(cx)); match cancel_result {
Some(Poll::Ready(Ok(()))) => {
debug!("client request body send cancelled, resetting stream");
this.pipe.as_mut().send_reset(h2::Reason::CANCEL);
drop(this.conn_drop_ref.take().expect("Future polled twice"));
drop(this.ping.take().expect("Future polled twice")); return Poll::Ready(());
}
Some(Poll::Ready(Err(_))) => { // Sender dropped without cancelling (normal response or error). // Stop polling the receiver.
*this.cancel_rx = None;
}
Some(Poll::Pending) | None => {}
}
impl<B, E, T> ClientTask<B, E, T> where
B: Body + 'static + Unpin,
B::Data: Send,
E: Http2ClientConnExec<B, T> + Clone + Unpin,
B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
T: Read + Write + Unpin,
{ fn poll_pipe(&mutself, f: FutCtx<B>, cx: &mut Context<'_>) { let ping = self.ping.clone();
// A one-shot channel so that send_task can tell pipe_task to // reset the stream when the client cancels the request. let (cancel_tx, cancel_rx) = oneshot::channel::<()>();
let send_stream = if !f.is_connect { if !f.eos { letmut pipe = PipeToSendStream::new(f.body, f.body_tx);
// 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();
pin_project! { pub(crate) struct ResponseFutMap<B, E> where
B: Body,
B: 'static,
{ #[pin]
fut: ResponseFuture,
ping: Option<Recorder>, #[pin]
send_stream: Option<Option<SendStream<SendBuf<<B as Body>::Data>>>>,
exec: E,
cancel_tx: Option<oneshot::Sender<()>>,
}
}
impl<B: Body + 'static, E> ResponseFutMap<B, E> { /// Signal the pipe_task to reset the stream (e.g. on client cancellation). pub(crate) fn cancel(self: Pin<&mutSelf>) { iflet Some(cancel_tx) = self.project().cancel_tx.take() { let _ = cancel_tx.send(());
}
}
}
impl<B, E> Future for ResponseFutMap<B, E> where
B: Body + 'static,
E: Http2UpgradedExec<B::Data>,
{ type Output = Result<Response<crate::body::Incoming>, (crate::Error, Option<Request<B>>)>;
let ping = this.ping.take().expect("Future polled twice"); let send_stream = this.send_stream.take().expect("Future polled twice");
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 Poll::Ready(Err(( crate::Error::new_h2(h2::Reason::INTERNAL_ERROR.into()),
None::<Request<B>>,
)));
} let (parts, recv_stream) = res.into_parts(); letmut res = Response::from_parts(parts, IncomingBody::empty());
let (pending, on_upgrade) = crate::upgrade::pending();
let (h2_up, up_task) = super::upgrade::pair(send_stream, recv_stream, ping); self.exec.execute_upgrade(up_task); let upgraded = Upgraded::new(h2_up, Bytes::new());
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(TrySendError {
error: crate::Error::new_h2(err),
message: None,
})); continue;
}
} self.poll_pipe(f, cx); continue;
}
Poll::Pending => match ready!(Pin::new(&mutself.conn_eof).poll(cx)) { // As of Rust 1.82, this pattern is no longer needed, and emits a warning. // But we cannot remove it as long as MSRV is less than that. #[allow(unused)]
Ok(never) => match never {},
Err(_conn_is_eof) => {
trace!("connection task is closed, closing dispatch task"); return Poll::Ready(Ok(Dispatched::Shutdown));
}
},
}
}
}
}
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet am 2026-08-22)
¤
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.