From eb12064fab73037941d23dc8245144d11d01fd99 Mon Sep 17 00:00:00 2001 From: Matthew Timms Date: Tue, 16 Apr 2024 02:18:52 +1000 Subject: [PATCH 01/40] feat: add support for pre-commit (#1396) --- .pre-commit-hooks.yaml | 18 ++++++++++++++++++ README.md | 22 ++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 .pre-commit-hooks.yaml diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml new file mode 100644 index 0000000000..fb9a1fe149 --- /dev/null +++ b/.pre-commit-hooks.yaml @@ -0,0 +1,18 @@ +- id: lychee + name: lychee + description: "Run 'lychee' for fast, async, stream-based link checking" + entry: lychee + language: system + args: ["--no-progress"] + types: [text] + pass_filenames: true + require_serial: true +- id: lychee-docker + name: lychee + description: "Run 'lychee' docker image for fast, async, stream-based link checking" + entry: lycheeverse/lychee:0.14 + language: docker_image + args: ["--no-progress"] + types: [text] + pass_filenames: true + require_serial: true diff --git a/README.md b/README.md index 2c4101abc2..82af6ac2a4 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ Available as a command-line utility, a library and a [GitHub Action](https://git - [Commandline usage](#commandline-usage) - [Library usage](#library-usage) - [GitHub Action Usage](#github-action-usage) +- [Pre-commit Usage](#pre-commit-usage) - [Contributing to lychee](#contributing-to-lychee) - [Debugging and improving async code](#debugging-and-improving-async-code) - [Troubleshooting and Workarounds](#troubleshooting-and-workarounds) @@ -551,6 +552,27 @@ folder. A GitHub Action that uses lychee is available as a separate repository: [lycheeverse/lychee-action](https://github.com/lycheeverse/lychee-action) which includes usage instructions. +## Pre-commit Usage + +Lychee can also be used as a [pre-commit](https://pre-commit.com/) hook. +```yaml +# .pre-commit-config.yaml +repos: + - repo: https://github.com/lycheeverse/lychee.git + rev: 0.14.3 + hooks: + - id: lychee + # Optionally include additional CLI arguments + args: ["--no-progress", "--exclude", "file://"] +``` + +Rather than running on staged-files only, Lychee can be run against an entire repository. +```yaml +- id: lychee + args: ["--no-progress", "."] + pass_filenames: false +``` + ## Contributing to lychee We'd be thankful for any contribution. \ From 382b53c5d02eeac79952fc99be017448368fa5a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Orhun=20Parmaks=C4=B1z?= Date: Thu, 18 Apr 2024 18:42:23 +0300 Subject: [PATCH 02/40] docs: add instructions for installing on Alpine Linux (#1406) --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 82af6ac2a4..3dffa8c798 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,13 @@ scoop install lychee pkg install lychee ``` +### Alpine Linux + +```sh + # available for Alpine Edge in testing repositories +apk add lychee +``` + ### Pre-built binaries We provide binaries for Linux, macOS, and Windows for every release. \ From 0c4bea80742d6d816972acbd269256814f510ec7 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Mon, 11 Mar 2024 16:40:11 +0100 Subject: [PATCH 03/40] Create request chain with basic authentication --- lychee-lib/src/chain/mod.rs | 39 +++++++++++++++++++++++++++++++++++++ lychee-lib/src/client.rs | 31 ++++++++++++++++------------- lychee-lib/src/lib.rs | 1 + 3 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 lychee-lib/src/chain/mod.rs diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs new file mode 100644 index 0000000000..359983dac1 --- /dev/null +++ b/lychee-lib/src/chain/mod.rs @@ -0,0 +1,39 @@ +use headers::authorization::Credentials; +use http::header::AUTHORIZATION; +use reqwest::Request; + +use crate::BasicAuthCredentials; + +pub(crate) type Chain = Vec + Send>>; + +pub(crate) fn traverse(chain: Chain, mut input: T) -> T { + for mut e in chain { + input = e.handle(input) + } + + input +} + +pub(crate) trait Chainable { + fn handle(&mut self, input: T) -> T; +} + +pub(crate) struct BasicAuth { + credentials: BasicAuthCredentials, +} + +impl BasicAuth { + pub(crate) fn new(credentials: BasicAuthCredentials) -> Self { + Self { credentials } + } +} + +impl Chainable for BasicAuth { + fn handle(&mut self, mut request: Request) -> Request { + request.headers_mut().append( + AUTHORIZATION, + self.credentials.to_authorization().0.encode(), + ); + request + } +} diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 801d4e9891..fa469d59a5 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -17,9 +17,8 @@ use std::{collections::HashSet, path::Path, sync::Arc, time::Duration}; #[cfg(all(feature = "email-check", feature = "native-tls"))] use check_if_email_exists::{check_email, CheckEmailInput, Reachable}; -use headers::authorization::Credentials; use http::{ - header::{HeaderMap, HeaderValue, AUTHORIZATION}, + header::{HeaderMap, HeaderValue}, StatusCode, }; use log::{debug, warn}; @@ -31,6 +30,7 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ + chain::{traverse, BasicAuth, Chain}, filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, @@ -647,23 +647,28 @@ impl Client { /// Check a URI using [reqwest](https://github.com/seanmonstar/reqwest). async fn check_default(&self, uri: &Uri, credentials: &Option) -> Status { - let request = match credentials { - Some(credentials) => self - .reqwest_client - .request(self.method.clone(), uri.as_str()) - .header(AUTHORIZATION, credentials.to_authorization().0.encode()) - .build(), - None => self - .reqwest_client - .request(self.method.clone(), uri.as_str()) - .build(), - }; + // todo: middleware + + // todo: create credentials middleware + let request = self + .reqwest_client + .request(self.method.clone(), uri.as_str()) + .build(); let request = match request { Ok(r) => r, Err(e) => return e.into(), }; + let mut chain: Chain = vec![]; + + if let Some(c) = credentials { + chain.push(Box::new(BasicAuth::new(c.clone()))); + } + + let request = traverse(chain, request); + + // todo: quirks middleware let request = self.quirks.apply(request); match self.reqwest_client.execute(request).await { diff --git a/lychee-lib/src/lib.rs b/lychee-lib/src/lib.rs index a74b899bc5..dd413868c4 100644 --- a/lychee-lib/src/lib.rs +++ b/lychee-lib/src/lib.rs @@ -54,6 +54,7 @@ mod basic_auth; mod client; /// A pool of clients, to handle concurrent checks pub mod collector; +mod chain; mod quirks; mod retry; mod types; From 4d8a21c2dd4b94fbc8c23dc3cea2c14a44dae404 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Mon, 11 Mar 2024 16:48:59 +0100 Subject: [PATCH 04/40] Test chain --- lychee-lib/src/chain/mod.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 359983dac1..222531488d 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -37,3 +37,24 @@ impl Chainable for BasicAuth { request } } + +mod test { + use super::Chainable; + + struct Add(i64); + + struct Request(i64); + + impl Chainable for Add { + fn handle(&mut self, req: Request) -> Request { + Request(req.0 + self.0) + } + } + + #[test] + fn example_chain() { + let chain: crate::chain::Chain = vec![Box::new(Add(10)), Box::new(Add(-3))]; + let result = crate::chain::traverse(chain, Request(0)); + assert_eq!(result.0, 7); + } +} From 237f690f18704f7fa9cf8137ec2113a40a978698 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Tue, 12 Mar 2024 08:49:46 +0100 Subject: [PATCH 05/40] Add quirks to request chain --- lychee-lib/src/client.rs | 5 +---- lychee-lib/src/quirks/mod.rs | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index fa469d59a5..66b4266242 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -660,7 +660,7 @@ impl Client { Err(e) => return e.into(), }; - let mut chain: Chain = vec![]; + let mut chain: Chain = vec![Box::new(self.quirks.clone())]; if let Some(c) = credentials { chain.push(Box::new(BasicAuth::new(c.clone()))); @@ -668,9 +668,6 @@ impl Client { let request = traverse(chain, request); - // todo: quirks middleware - let request = self.quirks.apply(request); - match self.reqwest_client.execute(request).await { Ok(ref response) => Status::new(response, self.accepted.clone()), Err(e) => e.into(), diff --git a/lychee-lib/src/quirks/mod.rs b/lychee-lib/src/quirks/mod.rs index 7f30cba854..76ffe8fb76 100644 --- a/lychee-lib/src/quirks/mod.rs +++ b/lychee-lib/src/quirks/mod.rs @@ -1,3 +1,4 @@ +use crate::chain::Chainable; use header::HeaderValue; use http::header; use once_cell::sync::Lazy; @@ -71,11 +72,11 @@ impl Default for Quirks { } } -impl Quirks { - /// Apply quirks to a given request. Only the first quirk regex pattern +impl Chainable for Quirks { + /// Handle quirks in a given request. Only the first quirk regex pattern /// matching the URL will be applied. The rest will be discarded for /// simplicity reasons. This limitation might be lifted in the future. - pub(crate) fn apply(&self, request: Request) -> Request { + fn handle(&mut self, request: Request) -> Request { for quirk in &self.quirks { if quirk.pattern.is_match(request.url().as_str()) { return (quirk.rewrite)(request); @@ -92,6 +93,8 @@ mod tests { use http::{header, Method}; use reqwest::{Request, Url}; + use crate::chain::Chainable; + use super::Quirks; #[derive(Debug)] @@ -113,7 +116,7 @@ mod tests { fn test_cratesio_request() { let url = Url::parse("https://crates.io/crates/lychee").unwrap(); let request = Request::new(Method::GET, url); - let modified = Quirks::default().apply(request); + let modified = Quirks::default().handle(request); assert_eq!( modified.headers().get(header::ACCEPT).unwrap(), @@ -125,7 +128,7 @@ mod tests { fn test_youtube_video_request() { let url = Url::parse("https://www.youtube.com/watch?v=NlKuICiT470&list=PLbWDhxwM_45mPVToqaIZNbZeIzFchsKKQ&index=7").unwrap(); let request = Request::new(Method::GET, url); - let modified = Quirks::default().apply(request); + let modified = Quirks::default().handle(request); let expected_url = Url::parse("https://img.youtube.com/vi/NlKuICiT470/0.jpg").unwrap(); assert_eq!( @@ -138,7 +141,7 @@ mod tests { fn test_youtube_video_shortlink_request() { let url = Url::parse("https://youtu.be/Rvu7N4wyFpk?t=42").unwrap(); let request = Request::new(Method::GET, url); - let modified = Quirks::default().apply(request); + let modified = Quirks::default().handle(request); let expected_url = Url::parse("https://img.youtube.com/vi/Rvu7N4wyFpk/0.jpg").unwrap(); assert_eq!( @@ -151,7 +154,7 @@ mod tests { fn test_non_video_youtube_url_untouched() { let url = Url::parse("https://www.youtube.com/channel/UCaYhcUwRBNscFNUKTjgPFiA").unwrap(); let request = Request::new(Method::GET, url.clone()); - let modified = Quirks::default().apply(request); + let modified = Quirks::default().handle(request); assert_eq!(MockRequest(modified), MockRequest::new(Method::GET, url)); } @@ -160,7 +163,7 @@ mod tests { fn test_no_quirk_applied() { let url = Url::parse("https://endler.dev").unwrap(); let request = Request::new(Method::GET, url.clone()); - let modified = Quirks::default().apply(request); + let modified = Quirks::default().handle(request); assert_eq!(MockRequest(modified), MockRequest::new(Method::GET, url)); } From 7783cdfe46f9c75286bf3726e3e4ed1a9821c5ac Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Tue, 12 Mar 2024 14:48:38 +0100 Subject: [PATCH 06/40] Pass down request_chain instead of credentials & add test --- lychee-lib/src/chain/mod.rs | 34 ++++++++++++---- lychee-lib/src/client.rs | 80 +++++++++++++++++++++---------------- 2 files changed, 72 insertions(+), 42 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 222531488d..7b9a564432 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,23 +1,38 @@ +use core::fmt::Debug; use headers::authorization::Credentials; use http::header::AUTHORIZATION; use reqwest::Request; use crate::BasicAuthCredentials; -pub(crate) type Chain = Vec + Send>>; +pub(crate) type RequestChain = Chain; -pub(crate) fn traverse(chain: Chain, mut input: T) -> T { - for mut e in chain { - input = e.handle(input) +#[derive(Debug)] +pub struct Chain(Vec + Send>>); + +impl Chain { + pub(crate) fn new(values: Vec + Send>>) -> Self { + Self(values) + } + + pub(crate) fn traverse(&mut self, mut input: T) -> T { + for e in self.0.iter_mut() { + input = e.handle(input) + } + + input } - input + pub(crate) fn push(&mut self, value: Box + Send>) { + self.0.push(value); + } } -pub(crate) trait Chainable { +pub(crate) trait Chainable: Debug { fn handle(&mut self, input: T) -> T; } +#[derive(Debug)] pub(crate) struct BasicAuth { credentials: BasicAuthCredentials, } @@ -41,8 +56,10 @@ impl Chainable for BasicAuth { mod test { use super::Chainable; + #[derive(Debug)] struct Add(i64); + #[derive(Debug)] struct Request(i64); impl Chainable for Add { @@ -53,8 +70,9 @@ mod test { #[test] fn example_chain() { - let chain: crate::chain::Chain = vec![Box::new(Add(10)), Box::new(Add(-3))]; - let result = crate::chain::traverse(chain, Request(0)); + use super::Chain; + let mut chain: Chain = Chain::new(vec![Box::new(Add(10)), Box::new(Add(-3))]); + let result = chain.traverse(Request(0)); assert_eq!(result.0, 7); } } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 66b4266242..00c11b647c 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -30,14 +30,14 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ - chain::{traverse, BasicAuth, Chain}, + chain::{BasicAuth, Chain, RequestChain}, filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, retry::RetryExt, types::uri::github::GithubUri, utils::fragment_checker::FragmentChecker, - BasicAuthCredentials, ErrorKind, Request, Response, Result, Status, Uri, + ErrorKind, Request, Response, Result, Status, Uri, }; #[cfg(all(feature = "email-check", feature = "native-tls"))] @@ -374,8 +374,6 @@ impl ClientBuilder { include_mail: self.include_mail, }; - let quirks = Quirks::default(); - Ok(Client { reqwest_client, github_client, @@ -386,7 +384,6 @@ impl ClientBuilder { method: self.method, accepted: self.accepted, require_https: self.require_https, - quirks, include_fragments: self.include_fragments, fragment_checker: FragmentChecker::new(), }) @@ -433,9 +430,6 @@ pub struct Client { /// This would treat unencrypted links as errors when HTTPS is available. require_https: bool, - /// Override behaviors for certain known issues with special URIs. - quirks: Quirks, - /// Enable the checking of fragments in links. include_fragments: bool, @@ -477,16 +471,23 @@ impl Client { // )); // } - self.remap(uri)?; + self.remap(uri)?; // todo: possible to do in request chain? if self.is_excluded(uri) { return Ok(Response::new(uri.clone(), Status::Excluded, source)); } + let quirks = Quirks::default(); + let mut request_chain: RequestChain = Chain::new(vec![Box::new(quirks)]); + + if let Some(c) = credentials { + request_chain.push(Box::new(BasicAuth::new(c.clone()))); + } + let status = match uri.scheme() { _ if uri.is_file() => self.check_file(uri).await, _ if uri.is_mail() => self.check_mail(uri).await, - _ => self.check_website(uri, credentials).await?, + _ => self.check_website(uri, &mut request_chain).await?, }; Ok(Response::new(uri.clone(), status, source)) @@ -522,12 +523,12 @@ impl Client { pub async fn check_website( &self, uri: &Uri, - credentials: &Option, + request_chain: &mut RequestChain, ) -> Result { - match self.check_website_inner(uri, credentials).await { + match self.check_website_inner(uri, request_chain).await { Status::Ok(code) if self.require_https && uri.scheme() == "http" => { if self - .check_website_inner(&uri.to_https()?, credentials) + .check_website_inner(&uri.to_https()?, request_chain) .await .is_success() { @@ -552,11 +553,7 @@ impl Client { /// - The URI is invalid. /// - The request failed. /// - The response status code is not accepted. - pub async fn check_website_inner( - &self, - uri: &Uri, - credentials: &Option, - ) -> Status { + pub async fn check_website_inner(&self, uri: &Uri, request_chain: &mut RequestChain) -> Status { // Workaround for upstream reqwest panic if validate_url(&uri.url) { if matches!(uri.scheme(), "http" | "https") { @@ -571,7 +568,7 @@ impl Client { return Status::Unsupported(ErrorKind::InvalidURI(uri.clone())); } - let status = self.retry_request(uri, credentials).await; + let status = self.retry_request(uri, request_chain).await; if status.is_success() { return status; } @@ -593,11 +590,11 @@ impl Client { /// Retry requests up to `max_retries` times /// with an exponential backoff. - async fn retry_request(&self, uri: &Uri, credentials: &Option) -> Status { + async fn retry_request(&self, uri: &Uri, request_chain: &mut RequestChain) -> Status { let mut retries: u64 = 0; let mut wait_time = self.retry_wait_time; - let mut status = self.check_default(uri, credentials).await; + let mut status = self.check_default(uri, request_chain).await; while retries < self.max_retries { if status.is_success() || !status.should_retry() { return status; @@ -605,7 +602,7 @@ impl Client { retries += 1; tokio::time::sleep(wait_time).await; wait_time = wait_time.saturating_mul(2); - status = self.check_default(uri, credentials).await; + status = self.check_default(uri, request_chain).await; } status } @@ -646,10 +643,7 @@ impl Client { } /// Check a URI using [reqwest](https://github.com/seanmonstar/reqwest). - async fn check_default(&self, uri: &Uri, credentials: &Option) -> Status { - // todo: middleware - - // todo: create credentials middleware + async fn check_default(&self, uri: &Uri, request_chain: &mut RequestChain) -> Status { let request = self .reqwest_client .request(self.method.clone(), uri.as_str()) @@ -660,13 +654,7 @@ impl Client { Err(e) => return e.into(), }; - let mut chain: Chain = vec![Box::new(self.quirks.clone())]; - - if let Some(c) = credentials { - chain.push(Box::new(BasicAuth::new(c.clone()))); - } - - let request = traverse(chain, request); + let request = request_chain.traverse(request); match self.reqwest_client.execute(request).await { Ok(ref response) => Status::new(response, self.accepted.clone()), @@ -773,7 +761,7 @@ mod tests { use wiremock::matchers::path; use super::ClientBuilder; - use crate::{mock_server, test_utils::get_mock_client_response, Uri}; + use crate::{mock_server, test_utils::get_mock_client_response, Request, Uri}; #[tokio::test] async fn test_nonexistent() { @@ -820,6 +808,30 @@ mod tests { assert!(res.status().is_failure()); } + #[tokio::test] + async fn test_basic_auth() { + let mut r = Request::new( + "https://authenticationtest.com/HTTPAuth/" + .try_into() + .unwrap(), + crate::InputSource::Stdin, + None, + None, + None, + ); + + let res = get_mock_client_response(r.clone()).await; + assert_eq!(res.status().code(), Some(401.try_into().unwrap())); + + r.credentials = Some(crate::BasicAuthCredentials { + username: "user".into(), + password: "pass".into(), + }); + + let res = get_mock_client_response(r.clone()).await; + assert!(res.status().is_success()); + } + #[tokio::test] async fn test_non_github() { let mock_server = mock_server!(StatusCode::OK); From 8da4592e2ac3fa4aeccdd48823792a52fc616194 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Wed, 13 Mar 2024 08:25:01 +0100 Subject: [PATCH 07/40] Introduce early exit in chain --- lychee-lib/src/chain/mod.rs | 76 ++++++++++++++++++++++++------------ lychee-lib/src/client.rs | 12 ++++-- lychee-lib/src/quirks/mod.rs | 29 ++++++++------ 3 files changed, 77 insertions(+), 40 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 7b9a564432..8ea86b960d 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,35 +1,46 @@ +use crate::{BasicAuthCredentials, Response}; use core::fmt::Debug; use headers::authorization::Credentials; use http::header::AUTHORIZATION; use reqwest::Request; -use crate::BasicAuthCredentials; +#[derive(Debug, PartialEq)] +pub(crate) enum ChainResult { + Chained(T), + EarlyExit(R), +} -pub(crate) type RequestChain = Chain; +pub(crate) type RequestChain = Chain; #[derive(Debug)] -pub struct Chain(Vec + Send>>); +pub struct Chain(Vec + Send>>); -impl Chain { - pub(crate) fn new(values: Vec + Send>>) -> Self { +impl Chain { + pub(crate) fn new(values: Vec + Send>>) -> Self { Self(values) } - pub(crate) fn traverse(&mut self, mut input: T) -> T { + pub(crate) fn traverse(&mut self, mut input: T) -> ChainResult { + use ChainResult::*; for e in self.0.iter_mut() { - input = e.handle(input) + match e.handle(input) { + Chained(r) => input = r, + EarlyExit(r) => { + return EarlyExit(r); + } + } } - input + Chained(input) } - pub(crate) fn push(&mut self, value: Box + Send>) { + pub(crate) fn push(&mut self, value: Box + Send>) { self.0.push(value); } } -pub(crate) trait Chainable: Debug { - fn handle(&mut self, input: T) -> T; +pub(crate) trait Chainable: Debug { + fn handle(&mut self, input: T) -> ChainResult; } #[derive(Debug)] @@ -43,36 +54,51 @@ impl BasicAuth { } } -impl Chainable for BasicAuth { - fn handle(&mut self, mut request: Request) -> Request { +impl Chainable for BasicAuth { + fn handle(&mut self, mut request: Request) -> ChainResult { request.headers_mut().append( AUTHORIZATION, self.credentials.to_authorization().0.encode(), ); - request + ChainResult::Chained(request) } } mod test { - use super::Chainable; + use super::{ChainResult, ChainResult::*, Chainable}; #[derive(Debug)] struct Add(i64); - #[derive(Debug)] - struct Request(i64); - - impl Chainable for Add { - fn handle(&mut self, req: Request) -> Request { - Request(req.0 + self.0) + #[derive(Debug, PartialEq, Eq)] + struct Result(i64); + + impl Chainable for Add { + fn handle(&mut self, req: Result) -> ChainResult { + let added = req.0 + self.0; + if added > 100 { + EarlyExit(Result(req.0)) + } else { + Chained(Result(added)) + } } } #[test] - fn example_chain() { + fn simple_chain() { + use super::Chain; + let mut chain: Chain = + Chain::new(vec![Box::new(Add(10)), Box::new(Add(-3))]); + let result = chain.traverse(Result(0)); + assert_eq!(result, Chained(Result(7))); + } + + #[test] + fn early_exit_chain() { use super::Chain; - let mut chain: Chain = Chain::new(vec![Box::new(Add(10)), Box::new(Add(-3))]); - let result = chain.traverse(Request(0)); - assert_eq!(result.0, 7); + let mut chain: Chain = + Chain::new(vec![Box::new(Add(80)), Box::new(Add(30)), Box::new(Add(1))]); + let result = chain.traverse(Result(0)); + assert_eq!(result, EarlyExit(Result(80))); } } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 00c11b647c..3e2d3e3cd9 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -30,6 +30,7 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ + chain::ChainResult::*, chain::{BasicAuth, Chain, RequestChain}, filter::{Excludes, Filter, Includes}, quirks::Quirks, @@ -654,11 +655,14 @@ impl Client { Err(e) => return e.into(), }; - let request = request_chain.traverse(request); + let result = request_chain.traverse(request); - match self.reqwest_client.execute(request).await { - Ok(ref response) => Status::new(response, self.accepted.clone()), - Err(e) => e.into(), + match result { + EarlyExit(r) => r.1.status, + Chained(r) => match self.reqwest_client.execute(r).await { + Ok(ref response) => Status::new(response, self.accepted.clone()), + Err(e) => e.into(), + }, } } diff --git a/lychee-lib/src/quirks/mod.rs b/lychee-lib/src/quirks/mod.rs index 76ffe8fb76..e93d392cb7 100644 --- a/lychee-lib/src/quirks/mod.rs +++ b/lychee-lib/src/quirks/mod.rs @@ -1,4 +1,7 @@ -use crate::chain::Chainable; +use crate::{ + chain::{ChainResult, Chainable}, + Response, +}; use header::HeaderValue; use http::header; use once_cell::sync::Lazy; @@ -72,11 +75,11 @@ impl Default for Quirks { } } -impl Chainable for Quirks { - /// Handle quirks in a given request. Only the first quirk regex pattern +impl Quirks { + /// Apply quirks to a given request. Only the first quirk regex pattern /// matching the URL will be applied. The rest will be discarded for /// simplicity reasons. This limitation might be lifted in the future. - fn handle(&mut self, request: Request) -> Request { + pub(crate) fn apply(&self, request: Request) -> Request { for quirk in &self.quirks { if quirk.pattern.is_match(request.url().as_str()) { return (quirk.rewrite)(request); @@ -87,14 +90,18 @@ impl Chainable for Quirks { } } +impl Chainable for Quirks { + fn handle(&mut self, input: Request) -> ChainResult { + ChainResult::Chained(self.apply(input)) + } +} + #[cfg(test)] mod tests { use header::HeaderValue; use http::{header, Method}; use reqwest::{Request, Url}; - use crate::chain::Chainable; - use super::Quirks; #[derive(Debug)] @@ -116,7 +123,7 @@ mod tests { fn test_cratesio_request() { let url = Url::parse("https://crates.io/crates/lychee").unwrap(); let request = Request::new(Method::GET, url); - let modified = Quirks::default().handle(request); + let modified = Quirks::default().apply(request); assert_eq!( modified.headers().get(header::ACCEPT).unwrap(), @@ -128,7 +135,7 @@ mod tests { fn test_youtube_video_request() { let url = Url::parse("https://www.youtube.com/watch?v=NlKuICiT470&list=PLbWDhxwM_45mPVToqaIZNbZeIzFchsKKQ&index=7").unwrap(); let request = Request::new(Method::GET, url); - let modified = Quirks::default().handle(request); + let modified = Quirks::default().apply(request); let expected_url = Url::parse("https://img.youtube.com/vi/NlKuICiT470/0.jpg").unwrap(); assert_eq!( @@ -141,7 +148,7 @@ mod tests { fn test_youtube_video_shortlink_request() { let url = Url::parse("https://youtu.be/Rvu7N4wyFpk?t=42").unwrap(); let request = Request::new(Method::GET, url); - let modified = Quirks::default().handle(request); + let modified = Quirks::default().apply(request); let expected_url = Url::parse("https://img.youtube.com/vi/Rvu7N4wyFpk/0.jpg").unwrap(); assert_eq!( @@ -154,7 +161,7 @@ mod tests { fn test_non_video_youtube_url_untouched() { let url = Url::parse("https://www.youtube.com/channel/UCaYhcUwRBNscFNUKTjgPFiA").unwrap(); let request = Request::new(Method::GET, url.clone()); - let modified = Quirks::default().handle(request); + let modified = Quirks::default().apply(request); assert_eq!(MockRequest(modified), MockRequest::new(Method::GET, url)); } @@ -163,7 +170,7 @@ mod tests { fn test_no_quirk_applied() { let url = Url::parse("https://endler.dev").unwrap(); let request = Request::new(Method::GET, url.clone()); - let modified = Quirks::default().handle(request); + let modified = Quirks::default().apply(request); assert_eq!(MockRequest(modified), MockRequest::new(Method::GET, url)); } From db1dc19c0a6c10dece18716f59c8ced8a2218bd2 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Wed, 13 Mar 2024 08:35:19 +0100 Subject: [PATCH 08/40] Implement Chainable directly for BasicAuthCredentials --- lychee-lib/src/chain/mod.rs | 25 +------------------ lychee-lib/src/client.rs | 4 +-- .../src/types/basic_auth/credentials.rs | 17 +++++++++++++ 3 files changed, 20 insertions(+), 26 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 8ea86b960d..97d534f94d 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,7 +1,5 @@ -use crate::{BasicAuthCredentials, Response}; +use crate::Response; use core::fmt::Debug; -use headers::authorization::Credentials; -use http::header::AUTHORIZATION; use reqwest::Request; #[derive(Debug, PartialEq)] @@ -43,27 +41,6 @@ pub(crate) trait Chainable: Debug { fn handle(&mut self, input: T) -> ChainResult; } -#[derive(Debug)] -pub(crate) struct BasicAuth { - credentials: BasicAuthCredentials, -} - -impl BasicAuth { - pub(crate) fn new(credentials: BasicAuthCredentials) -> Self { - Self { credentials } - } -} - -impl Chainable for BasicAuth { - fn handle(&mut self, mut request: Request) -> ChainResult { - request.headers_mut().append( - AUTHORIZATION, - self.credentials.to_authorization().0.encode(), - ); - ChainResult::Chained(request) - } -} - mod test { use super::{ChainResult, ChainResult::*, Chainable}; diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 3e2d3e3cd9..48e4c742e0 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -31,7 +31,7 @@ use typed_builder::TypedBuilder; use crate::{ chain::ChainResult::*, - chain::{BasicAuth, Chain, RequestChain}, + chain::{Chain, RequestChain}, filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, @@ -482,7 +482,7 @@ impl Client { let mut request_chain: RequestChain = Chain::new(vec![Box::new(quirks)]); if let Some(c) = credentials { - request_chain.push(Box::new(BasicAuth::new(c.clone()))); + request_chain.push(Box::new(c.clone())); } let status = match uri.scheme() { diff --git a/lychee-lib/src/types/basic_auth/credentials.rs b/lychee-lib/src/types/basic_auth/credentials.rs index 2452fb973b..bfbc519595 100644 --- a/lychee-lib/src/types/basic_auth/credentials.rs +++ b/lychee-lib/src/types/basic_auth/credentials.rs @@ -1,9 +1,17 @@ use std::str::FromStr; +use headers::authorization::Credentials; use headers::{authorization::Basic, Authorization}; +use http::header::AUTHORIZATION; +use reqwest::Request; use serde::Deserialize; use thiserror::Error; +use crate::{ + chain::{ChainResult, Chainable}, + Response, +}; + #[derive(Copy, Clone, Debug, Error, PartialEq)] pub enum BasicAuthCredentialsParseError { #[error("Invalid basic auth credentials syntax")] @@ -66,3 +74,12 @@ impl BasicAuthCredentials { Authorization::basic(&self.username, &self.password) } } + +impl Chainable for BasicAuthCredentials { + fn handle(&mut self, mut request: Request) -> ChainResult { + request + .headers_mut() + .append(AUTHORIZATION, self.to_authorization().0.encode()); + ChainResult::Chained(request) + } +} From 667105e13e73bd7e1fd5fd2da5fb02955fc5a208 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Wed, 13 Mar 2024 09:45:38 +0100 Subject: [PATCH 09/40] Move chain into check_website function --- lychee-lib/src/chain/mod.rs | 4 +--- lychee-lib/src/client.rs | 28 +++++++++++++--------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 97d534f94d..bbfd0d29aa 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,6 +1,4 @@ -use crate::Response; use core::fmt::Debug; -use reqwest::Request; #[derive(Debug, PartialEq)] pub(crate) enum ChainResult { @@ -8,7 +6,7 @@ pub(crate) enum ChainResult { EarlyExit(R), } -pub(crate) type RequestChain = Chain; +pub(crate) type RequestChain = Chain; #[derive(Debug)] pub struct Chain(Vec + Send>>); diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 48e4c742e0..f561e799dc 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -30,15 +30,14 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ - chain::ChainResult::*, - chain::{Chain, RequestChain}, + chain::{Chain, ChainResult::*, RequestChain}, filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, retry::RetryExt, types::uri::github::GithubUri, utils::fragment_checker::FragmentChecker, - ErrorKind, Request, Response, Result, Status, Uri, + BasicAuthCredentials, ErrorKind, Request, Response, Result, Status, Uri, }; #[cfg(all(feature = "email-check", feature = "native-tls"))] @@ -472,23 +471,16 @@ impl Client { // )); // } - self.remap(uri)?; // todo: possible to do in request chain? + self.remap(uri)?; if self.is_excluded(uri) { return Ok(Response::new(uri.clone(), Status::Excluded, source)); } - let quirks = Quirks::default(); - let mut request_chain: RequestChain = Chain::new(vec![Box::new(quirks)]); - - if let Some(c) = credentials { - request_chain.push(Box::new(c.clone())); - } - let status = match uri.scheme() { _ if uri.is_file() => self.check_file(uri).await, _ if uri.is_mail() => self.check_mail(uri).await, - _ => self.check_website(uri, &mut request_chain).await?, + _ => self.check_website(uri, credentials).await?, }; Ok(Response::new(uri.clone(), status, source)) @@ -524,12 +516,18 @@ impl Client { pub async fn check_website( &self, uri: &Uri, - request_chain: &mut RequestChain, + credentials: &Option, ) -> Result { - match self.check_website_inner(uri, request_chain).await { + let quirks = Quirks::default(); + let mut request_chain: RequestChain = Chain::new(vec![Box::new(quirks)]); + if let Some(c) = credentials { + request_chain.push(Box::new(c.clone())); + } + + match self.check_website_inner(uri, &mut request_chain).await { Status::Ok(code) if self.require_https && uri.scheme() == "http" => { if self - .check_website_inner(&uri.to_https()?, request_chain) + .check_website_inner(&uri.to_https()?, &mut request_chain) .await .is_success() { From 402482ca01e28b871971fd7528970a8010038c2c Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Wed, 13 Mar 2024 14:58:50 +0100 Subject: [PATCH 10/40] Update RequestChain & add chain to client --- lychee-lib/src/chain/mod.rs | 14 ++++- lychee-lib/src/client.rs | 55 +++++++++++++++++-- lychee-lib/src/quirks/mod.rs | 6 +- .../src/types/basic_auth/credentials.rs | 10 ++-- 4 files changed, 71 insertions(+), 14 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index bbfd0d29aa..4585a3383f 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,21 +1,33 @@ use core::fmt::Debug; +use crate::Status; + #[derive(Debug, PartialEq)] pub(crate) enum ChainResult { Chained(T), EarlyExit(R), } -pub(crate) type RequestChain = Chain; +pub(crate) type RequestChain = Chain; #[derive(Debug)] pub struct Chain(Vec + Send>>); +impl Default for Chain { + fn default() -> Self { + Self(vec![]) + } +} + impl Chain { pub(crate) fn new(values: Vec + Send>>) -> Self { Self(values) } + pub(crate) fn append(&mut self, other: &mut Chain) { + self.0.append(&mut other.0); + } + pub(crate) fn traverse(&mut self, mut input: T) -> ChainResult { use ChainResult::*; for e in self.0.iter_mut() { diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index f561e799dc..bd21f2ddfa 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -13,7 +13,12 @@ clippy::default_trait_access, clippy::used_underscore_binding )] -use std::{collections::HashSet, path::Path, sync::Arc, time::Duration}; +use std::{ + collections::HashSet, + path::Path, + sync::{Arc, Mutex}, + time::Duration, +}; #[cfg(all(feature = "email-check", feature = "native-tls"))] use check_if_email_exists::{check_email, CheckEmailInput, Reachable}; @@ -30,7 +35,7 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ - chain::{Chain, ChainResult::*, RequestChain}, + chain::{Chain, ChainResult::{Chained, EarlyExit}, RequestChain}, filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, @@ -274,6 +279,8 @@ pub struct ClientBuilder { /// Enable the checking of fragments in links. include_fragments: bool, + + request_chain: Arc>, } impl Default for ClientBuilder { @@ -386,6 +393,7 @@ impl ClientBuilder { require_https: self.require_https, include_fragments: self.include_fragments, fragment_checker: FragmentChecker::new(), + request_chain: self.request_chain, }) } } @@ -435,6 +443,8 @@ pub struct Client { /// Caches Fragments fragment_checker: FragmentChecker, + + request_chain: Arc>, } impl Client { @@ -520,6 +530,8 @@ impl Client { ) -> Result { let quirks = Quirks::default(); let mut request_chain: RequestChain = Chain::new(vec![Box::new(quirks)]); + request_chain.append(&mut self.request_chain.lock().unwrap()); + if let Some(c) = credentials { request_chain.push(Box::new(c.clone())); } @@ -656,7 +668,7 @@ impl Client { let result = request_chain.traverse(request); match result { - EarlyExit(r) => r.1.status, + EarlyExit(status) => status, Chained(r) => match self.reqwest_client.execute(r).await { Ok(ref response) => Status::new(response, self.accepted.clone()), Err(e) => e.into(), @@ -754,6 +766,7 @@ where mod tests { use std::{ fs::File, + sync::{Arc, Mutex}, time::{Duration, Instant}, }; @@ -763,7 +776,12 @@ mod tests { use wiremock::matchers::path; use super::ClientBuilder; - use crate::{mock_server, test_utils::get_mock_client_response, Request, Uri}; + use crate::{ + chain::{ChainResult, Chainable, RequestChain}, + mock_server, + test_utils::get_mock_client_response, + Request, Status, Uri, + }; #[tokio::test] async fn test_nonexistent() { @@ -1092,4 +1110,33 @@ mod tests { assert!(res.status().is_unsupported()); } } + + #[tokio::test] + async fn test_chain() { + use reqwest::Request; + use ChainResult::EarlyExit; + + #[derive(Debug)] + struct ExampleHandler(); + + impl Chainable for ExampleHandler { + fn handle(&mut self, _: Request) -> ChainResult { + EarlyExit(Status::Excluded) + } + } + + let chain = Arc::new(Mutex::new(RequestChain::new(vec![Box::new( + ExampleHandler {}, + )]))); + + let client = ClientBuilder::builder() + .request_chain(chain) + .build() + .client() + .unwrap(); + + let result = client.check("http://example.com"); + let res = result.await.unwrap(); + assert_eq!(res.status(), &Status::Excluded); + } } diff --git a/lychee-lib/src/quirks/mod.rs b/lychee-lib/src/quirks/mod.rs index e93d392cb7..fcfb5bfb25 100644 --- a/lychee-lib/src/quirks/mod.rs +++ b/lychee-lib/src/quirks/mod.rs @@ -1,6 +1,6 @@ use crate::{ chain::{ChainResult, Chainable}, - Response, + Status, }; use header::HeaderValue; use http::header; @@ -90,8 +90,8 @@ impl Quirks { } } -impl Chainable for Quirks { - fn handle(&mut self, input: Request) -> ChainResult { +impl Chainable for Quirks { + fn handle(&mut self, input: Request) -> ChainResult { ChainResult::Chained(self.apply(input)) } } diff --git a/lychee-lib/src/types/basic_auth/credentials.rs b/lychee-lib/src/types/basic_auth/credentials.rs index bfbc519595..ed10511344 100644 --- a/lychee-lib/src/types/basic_auth/credentials.rs +++ b/lychee-lib/src/types/basic_auth/credentials.rs @@ -7,10 +7,8 @@ use reqwest::Request; use serde::Deserialize; use thiserror::Error; -use crate::{ - chain::{ChainResult, Chainable}, - Response, -}; +use crate::chain::{ChainResult, Chainable}; +use crate::Status; #[derive(Copy, Clone, Debug, Error, PartialEq)] pub enum BasicAuthCredentialsParseError { @@ -75,8 +73,8 @@ impl BasicAuthCredentials { } } -impl Chainable for BasicAuthCredentials { - fn handle(&mut self, mut request: Request) -> ChainResult { +impl Chainable for BasicAuthCredentials { + fn handle(&mut self, mut request: Request) -> ChainResult { request .headers_mut() .append(AUTHORIZATION, self.to_authorization().0.encode()); From a90a35c329aa250826130022bc32f3912b76eb2f Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Thu, 14 Mar 2024 14:04:43 +0100 Subject: [PATCH 11/40] Add doc comment --- lychee-lib/src/client.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index bd21f2ddfa..1642f20ef1 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -35,7 +35,11 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ - chain::{Chain, ChainResult::{Chained, EarlyExit}, RequestChain}, + chain::{ + Chain, + ChainResult::{Chained, EarlyExit}, + RequestChain, + }, filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, @@ -280,6 +284,10 @@ pub struct ClientBuilder { /// Enable the checking of fragments in links. include_fragments: bool, + /// Requests run through this chain where each item in the chain + /// can modify the request. A chained item can also decide to exit + /// early and return a status, so that subsequent chain items are + /// skipped and no HTTP request is ever made. request_chain: Arc>, } @@ -1114,14 +1122,13 @@ mod tests { #[tokio::test] async fn test_chain() { use reqwest::Request; - use ChainResult::EarlyExit; #[derive(Debug)] struct ExampleHandler(); - impl Chainable for ExampleHandler { - fn handle(&mut self, _: Request) -> ChainResult { - EarlyExit(Status::Excluded) + impl Chainable for ExampleHandler { + fn handle(&mut self, _: Request) -> ChainResult { + ChainResult::EarlyExit(Status::Excluded) } } From 1fe6f2f1be389d0b2b78dc5ede46e0de24f97d7b Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Thu, 14 Mar 2024 14:34:19 +0100 Subject: [PATCH 12/40] Small improvements --- lychee-lib/src/client.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 1642f20ef1..4f61016631 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -838,15 +838,9 @@ mod tests { #[tokio::test] async fn test_basic_auth() { - let mut r = Request::new( - "https://authenticationtest.com/HTTPAuth/" - .try_into() - .unwrap(), - crate::InputSource::Stdin, - None, - None, - None, - ); + let mut r: Request = "https://authenticationtest.com/HTTPAuth/" + .try_into() + .unwrap(); let res = get_mock_client_response(r.clone()).await; assert_eq!(res.status().code(), Some(401.try_into().unwrap())); @@ -856,7 +850,7 @@ mod tests { password: "pass".into(), }); - let res = get_mock_client_response(r.clone()).await; + let res = get_mock_client_response(r).await; assert!(res.status().is_success()); } From c41cd5d6b95296fbc1f58bc890ad4addb8023b54 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Fri, 15 Mar 2024 12:40:12 +0100 Subject: [PATCH 13/40] Apply suggestions --- lychee-lib/src/chain/mod.rs | 32 +++++++++---------- lychee-lib/src/client.rs | 10 +++--- lychee-lib/src/quirks/mod.rs | 4 +-- .../src/types/basic_auth/credentials.rs | 4 +-- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 4585a3383f..6bdee55a48 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -4,8 +4,8 @@ use crate::Status; #[derive(Debug, PartialEq)] pub(crate) enum ChainResult { - Chained(T), - EarlyExit(R), + Next(T), + Done(R), } pub(crate) type RequestChain = Chain; @@ -31,15 +31,15 @@ impl Chain { pub(crate) fn traverse(&mut self, mut input: T) -> ChainResult { use ChainResult::*; for e in self.0.iter_mut() { - match e.handle(input) { - Chained(r) => input = r, - EarlyExit(r) => { - return EarlyExit(r); + match e.chain(input) { + Next(r) => input = r, + Done(r) => { + return Done(r); } } } - Chained(input) + Next(input) } pub(crate) fn push(&mut self, value: Box + Send>) { @@ -48,25 +48,25 @@ impl Chain { } pub(crate) trait Chainable: Debug { - fn handle(&mut self, input: T) -> ChainResult; + fn chain(&mut self, input: T) -> ChainResult; } mod test { use super::{ChainResult, ChainResult::*, Chainable}; #[derive(Debug)] - struct Add(i64); + struct Add(usize); #[derive(Debug, PartialEq, Eq)] - struct Result(i64); + struct Result(usize); impl Chainable for Add { - fn handle(&mut self, req: Result) -> ChainResult { + fn chain(&mut self, req: Result) -> ChainResult { let added = req.0 + self.0; if added > 100 { - EarlyExit(Result(req.0)) + Done(Result(req.0)) } else { - Chained(Result(added)) + Next(Result(added)) } } } @@ -75,9 +75,9 @@ mod test { fn simple_chain() { use super::Chain; let mut chain: Chain = - Chain::new(vec![Box::new(Add(10)), Box::new(Add(-3))]); + Chain::new(vec![Box::new(Add(7)), Box::new(Add(3))]); let result = chain.traverse(Result(0)); - assert_eq!(result, Chained(Result(7))); + assert_eq!(result, Next(Result(10))); } #[test] @@ -86,6 +86,6 @@ mod test { let mut chain: Chain = Chain::new(vec![Box::new(Add(80)), Box::new(Add(30)), Box::new(Add(1))]); let result = chain.traverse(Result(0)); - assert_eq!(result, EarlyExit(Result(80))); + assert_eq!(result, Done(Result(80))); } } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 4f61016631..88c1839ccc 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -37,7 +37,7 @@ use typed_builder::TypedBuilder; use crate::{ chain::{ Chain, - ChainResult::{Chained, EarlyExit}, + ChainResult::{Next, Done}, RequestChain, }, filter::{Excludes, Filter, Includes}, @@ -676,8 +676,8 @@ impl Client { let result = request_chain.traverse(request); match result { - EarlyExit(status) => status, - Chained(r) => match self.reqwest_client.execute(r).await { + Done(status) => status, + Next(r) => match self.reqwest_client.execute(r).await { Ok(ref response) => Status::new(response, self.accepted.clone()), Err(e) => e.into(), }, @@ -1121,8 +1121,8 @@ mod tests { struct ExampleHandler(); impl Chainable for ExampleHandler { - fn handle(&mut self, _: Request) -> ChainResult { - ChainResult::EarlyExit(Status::Excluded) + fn chain(&mut self, _: Request) -> ChainResult { + ChainResult::Done(Status::Excluded) } } diff --git a/lychee-lib/src/quirks/mod.rs b/lychee-lib/src/quirks/mod.rs index fcfb5bfb25..0b177d7554 100644 --- a/lychee-lib/src/quirks/mod.rs +++ b/lychee-lib/src/quirks/mod.rs @@ -91,8 +91,8 @@ impl Quirks { } impl Chainable for Quirks { - fn handle(&mut self, input: Request) -> ChainResult { - ChainResult::Chained(self.apply(input)) + fn chain(&mut self, input: Request) -> ChainResult { + ChainResult::Next(self.apply(input)) } } diff --git a/lychee-lib/src/types/basic_auth/credentials.rs b/lychee-lib/src/types/basic_auth/credentials.rs index ed10511344..87c17dacf6 100644 --- a/lychee-lib/src/types/basic_auth/credentials.rs +++ b/lychee-lib/src/types/basic_auth/credentials.rs @@ -74,10 +74,10 @@ impl BasicAuthCredentials { } impl Chainable for BasicAuthCredentials { - fn handle(&mut self, mut request: Request) -> ChainResult { + fn chain(&mut self, mut request: Request) -> ChainResult { request .headers_mut() .append(AUTHORIZATION, self.to_authorization().0.encode()); - ChainResult::Chained(request) + ChainResult::Next(request) } } From 377aceed60c63d72e68f79793d14dc22e39d4930 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Fri, 15 Mar 2024 12:46:36 +0100 Subject: [PATCH 14/40] Apply clippy suggestions --- lychee-lib/src/chain/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 6bdee55a48..7034b1d4aa 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -29,8 +29,8 @@ impl Chain { } pub(crate) fn traverse(&mut self, mut input: T) -> ChainResult { - use ChainResult::*; - for e in self.0.iter_mut() { + use ChainResult::{Done, Next}; + for e in &mut self.0 { match e.chain(input) { Next(r) => input = r, Done(r) => { @@ -52,7 +52,7 @@ pub(crate) trait Chainable: Debug { } mod test { - use super::{ChainResult, ChainResult::*, Chainable}; + use super::{ChainResult, ChainResult::{Done, Next}, Chainable}; #[derive(Debug)] struct Add(usize); From 17e29117008e3bf0d17d612742497888b027d920 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Fri, 15 Mar 2024 13:35:23 +0100 Subject: [PATCH 15/40] Move Arc and Mutex inside of Chain struct --- lychee-lib/src/chain/mod.rs | 34 ++++++++++++++++++++++------------ lychee-lib/src/client.rs | 13 ++++++------- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 7034b1d4aa..04225be3d8 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,6 +1,6 @@ -use core::fmt::Debug; - use crate::Status; +use core::fmt::Debug; +use std::sync::{Arc, Mutex}; #[derive(Debug, PartialEq)] pub(crate) enum ChainResult { @@ -11,26 +11,33 @@ pub(crate) enum ChainResult { pub(crate) type RequestChain = Chain; #[derive(Debug)] -pub struct Chain(Vec + Send>>); +pub struct Chain(Arc + Send>>>>); impl Default for Chain { fn default() -> Self { - Self(vec![]) + Self(Arc::new(Mutex::new(vec![]))) + } +} + +impl Clone for Chain { + fn clone(&self) -> Self { + Self(self.0.clone()) } } + impl Chain { pub(crate) fn new(values: Vec + Send>>) -> Self { - Self(values) + Self(Arc::new(Mutex::new(values))) } - pub(crate) fn append(&mut self, other: &mut Chain) { - self.0.append(&mut other.0); + pub(crate) fn append(&mut self, other: &Chain) { + self.0.lock().unwrap().append(&mut other.0.lock().unwrap()); } pub(crate) fn traverse(&mut self, mut input: T) -> ChainResult { use ChainResult::{Done, Next}; - for e in &mut self.0 { + for e in self.0.lock().unwrap().iter_mut() { match e.chain(input) { Next(r) => input = r, Done(r) => { @@ -43,7 +50,7 @@ impl Chain { } pub(crate) fn push(&mut self, value: Box + Send>) { - self.0.push(value); + self.0.lock().unwrap().push(value); } } @@ -52,7 +59,11 @@ pub(crate) trait Chainable: Debug { } mod test { - use super::{ChainResult, ChainResult::{Done, Next}, Chainable}; + use super::{ + ChainResult, + ChainResult::{Done, Next}, + Chainable, + }; #[derive(Debug)] struct Add(usize); @@ -74,8 +85,7 @@ mod test { #[test] fn simple_chain() { use super::Chain; - let mut chain: Chain = - Chain::new(vec![Box::new(Add(7)), Box::new(Add(3))]); + let mut chain: Chain = Chain::new(vec![Box::new(Add(7)), Box::new(Add(3))]); let result = chain.traverse(Result(0)); assert_eq!(result, Next(Result(10))); } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 88c1839ccc..e3d6376461 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -16,7 +16,7 @@ use std::{ collections::HashSet, path::Path, - sync::{Arc, Mutex}, + sync::Arc, time::Duration, }; @@ -288,7 +288,7 @@ pub struct ClientBuilder { /// can modify the request. A chained item can also decide to exit /// early and return a status, so that subsequent chain items are /// skipped and no HTTP request is ever made. - request_chain: Arc>, + request_chain: RequestChain, } impl Default for ClientBuilder { @@ -452,7 +452,7 @@ pub struct Client { /// Caches Fragments fragment_checker: FragmentChecker, - request_chain: Arc>, + request_chain: RequestChain, } impl Client { @@ -538,7 +538,7 @@ impl Client { ) -> Result { let quirks = Quirks::default(); let mut request_chain: RequestChain = Chain::new(vec![Box::new(quirks)]); - request_chain.append(&mut self.request_chain.lock().unwrap()); + request_chain.append(&self.request_chain); if let Some(c) = credentials { request_chain.push(Box::new(c.clone())); @@ -774,7 +774,6 @@ where mod tests { use std::{ fs::File, - sync::{Arc, Mutex}, time::{Duration, Instant}, }; @@ -1126,9 +1125,9 @@ mod tests { } } - let chain = Arc::new(Mutex::new(RequestChain::new(vec![Box::new( + let chain = RequestChain::new(vec![Box::new( ExampleHandler {}, - )]))); + )]); let client = ClientBuilder::builder() .request_chain(chain) From d92d3ba73364b73606b64898f0bb7e0e75faacde Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Wed, 20 Mar 2024 11:50:26 +0100 Subject: [PATCH 16/40] Extract checking functionality & make chain async --- lychee-lib/src/chain/mod.rs | 5 +- lychee-lib/src/checker.rs | 65 +++++++++++++++ lychee-lib/src/client.rs | 80 +++++-------------- lychee-lib/src/lib.rs | 3 +- lychee-lib/src/quirks/mod.rs | 2 +- .../src/types/basic_auth/credentials.rs | 2 +- 6 files changed, 93 insertions(+), 64 deletions(-) create mode 100644 lychee-lib/src/checker.rs diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 04225be3d8..3f0a8f0c1f 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -25,7 +25,6 @@ impl Clone for Chain { } } - impl Chain { pub(crate) fn new(values: Vec + Send>>) -> Self { Self(Arc::new(Mutex::new(values))) @@ -55,7 +54,7 @@ impl Chain { } pub(crate) trait Chainable: Debug { - fn chain(&mut self, input: T) -> ChainResult; + async fn chain(&mut self, input: T) -> ChainResult; } mod test { @@ -72,7 +71,7 @@ mod test { struct Result(usize); impl Chainable for Add { - fn chain(&mut self, req: Result) -> ChainResult { + async fn chain(&mut self, req: Result) -> ChainResult { let added = req.0 + self.0; if added > 100 { Done(Result(req.0)) diff --git a/lychee-lib/src/checker.rs b/lychee-lib/src/checker.rs new file mode 100644 index 0000000000..598884fefb --- /dev/null +++ b/lychee-lib/src/checker.rs @@ -0,0 +1,65 @@ +use crate::{ + chain::{Chain, ChainResult, Chainable}, + retry::RetryExt, + Status, +}; +use http::StatusCode; +use reqwest::Request; +use std::{collections::HashSet, time::Duration}; + +#[derive(Debug)] +pub(crate) struct Checker { + retry_wait_time: Duration, + max_retries: u64, + reqwest_client: reqwest::Client, + accepted: Option>, +} + +impl Checker { + pub(crate) fn new( + retry_wait_time: Duration, + max_retries: u64, + reqwest_client: reqwest::Client, + accepted: Option>, + ) -> Self { + Self { + retry_wait_time, + max_retries, + reqwest_client, + accepted, + } + } + + /// Retry requests up to `max_retries` times + /// with an exponential backoff. + pub(crate) async fn retry_request(&self, request: reqwest::Request) -> Status { + let mut retries: u64 = 0; + let mut wait_time = self.retry_wait_time; + + let mut status = self.check_default(request.try_clone().unwrap()).await; // TODO: try_clone + while retries < self.max_retries { + if status.is_success() || !status.should_retry() { + return status; + } + retries += 1; + tokio::time::sleep(wait_time).await; + wait_time = wait_time.saturating_mul(2); + status = self.check_default(request.try_clone().unwrap()).await; // TODO: try_clone + } + status + } + + /// Check a URI using [reqwest](https://github.com/seanmonstar/reqwest). + async fn check_default(&self, request: reqwest::Request) -> Status { + match self.reqwest_client.execute(request).await { + Ok(ref response) => Status::new(response, self.accepted.clone()), + Err(e) => e.into(), + } + } +} + +impl Chainable for Checker { + async fn chain(&mut self, input: Request) -> ChainResult { + ChainResult::Done(self.retry_request(input).await) + } +} diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index e3d6376461..1707171468 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -13,12 +13,7 @@ clippy::default_trait_access, clippy::used_underscore_binding )] -use std::{ - collections::HashSet, - path::Path, - sync::Arc, - time::Duration, -}; +use std::{collections::HashSet, path::Path, sync::Arc, time::Duration}; #[cfg(all(feature = "email-check", feature = "native-tls"))] use check_if_email_exists::{check_email, CheckEmailInput, Reachable}; @@ -35,11 +30,8 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ - chain::{ - Chain, - ChainResult::{Next, Done}, - RequestChain, - }, + chain::{Chain, RequestChain}, + checker::Checker, filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, @@ -587,7 +579,23 @@ impl Client { return Status::Unsupported(ErrorKind::InvalidURI(uri.clone())); } - let status = self.retry_request(uri, request_chain).await; + let request = self + .reqwest_client + .request(self.method.clone(), uri.as_str()) + .build(); + + let request = match request { + Ok(r) => r, + Err(e) => return e.into(), + }; + + let checker = Checker::new( + self.retry_wait_time, + self.max_retries, + self.reqwest_client.clone(), + self.accepted.clone(), + ); + let status = checker.retry_request(request).await; if status.is_success() { return status; } @@ -607,25 +615,6 @@ impl Client { status } - /// Retry requests up to `max_retries` times - /// with an exponential backoff. - async fn retry_request(&self, uri: &Uri, request_chain: &mut RequestChain) -> Status { - let mut retries: u64 = 0; - let mut wait_time = self.retry_wait_time; - - let mut status = self.check_default(uri, request_chain).await; - while retries < self.max_retries { - if status.is_success() || !status.should_retry() { - return status; - } - retries += 1; - tokio::time::sleep(wait_time).await; - wait_time = wait_time.saturating_mul(2); - status = self.check_default(uri, request_chain).await; - } - status - } - /// Check a `uri` hosted on `GitHub` via the GitHub API. /// /// # Caveats @@ -661,29 +650,6 @@ impl Client { Status::Ok(StatusCode::OK) } - /// Check a URI using [reqwest](https://github.com/seanmonstar/reqwest). - async fn check_default(&self, uri: &Uri, request_chain: &mut RequestChain) -> Status { - let request = self - .reqwest_client - .request(self.method.clone(), uri.as_str()) - .build(); - - let request = match request { - Ok(r) => r, - Err(e) => return e.into(), - }; - - let result = request_chain.traverse(request); - - match result { - Done(status) => status, - Next(r) => match self.reqwest_client.execute(r).await { - Ok(ref response) => Status::new(response, self.accepted.clone()), - Err(e) => e.into(), - }, - } - } - /// Check a `file` URI. pub async fn check_file(&self, uri: &Uri) -> Status { let Ok(path) = uri.url.to_file_path() else { @@ -1120,14 +1086,12 @@ mod tests { struct ExampleHandler(); impl Chainable for ExampleHandler { - fn chain(&mut self, _: Request) -> ChainResult { + async fn chain(&mut self, _: Request) -> ChainResult { ChainResult::Done(Status::Excluded) } } - let chain = RequestChain::new(vec![Box::new( - ExampleHandler {}, - )]); + let chain = RequestChain::new(vec![Box::new(ExampleHandler {})]); let client = ClientBuilder::builder() .request_chain(chain) diff --git a/lychee-lib/src/lib.rs b/lychee-lib/src/lib.rs index dd413868c4..a425063676 100644 --- a/lychee-lib/src/lib.rs +++ b/lychee-lib/src/lib.rs @@ -51,10 +51,11 @@ doc_comment::doctest!("../../README.md"); mod basic_auth; +mod chain; +mod checker; mod client; /// A pool of clients, to handle concurrent checks pub mod collector; -mod chain; mod quirks; mod retry; mod types; diff --git a/lychee-lib/src/quirks/mod.rs b/lychee-lib/src/quirks/mod.rs index 0b177d7554..bf0e758cb0 100644 --- a/lychee-lib/src/quirks/mod.rs +++ b/lychee-lib/src/quirks/mod.rs @@ -91,7 +91,7 @@ impl Quirks { } impl Chainable for Quirks { - fn chain(&mut self, input: Request) -> ChainResult { + async fn chain(&mut self, input: Request) -> ChainResult { ChainResult::Next(self.apply(input)) } } diff --git a/lychee-lib/src/types/basic_auth/credentials.rs b/lychee-lib/src/types/basic_auth/credentials.rs index 87c17dacf6..9c2ab3edca 100644 --- a/lychee-lib/src/types/basic_auth/credentials.rs +++ b/lychee-lib/src/types/basic_auth/credentials.rs @@ -74,7 +74,7 @@ impl BasicAuthCredentials { } impl Chainable for BasicAuthCredentials { - fn chain(&mut self, mut request: Request) -> ChainResult { + async fn chain(&mut self, mut request: Request) -> ChainResult { request .headers_mut() .append(AUTHORIZATION, self.to_authorization().0.encode()); From 0f012f3035a137d97622e04abfe98cdd89d90415 Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Wed, 20 Mar 2024 17:46:17 +0100 Subject: [PATCH 17/40] Use `async_trait` to fix issues with `Chain` type inference --- Cargo.lock | 5 +++-- lychee-lib/Cargo.toml | 1 + lychee-lib/src/chain/mod.rs | 20 +++++++++++-------- lychee-lib/src/checker.rs | 4 +++- lychee-lib/src/client.rs | 3 ++- lychee-lib/src/quirks/mod.rs | 2 ++ .../src/types/basic_auth/credentials.rs | 2 ++ 7 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4b3a458a06..1ab49f0e15 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -435,9 +435,9 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" dependencies = [ "proc-macro2", "quote", @@ -2394,6 +2394,7 @@ name = "lychee-lib" version = "0.14.3" dependencies = [ "async-stream", + "async-trait", "cached", "check-if-email-exists", "doc-comment", diff --git a/lychee-lib/Cargo.toml b/lychee-lib/Cargo.toml index 917266c5d7..0007424e75 100644 --- a/lychee-lib/Cargo.toml +++ b/lychee-lib/Cargo.toml @@ -13,6 +13,7 @@ version.workspace = true [dependencies] async-stream = "0.3.5" +async-trait = "0.1.78" cached = "0.46.1" check-if-email-exists = { version = "0.9.1", optional = true } email_address = "0.2.4" diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 3f0a8f0c1f..573aeb8c11 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,4 +1,5 @@ use crate::Status; +use async_trait::async_trait; use core::fmt::Debug; use std::sync::{Arc, Mutex}; @@ -34,10 +35,10 @@ impl Chain { self.0.lock().unwrap().append(&mut other.0.lock().unwrap()); } - pub(crate) fn traverse(&mut self, mut input: T) -> ChainResult { + pub(crate) async fn traverse(&mut self, mut input: T) -> ChainResult { use ChainResult::{Done, Next}; for e in self.0.lock().unwrap().iter_mut() { - match e.chain(input) { + match e.chain(input).await { Next(r) => input = r, Done(r) => { return Done(r); @@ -53,6 +54,7 @@ impl Chain { } } +#[async_trait] pub(crate) trait Chainable: Debug { async fn chain(&mut self, input: T) -> ChainResult; } @@ -63,6 +65,7 @@ mod test { ChainResult::{Done, Next}, Chainable, }; + use async_trait::async_trait; #[derive(Debug)] struct Add(usize); @@ -70,6 +73,7 @@ mod test { #[derive(Debug, PartialEq, Eq)] struct Result(usize); + #[async_trait] impl Chainable for Add { async fn chain(&mut self, req: Result) -> ChainResult { let added = req.0 + self.0; @@ -81,20 +85,20 @@ mod test { } } - #[test] - fn simple_chain() { + #[tokio::test] + async fn simple_chain() { use super::Chain; let mut chain: Chain = Chain::new(vec![Box::new(Add(7)), Box::new(Add(3))]); - let result = chain.traverse(Result(0)); + let result = chain.traverse(Result(0)).await; assert_eq!(result, Next(Result(10))); } - #[test] - fn early_exit_chain() { + #[tokio::test] + async fn early_exit_chain() { use super::Chain; let mut chain: Chain = Chain::new(vec![Box::new(Add(80)), Box::new(Add(30)), Box::new(Add(1))]); - let result = chain.traverse(Result(0)); + let result = chain.traverse(Result(0)).await; assert_eq!(result, Done(Result(80))); } } diff --git a/lychee-lib/src/checker.rs b/lychee-lib/src/checker.rs index 598884fefb..c6b76d3cec 100644 --- a/lychee-lib/src/checker.rs +++ b/lychee-lib/src/checker.rs @@ -1,8 +1,9 @@ use crate::{ - chain::{Chain, ChainResult, Chainable}, + chain::{ChainResult, Chainable}, retry::RetryExt, Status, }; +use async_trait::async_trait; use http::StatusCode; use reqwest::Request; use std::{collections::HashSet, time::Duration}; @@ -58,6 +59,7 @@ impl Checker { } } +#[async_trait] impl Chainable for Checker { async fn chain(&mut self, input: Request) -> ChainResult { ChainResult::Done(self.retry_request(input).await) diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 1707171468..49499a3a37 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -35,7 +35,6 @@ use crate::{ filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, - retry::RetryExt, types::uri::github::GithubUri, utils::fragment_checker::FragmentChecker, BasicAuthCredentials, ErrorKind, Request, Response, Result, Status, Uri, @@ -743,6 +742,7 @@ mod tests { time::{Duration, Instant}, }; + use async_trait::async_trait; use http::{header::HeaderMap, StatusCode}; use reqwest::header; use tempfile::tempdir; @@ -1085,6 +1085,7 @@ mod tests { #[derive(Debug)] struct ExampleHandler(); + #[async_trait] impl Chainable for ExampleHandler { async fn chain(&mut self, _: Request) -> ChainResult { ChainResult::Done(Status::Excluded) diff --git a/lychee-lib/src/quirks/mod.rs b/lychee-lib/src/quirks/mod.rs index bf0e758cb0..333b921310 100644 --- a/lychee-lib/src/quirks/mod.rs +++ b/lychee-lib/src/quirks/mod.rs @@ -2,6 +2,7 @@ use crate::{ chain::{ChainResult, Chainable}, Status, }; +use async_trait::async_trait; use header::HeaderValue; use http::header; use once_cell::sync::Lazy; @@ -90,6 +91,7 @@ impl Quirks { } } +#[async_trait] impl Chainable for Quirks { async fn chain(&mut self, input: Request) -> ChainResult { ChainResult::Next(self.apply(input)) diff --git a/lychee-lib/src/types/basic_auth/credentials.rs b/lychee-lib/src/types/basic_auth/credentials.rs index 9c2ab3edca..2bdf19e7b5 100644 --- a/lychee-lib/src/types/basic_auth/credentials.rs +++ b/lychee-lib/src/types/basic_auth/credentials.rs @@ -1,3 +1,4 @@ +use async_trait::async_trait; use std::str::FromStr; use headers::authorization::Credentials; @@ -73,6 +74,7 @@ impl BasicAuthCredentials { } } +#[async_trait] impl Chainable for BasicAuthCredentials { async fn chain(&mut self, mut request: Request) -> ChainResult { request From 3ec3a8228cc55655a3420831aabb33741b416d49 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Fri, 22 Mar 2024 12:39:20 +0100 Subject: [PATCH 18/40] Make checker part of the request chain --- lychee-lib/src/chain/mod.rs | 20 +++++++++---------- lychee-lib/src/checker.rs | 6 +++--- lychee-lib/src/client.rs | 40 ++++++++++++++++++------------------- 3 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 573aeb8c11..d303a92784 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,7 +1,8 @@ use crate::Status; use async_trait::async_trait; use core::fmt::Debug; -use std::sync::{Arc, Mutex}; +use std::sync::Arc; +use tokio::sync::Mutex; #[derive(Debug, PartialEq)] pub(crate) enum ChainResult { @@ -11,8 +12,10 @@ pub(crate) enum ChainResult { pub(crate) type RequestChain = Chain; +pub(crate) type InnerChain = Vec + Send>>; + #[derive(Debug)] -pub struct Chain(Arc + Send>>>>); +pub struct Chain(Arc>>); impl Default for Chain { fn default() -> Self { @@ -27,17 +30,13 @@ impl Clone for Chain { } impl Chain { - pub(crate) fn new(values: Vec + Send>>) -> Self { + pub(crate) fn new(values: InnerChain) -> Self { Self(Arc::new(Mutex::new(values))) } - pub(crate) fn append(&mut self, other: &Chain) { - self.0.lock().unwrap().append(&mut other.0.lock().unwrap()); - } - pub(crate) async fn traverse(&mut self, mut input: T) -> ChainResult { use ChainResult::{Done, Next}; - for e in self.0.lock().unwrap().iter_mut() { + for e in self.0.lock().await.iter_mut() { match e.chain(input).await { Next(r) => input = r, Done(r) => { @@ -49,8 +48,9 @@ impl Chain { Next(input) } - pub(crate) fn push(&mut self, value: Box + Send>) { - self.0.lock().unwrap().push(value); + // TODO: probably remove + pub(crate) fn into_inner(self) -> InnerChain { + Arc::try_unwrap(self.0).expect("Arc still has multiple owners").into_inner() } } diff --git a/lychee-lib/src/checker.rs b/lychee-lib/src/checker.rs index c6b76d3cec..c920550d74 100644 --- a/lychee-lib/src/checker.rs +++ b/lychee-lib/src/checker.rs @@ -17,7 +17,7 @@ pub(crate) struct Checker { } impl Checker { - pub(crate) fn new( + pub(crate) const fn new( retry_wait_time: Duration, max_retries: u64, reqwest_client: reqwest::Client, @@ -33,7 +33,7 @@ impl Checker { /// Retry requests up to `max_retries` times /// with an exponential backoff. - pub(crate) async fn retry_request(&self, request: reqwest::Request) -> Status { + pub(crate) async fn retry_request(&self, request: Request) -> Status { let mut retries: u64 = 0; let mut wait_time = self.retry_wait_time; @@ -51,7 +51,7 @@ impl Checker { } /// Check a URI using [reqwest](https://github.com/seanmonstar/reqwest). - async fn check_default(&self, request: reqwest::Request) -> Status { + async fn check_default(&self, request: Request) -> Status { match self.reqwest_client.execute(request).await { Ok(ref response) => Status::new(response, self.accepted.clone()), Err(e) => e.into(), diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 49499a3a37..cadd7e3463 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -30,14 +30,13 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ - chain::{Chain, RequestChain}, + chain::{Chain, ChainResult, RequestChain}, checker::Checker, filter::{Excludes, Filter, Includes}, quirks::Quirks, remap::Remaps, types::uri::github::GithubUri, - utils::fragment_checker::FragmentChecker, - BasicAuthCredentials, ErrorKind, Request, Response, Result, Status, Uri, + utils::fragment_checker::FragmentChecker, ErrorKind, Request, Response, Result, Status, Uri, }; #[cfg(all(feature = "email-check", feature = "native-tls"))] @@ -486,10 +485,20 @@ impl Client { return Ok(Response::new(uri.clone(), Status::Excluded, source)); } + let request_chain: RequestChain = Chain::new(vec![ + Box::::default(), + Box::new(Checker::new( + self.retry_wait_time, + self.max_retries, + self.reqwest_client.clone(), + self.accepted.clone(), + )), + ]); + let status = match uri.scheme() { _ if uri.is_file() => self.check_file(uri).await, _ if uri.is_mail() => self.check_mail(uri).await, - _ => self.check_website(uri, credentials).await?, + _ => self.check_website(uri, request_chain).await?, }; Ok(Response::new(uri.clone(), status, source)) @@ -525,16 +534,8 @@ impl Client { pub async fn check_website( &self, uri: &Uri, - credentials: &Option, + mut request_chain: RequestChain, ) -> Result { - let quirks = Quirks::default(); - let mut request_chain: RequestChain = Chain::new(vec![Box::new(quirks)]); - request_chain.append(&self.request_chain); - - if let Some(c) = credentials { - request_chain.push(Box::new(c.clone())); - } - match self.check_website_inner(uri, &mut request_chain).await { Status::Ok(code) if self.require_https && uri.scheme() == "http" => { if self @@ -588,13 +589,12 @@ impl Client { Err(e) => return e.into(), }; - let checker = Checker::new( - self.retry_wait_time, - self.max_retries, - self.reqwest_client.clone(), - self.accepted.clone(), - ); - let status = checker.retry_request(request).await; + let status = match request_chain.traverse(request).await { + // consider as excluded if no chain element has converted it to a done + ChainResult::Next(_) => Status::Excluded, + ChainResult::Done(status) => status, + }; + if status.is_success() { return status; } From 41e7f88da45f6999bf84d5a77f2016ee799d1c0a Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Wed, 3 Apr 2024 13:15:41 +0200 Subject: [PATCH 19/40] Add credentials to chain --- lychee-lib/src/checker.rs | 2 +- lychee-lib/src/client.rs | 7 +++++-- lychee-lib/src/types/basic_auth/credentials.rs | 11 +++++++---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lychee-lib/src/checker.rs b/lychee-lib/src/checker.rs index c920550d74..9a4d7b921e 100644 --- a/lychee-lib/src/checker.rs +++ b/lychee-lib/src/checker.rs @@ -8,7 +8,7 @@ use http::StatusCode; use reqwest::Request; use std::{collections::HashSet, time::Duration}; -#[derive(Debug)] +#[derive(Debug, Clone)] pub(crate) struct Checker { retry_wait_time: Duration, max_retries: u64, diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index cadd7e3463..62e32a73ff 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -36,7 +36,8 @@ use crate::{ quirks::Quirks, remap::Remaps, types::uri::github::GithubUri, - utils::fragment_checker::FragmentChecker, ErrorKind, Request, Response, Result, Status, Uri, + utils::fragment_checker::FragmentChecker, + ErrorKind, Request, Response, Result, Status, Uri, }; #[cfg(all(feature = "email-check", feature = "native-tls"))] @@ -465,7 +466,7 @@ impl Client { { let Request { ref mut uri, - ref credentials, + credentials, source, .. } = request.try_into()?; @@ -486,7 +487,9 @@ impl Client { } let request_chain: RequestChain = Chain::new(vec![ + // TODO: insert self.request_chain Box::::default(), + Box::new(credentials), Box::new(Checker::new( self.retry_wait_time, self.max_retries, diff --git a/lychee-lib/src/types/basic_auth/credentials.rs b/lychee-lib/src/types/basic_auth/credentials.rs index 2bdf19e7b5..906357f671 100644 --- a/lychee-lib/src/types/basic_auth/credentials.rs +++ b/lychee-lib/src/types/basic_auth/credentials.rs @@ -75,11 +75,14 @@ impl BasicAuthCredentials { } #[async_trait] -impl Chainable for BasicAuthCredentials { +impl Chainable for Option { async fn chain(&mut self, mut request: Request) -> ChainResult { - request - .headers_mut() - .append(AUTHORIZATION, self.to_authorization().0.encode()); + if let Some(credentials) = self { + request + .headers_mut() + .append(AUTHORIZATION, credentials.to_authorization().0.encode()); + } + ChainResult::Next(request) } } From 93afae54bb4893f3f3191f6aae4be03d6f8e6e92 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Fri, 5 Apr 2024 08:37:09 +0200 Subject: [PATCH 20/40] Create ClientRequestChain helper structure to combine multiple chains --- lychee-lib/src/chain/mod.rs | 37 +++++++++++++++++++++++++++++-------- lychee-lib/src/client.rs | 30 +++++++++++------------------- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index d303a92784..738b90800c 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -34,7 +34,7 @@ impl Chain { Self(Arc::new(Mutex::new(values))) } - pub(crate) async fn traverse(&mut self, mut input: T) -> ChainResult { + pub(crate) async fn traverse(&self, mut input: T) -> ChainResult { use ChainResult::{Done, Next}; for e in self.0.lock().await.iter_mut() { match e.chain(input).await { @@ -47,11 +47,6 @@ impl Chain { Next(input) } - - // TODO: probably remove - pub(crate) fn into_inner(self) -> InnerChain { - Arc::try_unwrap(self.0).expect("Arc still has multiple owners").into_inner() - } } #[async_trait] @@ -59,6 +54,32 @@ pub(crate) trait Chainable: Debug { async fn chain(&mut self, input: T) -> ChainResult; } +#[derive(Debug)] +pub(crate) struct ClientRequestChain<'a> { + chains: Vec<&'a RequestChain>, +} + +impl<'a> ClientRequestChain<'a> { + pub(crate) fn new(chains: Vec<&'a RequestChain>) -> Self { + Self { chains } + } + + pub(crate) async fn traverse(&self, mut input: reqwest::Request) -> Status { + use ChainResult::{Done, Next}; + for e in &self.chains { + match e.traverse(input).await { + Next(r) => input = r, + Done(r) => { + return r; + } + } + } + + // consider as excluded if no chain element has converted it to a done + Status::Excluded + } +} + mod test { use super::{ ChainResult, @@ -88,7 +109,7 @@ mod test { #[tokio::test] async fn simple_chain() { use super::Chain; - let mut chain: Chain = Chain::new(vec![Box::new(Add(7)), Box::new(Add(3))]); + let chain: Chain = Chain::new(vec![Box::new(Add(7)), Box::new(Add(3))]); let result = chain.traverse(Result(0)).await; assert_eq!(result, Next(Result(10))); } @@ -96,7 +117,7 @@ mod test { #[tokio::test] async fn early_exit_chain() { use super::Chain; - let mut chain: Chain = + let chain: Chain = Chain::new(vec![Box::new(Add(80)), Box::new(Add(30)), Box::new(Add(1))]); let result = chain.traverse(Result(0)).await; assert_eq!(result, Done(Result(80))); diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 62e32a73ff..7b52aecaaa 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -30,7 +30,7 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ - chain::{Chain, ChainResult, RequestChain}, + chain::{Chain, ClientRequestChain, RequestChain}, checker::Checker, filter::{Excludes, Filter, Includes}, quirks::Quirks, @@ -279,7 +279,7 @@ pub struct ClientBuilder { /// can modify the request. A chained item can also decide to exit /// early and return a status, so that subsequent chain items are /// skipped and no HTTP request is ever made. - request_chain: RequestChain, + plugin_request_chain: RequestChain, } impl Default for ClientBuilder { @@ -392,7 +392,7 @@ impl ClientBuilder { require_https: self.require_https, include_fragments: self.include_fragments, fragment_checker: FragmentChecker::new(), - request_chain: self.request_chain, + plugin_request_chain: self.plugin_request_chain, }) } } @@ -443,7 +443,7 @@ pub struct Client { /// Caches Fragments fragment_checker: FragmentChecker, - request_chain: RequestChain, + plugin_request_chain: RequestChain, } impl Client { @@ -487,7 +487,6 @@ impl Client { } let request_chain: RequestChain = Chain::new(vec![ - // TODO: insert self.request_chain Box::::default(), Box::new(credentials), Box::new(Checker::new( @@ -534,15 +533,11 @@ impl Client { /// - The request failed. /// - The response status code is not accepted. /// - The URI cannot be converted to HTTPS. - pub async fn check_website( - &self, - uri: &Uri, - mut request_chain: RequestChain, - ) -> Result { - match self.check_website_inner(uri, &mut request_chain).await { + pub async fn check_website(&self, uri: &Uri, request_chain: RequestChain) -> Result { + match self.check_website_inner(uri, &request_chain).await { Status::Ok(code) if self.require_https && uri.scheme() == "http" => { if self - .check_website_inner(&uri.to_https()?, &mut request_chain) + .check_website_inner(&uri.to_https()?, &request_chain) .await .is_success() { @@ -567,7 +562,7 @@ impl Client { /// - The URI is invalid. /// - The request failed. /// - The response status code is not accepted. - pub async fn check_website_inner(&self, uri: &Uri, request_chain: &mut RequestChain) -> Status { + pub async fn check_website_inner(&self, uri: &Uri, request_chain: &RequestChain) -> Status { // Workaround for upstream reqwest panic if validate_url(&uri.url) { if matches!(uri.scheme(), "http" | "https") { @@ -592,11 +587,8 @@ impl Client { Err(e) => return e.into(), }; - let status = match request_chain.traverse(request).await { - // consider as excluded if no chain element has converted it to a done - ChainResult::Next(_) => Status::Excluded, - ChainResult::Done(status) => status, - }; + let chain = ClientRequestChain::new(vec![&self.plugin_request_chain, request_chain]); + let status = chain.traverse(request).await; if status.is_success() { return status; @@ -1098,7 +1090,7 @@ mod tests { let chain = RequestChain::new(vec![Box::new(ExampleHandler {})]); let client = ClientBuilder::builder() - .request_chain(chain) + .plugin_request_chain(chain) .build() .client() .unwrap(); From a3184190b8cc8bb7a25d8739fa9456941b8dd4f3 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Fri, 5 Apr 2024 12:10:43 +0200 Subject: [PATCH 21/40] Small tweaks & extract method --- lychee-lib/src/client.rs | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 7b52aecaaa..72727ee49f 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -278,7 +278,7 @@ pub struct ClientBuilder { /// Requests run through this chain where each item in the chain /// can modify the request. A chained item can also decide to exit /// early and return a status, so that subsequent chain items are - /// skipped and no HTTP request is ever made. + /// skipped and the lychee-internal request chain is not activated. plugin_request_chain: RequestChain, } @@ -486,7 +486,7 @@ impl Client { return Ok(Response::new(uri.clone(), Status::Excluded, source)); } - let request_chain: RequestChain = Chain::new(vec![ + let chain: RequestChain = Chain::new(vec![ Box::::default(), Box::new(credentials), Box::new(Checker::new( @@ -500,7 +500,7 @@ impl Client { let status = match uri.scheme() { _ if uri.is_file() => self.check_file(uri).await, _ if uri.is_mail() => self.check_mail(uri).await, - _ => self.check_website(uri, request_chain).await?, + _ => self.check_website(uri, chain).await?, }; Ok(Response::new(uri.clone(), status, source)) @@ -533,11 +533,11 @@ impl Client { /// - The request failed. /// - The response status code is not accepted. /// - The URI cannot be converted to HTTPS. - pub async fn check_website(&self, uri: &Uri, request_chain: RequestChain) -> Result { - match self.check_website_inner(uri, &request_chain).await { + pub async fn check_website(&self, uri: &Uri, chain: RequestChain) -> Result { + match self.check_website_inner(uri, &chain).await { Status::Ok(code) if self.require_https && uri.scheme() == "http" => { if self - .check_website_inner(&uri.to_https()?, &request_chain) + .check_website_inner(&uri.to_https()?, &chain) .await .is_success() { @@ -562,7 +562,7 @@ impl Client { /// - The URI is invalid. /// - The request failed. /// - The response status code is not accepted. - pub async fn check_website_inner(&self, uri: &Uri, request_chain: &RequestChain) -> Status { + pub async fn check_website_inner(&self, uri: &Uri, chain: &RequestChain) -> Status { // Workaround for upstream reqwest panic if validate_url(&uri.url) { if matches!(uri.scheme(), "http" | "https") { @@ -587,16 +587,21 @@ impl Client { Err(e) => return e.into(), }; - let chain = ClientRequestChain::new(vec![&self.plugin_request_chain, request_chain]); - let status = chain.traverse(request).await; + let status = ClientRequestChain::new(vec![&self.plugin_request_chain, chain]) + .traverse(request) + .await; + + self.handle_github(status, uri).await + } + // Pull out the heavy machinery in case of a failed normal request. + // This could be a GitHub URL and we ran into the rate limiter. + // TODO: We should first try to parse the URI as GitHub URI first (Lucius, Jan 2023) + async fn handle_github(&self, status: Status, uri: &Uri) -> Status { if status.is_success() { return status; } - // Pull out the heavy machinery in case of a failed normal request. - // This could be a GitHub URL and we ran into the rate limiter. - // TODO: We should first try to parse the URI as GitHub URI first (Lucius, Jan 2023) if let Ok(github_uri) = GithubUri::try_from(uri) { let status = self.check_github(github_uri).await; // Only return Github status in case of success From d5b9b84db663fd09f84dfb2b862818106e7a73ab Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Thu, 11 Apr 2024 11:33:38 +0200 Subject: [PATCH 22/40] Extract function and add SAFETY note --- lychee-lib/src/checker.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lychee-lib/src/checker.rs b/lychee-lib/src/checker.rs index 9a4d7b921e..7dfad42051 100644 --- a/lychee-lib/src/checker.rs +++ b/lychee-lib/src/checker.rs @@ -37,7 +37,7 @@ impl Checker { let mut retries: u64 = 0; let mut wait_time = self.retry_wait_time; - let mut status = self.check_default(request.try_clone().unwrap()).await; // TODO: try_clone + let mut status = self.check_default(clone_unwrap(&request)).await; while retries < self.max_retries { if status.is_success() || !status.should_retry() { return status; @@ -45,7 +45,7 @@ impl Checker { retries += 1; tokio::time::sleep(wait_time).await; wait_time = wait_time.saturating_mul(2); - status = self.check_default(request.try_clone().unwrap()).await; // TODO: try_clone + status = self.check_default(clone_unwrap(&request)).await; } status } @@ -59,6 +59,12 @@ impl Checker { } } +/// SAFETY: unwrapping the `try_clone` of `reqwest::Request` is safe because a request only fails to be cloned when `body` of `Request` is a stream +/// and `body` cannot be a stream as long as the `stream` feature is disabled. +fn clone_unwrap(request: &Request) -> Request { + request.try_clone().unwrap() +} + #[async_trait] impl Chainable for Checker { async fn chain(&mut self, input: Request) -> ChainResult { From 9ed97213a1a15e33ab847e11d1440a615413d7de Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Apr 2024 15:38:01 +0200 Subject: [PATCH 23/40] Add documentation to `chain` module Also make `Chainable` and `ChainResult` public to support external plugins/handlers. --- lychee-lib/src/chain/mod.rs | 111 ++++++++++++++++++++++++++++++++++-- lychee-lib/src/client.rs | 16 +++--- lychee-lib/src/lib.rs | 2 + 3 files changed, 115 insertions(+), 14 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 738b90800c..c99f47b358 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -1,39 +1,89 @@ +//! [Chain of responsibility pattern][pattern] implementation. +//! +//! lychee is based on a chain of responsibility, where each handler can modify +//! a request and decide if it should be passed to the next element or not. +//! +//! The chain is implemented as a vector of [`Chainable`] handlers. It is +//! traversed by calling [`Chain::traverse`], which will call +//! [`Chainable::chain`] on each handler in the chain. +//! +//! To add external handlers, you can implement the [`Chainable`] trait and add +//! the handler to the chain. +//! +//! [pattern]: https://github.com/lpxxn/rust-design-pattern/blob/master/behavioral/chain_of_responsibility.rs use crate::Status; use async_trait::async_trait; use core::fmt::Debug; use std::sync::Arc; use tokio::sync::Mutex; +/// Result of a handler. +/// +/// This is used to decide if the chain should continue to the next handler or +/// stop and return the result: +/// +/// - If the chain should continue, the handler should return +/// [`ChainResult::Next`]. This will traverse the next handler in the chain. +/// - If the chain should stop, the handler should return [`ChainResult::Done`]. +/// This will stop the chain immediately and return the result of the handler. #[derive(Debug, PartialEq)] -pub(crate) enum ChainResult { +pub enum ChainResult { + /// Continue to the next handler in the chain. Next(T), + /// Stop the chain and return the result. Done(R), } +/// Request chain type +/// +/// This takes a request and returns a status. pub(crate) type RequestChain = Chain; +/// Inner chain type. +/// +/// This holds all handlers, which were chained together. +/// Handlers are traversed in order. +/// +/// Each handler needs to implement the `Chainable` trait and be `Send`, because +/// the chain is traversed concurrently and the handlers can be sent between +/// threads. pub(crate) type InnerChain = Vec + Send>>; +/// The outer chain type. +/// +/// This is a wrapper around the inner chain type and allows for +/// concurrent access to the chain. #[derive(Debug)] pub struct Chain(Arc>>); impl Default for Chain { fn default() -> Self { - Self(Arc::new(Mutex::new(vec![]))) + Self(Arc::new(Mutex::new(InnerChain::default()))) } } impl Clone for Chain { fn clone(&self) -> Self { + // Cloning the chain is a cheap operation, because the inner chain is + // wrapped in an `Arc` and `Mutex`. Self(self.0.clone()) } } impl Chain { + /// Create a new chain from a vector of chainable handlers pub(crate) fn new(values: InnerChain) -> Self { Self(Arc::new(Mutex::new(values))) } + /// Traverse the chain with the given input. + /// + /// This will call `chain` on each handler in the chain and return + /// the result. If a handler returns `ChainResult::Done`, the chain + /// will stop and return. + /// + /// If no handler returns `ChainResult::Done`, the chain will return + /// `ChainResult::Next` with the input. pub(crate) async fn traverse(&self, mut input: T) -> ChainResult { use ChainResult::{Done, Next}; for e in self.0.lock().await.iter_mut() { @@ -49,23 +99,71 @@ impl Chain { } } +/// Chainable trait for implementing request handlers +/// +/// This trait needs to be implemented by all chainable handlers. +/// It is the only requirement to handle requests in lychee. +/// +/// It takes an input request and returns a [`ChainResult`], which can be either +/// [`ChainResult::Next`] to continue to the next handler or +/// [`ChainResult::Done`] to stop the chain. +/// +/// The request can be modified by the handler before it is passed to the next +/// handler. This allows for modifying the request, such as adding headers or +/// changing the URL (e.g. for remapping or filtering). #[async_trait] -pub(crate) trait Chainable: Debug { +pub trait Chainable: Debug { + /// Given an input request, return a [`ChainResult`] to continue or stop the + /// chain. + /// + /// The input request can be modified by the handler before it is passed to + /// the next handler. + /// + /// # Example + /// + /// ``` + /// use lychee_lib::{Chainable, ChainResult, Status}; + /// use reqwest::Request; + /// use async_trait::async_trait; + /// + /// #[derive(Debug)] + /// struct AddHeader; + /// + /// #[async_trait] + /// impl Chainable for AddHeader { + /// async fn chain(&mut self, mut request: Request) -> ChainResult { + /// // You can modify the request however you like here + /// request.headers_mut().append("X-Header", "value".parse().unwrap()); + /// + /// // Pass the request to the next handler + /// ChainResult::Next(request) + /// } + /// } + /// ``` async fn chain(&mut self, input: T) -> ChainResult; } +/// Client request chains +/// +/// This struct holds all request chains. +/// +/// Usually, this is used to hold the default request chain and the external +/// plugin request chain. #[derive(Debug)] -pub(crate) struct ClientRequestChain<'a> { +pub(crate) struct ClientRequestChains<'a> { chains: Vec<&'a RequestChain>, } -impl<'a> ClientRequestChain<'a> { +impl<'a> ClientRequestChains<'a> { + /// Create a new chain of request chains. pub(crate) fn new(chains: Vec<&'a RequestChain>) -> Self { Self { chains } } + /// Traverse all request chains and resolve to a status. pub(crate) async fn traverse(&self, mut input: reqwest::Request) -> Status { use ChainResult::{Done, Next}; + for e in &self.chains { match e.traverse(input).await { Next(r) => input = r, @@ -75,7 +173,8 @@ impl<'a> ClientRequestChain<'a> { } } - // consider as excluded if no chain element has converted it to a done + // Consider the request to be excluded if no chain element has converted + // it to a `ChainResult::Done` Status::Excluded } } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 72727ee49f..0bbc422103 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -30,7 +30,7 @@ use secrecy::{ExposeSecret, SecretString}; use typed_builder::TypedBuilder; use crate::{ - chain::{Chain, ClientRequestChain, RequestChain}, + chain::{Chain, ClientRequestChains, RequestChain}, checker::Checker, filter::{Excludes, Filter, Includes}, quirks::Quirks, @@ -486,7 +486,7 @@ impl Client { return Ok(Response::new(uri.clone(), Status::Excluded, source)); } - let chain: RequestChain = Chain::new(vec![ + let default_chain: RequestChain = Chain::new(vec![ Box::::default(), Box::new(credentials), Box::new(Checker::new( @@ -500,7 +500,7 @@ impl Client { let status = match uri.scheme() { _ if uri.is_file() => self.check_file(uri).await, _ if uri.is_mail() => self.check_mail(uri).await, - _ => self.check_website(uri, chain).await?, + _ => self.check_website(uri, default_chain).await?, }; Ok(Response::new(uri.clone(), status, source)) @@ -533,11 +533,11 @@ impl Client { /// - The request failed. /// - The response status code is not accepted. /// - The URI cannot be converted to HTTPS. - pub async fn check_website(&self, uri: &Uri, chain: RequestChain) -> Result { - match self.check_website_inner(uri, &chain).await { + pub async fn check_website(&self, uri: &Uri, default_chain: RequestChain) -> Result { + match self.check_website_inner(uri, &default_chain).await { Status::Ok(code) if self.require_https && uri.scheme() == "http" => { if self - .check_website_inner(&uri.to_https()?, &chain) + .check_website_inner(&uri.to_https()?, &default_chain) .await .is_success() { @@ -562,7 +562,7 @@ impl Client { /// - The URI is invalid. /// - The request failed. /// - The response status code is not accepted. - pub async fn check_website_inner(&self, uri: &Uri, chain: &RequestChain) -> Status { + pub async fn check_website_inner(&self, uri: &Uri, default_chain: &RequestChain) -> Status { // Workaround for upstream reqwest panic if validate_url(&uri.url) { if matches!(uri.scheme(), "http" | "https") { @@ -587,7 +587,7 @@ impl Client { Err(e) => return e.into(), }; - let status = ClientRequestChain::new(vec![&self.plugin_request_chain, chain]) + let status = ClientRequestChains::new(vec![&self.plugin_request_chain, default_chain]) .traverse(request) .await; diff --git a/lychee-lib/src/lib.rs b/lychee-lib/src/lib.rs index a425063676..272bd78a7b 100644 --- a/lychee-lib/src/lib.rs +++ b/lychee-lib/src/lib.rs @@ -85,6 +85,8 @@ use openssl_sys as _; // required for vendored-openssl feature #[doc(inline)] pub use crate::{ basic_auth::BasicAuthExtractor, + // Expose the `Chainable` trait to allow defining external handlers (plugins) + chain::{ChainResult, Chainable}, // Constants get exposed so that the CLI can use the same defaults as the library client::{ check, Client, ClientBuilder, DEFAULT_MAX_REDIRECTS, DEFAULT_MAX_RETRIES, From 9b4fd8d0fc55ffff212322a1b7c90b4fa83818a1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 21 Apr 2024 15:52:00 +0200 Subject: [PATCH 24/40] Extend docs around `clone_unwrap` --- lychee-lib/src/chain/mod.rs | 2 +- lychee-lib/src/checker.rs | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index c99f47b358..020b8747e0 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -134,7 +134,7 @@ pub trait Chainable: Debug { /// async fn chain(&mut self, mut request: Request) -> ChainResult { /// // You can modify the request however you like here /// request.headers_mut().append("X-Header", "value".parse().unwrap()); - /// + /// /// // Pass the request to the next handler /// ChainResult::Next(request) /// } diff --git a/lychee-lib/src/checker.rs b/lychee-lib/src/checker.rs index 7dfad42051..695c00f11d 100644 --- a/lychee-lib/src/checker.rs +++ b/lychee-lib/src/checker.rs @@ -59,10 +59,17 @@ impl Checker { } } -/// SAFETY: unwrapping the `try_clone` of `reqwest::Request` is safe because a request only fails to be cloned when `body` of `Request` is a stream -/// and `body` cannot be a stream as long as the `stream` feature is disabled. +/// Clones a `reqwest::Request`. +/// +/// # Safety +/// +/// This panics if the request cannot be cloned. This should only happen if the +/// request body is a `reqwest` stream. We disable the `stream` feature, so the +/// body should never be a stream. +/// +/// See fn clone_unwrap(request: &Request) -> Request { - request.try_clone().unwrap() + request.try_clone().expect("Failed to clone request: body was a stream, which should be impossible with `stream` feature disabled") } #[async_trait] From e3a236b257b38ce16e4cfa0a76d0e3ecedfafd11 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Mon, 22 Apr 2024 09:26:12 +0200 Subject: [PATCH 25/40] Adjust documentation --- lychee-lib/src/chain/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 020b8747e0..6935275b4e 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -5,7 +5,7 @@ //! //! The chain is implemented as a vector of [`Chainable`] handlers. It is //! traversed by calling [`Chain::traverse`], which will call -//! [`Chainable::chain`] on each handler in the chain. +//! [`Chainable::chain`] on each handler in the chain consecutively. //! //! To add external handlers, you can implement the [`Chainable`] trait and add //! the handler to the chain. @@ -25,7 +25,7 @@ use tokio::sync::Mutex; /// - If the chain should continue, the handler should return /// [`ChainResult::Next`]. This will traverse the next handler in the chain. /// - If the chain should stop, the handler should return [`ChainResult::Done`]. -/// This will stop the chain immediately and return the result of the handler. +/// All subsequent chain elements are skipped and the result is returned. #[derive(Debug, PartialEq)] pub enum ChainResult { /// Continue to the next handler in the chain. From ddcca65e728a6a5d42b4778b3118407251c6e1cd Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Mon, 22 Apr 2024 10:59:29 +0200 Subject: [PATCH 26/40] Rename Chainable to Handler --- lychee-lib/src/chain/mod.rs | 22 +++++++++---------- lychee-lib/src/checker.rs | 4 ++-- lychee-lib/src/client.rs | 4 ++-- lychee-lib/src/lib.rs | 4 ++-- lychee-lib/src/quirks/mod.rs | 4 ++-- .../src/types/basic_auth/credentials.rs | 4 ++-- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 6935275b4e..59ea75c864 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -3,11 +3,11 @@ //! lychee is based on a chain of responsibility, where each handler can modify //! a request and decide if it should be passed to the next element or not. //! -//! The chain is implemented as a vector of [`Chainable`] handlers. It is +//! The chain is implemented as a vector of [`Handler`] handlers. It is //! traversed by calling [`Chain::traverse`], which will call -//! [`Chainable::chain`] on each handler in the chain consecutively. +//! [`Handler::chain`] on each handler in the chain consecutively. //! -//! To add external handlers, you can implement the [`Chainable`] trait and add +//! To add external handlers, you can implement the [`Handler`] trait and add //! the handler to the chain. //! //! [pattern]: https://github.com/lpxxn/rust-design-pattern/blob/master/behavioral/chain_of_responsibility.rs @@ -44,10 +44,10 @@ pub(crate) type RequestChain = Chain; /// This holds all handlers, which were chained together. /// Handlers are traversed in order. /// -/// Each handler needs to implement the `Chainable` trait and be `Send`, because +/// Each handler needs to implement the `Handler` trait and be `Send`, because /// the chain is traversed concurrently and the handlers can be sent between /// threads. -pub(crate) type InnerChain = Vec + Send>>; +pub(crate) type InnerChain = Vec + Send>>; /// The outer chain type. /// @@ -99,7 +99,7 @@ impl Chain { } } -/// Chainable trait for implementing request handlers +/// Handler trait for implementing request handlers /// /// This trait needs to be implemented by all chainable handlers. /// It is the only requirement to handle requests in lychee. @@ -112,7 +112,7 @@ impl Chain { /// handler. This allows for modifying the request, such as adding headers or /// changing the URL (e.g. for remapping or filtering). #[async_trait] -pub trait Chainable: Debug { +pub trait Handler: Debug { /// Given an input request, return a [`ChainResult`] to continue or stop the /// chain. /// @@ -122,7 +122,7 @@ pub trait Chainable: Debug { /// # Example /// /// ``` - /// use lychee_lib::{Chainable, ChainResult, Status}; + /// use lychee_lib::{Handler, ChainResult, Status}; /// use reqwest::Request; /// use async_trait::async_trait; /// @@ -130,7 +130,7 @@ pub trait Chainable: Debug { /// struct AddHeader; /// /// #[async_trait] - /// impl Chainable for AddHeader { + /// impl Handler for AddHeader { /// async fn chain(&mut self, mut request: Request) -> ChainResult { /// // You can modify the request however you like here /// request.headers_mut().append("X-Header", "value".parse().unwrap()); @@ -183,7 +183,7 @@ mod test { use super::{ ChainResult, ChainResult::{Done, Next}, - Chainable, + Handler, }; use async_trait::async_trait; @@ -194,7 +194,7 @@ mod test { struct Result(usize); #[async_trait] - impl Chainable for Add { + impl Handler for Add { async fn chain(&mut self, req: Result) -> ChainResult { let added = req.0 + self.0; if added > 100 { diff --git a/lychee-lib/src/checker.rs b/lychee-lib/src/checker.rs index 695c00f11d..ba89c1ce74 100644 --- a/lychee-lib/src/checker.rs +++ b/lychee-lib/src/checker.rs @@ -1,5 +1,5 @@ use crate::{ - chain::{ChainResult, Chainable}, + chain::{ChainResult, Handler}, retry::RetryExt, Status, }; @@ -73,7 +73,7 @@ fn clone_unwrap(request: &Request) -> Request { } #[async_trait] -impl Chainable for Checker { +impl Handler for Checker { async fn chain(&mut self, input: Request) -> ChainResult { ChainResult::Done(self.retry_request(input).await) } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 0bbc422103..15f9397cfb 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -750,7 +750,7 @@ mod tests { use super::ClientBuilder; use crate::{ - chain::{ChainResult, Chainable, RequestChain}, + chain::{ChainResult, Handler, RequestChain}, mock_server, test_utils::get_mock_client_response, Request, Status, Uri, @@ -1086,7 +1086,7 @@ mod tests { struct ExampleHandler(); #[async_trait] - impl Chainable for ExampleHandler { + impl Handler for ExampleHandler { async fn chain(&mut self, _: Request) -> ChainResult { ChainResult::Done(Status::Excluded) } diff --git a/lychee-lib/src/lib.rs b/lychee-lib/src/lib.rs index 272bd78a7b..98a0701485 100644 --- a/lychee-lib/src/lib.rs +++ b/lychee-lib/src/lib.rs @@ -85,8 +85,8 @@ use openssl_sys as _; // required for vendored-openssl feature #[doc(inline)] pub use crate::{ basic_auth::BasicAuthExtractor, - // Expose the `Chainable` trait to allow defining external handlers (plugins) - chain::{ChainResult, Chainable}, + // Expose the `Handler` trait to allow defining external handlers (plugins) + chain::{ChainResult, Handler}, // Constants get exposed so that the CLI can use the same defaults as the library client::{ check, Client, ClientBuilder, DEFAULT_MAX_REDIRECTS, DEFAULT_MAX_RETRIES, diff --git a/lychee-lib/src/quirks/mod.rs b/lychee-lib/src/quirks/mod.rs index 333b921310..69976fcc4c 100644 --- a/lychee-lib/src/quirks/mod.rs +++ b/lychee-lib/src/quirks/mod.rs @@ -1,5 +1,5 @@ use crate::{ - chain::{ChainResult, Chainable}, + chain::{ChainResult, Handler}, Status, }; use async_trait::async_trait; @@ -92,7 +92,7 @@ impl Quirks { } #[async_trait] -impl Chainable for Quirks { +impl Handler for Quirks { async fn chain(&mut self, input: Request) -> ChainResult { ChainResult::Next(self.apply(input)) } diff --git a/lychee-lib/src/types/basic_auth/credentials.rs b/lychee-lib/src/types/basic_auth/credentials.rs index 906357f671..74a4531617 100644 --- a/lychee-lib/src/types/basic_auth/credentials.rs +++ b/lychee-lib/src/types/basic_auth/credentials.rs @@ -8,7 +8,7 @@ use reqwest::Request; use serde::Deserialize; use thiserror::Error; -use crate::chain::{ChainResult, Chainable}; +use crate::chain::{ChainResult, Handler}; use crate::Status; #[derive(Copy, Clone, Debug, Error, PartialEq)] @@ -75,7 +75,7 @@ impl BasicAuthCredentials { } #[async_trait] -impl Chainable for Option { +impl Handler for Option { async fn chain(&mut self, mut request: Request) -> ChainResult { if let Some(credentials) = self { request From f2b1c29bd4e850b57dfa514d89cf4c3df9510dea Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Mon, 22 Apr 2024 13:57:50 +0200 Subject: [PATCH 27/40] Rename chain to handle --- lychee-lib/src/chain/mod.rs | 8 ++++---- lychee-lib/src/checker.rs | 2 +- lychee-lib/src/client.rs | 2 +- lychee-lib/src/quirks/mod.rs | 2 +- lychee-lib/src/types/basic_auth/credentials.rs | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 59ea75c864..bdf52a22b6 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -87,7 +87,7 @@ impl Chain { pub(crate) async fn traverse(&self, mut input: T) -> ChainResult { use ChainResult::{Done, Next}; for e in self.0.lock().await.iter_mut() { - match e.chain(input).await { + match e.handle(input).await { Next(r) => input = r, Done(r) => { return Done(r); @@ -131,7 +131,7 @@ pub trait Handler: Debug { /// /// #[async_trait] /// impl Handler for AddHeader { - /// async fn chain(&mut self, mut request: Request) -> ChainResult { + /// async fn handle(&mut self, mut request: Request) -> ChainResult { /// // You can modify the request however you like here /// request.headers_mut().append("X-Header", "value".parse().unwrap()); /// @@ -140,7 +140,7 @@ pub trait Handler: Debug { /// } /// } /// ``` - async fn chain(&mut self, input: T) -> ChainResult; + async fn handle(&mut self, input: T) -> ChainResult; } /// Client request chains @@ -195,7 +195,7 @@ mod test { #[async_trait] impl Handler for Add { - async fn chain(&mut self, req: Result) -> ChainResult { + async fn handle(&mut self, req: Result) -> ChainResult { let added = req.0 + self.0; if added > 100 { Done(Result(req.0)) diff --git a/lychee-lib/src/checker.rs b/lychee-lib/src/checker.rs index ba89c1ce74..428551ad2b 100644 --- a/lychee-lib/src/checker.rs +++ b/lychee-lib/src/checker.rs @@ -74,7 +74,7 @@ fn clone_unwrap(request: &Request) -> Request { #[async_trait] impl Handler for Checker { - async fn chain(&mut self, input: Request) -> ChainResult { + async fn handle(&mut self, input: Request) -> ChainResult { ChainResult::Done(self.retry_request(input).await) } } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 15f9397cfb..0bc1fd0f76 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -1087,7 +1087,7 @@ mod tests { #[async_trait] impl Handler for ExampleHandler { - async fn chain(&mut self, _: Request) -> ChainResult { + async fn handle(&mut self, _: Request) -> ChainResult { ChainResult::Done(Status::Excluded) } } diff --git a/lychee-lib/src/quirks/mod.rs b/lychee-lib/src/quirks/mod.rs index 69976fcc4c..39af65173c 100644 --- a/lychee-lib/src/quirks/mod.rs +++ b/lychee-lib/src/quirks/mod.rs @@ -93,7 +93,7 @@ impl Quirks { #[async_trait] impl Handler for Quirks { - async fn chain(&mut self, input: Request) -> ChainResult { + async fn handle(&mut self, input: Request) -> ChainResult { ChainResult::Next(self.apply(input)) } } diff --git a/lychee-lib/src/types/basic_auth/credentials.rs b/lychee-lib/src/types/basic_auth/credentials.rs index 74a4531617..1af0144c02 100644 --- a/lychee-lib/src/types/basic_auth/credentials.rs +++ b/lychee-lib/src/types/basic_auth/credentials.rs @@ -76,7 +76,7 @@ impl BasicAuthCredentials { #[async_trait] impl Handler for Option { - async fn chain(&mut self, mut request: Request) -> ChainResult { + async fn handle(&mut self, mut request: Request) -> ChainResult { if let Some(credentials) = self { request .headers_mut() From 8a44241a0612a27118f3871ea490744f26a31a69 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Apr 2024 12:57:43 +0000 Subject: [PATCH 28/40] Bump the dependencies group with 35 updates Bumps the dependencies group with 35 updates: | Package | From | To | | --- | --- | --- | | [anyhow](https://github.com/dtolnay/anyhow) | `1.0.79` | `1.0.82` | | [clap](https://github.com/clap-rs/clap) | `4.4.13` | `4.5.4` | | [console](https://github.com/console-rs/console) | `0.15.7` | `0.15.8` | | [env_logger](https://github.com/rust-cli/env_logger) | `0.10.1` | `0.11.1` | | [headers](https://github.com/hyperium/headers) | `0.3.9` | `0.4.0` | | [http](https://github.com/hyperium/http) | `0.2.11` | `1.0.0` | | [indicatif](https://github.com/console-rs/indicatif) | `0.17.7` | `0.17.8` | | [log](https://github.com/rust-lang/log) | `0.4.20` | `0.4.21` | | [openssl-sys](https://github.com/sfackler/rust-openssl) | `0.9.98` | `0.9.102` | | [regex](https://github.com/rust-lang/regex) | `1.10.2` | `1.10.4` | | [reqwest](https://github.com/seanmonstar/reqwest) | `0.11.23` | `0.12.4` | | [reqwest_cookie_store](https://github.com/pfernie/reqwest_cookie_store) | `0.6.0` | `0.7.0` | | [ring](https://github.com/briansmith/ring) | `0.17.7` | `0.17.8` | | [serde](https://github.com/serde-rs/serde) | `1.0.195` | `1.0.198` | | [serde_json](https://github.com/serde-rs/json) | `1.0.111` | `1.0.116` | | [strum](https://github.com/Peternator7/strum) | `0.25.0` | `0.26.2` | | [supports-color](https://github.com/zkat/supports-color) | `2.1.0` | `3.0.0` | | [tokio](https://github.com/tokio-rs/tokio) | `1.35.1` | `1.37.0` | | [tokio-stream](https://github.com/tokio-rs/tokio) | `0.1.14` | `0.1.15` | | [toml](https://github.com/toml-rs/toml) | `0.8.8` | `0.8.12` | | [assert_cmd](https://github.com/assert-rs/assert_cmd) | `2.0.12` | `2.0.14` | | [predicates](https://github.com/assert-rs/predicates-rs) | `3.0.4` | `3.1.0` | | [tempfile](https://github.com/Stebalien/tempfile) | `3.9.0` | `3.10.1` | | [uuid](https://github.com/uuid-rs/uuid) | `1.6.1` | `1.8.0` | | [wiremock](https://github.com/LukeMathWalker/wiremock-rs) | `0.5.22` | `0.6.0` | | [async-trait](https://github.com/dtolnay/async-trait) | `0.1.78` | `0.1.80` | | [cached](https://github.com/jaemk/cached) | `0.46.1` | `0.49.3` | | [html5ever](https://github.com/servo/html5ever) | `0.26.0` | `0.27.0` | | [hyper](https://github.com/hyperium/hyper) | `1.1.0` | `1.3.1` | | [octocrab](https://github.com/XAMPPRocky/octocrab) | `0.32.0` | `0.38.0` | | [pulldown-cmark](https://github.com/raphlinus/pulldown-cmark) | `0.9.3` | `0.10.2` | | [serde_with](https://github.com/jonasbb/serde_with) | `3.4.0` | `3.7.0` | | [thiserror](https://github.com/dtolnay/thiserror) | `1.0.56` | `1.0.59` | | [typed-builder](https://github.com/idanarye/rust-typed-builder) | `0.18.0` | `0.18.2` | | [rstest](https://github.com/la10736/rstest) | `0.18.2` | `0.19.0` | Updates `anyhow` from 1.0.79 to 1.0.82 - [Release notes](https://github.com/dtolnay/anyhow/releases) - [Commits](https://github.com/dtolnay/anyhow/compare/1.0.79...1.0.82) Updates `clap` from 4.4.13 to 4.5.4 - [Release notes](https://github.com/clap-rs/clap/releases) - [Changelog](https://github.com/clap-rs/clap/blob/master/CHANGELOG.md) - [Commits](https://github.com/clap-rs/clap/compare/v4.4.13...v4.5.4) Updates `console` from 0.15.7 to 0.15.8 - [Changelog](https://github.com/console-rs/console/blob/master/CHANGELOG.md) - [Commits](https://github.com/console-rs/console/compare/0.15.7...0.15.8) Updates `env_logger` from 0.10.1 to 0.11.1 - [Release notes](https://github.com/rust-cli/env_logger/releases) - [Changelog](https://github.com/rust-cli/env_logger/blob/main/CHANGELOG.md) - [Commits](https://github.com/rust-cli/env_logger/compare/v0.10.1...v0.11.1) Updates `headers` from 0.3.9 to 0.4.0 - [Commits](https://github.com/hyperium/headers/compare/headers-v0.3.9...headers-v0.4.0) Updates `http` from 0.2.11 to 1.0.0 - [Release notes](https://github.com/hyperium/http/releases) - [Changelog](https://github.com/hyperium/http/blob/master/CHANGELOG.md) - [Commits](https://github.com/hyperium/http/compare/v0.2.11...v1.0.0) Updates `indicatif` from 0.17.7 to 0.17.8 - [Release notes](https://github.com/console-rs/indicatif/releases) - [Commits](https://github.com/console-rs/indicatif/compare/0.17.7...0.17.8) Updates `log` from 0.4.20 to 0.4.21 - [Release notes](https://github.com/rust-lang/log/releases) - [Changelog](https://github.com/rust-lang/log/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/log/compare/0.4.20...0.4.21) Updates `openssl-sys` from 0.9.98 to 0.9.102 - [Release notes](https://github.com/sfackler/rust-openssl/releases) - [Commits](https://github.com/sfackler/rust-openssl/compare/openssl-sys-v0.9.98...openssl-sys-v0.9.102) Updates `regex` from 1.10.2 to 1.10.4 - [Release notes](https://github.com/rust-lang/regex/releases) - [Changelog](https://github.com/rust-lang/regex/blob/master/CHANGELOG.md) - [Commits](https://github.com/rust-lang/regex/compare/1.10.2...1.10.4) Updates `reqwest` from 0.11.23 to 0.12.4 - [Release notes](https://github.com/seanmonstar/reqwest/releases) - [Changelog](https://github.com/seanmonstar/reqwest/blob/master/CHANGELOG.md) - [Commits](https://github.com/seanmonstar/reqwest/compare/v0.11.23...v0.12.4) Updates `reqwest_cookie_store` from 0.6.0 to 0.7.0 - [Changelog](https://github.com/pfernie/reqwest_cookie_store/blob/main/CHANGELOG.md) - [Commits](https://github.com/pfernie/reqwest_cookie_store/compare/v0.6.0...v0.7.0) Updates `ring` from 0.17.7 to 0.17.8 - [Commits](https://github.com/briansmith/ring/commits) Updates `serde` from 1.0.195 to 1.0.198 - [Release notes](https://github.com/serde-rs/serde/releases) - [Commits](https://github.com/serde-rs/serde/compare/v1.0.195...v1.0.198) Updates `serde_json` from 1.0.111 to 1.0.116 - [Release notes](https://github.com/serde-rs/json/releases) - [Commits](https://github.com/serde-rs/json/compare/v1.0.111...v1.0.116) Updates `strum` from 0.25.0 to 0.26.2 - [Release notes](https://github.com/Peternator7/strum/releases) - [Changelog](https://github.com/Peternator7/strum/blob/master/CHANGELOG.md) - [Commits](https://github.com/Peternator7/strum/commits/v0.26.2) Updates `supports-color` from 2.1.0 to 3.0.0 - [Release notes](https://github.com/zkat/supports-color/releases) - [Changelog](https://github.com/zkat/supports-color/blob/main/CHANGELOG.md) - [Commits](https://github.com/zkat/supports-color/compare/v2.1.0...v3.0.0) Updates `tokio` from 1.35.1 to 1.37.0 - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-1.35.1...tokio-1.37.0) Updates `tokio-stream` from 0.1.14 to 0.1.15 - [Release notes](https://github.com/tokio-rs/tokio/releases) - [Commits](https://github.com/tokio-rs/tokio/compare/tokio-stream-0.1.14...tokio-stream-0.1.15) Updates `toml` from 0.8.8 to 0.8.12 - [Commits](https://github.com/toml-rs/toml/compare/toml-v0.8.8...toml-v0.8.12) Updates `assert_cmd` from 2.0.12 to 2.0.14 - [Changelog](https://github.com/assert-rs/assert_cmd/blob/master/CHANGELOG.md) - [Commits](https://github.com/assert-rs/assert_cmd/compare/v2.0.12...v2.0.14) Updates `predicates` from 3.0.4 to 3.1.0 - [Changelog](https://github.com/assert-rs/predicates-rs/blob/master/CHANGELOG.md) - [Commits](https://github.com/assert-rs/predicates-rs/compare/v3.0.4...v3.1.0) Updates `tempfile` from 3.9.0 to 3.10.1 - [Changelog](https://github.com/Stebalien/tempfile/blob/master/CHANGELOG.md) - [Commits](https://github.com/Stebalien/tempfile/compare/v3.9.0...v3.10.1) Updates `uuid` from 1.6.1 to 1.8.0 - [Release notes](https://github.com/uuid-rs/uuid/releases) - [Commits](https://github.com/uuid-rs/uuid/compare/1.6.1...1.8.0) Updates `wiremock` from 0.5.22 to 0.6.0 - [Changelog](https://github.com/LukeMathWalker/wiremock-rs/blob/main/CHANGELOG.md) - [Commits](https://github.com/LukeMathWalker/wiremock-rs/compare/v0.5.22...v0.6.0) Updates `async-trait` from 0.1.78 to 0.1.80 - [Release notes](https://github.com/dtolnay/async-trait/releases) - [Commits](https://github.com/dtolnay/async-trait/compare/0.1.78...0.1.80) Updates `cached` from 0.46.1 to 0.49.3 - [Changelog](https://github.com/jaemk/cached/blob/master/CHANGELOG.md) - [Commits](https://github.com/jaemk/cached/commits) Updates `html5ever` from 0.26.0 to 0.27.0 - [Commits](https://github.com/servo/html5ever/compare/html5ever-v0.26.0...v0.27.0) Updates `hyper` from 1.1.0 to 1.3.1 - [Release notes](https://github.com/hyperium/hyper/releases) - [Changelog](https://github.com/hyperium/hyper/blob/master/CHANGELOG.md) - [Commits](https://github.com/hyperium/hyper/compare/v1.1.0...v1.3.1) Updates `octocrab` from 0.32.0 to 0.38.0 - [Release notes](https://github.com/XAMPPRocky/octocrab/releases) - [Changelog](https://github.com/XAMPPRocky/octocrab/blob/main/CHANGELOG.md) - [Commits](https://github.com/XAMPPRocky/octocrab/compare/v0.32.0...v0.38.0) Updates `pulldown-cmark` from 0.9.3 to 0.10.2 - [Release notes](https://github.com/raphlinus/pulldown-cmark/releases) - [Commits](https://github.com/raphlinus/pulldown-cmark/compare/v0.9.3...v0.10.2) Updates `serde_with` from 3.4.0 to 3.7.0 - [Release notes](https://github.com/jonasbb/serde_with/releases) - [Commits](https://github.com/jonasbb/serde_with/compare/v3.4.0...v3.7.0) Updates `thiserror` from 1.0.56 to 1.0.59 - [Release notes](https://github.com/dtolnay/thiserror/releases) - [Commits](https://github.com/dtolnay/thiserror/compare/1.0.56...1.0.59) Updates `typed-builder` from 0.18.0 to 0.18.2 - [Changelog](https://github.com/idanarye/rust-typed-builder/blob/master/CHANGELOG.md) - [Commits](https://github.com/idanarye/rust-typed-builder/commits) Updates `rstest` from 0.18.2 to 0.19.0 - [Release notes](https://github.com/la10736/rstest/releases) - [Changelog](https://github.com/la10736/rstest/blob/master/CHANGELOG.md) - [Commits](https://github.com/la10736/rstest/compare/v0.18.2...v0.19.0) --- updated-dependencies: - dependency-name: anyhow dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: clap dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: console dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: env_logger dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: headers dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: http dependency-type: direct:production update-type: version-update:semver-major dependency-group: dependencies - dependency-name: indicatif dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: log dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: openssl-sys dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: regex dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: reqwest dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: reqwest_cookie_store dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: ring dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: serde dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: serde_json dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: strum dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: supports-color dependency-type: direct:production update-type: version-update:semver-major dependency-group: dependencies - dependency-name: tokio dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: tokio-stream dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: toml dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: assert_cmd dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: predicates dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: tempfile dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: uuid dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: wiremock dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: async-trait dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: cached dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: html5ever dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: hyper dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: octocrab dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: pulldown-cmark dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: serde_with dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies - dependency-name: thiserror dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: typed-builder dependency-type: direct:production update-type: version-update:semver-patch dependency-group: dependencies - dependency-name: rstest dependency-type: direct:production update-type: version-update:semver-minor dependency-group: dependencies ... Signed-off-by: dependabot[bot] --- Cargo.lock | 984 ++++++++++++++---------------- examples/builder/Cargo.toml | 8 +- examples/client_pool/Cargo.toml | 4 +- examples/collect_links/Cargo.toml | 10 +- examples/extract/Cargo.toml | 2 +- examples/simple/Cargo.toml | 2 +- lychee-bin/Cargo.toml | 50 +- lychee-lib/Cargo.toml | 50 +- 8 files changed, 509 insertions(+), 601 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1ab49f0e15..795e9c4b67 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,9 +67,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" dependencies = [ "anstyle", "anstyle-parse", @@ -115,9 +115,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "arc-swap" @@ -143,9 +143,9 @@ dependencies = [ [[package]] name = "assert_cmd" -version = "2.0.12" +version = "2.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" dependencies = [ "anstyle", "bstr", @@ -255,7 +255,7 @@ dependencies = [ "futures-lite 2.1.0", "parking", "polling 3.3.1", - "rustix 0.38.28", + "rustix 0.38.33", "slab", "tracing", "windows-sys 0.52.0", @@ -306,7 +306,7 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.28", + "rustix 0.38.33", "windows-sys 0.48.0", ] @@ -333,7 +333,7 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 0.38.28", + "rustix 0.38.33", "signal-hook-registry", "slab", "windows-sys 0.48.0", @@ -402,7 +402,7 @@ dependencies = [ "futures-util", "pin-utils", "socket2 0.4.10", - "trust-dns-resolver 0.21.2", + "trust-dns-resolver", ] [[package]] @@ -435,9 +435,9 @@ checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" [[package]] name = "async-trait" -version = "0.1.78" +version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "461abc97219de0eaaf81fe3ef974a540158f3d079c2ab200f891f1a2ef201e85" +checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", @@ -483,6 +483,12 @@ version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + [[package]] name = "benches" version = "0.0.0" @@ -535,7 +541,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" dependencies = [ "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.6", "serde", ] @@ -549,10 +555,10 @@ checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" name = "builder" version = "0.1.0" dependencies = [ - "http 0.2.11", + "http 1.0.0", "lychee-lib", "regex", - "reqwest", + "reqwest 0.12.4", "tokio", ] @@ -582,9 +588,9 @@ checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cached" -version = "0.46.1" +version = "0.49.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7c8c50262271cdf5abc979a5f76515c234e764fa025d1ba4862c0f0bcda0e95" +checksum = "8e8e463fceca5674287f32d252fb1d94083758b8709c160efae66d263e5f4eba" dependencies = [ "ahash", "cached_proc_macro", @@ -597,9 +603,9 @@ dependencies = [ [[package]] name = "cached_proc_macro" -version = "0.18.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c878c71c2821aa2058722038a59a67583a4240524687c6028571c9b395ded61f" +checksum = "ad9f16c0d84de31a2ab7fdf5f7783c14631f7075cf464eb3bb43119f61c9cb2a" dependencies = [ "darling 0.14.4", "proc-macro2", @@ -609,9 +615,9 @@ dependencies = [ [[package]] name = "cached_proc_macro_types" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a4f925191b4367301851c6d99b09890311d74b0d43f274c0b34c86d308a3663" +checksum = "ade8366b8bd5ba243f0a58f036cc0ca8a2f069cff1a2351ef1cac6b083e16fc0" [[package]] name = "cast" @@ -651,12 +657,12 @@ dependencies = [ "mailchecker", "md5", "pwned", - "rand 0.8.5", + "rand", "regex", - "reqwest", + "reqwest 0.11.27", "serde", "serde_json", - "trust-dns-proto 0.21.2", + "trust-dns-proto", ] [[package]] @@ -701,9 +707,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.4.13" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52bdc885e4cacc7f7c9eedc1ef6da641603180c783c41a15c264944deeaab642" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" dependencies = [ "clap_builder", "clap_derive", @@ -711,23 +717,23 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.12" +version = "4.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.10.0", + "strsim 0.11.1", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "syn 2.0.48", @@ -735,9 +741,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "client_pool" @@ -753,10 +759,10 @@ dependencies = [ name = "collect_links" version = "0.1.0" dependencies = [ - "http 0.2.11", + "http 1.0.0", "lychee-lib", "regex", - "reqwest", + "reqwest 0.12.4", "tokio", "tokio-stream", ] @@ -778,15 +784,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.7" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c926e00cc70edefdc64d3a5ff31cc65bb97a3460097762bd23afb4d8145fccf8" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" dependencies = [ "encode_unicode", "lazy_static", "libc", "unicode-width", - "windows-sys 0.45.0", + "windows-sys 0.52.0", ] [[package]] @@ -809,17 +815,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "percent-encoding", - "time", - "version_check", -] - [[package]] name = "cookie" version = "0.17.0" @@ -831,30 +826,13 @@ dependencies = [ "version_check", ] -[[package]] -name = "cookie_store" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d606d0fba62e13cf04db20536c05cb7f13673c161cb47a47a82b9b9e7d3f1daa" -dependencies = [ - "cookie 0.16.2", - "idna 0.2.3", - "log", - "publicsuffix", - "serde", - "serde_derive", - "serde_json", - "time", - "url", -] - [[package]] name = "cookie_store" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "387461abbc748185c3a6e1673d826918b450b87ff22639429c694619a83b6cf6" dependencies = [ - "cookie 0.17.0", + "cookie", "idna 0.3.0", "log", "publicsuffix", @@ -911,7 +889,7 @@ dependencies = [ "clap", "criterion-plot", "is-terminal", - "itertools 0.10.5", + "itertools", "num-traits", "once_cell", "oorandom", @@ -932,7 +910,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", - "itertools 0.10.5", + "itertools", ] [[package]] @@ -1158,14 +1136,13 @@ checksum = "7e962a19be5cfc3f3bf6dd8f61eb50107f356ad6270fbb3ed41476571db78be5" [[package]] name = "deadpool" -version = "0.9.5" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "421fe0f90f2ab22016f32a9881be5134fdd71c65298917084b0c7477cbc3856e" +checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" dependencies = [ "async-trait", "deadpool-runtime", "num_cpus", - "retain_mut", "tokio", ] @@ -1306,35 +1283,33 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 1.0.109", ] [[package]] -name = "enum-as-inner" -version = "0.6.0" +name = "env_filter" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ffccbb6966c05b32ef8fbac435df276c4ae4d3dc55a8cd0eb9745e6c12f546a" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.48", + "log", + "regex", ] [[package]] name = "env_logger" -version = "0.10.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" dependencies = [ + "anstream", + "anstyle", + "env_filter", "humantime", - "is-terminal", "log", - "regex", - "termcolor", ] [[package]] @@ -1664,17 +1639,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.11" @@ -1684,7 +1648,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "wasm-bindgen", ] @@ -1723,7 +1687,26 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 0.2.11", + "http 0.2.12", + "indexmap 2.1.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", "indexmap 2.1.0", "slab", "tokio", @@ -1755,14 +1738,14 @@ dependencies = [ [[package]] name = "headers" -version = "0.3.9" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06683b93020a07e3dbcf5f8c0f6d40080d725bea7936fc01ad345c01b97dc270" +checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ "base64 0.21.5", "bytes", "headers-core", - "http 0.2.11", + "http 1.0.0", "httpdate", "mime", "sha1 0.10.6", @@ -1770,11 +1753,11 @@ dependencies = [ [[package]] name = "headers-core" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 0.2.11", + "http 1.0.0", ] [[package]] @@ -1783,6 +1766,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.3" @@ -1808,16 +1797,16 @@ dependencies = [ [[package]] name = "html5ever" -version = "0.26.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7" +checksum = "c13771afe0e6e846f1e67d038d4cb29998a6779f93c809212e4e9c32efd244d4" dependencies = [ "log", "mac", "markup5ever", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -1831,9 +1820,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ "bytes", "fnv", @@ -1858,7 +1847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http 0.2.11", + "http 0.2.12", "pin-project-lite", ] @@ -1873,30 +1862,16 @@ dependencies = [ ] [[package]] -name = "http-range-header" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" - -[[package]] -name = "http-types" -version = "2.12.0" +name = "http-body-util" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" +checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ - "anyhow", - "async-channel 1.9.0", - "base64 0.13.1", - "futures-lite 1.13.0", - "http 0.2.11", - "infer", + "bytes", + "futures-core", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", - "rand 0.7.3", - "serde", - "serde_json", - "serde_qs", - "serde_urlencoded", - "url", ] [[package]] @@ -1937,8 +1912,8 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", - "http 0.2.11", + "h2 0.3.22", + "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", @@ -1953,43 +1928,55 @@ dependencies = [ [[package]] name = "hyper" -version = "1.1.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", + "futures-channel", + "futures-util", + "h2 0.4.4", "http 1.0.0", "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", "pin-project-lite", + "smallvec", "tokio", + "want", ] [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 0.2.11", - "hyper 0.14.28", + "http 1.0.0", + "hyper 1.3.1", + "hyper-util", "log", "rustls", "rustls-native-certs", + "rustls-pki-types", "tokio", "tokio-rustls", + "tower-service", ] [[package]] name = "hyper-timeout" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" +checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 0.14.28", + "hyper 1.3.1", + "hyper-util", "pin-project-lite", "tokio", - "tokio-io-timeout", + "tower-service", ] [[package]] @@ -2005,6 +1992,42 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.3.1", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.3.1", + "pin-project-lite", + "socket2 0.5.5", + "tokio", + "tower", + "tower-service", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.59" @@ -2055,16 +2078,6 @@ dependencies = [ "unicode-normalization", ] -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "0.5.0" @@ -2099,9 +2112,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb28741c9db9a713d93deb3bb9515c20788cef5815265bee4980e87bde7e0f25" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" dependencies = [ "console", "instant", @@ -2110,12 +2123,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "infer" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" - [[package]] name = "instant" version = "0.1.12" @@ -2151,7 +2158,7 @@ dependencies = [ "socket2 0.5.5", "widestring", "windows-sys 0.48.0", - "winreg", + "winreg 0.50.0", ] [[package]] @@ -2177,15 +2184,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" dependencies = [ "hermit-abi", - "rustix 0.38.28", + "rustix 0.38.33", "windows-sys 0.52.0", ] [[package]] name = "is_ci" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb" +checksum = "7655c9839580ee829dfacba1d1278c2b7883e50a277ff7541299489d6bdfdc45" [[package]] name = "itertools" @@ -2196,15 +2203,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.10" @@ -2274,9 +2272,9 @@ checksum = "db13adb97ab515a3691f56e4dbab09283d0b86cb45abd991d8634a9d6f501760" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libredox" @@ -2328,9 +2326,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" dependencies = [ "value-bag", ] @@ -2359,7 +2357,7 @@ dependencies = [ "env_logger", "futures", "headers", - "http 0.2.11", + "http 1.0.0", "humantime", "humantime-serde", "indicatif", @@ -2371,7 +2369,7 @@ dependencies = [ "predicates", "pretty_assertions", "regex", - "reqwest", + "reqwest 0.12.4", "reqwest_cookie_store", "ring", "secrecy", @@ -2404,8 +2402,8 @@ dependencies = [ "headers", "html5ever", "html5gum", - "http 0.2.11", - "hyper 1.1.0", + "http 1.0.0", + "hyper 1.3.1", "ip_network", "jwalk", "linkify", @@ -2418,7 +2416,7 @@ dependencies = [ "percent-encoding", "pulldown-cmark", "regex", - "reqwest", + "reqwest 0.12.4", "reqwest_cookie_store", "ring", "rstest", @@ -2454,9 +2452,9 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016" +checksum = "c7940b09815a02810a42b9e1bc41c069880a87de68e9b1dcbe754a3ba3b47c20" dependencies = [ "log", "phf", @@ -2527,7 +2525,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] @@ -2537,7 +2535,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" dependencies = [ - "getrandom 0.2.11", + "getrandom", ] [[package]] @@ -2637,24 +2635,26 @@ dependencies = [ [[package]] name = "octocrab" -version = "0.32.0" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abfeeafb5fa0da7046229ec3c7b3bd2981aae05c549871192c408d59fc0fffd5" +checksum = "68a8a3df00728324ad654ecd1ed449a60157c55b7ff8c109af3a35989687c367" dependencies = [ "arc-swap", "async-trait", - "base64 0.21.5", + "base64 0.22.0", "bytes", "cfg-if", "chrono", "either", "futures", "futures-util", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", "hyper-rustls", "hyper-timeout", + "hyper-util", "jsonwebtoken", "once_cell", "percent-encoding", @@ -2727,9 +2727,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" dependencies = [ "cc", "libc", @@ -2836,21 +2836,21 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "phf" -version = "0.10.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259" +checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" dependencies = [ - "phf_shared", + "phf_shared 0.11.2", ] [[package]] name = "phf_codegen" -version = "0.10.0" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" +checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.2", + "phf_shared 0.11.2", ] [[package]] @@ -2859,8 +2859,18 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ - "phf_shared", - "rand 0.8.5", + "phf_shared 0.10.0", + "rand", +] + +[[package]] +name = "phf_generator" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" +dependencies = [ + "phf_shared 0.11.2", + "rand", ] [[package]] @@ -2872,6 +2882,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.3" @@ -2974,7 +2993,7 @@ dependencies = [ "cfg-if", "concurrent-queue", "pin-project-lite", - "rustix 0.38.28", + "rustix 0.38.33", "tracing", "windows-sys 0.52.0", ] @@ -3005,14 +3024,13 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "predicates" -version = "3.0.4" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" +checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" dependencies = [ "anstyle", "difflib", "float-cmp", - "itertools 0.11.0", "normalize-line-endings", "predicates-core", "regex", @@ -3095,16 +3113,23 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.9.3" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.1", "getopts", "memchr", + "pulldown-cmark-escape", "unicase", ] +[[package]] +name = "pulldown-cmark-escape" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" + [[package]] name = "pwned" version = "0.5.0" @@ -3112,7 +3137,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f75258f75c681eb691607acdb325d0150907bc68826365b07476834df2664974" dependencies = [ "derive_builder", - "reqwest", + "reqwest 0.11.27", "serde", "serde_derive", "serde_json", @@ -3135,19 +3160,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] - [[package]] name = "rand" version = "0.8.5" @@ -3155,18 +3167,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", + "rand_chacha", + "rand_core", ] [[package]] @@ -3176,16 +3178,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core 0.6.4", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom 0.1.16", + "rand_core", ] [[package]] @@ -3194,16 +3187,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.11", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core 0.5.1", + "getrandom", ] [[package]] @@ -3241,20 +3225,20 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ - "getrandom 0.2.11", + "getrandom", "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.10.2" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.3", + "regex-automata 0.4.6", "regex-syntax 0.8.2", ] @@ -3269,9 +3253,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.3" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -3298,24 +3282,65 @@ checksum = "e898588f33fdd5b9420719948f9f2a32c922a246964576f71ba7f24f80610fbc" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "async-compression", "base64 0.21.5", "bytes", - "cookie 0.16.2", - "cookie_store 0.16.2", "encoding_rs", "futures-core", "futures-util", - "h2", - "http 0.2.11", + "h2 0.3.22", + "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", + "hyper-tls 0.5.0", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 1.0.4", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-socks", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg 0.50.0", +] + +[[package]] +name = "reqwest" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +dependencies = [ + "async-compression", + "base64 0.22.0", + "bytes", + "cookie", + "cookie_store", + "futures-core", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.3.1", "hyper-rustls", - "hyper-tls", + "hyper-tls 0.6.0", + "hyper-util", "ipnet", "js-sys", "log", @@ -3326,34 +3351,33 @@ dependencies = [ "pin-project-lite", "rustls", "rustls-native-certs", - "rustls-pemfile", + "rustls-pemfile 2.1.2", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "system-configuration", + "sync_wrapper", "tokio", "tokio-native-tls", "tokio-rustls", - "tokio-socks", "tokio-util", "tower-service", - "trust-dns-resolver 0.23.2", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "winreg 0.52.0", ] [[package]] name = "reqwest_cookie_store" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba529055ea150e42e4eb9c11dcd380a41025ad4d594b0cb4904ef28b037e1061" +checksum = "93ea5c6f30c19d766efe8d823c88f9abd1c56516648a0d4264ab2dc04cc19472" dependencies = [ "bytes", - "cookie_store 0.20.0", - "reqwest", + "cookie_store", + "reqwest 0.12.4", "url", ] @@ -3367,31 +3391,26 @@ dependencies = [ "quick-error", ] -[[package]] -name = "retain_mut" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" - [[package]] name = "ring" -version = "0.17.7" +version = "0.17.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", - "getrandom 0.2.11", + "cfg-if", + "getrandom", "libc", "spin", "untrusted", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "rstest" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97eeab2f3c0a199bc4be135c36c924b6590b88c377d416494288c14f2db30199" +checksum = "9d5316d2a1479eeef1ea21e7f9ddc67c191d497abc8fc3ba2467857abbb68330" dependencies = [ "futures", "futures-timer", @@ -3401,9 +3420,9 @@ dependencies = [ [[package]] name = "rstest_macros" -version = "0.18.2" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" +checksum = "04a9df72cc1f67020b0d63ad9bfe4a323e459ea7eb68e03bd9824db49f9a4c25" dependencies = [ "cfg-if", "glob", @@ -3447,9 +3466,9 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" dependencies = [ "bitflags 2.4.1", "errno", @@ -3460,24 +3479,27 @@ dependencies = [ [[package]] name = "rustls" -version = "0.21.10" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" dependencies = [ "log", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] name = "rustls-native-certs" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" dependencies = [ "openssl-probe", - "rustls-pemfile", + "rustls-pemfile 2.1.2", + "rustls-pki-types", "schannel", "security-framework", ] @@ -3491,13 +3513,30 @@ dependencies = [ "base64 0.21.5", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64 0.22.0", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" + [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] @@ -3537,16 +3576,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "secrecy" version = "0.8.0" @@ -3588,18 +3617,18 @@ checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" [[package]] name = "serde" -version = "1.0.195" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", @@ -3608,9 +3637,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", @@ -3627,17 +3656,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serde_qs" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" -dependencies = [ - "percent-encoding", - "serde", - "thiserror", -] - [[package]] name = "serde_spanned" version = "0.6.5" @@ -3661,9 +3679,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64cd236ccc1b7a29e7e2739f27c0b2dd199804abc4290e32f59f3b68d6405c23" +checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" dependencies = [ "base64 0.21.5", "chrono", @@ -3671,6 +3689,7 @@ dependencies = [ "indexmap 1.9.3", "indexmap 2.1.0", "serde", + "serde_derive", "serde_json", "serde_with_macros", "time", @@ -3678,9 +3697,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.4.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93634eb5f75a2323b16de4748022ac4297f9e76b6dced2be287a099f41b5e788" +checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" dependencies = [ "darling 0.20.3", "proc-macro2", @@ -3778,31 +3797,29 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "snafu" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4de37ad025c587a29e8f3f5605c00f70b98715ef90b9061a815b9e59e9042d6" +checksum = "75976f4748ab44f6e5332102be424e7c2dc18daeaf7e725f2040c3ebb133512e" dependencies = [ - "backtrace", - "doc-comment", "snafu-derive", ] [[package]] name = "snafu-derive" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990079665f075b699031e9c08fd3ab99be5029b96f3b78dc0709e8f77e4efebf" +checksum = "b4b19911debfb8c2fb1107bc6cb2d61868aaf53a988449213959bb1b5b1ed95f" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -3843,7 +3860,7 @@ dependencies = [ "new_debug_unreachable", "once_cell", "parking_lot", - "phf_shared", + "phf_shared 0.10.0", "precomputed-hash", "serde", ] @@ -3854,8 +3871,8 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.10.0", + "phf_shared 0.10.0", "proc-macro2", "quote", ] @@ -3872,35 +3889,46 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + [[package]] name = "strum" -version = "0.25.0" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" +checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "rustversion", "syn 2.0.48", ] +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + [[package]] name = "supports-color" -version = "2.1.0" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89" +checksum = "9829b314621dfc575df4e409e79f9d6a66a3bd707ab73f23cb4aa3a854ac854f" dependencies = [ - "is-terminal", "is_ci", ] @@ -3926,6 +3954,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -3964,7 +3998,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c138f99377e5d653a371cdad263615634cfc8467685dfe8e73e2b8e98f44b17" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro-error", "proc-macro2", "quote", @@ -3973,14 +4007,13 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", "fastrand 2.0.1", - "redox_syscall", - "rustix 0.38.28", + "rustix 0.38.33", "windows-sys 0.52.0", ] @@ -3995,15 +4028,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "termcolor" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] - [[package]] name = "termtree" version = "0.4.1" @@ -4012,18 +4036,18 @@ checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", @@ -4096,9 +4120,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" dependencies = [ "backtrace", "bytes", @@ -4113,16 +4137,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.2.0" @@ -4146,11 +4160,12 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" dependencies = [ "rustls", + "rustls-pki-types", "tokio", ] @@ -4168,9 +4183,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" dependencies = [ "futures-core", "pin-project-lite", @@ -4193,9 +4208,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.8" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" dependencies = [ "serde", "serde_spanned", @@ -4214,9 +4229,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.21.0" +version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ "indexmap 2.1.0", "serde", @@ -4244,17 +4259,16 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.4.4" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ "bitflags 2.4.1", "bytes", - "futures-core", "futures-util", - "http 0.2.11", - "http-body 0.4.6", - "http-range-header", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", "iri-string", "pin-project-lite", "tower", @@ -4331,7 +4345,7 @@ dependencies = [ "async-trait", "cfg-if", "data-encoding", - "enum-as-inner 0.4.0", + "enum-as-inner", "futures-channel", "futures-io", "futures-util", @@ -4339,7 +4353,7 @@ dependencies = [ "ipnet", "lazy_static", "log", - "rand 0.8.5", + "rand", "smallvec", "thiserror", "tinyvec", @@ -4347,31 +4361,6 @@ dependencies = [ "url", ] -[[package]] -name = "trust-dns-proto" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner 0.6.0", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.4.0", - "ipnet", - "once_cell", - "rand 0.8.5", - "smallvec", - "thiserror", - "tinyvec", - "tokio", - "tracing", - "url", -] - [[package]] name = "trust-dns-resolver" version = "0.21.2" @@ -4388,28 +4377,7 @@ dependencies = [ "resolv-conf", "smallvec", "thiserror", - "trust-dns-proto 0.21.2", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand 0.8.5", - "resolv-conf", - "smallvec", - "thiserror", - "tokio", - "tracing", - "trust-dns-proto 0.23.2", + "trust-dns-proto", ] [[package]] @@ -4420,18 +4388,18 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "typed-builder" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e47c0496149861b7c95198088cbf36645016b1a0734cf350c50e2a38e070f38a" +checksum = "77739c880e00693faef3d65ea3aad725f196da38b22fdc7ea6ded6e1ce4d3add" dependencies = [ "typed-builder-macro", ] [[package]] name = "typed-builder-macro" -version = "0.18.0" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "982ee4197351b5c9782847ef5ec1fdcaf50503fb19d68f9771adae314e72b492" +checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", @@ -4518,18 +4486,18 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.6.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" dependencies = [ - "getrandom 0.2.11", + "getrandom", ] [[package]] name = "value-bag" -version = "1.4.3" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62ce5bb364b23e66b528d03168df78b38c0f7b6fe17386928f29d5ab2e7cb2f7" +checksum = "74797339c3b98616c009c7c3eb53a0ce41e85c8ec66bd3db96ed132d20cfdee8" [[package]] name = "vcpkg" @@ -4577,12 +4545,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -4711,15 +4673,6 @@ dependencies = [ "windows-targets 0.52.0", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -4738,21 +4691,6 @@ dependencies = [ "windows-targets 0.52.0", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -4783,12 +4721,6 @@ dependencies = [ "windows_x86_64_msvc 0.52.0", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -4801,12 +4733,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -4819,12 +4745,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -4837,12 +4757,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -4855,12 +4769,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -4873,12 +4781,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -4891,12 +4793,6 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -4911,9 +4807,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winnow" -version = "0.5.32" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8434aeec7b290e8da5c3f0d628cb0eac6cabcb31d14bb74f779a08109a5914d6" +checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" dependencies = [ "memchr", ] @@ -4928,26 +4824,38 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" +dependencies = [ + "cfg-if", + "windows-sys 0.48.0", +] + [[package]] name = "wiremock" -version = "0.5.22" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a3a53eaf34f390dd30d7b1b078287dd05df2aa2e21a589ccb80f5c7253c2e9" +checksum = "ec874e1eef0df2dcac546057fe5e29186f09c378181cd7b635b4b7bcc98e9d81" dependencies = [ "assert-json-diff", "async-trait", "base64 0.21.5", "deadpool", "futures", - "futures-timer", - "http-types", - "hyper 0.14.28", + "http 1.0.0", + "http-body-util", + "hyper 1.3.1", + "hyper-util", "log", "once_cell", "regex", "serde", "serde_json", "tokio", + "url", ] [[package]] diff --git a/examples/builder/Cargo.toml b/examples/builder/Cargo.toml index d2eba27bc2..a10a1b6367 100644 --- a/examples/builder/Cargo.toml +++ b/examples/builder/Cargo.toml @@ -9,10 +9,10 @@ path = "builder.rs" [dependencies] lychee-lib = { path = "../../lychee-lib", default-features = false } -tokio = { version = "1.35.1", features = ["full"] } -regex = "1.10.2" -http = "0.2.10" -reqwest = { version = "0.11.23", default-features = false, features = ["gzip"] } +tokio = { version = "1.37.0", features = ["full"] } +regex = "1.10.4" +http = "1.0.0" +reqwest = { version = "0.12.4", default-features = false, features = ["gzip"] } [features] email-check = ["lychee-lib/email-check"] diff --git a/examples/client_pool/Cargo.toml b/examples/client_pool/Cargo.toml index 9acf75cff2..47d73c4e5d 100644 --- a/examples/client_pool/Cargo.toml +++ b/examples/client_pool/Cargo.toml @@ -9,9 +9,9 @@ path = "client_pool.rs" [dependencies] futures = "0.3.30" -tokio-stream = "0.1.14" +tokio-stream = "0.1.15" lychee-lib = { path = "../../lychee-lib", default-features = false } -tokio = { version = "1.35.1", features = ["full"] } +tokio = { version = "1.37.0", features = ["full"] } [features] email-check = ["lychee-lib/email-check"] diff --git a/examples/collect_links/Cargo.toml b/examples/collect_links/Cargo.toml index 9be0888971..74aa700f22 100644 --- a/examples/collect_links/Cargo.toml +++ b/examples/collect_links/Cargo.toml @@ -9,11 +9,11 @@ path = "collect_links.rs" [dependencies] lychee-lib = { path = "../../lychee-lib", default-features = false } -tokio = { version = "1.35.1", features = ["full"] } -regex = "1.10.2" -http = "0.2.10" -tokio-stream = "0.1.14" -reqwest = { version = "0.11.23", default-features = false, features = ["gzip"] } +tokio = { version = "1.37.0", features = ["full"] } +regex = "1.10.4" +http = "1.0.0" +tokio-stream = "0.1.15" +reqwest = { version = "0.12.4", default-features = false, features = ["gzip"] } [features] email-check = ["lychee-lib/email-check"] diff --git a/examples/extract/Cargo.toml b/examples/extract/Cargo.toml index 3b404df6a6..160a86726c 100644 --- a/examples/extract/Cargo.toml +++ b/examples/extract/Cargo.toml @@ -9,7 +9,7 @@ path = "extract.rs" [dependencies] lychee-lib = { path = "../../lychee-lib", default-features = false } -tokio = { version = "1.35.1", features = ["full"] } +tokio = { version = "1.37.0", features = ["full"] } [features] email-check = ["lychee-lib/email-check"] diff --git a/examples/simple/Cargo.toml b/examples/simple/Cargo.toml index 8915c213f4..80dc93015f 100644 --- a/examples/simple/Cargo.toml +++ b/examples/simple/Cargo.toml @@ -9,7 +9,7 @@ path = "simple.rs" [dependencies] lychee-lib = { path = "../../lychee-lib", default-features = false } -tokio = { version = "1.35.1", features = ["full"] } +tokio = { version = "1.37.0", features = ["full"] } [features] email-check = ["lychee-lib/email-check"] diff --git a/lychee-bin/Cargo.toml b/lychee-bin/Cargo.toml index f32048acfc..8fec633f5c 100644 --- a/lychee-bin/Cargo.toml +++ b/lychee-bin/Cargo.toml @@ -16,57 +16,57 @@ version.workspace = true # requires all dependencies to have a version number. lychee-lib = { path = "../lychee-lib", version = "0.14.3", default-features = false } -anyhow = "1.0.78" +anyhow = "1.0.82" assert-json-diff = "2.0.2" -clap = { version = "4.4.12", features = ["env", "derive"] } -console = "0.15.7" +clap = { version = "4.5.4", features = ["env", "derive"] } +console = "0.15.8" const_format = "0.2.32" csv = "1.3.0" dashmap = { version = "5.5.3", features = ["serde"] } -env_logger = "0.10.1" +env_logger = "0.11.1" futures = "0.3.30" -headers = "0.3.8" -http = "0.2.10" +headers = "0.4.0" +http = "1.0.0" humantime = "2.1.0" humantime-serde = "1.1.1" -indicatif = "0.17.7" -log = "0.4.20" +indicatif = "0.17.8" +log = "0.4.21" once_cell = "1.19.0" -openssl-sys = { version = "0.9.98", optional = true } +openssl-sys = { version = "0.9.102", optional = true } pad = "0.1.6" -regex = "1.10.2" -reqwest = { version = "0.11.23", default-features = false, features = [ +regex = "1.10.4" +reqwest = { version = "0.12.4", default-features = false, features = [ "gzip", "json", ] } -reqwest_cookie_store = "0.6.0" +reqwest_cookie_store = "0.7.0" # Make build work on Apple Silicon. # See https://github.com/briansmith/ring/issues/1163 # This is necessary for the homebrew build # https://github.com/Homebrew/homebrew-core/pull/70216 -ring = "0.17.7" +ring = "0.17.8" secrecy = { version = "0.8.0", features = ["serde"] } -serde = { version = "1.0.193", features = ["derive"] } -serde_json = "1.0.109" -strum = { version = "0.25.0", features = ["derive"] } -supports-color = "2.1.0" +serde = { version = "1.0.198", features = ["derive"] } +serde_json = "1.0.116" +strum = { version = "0.26.2", features = ["derive"] } +supports-color = "3.0.0" tabled = "0.15.0" -tokio = { version = "1.35.1", features = ["full"] } -tokio-stream = "0.1.14" -toml = "0.8.8" +tokio = { version = "1.37.0", features = ["full"] } +tokio-stream = "0.1.15" +toml = "0.8.12" [dev-dependencies] -assert_cmd = "2.0.12" -predicates = "3.0.4" +assert_cmd = "2.0.14" +predicates = "3.1.0" pretty_assertions = "1.4.0" -tempfile = "3.9.0" +tempfile = "3.10.1" tracing-subscriber = { version = "0.3.18", default-features = false, features = [ "fmt", "registry", "env-filter", ] } -uuid = { version = "1.6.1", features = ["v4"] } -wiremock = "0.5.22" +uuid = { version = "1.8.0", features = ["v4"] } +wiremock = "0.6.0" # console-subscriber is not yet published to crates.io # Users have to uncomment this section and the feature below and build lychee diff --git a/lychee-lib/Cargo.toml b/lychee-lib/Cargo.toml index 0007424e75..7fb539db25 100644 --- a/lychee-lib/Cargo.toml +++ b/lychee-lib/Cargo.toml @@ -13,49 +13,49 @@ version.workspace = true [dependencies] async-stream = "0.3.5" -async-trait = "0.1.78" -cached = "0.46.1" +async-trait = "0.1.80" +cached = "0.49.3" check-if-email-exists = { version = "0.9.1", optional = true } email_address = "0.2.4" futures = "0.3.30" glob = "0.3.1" -headers = "0.3.8" -html5ever = "0.26.0" +headers = "0.4.0" +html5ever = "0.27.0" html5gum = "0.5.7" -http = "0.2.10" -hyper = "1.1.0" +http = "1.0.0" +hyper = "1.3.1" ip_network = "0.4.1" jwalk = "0.8.1" linkify = "0.10.0" -log = "0.4.20" -octocrab = "0.32.0" +log = "0.4.21" +octocrab = "0.38.0" once_cell = "1.19.0" -openssl-sys = { version = "0.9.98", optional = true } +openssl-sys = { version = "0.9.102", optional = true } path-clean = "1.0.1" percent-encoding = "2.3.1" -pulldown-cmark = "0.9.3" -regex = "1.10.2" +pulldown-cmark = "0.10.2" +regex = "1.10.4" # Use trust-dns to avoid lookup failures on high concurrency # https://github.com/seanmonstar/reqwest/issues/296 -reqwest = { version = "0.11.23", default-features = false, features = [ +reqwest = { version = "0.12.4", default-features = false, features = [ "gzip", "trust-dns", "cookies", ] } -reqwest_cookie_store = "0.6.0" +reqwest_cookie_store = "0.7.0" # Make build work on Apple Silicon. # See https://github.com/briansmith/ring/issues/1163 # This is necessary for the homebrew build # https://github.com/Homebrew/homebrew-core/pull/70216 -ring = "0.17.7" +ring = "0.17.8" secrecy = "0.8.0" -serde = { version = "1.0.193", features = ["derive"] } -serde_with = "3.4.0" +serde = { version = "1.0.198", features = ["derive"] } +serde_with = "3.7.0" shellexpand = "3.1.0" -thiserror = "1.0.53" -tokio = { version = "1.35.1", features = ["full"] } -toml = "0.8.8" -typed-builder = "0.18.0" +thiserror = "1.0.59" +tokio = { version = "1.37.0", features = ["full"] } +toml = "0.8.12" +typed-builder = "0.18.2" url = { version = "2.5.0", features = ["serde"] } [dependencies.par-stream] @@ -64,11 +64,11 @@ features = ["runtime-tokio"] [dev-dependencies] doc-comment = "0.3.3" -tempfile = "3.9.0" -wiremock = "0.5.22" -serde_json = "1.0.109" -rstest = "0.18.2" -toml = "0.8.8" +tempfile = "3.10.1" +wiremock = "0.6.0" +serde_json = "1.0.116" +rstest = "0.19.0" +toml = "0.8.12" [features] From e0b4c739874a9df69dd9679030b067d0a8cc27a6 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Wed, 24 Apr 2024 11:35:56 +0200 Subject: [PATCH 29/40] Adapt to breaking changes & revert to pulldown-cmark 0.9 --- Cargo.lock | 11 ++--------- lychee-bin/src/archive/mod.rs | 4 ++-- lychee-lib/Cargo.toml | 2 +- lychee-lib/src/extract/html/html5ever.rs | 10 +++++----- 4 files changed, 10 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 795e9c4b67..9fd2b116dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3113,23 +3113,16 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.10.2" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0530d13d87d1f549b66a3e8d0c688952abe5994e204ed62615baaf25dc029c" +checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ "bitflags 2.4.1", "getopts", "memchr", - "pulldown-cmark-escape", "unicase", ] -[[package]] -name = "pulldown-cmark-escape" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5d8f9aa0e3cbcfaf8bf00300004ee3b72f74770f9cbac93f6928771f613276b" - [[package]] name = "pwned" version = "0.5.0" diff --git a/lychee-bin/src/archive/mod.rs b/lychee-bin/src/archive/mod.rs index 7246f8a82d..767b232530 100644 --- a/lychee-bin/src/archive/mod.rs +++ b/lychee-bin/src/archive/mod.rs @@ -1,7 +1,7 @@ use reqwest::{Error, Url}; use serde::{Deserialize, Serialize}; use std::fmt::Display; -use strum::{Display, EnumIter, EnumString, EnumVariantNames}; +use strum::{Display, EnumIter, EnumString, VariantNames}; use crate::color::{color, GREEN, PINK}; @@ -23,7 +23,7 @@ impl Display for Suggestion { } #[non_exhaustive] -#[derive(Debug, Deserialize, Default, Clone, Display, EnumIter, EnumString, EnumVariantNames)] +#[derive(Debug, Deserialize, Default, Clone, Display, EnumIter, EnumString, VariantNames)] pub(crate) enum Archive { #[serde(rename = "wayback")] #[strum(serialize = "wayback", ascii_case_insensitive)] diff --git a/lychee-lib/Cargo.toml b/lychee-lib/Cargo.toml index 7fb539db25..00ec685baa 100644 --- a/lychee-lib/Cargo.toml +++ b/lychee-lib/Cargo.toml @@ -33,7 +33,7 @@ once_cell = "1.19.0" openssl-sys = { version = "0.9.102", optional = true } path-clean = "1.0.1" percent-encoding = "2.3.1" -pulldown-cmark = "0.10.2" +pulldown-cmark = "0.9.6" regex = "1.10.4" # Use trust-dns to avoid lookup failures on high concurrency # https://github.com/seanmonstar/reqwest/issues/296 diff --git a/lychee-lib/src/extract/html/html5ever.rs b/lychee-lib/src/extract/html/html5ever.rs index 25ed226aa2..bceed2ceb1 100644 --- a/lychee-lib/src/extract/html/html5ever.rs +++ b/lychee-lib/src/extract/html/html5ever.rs @@ -166,7 +166,7 @@ impl LinkExtractor { /// Extract unparsed URL strings from an HTML string. pub(crate) fn extract_html(buf: &str, include_verbatim: bool) -> Vec { - let mut input = BufferQueue::new(); + let mut input = BufferQueue::default(); input.push_back(StrTendril::from(buf)); let mut tokenizer = Tokenizer::new( @@ -267,9 +267,9 @@ mod tests { #[test] fn test_include_nofollow() { let input = r#" - do not follow me - do not follow me - do not follow me + do not follow me + do not follow me + do not follow me "#; let expected = vec![RawUri { text: "https://example.org".to_string(), @@ -286,7 +286,7 @@ mod tests { - i'm fine + i'm fine "#; let expected = vec![RawUri { text: "https://example.org".to_string(), From 730f5310b1c1add05e3783a640abfe404e5b72df Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Wed, 24 Apr 2024 16:38:06 +0200 Subject: [PATCH 30/40] Disable lint with false positive --- lychee-bin/src/stats.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lychee-bin/src/stats.rs b/lychee-bin/src/stats.rs index d412cdd930..325a631b34 100644 --- a/lychee-bin/src/stats.rs +++ b/lychee-bin/src/stats.rs @@ -1,3 +1,6 @@ +// Disable lint, clippy thinks that InputSource has inner mutability, but this seems like a flase positive +#![allow(clippy::mutable_key_type)] + use std::collections::{HashMap, HashSet}; use crate::archive::Suggestion; From e0059c4292dc2369f1040100a41628607c99a971 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Thu, 25 Apr 2024 08:32:35 +0200 Subject: [PATCH 31/40] Fix typo --- lychee-bin/src/stats.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lychee-bin/src/stats.rs b/lychee-bin/src/stats.rs index 325a631b34..1254e62884 100644 --- a/lychee-bin/src/stats.rs +++ b/lychee-bin/src/stats.rs @@ -1,4 +1,4 @@ -// Disable lint, clippy thinks that InputSource has inner mutability, but this seems like a flase positive +// Disable lint, clippy thinks that InputSource has inner mutability, but this seems like a false positive #![allow(clippy::mutable_key_type)] use std::collections::{HashMap, HashSet}; From 8451f8846b262c57450a8a9d93c673be87c0b623 Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Thu, 25 Apr 2024 09:36:20 +0200 Subject: [PATCH 32/40] Bump to version 0.15.0 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- README.md | 2 +- lychee-bin/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9fd2b116dd..55f42cdbd8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2344,7 +2344,7 @@ dependencies = [ [[package]] name = "lychee" -version = "0.14.3" +version = "0.15.0" dependencies = [ "anyhow", "assert-json-diff", @@ -2389,7 +2389,7 @@ dependencies = [ [[package]] name = "lychee-lib" -version = "0.14.3" +version = "0.15.0" dependencies = [ "async-stream", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 7e299a5cc8..8aa2c32515 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["lychee-bin", "lychee-lib", "examples/*", "benches"] resolver = "2" [workspace.package] -version = "0.14.3" +version = "0.15.0" [profile.release] debug = true diff --git a/README.md b/README.md index 3dffa8c798..f6e397418f 100644 --- a/README.md +++ b/README.md @@ -566,7 +566,7 @@ Lychee can also be used as a [pre-commit](https://pre-commit.com/) hook. # .pre-commit-config.yaml repos: - repo: https://github.com/lycheeverse/lychee.git - rev: 0.14.3 + rev: 0.15.0 hooks: - id: lychee # Optionally include additional CLI arguments diff --git a/lychee-bin/Cargo.toml b/lychee-bin/Cargo.toml index 8fec633f5c..eec7fa0fae 100644 --- a/lychee-bin/Cargo.toml +++ b/lychee-bin/Cargo.toml @@ -14,7 +14,7 @@ version.workspace = true [dependencies] # NOTE: We need to specify the version of lychee-lib here because crates.io # requires all dependencies to have a version number. -lychee-lib = { path = "../lychee-lib", version = "0.14.3", default-features = false } +lychee-lib = { path = "../lychee-lib", version = "0.15.0", default-features = false } anyhow = "1.0.82" assert-json-diff = "2.0.2" From fc85695d2143784f932cff57e1a044ae208abd76 Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Thu, 25 Apr 2024 15:29:36 +0200 Subject: [PATCH 33/40] Gracefully handle invalid URIs (#1414) With the upgrade to `reqwest` 0.12, we can finally handle a long-standing issue, when Urls could not be parsed to Uris. Previously, we would panic, but we can now handle that situation gracefully and return an error instead. I've also renamed `Status::is_failure` to `Status::is_error`, because the notion of failures no longer exists in the codebase and we use the term "error" consistently throughout the codebase instead. This is technically a breaking change in the API, but it's fine since we have not released a stable version yet. More information about the URI parsing issue: - https://github.com/lycheeverse/lychee/issues/539 - https://github.com/seanmonstar/reqwest/issues/668 --- lychee-bin/src/commands/check.rs | 43 ++++++++++++++++++++++++++------ lychee-bin/src/stats.rs | 2 +- lychee-lib/src/client.rs | 22 ++++++++-------- lychee-lib/src/types/status.rs | 2 +- 4 files changed, 48 insertions(+), 21 deletions(-) diff --git a/lychee-bin/src/commands/check.rs b/lychee-bin/src/commands/check.rs index 92da1ad614..f1ca1afa2a 100644 --- a/lychee-bin/src/commands/check.rs +++ b/lychee-bin/src/commands/check.rs @@ -10,7 +10,7 @@ use reqwest::Url; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; -use lychee_lib::{Client, Request, Response}; +use lychee_lib::{Client, ErrorKind, Request, Response}; use lychee_lib::{InputSource, Result}; use lychee_lib::{ResponseBody, Status}; @@ -225,6 +225,25 @@ async fn request_channel_task( .await; } +/// Check a URL and return a response. +/// +/// # Errors +/// +/// This can fail when the URL could not be parsed to a URI. +async fn check_url(client: &Client, request: Request) -> Response { + // Request was not cached; run a normal check + let uri = request.uri.clone(); + let source = request.source.clone(); + client.check(request).await.unwrap_or_else(|e| { + log::error!("Error checking URL {}: Cannot parse URL to URI: {}", uri, e); + Response::new( + uri.clone(), + Status::Error(ErrorKind::InvalidURI(uri.clone())), + source, + ) + }) +} + /// Handle a single request async fn handle( client: &Client, @@ -250,12 +269,7 @@ async fn handle( } // Request was not cached; run a normal check - // - // This can panic when the Url could not be parsed to a Uri. - // See https://github.com/servo/rust-url/issues/554 - // See https://github.com/seanmonstar/reqwest/issues/668 - // TODO: Handle error as soon as https://github.com/seanmonstar/reqwest/pull/1399 got merged - let response = client.check(request).await.expect("cannot check URI"); + let response = check_url(client, request).await; // - Never cache filesystem access as it is fast already so caching has no // benefit. @@ -318,7 +332,7 @@ fn get_failed_urls(stats: &mut ResponseStats) -> Vec<(InputSource, Url)> { mod tests { use log::info; - use lychee_lib::{CacheStatus, InputSource, ResponseBody, Uri}; + use lychee_lib::{CacheStatus, ClientBuilder, InputSource, ResponseBody, Uri}; use crate::formatters; @@ -367,4 +381,17 @@ mod tests { let buf = String::from_utf8_lossy(&buf); assert_eq!(buf, "↻ [200] http://127.0.0.1/ | Cached: OK (cached)\n"); } + + #[tokio::test] + async fn test_invalid_url() { + // Run a normal request with an invalid Url + let client = ClientBuilder::builder().build().client().unwrap(); + let request = Request::try_from("http://\"").unwrap(); + let response = check_url(&client, request).await; + assert!(response.status().is_error()); + assert!(matches!( + response.status(), + Status::Error(ErrorKind::InvalidURI(_)) + )); + } } diff --git a/lychee-bin/src/stats.rs b/lychee-bin/src/stats.rs index 1254e62884..01c91a29b5 100644 --- a/lychee-bin/src/stats.rs +++ b/lychee-bin/src/stats.rs @@ -63,7 +63,7 @@ impl ResponseStats { self.increment_status_counters(status); match status { - _ if status.is_failure() => { + _ if status.is_error() => { let fail = self.fail_map.entry(source).or_default(); fail.insert(response.1); } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 0bc1fd0f76..e7d687761a 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -761,13 +761,13 @@ mod tests { let mock_server = mock_server!(StatusCode::NOT_FOUND); let res = get_mock_client_response(mock_server.uri()).await; - assert!(res.status().is_failure()); + assert!(res.status().is_error()); } #[tokio::test] async fn test_nonexistent_with_path() { let res = get_mock_client_response("http://127.0.0.1/invalid").await; - assert!(res.status().is_failure()); + assert!(res.status().is_error()); } #[tokio::test] @@ -779,7 +779,7 @@ mod tests { #[tokio::test] async fn test_github_nonexistent_repo() { let res = get_mock_client_response("https://github.com/lycheeverse/not-lychee").await; - assert!(res.status().is_failure()); + assert!(res.status().is_error()); } #[tokio::test] @@ -788,7 +788,7 @@ mod tests { "https://github.com/lycheeverse/lychee/blob/master/NON_EXISTENT_FILE.md", ) .await; - assert!(res.status().is_failure()); + assert!(res.status().is_error()); } #[tokio::test] @@ -798,7 +798,7 @@ mod tests { assert!(res.status().is_success()); let res = get_mock_client_response("https://www.youtube.com/watch?v=invalidNlKuICiT470&list=PLbWDhxwM_45mPVToqaIZNbZeIzFchsKKQ&index=7").await; - assert!(res.status().is_failure()); + assert!(res.status().is_error()); } #[tokio::test] @@ -831,7 +831,7 @@ mod tests { async fn test_invalid_ssl() { let res = get_mock_client_response("https://expired.badssl.com/").await; - assert!(res.status().is_failure()); + assert!(res.status().is_error()); // Same, but ignore certificate error let res = ClientBuilder::builder() @@ -920,7 +920,7 @@ mod tests { .client() .unwrap(); let res = client.check("http://example.com").await.unwrap(); - assert!(res.status().is_failure()); + assert!(res.status().is_error()); } #[tokio::test] @@ -984,7 +984,7 @@ mod tests { let res = client.check(mock_server.uri()).await.unwrap(); let end = start.elapsed(); - assert!(res.status().is_failure()); + assert!(res.status().is_error()); // on slow connections, this might take a bit longer than nominal // backed-off timeout (7 secs) @@ -996,7 +996,7 @@ mod tests { let client = ClientBuilder::builder().build().client().unwrap(); // This request will fail, but it won't panic let res = client.check("http://\"").await.unwrap(); - assert!(res.status().is_failure()); + assert!(res.status().is_error()); } #[tokio::test] @@ -1029,7 +1029,7 @@ mod tests { .unwrap(); let res = client.check(redirect_uri.clone()).await.unwrap(); - assert!(res.status().is_failure()); + assert!(res.status().is_error()); let client = ClientBuilder::builder() .max_redirects(1_usize) @@ -1060,7 +1060,7 @@ mod tests { .unwrap(); let res = client.check(mock_server.uri()).await.unwrap(); - assert!(res.status().is_failure()); + assert!(res.status().is_error()); } #[tokio::test] diff --git a/lychee-lib/src/types/status.rs b/lychee-lib/src/types/status.rs index a4a9a01fb5..bd838a0a87 100644 --- a/lychee-lib/src/types/status.rs +++ b/lychee-lib/src/types/status.rs @@ -160,7 +160,7 @@ impl Status { #[inline] #[must_use] /// Returns `true` if the check was not successful - pub const fn is_failure(&self) -> bool { + pub const fn is_error(&self) -> bool { matches!( self, Status::Error(_) | Status::Cached(CacheStatus::Error(_)) | Status::Timeout(_) From 25a3eb1a3acf641ad8f683182f0777aa3c52bd9d Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Thu, 25 Apr 2024 15:31:03 +0200 Subject: [PATCH 34/40] Chain visibility (#1415) * Make chain public * Make function on chain public * Add must_use attribute * Make RequestChain type public * Add chain usage example --- Cargo.lock | 763 ++++++++++++++++++---------------- examples/chain/Cargo.toml | 20 + examples/chain/LICENSE-APACHE | 201 +++++++++ examples/chain/LICENSE-MIT | 21 + examples/chain/chain.rs | 34 ++ lychee-lib/src/chain/mod.rs | 5 +- lychee-lib/src/lib.rs | 2 +- 7 files changed, 684 insertions(+), 362 deletions(-) create mode 100644 examples/chain/Cargo.toml create mode 100644 examples/chain/LICENSE-APACHE create mode 100644 examples/chain/LICENSE-MIT create mode 100644 examples/chain/chain.rs diff --git a/Cargo.lock b/Cargo.lock index 55f42cdbd8..9829e7e005 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "once_cell", @@ -31,18 +31,18 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "android-tzdata" @@ -81,9 +81,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "anstyle-parse" @@ -121,9 +121,9 @@ checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "arc-swap" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" [[package]] name = "ascii_utils" @@ -169,22 +169,22 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.1.1" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ca33f4bc4ed1babef42cad36cc1f51fa88be00420404e5b1e80ab1b18f7678c" +checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" dependencies = [ "concurrent-queue", - "event-listener 4.0.2", - "event-listener-strategy", + "event-listener 5.3.0", + "event-listener-strategy 0.5.1", "futures-core", "pin-project-lite", ] [[package]] name = "async-compression" -version = "0.4.5" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc2d0cfb2a7388d34f590e76686704c494ed7aaceed62ee1ba35cbf363abc2a5" +checksum = "07dbbf24db18d609b1462965249abdf49129ccad073ec257da372adc83259c60" dependencies = [ "flate2", "futures-core", @@ -195,15 +195,14 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.8.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" dependencies = [ - "async-lock 3.2.0", "async-task", "concurrent-queue", - "fastrand 2.0.1", - "futures-lite 2.1.0", + "fastrand 2.0.2", + "futures-lite 2.3.0", "slab", ] @@ -213,12 +212,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05b1b633a2115cd122d73b955eadd9916c18c8f510ec9cd1686404c60ad1c29c" dependencies = [ - "async-channel 2.1.1", + "async-channel 2.2.1", "async-executor", - "async-io 2.2.2", - "async-lock 3.2.0", + "async-io 2.3.2", + "async-lock 3.3.0", "blocking", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "once_cell", ] @@ -244,18 +243,18 @@ dependencies = [ [[package]] name = "async-io" -version = "2.2.2" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afaa937395a620e33dc6a742c593c01aced20aa376ffb0f628121198578ccc7" +checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" dependencies = [ - "async-lock 3.2.0", + "async-lock 3.3.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "parking", - "polling 3.3.1", - "rustix 0.38.33", + "polling 3.7.0", + "rustix 0.38.34", "slab", "tracing", "windows-sys 0.52.0", @@ -272,12 +271,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7125e42787d53db9dd54261812ef17e937c95a51e4d291373b670342fa44310c" +checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" dependencies = [ - "event-listener 4.0.2", - "event-listener-strategy", + "event-listener 4.0.3", + "event-listener-strategy 0.4.0", "pin-project-lite", ] @@ -306,37 +305,37 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.33", + "rustix 0.38.34", "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.0.5" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd55a5ba1179988837d24ab4c7cc8ed6efdeff578ede0416b4225a5fca35bd0" +checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" dependencies = [ - "async-io 2.2.2", - "async-lock 2.8.0", + "async-io 2.3.2", + "async-lock 3.3.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.33", + "rustix 0.38.34", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -424,7 +423,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -441,7 +440,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -452,15 +451,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ "addr2line", "cc", @@ -479,9 +478,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" @@ -505,9 +504,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "block-buffer" @@ -524,21 +523,21 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" dependencies = [ - "async-channel 2.1.1", - "async-lock 3.2.0", + "async-channel 2.2.1", + "async-lock 3.3.0", "async-task", - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-io", - "futures-lite 2.1.0", + "futures-lite 2.3.0", "piper", "tracing", ] [[package]] name = "bstr" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c48f0051a4b4c5e0b6d365cd04af53aeaa209e3cc15ec2cdb69e73cc87fbd0dc" +checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", "regex-automata 0.4.6", @@ -555,7 +554,7 @@ checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" name = "builder" version = "0.1.0" dependencies = [ - "http 1.0.0", + "http 1.1.0", "lychee-lib", "regex", "reqwest 0.12.4", @@ -564,27 +563,27 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "by_address" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf8dba2868114ed769a1f2590fc9ae5eb331175b44313b6c9b922f8f7ca813d0" +checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytecount" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1e5f035d16fc623ae5f74981db80a439803888314e3a555fd6f04acd51a3205" +checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" [[package]] name = "bytes" -version = "1.5.0" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" [[package]] name = "cached" @@ -627,12 +626,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.83" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" [[package]] name = "cfg-if" @@ -640,6 +636,16 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chain" +version = "0.1.0" +dependencies = [ + "async-trait", + "lychee-lib", + "reqwest 0.12.4", + "tokio", +] + [[package]] name = "check-if-email-exists" version = "0.9.1" @@ -651,7 +657,7 @@ dependencies = [ "async-smtp", "async-std", "async-std-resolver", - "fast-socks5 0.9.2", + "fast-socks5 0.9.6", "levenshtein", "log", "mailchecker", @@ -667,22 +673,22 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.48.5", + "windows-targets 0.52.5", ] [[package]] name = "ciborium" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "effd91f6c78e5a4ace8a5d3c0b6bfaec9e2baaef55f3efc00e45fb2e477ee926" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", @@ -691,15 +697,15 @@ dependencies = [ [[package]] name = "ciborium-io" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdf919175532b369853f5d5e20b26b43112613fd6fe7aee757e35f7a44642656" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "defaa24ecc093c77630e6c15e17c51f5e187bf35ee514f4e2d67baaa96dae22b" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", @@ -736,7 +742,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -759,7 +765,7 @@ dependencies = [ name = "collect_links" version = "0.1.0" dependencies = [ - "http 1.0.0", + "http 1.1.0", "lychee-lib", "regex", "reqwest 0.12.4", @@ -870,9 +876,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.3.2" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" dependencies = [ "cfg-if", ] @@ -915,11 +921,10 @@ dependencies = [ [[package]] name = "crossbeam" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eb9105919ca8e40d437fc9cbb8f1975d916f1bd28afe795a48aae32a2cc8920" +checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" dependencies = [ - "cfg-if", "crossbeam-channel", "crossbeam-deque", "crossbeam-epoch", @@ -929,54 +934,52 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.10" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a9b73a36529d9c47029b9fb3a6f0ea3cc916a261195352ba19e770fc1748b2" +checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ - "cfg-if", "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" -version = "0.9.17" +version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ - "autocfg", - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc6598521bb5a83d491e8c1fe51db7296019d2ca3cb93cc6c2a20369a4d78a2" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" dependencies = [ - "cfg-if", "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.18" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c" -dependencies = [ - "cfg-if", -] +checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "crypto-common" @@ -1031,12 +1034,12 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" dependencies = [ - "darling_core 0.20.3", - "darling_macro 0.20.3", + "darling_core 0.20.8", + "darling_macro 0.20.8", ] [[package]] @@ -1069,16 +1072,16 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -1105,13 +1108,13 @@ dependencies = [ [[package]] name = "darling_macro" -version = "0.20.3" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ - "darling_core 0.20.3", + "darling_core 0.20.8", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -1249,9 +1252,9 @@ checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "either" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "email_address" @@ -1270,9 +1273,9 @@ checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" dependencies = [ "cfg-if", ] @@ -1301,9 +1304,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.11.1" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05e7cf40684ae96ade6232ed84582f40ce0a66efcd43a5117aef610534f8e0b8" +checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" dependencies = [ "anstream", "anstyle", @@ -1347,9 +1350,20 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.2" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "218a870470cce1469024e9fb66b901aa983929d81304a1cdb299f28118e550d5" +checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" dependencies = [ "concurrent-queue", "parking", @@ -1362,7 +1376,17 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" dependencies = [ - "event-listener 4.0.2", + "event-listener 4.0.3", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" +dependencies = [ + "event-listener 5.3.0", "pin-project-lite", ] @@ -1389,9 +1413,9 @@ dependencies = [ [[package]] name = "fast-socks5" -version = "0.9.2" +version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d449e348301d5fb9b0e5781510d8235ffe3bbac3286bd305462736a9e7043039" +checksum = "f89f36d4ee12370d30d57b16c7e190950a1a916e7dbbb5fd5a412f5ef913fe84" dependencies = [ "anyhow", "async-trait", @@ -1421,9 +1445,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" [[package]] name = "flate2" @@ -1562,11 +1586,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.1.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeee267a1883f7ebef3700f262d2d54de95dfaf38189015a74fdc4e0c7ad8143" +checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-core", "futures-io", "parking", @@ -1581,7 +1605,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -1598,9 +1622,9 @@ checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-timer" -version = "3.0.2" +version = "3.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c" +checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" @@ -1641,9 +1665,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -1678,9 +1702,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.22" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" dependencies = [ "bytes", "fnv", @@ -1688,7 +1712,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap 2.1.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1706,8 +1730,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 1.0.0", - "indexmap 2.1.0", + "http 1.1.0", + "indexmap 2.2.6", "slab", "tokio", "tokio-util", @@ -1716,9 +1740,13 @@ dependencies = [ [[package]] name = "half" -version = "1.8.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if", + "crunchy", +] [[package]] name = "hashbrown" @@ -1742,10 +1770,10 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "322106e6bd0cba2d5ead589ddb8150a13d7c4217cf80d7c4f682ca994ccc6aa9" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "headers-core", - "http 1.0.0", + "http 1.1.0", "httpdate", "mime", "sha1 0.10.6", @@ -1757,7 +1785,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54b4a22553d4242c49fddb9ba998a99962b5cc6f22cb5a3482bec22522403ce4" dependencies = [ - "http 1.0.0", + "http 1.1.0", ] [[package]] @@ -1774,9 +1802,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "hex" @@ -1806,7 +1834,7 @@ dependencies = [ "markup5ever", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -1831,9 +1859,9 @@ dependencies = [ [[package]] name = "http" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -1858,7 +1886,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.0.0", + "http 1.1.0", ] [[package]] @@ -1869,7 +1897,7 @@ checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" dependencies = [ "bytes", "futures-core", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "pin-project-lite", ] @@ -1912,14 +1940,14 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.22", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.6", "tokio", "tower-service", "tracing", @@ -1936,7 +1964,7 @@ dependencies = [ "futures-channel", "futures-util", "h2 0.4.4", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "httparse", "httpdate", @@ -1954,7 +1982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", - "http 1.0.0", + "http 1.1.0", "hyper 1.3.1", "hyper-util", "log", @@ -2017,11 +2045,11 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "hyper 1.3.1", "pin-project-lite", - "socket2 0.5.5", + "socket2 0.5.6", "tokio", "tower", "tower-service", @@ -2030,9 +2058,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.59" +version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -2101,9 +2129,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2155,7 +2183,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b58db92f96b720de98181bbbe63c831e87005ab460c1bf306eb2622b4707997f" dependencies = [ - "socket2 0.5.5", + "socket2 0.5.6", "widestring", "windows-sys 0.48.0", "winreg 0.50.0", @@ -2169,9 +2197,9 @@ checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "iri-string" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21859b667d66a4c1dacd9df0863b3efb65785474255face87f5bca39dd8407c0" +checksum = "7f5f6c2df22c009ac44f6f1499308e7a3ac7ba42cd2378475cc691510e1eef1b" dependencies = [ "memchr", "serde", @@ -2179,12 +2207,12 @@ dependencies = [ [[package]] name = "is-terminal" -version = "0.4.10" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" +checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", - "rustix 0.38.33", + "libc", "windows-sys 0.52.0", ] @@ -2205,9 +2233,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "jetscii" @@ -2217,20 +2245,20 @@ checksum = "47f142fe24a9c9944451e8349de0a56af5f3e7226dc46f3ed4d4ecc0b85af75e" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "jsonwebtoken" -version = "9.2.0" +version = "9.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "js-sys", "pem", "ring", @@ -2278,13 +2306,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "libc", - "redox_syscall", ] [[package]] @@ -2310,9 +2337,9 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -2357,7 +2384,7 @@ dependencies = [ "env_logger", "futures", "headers", - "http 1.0.0", + "http 1.1.0", "humantime", "humantime-serde", "indicatif", @@ -2402,7 +2429,7 @@ dependencies = [ "headers", "html5ever", "html5gum", - "http 1.0.0", + "http 1.1.0", "hyper 1.3.1", "ip_network", "jwalk", @@ -2452,9 +2479,9 @@ dependencies = [ [[package]] name = "markup5ever" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7940b09815a02810a42b9e1bc41c069880a87de68e9b1dcbe754a3ba3b47c20" +checksum = "16ce3abbeba692c8b8441d036ef91aea6df8da2c6b6e21c7e14d3c18e526be45" dependencies = [ "log", "phf", @@ -2493,9 +2520,9 @@ checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "mime" @@ -2511,18 +2538,18 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" dependencies = [ "adler", ] [[package]] name = "mio" -version = "0.8.10" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "wasi", @@ -2558,9 +2585,9 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nom" @@ -2589,21 +2616,26 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-integer" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ - "autocfg", "num-traits", ] [[package]] name = "num-traits" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] @@ -2648,7 +2680,7 @@ dependencies = [ "either", "futures", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "http-body-util", "hyper 1.3.1", @@ -2686,11 +2718,11 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "cfg-if", "foreign-types", "libc", @@ -2707,7 +2739,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -2718,9 +2750,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.2.1+3.2.0" +version = "300.2.3+3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3" +checksum = "5cff92b6f71555b61bb9315f7c64da3ca43d87531622120fea0195fc761b4843" dependencies = [ "cc", ] @@ -2820,11 +2852,11 @@ checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" [[package]] name = "pem" -version = "3.0.3" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.21.5", + "base64 0.22.0", "serde", ] @@ -2893,29 +2925,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" [[package]] name = "pin-utils" @@ -2930,15 +2962,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" dependencies = [ "atomic-waker", - "fastrand 2.0.1", + "fastrand 2.0.2", "futures-io", ] [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] name = "plotters" @@ -2986,14 +3018,15 @@ dependencies = [ [[package]] name = "polling" -version = "3.3.1" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf63fa624ab313c11656b4cda960bfc46c410187ad493c41f6ba2d8c1e991c9e" +checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" dependencies = [ "cfg-if", "concurrent-queue", + "hermit-abi", "pin-project-lite", - "rustix 0.38.33", + "rustix 0.38.34", "tracing", "windows-sys 0.52.0", ] @@ -3088,9 +3121,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.76" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -3117,7 +3150,7 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57206b407293d2bcd3af849ce869d52068623f19e1b5ff8e8778e3309439682b" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "getopts", "memchr", "unicase", @@ -3146,9 +3179,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.35" +version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] @@ -3185,9 +3218,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.8.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", @@ -3195,9 +3228,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -3214,9 +3247,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", @@ -3232,7 +3265,7 @@ dependencies = [ "aho-corasick", "memchr", "regex-automata 0.4.6", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -3252,7 +3285,7 @@ checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.2", + "regex-syntax 0.8.3", ] [[package]] @@ -3263,9 +3296,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "relative-path" @@ -3279,12 +3312,12 @@ version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.22", + "h2 0.3.26", "http 0.2.12", "http-body 0.4.6", "hyper 0.14.28", @@ -3325,9 +3358,11 @@ dependencies = [ "bytes", "cookie", "cookie_store", + "encoding_rs", "futures-core", "futures-util", - "http 1.0.0", + "h2 0.4.4", + "http 1.1.0", "http-body 1.0.0", "http-body-util", "hyper 1.3.1", @@ -3350,6 +3385,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", @@ -3424,7 +3460,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.48", + "syn 2.0.60", "unicode-ident", ] @@ -3459,14 +3495,14 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.33" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cc72858054fcff6d7dea32df2aeaee6a7c24227366d7ea429aada2f26b16ad" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "errno", "libc", - "linux-raw-sys 0.4.12", + "linux-raw-sys 0.4.13", "windows-sys 0.52.0", ] @@ -3503,7 +3539,7 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64 0.21.5", + "base64 0.21.7", ] [[package]] @@ -3518,15 +3554,15 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd36cc4259e3e4514335c4a138c6b43171a8d61d8f5c9348f9fc7529416f247" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" [[package]] name = "rustls-webpki" -version = "0.102.2" +version = "0.102.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faaa0a62740bedb9b2ef5afa303da42764c012f743917351dc9a237ea1663610" +checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" dependencies = [ "ring", "rustls-pki-types", @@ -3535,15 +3571,15 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" @@ -3581,9 +3617,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "2.9.2" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -3594,9 +3630,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" dependencies = [ "core-foundation-sys", "libc", @@ -3604,9 +3640,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0" +checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" @@ -3625,7 +3661,7 @@ checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -3641,9 +3677,9 @@ dependencies = [ [[package]] name = "serde_path_to_error" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -3672,15 +3708,15 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.7.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee80b0e361bbf88fd2f6e242ccd19cfda072cb0faa6ae694ecee08199938569a" +checksum = "2c85f8e96d1d6857f13768fcbd895fcb06225510022a2774ed8b5150581847b0" dependencies = [ - "base64 0.21.5", + "base64 0.22.0", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_derive", "serde_json", @@ -3690,14 +3726,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.7.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" +checksum = "c8b3a576c4eb2924262d5951a3b737ccaf16c931e39a2810c36f9a7e25575557" dependencies = [ - "darling 0.20.3", + "darling 0.20.8", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -3746,9 +3782,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -3812,7 +3848,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -3827,12 +3863,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -3907,7 +3943,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -3938,9 +3974,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.48" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ "proc-macro2", "quote", @@ -4005,8 +4041,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.0.1", - "rustix 0.38.33", + "fastrand 2.0.2", + "rustix 0.38.34", "windows-sys 0.52.0", ] @@ -4044,14 +4080,14 @@ checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "thread_local" -version = "1.1.7" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" dependencies = [ "cfg-if", "once_cell", @@ -4059,12 +4095,13 @@ dependencies = [ [[package]] name = "time" -version = "0.3.31" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f657ba42c3f86e7680e53c8cd3af8abbe56b5491790b46e22e19c0d57463583e" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "num-conv", "powerfmt", "serde", "time-core", @@ -4079,10 +4116,11 @@ checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26197e33420244aeb70c3e8c78376ca46571bc4e701e4791c2cd9f57dcb3a43f" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ + "num-conv", "time-core", ] @@ -4125,7 +4163,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2 0.5.6", "tokio-macros", "windows-sys 0.48.0", ] @@ -4138,7 +4176,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -4226,7 +4264,7 @@ version = "0.22.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", @@ -4256,10 +4294,10 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e9cd434a998747dd2c4276bc96ee2e0c7a2eadf3cae88e52be55a05fa9053f5" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.5.0", "bytes", "futures-util", - "http 1.0.0", + "http 1.1.0", "http-body 1.0.0", "http-body-util", "iri-string", @@ -4302,7 +4340,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -4396,7 +4434,7 @@ checksum = "1f718dfaf347dcb5b983bfc87608144b0bad87970aebcbea5ce44d2a30c08e63" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] @@ -4416,9 +4454,9 @@ dependencies = [ [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -4428,9 +4466,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" dependencies = [ "tinyvec", ] @@ -4521,9 +4559,9 @@ checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" [[package]] name = "walkdir" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", @@ -4546,9 +4584,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -4556,24 +4594,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" dependencies = [ "cfg-if", "js-sys", @@ -4583,9 +4621,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4593,28 +4631,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", @@ -4622,9 +4660,9 @@ dependencies = [ [[package]] name = "widestring" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" [[package]] name = "winapi" @@ -4644,11 +4682,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -4663,7 +4701,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -4681,7 +4719,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.5", ] [[package]] @@ -4701,17 +4739,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", ] [[package]] @@ -4722,9 +4761,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" @@ -4734,9 +4773,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" @@ -4746,9 +4785,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" @@ -4758,9 +4803,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" @@ -4770,9 +4815,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" @@ -4782,9 +4827,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" @@ -4794,9 +4839,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" [[package]] name = "winnow" @@ -4835,10 +4880,10 @@ checksum = "ec874e1eef0df2dcac546057fe5e29186f09c378181cd7b635b4b7bcc98e9d81" dependencies = [ "assert-json-diff", "async-trait", - "base64 0.21.5", + "base64 0.21.7", "deadpool", "futures", - "http 1.0.0", + "http 1.1.0", "http-body-util", "hyper 1.3.1", "hyper-util", @@ -4874,11 +4919,11 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn 2.0.60", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "63381fa6624bf92130a6b87c0d07380116f80b565c42cf0d754136f0238359ef" diff --git a/examples/chain/Cargo.toml b/examples/chain/Cargo.toml new file mode 100644 index 0000000000..48d403ed8b --- /dev/null +++ b/examples/chain/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "chain" +version = "0.1.0" +edition = "2021" + +[[example]] +name = "chain" +path = "chain.rs" + +[dependencies] +async-trait = "0.1.80" +lychee-lib = { path = "../../lychee-lib", default-features = false } +reqwest = "0.12.4" +tokio = { version = "1.37.0", features = ["full"] } + +[features] +email-check = ["lychee-lib/email-check"] +native-tls = ["lychee-lib/native-tls"] +rustls-tls = ["lychee-lib/rustls-tls"] +default = ["native-tls", "email-check"] diff --git a/examples/chain/LICENSE-APACHE b/examples/chain/LICENSE-APACHE new file mode 100644 index 0000000000..f51e79e400 --- /dev/null +++ b/examples/chain/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright 2020 The lychee maintainers + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/examples/chain/LICENSE-MIT b/examples/chain/LICENSE-MIT new file mode 100644 index 0000000000..f702c7bc16 --- /dev/null +++ b/examples/chain/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 The lychee maintainers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/examples/chain/chain.rs b/examples/chain/chain.rs new file mode 100644 index 0000000000..57aa70961e --- /dev/null +++ b/examples/chain/chain.rs @@ -0,0 +1,34 @@ +use async_trait::async_trait; +use lychee_lib::{chain::RequestChain, ChainResult, ClientBuilder, Handler, Result, Status}; +use reqwest::{Method, Request}; + +#[derive(Debug)] +struct MyHandler {} + +#[async_trait] +impl Handler for MyHandler { + async fn handle(&mut self, mut request: Request) -> ChainResult { + // Handle special case of some website (fictional example) + if request.url().domain() == Some("wikipedia.org") && request.url().path() == "/home" { + request.url_mut().set_path("/foo-bar"); + *request.method_mut() = Method::PUT; + } + + ChainResult::Next(request) + } +} + +#[tokio::main] +async fn main() -> Result<()> { + let chain = RequestChain::new(vec![Box::new(MyHandler {})]); + + let client = ClientBuilder::builder() + .plugin_request_chain(chain) + .build() + .client()?; + + let result = client.check("https://wikipedia.org/home").await; + println!("{:?}", result); + + Ok(()) +} diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index bdf52a22b6..92de75b68b 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -37,7 +37,7 @@ pub enum ChainResult { /// Request chain type /// /// This takes a request and returns a status. -pub(crate) type RequestChain = Chain; +pub type RequestChain = Chain; /// Inner chain type. /// @@ -72,7 +72,8 @@ impl Clone for Chain { impl Chain { /// Create a new chain from a vector of chainable handlers - pub(crate) fn new(values: InnerChain) -> Self { + #[must_use] + pub fn new(values: InnerChain) -> Self { Self(Arc::new(Mutex::new(values))) } diff --git a/lychee-lib/src/lib.rs b/lychee-lib/src/lib.rs index 98a0701485..13cc5d4e42 100644 --- a/lychee-lib/src/lib.rs +++ b/lychee-lib/src/lib.rs @@ -51,7 +51,7 @@ doc_comment::doctest!("../../README.md"); mod basic_auth; -mod chain; +pub mod chain; mod checker; mod client; /// A pool of clients, to handle concurrent checks From 7be088bbfcf673b606c60df150d890a5e7a09f96 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Fri, 26 Apr 2024 06:44:24 +1000 Subject: [PATCH 35/40] Fix spelling; `Github` -> `GitHub` (#1416) --- lychee-bin/src/main.rs | 2 +- lychee-bin/tests/cli.rs | 2 +- lychee-lib/src/client.rs | 4 ++-- lychee-lib/src/collector.rs | 2 +- lychee-lib/src/extract/mod.rs | 2 +- lychee-lib/src/retry.rs | 2 +- lychee-lib/src/types/error.rs | 12 ++++++------ lychee-lib/src/types/uri/github.rs | 6 +++--- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lychee-bin/src/main.rs b/lychee-bin/src/main.rs index 3535fea9d5..2715d178a8 100644 --- a/lychee-bin/src/main.rs +++ b/lychee-bin/src/main.rs @@ -383,7 +383,7 @@ async fn run(opts: &LycheeOptions) -> Result { if github_issues && opts.config.github_token.is_none() { let mut handle = io::stderr(); - color!(handle, YELLOW, "\u{1f4a1} There were issues with Github URLs. You could try setting a Github token and running lychee again.",)?; + color!(handle, YELLOW, "\u{1f4a1} There were issues with GitHub URLs. You could try setting a GitHub token and running lychee again.",)?; } if opts.config.cache { diff --git a/lychee-bin/tests/cli.rs b/lychee-bin/tests/cli.rs index aed26341ea..931783a668 100644 --- a/lychee-bin/tests/cli.rs +++ b/lychee-bin/tests/cli.rs @@ -416,7 +416,7 @@ mod cli { "✗ [404] https://github.com/mre/idiomatic-rust-doesnt-exist-man | Failed: Network error: Not Found" )) .stderr(contains( - "There were issues with Github URLs. You could try setting a Github token and running lychee again.", + "There were issues with GitHub URLs. You could try setting a GitHub token and running lychee again.", )); } diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index e7d687761a..77b132f700 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -300,7 +300,7 @@ impl ClientBuilder { /// - The reqwest client cannot be instantiated. This occurs if a TLS /// backend cannot be initialized or the resolver fails to load the system /// configuration. See [here]. - /// - The Github client cannot be created. Since the implementation also + /// - The GitHub client cannot be created. Since the implementation also /// uses reqwest under the hood, this errors in the same circumstances as /// the last one. /// @@ -604,7 +604,7 @@ impl Client { if let Ok(github_uri) = GithubUri::try_from(uri) { let status = self.check_github(github_uri).await; - // Only return Github status in case of success + // Only return GitHub status in case of success // Otherwise return the original error, which has more information if status.is_success() { return status; diff --git a/lychee-lib/src/collector.rs b/lychee-lib/src/collector.rs index 821b3593d7..ed048abc9e 100644 --- a/lychee-lib/src/collector.rs +++ b/lychee-lib/src/collector.rs @@ -351,7 +351,7 @@ mod tests { async fn test_relative_url_with_base_extracted_from_input() { let contents = r#" "#; diff --git a/lychee-lib/src/extract/mod.rs b/lychee-lib/src/extract/mod.rs index 39d15cf8db..e0c0ee44bb 100644 --- a/lychee-lib/src/extract/mod.rs +++ b/lychee-lib/src/extract/mod.rs @@ -190,7 +190,7 @@ mod tests { let contents = r#" "#; diff --git a/lychee-lib/src/retry.rs b/lychee-lib/src/retry.rs index 0eed204f20..cff2eaa487 100644 --- a/lychee-lib/src/retry.rs +++ b/lychee-lib/src/retry.rs @@ -95,7 +95,7 @@ impl RetryExt for ErrorKind { // If the error is a `reqwest::Error`, delegate to that if let Some(r) = self.reqwest_error() { r.should_retry() - // Github errors sometimes wrap `reqwest` errors. + // GitHub errors sometimes wrap `reqwest` errors. // In that case, delegate to the underlying error. } else if let Some(octocrab::Error::Http { source, diff --git a/lychee-lib/src/types/error.rs b/lychee-lib/src/types/error.rs index 0ebd031b05..f2104741ea 100644 --- a/lychee-lib/src/types/error.rs +++ b/lychee-lib/src/types/error.rs @@ -24,7 +24,7 @@ pub enum ErrorKind { #[error("Error creating request client: {0}")] BuildRequestClient(#[source] reqwest::Error), - /// Network error while using Github API + /// Network error while using GitHub API #[error("Network error (GitHub client)")] GithubRequest(#[from] octocrab::Error), @@ -44,12 +44,12 @@ pub enum ErrorKind { #[error("Attempted to interpret an invalid sequence of bytes as a string")] Utf8(#[from] std::str::Utf8Error), - /// The Github client required for making requests cannot be created - #[error("Error creating Github client")] + /// The GitHub client required for making requests cannot be created + #[error("Error creating GitHub client")] BuildGithubClient(#[source] octocrab::Error), - /// Invalid Github URL - #[error("Github URL is invalid: {0}")] + /// Invalid GitHub URL + #[error("GitHub URL is invalid: {0}")] InvalidGithubUrl(String), /// The input is empty and not accepted as a valid URL @@ -102,7 +102,7 @@ pub enum ErrorKind { #[error("UNIX glob pattern is invalid")] InvalidGlobPattern(#[from] glob::PatternError), - /// The Github API could not be called because of a missing Github token. + /// The GitHub API could not be called because of a missing GitHub token. #[error("GitHub token not specified. To check GitHub links reliably, use `--github-token` flag / `GITHUB_TOKEN` env var.")] MissingGitHubToken, diff --git a/lychee-lib/src/types/uri/github.rs b/lychee-lib/src/types/uri/github.rs index 68ba3d97b3..514f2e0e76 100644 --- a/lychee-lib/src/types/uri/github.rs +++ b/lychee-lib/src/types/uri/github.rs @@ -23,7 +23,7 @@ static GITHUB_API_EXCLUDED_ENDPOINTS: Lazy> = Lazy::new(|| ]) }); -/// Uri path segments extracted from a Github URL +/// Uri path segments extracted from a GitHub URL #[derive(PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct GithubUri { /// Organization name @@ -35,7 +35,7 @@ pub struct GithubUri { } impl GithubUri { - /// Create a new Github URI without an endpoint + /// Create a new GitHub URI without an endpoint #[cfg(test)] fn new>(owner: T, repo: T) -> Self { GithubUri { @@ -83,7 +83,7 @@ impl GithubUri { if parts.len() < 2 { // Not a valid org/repo pair. - // Note: We don't check for exactly 2 here, because the Github + // Note: We don't check for exactly 2 here, because the GitHub // API doesn't handle checking individual files inside repos or // paths like , so we are more // permissive and only check for repo existence. This is the From 0956ec6c38d50a30cdbf91ad9ea7cb19326c5427 Mon Sep 17 00:00:00 2001 From: John Bampton Date: Fri, 26 Apr 2024 16:22:44 +1000 Subject: [PATCH 36/40] Fix spelling and remove unneeded trailing whitespace (#1417) --- fixtures/bench/arch.html | 84 ++++++++++++------------ fixtures/fragments/file1.md | 2 +- lychee-lib/src/extract/html/html5ever.rs | 2 +- lychee-lib/src/extract/html/html5gum.rs | 2 +- lychee-lib/src/extract/html/mod.rs | 2 +- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/fixtures/bench/arch.html b/fixtures/bench/arch.html index a4b53101c9..dc6736caa3 100644 --- a/fixtures/bench/arch.html +++ b/fixtures/bench/arch.html @@ -72,28 +72,28 @@

- + Search - + @@ -129,48 +129,48 @@

Navigation menu

- +
- @@ -235,9 +235,9 @@

More collapsed

- +
    - +
    @@ -259,7 +259,7 @@

    List of applications

    From ArchWiki
    - +

    This article is a general list of applications sorted by category, as a reference for those looking for packages. Many sections are split between console and graphical applications.

    -
    Tip: +
    Tip:
    • This page exists primarily to make it easier to search for alternatives to an application that you do not know under which section has been added. Use the links in the template at the top to view the main sections as separate pages.
    • Please consider installing the pkgstats package, which provides a timer that sends a list of the packages installed on your system, along with the architecture and the mirrors you use, to the Arch Linux developers in order to help them prioritize their efforts and make the distribution even better. The information is sent anonymously and cannot be used to identify you. You can view the collected data at the Statistics page. More information is available in this forum thread.
    • Daemon packages usually include the relevant systemd unit file to start; some packages even include different ones. After installation pacman -Qql package | grep -Fe .service -e .socket can be used to check and find the relevant one.
    @@ -1234,7 +1234,7 @@
    Browsers based o
    • wyeb — A vim-like web browser inspired by dwb and luakit with Adblock.
    https://github.com/jun7/wyeb || wyeb-gitAUR
    Browsers based on qt5-webkit
    -
    • OSPKit — Webkit based html browser for printing.
    +
    • OSPKit — Webkit based HTML browser for printing.
    http://osp.kitchen/tools/ospkit/ || ospkit-gitAUR
    • Otter Browser — Browser aiming to recreate classic Opera (12.x) UI using Qt5.
    https://otter-browser.org/ || otter-browser
    @@ -1436,7 +1436,7 @@

    Cloud storage servers
  • Seafile — An online file storage and collaboration tool with advanced support for file syncing, privacy protection and teamwork.
  • https://www.seafile.com/ || seafile-serverAUR

    Cloud synchronization clients

    -
    Tip: +
    Tip: @@ -7029,7 +7029,7 @@

    See also

  • Arch Linux Forums / LnF Awards 2012 - The best Light & Fast apps of 2012
  • Arch Linux Forums / most popular apps of 2013-2014
  • Arch Linux Forums / most popular apps of 2017+ (requires login)
  • - - \ No newline at end of file + diff --git a/fixtures/fragments/file1.md b/fixtures/fragments/file1.md index 9b90727db9..7f63b15890 100644 --- a/fixtures/fragments/file1.md +++ b/fixtures/fragments/file1.md @@ -28,7 +28,7 @@ Therefore we put the test into a code block for now to prevent false positives. [Link to explicit fragment](#explicit-fragment) -[To the html doc](file.html#a-word) +[To the HTML doc](file.html#a-word) ## Custom Fragments diff --git a/lychee-lib/src/extract/html/html5ever.rs b/lychee-lib/src/extract/html/html5ever.rs index bceed2ceb1..b9f6373fc3 100644 --- a/lychee-lib/src/extract/html/html5ever.rs +++ b/lychee-lib/src/extract/html/html5ever.rs @@ -124,7 +124,7 @@ impl LinkExtractor { } } - /// Extract all semantically known links from a given html attribute. + /// Extract all semantically known links from a given HTML attribute. #[allow(clippy::unnested_or_patterns)] pub(crate) fn extract_urls_from_elem_attr<'a>( attr_name: &str, diff --git a/lychee-lib/src/extract/html/html5gum.rs b/lychee-lib/src/extract/html/html5gum.rs index 9555beeb54..ee61e6439a 100644 --- a/lychee-lib/src/extract/html/html5gum.rs +++ b/lychee-lib/src/extract/html/html5gum.rs @@ -45,7 +45,7 @@ impl LinkExtractor { } } - /// Extract all semantically known links from a given html attribute. + /// Extract all semantically known links from a given HTML attribute. #[allow(clippy::unnested_or_patterns)] pub(crate) fn extract_urls_from_elem_attr<'a>( attr_name: &str, diff --git a/lychee-lib/src/extract/html/mod.rs b/lychee-lib/src/extract/html/mod.rs index ef02c06fc4..b990acfda4 100644 --- a/lychee-lib/src/extract/html/mod.rs +++ b/lychee-lib/src/extract/html/mod.rs @@ -1,4 +1,4 @@ -//! Extract links and fragments from html documents +//! Extract links and fragments from HTML documents pub(crate) mod html5ever; pub(crate) mod html5gum; mod srcset; From 0a54079d01ba9a4ff9850d8e46d3ba64743d2c8d Mon Sep 17 00:00:00 2001 From: Thomas Zahner Date: Mon, 29 Apr 2024 09:28:34 +0200 Subject: [PATCH 37/40] Bump to version 0.15.1 --- Cargo.lock | 4 ++-- Cargo.toml | 2 +- README.md | 2 +- lychee-bin/Cargo.toml | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9829e7e005..fd93bd9065 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2371,7 +2371,7 @@ dependencies = [ [[package]] name = "lychee" -version = "0.15.0" +version = "0.15.1" dependencies = [ "anyhow", "assert-json-diff", @@ -2416,7 +2416,7 @@ dependencies = [ [[package]] name = "lychee-lib" -version = "0.15.0" +version = "0.15.1" dependencies = [ "async-stream", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 8aa2c32515..3e34e95f73 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = ["lychee-bin", "lychee-lib", "examples/*", "benches"] resolver = "2" [workspace.package] -version = "0.15.0" +version = "0.15.1" [profile.release] debug = true diff --git a/README.md b/README.md index f6e397418f..5c2e3c057b 100644 --- a/README.md +++ b/README.md @@ -566,7 +566,7 @@ Lychee can also be used as a [pre-commit](https://pre-commit.com/) hook. # .pre-commit-config.yaml repos: - repo: https://github.com/lycheeverse/lychee.git - rev: 0.15.0 + rev: 0.15.1 hooks: - id: lychee # Optionally include additional CLI arguments diff --git a/lychee-bin/Cargo.toml b/lychee-bin/Cargo.toml index eec7fa0fae..800a297ecf 100644 --- a/lychee-bin/Cargo.toml +++ b/lychee-bin/Cargo.toml @@ -14,7 +14,7 @@ version.workspace = true [dependencies] # NOTE: We need to specify the version of lychee-lib here because crates.io # requires all dependencies to have a version number. -lychee-lib = { path = "../lychee-lib", version = "0.15.0", default-features = false } +lychee-lib = { path = "../lychee-lib", version = "0.15.1", default-features = false } anyhow = "1.0.82" assert-json-diff = "2.0.2" From 975901d47094dd2962716f153c2ddc455c8d0b53 Mon Sep 17 00:00:00 2001 From: Johannes Schindelin Date: Mon, 13 May 2024 19:07:02 +0200 Subject: [PATCH 38/40] Fix clippy errors (#1423) * Enclose Markdown links in brackets The current clippy version (v0.1.78) says "you should put bare URLs between `<`/`>` or make a proper Markdown link" and refers to https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown Signed-off-by: Johannes Schindelin * Enclose documentation item in backticks Clippy v0.1.78 complains about the IPv6 network mask, insisting that it is missing backticks. So backticks it gets. Signed-off-by: Johannes Schindelin * Avoid error claiming `Add(usize)` is dead code Clippy v0.1.78 identifies this as dead code. However, further down in the same file, there is clearly a user: impl Handler for Add { This might be yet another incarnation of https://github.com/rust-lang/rust/issues/56750 Let's just mark it as intentionally dead-code, even if this is untrue, to make clippy happy again. Signed-off-by: Johannes Schindelin --------- Signed-off-by: Johannes Schindelin --- README.md | 2 +- lychee-bin/src/options.rs | 2 +- lychee-lib/src/chain/mod.rs | 1 + lychee-lib/src/client.rs | 2 +- lychee-lib/src/filter/mod.rs | 2 +- lychee-lib/src/types/status.rs | 2 +- 6 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5c2e3c057b..51a0fd4aca 100644 --- a/README.md +++ b/README.md @@ -429,7 +429,7 @@ Options: [default: get] -b, --base - Base URL or website root directory to check relative URLs e.g. https://example.com or `/path/to/public` + Base URL or website root directory to check relative URLs e.g. or `/path/to/public` --basic-auth Basic authentication support. E.g. `http://example.com username:password` diff --git a/lychee-bin/src/options.rs b/lychee-bin/src/options.rs index ee3c1313fa..925023a576 100644 --- a/lychee-bin/src/options.rs +++ b/lychee-bin/src/options.rs @@ -350,7 +350,7 @@ separated list of accepted status codes. This example will accept 200, 201, pub(crate) method: String, /// Base URL or website root directory to check relative URLs - /// e.g. https://example.com or `/path/to/public` + /// e.g. or `/path/to/public` #[arg(short, long, value_parser= parse_base)] #[serde(default)] pub(crate) base: Option, diff --git a/lychee-lib/src/chain/mod.rs b/lychee-lib/src/chain/mod.rs index 92de75b68b..c6fbc08af0 100644 --- a/lychee-lib/src/chain/mod.rs +++ b/lychee-lib/src/chain/mod.rs @@ -188,6 +188,7 @@ mod test { }; use async_trait::async_trait; + #[allow(dead_code)] // work-around #[derive(Debug)] struct Add(usize); diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 77b132f700..42cfa4b730 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -269,7 +269,7 @@ pub struct ClientBuilder { /// Cookie store used for requests. /// - /// See https://docs.rs/reqwest/latest/reqwest/struct.ClientBuilder.html#method.cookie_store + /// See cookie_jar: Option>, /// Enable the checking of fragments in links. diff --git a/lychee-lib/src/filter/mod.rs b/lychee-lib/src/filter/mod.rs index f970cf299f..f8d6130716 100644 --- a/lychee-lib/src/filter/mod.rs +++ b/lychee-lib/src/filter/mod.rs @@ -124,7 +124,7 @@ pub struct Filter { /// Example: 169.254.0.0 pub exclude_link_local_ips: bool, /// For IPv4: 127.0.0.1/8 - /// For IPv6: ::1/128 + /// For IPv6: `::1/128` pub exclude_loopback_ips: bool, /// Example: octocat@github.com pub include_mail: bool, diff --git a/lychee-lib/src/types/status.rs b/lychee-lib/src/types/status.rs index bd838a0a87..b2ee128c77 100644 --- a/lychee-lib/src/types/status.rs +++ b/lychee-lib/src/types/status.rs @@ -36,7 +36,7 @@ pub enum Status { Excluded, /// The request type is currently not supported, /// for example when the URL scheme is `slack://`. - /// See https://github.com/lycheeverse/lychee/issues/199 + /// See Unsupported(ErrorKind), /// Cached request status from previous run Cached(CacheStatus), From 9e031b62563215be97afe91265520903382a6b19 Mon Sep 17 00:00:00 2001 From: ESP-Kirill <105578077+f-hollow@users.noreply.github.com> Date: Tue, 14 May 2024 01:12:56 +0800 Subject: [PATCH 39/40] Fix typo in pre-commit config in README (#1424) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 51a0fd4aca..0efc73cf2b 100644 --- a/README.md +++ b/README.md @@ -566,7 +566,7 @@ Lychee can also be used as a [pre-commit](https://pre-commit.com/) hook. # .pre-commit-config.yaml repos: - repo: https://github.com/lycheeverse/lychee.git - rev: 0.15.1 + rev: v0.15.1 hooks: - id: lychee # Optionally include additional CLI arguments From c3f7fe7ad4d88a403c5ef0280233200967863d67 Mon Sep 17 00:00:00 2001 From: n4n5 <56606507+Its-Just-Nans@users.noreply.github.com> Date: Sun, 19 May 2024 20:31:38 +0200 Subject: [PATCH 40/40] Exclude `tel` scheme from being checked (#1429) --- lychee-lib/src/client.rs | 17 ++++++++++++++++ lychee-lib/src/extract/html/html5ever.rs | 26 +++++++++++++++++++++++- lychee-lib/src/extract/html/html5gum.rs | 25 ++++++++++++++++++++++- lychee-lib/src/filter/mod.rs | 1 + lychee-lib/src/types/uri/valid.rs | 15 ++++++++++++++ 5 files changed, 82 insertions(+), 2 deletions(-) diff --git a/lychee-lib/src/client.rs b/lychee-lib/src/client.rs index 42cfa4b730..3369abf0b5 100644 --- a/lychee-lib/src/client.rs +++ b/lychee-lib/src/client.rs @@ -500,6 +500,7 @@ impl Client { let status = match uri.scheme() { _ if uri.is_file() => self.check_file(uri).await, _ if uri.is_mail() => self.check_mail(uri).await, + _ if uri.is_tel() => self.check_tel(uri).await, _ => self.check_website(uri, default_chain).await?, }; @@ -702,6 +703,14 @@ impl Client { pub async fn check_mail(&self, _uri: &Uri) -> Status { Status::Excluded } + + /// Check a tel + /// + /// This implementation simply excludes all tel. + #[allow(clippy::unused_async)] + pub async fn check_tel(&self, _uri: &Uri) -> Status { + Status::Excluded + } } // Check if the given `Url` would cause `reqwest` to panic. @@ -907,6 +916,14 @@ mod tests { })); } + #[tokio::test] + async fn test_include_tel() { + let client = ClientBuilder::builder().build().client().unwrap(); + assert!(client.is_excluded(&Uri { + url: "tel:1234567890".try_into().unwrap() + })); + } + #[tokio::test] async fn test_require_https() { let client = ClientBuilder::builder().build().client().unwrap(); diff --git a/lychee-lib/src/extract/html/html5ever.rs b/lychee-lib/src/extract/html/html5ever.rs index b9f6373fc3..1ee03b836f 100644 --- a/lychee-lib/src/extract/html/html5ever.rs +++ b/lychee-lib/src/extract/html/html5ever.rs @@ -89,9 +89,10 @@ impl TokenSink for LinkExtractor { // This ignores links like `` let is_email = is_email_link(url); let is_mailto = url.starts_with("mailto:"); + let is_phone = url.starts_with("tel:"); let is_href = attr.name.local.as_ref() == "href"; - !is_email || (is_mailto && is_href) + !is_email || (is_mailto && is_href) || (is_phone && is_href) }) .map(|url| RawUri { text: url.to_string(), @@ -318,6 +319,29 @@ mod tests { let uris = extract_html(input, false); assert_eq!(uris, expected); } + + #[test] + fn test_valid_tel() { + let input = r#" + + + + Test + + + + + "#; + + let expected = vec![RawUri { + text: "tel:1234567890".to_string(), + element: Some("a".to_string()), + attribute: Some("href".to_string()), + }]; + let uris = extract_html(input, false); + assert_eq!(uris, expected); + } + #[test] fn test_exclude_email_without_mailto() { let input = r#" diff --git a/lychee-lib/src/extract/html/html5gum.rs b/lychee-lib/src/extract/html/html5gum.rs index ee61e6439a..cf9d88f489 100644 --- a/lychee-lib/src/extract/html/html5gum.rs +++ b/lychee-lib/src/extract/html/html5gum.rs @@ -172,9 +172,10 @@ impl LinkExtractor { // This ignores links like `` let is_email = is_email_link(url); let is_mailto = url.starts_with("mailto:"); + let is_phone = url.starts_with("tel:"); let is_href = attr == "href"; - !is_email || (is_mailto && is_href) + !is_email || (is_mailto && is_href) || (is_phone && is_href) }) .map(|url| RawUri { text: url.to_string(), @@ -453,6 +454,28 @@ mod tests { assert_eq!(uris, expected); } + #[test] + fn test_valid_tel() { + let input = r#" + + + + Test + + + + + "#; + + let expected = vec![RawUri { + text: "tel:1234567890".to_string(), + element: Some("a".to_string()), + attribute: Some("href".to_string()), + }]; + let uris = extract_html(input, false); + assert_eq!(uris, expected); + } + #[test] fn test_valid_email() { let input = r#" diff --git a/lychee-lib/src/filter/mod.rs b/lychee-lib/src/filter/mod.rs index f8d6130716..a6cda08656 100644 --- a/lychee-lib/src/filter/mod.rs +++ b/lychee-lib/src/filter/mod.rs @@ -214,6 +214,7 @@ impl Filter { || self.is_host_excluded(uri) || self.is_ip_excluded(uri) || self.is_mail_excluded(uri) + || uri.is_tel() || is_example_domain(uri) || is_unsupported_domain(uri) { diff --git a/lychee-lib/src/types/uri/valid.rs b/lychee-lib/src/types/uri/valid.rs index 87954b4e7a..9e9bb72a72 100644 --- a/lychee-lib/src/types/uri/valid.rs +++ b/lychee-lib/src/types/uri/valid.rs @@ -96,6 +96,13 @@ impl Uri { self.scheme() == "mailto" } + #[inline] + #[must_use] + /// Check if the URI is a tel + pub fn is_tel(&self) -> bool { + self.scheme() == "tel" + } + #[inline] #[must_use] /// Check if the URI is a file @@ -325,6 +332,14 @@ mod tests { ); } + #[test] + fn test_uri_tel() { + assert_eq!( + Uri::try_from("tel:1234567890"), + Ok(Uri::try_from("tel:1234567890").unwrap()) + ); + } + #[test] fn test_uri_host_ip_v4() { assert_eq!(