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}