use std::{
fmt, ops,
pin::{Pin, pin},
sync::Arc,
};
use futures::{
FutureExt, Sink, SinkExt, StreamExt,
future::{self, BoxFuture},
never::Never,
stream::{self, FusedStream},
};
use snafu::Snafu;
use tokio::sync::Mutex as AsyncMutex;
use tokio_util::task::AbortOnDropHandle;
use tracing::Instrument;
use crate::{
buflist::BufList,
codec::{
DecodeExt, EncodeExt, ErasedPeekableBiStream, ErasedPeekableUniStream, Feed, SinkWriter,
StreamReader,
},
connection::{ConnectionGoaway, ConnectionState, LifecycleExt, StreamError},
dhttp::{
frame::{Frame, stream::FrameStream},
goaway::Goaway,
settings::Settings,
stream::UnidirectionalStream,
},
error::{
Code, H3CriticalStreamClosed, H3FrameUnexpected, H3IdError, H3MissingSettings,
H3StreamCreationError,
},
message::stream::guard,
protocol::{ProductProtocol, Protocol, Protocols, StreamVerdict},
quic::{self, CancelStreamExt, ConnectionError, GetStreamIdExt, StopStreamExt},
util::{ring_channel::RingChannel, set_once::SetOnce, watch::Watch},
varint::VarInt,
};
type BoxSink<'a, Item, Err> = Pin<Box<dyn Sink<Item, Error = Err> + Send + 'a>>;
#[derive(Debug)]
pub struct DHttpState {
pub local_settings: Arc<Settings>,
pub peer_settings: SetOnce<Arc<Settings>>,
pub local_goaway: Watch<Goaway>,
pub peer_goaway: Watch<Goaway>,
pub max_initialized_stream_id: Watch<VarInt>,
pub max_received_stream_id: Watch<VarInt>,
}
#[cfg(test)]
mod tests {
use std::{
collections::hash_map::DefaultHasher,
hash::{Hash, Hasher},
pin::Pin,
sync::Arc,
task::{Context, Poll},
};
use bytes::Bytes;
use futures::{Sink, Stream};
use super::*;
use crate::{
codec::{BoxReadStream, BoxWriteStream, SinkWriter, StreamReader},
connection::{ConnectionState, tests::MockConnection},
dhttp::settings::{EnableConnectProtocol, Settings},
protocol::Protocols,
quic::{self, GetStreamIdExt},
};
#[derive(Debug)]
struct TestReadStream {
stream_id: VarInt,
}
impl quic::GetStreamId for TestReadStream {
fn poll_stream_id(
self: Pin<&mut Self>,
_cx: &mut Context,
) -> Poll<Result<VarInt, quic::StreamError>> {
Poll::Ready(Ok(self.get_mut().stream_id))
}
}
impl quic::StopStream for TestReadStream {
fn poll_stop(
self: Pin<&mut Self>,
_cx: &mut Context,
_code: VarInt,
) -> Poll<Result<(), quic::StreamError>> {
Poll::Ready(Ok(()))
}
}
impl Stream for TestReadStream {
type Item = Result<Bytes, quic::StreamError>;
fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
Poll::Ready(None)
}
}
#[derive(Debug)]
struct TestWriteStream {
stream_id: VarInt,
}
impl quic::GetStreamId for TestWriteStream {
fn poll_stream_id(
self: Pin<&mut Self>,
_cx: &mut Context,
) -> Poll<Result<VarInt, quic::StreamError>> {
Poll::Ready(Ok(self.get_mut().stream_id))
}
}
impl quic::CancelStream for TestWriteStream {
fn poll_cancel(
self: Pin<&mut Self>,
_cx: &mut Context,
_code: VarInt,
) -> Poll<Result<(), quic::StreamError>> {
Poll::Ready(Ok(()))
}
}
impl Sink<Bytes> for TestWriteStream {
type Error = quic::StreamError;
fn poll_ready(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn start_send(self: Pin<&mut Self>, _item: Bytes) -> Result<(), Self::Error> {
Ok(())
}
fn poll_flush(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn poll_close(
self: Pin<&mut Self>,
_cx: &mut Context<'_>,
) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
}
fn test_erased_streams(stream_id: u32) -> (GuardedStreamReader, GuardedStreamWriter) {
let stream_id = VarInt::from_u32(stream_id);
let reader =
StreamReader::new(guard::GuardedQuicReader::new(
Box::pin(TestReadStream { stream_id }) as BoxReadStream,
));
let writer =
SinkWriter::new(guard::GuardedQuicWriter::new(
Box::pin(TestWriteStream { stream_id }) as BoxWriteStream,
));
(reader, writer)
}
fn test_connection_state() -> ConnectionState<MockConnection> {
let quic = Arc::new(MockConnection::new());
let erased_connection: Arc<dyn quic::DynConnection> = quic.clone();
let mut protocols = Protocols::new();
protocols.insert(DHttpProtocol::new_for_test(erased_connection));
ConnectionState::new_for_test(quic, Arc::new(protocols))
}
fn hash_of<T: Hash>(t: &T) -> u64 {
let mut h = DefaultHasher::new();
t.hash(&mut h);
h.finish()
}
#[test]
fn dhttp_factory_same_settings_equal_hash() {
let s1 = Arc::new(Settings::default());
let s2 = Arc::new(Settings::default());
let f1 = DHttpProtocolFactory::new(s1);
let f2 = DHttpProtocolFactory::new(s2);
assert_eq!(hash_of(&f1), hash_of(&f2));
}
#[test]
fn dhttp_factory_different_settings_different_hash() {
let s_default = Settings::default();
let mut s_other = Settings::default();
s_other.set(EnableConnectProtocol::setting(true));
let f1 = DHttpProtocolFactory::new(Arc::new(s_default));
let f2 = DHttpProtocolFactory::new(Arc::new(s_other));
assert_ne!(hash_of(&f1), hash_of(&f2));
}
#[test]
fn dhttp_factory_same_settings_eq() {
let s1 = Arc::new(Settings::default());
let s2 = Arc::new(Settings::default());
let f1 = DHttpProtocolFactory::new(s1);
let f2 = DHttpProtocolFactory::new(s2);
assert_eq!(f1, f2);
}
#[test]
fn dhttp_factory_different_settings_not_eq() {
let s_default = Settings::default();
let mut s_other = Settings::default();
s_other.set(EnableConnectProtocol::setting(true));
let f1 = DHttpProtocolFactory::new(Arc::new(s_default));
let f2 = DHttpProtocolFactory::new(Arc::new(s_other));
assert_ne!(f1, f2);
}
#[test]
fn initialized_stream_updates_initialized_only() {
let state = DHttpState::new(Arc::new(Settings::default()));
let stream_id = VarInt::from_u32(7);
state
.register_initialized_stream(stream_id)
.expect("initialized stream should be accepted");
assert_eq!(state.max_initialized_stream_id.peek(), Some(stream_id));
assert_eq!(state.max_received_stream_id.peek(), None);
}
#[test]
fn accepted_stream_updates_received_only() {
let state = DHttpState::new(Arc::new(Settings::default()));
let stream_id = VarInt::from_u32(9);
state
.register_accepted_stream(stream_id)
.expect("accepted stream should be accepted");
assert_eq!(state.max_received_stream_id.peek(), Some(stream_id));
assert_eq!(state.max_initialized_stream_id.peek(), None);
}
#[test]
fn initialized_stream_rejected_after_peer_goaway_latched() {
let state = DHttpState::new(Arc::new(Settings::default()));
state
.apply_peer_goaway(Goaway::new(VarInt::from_u32(13)))
.expect("first peer goaway should be accepted");
let error = state
.register_initialized_stream(VarInt::from_u32(11))
.expect_err("initialized stream must be rejected after peer goaway");
assert_eq!(error, ConnectionGoaway::Peer);
}
#[test]
fn apply_peer_goaway_rejects_increasing_stream_id_ordering() {
let state = DHttpState::new(Arc::new(Settings::default()));
state
.apply_peer_goaway(Goaway::new(VarInt::from_u32(20)))
.expect("first peer goaway should be accepted");
let error = state
.apply_peer_goaway(Goaway::new(VarInt::from_u32(21)))
.expect_err("increasing peer goaway stream id must be rejected");
assert!(matches!(
error,
StreamError::Code {
source
} if source.code() == Code::H3_ID_ERROR
));
}
#[tokio::test]
async fn peer_goaway_covers_resolves_immediately_when_already_covered() {
let state = DHttpState::new(Arc::new(Settings::default()));
let goaway = Goaway::new(VarInt::from_u32(8));
state
.apply_peer_goaway(goaway)
.expect("peer goaway should be accepted");
let observed = state.peer_goaway_covers(VarInt::from_u32(10)).await;
assert_eq!(observed, goaway);
}
#[tokio::test]
async fn peer_goaway_covers_waits_until_covered_boundary() {
let state = Arc::new(DHttpState::new(Arc::new(Settings::default())));
let waiter_state = state.clone();
let waiter =
tokio::spawn(
async move { waiter_state.peer_goaway_covers(VarInt::from_u32(10)).await },
);
tokio::task::yield_now().await;
state
.apply_peer_goaway(Goaway::new(VarInt::from_u32(12)))
.expect("non-covering goaway should be accepted");
tokio::task::yield_now().await;
assert!(!waiter.is_finished());
let covering = Goaway::new(VarInt::from_u32(9));
state
.apply_peer_goaway(covering)
.expect("covering goaway should be accepted");
assert_eq!(waiter.await.expect("join should succeed"), covering);
}
#[tokio::test]
async fn inbound_accept_not_blocked_by_peer_goaway_signal() {
let state = test_connection_state();
state
.dhttp()
.peer_goaway
.set(Goaway::new(VarInt::from_u32(4)));
_ = state
.dhttp()
.unresolved_request_streams
.send(test_erased_streams(6));
let (mut reader, _writer) = state
.accept_raw_message_stream()
.await
.expect("peer goaway must not hard-stop inbound accept");
assert_eq!(
reader.stream_id().await.expect("stream id"),
VarInt::from_u32(6)
);
}
#[tokio::test]
async fn inbound_accept_rejects_stream_at_or_above_local_goaway_boundary() {
let state = test_connection_state();
state
.dhttp()
.local_goaway
.set(Goaway::new(VarInt::from_u32(9)));
_ = state
.dhttp()
.unresolved_request_streams
.send(test_erased_streams(9));
let error = state
.accept_raw_message_stream()
.await
.err()
.expect("stream at local goaway boundary should be rejected");
assert!(matches!(
error,
AcceptRawMessageStreamError::Goaway {
source: ConnectionGoaway::Local
}
));
}
#[tokio::test]
async fn inbound_accept_allows_stream_below_local_goaway_boundary() {
let state = test_connection_state();
state
.dhttp()
.local_goaway
.set(Goaway::new(VarInt::from_u32(7)));
_ = state
.dhttp()
.unresolved_request_streams
.send(test_erased_streams(3));
let (mut reader, _writer) = state
.accept_raw_message_stream()
.await
.expect("stream below local goaway boundary should be accepted");
assert_eq!(
reader.stream_id().await.expect("stream id"),
VarInt::from_u32(3)
);
}
#[tokio::test]
async fn queued_streams_are_drained_with_local_goaway_boundary_split() {
let state = test_connection_state();
state
.dhttp()
.local_goaway
.set(Goaway::new(VarInt::from_u32(6)));
_ = state
.dhttp()
.unresolved_request_streams
.send(test_erased_streams(4));
_ = state
.dhttp()
.unresolved_request_streams
.send(test_erased_streams(8));
let (mut reader, _writer) = state
.accept_raw_message_stream()
.await
.expect("stream below boundary should be delivered first");
assert_eq!(
reader.stream_id().await.expect("stream id"),
VarInt::from_u32(4)
);
let error = state
.accept_raw_message_stream()
.await
.err()
.expect("stream at or above boundary should be rejected");
assert!(matches!(
error,
AcceptRawMessageStreamError::Goaway {
source: ConnectionGoaway::Local
}
));
}
#[tokio::test]
async fn latched_local_goaway_after_watcher_creation_still_enforces_boundary() {
let state = test_connection_state();
let wait_state = state.clone();
let accept_task = tokio::spawn(async move { wait_state.accept_raw_message_stream().await });
tokio::task::yield_now().await;
state
.dhttp()
.local_goaway
.set(Goaway::new(VarInt::from_u32(10)));
_ = state
.dhttp()
.unresolved_request_streams
.send(test_erased_streams(10));
let error = accept_task
.await
.expect("join should succeed")
.err()
.expect("boundary should apply even if goaway was set after accept started");
assert!(matches!(
error,
AcceptRawMessageStreamError::Goaway {
source: ConnectionGoaway::Local
}
));
}
}
impl DHttpState {
fn new(local_settings: Arc<Settings>) -> Self {
Self {
local_settings,
peer_settings: SetOnce::new(),
local_goaway: Watch::new(),
peer_goaway: Watch::new(),
max_initialized_stream_id: Watch::new(),
max_received_stream_id: Watch::new(),
}
}
pub(crate) fn begin_local_goaway(&self) -> Goaway {
let mut local_goaway = self.local_goaway.lock();
let max_received_stream_id = self.max_received_stream_id.lock();
let max_received_stream_id = max_received_stream_id
.get()
.copied()
.unwrap_or(VarInt::from_u32(0));
let goaway = Goaway::new(max_received_stream_id);
local_goaway.set(goaway);
goaway
}
pub(crate) fn apply_peer_goaway(&self, goaway: Goaway) -> Result<(), StreamError> {
let mut peer_goaway = self.peer_goaway.lock();
if let Some(previous_goaway) = peer_goaway.get().copied()
&& goaway.stream_id() > previous_goaway.stream_id()
{
return Err(H3IdError::GoawayStreamIdOrdering.into());
}
tracing::debug!(
previous_stream_id = ?peer_goaway.get().map(|item| item.stream_id()),
new_stream_id = ?goaway.stream_id(),
"Received peer GOAWAY"
);
peer_goaway.set(goaway);
Ok(())
}
async fn handle_control_stream<S: quic::ReadStream>(
&self,
stream: StreamReader<S>,
) -> Result<Never, StreamError> {
let mut control_frame_stream = pin!(FrameStream::new(stream));
let mut settings_frame = control_frame_stream
.as_mut()
.next_frame()
.await
.ok_or(H3CriticalStreamClosed::Control)??;
if settings_frame.r#type() != Frame::SETTINGS_FRAME_TYPE {
return Err(H3MissingSettings.into());
}
let settings = Arc::new(settings_frame.decode_one::<Settings>().await?);
tracing::debug!(?settings, "received remote settings");
self.peer_settings
.set(settings)
.expect("handle control task set once");
loop {
let mut frame = control_frame_stream
.as_mut()
.next_unreserved_frame()
.await
.ok_or(H3CriticalStreamClosed::Control)??;
if frame.r#type() == Frame::SETTINGS_FRAME_TYPE {
return Err(H3FrameUnexpected::DuplicateSettings.into());
} else if frame.r#type() == Frame::GOAWAY_FRAME_TYPE {
let goaway = frame.decode_one::<Goaway>().await?;
self.apply_peer_goaway(goaway)?;
} else {
}
}
}
pub(crate) fn register_initialized_stream(
&self,
stream_id: VarInt,
) -> Result<(), ConnectionGoaway> {
let peer_goaway = self.peer_goaway.lock();
if peer_goaway.get().is_some() {
return Err(ConnectionGoaway::Peer);
}
let mut max_initialized_stream_id = self.max_initialized_stream_id.lock();
max_initialized_stream_id.set(
max_initialized_stream_id
.get()
.map_or(stream_id, |current| *current.max(&stream_id)),
);
Ok(())
}
pub(crate) fn register_accepted_stream(
&self,
stream_id: VarInt,
) -> Result<(), ConnectionGoaway> {
let local_goaway = self.local_goaway.lock();
if let Some(goaway) = local_goaway.get().copied()
&& stream_id >= goaway.stream_id()
{
return Err(ConnectionGoaway::Local);
}
let mut max_received_stream_id = self.max_received_stream_id.lock();
max_received_stream_id.set(
max_received_stream_id
.get()
.map_or(stream_id, |current| *current.max(&stream_id)),
);
Ok(())
}
pub(crate) async fn peer_goaway_covers(&self, stream_id: VarInt) -> Goaway {
if let Some(goaway) = self.peer_goaway.peek()
&& stream_id >= goaway.stream_id()
{
return goaway;
}
let effective_peer_goaway = self
.peer_goaway
.watch()
.filter(move |goaway| future::ready(stream_id >= goaway.stream_id()));
let mut effective_peer_goaway = pin!(effective_peer_goaway.fuse());
effective_peer_goaway.select_next_some().await
}
}
type FrameSink = Feed<BoxSink<'static, Frame<BufList>, StreamError>, Frame<BufList>>;
pub type BoxDynQuicStreamReader = guard::GuardedQuicReader;
pub type BoxDynQuicStreamWriter = guard::GuardedQuicWriter;
type GuardedStreamReader = StreamReader<BoxDynQuicStreamReader>;
type GuardedStreamWriter = SinkWriter<BoxDynQuicStreamWriter>;
pub struct DHttpProtocol {
pub state: Arc<DHttpState>,
pub connection: Arc<dyn quic::DynConnection>,
pub control_stream: AsyncMutex<FrameSink>,
handle_control_stream: SetOnce<AbortOnDropHandle<()>>,
unresolved_request_streams: RingChannel<(GuardedStreamReader, GuardedStreamWriter)>,
}
impl ops::Deref for DHttpProtocol {
type Target = Arc<DHttpState>;
fn deref(&self) -> &Self::Target {
&self.state
}
}
impl std::fmt::Debug for DHttpProtocol {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("DHttpLayer")
.field("state", &self.state)
.field("control_stream", &"...")
.finish()
}
}
impl DHttpProtocol {
pub async fn max_unresolved_request_streams(&self) -> usize {
self.unresolved_request_streams.capacity()
}
async fn accept_uni(
&self,
mut stream: ErasedPeekableUniStream,
) -> Result<StreamVerdict<ErasedPeekableUniStream>, StreamError> {
let Ok(stream_type) = stream.decode_one::<VarInt>().await else {
return Ok(StreamVerdict::Passed(stream));
};
let raw = stream_type.into_inner();
if stream_type == UnidirectionalStream::CONTROL_STREAM_TYPE {
let state = self.state.clone();
let connection = self.connection.clone();
let init_handle_control_task = || {
let handle_control = async move {
tokio::select! {
biased;
Err(stream_error) = state.handle_control_stream(stream.into_stream_reader()) => {
connection.handle_stream_error(stream_error).await;
}
_connection_error = connection.closed() => {
}
}
};
AbortOnDropHandle::new(tokio::spawn(handle_control.in_current_span()))
};
if self
.handle_control_stream
.set_with(init_handle_control_task)
.is_err()
{
return Err(H3StreamCreationError::DuplicateControlStream.into());
}
Ok(StreamVerdict::Accepted)
} else if stream_type == UnidirectionalStream::PUSH_STREAM_TYPE {
Err(H3IdError::PushIdExceedsLimit.into())
} else if raw >= 0x21 && (raw - 0x21).is_multiple_of(0x1f) {
stream.stop(Code::H3_NO_ERROR.into_inner()).await?;
Ok(StreamVerdict::Accepted)
} else {
Ok(StreamVerdict::Passed(stream))
}
}
async fn accept_bi(
&self,
(mut reader, writer): ErasedPeekableBiStream,
) -> Result<StreamVerdict<ErasedPeekableBiStream>, StreamError> {
let frame_type = match reader.decode_one::<VarInt>().await {
Ok(v) => v,
Err(_) => {
return Ok(StreamVerdict::Passed((reader, writer)));
}
};
if Self::is_http3_frame_type(frame_type) {
Pin::new(&mut reader).reset();
let reader = reader
.into_stream_reader()
.map_stream(guard::GuardedQuicReader::new);
let writer = writer.map_sink(guard::GuardedQuicWriter::new);
let item = (reader, writer);
if let Some(mut unresolved) = self.unresolved_request_streams.send(item) {
let code = Code::H3_REQUEST_REJECTED.into_inner();
_ = tokio::join!(unresolved.0.stop(code), unresolved.1.cancel(code));
}
Ok(StreamVerdict::Accepted)
} else {
Ok(StreamVerdict::Passed((reader, writer)))
}
}
const fn is_http3_frame_type(frame_type: VarInt) -> bool {
let raw = frame_type.into_inner();
matches!(raw, 0x00 | 0x01 | 0x03 | 0x04 | 0x05 | 0x07 | 0x0d)
|| (raw >= 0x21 && (raw - 0x21).is_multiple_of(0x1f))
}
#[cfg(test)]
pub(crate) fn new_for_test(connection: Arc<dyn quic::DynConnection>) -> Self {
let sink: BoxSink<'static, Frame<BufList>, StreamError> =
Box::pin(futures::sink::drain().sink_map_err(|never| match never {}));
Self {
state: Arc::new(DHttpState::new(Arc::new(Settings::default()))),
connection,
control_stream: AsyncMutex::new(Feed::new(sink)),
handle_control_stream: SetOnce::new(),
unresolved_request_streams: RingChannel::new(32),
}
}
}
impl Protocol for DHttpProtocol {
fn accept_uni<'a>(
&'a self,
stream: ErasedPeekableUniStream,
) -> BoxFuture<'a, Result<StreamVerdict<ErasedPeekableUniStream>, StreamError>> {
Box::pin(self.accept_uni(stream))
}
fn accept_bi<'a>(
&'a self,
stream: ErasedPeekableBiStream,
) -> BoxFuture<'a, Result<StreamVerdict<ErasedPeekableBiStream>, StreamError>> {
Box::pin(self.accept_bi(stream))
}
}
#[derive(Default, Debug, Clone, Hash, PartialEq, Eq)]
pub struct DHttpProtocolFactory {
local_settings: Arc<Settings>,
}
impl fmt::Display for DHttpProtocolFactory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DHTTP/3")
}
}
impl DHttpProtocolFactory {
pub fn new(local_settings: Arc<Settings>) -> Self {
Self { local_settings }
}
pub async fn init<C: quic::Connection>(
&self,
conn: &Arc<C>,
) -> Result<DHttpProtocol, quic::ConnectionError> {
let uni_stream = SinkWriter::new(Box::pin(conn.open_uni().await?));
let mut control_stream = match UnidirectionalStream::initial_control_stream(uni_stream)
.await
{
Ok(stream) => {
let control_frame_sink = stream.into_encode_sink().sink_map_err(
|error: quic::StreamError| match error {
quic::StreamError::Reset { .. } => H3CriticalStreamClosed::Control.into(),
quic_stream_error => quic_stream_error.into(),
},
);
Feed::new(Box::pin(control_frame_sink) as BoxSink<_, _>)
}
Err(stream_error) => {
conn.handle_stream_error(stream_error).await;
return Err(conn.closed().await);
}
};
let Ok(settings_frame) = BufList::new().encode(self.local_settings.as_ref()).await;
match control_stream.send(settings_frame).await {
Ok(()) => (),
Err(stream_error) => {
conn.handle_stream_error(stream_error).await;
return Err(conn.closed().await);
}
}
let connection: Arc<dyn quic::DynConnection> = conn.clone();
Ok(DHttpProtocol {
state: Arc::new(DHttpState::new(self.local_settings.clone())),
connection,
control_stream: AsyncMutex::new(control_stream),
handle_control_stream: SetOnce::new(),
unresolved_request_streams: RingChannel::new(32), })
}
}
impl<C: quic::Connection> ProductProtocol<C> for DHttpProtocolFactory {
type Protocol = DHttpProtocol;
fn init<'a>(
&'a self,
conn: &'a Arc<C>,
layers: &'a Protocols,
) -> BoxFuture<'a, Result<Self::Protocol, ConnectionError>> {
_ = layers;
Box::pin(self.init(conn))
}
}
impl<C: ?Sized> ConnectionState<C> {
#[doc(alias = "http")]
pub fn dhttp(&self) -> &DHttpProtocol {
self.protocol::<DHttpProtocol>()
.expect("DHttpProtocol is always initialized by ConnectionBuilder")
}
pub fn settings(&self) -> Arc<Settings> {
self.dhttp().local_settings.clone()
}
pub fn max_received_stream_id(&self) -> Option<VarInt> {
self.dhttp().max_received_stream_id.peek()
}
pub fn max_initialized_stream_id(&self) -> Option<VarInt> {
self.dhttp().max_initialized_stream_id.peek()
}
pub fn peek_peer_goaway(&self) -> Option<Goaway> {
self.dhttp().peer_goaway.peek()
}
}
impl<C: quic::DynLifecycle + Sync> ConnectionState<C> {
pub async fn peer_settings(
&self,
) -> impl Future<Output = Result<Arc<Settings>, quic::ConnectionError>> + Send + use<'_, C>
{
let error = self.closed();
(self.dhttp().peer_settings.get()).then(|option| match option {
Some(settings) => future::ready(Ok(settings)).left_future(),
None => error.map(Err).right_future(),
})
}
pub fn peer_goawaies(
&self,
) -> impl FusedStream<Item = Result<Goaway, quic::ConnectionError>> + Send + use<'_, C> {
stream::select(
self.dhttp().peer_goaway.watch().map(Ok),
self.closed().map(Err).into_stream(),
)
}
pub async fn goaway(&self) -> Result<(), quic::ConnectionError> {
let send_goaway = async {
let dhttp = self.dhttp();
let mut control_stream = dhttp.control_stream.lock().await;
let Ok(goaway_frame) = BufList::new().encode(dhttp.begin_local_goaway()).await;
control_stream.send(goaway_frame).await
};
if let Err(stream_error) = send_goaway.await {
self.quic().handle_stream_error(stream_error).await;
return Err(self.closed().await);
}
Ok(())
}
}
#[derive(Debug, Snafu, Clone)]
pub enum InitialRawMessageStreamError {
#[snafu(transparent)]
Connection { source: quic::ConnectionError },
#[snafu(transparent)]
ResponseStream { source: quic::StreamError },
#[snafu(transparent)]
Goaway { source: ConnectionGoaway },
}
#[derive(Debug, Snafu, Clone)]
#[snafu(module)]
pub enum AcceptRawMessageStreamError {
#[snafu(transparent)]
Connection { source: quic::ConnectionError },
#[snafu(transparent)]
RequestStream { source: quic::StreamError },
#[snafu(transparent)]
Goaway { source: ConnectionGoaway },
}
impl<C: quic::Lifecycle + quic::ManageStream + Send + Sync> ConnectionState<C> {
pub async fn initial_raw_message_stream(
&self,
) -> Result<(GuardedStreamReader, GuardedStreamWriter), InitialRawMessageStreamError> {
let (reader, writer) = self.open_bi().await?;
let (mut reader, writer) = (Box::pin(reader), Box::pin(writer));
self.dhttp()
.register_initialized_stream(reader.stream_id().await?)?;
Ok((
StreamReader::new(guard::GuardedQuicReader::new(reader)),
SinkWriter::new(guard::GuardedQuicWriter::new(writer)),
))
}
pub async fn accept_raw_message_stream(
&self,
) -> Result<(GuardedStreamReader, GuardedStreamWriter), AcceptRawMessageStreamError> {
let dhttp = self.dhttp();
let (mut reader, mut writer) = tokio::select! {
stream = dhttp.unresolved_request_streams.receive() => stream,
connection_error = self.closed() => return Err(connection_error.into()),
};
let stream_id = reader.stream_id().await?;
match dhttp.register_accepted_stream(stream_id) {
Ok(()) => Ok((reader, writer)),
Err(ConnectionGoaway::Local) => {
let code = Code::H3_REQUEST_REJECTED.into_inner();
_ = tokio::join!(reader.stop(code), writer.cancel(code));
Err(ConnectionGoaway::Local.into())
}
Err(ConnectionGoaway::Peer) => {
unreachable!("inbound acceptance is not gated by peer_goaway")
}
}
}
}