Compare commits

..

2 commits

Author SHA1 Message Date
fa7626b78a
fix(example): args parser 2024-08-05 16:09:59 +04:00
1d6e22238c
feat: ClientBuilder 2024-08-05 16:09:39 +04:00
5 changed files with 133 additions and 7 deletions

View file

@ -9,10 +9,11 @@ async fn main() -> Result<(), tokio_gemini::LibError> {
let mut args = std::env::args(); let mut args = std::env::args();
let mut insecure = false; let mut insecure = false;
let mut url = "gemini://geminiprotocol.net/".to_owned(); let mut url = "gemini://geminiprotocol.net/".to_owned();
if let Some(arg) = args.nth(1) { _ = args.next(); // skip exe path
if let Some(arg) = args.next() {
if arg == "-k" { if arg == "-k" {
insecure = true; insecure = true;
if let Some(arg) = args.nth(2) { if let Some(arg) = args.next() {
url = arg; url = arg;
} }
} else { } else {

116
src/builder.rs Normal file
View file

@ -0,0 +1,116 @@
use std::sync::Arc;
use crate::{
certs::verifier::{CustomCertVerifier, SelfsignedCertVerifier},
Client,
};
use tokio_rustls::rustls::{
self,
client::{danger::ServerCertVerifier, WebPkiServerVerifier},
pki_types::TrustAnchor,
SupportedProtocolVersion,
};
pub struct ClientBuilder {
root_certs: rustls::RootCertStore,
ss_verifier: Option<Box<dyn SelfsignedCertVerifier>>,
custom_verifier: Option<Arc<dyn ServerCertVerifier + 'static>>,
tls_versions: Option<&'static [&'static SupportedProtocolVersion]>,
}
impl ClientBuilder {
pub fn new() -> Self {
ClientBuilder {
root_certs: rustls::RootCertStore::empty(),
ss_verifier: None,
custom_verifier: None,
tls_versions: None,
}
}
pub fn build(self) -> Client {
let provider = rustls::crypto::CryptoProvider::get_default()
.map(|c| c.clone())
.unwrap_or_else(|| Arc::new(rustls::crypto::ring::default_provider()));
let tls_config = rustls::ClientConfig::builder_with_provider(provider.clone())
.with_protocol_versions(if let Some(versions) = self.tls_versions {
versions
} else {
rustls::DEFAULT_VERSIONS
})
.unwrap();
let tls_config = if let Some(cv) = self.custom_verifier {
tls_config.dangerous().with_custom_certificate_verifier(cv)
} else if let Some(ssv) = self.ss_verifier {
tls_config
.dangerous()
.with_custom_certificate_verifier(Arc::new(CustomCertVerifier {
provider: provider.clone(),
webpki_verifier: if !self.root_certs.is_empty() {
Some(
WebPkiServerVerifier::builder_with_provider(
Arc::new(self.root_certs),
provider,
)
.build()
// panics only if roots are empty (that is checked above)
// or CRLs couldn't be parsed (we didn't provide any)
.unwrap(),
)
} else {
None
},
ss_allowed: true,
ss_verifier: ssv,
}))
} else {
tls_config.with_root_certificates(self.root_certs)
};
// TODO
let tls_config = tls_config.with_no_client_auth();
Client::from(tls_config)
}
pub fn with_webpki_roots(mut self) -> Self {
self.root_certs
.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
self
}
pub fn with_custom_roots(
mut self,
iter: impl IntoIterator<Item = TrustAnchor<'static>>,
) -> Self {
self.root_certs.extend(iter);
self
}
pub fn with_selfsigned_cert_verifier(
mut self,
ss_verifier: impl SelfsignedCertVerifier + 'static,
) -> Self {
self.ss_verifier = Some(Box::new(ss_verifier));
self
}
pub fn with_custom_verifier(
mut self,
custom_verifier: impl ServerCertVerifier + 'static,
) -> Self {
self.custom_verifier = Some(Arc::new(custom_verifier));
self
}
pub fn with_tls_versions(
mut self,
versions: &'static [&'static SupportedProtocolVersion],
) -> Self {
self.tls_versions = Some(versions);
self
}
}

View file

@ -1,3 +1,5 @@
use std::sync::Arc;
pub use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime}; pub use tokio_rustls::rustls::pki_types::{CertificateDer, ServerName, UnixTime};
use tokio_rustls::rustls::{ use tokio_rustls::rustls::{
@ -21,10 +23,10 @@ pub struct SelfsignedCert {
} }
pub struct CustomCertVerifier { pub struct CustomCertVerifier {
provider: rustls::crypto::CryptoProvider, pub(crate) provider: Arc<rustls::crypto::CryptoProvider>,
webpki_verifier: Option<rustls::client::WebPkiServerVerifier>, pub(crate) webpki_verifier: Option<Arc<rustls::client::WebPkiServerVerifier>>,
ss_allowed: bool, pub(crate) ss_allowed: bool,
ss_verifier: dyn SelfsignedCertVerifier, pub(crate) ss_verifier: Box<dyn SelfsignedCertVerifier>,
} }
impl ServerCertVerifier for CustomCertVerifier { impl ServerCertVerifier for CustomCertVerifier {

View file

@ -1,4 +1,4 @@
use crate::{error::*, response::Response, status::*}; use crate::{builder::ClientBuilder, error::*, response::Response, status::*};
use std::net::ToSocketAddrs; use std::net::ToSocketAddrs;
use std::sync::Arc; use std::sync::Arc;
@ -37,6 +37,12 @@ impl From<rustls::ClientConfig> for Client {
} }
} }
impl Client {
pub fn builder() -> ClientBuilder {
ClientBuilder::new()
}
}
impl Client { impl Client {
pub async fn request(self: &Self, url_str: &str) -> Result<Response, LibError> { pub async fn request(self: &Self, url_str: &str) -> Result<Response, LibError> {
let url = Url::parse(url_str).map_err(|e| InvalidUrl::ParseError(e))?; let url = Url::parse(url_str).map_err(|e| InvalidUrl::ParseError(e))?;

View file

@ -1,3 +1,4 @@
pub mod builder;
pub mod certs; pub mod certs;
pub mod client; pub mod client;
pub mod error; pub mod error;