use std::fmt; use std::io; use std::marker::PhantomData; #[cfg(all(feature = "server", feature = "runtime"))] use std::time::Duration;
use bytes::{Buf, Bytes}; use http::header::{HeaderValue, CONNECTION}; use http::{HeaderMap, Method, Version}; use httparse::ParserConfig; use tokio::io::{AsyncRead, AsyncWrite}; #[cfg(all(feature = "server", feature = "runtime"))] use tokio::time::Sleep; use tracing::{debug, error, trace};
/// This handles a connection, which will have been established over an /// `AsyncRead + AsyncWrite` (like a socket), and will likely include multiple /// `Transaction`s over HTTP. /// /// The connection will determine when a message begins and ends as well as /// determine if this connection can be kept alive after the message, /// or if it is complete. pub(crate) struct Conn<I, B, T> {
io: Buffered<I, EncodedBuf<B>>,
state: State,
_marker: PhantomData<fn(T)>,
}
fn on_read_head_error<Z>(&mutself, e: crate::Error) -> Poll<Option<crate::Result<Z>>> { // If we are currently waiting on a message, then an empty // message should be reported as an error. If not, it is just // the connection closing gracefully. let must_error = self.should_error_on_eof(); self.close_read(); self.io.consume_leading_lines(); let was_mid_parse = e.is_parse() || !self.io.read_buf().is_empty(); if was_mid_parse || must_error { // We check if the buf contains the h2 Preface
debug!( "parse error ({}) with {} bytes",
e, self.io.read_buf().len()
); matchself.on_parse_error(e) {
Ok(()) => Poll::Pending, // XXX: wat?
Err(e) => Poll::Ready(Some(Err(e))),
}
} else {
debug!("read eof"); self.close_write();
Poll::Ready(None)
}
}
let (reading, ret) = matchself.state.reading {
Reading::Body(refmut decoder) => { match ready!(decoder.decode(cx, &mutself.io)) {
Ok(slice) => { let (reading, chunk) = if decoder.is_eof() {
debug!("incoming body completed");
(
Reading::KeepAlive, if !slice.is_empty() {
Some(Ok(slice))
} else {
None
},
)
} elseif slice.is_empty() {
error!("incoming body unexpectedly ended"); // This should be unreachable, since all 3 decoders // either set eof=true or return an Err when reading // an empty slice...
(Reading::Closed, None)
} else { return Poll::Ready(Some(Ok(slice)));
};
(reading, Poll::Ready(chunk))
}
Err(e) => {
debug!("incoming body decode error: {}", e);
(Reading::Closed, Poll::Ready(Some(Err(e))))
}
}
}
Reading::Continue(ref decoder) => { // Write the 100 Continue if not already responded... iflet Writing::Init = self.state.writing {
trace!("automatically sending 100 Continue"); let cont = b"HTTP/1.1 100 Continue\r\n\r\n"; self.io.headers_buf().extend_from_slice(cont);
}
// And now recurse once in the Reading::Body state... self.state.reading = Reading::Body(decoder.clone()); returnself.poll_read_body(cx);
}
_ => unreachable!("poll_read_body invalid state: {:?}", self.state.reading),
};
self.state.reading = reading; self.try_keep_alive(cx);
ret
}
pub(crate) fn wants_read_again(&mutself) -> bool { let ret = self.state.notify_read; self.state.notify_read = false;
ret
}
// This will check to make sure the io object read is empty. // // This should only be called for Clients wanting to enter the idle // state. fn require_empty_read(&mutself, cx: &mut task::Context<'_>) -> Poll<crate::Result<()>> {
debug_assert!(!self.can_read_head() && !self.can_read_body() && !self.is_read_closed());
debug_assert!(!self.is_mid_message());
debug_assert!(T::is_client());
if !self.io.read_buf().is_empty() {
debug!("received an unexpected {} bytes", self.io.read_buf().len()); return Poll::Ready(Err(crate::Error::new_unexpected_message()));
}
let num_read = ready!(self.force_io_read(cx)).map_err(crate::Error::new_io)?;
if num_read == 0 { let ret = ifself.should_error_on_eof() {
trace!("found unexpected EOF on busy connection: {:?}", self.state);
Poll::Ready(Err(crate::Error::new_incomplete()))
} else {
trace!("found EOF on idle connection, closing");
Poll::Ready(Ok(()))
};
// order is important: should_error needs state BEFORE close_read self.state.close_read(); return ret;
}
debug!( "received unexpected {} bytes on an idle connection",
num_read
);
Poll::Ready(Err(crate::Error::new_unexpected_message()))
}
let result = ready!(self.io.poll_read_from_io(cx));
Poll::Ready(result.map_err(|e| {
trace!("force_io_read; io error = {:?}", e); self.state.close();
e
}))
}
fn maybe_notify(&mutself, cx: &mut task::Context<'_>) { // its possible that we returned NotReady from poll() without having // exhausted the underlying Io. We would have done this when we // determined we couldn't keep reading until we knew how writing // would finish.
pub(crate) fn write_full_msg(&mutself, head: MessageHead<T::Outgoing>, body: B) { iflet Some(encoder) = self.encode_head(head, Some(BodyLength::Known(body.remaining() as u64)))
{ let is_last = encoder.is_last(); // Make sure we don't write a body if we weren't actually allowed // to do so, like because its a HEAD request. if !encoder.is_eof() {
encoder.danger_full_buf(body, self.io.write_buf());
} self.state.writing = if is_last {
Writing::Closed
} else {
Writing::KeepAlive
}
}
}
// Fix keep-alive when Connection: keep-alive header is not present fn fix_keep_alive(&mutself, head: &mut MessageHead<T::Outgoing>) { let outgoing_is_keep_alive = head
.headers
.get(CONNECTION)
.map(connection_keep_alive)
.unwrap_or(false);
if !outgoing_is_keep_alive { match head.version { // If response is version 1.0 and keep-alive is not present in the response, // disable keep-alive so the server closes the connection
Version::HTTP_10 => self.state.disable_keep_alive(), // If response is version 1.1 and keep-alive is wanted, add // Connection: keep-alive header when not present
Version::HTTP_11 => { ifself.state.wants_keep_alive() {
head.headers
.insert(CONNECTION, HeaderValue::from_static("keep-alive"));
}
}
_ => (),
}
}
}
// If we know the remote speaks an older version, we try to fix up any messages // to work with our older peer. fn enforce_version(&mutself, head: &mutMessageHead<T::Outgoing>) { iflet Version::HTTP_10 = self.state.version { // Fixes response or connection when keep-alive header is not present self.fix_keep_alive(head); // If the remote only knows HTTP/1.0, we should force ourselves // to do only speak HTTP/1.0 as well.
head.version = Version::HTTP_10;
} // If the remote speaks HTTP/1.1, then it *should* be fine with // both HTTP/1.0 and HTTP/1.1 from us. So again, we just let // the user's headers be.
}
pub(crate) fn write_body(&mutself, chunk: B) {
debug_assert!(self.can_write_body() && self.can_buffer_body()); // empty chunks should be discarded at Dispatcher level
debug_assert!(chunk.remaining() != 0);
let state = matchself.state.writing {
Writing::Body(refmut encoder) => { self.io.buffer(encoder.encode(chunk));
// When we get a parse error, depending on what side we are, we might be able // to write a response before closing the connection. // // - Client: there is nothing we can do // - Server: if Response hasn't been written yet, we can send a 4xx response fn on_parse_error(&mutself, err: crate::Error) -> crate::Result<()> { iflet Writing::Init = self.state.writing { ifself.has_h2_prefix() { return Err(crate::Error::new_version_h2());
} iflet Some(msg) = T::on_error(&err) { // Drop the cached headers so as to not trigger a debug // assert in `write_head`... self.state.cached_headers.take(); self.write_head(msg, None); self.state.error = Some(err); return Ok(());
}
}
/// If the read side can be cheaply drained, do so. Otherwise, close. pub(super) fn poll_drain_or_close_read(&mutself, cx: &e='color:red'>mut task::Context<'_>) { iflet Reading::Continue(ref decoder) = self.state.reading { // skip sending the 100-continue // just move forward to a read, in case a tiny body was included self.state.reading = Reading::Body(decoder.clone());
}
let _ = self.poll_read_body(cx);
// If still in Reading::Body, just give up matchself.state.reading {
Reading::Init | Reading::KeepAlive => trace!("body drained"),
_ => self.close_read(),
}
}
// B and T are never pinned impl<I: Unpin, B, T> Unpin for Conn<I, B, T> {}
struct State {
allow_half_close: bool, /// Re-usable HeaderMap to reduce allocating new ones.
cached_headers: Option<HeaderMap>, /// If an error occurs when there wasn't a direct way to return it /// back to the user, this is set.
error: Option<crate::Error>, /// Current keep-alive status.
keep_alive: KA, /// If mid-message, the HTTP Method that started it. /// /// This is used to know things such as if the message can include /// a body or not.
method: Option<Method>,
h1_parser_config: ParserConfig, #[cfg(all(feature = "server", feature = "runtime"))]
h1_header_read_timeout: Option<Duration>, #[cfg(all(feature = "server", feature = "runtime"))]
h1_header_read_timeout_fut: Option<Pin<Box<Sleep>>>, #[cfg(all(feature = "server", feature = "runtime"))]
h1_header_read_timeout_running: bool,
preserve_header_case: bool, #[cfg(feature = "ffi")]
preserve_header_order: bool,
title_case_headers: bool,
h09_responses: bool, /// If set, called with each 1xx informational response received for /// the current request. MUST be unset after a non-1xx response is /// received. #[cfg(feature = "ffi")]
on_informational: Option<crate::ffi::OnInformational>, #[cfg(feature = "ffi")]
raw_headers: bool, /// Set to true when the Dispatcher should poll read operations /// again. See the `maybe_notify` method for more.
notify_read: bool, /// State of allowed reads
reading: Reading, /// State of allowed writes
writing: Writing, /// An expected pending HTTP upgrade.
upgrade: Option<crate::upgrade::Pending>, /// Either HTTP/1.0 or 1.1 connection
version: Version,
}
// !T::should_read_first() means Client. // // If Client connection has just gone idle, the Dispatcher // should try the poll loop one more time, so as to poll the // pending requests stream. if !T::should_read_first() { self.notify_read = true;
}
}
#[cfg(test)] mod tests { #[cfg(feature = "nightly")] #[bench] fn bench_read_head_short(b: &mut ::test::Bencher) { usesuper::*; let s = b"GET / HTTP/1.1\r\nHost: localhost:8080\r\n\r\n"; let len = s.len();
b.bytes = len as u64;
// an empty IO, we'll be skipping and using the read buffer anyways let io = tokio_test::io::Builder::new().build(); letmut conn = Conn::<_, bytes::Bytes, crate::proto::h1::ServerTransaction>::new(io);
*conn.io.read_buf_mut() = ::bytes::BytesMut::from(&s[..]);
conn.state.cached_headers = Some(HeaderMap::with_capacity(2));
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
// When the body is done, `poll` MUST return a `Body` frame with chunk set to `None` matchconn.poll(){ Ok(Async::Ready(Some(Frame::Body{chunk:None})))=>(), other=>panic!("unexpectedframe:{:?}",other) }
// test that once writing is done, unparks letf=future::lazy(||{ letio=AsyncIo::new_buf(vec![],4096); letmutconn=Conn::<_,proto::Bytes,ServerTransaction>::new(io); conn.state.reading=Reading::KeepAlive; assert!(conn.poll().unwrap().is_not_ready());
// test that flushing when not waiting on read doesn't unpark letf=future::lazy(||{ letio=AsyncIo::new_buf(vec![],4096); letmutconn=Conn::<_,proto::Bytes,ServerTransaction>::new(io); conn.state.writing=Writing::KeepAlive; assert!(conn.poll_complete().unwrap().is_ready()); Ok::<(),()>(()) }); ::futures::executor::spawn(f).poll_future_notify(&car(false),0).unwrap();
// test that flushing and writing isn't done doesn't unpark letf=future::lazy(||{ letio=AsyncIo::new_buf(vec![],4096); letmutconn=Conn::<_,proto::Bytes,ServerTransaction>::new(io); conn.state.reading=Reading::KeepAlive; assert!(conn.poll().unwrap().is_not_ready()); conn.state.writing=Writing::Body(Encoder::length(5_000)); assert!(conn.poll_complete().unwrap().is_ready()); Ok::<(),()>(()) }); ::futures::executor::spawn(f).poll_future_notify(&car(false),0).unwrap(); }
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.24Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-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.