From 78455a4496830e3989b7cf17db203b5554191fc1 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Thu, 11 Jan 2018 15:00:16 -0800 Subject: [PATCH] Add docs for shared types (#216) Add documentation for types shared between the client and server. --- src/client.rs | 4 +- src/error.rs | 27 ++---- src/frame/reason.rs | 14 ++- src/share.rs | 216 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 233 insertions(+), 28 deletions(-) diff --git a/src/client.rs b/src/client.rs index b042f5a..1494989 100644 --- a/src/client.rs +++ b/src/client.rs @@ -884,7 +884,7 @@ impl Builder { self } - /// Set the maximum number of concurrent locally reset streams. + /// Sets the maximum number of concurrent locally reset streams. /// /// When a stream is explicitly reset by either calling /// [`SendResponse::send_reset`] or by dropping a [`SendResponse`] instance @@ -972,7 +972,7 @@ impl Builder { self } - /// Set the first stream ID to something other than 1. + /// Sets the first stream ID to something other than 1. #[cfg(feature = "unstable")] pub fn initial_stream_id(&mut self, stream_id: u32) -> &mut Self { self.stream_id = stream_id.into(); diff --git a/src/error.rs b/src/error.rs index d84f7af..d8a747c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,7 +5,16 @@ use std::{error, fmt, io}; pub use frame::Reason; -/// The error type for HTTP/2 operations +/// Represents HTTP/2.0 operation errors. +/// +/// `Error` covers error cases raised by protocol errors caused by the +/// peer, I/O (transport) errors, and errors caused by the user of the library. +/// +/// If the error was caused by the remote peer, then it will contain a +/// [`Reason`] which can be obtained with the [`reason`] function. +/// +/// [`Reason`]: struct.Reason.html +/// [`reason`]: #method.reason #[derive(Debug)] pub struct Error { kind: Kind, @@ -112,19 +121,3 @@ impl error::Error for Error { } } } - -// ===== impl User ===== - -/* -impl User { - pub fn description(&self) -> &str { - user_desc!(*self) - } -} - -impl fmt::Display for User { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "{}", self.description()) - } -} -*/ diff --git a/src/frame/reason.rs b/src/frame/reason.rs index 4584bb0..a33b988 100644 --- a/src/frame/reason.rs +++ b/src/frame/reason.rs @@ -1,11 +1,21 @@ use std::fmt; -/// HTTP2 Error codes +/// HTTP/2.0 error codes. +/// +/// Error codes are used in `RST_STREAM` and `GOAWAY` frames to convey the +/// reasons for the stream or connection error. For example, +/// [`SendStream::send_reset`] takes a `Reason` argument. Also, the `Error` type +/// may contain a `Reason`. +/// +/// Error codes share a common code space. Some error codes apply only to +/// streams, others apply only to connections, and others may apply to either. +/// See [RFC 7540] for more information. /// /// See [Error Codes in the spec][spec]. /// -/// [spec}: http://httpwg.org/specs/rfc7540.html#ErrorCodes +/// [spec]: http://httpwg.org/specs/rfc7540.html#ErrorCodes +/// [`SendStream::send_reset`]: struct.SendStream.html#method.send_reset #[derive(PartialEq, Eq, Clone, Copy)] pub struct Reason(u32); diff --git a/src/share.rs b/src/share.rs index 081abcb..5302b94 100644 --- a/src/share.rs +++ b/src/share.rs @@ -8,13 +8,116 @@ use http::{HeaderMap}; use std::fmt; -/// Send the body stream and trailers to the peer. +/// Sends the body stream and trailers to the remote peer. +/// +/// # Overview +/// +/// A `SendStream` is provided by [`SendRequest`] and [`SendResponse`] once the +/// HTTP/2.0 message header has been sent sent. It is used to stream the message +/// body and send the message trailers. See method level documentation for more +/// details. +/// +/// The `SendStream` instance is also used to manage outbound flow control. +/// +/// If a `SendStream` is dropped without explicitly closing the send stream, a +/// `RST_STREAM` frame will be sent. This essentially cancels the request / +/// response exchange. +/// +/// The ways to explicitly close the send stream are: +/// +/// * Set `end_of_stream` to true when calling [`send_request`], +/// [`send_response`], or [`send_data`]. +/// * Send trailers with [`send_trailers`]. +/// * Explicitly reset the stream with [`send_reset`]. +/// +/// # Flow control +/// +/// In HTTP/2.0, data cannot be sent to the remote peer unless there is +/// available window capacity on both the stream and the connection. When a data +/// frame is sent, both the stream window and the connection window are +/// decremented. When the stream level window reaches zero, no further data can +/// be sent on that stream. When the connection level window reaches zero, no +/// further data can be sent on any stream for that connection. +/// +/// When the remote peer is ready to receive more data, it sends `WINDOW_UPDATE` +/// frames. These frames increment the windows. See the [specification] for more +/// details on the principles of HTTP/2.0 flow control. +/// +/// The implications for sending data are that the caller **should** ensure that +/// both the stream and the connection has available window capacity before +/// loading the data to send into memory. The `SendStream` instance provides the +/// necessary APIs to perform this logic. This, however, is not an oblication. +/// If the caller attempts to send data on a stream when there is no available +/// window capacity, the library will buffer the data until capacity becomes +/// available, at which point the buffer will be flushed to the connection. +/// +/// **NOTE**: There is no bound on the amount of data that the library will +/// buffer. If you are sending large amounts of data, you really should hook +/// into the flow control lifecycle. Otherwise, you risk using up significant +/// amounts of memory. +/// +/// To hook into the flow control lifecycle, the caller signals to the library +/// that it intends to send data by calling [`reserve_capacity`], specifying the +/// amount of data, in octets, that the caller intends to send. After this, +/// `poll_capacity` is used to be notified when the requested capacity is +/// assigned to the stream. Once [`poll_capacity`] returns `Ready` with the number +/// of octets available to the stream, the caller is able to actually send the +/// data using [`send_data`]. +/// +/// Because there is also a connection level window that applies to **all** +/// streams on a connection, when capacity is assigned to a stream (indicated by +/// `poll_capacity` returning `Ready`), this capacity is reserved on the +/// connection and will **not** be assigned to any other stream. If data is +/// never written to the stream, that capacity is effectively lost to other +/// streams and this introduces the risk of deadlocking a connection. +/// +/// To avoid throttling data on a connection, the caller should not reserve +/// capacity until ready to send data and once any capacity is assigned to the +/// stream, the caller should immediately send data consuming this capacity. +/// There is no guarantee as to when the full capacity requested will become +/// available. For example, if the caller requests 64 KB of data and 512 bytes +/// become available, the caller should immediately send 512 bytes of data. +/// +/// See [`reserve_capacity`] documentation for more details. +/// +/// [`SendRequest`]: client/struct.SendRequest.html +/// [`SendResponse`]: server/struct.SendResponse.html +/// [specification]: http://httpwg.org/specs/rfc7540.html#FlowControl +/// [`reserve_capacity`]: #method.reserve_capacity +/// [`poll_capacity`]: #method.poll_capacity +/// [`send_data`]: #method.send_data +/// [`send_request`]: client/struct.SendRequest.html#method.send_request +/// [`send_response`]: server/struct.SendResponse.html#method.send_response +/// [`send_data`]: #method.send_data +/// [`send_trailers`]: #method.send_trailers +/// [`send_reset`]: #method.send_reset #[derive(Debug)] pub struct SendStream { inner: proto::StreamRef, } -/// Receive the body stream and trailers from the peer. +/// Receives the body stream and trailers from the remote peer. +/// +/// A `RecvStream` is provided by [`client::ResponseFuture`] and +/// [`server::Connection`] with the received HTTP/2.0 message head (the response +/// and request head respectively). +/// +/// A `RecvStream` instance is used to receive the streaming message body and +/// any trailers from the remote peer. It is also used to manage inbound flow +/// control. +/// +/// See method level documentation for more details on receiving data. See +/// [`ReleaseCapacity`] for more details on inbound flow control. +/// +/// Note that this type implements [`Stream`], yielding the received data frames. +/// When this implementation is used, the capacity is immediately released when +/// the data is yielded. It is recommended to only use this API when the data +/// will not be retained in memory for extended periods of time. +/// +/// [`client::ResponseFuture`]: client/struct.ResponseFuture.html +/// [`server::Connection`]: server/struct.Connection.html +/// [`ReleaseCapacity`]: struct.ReleaseCapacity.html +/// [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html #[must_use = "streams do nothing unless polled"] pub struct RecvStream { inner: ReleaseCapacity, @@ -84,36 +187,135 @@ impl SendStream { SendStream { inner } } - /// Request capacity to send data + /// Requests capacity to send data. + /// + /// This function is used to express intent to send data. This requests + /// connection level capacity. Once the capacity is available, it is + /// assigned to the stream and not reused by other streams. + /// + /// This function may be called repeatedly. The `capacity` argument is the + /// **total** amount of requested capacity. Sequential calls to + /// `reserve_capacity` are *not* additive. Given the following: + /// + /// ```rust + /// # use h2::*; + /// # fn doc(mut send_stream: SendStream<&'static [u8]>) { + /// send_stream.reserve_capacity(100); + /// send_stream.reserve_capacity(200); + /// # } + /// ``` + /// + /// After the second call to `reserve_capacity`, the *total* requested + /// capacity will be 200. + /// + /// `reserve_capacity` is also used to cancel previous capacity requests. + /// Given the following: + /// + /// ```rust + /// # use h2::*; + /// # fn doc(mut send_stream: SendStream<&'static [u8]>) { + /// send_stream.reserve_capacity(100); + /// send_stream.reserve_capacity(0); + /// # } + /// ``` + /// + /// After the second call to `reserve_capacity`, the *total* requested + /// capcaity will be 0, i.e. there is no requested capacity for the stream. + /// + /// If `reserve_capacity` is called with a lower value than the amount of + /// capacity **currently** assigned to the stream, this capacity will be + /// returned to the connection to be re-assigned to other streams. + /// + /// Also, the amount of capacity that is reserved gets decremented as data + /// is sent. For example: + /// + /// ```rust + /// # use h2::*; + /// # fn doc(mut send_stream: SendStream<&'static [u8]>) { + /// send_stream.reserve_capacity(100); + /// + /// let capacity = send_stream.poll_capacity(); + /// // capacity == 5; + /// + /// send_stream.send_data(b"hello", false).unwrap(); + /// // At this point, the total amount of requested capacity is 95 bytes. + /// + /// // Calling `reserve_capacity` with `100` again essentially requests an + /// // additional 5 bytes. + /// send_stream.reserve_capacity(100); + /// # } + /// ``` + /// + /// See [Flow contro](struct.SendStream.html#flow-control) for an overview + /// of how send flow control works. pub fn reserve_capacity(&mut self, capacity: usize) { // TODO: Check for overflow self.inner.reserve_capacity(capacity as WindowSize) } /// Returns the stream's current send capacity. + /// + /// This allows the caller to check the current amount of available capacity + /// before sending data. pub fn capacity(&self) -> usize { self.inner.capacity() as usize } - /// Request to be notified when the stream's capacity increases + /// Requests to be notified when the stream's capacity increases. + /// + /// Before calling this, capacity should be requested with + /// [`reserve_capacity`]. Once capacity is requested, the connection will + /// assign capacity to the stream **as it becomes available**. There is no + /// guarantee as to when and in what increments capacity gets assigned to + /// the stream. + /// + /// To get notified when the available capacity increases, the caller calls + /// `poll_capacity`, which returns `Ready(Some(n))` when `n` has been + /// increased by the connection. Note that `n` here represents the **total** + /// amount of assigned capacity at that point in time. It is also possible + /// that `n` is lower than the previous call if, since then, the caller has + /// sent data. pub fn poll_capacity(&mut self) -> Poll, ::Error> { let res = try_ready!(self.inner.poll_capacity()); Ok(Async::Ready(res.map(|v| v as usize))) } - /// Send a single data frame + /// Sends a single data frame to the remote peer. + /// + /// This function may be called repeatedly as long as `end_of_stream` is set + /// to `false`. Setting `end_of_stream` to `true` sets the end stream flag + /// on the data frame. Any further calls to `send_data` or `send_trailers` + /// will return an [`Error`]. + /// + /// `send_data` can be called without reserving capacity. In this case, the + /// data is buffered and the capacity is implicitly requested. Once the + /// capacity becomes available, the data is flushed to the connection. + /// However, this buffering is unbounded. As such, sending large amounts of + /// data without reserving capacity before hand could result in large + /// amounts of data being buffered in memory. + /// + /// [`Error`]: struct.Error.html pub fn send_data(&mut self, data: B, end_of_stream: bool) -> Result<(), ::Error> { self.inner .send_data(data.into_buf(), end_of_stream) .map_err(Into::into) } - /// Send trailers + /// Sends trailers to the remote peer. + /// + /// Sending trailers implicitly closes the send stream. Once the send stream + /// is closed, no more data can be sent. pub fn send_trailers(&mut self, trailers: HeaderMap) -> Result<(), ::Error> { self.inner.send_trailers(trailers).map_err(Into::into) } - /// Reset the stream + /// Resets the stream. + /// + /// This cancels the request / response exchange. If the response has not + /// yet been received, the associatd `ResponseFuture` will return an + /// [`Error`] to reflect the canceled exchange. + /// + /// [`Error`]: struct.Error.html pub fn send_reset(&mut self, reason: Reason) { self.inner.send_reset(reason) }