webfinger_rs/
actix.rs

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