Use OriginalUri for Axum ActivityData (#141)

* Use OriginalUri for axum ActivityData

When the inbox path is under a nested `Router`, the received request has a URI
with the common prefix stripped. This causes incoming signatures to be considered
invalid since the path is different (see https://github.com/LemmyNet/activitypub-federation-rust/issues/107#issuecomment-2767428107)

This commit uses `OriginalUri` for URI extraction instead, which will retrieve
the full URI regardless of router nesting

* Use router nesting for local_federation

With 8c787f5, router nesting is supported correctly in axum

* Fix docs typo (#143)

---------

Co-authored-by: Zami <szgie@proton.me>
Co-authored-by: Felix Ableitner <me@nutomic.com>
This commit is contained in:
Kevin Kuriakose 2025-06-02 14:26:25 +05:30 committed by GitHub
parent 80dce32279
commit 7994df3706
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 14 additions and 6 deletions

View file

@ -85,6 +85,7 @@ http02 = { package = "http", version = "0.2.12", optional = true }
# Axum # Axum
axum = { version = "0.8.4", features = [ axum = { version = "0.8.4", features = [
"json", "json",
"original-uri",
], default-features = false, optional = true } ], default-features = false, optional = true }
tower = { version = "0.5.2", optional = true } tower = { version = "0.5.2", optional = true }

View file

@ -29,6 +29,7 @@ pub fn listen(config: &FederationConfig<DatabaseHandle>) -> Result<(), Error> {
let hostname = config.domain(); let hostname = config.domain();
info!("Listening with axum on {hostname}"); info!("Listening with axum on {hostname}");
let config = config.clone(); let config = config.clone();
let app = Router::new() let app = Router::new()
.route("/{user}/inbox", post(http_post_user_inbox)) .route("/{user}/inbox", post(http_post_user_inbox))
.route("/{user}", get(http_get_user)) .route("/{user}", get(http_get_user))

View file

@ -11,11 +11,11 @@ use crate::{
}; };
use axum::{ use axum::{
body::Body, body::Body,
extract::FromRequest, extract::{FromRequest, FromRequestParts, OriginalUri},
http::{Request, StatusCode}, http::{Request, StatusCode},
response::{IntoResponse, Response}, response::{IntoResponse, Response},
}; };
use http::{HeaderMap, Method, Uri}; use http::{HeaderMap, Method};
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use tracing::debug; use tracing::debug;
@ -53,7 +53,7 @@ where
pub struct ActivityData { pub struct ActivityData {
headers: HeaderMap, headers: HeaderMap,
method: Method, method: Method,
uri: Uri, uri: OriginalUri,
body: Vec<u8>, body: Vec<u8>,
} }
@ -63,8 +63,14 @@ where
{ {
type Rejection = Response; type Rejection = Response;
async fn from_request(req: Request<Body>, _state: &S) -> Result<Self, Self::Rejection> { async fn from_request(req: Request<Body>, state: &S) -> Result<Self, Self::Rejection> {
let (parts, body) = req.into_parts(); let (mut parts, body) = req.into_parts();
// take the full URI to handle nested routers
// OriginalUri::from_request_parts has an Infallible error type
let uri = OriginalUri::from_request_parts(&mut parts, state)
.await
.expect("infallible");
// this wont work if the body is an long running stream // this wont work if the body is an long running stream
let bytes = axum::body::to_bytes(body, usize::MAX) let bytes = axum::body::to_bytes(body, usize::MAX)
@ -74,7 +80,7 @@ where
Ok(Self { Ok(Self {
headers: parts.headers, headers: parts.headers,
method: parts.method, method: parts.method,
uri: parts.uri, uri,
body: bytes.to_vec(), body: bytes.to_vec(),
}) })
} }