hickory_proto/h3/
h3_server.rs

1// Copyright 2015-2022 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! HTTP/3 related server items
9
10use alloc::sync::Arc;
11use std::{io, net::SocketAddr};
12
13use bytes::Bytes;
14use h3::server::{Connection, RequestStream};
15use h3_quinn::{BidiStream, Endpoint};
16use http::Request;
17use quinn::crypto::rustls::QuicServerConfig;
18use quinn::{EndpointConfig, ServerConfig};
19use rustls::server::ResolvesServerCert;
20use rustls::server::ServerConfig as TlsServerConfig;
21use rustls::version::TLS13;
22
23use crate::{error::ProtoError, rustls::default_provider, udp::UdpSocket};
24
25use super::ALPN_H3;
26
27/// A DNS-over-HTTP/3 Server, see H3ClientStream for the client counterpart
28pub struct H3Server {
29    endpoint: Endpoint,
30}
31
32impl H3Server {
33    /// Construct the new Acceptor with the associated pkcs12 data
34    pub async fn new(
35        name_server: SocketAddr,
36        server_cert_resolver: Arc<dyn ResolvesServerCert>,
37    ) -> Result<Self, ProtoError> {
38        // setup a new socket for the server to use
39        let socket = <tokio::net::UdpSocket as UdpSocket>::bind(name_server).await?;
40        Self::with_socket(socket, server_cert_resolver)
41    }
42
43    /// Construct the new server with an existing socket
44    pub fn with_socket(
45        socket: tokio::net::UdpSocket,
46        server_cert_resolver: Arc<dyn ResolvesServerCert>,
47    ) -> Result<Self, ProtoError> {
48        let mut config = TlsServerConfig::builder_with_provider(Arc::new(default_provider()))
49            .with_protocol_versions(&[&TLS13])
50            .expect("TLS1.3 not supported")
51            .with_no_client_auth()
52            .with_cert_resolver(server_cert_resolver);
53
54        config.alpn_protocols = vec![ALPN_H3.to_vec()];
55
56        let mut server_config =
57            ServerConfig::with_crypto(Arc::new(QuicServerConfig::try_from(config).unwrap()));
58        server_config.transport = Arc::new(super::transport());
59
60        let socket = socket.into_std()?;
61
62        let endpoint = Endpoint::new(
63            EndpointConfig::default(),
64            Some(server_config),
65            socket,
66            Arc::new(quinn::TokioRuntime),
67        )?;
68
69        Ok(Self { endpoint })
70    }
71
72    /// Accept the next incoming connection.
73    ///
74    /// # Returns
75    ///
76    /// A remote connection that could accept many potential requests and the remote socket address
77    pub async fn accept(&mut self) -> Result<Option<(H3Connection, SocketAddr)>, ProtoError> {
78        let connecting = match self.endpoint.accept().await {
79            Some(conn) => conn,
80            None => return Ok(None),
81        };
82
83        let remote_addr = connecting.remote_address();
84        let connection = connecting.await?;
85        Ok(Some((
86            H3Connection {
87                connection: Connection::new(h3_quinn::Connection::new(connection))
88                    .await
89                    .map_err(|e| ProtoError::from(format!("h3 connection failed: {e}")))?,
90            },
91            remote_addr,
92        )))
93    }
94
95    /// Returns the address this server is listening on
96    ///
97    /// This can be useful in tests, where a random port can be associated with the server by binding on `127.0.0.1:0` and then getting the
98    ///   associated port address with this function.
99    pub fn local_addr(&self) -> Result<SocketAddr, io::Error> {
100        self.endpoint.local_addr()
101    }
102}
103
104/// A HTTP/3 connection.
105pub struct H3Connection {
106    connection: Connection<h3_quinn::Connection, Bytes>,
107}
108
109impl H3Connection {
110    /// Accept the next request from the client
111    pub async fn accept(
112        &mut self,
113    ) -> Option<Result<(Request<()>, RequestStream<BidiStream<Bytes>, Bytes>), ProtoError>> {
114        match self.connection.accept().await {
115            Ok(Some((request, stream))) => Some(Ok((request, stream))),
116            Ok(None) => None,
117            Err(e) => Some(Err(ProtoError::from(format!("h3 request failed: {e}")))),
118        }
119    }
120
121    /// Shutdown the connection.
122    pub async fn shutdown(&mut self) -> Result<(), ProtoError> {
123        self.connection
124            .shutdown(0)
125            .await
126            .map_err(|e| ProtoError::from(format!("h3 connection shutdown failed: {e}")))
127    }
128}