Skip to main content

webtrans_quinn/
connect.rs

1//! HTTP/3 CONNECT request/response handling for WebTransport sessions.
2
3use webtrans_proto::{ConnectRequest, ConnectResponse, VarInt};
4
5use thiserror::Error;
6use url::Url;
7
8#[derive(Error, Debug, Clone)]
9pub enum ConnectError {
10    #[error("quic stream was closed early")]
11    UnexpectedEnd,
12
13    #[error("protocol error: {0}")]
14    ProtoError(#[from] webtrans_proto::ConnectError),
15
16    #[error("connection error")]
17    ConnectionError(#[from] quinn::ConnectionError),
18
19    #[error("read error")]
20    ReadError(#[from] quinn::ReadError),
21
22    #[error("write error")]
23    WriteError(#[from] quinn::WriteError),
24
25    #[error("http error status: {0}")]
26    ErrorStatus(http::StatusCode),
27}
28
29pub struct Connect {
30    // The CONNECT request sent by the client.
31    request: ConnectRequest,
32
33    // Keep references to send/recv streams so they remain open until drop.
34    send: quinn::SendStream,
35
36    #[allow(dead_code)]
37    recv: quinn::RecvStream,
38}
39
40impl Connect {
41    pub async fn accept(conn: &quinn::Connection) -> Result<Self, ConnectError> {
42        // Accept the stream used for the HTTP CONNECT request.
43        // Any other request type is treated as an error.
44        let (send, mut recv) = conn.accept_bi().await?;
45
46        let request = webtrans_proto::ConnectRequest::read(&mut recv).await?;
47        tracing::debug!("received CONNECT request: {request:?}");
48
49        // The request decoded successfully, so we can respond.
50        Ok(Self {
51            request,
52            send,
53            recv,
54        })
55    }
56
57    // Called by the server to send a response to the client.
58    pub async fn respond(&mut self, status: http::StatusCode) -> Result<(), ConnectError> {
59        let resp = ConnectResponse { status };
60
61        tracing::debug!("sending CONNECT response: {resp:?}");
62        resp.write(&mut self.send).await?;
63
64        Ok(())
65    }
66
67    pub async fn reject(&mut self, status: http::StatusCode) -> Result<(), ConnectError> {
68        self.respond(status).await?;
69        self.send
70            .finish()
71            .map_err(|_| ConnectError::UnexpectedEnd)?;
72        // Once the response and FIN are queued, a peer may immediately close
73        // the rejected connection. Waiting here keeps the control streams alive
74        // long enough to avoid racing the response, but the resulting stop or
75        // connection-close status does not invalidate the rejection.
76        let _ = self.send.stopped().await;
77        Ok(())
78    }
79
80    pub async fn open(conn: &quinn::Connection, url: Url) -> Result<Self, ConnectError> {
81        // Create a stream for sending the CONNECT request.
82        let (mut send, mut recv) = conn.open_bi().await?;
83
84        // Create a CONNECT request to send using HTTP/3.
85        let request = ConnectRequest { url };
86
87        tracing::debug!("sending CONNECT request: {request:?}");
88        request.write(&mut send).await?;
89
90        let response = webtrans_proto::ConnectResponse::read(&mut recv).await?;
91        tracing::debug!("received CONNECT response: {response:?}");
92
93        // Return an error if the response is not 200 OK.
94        if response.status != http::StatusCode::OK {
95            return Err(ConnectError::ErrorStatus(response.status));
96        }
97
98        Ok(Self {
99            request,
100            send,
101            recv,
102        })
103    }
104
105    // The session ID is the stream ID of the CONNECT request.
106    pub fn session_id(&self) -> VarInt {
107        // Convert Quinn's VarInt to the WebTransport VarInt without adding a proto dependency.
108        let stream_id = quinn::VarInt::from(self.send.id());
109        VarInt::try_from(stream_id.into_inner()).unwrap()
110    }
111
112    // The URL from the CONNECT request.
113    pub fn url(&self) -> &Url {
114        &self.request.url
115    }
116
117    pub(super) fn into_inner(self) -> (quinn::SendStream, quinn::RecvStream) {
118        (self.send, self.recv)
119    }
120}