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 std::{io, net::SocketAddr, sync::Arc};
11
12use bytes::Bytes;
13use h3::server::{Connection, RequestStream};
14use h3_quinn::{BidiStream, Endpoint};
15use http::Request;
16use quinn::{EndpointConfig, ServerConfig};
17use rustls::{server::ServerConfig as TlsServerConfig, version::TLS13, Certificate, PrivateKey};
18
19use crate::{error::ProtoError, udp::UdpSocket};
20
21use super::ALPN_H3;
22
23/// A DNS-over-HTTP/3 Server, see H3ClientStream for the client counterpart
24pub struct H3Server {
25    endpoint: Endpoint,
26}
27
28impl H3Server {
29    /// Construct the new Acceptor with the associated pkcs12 data
30    pub async fn new(
31        name_server: SocketAddr,
32        cert: Vec<Certificate>,
33        key: PrivateKey,
34    ) -> Result<Self, ProtoError> {
35        // setup a new socket for the server to use
36        let socket = <tokio::net::UdpSocket as UdpSocket>::bind(name_server).await?;
37        Self::with_socket(socket, cert, key)
38    }
39
40    /// Construct the new server with an existing socket
41    pub fn with_socket(
42        socket: tokio::net::UdpSocket,
43        cert: Vec<Certificate>,
44        key: PrivateKey,
45    ) -> Result<Self, ProtoError> {
46        let mut config = TlsServerConfig::builder()
47            .with_safe_default_cipher_suites()
48            .with_safe_default_kx_groups()
49            .with_protocol_versions(&[&TLS13])
50            .expect("TLS1.3 not supported")
51            .with_no_client_auth()
52            .with_single_cert(cert, key)?;
53
54        config.alpn_protocols = vec![ALPN_H3.to_vec()];
55
56        let mut server_config = ServerConfig::with_crypto(Arc::new(config));
57        server_config.transport = Arc::new(super::transport());
58
59        let socket = socket.into_std()?;
60
61        let endpoint = Endpoint::new(
62            EndpointConfig::default(),
63            Some(server_config),
64            socket,
65            Arc::new(quinn::TokioRuntime),
66        )?;
67
68        Ok(Self { endpoint })
69    }
70
71    /// Accept the next incoming connection.
72    ///
73    /// # Returns
74    ///
75    /// A remote connection that could accept many potential requests and the remote socket address
76    pub async fn accept(&mut self) -> Result<Option<(H3Connection, SocketAddr)>, ProtoError> {
77        let connecting = match self.endpoint.accept().await {
78            Some(conn) => conn,
79            None => return Ok(None),
80        };
81
82        let remote_addr = connecting.remote_address();
83        let connection = connecting.await?;
84        Ok(Some((
85            H3Connection {
86                connection: Connection::new(h3_quinn::Connection::new(connection))
87                    .await
88                    .map_err(|e| ProtoError::from(format!("h3 connection failed: {e}")))?,
89            },
90            remote_addr,
91        )))
92    }
93
94    /// Returns the address this server is listening on
95    ///
96    /// 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
97    ///   associated port address with this function.
98    pub fn local_addr(&self) -> Result<SocketAddr, io::Error> {
99        self.endpoint.local_addr()
100    }
101}
102
103/// A HTTP/3 connection.
104pub struct H3Connection {
105    connection: Connection<h3_quinn::Connection, Bytes>,
106}
107
108impl H3Connection {
109    /// Accept the next request from the client
110    pub async fn accept(
111        &mut self,
112    ) -> Option<Result<(Request<()>, RequestStream<BidiStream<Bytes>, Bytes>), ProtoError>> {
113        match self.connection.accept().await {
114            Ok(Some((request, stream))) => Some(Ok((request, stream))),
115            Ok(None) => None,
116            Err(e) => Some(Err(ProtoError::from(format!("h3 request failed: {e}")))),
117        }
118    }
119
120    /// Shutdown the connection.
121    pub async fn shutdown(&mut self) -> Result<(), ProtoError> {
122        self.connection
123            .shutdown(0)
124            .await
125            .map_err(|e| ProtoError::from(format!("h3 connection shutdown failed: {e}")))
126    }
127}