diff --git a/.woodpecker.yml b/.woodpecker.yml index b82dd94..f1dd4a0 100644 --- a/.woodpecker.yml +++ b/.woodpecker.yml @@ -1,5 +1,5 @@ variables: - - &rust_image "rust:1.78-bullseye" + - &rust_image "rust:1.84-bullseye" steps: cargo_fmt: diff --git a/Cargo.toml b/Cargo.toml index 58d0e97..46d18ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,6 @@ tracing = "0.1.41" base64 = "0.22.1" rand = "0.8.5" rsa = "0.9.7" -once_cell = "1.20.2" http = "1.2.0" sha2 = { version = "0.10.8", features = ["oid"] } thiserror = "2.0.11" @@ -81,7 +80,6 @@ diesel = { version = "2.2.6", features = [ ], default-features = false, optional = true } futures = "0.3.31" moka = { version = "0.12.10", features = ["future"] } - # Actix-web actix-web = { version = "4.9.0", default-features = false, optional = true } http02 = { package = "http", version = "0.2.12", optional = true } @@ -93,9 +91,9 @@ tower = { version = "0.5.2", optional = true } [dev-dependencies] anyhow = "1.0.95" axum = { version = "0.8.2", features = ["macros"] } -axum-extra = { version = "0.9.3", features = ["typed-header"] } -env_logger = "0.11.3" -tokio = { version = "1.38.0", features = ["full"] } +axum-extra = { version = "0.10.0", features = ["typed-header"] } +env_logger = "0.11.6" +tokio = { version = "1.43.0", features = ["full"] } [profile.dev] strip = "symbols" diff --git a/examples/local_federation/axum/http.rs b/examples/local_federation/axum/http.rs index dd9d002..e010a73 100644 --- a/examples/local_federation/axum/http.rs +++ b/examples/local_federation/axum/http.rs @@ -30,8 +30,8 @@ pub fn listen(config: &FederationConfig) -> Result<(), Error> { info!("Listening with axum on {hostname}"); let config = config.clone(); let app = Router::new() - .route("/:user/inbox", post(http_post_user_inbox)) - .route("/:user", get(http_get_user)) + .route("/{user}/inbox", post(http_post_user_inbox)) + .route("/{user}", get(http_get_user)) .route("/.well-known/webfinger", get(webfinger)) .layer(FederationMiddleware::new(config)); diff --git a/src/activity_queue.rs b/src/activity_queue.rs index 5025e49..c666e7a 100644 --- a/src/activity_queue.rs +++ b/src/activity_queue.rs @@ -361,10 +361,10 @@ impl ActivityQueue { pub(crate) async fn shutdown(self, wait_for_retries: bool) -> Result, Error> { drop(self.sender); - self.sender_task.await.map_err(|_| Error::NotFound)?; + self.sender_task.await?; if wait_for_retries { - self.retry_sender_task.await.map_err(|_| Error::NotFound)?; + self.retry_sender_task.await?; } Ok(self.stats) diff --git a/src/actix_web/inbox.rs b/src/actix_web/inbox.rs index 9bce475..0912e72 100644 --- a/src/actix_web/inbox.rs +++ b/src/actix_web/inbox.rs @@ -122,14 +122,14 @@ mod test { let (_, _, config) = setup_receive_test().await; let actor = Url::parse("http://ds9.lemmy.ml/u/lemmy_alpha").unwrap(); - let id = "http://localhost:123/1"; + let activity_id = "http://localhost:123/1"; let activity = json!({ "actor": actor.as_str(), "to": ["https://www.w3.org/ns/activitystreams#Public"], "object": "http://ds9.lemmy.ml/post/1", "cc": ["http://enterprise.lemmy.ml/c/main"], "type": "Delete", - "id": id + "id": activity_id } ); let body: Bytes = serde_json::to_vec(&activity).unwrap().into(); @@ -144,8 +144,8 @@ mod test { .await; match res { - Err(Error::ParseReceivedActivity(_, url)) => { - assert_eq!(id, url.expect("has url").as_str()); + Err(Error::ParseReceivedActivity { err: _, id }) => { + assert_eq!(activity_id, id.expect("has url").as_str()); } _ => unreachable!(), } diff --git a/src/axum/inbox.rs b/src/axum/inbox.rs index eb2a4d9..84567e0 100644 --- a/src/axum/inbox.rs +++ b/src/axum/inbox.rs @@ -9,7 +9,6 @@ use crate::{ parse_received_activity, traits::{ActivityHandler, Actor, Object}, }; -use async_trait::async_trait; use axum::{ body::Body, extract::FromRequest, @@ -58,7 +57,6 @@ pub struct ActivityData { body: Vec, } -#[async_trait] impl FromRequest for ActivityData where S: Send + Sync, diff --git a/src/axum/middleware.rs b/src/axum/middleware.rs index be76ab3..2f8b0e9 100644 --- a/src/axum/middleware.rs +++ b/src/axum/middleware.rs @@ -1,5 +1,4 @@ use crate::config::{Data, FederationConfig, FederationMiddleware}; -use async_trait::async_trait; use axum::{body::Body, extract::FromRequestParts, http::Request, response::Response}; use http::{request::Parts, StatusCode}; use std::task::{Context, Poll}; @@ -44,7 +43,6 @@ where } } -#[async_trait] impl FromRequestParts for Data where S: Send + Sync, diff --git a/src/error.rs b/src/error.rs index fa4a71b..06c8085 100644 --- a/src/error.rs +++ b/src/error.rs @@ -44,11 +44,16 @@ pub enum Error { #[error("Failed to parse object {1} with content {2}: {0}")] ParseFetchedObject(serde_json::Error, Url, String), /// Failed to parse an activity received from another instance - #[error("Failed to parse incoming activity {}: {0}", match .1 { + #[error("Failed to parse incoming activity {}: {0}", match .id { Some(t) => format!("with id {t}"), None => String::new(), })] - ParseReceivedActivity(serde_json::Error, Option), + ParseReceivedActivity { + /// The parse error + err: serde_json::Error, + /// ID of the Activitypub object which caused this error + id: Option, + }, /// Reqwest Middleware Error #[error(transparent)] ReqwestMiddleware(#[from] reqwest_middleware::Error), @@ -101,42 +106,6 @@ impl From for Error { } } -impl From for Error { - fn from(value: url::ParseError) -> Self { - Error::UrlParse(value) - } -} - -impl From for Error { - fn from(value: WebFingerError) -> Self { - Error::WebfingerResolveFailed(value) - } -} - -impl From for Error { - fn from(value: FromUtf8Error) -> Self { - Error::Utf8(value) - } -} - -impl From for Error { - fn from(value: SignError) -> Self { - Error::SignError(value) - } -} - -impl From for Error { - fn from(value: reqwest::Error) -> Self { - Error::Reqwest(value) - } -} - -impl From for Error { - fn from(value: reqwest_middleware::Error) -> Self { - Error::ReqwestMiddleware(value) - } -} - impl PartialEq for Error { fn eq(&self, other: &Self) -> bool { std::mem::discriminant(self) == std::mem::discriminant(other) diff --git a/src/fetch/collection_id.rs b/src/fetch/collection_id.rs index 8c796f4..6aa9881 100644 --- a/src/fetch/collection_id.rs +++ b/src/fetch/collection_id.rs @@ -104,7 +104,7 @@ where } #[cfg(feature = "diesel")] -const _IMPL_DIESEL_NEW_TYPE_FOR_COLLECTION_ID: () = { +const _: () = { use diesel::{ backend::Backend, deserialize::{FromSql, FromStaticSqlRow}, diff --git a/src/fetch/object_id.rs b/src/fetch/object_id.rs index b6aafc6..136ec38 100644 --- a/src/fetch/object_id.rs +++ b/src/fetch/object_id.rs @@ -272,7 +272,7 @@ where } #[cfg(feature = "diesel")] -const _IMPL_DIESEL_NEW_TYPE_FOR_OBJECT_ID: () = { +const _: () = { use diesel::{ backend::Backend, deserialize::{FromSql, FromStaticSqlRow}, diff --git a/src/fetch/webfinger.rs b/src/fetch/webfinger.rs index f66a181..9385455 100644 --- a/src/fetch/webfinger.rs +++ b/src/fetch/webfinger.rs @@ -7,10 +7,9 @@ use crate::{ }; use http::HeaderValue; use itertools::Itertools; -use once_cell::sync::Lazy; use regex::Regex; use serde::{Deserialize, Serialize}; -use std::{collections::HashMap, fmt::Display}; +use std::{collections::HashMap, fmt::Display, sync::LazyLock}; use tracing::debug; use url::Url; @@ -120,20 +119,18 @@ pub fn extract_webfinger_name<'i, T>(query: &'i str, data: &Data) -> Result<& where T: Clone, { - static WEBFINGER_REGEX: Lazy = - Lazy::new(|| Regex::new(r"^acct:([\p{L}0-9_\.\-]+)@(.*)$").expect("compile regex")); + static WEBFINGER_REGEX: LazyLock = + LazyLock::new(|| Regex::new(r"^acct:([\p{L}0-9_\.\-]+)@(.*)$").expect("compile regex")); // Regex to extract usernames from webfinger query. Supports different alphabets using `\p{L}`. // TODO: This should use a URL parser let captures = WEBFINGER_REGEX .captures(query) - .ok_or(WebFingerError::WrongFormat.into_crate_error())?; + .ok_or(WebFingerError::WrongFormat)?; - let account_name = captures - .get(1) - .ok_or(WebFingerError::WrongFormat.into_crate_error())?; + let account_name = captures.get(1).ok_or(WebFingerError::WrongFormat)?; if captures.get(2).map(|m| m.as_str()) != Some(data.domain()) { - return Err(WebFingerError::WrongDomain.into_crate_error()); + return Err(WebFingerError::WrongDomain.into()); } Ok(account_name.as_str()) } diff --git a/src/http_signatures.rs b/src/http_signatures.rs index f30e75c..95d0a12 100644 --- a/src/http_signatures.rs +++ b/src/http_signatures.rs @@ -19,7 +19,6 @@ use http_signature_normalization_reqwest::{ prelude::{Config, SignExt}, DefaultSpawner, }; -use once_cell::sync::Lazy; use reqwest::Request; use reqwest_middleware::RequestBuilder; use rsa::{ @@ -30,7 +29,7 @@ use rsa::{ }; use serde::Deserialize; use sha2::{Digest, Sha256}; -use std::{collections::BTreeMap, fmt::Debug, time::Duration}; +use std::{collections::BTreeMap, fmt::Debug, sync::LazyLock, time::Duration}; use tracing::debug; use url::Url; @@ -82,9 +81,9 @@ pub(crate) async fn sign_request( private_key: RsaPrivateKey, http_signature_compat: bool, ) -> Result { - static CONFIG: Lazy> = - Lazy::new(|| Config::new().set_expiration(EXPIRES_AFTER)); - static CONFIG_COMPAT: Lazy = Lazy::new(|| { + static CONFIG: LazyLock> = + LazyLock::new(|| Config::new().set_expiration(EXPIRES_AFTER)); + static CONFIG_COMPAT: LazyLock = LazyLock::new(|| { Config::new() .mastodon_compat() .set_expiration(EXPIRES_AFTER) @@ -185,7 +184,7 @@ fn verify_signature_inner( uri: &Uri, public_key: &str, ) -> Result<(), Error> { - static CONFIG: Lazy = Lazy::new(|| { + static CONFIG: LazyLock = LazyLock::new(|| { http_signature_normalization::Config::new() .set_expiration(EXPIRES_AFTER) .require_digest() @@ -288,9 +287,10 @@ pub mod test { use rsa::{pkcs1::DecodeRsaPrivateKey, pkcs8::DecodePrivateKey}; use std::str::FromStr; - static ACTOR_ID: Lazy = Lazy::new(|| Url::parse("https://example.com/u/alice").unwrap()); - static INBOX_URL: Lazy = - Lazy::new(|| Url::parse("https://example.com/u/alice/inbox").unwrap()); + static ACTOR_ID: LazyLock = + LazyLock::new(|| Url::parse("https://example.com/u/alice").unwrap()); + static INBOX_URL: LazyLock = + LazyLock::new(|| Url::parse("https://example.com/u/alice/inbox").unwrap()); #[tokio::test] async fn test_sign() { diff --git a/src/lib.rs b/src/lib.rs index 0a44fc9..b7fe013 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,10 +52,10 @@ where ::Error: From, Datatype: Clone, { - let activity: Activity = serde_json::from_slice(body).map_err(|e| { + let activity: Activity = serde_json::from_slice(body).map_err(|err| { // Attempt to include activity id in error message let id = extract_id(body).ok(); - Error::ParseReceivedActivity(e, id) + Error::ParseReceivedActivity { err, id } })?; data.config.verify_url_and_domain(&activity).await?; let actor = ObjectId::::from(activity.actor().clone()) diff --git a/src/traits.rs b/src/traits.rs index 9976bda..7ca20d5 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -346,8 +346,8 @@ pub mod tests { protocol::verification::verify_domains_match, }; use activitystreams_kinds::{activity::FollowType, actor::PersonType}; - use once_cell::sync::Lazy; use serde::{Deserialize, Serialize}; + use std::sync::LazyLock; #[derive(Clone)] pub struct DbConnection; @@ -389,9 +389,10 @@ pub mod tests { pub local: bool, } - pub static DB_USER_KEYPAIR: Lazy = Lazy::new(|| generate_actor_keypair().unwrap()); + pub static DB_USER_KEYPAIR: LazyLock = + LazyLock::new(|| generate_actor_keypair().unwrap()); - pub static DB_USER: Lazy = Lazy::new(|| DbUser { + pub static DB_USER: LazyLock = LazyLock::new(|| DbUser { name: String::new(), federation_id: "https://localhost/123".parse().unwrap(), inbox: "https://localhost/123/inbox".parse().unwrap(),