docs: add doc-comments to lib.rs and client

This commit is contained in:
DarkCat09 2024-08-07 20:10:23 +04:00
parent 307e54f256
commit 39b1009048
Signed by: DarkCat09
GPG key ID: 0A26CD5B3345D6E3
3 changed files with 57 additions and 3 deletions

View file

@ -20,12 +20,15 @@ pub struct ClientBuilder {
} }
impl Default for ClientBuilder { impl Default for ClientBuilder {
/// Same as [`ClientBuilder::new()`].
fn default() -> Self { fn default() -> Self {
Self::new() Self::new()
} }
} }
impl ClientBuilder { impl ClientBuilder {
/// Instantiate a builder with empty [`rustls::RootCertStore`],
/// no cert verifiers and default TLS versions.
pub fn new() -> Self { pub fn new() -> Self {
ClientBuilder { ClientBuilder {
root_certs: rustls::RootCertStore::empty(), root_certs: rustls::RootCertStore::empty(),
@ -35,6 +38,7 @@ impl ClientBuilder {
} }
} }
/// Construct a [`Client`] from the configuration.
pub fn build(self) -> Client { pub fn build(self) -> Client {
let provider = rustls::crypto::CryptoProvider::get_default() let provider = rustls::crypto::CryptoProvider::get_default()
.cloned() .cloned()
@ -82,12 +86,17 @@ impl ClientBuilder {
Client::from(tls_config) Client::from(tls_config)
} }
/// Include webpki trust anchors.
/// Not recommended (useless) as most Gemini capsules use self-signed
/// TLS certs and properly configured TOFU policy is enough.
pub fn with_webpki_roots(mut self) -> Self { pub fn with_webpki_roots(mut self) -> Self {
self.root_certs self.root_certs
.extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); .extend(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
self self
} }
/// Include custom trust anchors.
/// Not recommended (useless), see note for [`ClientBuilder::with_webpki_roots`].
pub fn with_custom_roots( pub fn with_custom_roots(
mut self, mut self,
iter: impl IntoIterator<Item = TrustAnchor<'static>>, iter: impl IntoIterator<Item = TrustAnchor<'static>>,
@ -96,6 +105,10 @@ impl ClientBuilder {
self self
} }
/// Include a self-signed cert verifier.
/// If you only need a known_hosts file, consider using
/// [`crate::certs::file_sscv::FileBasedCertVerifier`],
/// it will do all the work for you.
pub fn with_selfsigned_cert_verifier( pub fn with_selfsigned_cert_verifier(
mut self, mut self,
ss_verifier: impl SelfsignedCertVerifier + 'static, ss_verifier: impl SelfsignedCertVerifier + 'static,
@ -104,6 +117,10 @@ impl ClientBuilder {
self self
} }
/// Include a custom TLS cert verifier implementing rustls' [`ServerCertVerifier`].
/// Normally need to be used only for [`crate::certs::insecure::AllowAllCertVerifier`].
/// Note: the webpki verifier and a self-signed cert verifier are not called
/// when a custom verifier is set.
pub fn with_custom_verifier( pub fn with_custom_verifier(
mut self, mut self,
custom_verifier: impl ServerCertVerifier + 'static, custom_verifier: impl ServerCertVerifier + 'static,
@ -112,6 +129,8 @@ impl ClientBuilder {
self self
} }
/// Limit the supported TLS versions list to the specified ones.
/// Default is [`rustls::DEFAULT_VERSIONS`].
pub fn with_tls_versions( pub fn with_tls_versions(
mut self, mut self,
versions: &'static [&'static SupportedProtocolVersion], versions: &'static [&'static SupportedProtocolVersion],

View file

@ -1,3 +1,5 @@
//! Everything related to Gemini client except cert verification
pub mod builder; pub mod builder;
use crate::{error::*, response::Response, status::*}; use crate::{error::*, response::Response, status::*};
@ -21,6 +23,8 @@ pub struct Client {
} }
impl Default for Client { impl Default for Client {
/// Create a Client with webpki_roots trust anchors and no client auth cert.
/// Will be possibly removed in next versions.
fn default() -> Self { fn default() -> Self {
let roots = let roots =
rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned()); rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
@ -32,6 +36,7 @@ impl Default for Client {
} }
impl From<rustls::ClientConfig> for Client { impl From<rustls::ClientConfig> for Client {
/// Create a Client from a Rustls config.
#[inline] #[inline]
fn from(config: rustls::ClientConfig) -> Self { fn from(config: rustls::ClientConfig) -> Self {
Client { Client {
@ -41,18 +46,30 @@ impl From<rustls::ClientConfig> for Client {
} }
impl Client { impl Client {
/// Create a Client with a customized configuration,
/// see [`ClientBuilder`] methods.
pub fn builder() -> ClientBuilder { pub fn builder() -> ClientBuilder {
ClientBuilder::new() ClientBuilder::new()
} }
} }
impl Client { impl Client {
/// Parse the given URL, do some checks,
/// take host and port (default is 1965) from the URL
/// and perform a Gemini request, returning [`Response`] on success.
///
/// # Errors
/// Everything is converted into [`LibError`].
/// - [`InvalidUrl::ParseError`] means that the given URL cannot be parsed.
/// - [`InvalidUrl::SchemeNotGemini`] is returned when the scheme is not `gemini://`
/// (for proxying use [`Client::request_with_host`]).
/// - [`InvalidUrl::UserinfoPresent`] is returned when the URL contains userinfo -- `user:password@` --
/// which is forbidden by the Gemini specs.
/// - For the rest, see [`Client::request_with_host`].
pub async fn request(&self, url_str: &str) -> Result<Response, LibError> { pub async fn request(&self, url_str: &str) -> Result<Response, LibError> {
let url = Url::parse(url_str).map_err(InvalidUrl::ParseError)?; let url = Url::parse(url_str).map_err(InvalidUrl::ParseError)?;
// for proxying http(s) through gemini server, // deny non-Gemini requests
// use Client::request_with_host
if url.scheme() != "gemini" { if url.scheme() != "gemini" {
// deny non-Gemini req
return Err(InvalidUrl::SchemeNotGemini.into()); return Err(InvalidUrl::SchemeNotGemini.into());
} }
// userinfo (user:pswd@) is not allowed in Gemini // userinfo (user:pswd@) is not allowed in Gemini
@ -66,6 +83,21 @@ impl Client {
self.request_with_host(url_str, host, port).await self.request_with_host(url_str, host, port).await
} }
/// Perform a Gemini request with the specified host, port and URL.
/// Non-`gemini://` URLs is OK if the remote server supports proxying.
///
/// # Errors
/// Everything is converted into [`LibError`].
/// - [`InvalidUrl::ConvertError`] is an error while converting
/// host and port into [`std::net::SocketAddr`] or [`pki_types::ServerName`].
/// - [`std::io::Error`] is returned in nearly all cases:
/// could not open a TCP connection, perform a TLS handshake,
/// write to or read from the TCP stream.
/// Check the inner error if you need to determine what exactly happened
/// - [`LibError::StatusOutOfRange`] means that a server returned an incorrect status code
/// (less than 10 or greater than 69).
/// - [`LibError::MessageNotUtf8`] is returned when metadata (the text after the status code)
/// is not in UTF-8 and cannot be converted to string without errors.
pub async fn request_with_host( pub async fn request_with_host(
&self, &self,
url_str: &str, url_str: &str,

View file

@ -1,3 +1,6 @@
//! Gemini client and server implementaion in async Rust with Tokio and Rustls.
//! You probably need [`Client::builder()`]
pub mod certs; pub mod certs;
pub mod client; pub mod client;
pub mod error; pub mod error;