Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 94 additions & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ use crate::codec::{Codec, SendError, UserError};
use crate::ext::Protocol;
use crate::frame::{Headers, Pseudo, Reason, Settings, StreamId};
use crate::proto::{self, Error};
use crate::{FlowControl, PingPong, RecvStream, SendStream};
use crate::{FlowControl, PeerSettings, PingPong, RecvStream, SendStream};

use bytes::{Buf, Bytes};
use http::{uri, HeaderMap, Method, Request, Response, Version};
Expand Down Expand Up @@ -540,6 +540,42 @@ where
})
}

/// Polls for the `SETTINGS` parameters the server peer sent.
///
/// A client is permitted to send requests without waiting for the peer's
/// `SETTINGS` ([RFC 9113 Section 3.4][1]), so a fresh connection has not
/// seen them. This returns `Pending` until that frame has been received
/// and acknowledged, and errors if the connection ends first.
///
/// [1]: https://datatracker.ietf.org/doc/html/rfc9113#section-3.4
pub fn poll_peer_settings(&self, cx: &Context) -> Poll<Result<PeerSettings, crate::Error>> {
self.inner.poll_peer_settings(cx)
}

/// Waits for the `SETTINGS` parameters the server peer sent.
///
/// The `async` form of [`poll_peer_settings`].
///
/// # Examples
///
/// ```
/// # use bytes::Bytes;
/// # use h2::client::SendRequest;
/// # async fn doc(client: SendRequest<Bytes>) -> Result<(), h2::Error> {
/// let settings = client.peer_settings().await?;
///
/// if settings.get(0x4d44) == Some(1) {
/// // the peer advertised whatever 0x4d44 means to your extension
/// }
/// # Ok(())
/// # }
/// ```
///
/// [`poll_peer_settings`]: SendRequest::poll_peer_settings
pub async fn peer_settings(&self) -> Result<PeerSettings, crate::Error> {
crate::poll_fn(|cx| self.poll_peer_settings(cx)).await
}

/// Returns whether the [extended CONNECT protocol][1] is enabled or not.
///
/// This setting is configured by the server peer by sending the
Expand Down Expand Up @@ -707,6 +743,63 @@ impl Builder {
self
}

/// Sets the `SETTINGS` parameter `id` to `value`.
///
/// [RFC 9113 Section 5.5][1] permits an extension to define new settings
/// and makes using one safe without prior arrangement: implementations
/// "MUST ignore unknown or unsupported values in all extensible protocol
/// elements". So an identifier h2 knows nothing about is sent unexamined,
/// and is safe to send to a peer that knows nothing about it either.
/// Identifiers are allocated in the IANA ["HTTP/2 Settings" registry][reg];
/// h2 carries the number and does not define what it means.
///
/// An identifier h2 does define is routed to the setting it names and
/// checked the same way, so `setting(4, n)` is another spelling of
/// [`initial_window_size`](Builder::initial_window_size) rather than a
/// second copy of it, and one identifier can never be sent twice in one
/// frame. What the peer sent is read with [`SendRequest::peer_settings`].
///
/// # Examples
///
/// ```
/// # use tokio::io::{AsyncRead, AsyncWrite};
/// # use h2::client::*;
/// # use bytes::Bytes;
/// #
/// # async fn doc<T: AsyncRead + AsyncWrite + Unpin>(my_io: T)
/// # -> Result<((SendRequest<Bytes>, Connection<T, Bytes>)), h2::Error>
/// # {
/// // An extension names its own identifiers; h2 only carries them.
/// const ENABLE_METADATA: u16 = 0x4d44;
///
/// let client_fut = Builder::new()
/// .setting(ENABLE_METADATA, 1)
/// .handshake(my_io);
/// # client_fut.await
/// # }
/// # pub fn main() {}
/// ```
///
/// # Panics
///
/// Panics on a value the method named for that setting would panic on:
/// `MAX_FRAME_SIZE` (`5`) outside the range
/// [`Builder::max_frame_size`] accepts.
///
/// Panics if twelve identifiers h2 does not itself define are already set
/// and this is not one of them, since a setting a caller asked for must not
/// be quietly left unsent. The twelve are what a `SETTINGS` frame has to
/// give here once room is reserved for every identifier h2 defines, so
/// those can never panic.
///
/// [1]: https://datatracker.ietf.org/doc/html/rfc9113#section-5.5
/// [reg]: https://www.iana.org/assignments/http2-parameters/http2-parameters.xhtml
/// [`SendRequest::peer_settings`]: SendRequest::peer_settings
pub fn setting(&mut self, id: u16, value: u32) -> &mut Self {
self.settings.set(id, Some(value));
self
}

/// Indicates the initial window size (in octets) for connection-level flow control
/// for received data.
///
Expand Down
33 changes: 33 additions & 0 deletions src/ext.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,37 @@
//! Extensions specific to the HTTP/2 protocol.
//!
//! [RFC 9113 Section 5.5][ext] permits an extension to define new frame types,
//! new settings, or new error codes, and obliges an endpoint to ignore what it
//! does not understand, which is what makes an extension point usable
//! "without prior arrangement or negotiation". h2 implements no extension. What
//! it offers is the means to carry one, and this module is only part of that:
//!
//! - **`SETTINGS` parameters.** Identifiers come from the IANA ["HTTP/2
//! Settings" registry][reg]; h2 carries the number without defining it.
//! [`client::Builder::setting`] and
//! [`server::Builder::setting`] send one by identifier;
//! [`SendRequest::peer_settings`] and [`server::Connection::peer_settings`]
//! read back what the peer sent. h2 applies no rule to an identifier it does
//! not define, and rejects no value of one.
//! - **Error codes.** [`Reason`] is a `u32` either way, so a code this crate
//! does not name is carried verbatim.
//! - **Frame types.** A frame whose type h2 does not know is discarded, as 5.5
//! requires. Sending one is not supported.
//!
//! [`Protocol`] below is a different thing: not an HTTP/2 extension point, but
//! the `:protocol` pseudo-header of [RFC 8441][8441], the extension that lets
//! a CONNECT name another protocol to carry on its stream. h2 checks the name
//! is UTF-8 and does not otherwise interpret it, so a protocol h2 has never
//! heard of works without a change here.
//!
//! [ext]: https://datatracker.ietf.org/doc/html/rfc9113#section-5.5
//! [reg]: https://www.iana.org/assignments/http2-parameters/http2-parameters.xhtml
//! [8441]: https://datatracker.ietf.org/doc/html/rfc8441#section-4
//! [`client::Builder::setting`]: crate::client::Builder::setting
//! [`server::Builder::setting`]: crate::server::Builder::setting
//! [`SendRequest::peer_settings`]: crate::client::SendRequest::peer_settings
//! [`server::Connection::peer_settings`]: crate::server::Connection::peer_settings
//! [`Reason`]: crate::Reason

use crate::hpack::BytesStr;

Expand Down
1 change: 1 addition & 0 deletions src/frame/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub use self::priority::{Priority, StreamDependency};
pub use self::reason::Reason;
pub use self::reset::Reset;
pub use self::settings::Settings;
pub(crate) use self::settings::MAX_SETTINGS;
pub use self::stream_id::{StreamId, StreamIdOverflow};
pub use self::window_update::WindowUpdate;

Expand Down
Loading