diff --git a/quinn-h3/src/lib.rs b/quinn-h3/src/lib.rs index 86ce7a7ff..573447b59 100644 --- a/quinn-h3/src/lib.rs +++ b/quinn-h3/src/lib.rs @@ -34,46 +34,60 @@ use std::io::ErrorKind; use proto::ErrorCode; +/// A future that resolves when the handshake ends durinng a 0-RTT exchange pub type ZeroRttAccepted = quinn::ZeroRttAccepted; +/// General error enum for this crate #[derive(Debug, Error)] pub enum Error { + /// Cannot make a new request, bescause the connection is closing #[error(display = "Connection is closing, resquest aborted")] Aborted, + /// Protocol violation detected by the internal HTTP/3 protocol state machine #[error(display = "H3 protocol error: {:?}", _0)] Proto(proto::connection::Error), + /// Error occurred at the `QUIC` level #[error(display = "QUIC protocol error: {}", _0)] Quic(quinn::ConnectionError), + /// A `QUIC`-specific read error occurred #[error(display = "QUIC read error: {}", _0)] Read(ReadError), + /// A `QUIC`-specific write error occurred #[error(display = "QUIC write error: {}", _0)] Write(WriteError), + /// Programming error within the crate's code #[error(display = "Internal error: {}", _0)] Internal(String), + /// The peer's behavior was detected as incorrect or malicious #[error(display = "Incorrect peer behavior: {}", _0)] Peer(String), + /// The peer tried to open an unidirectional stream with an unknown type code #[error(display = "unknown stream type {}", _0)] UnknownStream(u64), + /// An IO error occurred #[error(display = "IO error: {}", _0)] Io(std::io::Error), + /// An overflow occurred into the `QPACK` decoder #[error(display = "Overflow max data size")] Overflow, + /// A future has been polled after it was already finished #[error(display = "Polled after finished")] Poll, + /// The peer issued an HTTP/3 error code and an optional text description #[error(display = "Http error: {:?}", _0)] Http(HttpError, Option), } impl Error { - pub fn peer>(msg: T) -> Self { + pub(crate) fn peer>(msg: T) -> Self { Error::Peer(msg.into()) } - pub fn internal>(msg: T) -> Self { + pub(crate) fn internal>(msg: T) -> Self { Error::Internal(msg.into()) } - pub fn try_into_quic(&self) -> Option<&quinn_proto::ConnectionError> { + pub(crate) fn try_into_quic(&self) -> Option<&quinn_proto::ConnectionError> { match self { Error::Quic(e) => Some(e), Error::Write(quinn::WriteError::ConnectionClosed(e)) => Some(e), @@ -159,27 +173,52 @@ impl From for Error { } } +/// Errors defined by the HTTP/3 protocol +/// +/// Read the [`HTTP/3 specification`] for more details. +/// +/// [`HTTP/3 specification`]: https://quicwg.org/base-drafts/draft-ietf-quic-http.html#name-http-3-error-codes #[derive(Debug)] pub enum HttpError { + /// This is used when the connection or stream needs to be closed, but there is no error to signal NoError, + /// Peer violated protocol requirements in a way which doesn't match a more specific error code, or endpoint declines to use the more specific error code GeneralProtocolError, + /// An internal error has occurred in the HTTP stack InternalError, + /// The endpoint detected that its peer created a stream that it will not accept StreamCreationError, + /// A stream required by the connection was closed or reset ClosedCriticalStream, + /// A frame was received which was not permitted in the current state or on the current stream FrameUnexpected, + /// A frame that fails to satisfy layout requirements or with an invalid size was received FrameError, + /// The endpoint detected that its peer is exhibiting a behavior that might be generating excessive load ExcessiveLoad, + /// A Stream ID or Push ID was used incorrectly, such as exceeding a limit, reducing a limit, or being reused IdError, + /// An endpoint detected an error in the payload of a SETTINGS frame SettingsError, + /// No SETTINGS frame was received at the beginning of the control stream MissingSettings, + /// A server rejected a request without performing any application processing RequestRejected, + /// The request or its response (including pushed response) is cancelled RequestCancelled, + /// The client's stream terminated without containing a fully-formed request RequestIncomplete, + /// The connection established in response to a CONNECT request was reset or abnormally closed ConnectError, + /// The requested operation cannot be served over HTTP/3. The peer should retry over HTTP/1.1 VersionFallback, + /// Decompression of a header block failed QpackDecompressionFailed, + /// Error on the encoder stream QpackEncoderStreamError, + /// Error on the decoder stream QpackDecoderStreamError, + /// Unknown error code Unknown(u32), } @@ -210,7 +249,7 @@ impl From for HttpError { } } -/// TLS ALPN value for H3 +/// TLS ALPN value for the HTTP/3 protocol pub const ALPN: &[u8] = b"h3-27"; impl From for (ErrorCode, String, Error) { diff --git a/quinn-h3/src/proto/settings.rs b/quinn-h3/src/proto/settings.rs index 61e83c94b..e39cab3e5 100644 --- a/quinn-h3/src/proto/settings.rs +++ b/quinn-h3/src/proto/settings.rs @@ -36,6 +36,14 @@ impl Codec for SettingId { } } +/// Settings for a HTTP/3 connection +/// +/// The HTTP/3 protocol offers a few settings to configure limits and header encoding +/// parameters of a connection. +/// +/// See the [QPACK] specification for more details. +/// +/// [QPACK]: https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html #[derive(Clone, Debug)] pub struct Settings { max_header_list_size: u64, @@ -56,6 +64,8 @@ impl Default for Settings { impl Settings { /// Create settings with quinn-h3's recomended values + /// + /// Enables `QPACK`. pub fn new() -> Self { Self { max_header_list_size: 0, @@ -64,6 +74,9 @@ impl Settings { } } + /// The maximum number of entries in headers and trailers + /// + /// `0` means infinity. pub fn max_header_list_size(&self) -> u64 { if self.max_header_list_size == 0 { return std::u64::MAX; @@ -71,14 +84,23 @@ impl Settings { self.max_header_list_size } + /// The maximum size for `QPACK` encoding dynamic table + /// + /// `0` means `QPACK` is disabled. pub fn qpack_max_table_capacity(&self) -> u64 { self.qpack_max_table_capacity } + /// The maximum number of request waiting to be decoded with arriving encoder data + /// + /// If `0`, the peer won't send any dynamically encoded headers. pub fn qpack_max_blocked_streams(&self) -> u64 { self.qpack_max_blocked_streams } + /// Set the maximum number of entries in headers and trailers + /// + /// `0` means infinity. pub fn set_max_header_list_size(&mut self, value: u64) -> Result<&mut Self, InvalidValue> { if value > VarInt::MAX.into_inner() as u64 { return Err(InvalidValue(SettingId::QPACK_MAX_TABLE_CAPACITY, value)); @@ -87,6 +109,9 @@ impl Settings { Ok(self) } + /// Set the maximum size for `QPACK` encoding dynamic table + /// + /// If `0`, the peer won't send any dynamically encoded headers. pub fn set_qpack_max_blocked_streams(&mut self, value: u64) -> Result<&mut Self, InvalidValue> { if value > MAX_BLOCKED_STREAMS_MAX { return Err(InvalidValue(SettingId::QPACK_MAX_BLOCKED_STREAMS, value)); @@ -95,6 +120,9 @@ impl Settings { Ok(self) } + /// Set the maximum number of request waiting to be decoded with arriving encoder data + /// + /// Set this to `0` to disable `QPACK`. pub fn set_qpack_max_table_capacity(&mut self, value: u64) -> Result<&mut Self, InvalidValue> { if value > MAX_TABLE_CAPACITY_MAX { return Err(InvalidValue(SettingId::QPACK_MAX_TABLE_CAPACITY, value));