use std::io::{Read, Write};
use std::net::TcpStream;
use std::sync::Arc;
use rustls::pki_types::ServerName;
use rustls::{ClientConfig, ClientConnection, RootCertStore, Stream};
use crate::error::Error;
pub fn exchange_https(host: &str, port: u16, request_bytes: &[u8]) -> Result<Vec<u8>, Error> {
install_default_crypto_provider();
let address = format!("{host}:{port}");
let stream = TcpStream::connect(address)?;
let config = make_client_config();
let server_name = ServerName::try_from(host.to_owned()).map_err(|_| Error::InvalidUrl {
source: host.to_owned(),
})?;
let connection =
ClientConnection::new(Arc::new(config), server_name).map_err(|err| Error::Tls {
message: format!("{err}"),
})?;
transmit(connection, stream, request_bytes)
}
pub(crate) fn install_default_crypto_provider() {
let _ = rustls::crypto::ring::default_provider().install_default();
}
pub(crate) fn make_client_config() -> ClientConfig {
let root_store: RootCertStore = webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect();
ClientConfig::builder()
.with_root_certificates(root_store)
.with_no_client_auth()
}
fn transmit(
connection: ClientConnection,
stream: TcpStream,
request_bytes: &[u8],
) -> Result<Vec<u8>, Error> {
let mut connection = connection;
let mut stream = stream;
let mut tls = Stream::new(&mut connection, &mut stream);
tls.write_all(request_bytes)
.map_err(|err| io_to_tls_error(&err))?;
tls.flush().map_err(|err| io_to_tls_error(&err))?;
let mut buffer = Vec::new();
tls.read_to_end(&mut buffer)
.map_err(|err| io_to_tls_error(&err))?;
Ok(buffer)
}
fn io_to_tls_error(err: &std::io::Error) -> Error {
Error::Tls {
message: format!("{err}"),
}
}