This repository has been archived on 2023-07-11. You can view files and clone it, but cannot push or open issues or pull requests.
buzzrelay/src/send.rs

96 lines
3.0 KiB
Rust
Raw Normal View History

2022-12-20 03:13:44 +01:00
use std::{
sync::Arc,
time::Instant,
};
2022-12-13 04:12:35 +01:00
use http::StatusCode;
2022-12-11 01:07:39 +01:00
use http_digest_headers::{DigestHeader, DigestMethod};
2022-12-20 03:13:44 +01:00
use metrics::histogram;
2022-12-11 01:07:39 +01:00
use serde::Serialize;
use sigh::{PrivateKey, SigningConfig, alg::RsaSha256};
#[derive(Debug, thiserror::Error)]
pub enum SendError {
#[error("HTTP Digest generation error")]
Digest,
#[error("JSON encoding error")]
Json(#[from] serde_json::Error),
#[error("Signature error")]
Signature(#[from] sigh::Error),
#[error("HTTP request error")]
HttpReq(#[from] http::Error),
#[error("HTTP client error")]
Http(#[from] reqwest::Error),
2022-12-13 04:12:35 +01:00
#[error("Invalid URI")]
InvalidUri,
#[error("Error response from remote")]
Response(String),
2022-12-11 01:07:39 +01:00
}
pub async fn send<T: Serialize>(
client: &reqwest::Client,
2022-12-13 04:12:35 +01:00
uri: &str,
2022-12-11 01:07:39 +01:00
key_id: &str,
private_key: &PrivateKey,
2022-12-19 21:20:13 +01:00
body: &T,
) -> Result<(), SendError> {
let body = Arc::new(
serde_json::to_vec(body)
.map_err(SendError::Json)?
);
send_raw(client, uri, key_id, private_key, body).await
}
pub async fn send_raw(
client: &reqwest::Client,
uri: &str,
key_id: &str,
private_key: &PrivateKey,
body: Arc<Vec<u8>>,
2022-12-11 01:07:39 +01:00
) -> Result<(), SendError> {
let mut digest_header = DigestHeader::new()
.with_method(DigestMethod::SHA256, &body)
.map(|h| format!("{}", h))
.map_err(|_| SendError::Digest)?;
if digest_header.starts_with("sha-") {
digest_header.replace_range(..4, "SHA-");
}
// mastodon uses base64::alphabet::STANDARD, not base64::alphabet::URL_SAFE
digest_header.replace_range(
7..,
2022-12-20 00:15:00 +01:00
&digest_header[7..].replace('-', "+").replace('_', "/")
2022-12-11 01:07:39 +01:00
);
2022-12-13 04:12:35 +01:00
let url = reqwest::Url::parse(uri)
.map_err(|_| SendError::InvalidUri)?;
2022-12-20 03:13:44 +01:00
let host = format!("{}", url.host().ok_or(SendError::InvalidUri)?);
2022-12-11 01:07:39 +01:00
let mut req = http::Request::builder()
.method("POST")
2022-12-13 04:12:35 +01:00
.uri(uri)
2022-12-20 03:13:44 +01:00
.header("host", &host)
2022-12-11 01:07:39 +01:00
.header("content-type", "application/activity+json")
.header("date", chrono::Utc::now().to_rfc2822()
.replace("+0000", "GMT"))
.header("digest", digest_header)
2022-12-19 21:20:13 +01:00
.body(body.as_ref().clone())
2022-12-11 01:07:39 +01:00
.map_err(SendError::HttpReq)?;
2022-12-20 03:13:44 +01:00
let t1 = Instant::now();
2022-12-20 00:15:00 +01:00
SigningConfig::new(RsaSha256, private_key, key_id)
2022-12-11 01:07:39 +01:00
.sign(&mut req)?;
2022-12-20 03:13:44 +01:00
let t2 = Instant::now();
2022-12-18 21:31:50 +01:00
let req: reqwest::Request = req.try_into()?;
let res = client.execute(req)
2022-12-11 01:07:39 +01:00
.await?;
2022-12-20 03:13:44 +01:00
let t3 = Instant::now();
2022-12-20 04:10:45 +01:00
histogram!("relay_http_request_duration", t2 - t1);
2022-12-13 04:12:35 +01:00
if res.status() >= StatusCode::OK && res.status() < StatusCode::MULTIPLE_CHOICES {
2022-12-20 04:10:45 +01:00
histogram!("relay_http_response_duration", t3 - t2, "res" => "ok", "host" => host);
2022-12-13 04:12:35 +01:00
Ok(())
} else {
2022-12-20 04:10:45 +01:00
histogram!("relay_http_response_duration", t3 - t2, "res" => "err", "host" => host);
2023-03-03 02:20:26 +01:00
tracing::error!("send_raw {} response HTTP {}", url, res.status());
2022-12-13 04:12:35 +01:00
let response = res.text().await?;
2023-03-03 02:20:26 +01:00
tracing::error!("send_raw {} response body: {:?}", url, response);
2022-12-13 04:12:35 +01:00
Err(SendError::Response(response))
}
2022-12-11 01:07:39 +01:00
}