webfinger_rs/
actix.rs

1use std::{future::Future, pin::Pin};
2
3use actix_web::{dev::Payload, web::Json, FromRequest, HttpRequest, HttpResponse, Responder};
4use tracing::trace;
5
6use crate::{WebFingerRequest, WebFingerResponse};
7
8impl Responder for WebFingerResponse {
9    type Body = <Json<WebFingerResponse> as Responder>::Body;
10
11    fn respond_to(self, _request: &HttpRequest) -> HttpResponse<Self::Body> {
12        Json(self).respond_to(_request)
13    }
14}
15
16impl FromRequest for WebFingerRequest {
17    type Error = actix_web::Error;
18
19    type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>> + Send>>;
20
21    fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future {
22        trace!(?req, "extracting WebFingerRequest from request");
23        let host = req
24            .uri()
25            .host()
26            .or_else(|| req.headers().get("host").and_then(|h| h.to_str().ok()))
27            .map(|h| h.to_string());
28        let resource = req
29            .query_string()
30            .split('&')
31            .find_map(|param| param.split_once('=').filter(|(key, _)| *key == "resource"))
32            .map(|(_, value)| value.to_string());
33        let rels_from_query: Vec<_> = req
34            .query_string()
35            .split('&')
36            .filter_map(|param| param.split_once('=').filter(|(key, _)| *key == "rel"))
37            .map(|(_, value)| value.to_string())
38            .collect();
39        Box::pin(async move {
40            let resource = resource.ok_or(actix_web::error::ErrorBadRequest("missing resource"))?;
41            let host = host.ok_or(actix_web::error::ErrorBadRequest("missing host"))?;
42            let mut request_builder = WebFingerRequest::builder(resource).unwrap().host(host);
43            for rel in rels_from_query {
44                request_builder = request_builder.rel(rel);
45            }
46            Ok(request_builder.build())
47        })
48    }
49}