Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: LemmyNet/activitypub-federation-rust
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 0.5.2
Choose a base ref
...
head repository: LemmyNet/activitypub-federation-rust
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 0.5.3
Choose a head ref
  • 4 commits
  • 13 files changed
  • 1 contributor

Commits on Apr 9, 2024

  1. Retry activity send in case of timeout or rate limit (#102)

    Nutomic authored Apr 9, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    5402bc9 View commit details
  2. Allow fetching from local domain in case it redirects to remote (#104)

    * Allow fetching from local domain in case it redirects to remote
    
    * clippy
    
    * fix lemmy tests
    Nutomic authored Apr 9, 2024
    Copy the full SHA
    a2ac97d View commit details
  3. Avoid running ci checks twice (#105)

    * Avoid running ci checks twice
    
    * upgrade rust
    
    * move clippy config to cargo.toml
    Nutomic authored Apr 9, 2024
    Copy the full SHA
    7def01a View commit details
  4. Version 0.5.3

    Nutomic committed Apr 9, 2024
    Copy the full SHA
    779313a View commit details
42 changes: 22 additions & 20 deletions .woodpecker.yml
Original file line number Diff line number Diff line change
@@ -1,54 +1,56 @@
pipeline:
variables:
- &rust_image "rust:1.77-bullseye"

steps:
cargo_fmt:
image: rustdocker/rust:nightly
commands:
- /root/.cargo/bin/cargo fmt -- --check

cargo_check:
image: rust:1.70-bullseye
environment:
CARGO_HOME: .cargo
commands:
- cargo check --all-features --all-targets
when:
- event: pull_request

cargo_clippy:
image: rust:1.70-bullseye
image: *rust_image
environment:
CARGO_HOME: .cargo
commands:
- rustup component add clippy
- cargo clippy --all-targets --all-features --
-D warnings -D deprecated -D clippy::perf -D clippy::complexity
-D clippy::dbg_macro -D clippy::inefficient_to_string
-D clippy::items-after-statements -D clippy::implicit_clone
-D clippy::wildcard_imports -D clippy::cast_lossless
-D clippy::manual_string_new -D clippy::redundant_closure_for_method_calls
- cargo clippy --all-features -- -D clippy::unwrap_used
- cargo clippy --all-targets --all-features
when:
- event: pull_request

cargo_test:
image: rust:1.70-bullseye
image: *rust_image
environment:
CARGO_HOME: .cargo
commands:
- cargo test --all-features --no-fail-fast
when:
- event: pull_request

cargo_doc:
image: rust:1.70-bullseye
image: *rust_image
environment:
CARGO_HOME: .cargo
commands:
- cargo doc --all-features
when:
- event: pull_request

cargo_run_actix_example:
image: rust:1.70-bullseye
image: *rust_image
environment:
CARGO_HOME: .cargo
commands:
- cargo run --example local_federation actix-web
when:
- event: pull_request

cargo_run_axum_example:
image: rust:1.70-bullseye
image: *rust_image
environment:
CARGO_HOME: .cargo
commands:
- cargo run --example local_federation axum
when:
- event: pull_request
19 changes: 18 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "activitypub_federation"
version = "0.5.2"
version = "0.5.3"
edition = "2021"
description = "High-level Activitypub framework"
keywords = ["activitypub", "activitystreams", "federation", "fediverse"]
@@ -14,6 +14,23 @@ actix-web = ["dep:actix-web"]
axum = ["dep:axum", "dep:tower", "dep:hyper", "dep:http-body-util"]
diesel = ["dep:diesel"]

[lints.rust]
warnings = "deny"
deprecated = "deny"

[lints.clippy]
perf = "deny"
complexity = "deny"
dbg_macro = "deny"
inefficient_to_string = "deny"
items-after-statements = "deny"
implicit_clone = "deny"
wildcard_imports = "deny"
cast_lossless = "deny"
manual_string_new = "deny"
redundant_closure_for_method_calls = "deny"
unwrap_used = "deny"

[dependencies]
chrono = { version = "0.4.34", features = ["clock"], default-features = false }
serde = { version = "1.0.197", features = ["derive"] }
2 changes: 2 additions & 0 deletions examples/live_federation/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]

use crate::{
database::Database,
http::{http_get_user, http_post_user_inbox, webfinger},
1 change: 1 addition & 0 deletions examples/local_federation/instance.rs
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ pub async fn new_instance(
.domain(hostname)
.signed_fetch_actor(&system_user)
.app_data(database)
.url_verifier(Box::new(MyUrlVerifier()))
.debug(true)
.build()
.await?;
2 changes: 2 additions & 0 deletions examples/local_federation/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#![allow(clippy::unwrap_used)]

use crate::{
instance::{listen, new_instance, Webserver},
objects::post::DbPost,
1 change: 1 addition & 0 deletions src/activity_queue.rs
Original file line number Diff line number Diff line change
@@ -416,6 +416,7 @@ async fn retry<T, E: Display + Debug, F: Future<Output = Result<T, E>>, A: FnMut
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::http_signatures::generate_actor_keypair;
78 changes: 67 additions & 11 deletions src/activity_sending.rs
Original file line number Diff line number Diff line change
@@ -12,14 +12,17 @@ use crate::{
};
use bytes::Bytes;
use futures::StreamExt;
use http::StatusCode;
use httpdate::fmt_http_date;
use itertools::Itertools;
use openssl::pkey::{PKey, Private};
use reqwest::header::{HeaderMap, HeaderName, HeaderValue};
use reqwest::{
header::{HeaderMap, HeaderName, HeaderValue},
Response,
};
use reqwest_middleware::ClientWithMiddleware;
use serde::Serialize;
use std::{
self,
fmt::{Debug, Display},
time::{Duration, SystemTime},
};
@@ -90,20 +93,30 @@ impl SendActivityTask {
)
.await?;
let response = client.execute(request).await?;
self.handle_response(response).await
}

match response {
o if o.status().is_success() => {
/// Based on the HTTP status code determines if an activity was delivered successfully. In that case
/// Ok is returned. Otherwise it returns Err and the activity send should be retried later.
///
/// Equivalent code in mastodon: https://github.com/mastodon/mastodon/blob/v4.2.8/app/helpers/jsonld_helper.rb#L215-L217
async fn handle_response(&self, response: Response) -> Result<(), Error> {
match response.status() {
status if status.is_success() => {
debug!("Activity {self} delivered successfully");
Ok(())
}
o if o.status().is_client_error() => {
let text = o.text_limited().await?;
status
if status.is_client_error()
&& status != StatusCode::REQUEST_TIMEOUT
&& status != StatusCode::TOO_MANY_REQUESTS =>
{
let text = response.text_limited().await?;
debug!("Activity {self} was rejected, aborting: {text}");
Ok(())
}
o => {
let status = o.status();
let text = o.text_limited().await?;
status => {
let text = response.text_limited().await?;

Err(Error::Other(format!(
"Activity {self} failure with status {status}: {text}",
@@ -211,11 +224,10 @@ pub(crate) fn generate_request_headers(inbox_url: &Url) -> HeaderMap {
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::{config::FederationConfig, http_signatures::generate_actor_keypair};
use bytes::Bytes;
use http::StatusCode;
use std::{
sync::{atomic::AtomicUsize, Arc},
time::Instant,
@@ -289,4 +301,48 @@ mod tests {
info!("Queue Sent: {:?}", start.elapsed());
Ok(())
}

#[tokio::test]
async fn test_handle_response() {
let keypair = generate_actor_keypair().unwrap();
let message = SendActivityTask {
actor_id: "http://localhost:8001".parse().unwrap(),
activity_id: "http://localhost:8001/activity".parse().unwrap(),
activity: "{}".into(),
inbox: "http://localhost:8001".parse().unwrap(),
private_key: keypair.private_key().unwrap(),
http_signature_compat: true,
};

let res = |status| {
http::Response::builder()
.status(status)
.body(vec![])
.unwrap()
.into()
};

assert!(message.handle_response(res(StatusCode::OK)).await.is_ok());
assert!(message
.handle_response(res(StatusCode::BAD_REQUEST))
.await
.is_ok());

assert!(message
.handle_response(res(StatusCode::MOVED_PERMANENTLY))
.await
.is_err());
assert!(message
.handle_response(res(StatusCode::REQUEST_TIMEOUT))
.await
.is_err());
assert!(message
.handle_response(res(StatusCode::TOO_MANY_REQUESTS))
.await
.is_err());
assert!(message
.handle_response(res(StatusCode::INTERNAL_SERVER_ERROR))
.await
.is_err());
}
}
1 change: 1 addition & 0 deletions src/actix_web/inbox.rs
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ where
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod test {
use super::*;
use crate::{
11 changes: 8 additions & 3 deletions src/fetch/mod.rs
Original file line number Diff line number Diff line change
@@ -69,10 +69,17 @@ pub async fn fetch_object_http<T: Clone, Kind: DeserializeOwned>(
return Err(Error::FetchInvalidContentType(res.url));
}

// Ensure id field matches final url
// Ensure id field matches final url after redirect
if res.object_id.as_ref() != Some(&res.url) {
return Err(Error::FetchWrongId(res.url));
}

// Dont allow fetching local object. Only check this after the request as a local url
// may redirect to a remote object.
if data.config.is_local_url(&res.url) {
return Err(Error::NotFound);
}

Ok(res)
}

@@ -84,8 +91,6 @@ async fn fetch_object_http_with_accept<T: Clone, Kind: DeserializeOwned>(
content_type: &HeaderValue,
) -> Result<FetchObjectResponse<Kind>, Error> {
let config = &data.config;
// dont fetch local objects this way
debug_assert!(url.domain() != Some(&config.domain));
config.verify_url_valid(url).await?;
info!("Fetching remote object {}", url.to_string());

15 changes: 5 additions & 10 deletions src/fetch/object_id.rs
Original file line number Diff line number Diff line change
@@ -88,19 +88,13 @@ where
<Kind as Object>::Error: From<Error>,
{
let db_object = self.dereference_from_db(data).await?;
// if its a local object, only fetch it from the database and not over http
if data.config.is_local_url(&self.0) {
return match db_object {
None => Err(Error::NotFound.into()),
Some(o) => Ok(o),
};
}

// object found in database
if let Some(object) = db_object {
// object is old and should be refetched
if let Some(last_refreshed_at) = object.last_refreshed_at() {
if should_refetch_object(last_refreshed_at) {
let is_local = data.config.is_local_url(&self.0);
if !is_local && should_refetch_object(last_refreshed_at) {
// object is outdated and should be refetched
return self.dereference_from_http(data, Some(object)).await;
}
}
@@ -345,9 +339,10 @@ const _IMPL_DIESEL_NEW_TYPE_FOR_OBJECT_ID: () = {
};

#[cfg(test)]
#[allow(clippy::unwrap_used)]
pub mod tests {
use super::*;
use crate::{fetch::object_id::should_refetch_object, traits::tests::DbUser};
use crate::traits::tests::DbUser;

#[test]
fn test_deserialize() {
1 change: 1 addition & 0 deletions src/fetch/webfinger.rs
Original file line number Diff line number Diff line change
@@ -245,6 +245,7 @@ pub struct WebfingerLink {
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::{
1 change: 1 addition & 0 deletions src/http_signatures.rs
Original file line number Diff line number Diff line change
@@ -275,6 +275,7 @@ pub(crate) fn verify_body_hash(
}

#[cfg(test)]
#[allow(clippy::unwrap_used)]
pub mod test {
use super::*;
use crate::activity_sending::generate_request_headers;
2 changes: 1 addition & 1 deletion src/traits.rs
Original file line number Diff line number Diff line change
@@ -343,7 +343,7 @@ pub mod tests {
error::Error,
fetch::object_id::ObjectId,
http_signatures::{generate_actor_keypair, Keypair},
protocol::{public_key::PublicKey, verification::verify_domains_match},
protocol::verification::verify_domains_match,
};
use activitystreams_kinds::{activity::FollowType, actor::PersonType};
use once_cell::sync::Lazy;