actix/
actix.rs

1use std::net::{Ipv4Addr, SocketAddr};
2
3use actix_web::{App, HttpServer, get};
4use color_eyre::Result;
5use color_eyre::eyre::{Context, eyre};
6use rustls::ServerConfig;
7use tracing::info;
8use tracing::level_filters::LevelFilter;
9use webfinger_rs::{Link, Rel, WELL_KNOWN_PATH, WebFingerRequest, WebFingerResponse};
10
11const SUBJECT: &str = "acct:carol@localhost";
12
13#[actix_web::main]
14async fn main() -> Result<()> {
15    color_eyre::install()?;
16    let env_filter = tracing_subscriber::EnvFilter::builder()
17        .with_default_directive(LevelFilter::INFO.into())
18        .from_env_lossy();
19    tracing_subscriber::fmt().with_env_filter(env_filter).init();
20
21    let addrs = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 3000);
22    let config = tls_config()?;
23    let server =
24        HttpServer::new(|| App::new().service(webfinger)).bind_rustls_0_23(addrs, config)?;
25    info!("Listening at https://{addrs}{WELL_KNOWN_PATH}?resource={SUBJECT}");
26    server.run().await?;
27    Ok(())
28}
29
30fn tls_config() -> Result<ServerConfig> {
31    let self_signed_cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])
32        .wrap_err("failed to generate self signed certificat for localhost")?;
33    let cert_chain = self_signed_cert.cert.into();
34    let key_der = self_signed_cert
35        .key_pair
36        .serialize_der()
37        .try_into()
38        .map_err(|s: &str| eyre!(s))?;
39    // self_signed_cert.cert.
40    ServerConfig::builder()
41        .with_no_client_auth()
42        .with_single_cert(vec![cert_chain], key_der)
43        .wrap_err("failed to create tls config")
44}
45
46#[get("/.well-known/webfinger")]
47async fn webfinger(request: WebFingerRequest) -> actix_web::Result<WebFingerResponse> {
48    info!("fetching webfinger resource: {:?}", request);
49    let subject = request.resource.to_string();
50    if subject != SUBJECT {
51        let message = format!("{subject} does not exist");
52        return Err(actix_web::error::ErrorNotFound(message))?;
53    }
54    let rel = Rel::new("http://webfinger.net/rel/profile-page");
55    let response = if request.rels.is_empty() || request.rels.contains(&rel) {
56        let link = Link::builder(rel).href(format!("https://example.com/profile/{subject}"));
57        WebFingerResponse::builder(subject).link(link).build()
58    } else {
59        WebFingerResponse::builder(subject).build()
60    };
61    Ok(response)
62}