use std::sync::Arc;
use tokio::net::{TcpStream, ToSocketAddrs};
use tokio_rustls::TlsAcceptor;
use tokio_rustls::TlsConnector;
use crate::transport::StreamTransport;
use crate::{alpn, Config, Error, Session, Version};
pub async fn connect<'a>(
addr: impl ToSocketAddrs,
server_name: &str,
config: Arc<rustls::ClientConfig>,
entries: impl IntoIterator<Item = (&'a str, &'a [Version])>,
require_protocol: bool,
) -> Result<Session, Error> {
let stream = TcpStream::connect(&addr).await?;
let server_name = rustls::pki_types::ServerName::try_from(server_name)
.map_err(|_| Error::InvalidServerName)?
.to_owned();
let prefixed = alpn::build(entries, require_protocol);
let mut config = (*config).clone();
config.alpn_protocols = prefixed.iter().map(|s| s.as_bytes().to_vec()).collect();
tracing::debug!(?prefixed, "TLS connecting");
let connector = TlsConnector::from(Arc::new(config));
let tls_stream = connector.connect(server_name, stream).await?;
let negotiated = tls_stream.get_ref().1.alpn_protocol();
let negotiated_str = negotiated.and_then(|a| std::str::from_utf8(a).ok());
tracing::debug!(?negotiated_str, "TLS negotiated ALPN");
let (version, protocol) = alpn::parse(negotiated_str);
tracing::debug!(?version, ?protocol, "parsed ALPN");
if require_protocol && protocol.is_none() {
return Err(Error::InvalidProtocol(
negotiated_str.unwrap_or("<none>").to_string(),
));
}
let session_config = Config::new(version, protocol);
let transport = StreamTransport::new(tls_stream, version, session_config.max_record_size);
Ok(Session::connect(transport, session_config))
}
pub async fn accept(
stream: TcpStream,
config: Arc<rustls::ServerConfig>,
) -> Result<Session, Error> {
let acceptor = TlsAcceptor::from(config);
let tls_stream = acceptor.accept(stream).await?;
let negotiated = tls_stream.get_ref().1.alpn_protocol();
let negotiated_str = negotiated.and_then(|a| std::str::from_utf8(a).ok());
tracing::debug!(?negotiated_str, "TLS accepted, negotiated ALPN");
let (version, protocol) = alpn::parse(negotiated_str);
tracing::debug!(?version, ?protocol, "parsed ALPN");
let session_config = Config::new(version, protocol);
let transport = StreamTransport::new(tls_stream, version, session_config.max_record_size);
Ok(Session::accept(transport, session_config))
}