1 use crate::{check_is_apub_id_valid, APUB_JSON_CONTENT_TYPE};
3 use lemmy_utils::{request::retry, LemmyError};
4 use reqwest::{Client, StatusCode};
5 use serde::Deserialize;
6 use std::time::Duration;
10 /// Maximum number of HTTP requests allowed to handle a single incoming activity (or a single object
11 /// fetch through the search).
13 /// Tests are passing with a value of 5, so 10 should be safe for production.
14 static MAX_REQUEST_NUMBER: i32 = 10;
16 #[derive(Debug, Error)]
17 pub(in crate::fetcher) struct FetchError {
18 pub inner: anyhow::Error,
19 pub status_code: Option<StatusCode>,
22 impl From<LemmyError> for FetchError {
23 fn from(t: LemmyError) -> Self {
31 impl From<reqwest::Error> for FetchError {
32 fn from(t: reqwest::Error) -> Self {
33 let status = t.status();
41 impl std::fmt::Display for FetchError {
42 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
43 std::fmt::Display::fmt(&self, f)
47 /// Fetch any type of ActivityPub object, handling things like HTTP headers, deserialisation,
49 pub(in crate::fetcher) async fn fetch_remote_object<Response>(
52 recursion_counter: &mut i32,
53 ) -> Result<Response, FetchError>
55 Response: for<'de> Deserialize<'de> + std::fmt::Debug,
57 *recursion_counter += 1;
58 if *recursion_counter > MAX_REQUEST_NUMBER {
59 return Err(LemmyError::from(anyhow!("Maximum recursion depth reached")).into());
61 check_is_apub_id_valid(&url)?;
63 let timeout = Duration::from_secs(60);
68 .header("Accept", APUB_JSON_CONTENT_TYPE)
74 if res.status() == StatusCode::GONE {
75 return Err(FetchError {
76 inner: anyhow!("Remote object {} was deleted", url),
77 status_code: Some(res.status()),