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}