1#![cfg_attr(docsrs, feature(doc_cfg))]
21use hyper::server::conn::AddrIncoming;
77use hyper::server::{Builder, Server};
78#[cfg(feature = "tls-openssl")]
79use openssl::pkey::PKey;
80#[cfg(feature = "tls-openssl")]
81use openssl::ssl::{SslContext, SslContextBuilder, SslFiletype, SslMethod, SslRef};
82#[cfg(feature = "tls-openssl")]
83use openssl::x509::X509;
84#[cfg(feature = "tls-rustls")]
85use rustls::ServerConfig;
86#[cfg(feature = "tls-rustls")]
87use rustls_pemfile;
88use std::net::SocketAddr;
89use std::path::Path;
90#[cfg(any(feature = "tls-rustls", feature = "tls-openssl"))]
91use tls_listener::hyper::WrappedAccept;
92#[cfg(feature = "tls-rustls")]
93use tokio_rustls::rustls::{Certificate, PrivateKey};
94#[cfg(feature = "tls-rustls")]
95use tokio_rustls::TlsAcceptor;
96
97pub use hyper;
98#[cfg(feature = "tls-openssl")]
99pub use openssl;
100#[cfg(feature = "tls-rustls")]
101pub use rustls;
102#[cfg(any(feature = "tls-rustls", feature = "tls-openssl"))]
103pub use tls_listener;
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub enum Protocols {
108 ALL,
110 #[cfg(feature = "hyper-h1")]
111 #[cfg_attr(docsrs, doc(cfg(feature = "hyper-h1")))]
112 HTTP1,
114 #[cfg(feature = "hyper-h2")]
115 #[cfg_attr(docsrs, doc(cfg(feature = "hyper-h2")))]
116 HTTP2,
118}
119
120pub type Error = Box<dyn std::error::Error>;
122
123#[cfg(feature = "tls-rustls")]
124fn rustls_server_config_from_readers<R: std::io::Read>(
125 cert: R,
126 key: R,
127 protocols: Protocols,
128) -> Result<ServerConfig, Error> {
129 use std::io::{self, BufReader};
130 let certs = rustls_pemfile::certs(&mut BufReader::new(cert))
132 .map(|mut certs| certs.drain(..).map(Certificate).collect())?;
133 let mut keys: Vec<PrivateKey> = rustls_pemfile::pkcs8_private_keys(&mut BufReader::new(key))
134 .map(|mut keys| keys.drain(..).map(PrivateKey).collect())?;
135 let mut config = ServerConfig::builder()
136 .with_safe_defaults()
137 .with_no_client_auth()
138 .with_single_cert(certs, keys.remove(0))
139 .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?;
140
141 config.alpn_protocols = match protocols {
143 #[cfg(all(feature = "hyper-h1", feature = "hyper-h2"))]
144 Protocols::ALL => vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()],
145
146 #[cfg(all(feature = "hyper-h1", not(feature = "hyper-h2")))]
147 Protocols::ALL => vec![b"http/1.1".to_vec(), b"http/1.0".to_vec()],
148
149 #[cfg(all(not(feature = "hyper-h1"), feature = "hyper-h2"))]
150 Protocols::ALL => vec![b"h2".to_vec()],
151
152 #[cfg(feature = "hyper-h1")]
153 Protocols::HTTP1 => vec![b"http/1.1".to_vec(), b"http/1.0".to_vec()],
154 #[cfg(feature = "hyper-h2")]
155 Protocols::HTTP2 => vec![b"h2".to_vec()],
156 };
157 Ok(config)
158}
159
160#[cfg(feature = "tls-rustls")]
161#[cfg_attr(docsrs, doc(cfg(feature = "tls-rustls")))]
162pub fn rustls_server_config_from_pem_files<P: AsRef<Path>, Q: AsRef<Path>>(
167 cert_file: P,
168 key_file: Q,
169 protocols: Protocols,
170) -> Result<ServerConfig, Error> {
171 use std::fs::File;
172 rustls_server_config_from_readers(File::open(cert_file)?, File::open(key_file)?, protocols)
173}
174
175#[cfg(feature = "tls-rustls")]
176#[cfg_attr(docsrs, doc(cfg(feature = "tls-rustls")))]
177pub fn rustls_server_config_from_pem_data<'a>(
182 cert: &'a [u8],
183 key: &'a [u8],
184 protocols: Protocols,
185) -> Result<ServerConfig, Error> {
186 rustls_server_config_from_readers(cert, key, protocols)
187}
188
189#[cfg(feature = "tls-openssl")]
190fn ssl_context_set_alpns(
191 builder: &mut SslContextBuilder,
192 protocols: Protocols,
193) -> Result<(), Error> {
194 let protos = match protocols {
196 #[cfg(all(feature = "hyper-h1", feature = "hyper-h2"))]
197 Protocols::ALL => &b"\x02h2\x08http/1.1\x08http/1.0"[..],
198
199 #[cfg(all(feature = "hyper-h1", not(feature = "hyper-h2")))]
200 Protocols::ALL => &b"\x08http/1.1\x08http/1.0"[..],
201
202 #[cfg(all(not(feature = "hyper-h1"), feature = "hyper-h2"))]
203 Protocols::ALL => &b"\x02h2"[..],
204
205 #[cfg(feature = "hyper-h1")]
206 Protocols::HTTP1 => &b"\x08http/1.1\x08http/1.0"[..],
207 #[cfg(feature = "hyper-h2")]
208 Protocols::HTTP2 => &b"\x02h2"[..],
209 };
210 builder.set_alpn_protos(protos)?;
211 builder.set_alpn_select_callback(move |_: &mut SslRef, list: &[u8]| {
213 openssl::ssl::select_next_proto(protos, list).ok_or(openssl::ssl::AlpnError::NOACK)
214 });
215 Ok(())
216}
217
218#[cfg(feature = "tls-openssl")]
219#[cfg_attr(docsrs, doc(cfg(feature = "tls-openssl")))]
220pub fn ssl_context_builder_from_pem_files<P: AsRef<Path>, Q: AsRef<Path>>(
225 cert_file: P,
226 key_file: Q,
227 protocols: Protocols,
228) -> Result<SslContextBuilder, Error> {
229 let mut builder = SslContext::builder(SslMethod::tls_server()).unwrap();
230 builder.set_certificate_chain_file(cert_file)?;
231 builder.set_private_key_file(key_file, SslFiletype::PEM)?;
232 ssl_context_set_alpns(&mut builder, protocols)?;
233 Ok(builder)
234}
235
236#[cfg(feature = "tls-openssl")]
237#[cfg_attr(docsrs, doc(cfg(feature = "tls-openssl")))]
238pub fn ssl_context_builder_from_pem_data<'a>(
243 cert: &'a [u8],
244 key: &'a [u8],
245 protocols: Protocols,
246) -> Result<SslContextBuilder, Error> {
247 let mut builder = SslContext::builder(SslMethod::tls_server()).unwrap();
248 let mut certs = X509::stack_from_pem(cert)?;
249 let mut certs = certs.drain(..);
250 builder.set_certificate(certs.next().ok_or("no leaf certificate")?.as_ref())?;
251 certs.try_for_each(|cert| builder.add_extra_chain_cert(cert))?;
252 builder.set_private_key(PKey::private_key_from_pem(key)?.as_ref())?;
253 ssl_context_set_alpns(&mut builder, protocols)?;
254 Ok(builder)
255}
256
257#[cfg(feature = "tls-rustls")]
258pub type TlsListener = tls_listener::TlsListener<WrappedAccept<AddrIncoming>, TlsAcceptor>;
260#[cfg(all(not(docsrs), feature = "tls-openssl"))]
261pub type TlsListener = tls_listener::TlsListener<WrappedAccept<AddrIncoming>, SslContext>;
263
264pub fn listener_from_pem_files<P: AsRef<Path>, Q: AsRef<Path>>(
274 cert_file: P,
275 key_file: Q,
276 protocols: Protocols,
277 addr: &SocketAddr,
278) -> Result<TlsListener, Error> {
279 #[cfg(feature = "tls-rustls")]
280 let acceptor = {
281 use std::sync::Arc;
282 let config = rustls_server_config_from_pem_files(cert_file, key_file, protocols)?;
283 TlsAcceptor::from(Arc::new(config))
284 };
285 #[cfg(feature = "tls-openssl")]
286 let acceptor = {
287 let builder = ssl_context_builder_from_pem_files(cert_file, key_file, protocols)?;
288 builder.build()
289 };
290 Ok(TlsListener::new_hyper(acceptor, AddrIncoming::bind(addr)?))
291}
292
293pub fn listener_from_pem_data<'a>(
303 cert: &'a [u8],
304 key: &'a [u8],
305 protocols: Protocols,
306 addr: &SocketAddr,
307) -> Result<TlsListener, Error> {
308 #[cfg(feature = "tls-rustls")]
309 let acceptor = {
310 use std::sync::Arc;
311 let config = rustls_server_config_from_pem_data(cert, key, protocols)?;
312 TlsAcceptor::from(Arc::new(config))
313 };
314 #[cfg(feature = "tls-openssl")]
315 let acceptor = {
316 let builder = ssl_context_builder_from_pem_data(cert, key, protocols)?;
317 builder.build()
318 };
319 Ok(TlsListener::new_hyper(acceptor, AddrIncoming::bind(&addr)?))
320}
321
322pub fn hyper_from_pem_files<P: AsRef<Path>, Q: AsRef<Path>>(
332 cert_file: P,
333 key_file: Q,
334 protocols: Protocols,
335 addr: &SocketAddr,
336) -> Result<Builder<TlsListener>, Error> {
337 let listener = listener_from_pem_files(cert_file, key_file, protocols, addr)?;
338 let builder = Server::builder(listener);
339 Ok(match protocols {
340 Protocols::ALL => builder,
341 #[cfg(feature = "hyper-h1")]
342 Protocols::HTTP1 => builder.http1_only(true),
343 #[cfg(feature = "hyper-h2")]
344 Protocols::HTTP2 => builder.http2_only(true),
345 })
346}
347
348pub fn hyper_from_pem_data<'a>(
358 cert: &'a [u8],
359 key: &'a [u8],
360 protocols: Protocols,
361 addr: &SocketAddr,
362) -> Result<Builder<TlsListener>, Error> {
363 let listener = listener_from_pem_data(cert, key, protocols, addr)?;
364 let builder = Server::builder(listener);
365 Ok(match protocols {
366 Protocols::ALL => builder,
367 #[cfg(feature = "hyper-h1")]
368 Protocols::HTTP1 => builder.http1_only(true),
369 #[cfg(feature = "hyper-h2")]
370 Protocols::HTTP2 => builder.http2_only(true),
371 })
372}
373
374#[cfg(test)]
375mod tests {
376 #[test]
377 #[cfg(feature = "tls-rustls")]
378 fn test_rustls_server_config_from_readers() {
379 use super::*;
380 const CERT: &[u8] = include_bytes!("../data/cert.pem");
381 const KEY: &[u8] = include_bytes!("../data/key.pem");
382 let config = rustls_server_config_from_readers(CERT, KEY, Protocols::ALL).unwrap();
383 #[cfg(all(feature = "hyper-h1", feature = "hyper-h2"))]
384 assert_eq!(
385 config.alpn_protocols,
386 vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()]
387 );
388 #[cfg(all(not(feature = "hyper-h1"), feature = "hyper-h2"))]
389 assert_eq!(config.alpn_protocols, vec![b"h2".to_vec()]);
390 #[cfg(all(feature = "hyper-h1", not(feature = "hyper-h2")))]
391 assert_eq!(
392 config.alpn_protocols,
393 vec![b"http/1.1".to_vec(), b"http/1.0".to_vec()]
394 );
395
396 #[cfg(feature = "hyper-h1")]
397 {
398 let config = rustls_server_config_from_readers(CERT, KEY, Protocols::HTTP1).unwrap();
399 assert_eq!(
400 config.alpn_protocols,
401 vec![b"http/1.1".to_vec(), b"http/1.0".to_vec()]
402 );
403 }
404 #[cfg(feature = "hyper-h2")]
405 {
406 let config = rustls_server_config_from_readers(CERT, KEY, Protocols::HTTP2).unwrap();
407 assert_eq!(config.alpn_protocols, vec![b"h2".to_vec()]);
408 }
409 }
410}