use std::{
future::Future,
pin::Pin,
sync::{atomic::AtomicBool, Arc},
task::{Context, Poll},
time::Duration,
};
use bytes::Bytes;
use futures_util::{pin_mut, FutureExt};
use http::{Request, Response, StatusCode};
use http_body::Body;
use rustc_hash::{FxHashMap, FxHashSet};
use tokio_util::sync::CancellationToken;
use super::codec::{
Frame, FrameDecoder, FrameWriter, Setting, CLIENT_PREFACE, DEFAULT_INITIAL_WINDOW_SIZE,
DEFAULT_MAX_FRAME_SIZE, MAX_FRAME_SIZE_LIMIT,
};
use super::date::DateCache;
use super::error::Reason;
use super::hpack::{Decoder as HpackDecoder, Encoder, Header as HpackHeader, HpackError};
use super::sanitize_response;
use super::stream::{
BodyMsg, H2Body, MalformedRequest, ParsedRequest, StreamDriver, StreamEntry, StreamMsg,
};
use crate::early_hints::EarlyHints;
use crate::Incoming;
#[derive(Debug, Clone, Copy)]
pub struct ConnectionOptions {
pub send_continue_response: bool,
pub send_date_header: bool,
pub max_concurrent_streams: u32,
pub initial_stream_window_size: u32,
pub initial_connection_window_size: u32,
pub max_frame_size: u32,
pub max_header_list_size: u32,
pub enable_connect_protocol: bool,
pub idle_timeout: Option<Duration>,
}
impl Default for ConnectionOptions {
#[inline]
fn default() -> Self {
ConnectionOptions {
send_continue_response: false,
send_date_header: true,
max_concurrent_streams: 100,
initial_stream_window_size: DEFAULT_INITIAL_WINDOW_SIZE,
initial_connection_window_size: DEFAULT_INITIAL_WINDOW_SIZE,
max_frame_size: DEFAULT_MAX_FRAME_SIZE as u32,
max_header_list_size: u32::MAX,
enable_connect_protocol: false,
idle_timeout: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PeerSettings {
pub(crate) header_table_size: u32,
pub(crate) enable_push: u32,
pub(crate) initial_window_size: u32,
pub(crate) max_frame_size: usize,
#[allow(dead_code)]
pub(crate) max_header_list_size: u32,
}
impl Default for PeerSettings {
#[inline]
fn default() -> Self {
PeerSettings {
header_table_size: 4096,
enable_push: 1,
initial_window_size: DEFAULT_INITIAL_WINDOW_SIZE,
max_frame_size: DEFAULT_MAX_FRAME_SIZE,
max_header_list_size: u32::MAX,
}
}
}
pub struct Connection<Io> {
io: Io,
decoder: FrameDecoder,
writer: FrameWriter,
out: Vec<u8>,
encoder: Encoder,
request_decoder: HpackDecoder,
peer: PeerSettings,
#[allow(dead_code)]
local: PeerSettings,
preface_timeout: Option<Duration>,
streams: FxHashMap<u32, StreamEntry>,
conn_window: i64,
closed_streams: FxHashSet<u32>,
#[allow(dead_code)]
opts: ConnectionOptions,
wake_tx: Option<kanal::AsyncSender<()>>,
complete_blocks: Vec<u32>,
drain_ids: Vec<u32>,
highest_stream_id: u32,
closing: bool,
graceful: bool,
graceful_last_stream: u32,
shutdown: Option<CancellationToken>,
date_cache: Arc<DateCache>,
}
impl<Io> Connection<Io>
where
Io: tokio::io::AsyncRead + tokio::io::AsyncWrite + Unpin,
{
#[inline]
pub fn new(io: Io, preface_timeout: Option<Duration>) -> Connection<Io> {
Connection {
io,
decoder: FrameDecoder::new(DEFAULT_MAX_FRAME_SIZE),
writer: FrameWriter::new(DEFAULT_MAX_FRAME_SIZE),
out: Vec::new(),
encoder: Encoder::new(4096),
request_decoder: HpackDecoder::new(4096),
peer: PeerSettings::default(),
local: PeerSettings::default(),
preface_timeout,
streams: FxHashMap::default(),
conn_window: DEFAULT_INITIAL_WINDOW_SIZE as i64,
closed_streams: FxHashSet::default(),
opts: ConnectionOptions::default(),
wake_tx: None,
complete_blocks: Vec::new(),
drain_ids: Vec::new(),
highest_stream_id: 0,
closing: false,
graceful: false,
graceful_last_stream: 0,
shutdown: None,
date_cache: Arc::new(DateCache::new()),
}
}
#[inline]
pub fn with_shutdown(mut self, token: CancellationToken) -> Self {
self.shutdown = Some(token);
self
}
#[inline]
pub async fn drive(self) -> std::io::Result<()> {
self.handle(
Arc::new(|_| std::future::pending::<Result<Response<Incoming>, std::io::Error>>()),
ConnectionOptions::default(),
)
.await
}
#[inline]
pub async fn handle<F, Fut, ResB, ResBE, ResE>(
mut self,
request_fn: Arc<F>,
options: ConnectionOptions,
) -> std::io::Result<()>
where
F: Fn(Request<Incoming>) -> Fut + 'static,
Fut: Future<Output = Result<Response<ResB>, ResE>> + 'static,
ResB: Body<Data = Bytes, Error = ResBE> + Unpin + 'static,
ResBE: std::error::Error + 'static,
ResE: std::error::Error + 'static,
{
self.opts = options;
self.request_decoder
.set_max_header_list_size(self.opts.max_header_list_size as usize);
self.decoder
.set_max_frame_size(self.opts.max_frame_size as usize);
self.writer.max_frame_size = self.opts.max_frame_size as usize;
self.peer.max_frame_size = self.opts.max_frame_size as usize;
self.conn_window = self.opts.initial_connection_window_size as i64;
match self.read_preface().await? {
None => return Ok(()), Some(false) => {
self.goaway(Reason::ProtocolError, b"invalid connection preface");
self.flush().await?;
return Ok(());
}
Some(true) => {}
}
self.writer.write_settings(
&mut self.out,
&[
Setting {
id: 0x03,
value: self.opts.max_concurrent_streams,
},
Setting {
id: 0x04,
value: self.opts.initial_stream_window_size,
},
Setting {
id: 0x05,
value: self.opts.max_frame_size,
},
Setting {
id: 0x08,
value: if self.opts.enable_connect_protocol {
1
} else {
0
},
},
],
);
self.flush().await?;
let (wake_tx, wake_rx) = kanal::bounded_async(1);
self.wake_tx = Some(wake_tx);
let mut buf = [0u8; 8192];
let mut peer_goaway = false;
while !peer_goaway && !(self.graceful && self.streams.is_empty()) {
let wake_recv = wake_rx.recv().fuse();
let read = tokio::io::AsyncReadExt::read(&mut self.io, &mut buf).fuse();
let shutdown_token = self.shutdown.clone();
let shutdown_fut: Pin<Box<dyn futures_util::future::FusedFuture<Output = ()> + Send>> =
match &shutdown_token {
Some(token) => Box::pin(token.cancelled().fuse()),
None => Box::pin(futures_util::future::pending().fuse()),
};
pin_mut!(wake_recv);
pin_mut!(read);
pin_mut!(shutdown_fut);
let mut idle: Pin<Box<dyn futures_util::future::FusedFuture<Output = ()>>> =
match self.opts.idle_timeout {
Some(d) => Box::pin(
vibeio::time::timeout(d, futures_util::future::pending::<()>())
.map(|_| ())
.fuse(),
),
None => Box::pin(futures_util::future::pending::<()>().fuse()),
};
futures_util::select! {
n = read => {
let n = match n {
Ok(n) => n,
Err(e) if self.streams.is_empty()
&& matches!(
e.kind(),
std::io::ErrorKind::BrokenPipe
| std::io::ErrorKind::ConnectionReset
| std::io::ErrorKind::ConnectionAborted
| std::io::ErrorKind::UnexpectedEof
) => {
return Ok(())
}
Err(e) => Err(e)?
};
if n == 0 {
break; }
self.decoder.extend(&buf[..n]);
peer_goaway = self.process_frames(&request_fn).await?;
self.drain_outbound();
self.flush().await?;
}
_ = wake_recv => {
self.drain_outbound();
self.flush().await?;
}
_ = shutdown_fut => {
self.begin_graceful_shutdown();
self.flush().await?;
}
_ = idle => {
self.begin_graceful_shutdown();
self.flush().await?;
break;
}
}
}
if self.graceful {
self.finish_graceful_shutdown();
}
self.flush().await?;
Ok(())
}
#[inline]
async fn read_preface(&mut self) -> std::io::Result<Option<bool>> {
let mut magic = [0u8; CLIENT_PREFACE.len()];
match self.preface_timeout {
Some(timeout) => {
match vibeio::time::timeout(
timeout,
tokio::io::AsyncReadExt::read_exact(&mut self.io, &mut magic),
)
.await
{
Ok(result) => {
result?;
}
Err(_elapsed) => return Ok(None),
}
}
None => {
tokio::io::AsyncReadExt::read_exact(&mut self.io, &mut magic).await?;
}
}
Ok(Some(magic == CLIENT_PREFACE))
}
#[inline]
async fn process_frames<F, Fut, ResB, ResBE, ResE>(
&mut self,
request_fn: &Arc<F>,
) -> std::io::Result<bool>
where
F: Fn(Request<Incoming>) -> Fut + 'static,
Fut: Future<Output = Result<Response<ResB>, ResE>> + 'static,
ResB: Body<Data = Bytes, Error = ResBE> + Unpin + 'static,
ResBE: std::error::Error + 'static,
ResE: std::error::Error + 'static,
{
loop {
let frame = match self.decoder.next_frame() {
Ok(Some(frame)) => frame,
Ok(None) => return Ok(false),
Err(error) => {
self.goaway(error.reason, b"frame error");
self.flush().await?;
return Ok(true);
}
};
match frame {
Frame::Settings {
ack: false,
settings,
} => {
self.apply_peer_settings(&settings);
self.writer.write_settings_ack(&mut self.out);
}
Frame::Settings { ack: true, .. } => {}
Frame::Ping {
ack: false,
payload,
} => {
self.writer.write_ping_ack(&mut self.out, &payload);
}
Frame::Ping { ack: true, .. } => {}
Frame::GoAway { .. } => return Ok(true),
Frame::Headers {
stream_id,
end_stream,
end_headers,
block,
..
} => {
self.handle_headers_frame(stream_id, end_stream, end_headers, &block);
}
Frame::Continuation {
stream_id,
end_headers,
block,
} => {
self.handle_continuation(stream_id, end_headers, &block);
}
Frame::Data {
stream_id,
end_stream,
data,
} => {
self.handle_data_frame(stream_id, end_stream, data).await;
}
Frame::Reset {
stream_id,
error_code,
} => {
self.handle_reset_frame(stream_id, error_code);
}
Frame::Priority { .. } => {}
Frame::WindowUpdate {
stream_id,
increment,
} => self.handle_window_update(stream_id, increment),
Frame::PushPromise { .. } => {
self.goaway(Reason::ProtocolError, b"push promise to server");
}
Frame::Unknown { .. } => {}
}
if let Some(id) = self.take_complete_block() {
self.finalize_field_block(id, request_fn).await;
}
if self.closing {
self.flush().await?;
return Ok(true);
}
}
}
}
struct ConnBody {
inner: Pin<Box<dyn Body<Data = Bytes, Error = std::io::Error>>>,
}
impl ConnBody {
#[inline]
fn new<ResB, ResBE>(body: ResB) -> Self
where
ResB: Body<Data = Bytes, Error = ResBE> + Unpin + 'static,
ResBE: std::error::Error + 'static,
{
ConnBody {
inner: Box::pin(BodyAdapter(Some(Box::pin(body)))),
}
}
}
impl Body for ConnBody {
type Data = Bytes;
type Error = std::io::Error;
#[inline]
fn poll_frame(
mut self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
self.inner.as_mut().poll_frame(cx)
}
#[inline]
fn size_hint(&self) -> http_body::SizeHint {
self.inner.size_hint()
}
}
#[inline]
fn e2io<E: std::fmt::Display>(e: E) -> std::io::Error {
#[derive(Debug)]
struct Msg(String);
impl std::fmt::Display for Msg {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
impl std::error::Error for Msg {}
std::io::Error::other(Msg(format!("{e}")))
}
struct BodyAdapter<ResB>(Option<Pin<Box<ResB>>>);
impl<ResB, ResBE> Body for BodyAdapter<ResB>
where
ResB: Body<Data = Bytes, Error = ResBE>,
ResBE: std::error::Error + 'static,
{
type Data = Bytes;
type Error = std::io::Error;
#[inline]
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
let this = self.get_mut();
let Some(inner) = this.0.as_mut() else {
return Poll::Ready(None);
};
match inner.as_mut().poll_frame(cx) {
Poll::Ready(Some(Ok(frame))) => Poll::Ready(Some(Ok(frame))),
Poll::Ready(Some(Err(error))) => Poll::Ready(Some(Err(e2io(error)))),
Poll::Ready(None) => {
this.0 = None;
Poll::Ready(None)
}
Poll::Pending => Poll::Pending,
}
}
#[inline]
fn size_hint(&self) -> http_body::SizeHint {
match &self.0 {
Some(body) => body.size_hint(),
None => http_body::SizeHint::default(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum StreamDataState {
Idle,
Closed,
Bad,
Gone,
Ok,
}
mod handlers;
#[cfg(test)]
mod tests;