Skip to main content

wtransport/
endpoint.rs

1use crate::config::ClientConfig;
2use crate::config::DnsResolver;
3use crate::config::ServerConfig;
4use crate::connection::Connection;
5use crate::driver::streams::session::StreamSession;
6use crate::driver::streams::ProtoReadError;
7use crate::driver::streams::ProtoWriteError;
8use crate::driver::utils::varint_w2q;
9use crate::driver::Driver;
10use crate::error::ConnectingError;
11use crate::error::ConnectionError;
12use crate::VarInt;
13use quinn::TokioRuntime;
14use std::collections::HashMap;
15use std::future::Future;
16use std::future::IntoFuture;
17use std::marker::PhantomData;
18use std::net::SocketAddr;
19use std::net::SocketAddrV4;
20use std::net::SocketAddrV6;
21use std::pin::Pin;
22use std::sync::Arc;
23use std::task::Context;
24use std::task::Poll;
25use tracing::debug;
26use url::Host;
27use url::Url;
28use wtransport_proto::error::ErrorCode;
29use wtransport_proto::frame::FrameKind;
30use wtransport_proto::headers::Headers;
31use wtransport_proto::session::ReservedHeader;
32use wtransport_proto::session::SessionRequest as SessionRequestProto;
33use wtransport_proto::session::SessionResponse as SessionResponseProto;
34
35/// Helper structure for Endpoint types.
36pub mod endpoint_side {
37    use super::*;
38
39    /// Type of endpoint accepting multiple WebTransport connections.
40    ///
41    /// Use [`Endpoint::server`] to create and server-endpoint.
42    pub struct Server {
43        pub(super) _marker: PhantomData<()>,
44    }
45
46    /// Type of endpoint opening a WebTransport connection.
47    ///
48    /// Use [`Endpoint::client`] to create and client-endpoint.
49    pub struct Client {
50        pub(super) dns_resolver: Arc<dyn DnsResolver + Send + Sync>,
51    }
52}
53
54/// Entrypoint for creating client or server connections.
55///
56/// A single endpoint can be used to accept or connect multiple connections.
57/// Each endpoint internally binds an UDP socket.
58///
59/// # Server
60/// Use [`Endpoint::server`] for creating a server-side endpoint.
61/// Afterwards use the method [`Endpoint::accept`] for awaiting on incoming session request.
62///
63/// ```no_run
64/// # use anyhow::Result;
65/// # use wtransport::ServerConfig;
66/// # use wtransport::Identity;
67/// use wtransport::Endpoint;
68///
69/// # async fn run() -> Result<()> {
70/// # let config = ServerConfig::builder()
71/// #       .with_bind_default(4433)
72/// #       .with_identity(Identity::self_signed(["doc"]).unwrap())
73/// #       .build();
74/// let server = Endpoint::server(config)?;
75/// loop {
76///     let incoming_session = server.accept().await;
77///     // Spawn task that handles client incoming session...
78/// }
79/// # Ok(())
80/// # }
81/// ```
82///
83/// # Client
84/// Use [`Endpoint::client`] for creating a client-side endpoint and use [`Endpoint::connect`]
85/// to connect to a server specifying the URL.
86///
87/// ```no_run
88/// # use anyhow::Result;
89/// use wtransport::ClientConfig;
90/// use wtransport::Endpoint;
91///
92/// # async fn run() -> Result<()> {
93/// let connection = Endpoint::client(ClientConfig::default())?
94///     .connect("https://localhost:4433")
95///     .await?;
96/// # Ok(())
97/// # }
98/// ```
99pub struct Endpoint<Side> {
100    endpoint: quinn::Endpoint,
101    side: Side,
102}
103
104impl<Side> Endpoint<Side> {
105    /// Closes all of this endpoint's connections immediately and cease accepting new connections.
106    pub fn close(&self, error_code: VarInt, reason: &[u8]) {
107        self.endpoint.close(varint_w2q(error_code), reason);
108    }
109
110    /// Waits for all connections on the endpoint to be cleanly shut down.
111    pub async fn wait_idle(&self) {
112        self.endpoint.wait_idle().await;
113    }
114
115    /// Gets the local [`SocketAddr`] the underlying socket is bound to.
116    pub fn local_addr(&self) -> std::io::Result<SocketAddr> {
117        self.endpoint.local_addr()
118    }
119
120    /// Get the number of connections that are currently open.
121    pub fn open_connections(&self) -> usize {
122        self.endpoint.open_connections()
123    }
124}
125
126impl Endpoint<endpoint_side::Server> {
127    /// Constructs a *server* endpoint.
128    pub fn server(server_config: ServerConfig) -> std::io::Result<Self> {
129        let endpoint_config = server_config.endpoint_config;
130        let quic_config = server_config.quic_config;
131        let socket = server_config.bind_address_config.bind_socket()?;
132        let runtime = Arc::new(TokioRuntime);
133
134        let endpoint = quinn::Endpoint::new(endpoint_config, Some(quic_config), socket, runtime)?;
135
136        Ok(Self {
137            endpoint,
138            side: endpoint_side::Server {
139                _marker: PhantomData,
140            },
141        })
142    }
143
144    /// Get the next incoming connection attempt from a client.
145    pub async fn accept(&self) -> IncomingSession {
146        let quic_incoming = self
147            .endpoint
148            .accept()
149            .await
150            .expect("Endpoint cannot be closed");
151
152        debug!("New incoming QUIC connection");
153
154        IncomingSession(quic_incoming)
155    }
156
157    /// Reloads the server configuration.
158    ///
159    /// Useful for e.g. refreshing TLS certificates without disrupting existing connections.
160    ///
161    /// # Arguments
162    ///
163    /// * `server_config` - The new configuration for the server.
164    /// * `rebind` - A boolean indicating whether the server should rebind its socket.
165    ///   If `true`, the server will bind to a new socket with the provided configuration.
166    ///   If `false`, the bind address configuration will be ignored.
167    pub fn reload_config(&self, server_config: ServerConfig, rebind: bool) -> std::io::Result<()> {
168        if rebind {
169            let socket = server_config.bind_address_config.bind_socket()?;
170            self.endpoint.rebind(socket)?;
171        }
172
173        let quic_config = server_config.quic_config;
174        self.endpoint.set_server_config(Some(quic_config));
175
176        Ok(())
177    }
178}
179
180impl Endpoint<endpoint_side::Client> {
181    /// Constructs a *client* endpoint.
182    pub fn client(client_config: ClientConfig) -> std::io::Result<Self> {
183        let endpoint_config = client_config.endpoint_config;
184        let quic_config = client_config.quic_config;
185        let socket = client_config.bind_address_config.bind_socket()?;
186        let runtime = Arc::new(TokioRuntime);
187
188        let mut endpoint = quinn::Endpoint::new(endpoint_config, None, socket, runtime)?;
189
190        endpoint.set_default_client_config(quic_config);
191
192        Ok(Self {
193            endpoint,
194            side: endpoint_side::Client {
195                dns_resolver: client_config.dns_resolver,
196            },
197        })
198    }
199
200    /// Establishes a WebTransport connection to a specified URL.
201    ///
202    /// This method initiates a WebTransport connection to the specified URL.
203    /// It validates the URL, and performs necessary steps to establish a secure connection.
204    ///
205    /// # Arguments
206    ///
207    /// * `options` - Connection options specifying the URL and additional headers.
208    ///   It can be simply an [URL](https://en.wikipedia.org/wiki/URL) string representing
209    ///   the WebTransport endpoint to connect to. It must have an `https` scheme.
210    ///   The URL can specify either an IP address or a hostname.
211    ///   When specifying a hostname, the method will internally perform DNS resolution,
212    ///   configured with
213    ///   [`ClientConfigBuilder::dns_resolver`](crate::config::ClientConfigBuilder::dns_resolver).
214    ///
215    /// # Examples
216    ///
217    /// Connect using a URL with a hostname (DNS resolution is performed):
218    ///
219    /// ```no_run
220    /// # use anyhow::Result;
221    /// # use wtransport::endpoint::endpoint_side::Client;
222    /// # async fn example(endpoint: wtransport::Endpoint<Client>) -> Result<()> {
223    /// let url = "https://example.com:4433/webtransport";
224    /// let connection = endpoint.connect(url).await?;
225    /// # Ok(())
226    /// # }
227    /// ```
228    ///
229    /// Connect using a URL with an IP address:
230    ///
231    /// ```no_run
232    /// # use anyhow::Result;
233    /// # use wtransport::endpoint::endpoint_side::Client;
234    /// # async fn example(endpoint: wtransport::Endpoint<Client>) -> Result<()> {
235    /// let url = "https://127.0.0.1:4343/webtransport";
236    /// let connection = endpoint.connect(url).await?;
237    /// # Ok(())
238    /// # }
239    /// ```
240    ///
241    /// Connect adding an additional header:
242    ///
243    /// ```no_run
244    /// # use anyhow::Result;
245    /// # use wtransport::endpoint::endpoint_side::Client;
246    /// # use wtransport::endpoint::ConnectOptions;
247    /// # async fn example(endpoint: wtransport::Endpoint<Client>) -> Result<()> {
248    /// let options = ConnectOptions::builder("https://example.com:4433/webtransport")
249    ///     .add_header("Authorization", "AuthToken")
250    ///     .build();
251    /// let connection = endpoint.connect(options).await?;
252    /// # Ok(())
253    /// # }
254    /// ```
255    pub async fn connect<O>(&self, options: O) -> Result<Connection, ConnectingError>
256    where
257        O: IntoConnectOptions,
258    {
259        let options = options.into_options();
260
261        let url = Url::parse(&options.url)
262            .map_err(|parse_error| ConnectingError::InvalidUrl(parse_error.to_string()))?;
263
264        if url.scheme() != "https" {
265            return Err(ConnectingError::InvalidUrl(
266                "WebTransport URL scheme must be 'https'".to_string(),
267            ));
268        }
269
270        let host = url.host().expect("https scheme must have an host");
271        let port = url.port().unwrap_or(443);
272
273        let (socket_address, server_name) = match host {
274            Host::Domain(domain) => {
275                let socket_address = self
276                    .side
277                    .dns_resolver
278                    .resolve(&format!("{domain}:{port}"))
279                    .await
280                    .map_err(ConnectingError::DnsLookup)?
281                    .ok_or(ConnectingError::DnsNotFound)?;
282
283                (socket_address, domain.to_string())
284            }
285            Host::Ipv4(address) => {
286                let socket_address = SocketAddr::V4(SocketAddrV4::new(address, port));
287                (socket_address, address.to_string())
288            }
289            Host::Ipv6(address) => {
290                let socket_address = SocketAddr::V6(SocketAddrV6::new(address, port, 0, 0));
291                (socket_address, address.to_string())
292            }
293        };
294
295        let quic_connection = self
296            .endpoint
297            .connect(socket_address, &server_name)
298            .map_err(ConnectingError::with_connect_error)?
299            .await
300            .map_err(|connection_error| {
301                ConnectingError::ConnectionError(connection_error.into())
302            })?;
303
304        let driver = Driver::init(quic_connection.clone());
305
306        let settings = driver.accept_settings().await.map_err(|driver_error| {
307            ConnectingError::ConnectionError(ConnectionError::with_driver_error(
308                driver_error,
309                &quic_connection,
310            ))
311        })?;
312
313        // Extended CONNECT is accepted only after the server opted in.
314        if settings.get(wtransport_proto::settings::SettingId::EnableConnectProtocol)
315            != Some(VarInt::from_u32(1))
316        {
317            return Err(ConnectingError::ConnectionError(
318                ConnectionError::local_h3_error(ErrorCode::Settings),
319            ));
320        }
321
322        let mut session_request_proto =
323            SessionRequestProto::new(url.as_ref()).expect("Url has been already validate");
324
325        for (k, v) in options.additional_headers {
326            session_request_proto
327                .insert(k.clone(), v)
328                .map_err(|ReservedHeader| ConnectingError::ReservedHeader(k))?;
329        }
330
331        let mut stream_session = match driver.open_session(session_request_proto).await {
332            Ok(stream_session) => stream_session,
333            Err(driver_error) => {
334                return Err(ConnectingError::ConnectionError(
335                    ConnectionError::with_driver_error(driver_error, &quic_connection),
336                ))
337            }
338        };
339
340        let session_id = stream_session.session_id();
341
342        match stream_session
343            .write_frame(stream_session.request().headers().generate_frame())
344            .await
345        {
346            Ok(()) => {}
347            Err(ProtoWriteError::Stopped) => {
348                return Err(ConnectingError::SessionRejected);
349            }
350            Err(ProtoWriteError::NotConnected) => {
351                return Err(ConnectingError::with_no_connection(&quic_connection));
352            }
353        }
354
355        let frame = loop {
356            let frame = match stream_session.read_frame().await {
357                Ok(frame) => frame,
358                Err(ProtoReadError::H3(error_code)) => {
359                    quic_connection.close(varint_w2q(error_code.to_code()), b"");
360                    return Err(ConnectingError::ConnectionError(
361                        ConnectionError::local_h3_error(error_code),
362                    ));
363                }
364                Err(ProtoReadError::IO(_io_error)) => {
365                    return Err(ConnectingError::with_no_connection(&quic_connection));
366                }
367            };
368
369            if let FrameKind::Exercise(_) = frame.kind() {
370                continue;
371            }
372            break frame;
373        };
374
375        if !matches!(frame.kind(), FrameKind::Headers) {
376            quic_connection.close(varint_w2q(ErrorCode::FrameUnexpected.to_code()), b"");
377            return Err(ConnectingError::ConnectionError(
378                ConnectionError::local_h3_error(ErrorCode::FrameUnexpected),
379            ));
380        }
381
382        let headers = match Headers::with_frame(&frame) {
383            Ok(headers) => headers,
384            Err(error_code) => {
385                quic_connection.close(varint_w2q(error_code.to_code()), b"");
386                return Err(ConnectingError::ConnectionError(
387                    ConnectionError::local_h3_error(error_code),
388                ));
389            }
390        };
391
392        let Ok(session_response) = SessionResponseProto::try_from(headers) else {
393            quic_connection.close(varint_w2q(ErrorCode::Message.to_code()), b"");
394            return Err(ConnectingError::ConnectionError(
395                ConnectionError::local_h3_error(ErrorCode::Message),
396            ));
397        };
398
399        if session_response.code().is_successful() {
400            match driver.register_session(stream_session).await {
401                Ok(()) => {}
402                Err(driver_error) => {
403                    return Err(ConnectingError::ConnectionError(
404                        ConnectionError::with_driver_error(driver_error, &quic_connection),
405                    ))
406                }
407            }
408        } else {
409            return Err(ConnectingError::SessionRejected);
410        }
411
412        Ok(Connection::new(quic_connection, driver, session_id))
413    }
414}
415
416/// Options for establishing a client WebTransport connection.
417///
418/// Used in [`Endpoint::connect`].
419///
420/// # Examples
421///
422/// ```no_run
423/// # use anyhow::Result;
424/// # use wtransport::endpoint::endpoint_side::Client;
425/// # use wtransport::endpoint::ConnectOptions;
426/// # async fn example(endpoint: wtransport::Endpoint<Client>) -> Result<()> {
427/// let options = ConnectOptions::builder("https://example.com:4433/webtransport")
428///     .add_header("Authorization", "AuthToken")
429///     .build();
430/// let connection = endpoint.connect(options).await?;
431/// # Ok(())
432/// # }
433/// ```
434#[derive(Debug, Clone)]
435pub struct ConnectOptions {
436    url: String,
437    additional_headers: HashMap<String, String>,
438}
439
440impl ConnectOptions {
441    /// Creates a new `ConnectOptions` using a builder pattern.
442    ///
443    /// # Arguments
444    ///
445    /// * `url` - A [URL](https://en.wikipedia.org/wiki/URL) string representing the WebTransport
446    ///   endpoint to connect to. It must have an `https` scheme.
447    ///   The URL can specify either an IP address or a hostname.
448    ///   When specifying a hostname, the method will internally perform DNS resolution,
449    ///   configured with
450    ///   [`ClientConfigBuilder::dns_resolver`](crate::config::ClientConfigBuilder::dns_resolver).
451    pub fn builder<S>(url: S) -> ConnectRequestBuilder
452    where
453        S: ToString,
454    {
455        ConnectRequestBuilder {
456            url: url.to_string(),
457            additional_headers: Default::default(),
458        }
459    }
460
461    /// Gets the URL which this will connect to.
462    pub fn url(&self) -> &str {
463        &self.url
464    }
465
466    /// Gets the additional headers that will be passed when connecting.
467    pub fn additional_headers(&self) -> &HashMap<String, String> {
468        &self.additional_headers
469    }
470}
471
472/// A trait for converting types into `ConnectOptions`.
473pub trait IntoConnectOptions {
474    /// Perform value-to-value conversion into [`ConnectOptions`].
475    fn into_options(self) -> ConnectOptions;
476}
477
478/// A builder for [`ConnectOptions`].
479///
480/// See [`ConnectOptions::builder`].
481pub struct ConnectRequestBuilder {
482    url: String,
483    additional_headers: HashMap<String, String>,
484}
485
486impl ConnectRequestBuilder {
487    /// Adds a header to the connection options.
488    ///
489    /// # Examples
490    ///
491    /// ```rust
492    /// use wtransport::endpoint::ConnectOptions;
493    ///
494    /// let options = ConnectOptions::builder("https://example.com:4433/webtransport")
495    ///     .add_header("Authorization", "AuthToken")
496    ///     .build();
497    /// ```
498    pub fn add_header<K, V>(mut self, key: K, value: V) -> Self
499    where
500        K: ToString,
501        V: ToString,
502    {
503        self.additional_headers
504            .insert(key.to_string(), value.to_string());
505        self
506    }
507
508    /// Constructs the [`ConnectOptions`] from the builder configuration.
509    pub fn build(self) -> ConnectOptions {
510        ConnectOptions {
511            url: self.url,
512            additional_headers: self.additional_headers,
513        }
514    }
515}
516
517impl IntoConnectOptions for ConnectRequestBuilder {
518    fn into_options(self) -> ConnectOptions {
519        self.build()
520    }
521}
522
523impl IntoConnectOptions for ConnectOptions {
524    fn into_options(self) -> ConnectOptions {
525        self
526    }
527}
528
529impl<S> IntoConnectOptions for S
530where
531    S: ToString,
532{
533    fn into_options(self) -> ConnectOptions {
534        ConnectOptions::builder(self).build()
535    }
536}
537
538type DynFutureIncomingSession =
539    dyn Future<Output = Result<SessionRequest, ConnectionError>> + Send + Sync;
540
541/// [`IntoFuture`] for an in-progress incoming connection attempt.
542///
543/// Created by [`Endpoint::accept`].
544pub struct IncomingSession(quinn::Incoming);
545
546impl IncomingSession {
547    /// The peer's UDP address.
548    pub fn remote_address(&self) -> SocketAddr {
549        self.0.remote_address()
550    }
551
552    /// Whether the socket address that is initiating this connection has been validated.
553    ///
554    /// This means that the sender of the initial packet has proved that they can receive traffic
555    /// sent to `self.remote_address()`.
556    pub fn remote_address_validated(&self) -> bool {
557        self.0.remote_address_validated()
558    }
559
560    /// Respond with a retry packet, requiring the client to retry with address validation
561    ///
562    /// # Panics
563    ///
564    /// If `remote_address_validated()` is true.
565    pub fn retry(self) {
566        self.0.retry().expect("remote address already verified");
567    }
568
569    /// Reject this incoming connection attempt.
570    pub fn refuse(self) {
571        self.0.refuse();
572    }
573
574    /// Ignore this incoming connection attempt, not sending any packet in response.
575    pub fn ignore(self) {
576        self.0.ignore();
577    }
578}
579
580impl IntoFuture for IncomingSession {
581    type IntoFuture = IncomingSessionFuture;
582    type Output = Result<SessionRequest, ConnectionError>;
583
584    fn into_future(self) -> Self::IntoFuture {
585        IncomingSessionFuture::new(self.0)
586    }
587}
588
589/// [`Future`] for an in-progress incoming connection attempt.
590///
591/// Created by awaiting an [`IncomingSession`]
592pub struct IncomingSessionFuture(Pin<Box<DynFutureIncomingSession>>);
593
594impl IncomingSessionFuture {
595    /// Creates a future from [`quinn::Incoming`].
596    ///
597    /// The returned future resolves to a [`SessionRequest`] once the QUIC connection
598    /// is established and the WebTransport session has been accepted.
599    #[cfg(feature = "quinn")]
600    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
601    pub fn with_quic_incoming(quic_incoming: quinn::Incoming) -> Self {
602        Self::new(quic_incoming)
603    }
604
605    /// Creates a future from [`quinn::Connecting`].
606    ///
607    /// This is useful when a [`quinn::Connecting`] is already available and needs to be
608    /// driven to completion to accept an incoming WebTransport session.
609    ///
610    /// The returned future resolves to a [`SessionRequest`] once the QUIC connection
611    /// is established and the WebTransport session has been accepted.
612    #[cfg(feature = "quinn")]
613    #[cfg_attr(docsrs, doc(cfg(feature = "quinn")))]
614    pub fn with_quic_connecting(quic_connecting: quinn::Connecting) -> Self {
615        Self(Box::pin(async move {
616            let quic_connection = quic_connecting.await?;
617            Self::accept(quic_connection).await
618        }))
619    }
620
621    fn new(quic_incoming: quinn::Incoming) -> Self {
622        Self(Box::pin(async move {
623            let quic_connection = quic_incoming.await?;
624            Self::accept(quic_connection).await
625        }))
626    }
627
628    async fn accept(quic_connection: quinn::Connection) -> Result<SessionRequest, ConnectionError> {
629        let driver = Driver::init(quic_connection.clone());
630
631        let _settings = driver.accept_settings().await.map_err(|driver_error| {
632            ConnectionError::with_driver_error(driver_error, &quic_connection)
633        })?;
634
635        // TODO(biagio): validate settings
636
637        let stream_session = driver.accept_session().await.map_err(|driver_error| {
638            ConnectionError::with_driver_error(driver_error, &quic_connection)
639        })?;
640
641        Ok(SessionRequest::new(quic_connection, driver, stream_session))
642    }
643}
644
645impl Future for IncomingSessionFuture {
646    type Output = Result<SessionRequest, ConnectionError>;
647
648    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
649        Future::poll(self.0.as_mut(), cx)
650    }
651}
652
653/// A incoming client session request.
654///
655/// Server should use methods [`accept`](Self::accept), [`forbidden`](Self::forbidden),
656/// or [`not_found`](Self::not_found) in order to validate or reject the client request.
657pub struct SessionRequest {
658    quic_connection: quinn::Connection,
659    driver: Driver,
660    stream_session: StreamSession,
661}
662
663impl SessionRequest {
664    pub(crate) fn new(
665        quic_connection: quinn::Connection,
666        driver: Driver,
667        stream_session: StreamSession,
668    ) -> Self {
669        Self {
670            quic_connection,
671            driver,
672            stream_session,
673        }
674    }
675
676    /// Returns the peer's UDP address.
677    ///
678    /// **Note**: as QUIC supports migration, remote address may change
679    /// during connection. Furthermore, when IPv6 support is enabled, IPv4
680    /// addresses may be mapped to IPv6.
681    #[inline(always)]
682    pub fn remote_address(&self) -> SocketAddr {
683        self.quic_connection.remote_address()
684    }
685
686    /// Returns the `:authority` field of the request.
687    pub fn authority(&self) -> &str {
688        self.stream_session.request().authority()
689    }
690
691    /// Returns the `:path` field of the request.
692    pub fn path(&self) -> &str {
693        self.stream_session.request().path()
694    }
695
696    /// Returns the `origin` field of the request if present.
697    pub fn origin(&self) -> Option<&str> {
698        self.stream_session.request().origin()
699    }
700
701    /// Returns the `user-agent` field of the request if present.
702    pub fn user_agent(&self) -> Option<&str> {
703        self.stream_session.request().user_agent()
704    }
705
706    /// Returns all header fields associated with the request.
707    pub fn headers(&self) -> &HashMap<String, String> {
708        self.stream_session.request().headers().as_ref()
709    }
710
711    /// Accepts the client request with headers.
712    pub async fn accept_with_headers<I, K, V>(
713        self,
714        headers: I,
715    ) -> Result<Connection, ConnectionError>
716    where
717        I: IntoIterator<Item = (K, V)>,
718        K: Into<String>,
719        V: Into<String>,
720    {
721        self.accept_impl(
722            headers
723                .into_iter()
724                .map(|(k, v)| (k.into(), v.into()))
725                .collect(),
726        )
727        .await
728    }
729
730    /// Accepts the client request and it establishes the WebTransport session.
731    pub async fn accept(self) -> Result<Connection, ConnectionError> {
732        self.accept_impl(HashMap::new()).await
733    }
734
735    /// Rejects the client request by replying with `403` status code.
736    pub async fn forbidden(self) {
737        self.reject(SessionResponseProto::forbidden()).await;
738    }
739
740    /// Rejects the client request by replying with `404` status code.
741    pub async fn not_found(self) {
742        self.reject(SessionResponseProto::not_found()).await;
743    }
744
745    /// Rejects the client request by replying with `429` status code.
746    pub async fn too_many_requests(self) {
747        self.reject(SessionResponseProto::too_many_requests()).await;
748    }
749
750    async fn accept_impl(
751        mut self,
752        headers: HashMap<String, String>,
753    ) -> Result<Connection, ConnectionError> {
754        let mut response = SessionResponseProto::ok();
755
756        for (key, value) in headers {
757            response.add(key, value);
758        }
759
760        self.send_response(response).await?;
761
762        let session_id = self.stream_session.session_id();
763
764        self.driver
765            .register_session(self.stream_session)
766            .await
767            .map_err(|driver_error| {
768                ConnectionError::with_driver_error(driver_error, &self.quic_connection)
769            })?;
770
771        Ok(Connection::new(
772            self.quic_connection,
773            self.driver,
774            session_id,
775        ))
776    }
777
778    async fn reject(mut self, response: SessionResponseProto) {
779        let _ = self.send_response(response).await;
780        self.stream_session.finish().await;
781    }
782
783    async fn send_response(
784        &mut self,
785        response: SessionResponseProto,
786    ) -> Result<(), ConnectionError> {
787        let frame = response.headers().generate_frame();
788
789        match self.stream_session.write_frame(frame).await {
790            Ok(()) => Ok(()),
791            Err(ProtoWriteError::NotConnected) => {
792                Err(ConnectionError::no_connect(&self.quic_connection))
793            }
794            Err(ProtoWriteError::Stopped) => {
795                self.quic_connection
796                    .close(varint_w2q(ErrorCode::ClosedCriticalStream.to_code()), b"");
797
798                Err(ConnectionError::local_h3_error(
799                    ErrorCode::ClosedCriticalStream,
800                ))
801            }
802        }
803    }
804}