diff --git a/README.md b/README.md index a565e9a4f5..b35b1d124b 100644 --- a/README.md +++ b/README.md @@ -393,6 +393,9 @@ Options: [default: 3] + --min-tls + Minimum accepted TLS Version + --max-concurrency Maximum number of concurrent network requests diff --git a/lychee-bin/src/client.rs b/lychee-bin/src/client.rs index 7c9e81270b..7cedc91f9f 100644 --- a/lychee-bin/src/client.rs +++ b/lychee-bin/src/client.rs @@ -76,6 +76,7 @@ pub(crate) fn create(cfg: &Config, cookie_jar: Option<&Arc>) - .accepted(accepted) .require_https(cfg.require_https) .cookie_jar(cookie_jar.cloned()) + .min_tls_version(cfg.min_tls.clone().map(Into::into)) .include_fragments(cfg.include_fragments) .fallback_extensions(cfg.fallback_extensions.clone()) .build() diff --git a/lychee-bin/src/options.rs b/lychee-bin/src/options.rs index 8eb038b360..4d343ee9d2 100644 --- a/lychee-bin/src/options.rs +++ b/lychee-bin/src/options.rs @@ -10,6 +10,7 @@ use lychee_lib::{ StatusCodeSelector, DEFAULT_MAX_REDIRECTS, DEFAULT_MAX_RETRIES, DEFAULT_RETRY_WAIT_TIME_SECS, DEFAULT_TIMEOUT_SECS, DEFAULT_USER_AGENT, }; +use reqwest::tls; use secrecy::{ExposeSecret, SecretString}; use serde::Deserialize; use std::path::Path; @@ -46,6 +47,34 @@ const HELP_MSG_CONFIG_FILE: &str = formatcp!( const TIMEOUT_STR: &str = concatcp!(DEFAULT_TIMEOUT_SECS); const RETRY_WAIT_TIME_STR: &str = concatcp!(DEFAULT_RETRY_WAIT_TIME_SECS); +#[derive(Debug, Display, Deserialize, Default, Clone, EnumString)] +#[non_exhaustive] +pub(crate) enum TlsVersion { + #[serde(rename = "TLSv1_0")] + #[strum(serialize = "TLSv1_0")] + V1_0, + #[serde(rename = "TLSv1_1")] + #[strum(serialize = "TLSv1_1")] + V1_1, + #[serde(rename = "TLSv1_2")] + #[strum(serialize = "TLSv1_2")] + #[default] + V1_2, + #[serde(rename = "TLSv1_3")] + #[strum(serialize = "TLSv1_3")] + V1_3, +} +impl From for tls::Version { + fn from(ver: TlsVersion) -> Self { + match ver { + TlsVersion::V1_0 => tls::Version::TLS_1_0, + TlsVersion::V1_1 => tls::Version::TLS_1_1, + TlsVersion::V1_2 => tls::Version::TLS_1_2, + TlsVersion::V1_3 => tls::Version::TLS_1_3, + } + } +} + /// The format to use for the final status report #[derive(Debug, Deserialize, Default, Clone, Display, EnumIter, VariantNames, PartialEq)] #[non_exhaustive] @@ -319,6 +348,11 @@ and 501." #[serde(default = "max_retries")] pub(crate) max_retries: u64, + /// Minimum accepted TLS Version + #[arg(long)] + #[serde(default)] + pub(crate) min_tls: Option, + /// Maximum number of concurrent network requests #[arg(long, default_value = &MAX_CONCURRENCY_STR)] #[serde(default = "max_concurrency")] diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 7fe8f08ca0..cd30cbb6dc 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -22,7 +22,7 @@ use http::{ use log::{debug, warn}; use octocrab::Octocrab; use regex::RegexSet; -use reqwest::{header, redirect}; +use reqwest::{header, redirect, tls}; use reqwest_cookie_store::CookieStoreMutex; use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; @@ -192,6 +192,9 @@ pub struct ClientBuilder { #[builder(default = DEFAULT_MAX_RETRIES)] max_retries: u64, + /// Minimum accepted TLS version. + min_tls_version: Option, + /// User-agent used for checking links. /// /// Defaults to [`DEFAULT_USER_AGENT`]. @@ -352,6 +355,10 @@ impl ClientBuilder { builder = builder.cookie_provider(cookie_jar); } + if let Some(min_tls) = self.min_tls_version { + builder = builder.min_tls_version(min_tls); + } + let reqwest_client = match self.timeout { Some(t) => builder.timeout(t), None => builder,