h3/client/
stream.rs

1use bytes::Buf;
2use futures_util::future;
3use http::{HeaderMap, Response};
4use quic::StreamId;
5#[cfg(feature = "tracing")]
6use tracing::instrument;
7
8use crate::{
9    connection::{self},
10    error::{
11        connection_error_creators::{CloseStream, HandleFrameStreamErrorOnRequestStream},
12        internal_error::InternalConnectionError,
13        Code, StreamError,
14    },
15    proto::{frame::Frame, headers::Header},
16    qpack,
17    quic::{self},
18    shared_state::{ConnectionState, SharedState},
19};
20use std::{
21    convert::TryFrom,
22    task::{Context, Poll},
23};
24
25/// Manage request bodies transfer, response and trailers.
26///
27/// Once a request has been sent via [`crate::client::SendRequest::send_request()`], a response can be awaited by calling
28/// [`RequestStream::recv_response()`]. A body for this request can be sent with [`RequestStream::send_data()`], then the request
29/// shall be completed by either sending trailers with  [`RequestStream::finish()`].
30///
31/// After receiving the response's headers, it's body can be read by [`RequestStream::recv_data()`] until it returns
32/// `None`. Then the trailers will eventually be available via [`RequestStream::recv_trailers()`].
33///
34/// TODO: If data is polled before the response has been received, an error will be thrown.
35///
36/// TODO: If trailers are polled but the body hasn't been fully received, an UNEXPECT_FRAME error will be
37/// thrown
38///
39/// Whenever the client wants to cancel this request, it can call [`RequestStream::stop_sending()`], which will
40/// put an end to any transfer concerning it.
41///
42/// # Examples
43///
44/// ```rust
45/// # use h3::{quic, client::*};
46/// # use http::{Request, Response};
47/// # use bytes::Buf;
48/// # use tokio::io::AsyncWriteExt;
49/// # async fn doc<T,B>(mut req_stream: RequestStream<T, B>) -> Result<(), Box<dyn std::error::Error>>
50/// # where
51/// #     T: quic::RecvStream,
52/// # {
53/// // Prepare the HTTP request to send to the server
54/// let request = Request::get("https://www.example.com/").body(())?;
55///
56/// // Receive the response
57/// let response = req_stream.recv_response().await?;
58/// // Receive the body
59/// while let Some(mut chunk) = req_stream.recv_data().await? {
60///     let mut out = tokio::io::stdout();
61///     out.write_all_buf(&mut chunk).await?;
62///     out.flush().await?;
63/// }
64/// # Ok(())
65/// # }
66/// # pub fn main() {}
67/// ```
68///
69/// [`send_request()`]: struct.SendRequest.html#method.send_request
70/// [`recv_response()`]: #method.recv_response
71/// [`recv_data()`]: #method.recv_data
72/// [`send_data()`]: #method.send_data
73/// [`send_trailers()`]: #method.send_trailers
74/// [`recv_trailers()`]: #method.recv_trailers
75/// [`finish()`]: #method.finish
76/// [`stop_sending()`]: #method.stop_sending
77pub struct RequestStream<S, B> {
78    pub(super) inner: connection::RequestStream<S, B>,
79}
80
81impl<S, B> ConnectionState for RequestStream<S, B> {
82    fn shared_state(&self) -> &SharedState {
83        &self.inner.conn_state
84    }
85}
86
87impl<S, B> CloseStream for RequestStream<S, B> {}
88
89impl<S, B> RequestStream<S, B>
90where
91    S: quic::RecvStream,
92{
93    /// Receive the HTTP/3 response
94    ///
95    /// This should be called before trying to receive any data with [`recv_data()`].
96    ///
97    /// [`recv_data()`]: #method.recv_data
98    #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))]
99    pub async fn recv_response(&mut self) -> Result<Response<()>, StreamError> {
100        let mut frame = future::poll_fn(|cx| self.inner.stream.poll_next(cx))
101            .await
102            .map_err(|e| self.handle_frame_stream_error_on_request_stream(e))?
103            .ok_or_else(|| {
104                //= https://www.rfc-editor.org/rfc/rfc9114#section-4.1
105                //# Receipt of an invalid sequence of frames MUST be treated as a
106                //# connection error of type H3_FRAME_UNEXPECTED.
107                self.handle_connection_error_on_stream(InternalConnectionError::new(
108                    Code::H3_FRAME_UNEXPECTED,
109                    "Stream finished without receiving response headers".to_string(),
110                ))
111            })?;
112
113        //= https://www.rfc-editor.org/rfc/rfc9114#section-7.2.5
114        //= type=TODO
115        //# A client MUST treat
116        //# receipt of a PUSH_PROMISE frame that contains a larger push ID than
117        //# the client has advertised as a connection error of H3_ID_ERROR.
118
119        //= https://www.rfc-editor.org/rfc/rfc9114#section-7.2.5
120        //= type=TODO
121        //# If a client
122        //# receives a push ID that has already been promised and detects a
123        //# mismatch, it MUST respond with a connection error of type
124        //# H3_GENERAL_PROTOCOL_ERROR.
125
126        let decoded = if let Frame::Headers(ref mut encoded) = frame {
127            match qpack::decode_stateless(encoded, self.inner.max_field_section_size) {
128                //= https://www.rfc-editor.org/rfc/rfc9114#section-4.2.2
129                //# An HTTP/3 implementation MAY impose a limit on the maximum size of
130                //# the message header it will accept on an individual HTTP message.
131                Err(qpack::DecoderError::HeaderTooLong(cancel_size)) => {
132                    self.inner.stop_sending(Code::H3_REQUEST_CANCELLED);
133                    return Err(StreamError::HeaderTooBig {
134                        actual_size: cancel_size,
135                        max_size: self.inner.max_field_section_size,
136                    });
137                }
138                Ok(decoded) => decoded,
139                Err(_e) => {
140                    return Err(
141                        self.handle_connection_error_on_stream(InternalConnectionError {
142                            code: Code::QPACK_DECOMPRESSION_FAILED,
143                            message: "Failed to decode headers".to_string(),
144                        }),
145                    )
146                }
147            }
148        } else {
149            //= https://www.rfc-editor.org/rfc/rfc9114#section-4.1
150            //# Receipt of an invalid sequence of frames MUST be treated as a
151            //# connection error of type H3_FRAME_UNEXPECTED.
152
153            return Err(
154                self.handle_connection_error_on_stream(InternalConnectionError::new(
155                    Code::H3_FRAME_UNEXPECTED,
156                    "First response frame is not headers".to_string(),
157                )),
158            );
159        };
160
161        let qpack::Decoded { fields, .. } = decoded;
162
163        let (status, headers) = Header::try_from(fields)
164            .map_err(|_e| {
165                self.inner.stream.stop_sending(Code::H3_REQUEST_CANCELLED);
166                StreamError::StreamError {
167                    code: Code::H3_MESSAGE_ERROR,
168                    reason: "Received malformed header".to_string(),
169                }
170            })?
171            .into_response_parts()
172            .map_err(|_e| {
173                self.inner.stream.stop_sending(Code::H3_REQUEST_CANCELLED);
174                StreamError::StreamError {
175                    code: Code::H3_MESSAGE_ERROR,
176                    reason: "Received malformed header".to_string(),
177                }
178            })?;
179        let mut resp = Response::new(());
180        *resp.status_mut() = status;
181        *resp.headers_mut() = headers;
182        *resp.version_mut() = http::Version::HTTP_3;
183
184        Ok(resp)
185    }
186
187    /// Receive some of the request body.
188    // TODO what if called before recv_response ?
189    #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))]
190    pub async fn recv_data(&mut self) -> Result<Option<impl Buf>, StreamError> {
191        future::poll_fn(|cx| self.poll_recv_data(cx)).await
192    }
193
194    /// Receive request body
195    pub fn poll_recv_data(
196        &mut self,
197        cx: &mut Context<'_>,
198    ) -> Poll<Result<Option<impl Buf>, StreamError>> {
199        self.inner.poll_recv_data(cx)
200    }
201
202    /// Receive an optional set of trailers for the response.
203    #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))]
204    pub async fn recv_trailers(&mut self) -> Result<Option<HeaderMap>, StreamError> {
205        future::poll_fn(|cx| self.poll_recv_trailers(cx)).await
206    }
207
208    /// Poll receive an optional set of trailers for the response.
209    #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))]
210    pub fn poll_recv_trailers(
211        &mut self,
212        cx: &mut Context<'_>,
213    ) -> Poll<Result<Option<HeaderMap>, StreamError>> {
214        let res = self.inner.poll_recv_trailers(cx);
215        if let Poll::Ready(Err(e)) = &res {
216            if let StreamError::HeaderTooBig { .. } = e {
217                self.inner.stream.stop_sending(Code::H3_REQUEST_CANCELLED);
218            }
219        }
220        res
221    }
222
223    /// Tell the peer to stop sending into the underlying QUIC stream
224    #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))]
225    pub fn stop_sending(&mut self, error_code: Code) {
226        // TODO take by value to prevent any further call as this request is cancelled
227        // rename `cancel()` ?
228        self.inner.stream.stop_sending(error_code)
229    }
230
231    /// Returns the underlying stream id
232    pub fn id(&self) -> StreamId {
233        self.inner.stream.id()
234    }
235}
236
237impl<S, B> RequestStream<S, B>
238where
239    S: quic::SendStream<B>,
240    B: Buf,
241{
242    /// Send some data on the request body.
243    #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))]
244    pub async fn send_data(&mut self, buf: B) -> Result<(), StreamError> {
245        self.inner.send_data(buf).await
246    }
247
248    /// Stop a stream with an error code
249    ///
250    /// The code can be [`Code::H3_NO_ERROR`].
251    pub fn stop_stream(&mut self, error_code: Code) {
252        self.inner.stop_stream(error_code);
253    }
254
255    /// Send a set of trailers to end the request.
256    ///
257    /// [`RequestStream::finish()`] must be called to finalize a request.
258    #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))]
259    pub async fn send_trailers(&mut self, trailers: HeaderMap) -> Result<(), StreamError> {
260        self.inner.send_trailers(trailers).await
261    }
262
263    /// End the request without trailers.
264    ///
265    /// [`RequestStream::finish()`] must be called to finalize a request.
266    #[cfg_attr(feature = "tracing", instrument(skip_all, level = "trace"))]
267    pub async fn finish(&mut self) -> Result<(), StreamError> {
268        self.inner.finish().await
269    }
270
271    //= https://www.rfc-editor.org/rfc/rfc9114#section-4.1.1
272    //= type=TODO
273    //# Implementations SHOULD cancel requests by abruptly terminating any
274    //# directions of a stream that are still open.  To do so, an
275    //# implementation resets the sending parts of streams and aborts reading
276    //# on the receiving parts of streams; see Section 2.4 of
277    //# [QUIC-TRANSPORT].
278}
279
280impl<S, B> RequestStream<S, B>
281where
282    S: quic::BidiStream<B>,
283    B: Buf,
284{
285    /// Split this stream into two halves that can be driven independently.
286    pub fn split(
287        self,
288    ) -> (
289        RequestStream<S::SendStream, B>,
290        RequestStream<S::RecvStream, B>,
291    ) {
292        let (send, recv) = self.inner.split();
293        (RequestStream { inner: send }, RequestStream { inner: recv })
294    }
295}