git-next/crates/server/src/actors/webhook/server.rs

104 lines
4 KiB
Rust
Raw Normal View History

//
use std::net::SocketAddr;
use actix::prelude::*;
use git_next_config::{ForgeAlias, RepoAlias};
use git_next_repo_actor::webhook::{self, WebhookAuth, WebhookMessage};
use tracing::{info, warn};
use warp::reject::Rejection;
pub async fn start(socket_addr: SocketAddr, address: actix::prelude::Recipient<WebhookMessage>) {
// start webhook server
use warp::Filter;
// Define the Warp route to handle incoming HTTP requests
let route = warp::post()
.map(move || address.clone())
.and(warp::path::param())
.and(warp::path::param())
.and(warp::header::headers_cloned())
.and(warp::body::bytes())
.and_then(
|recipient: Recipient<WebhookMessage>,
forge_alias: String,
repo_alias: String,
// query: String,
headers: warp::http::HeaderMap,
body: bytes::Bytes| async move {
info!("POST received");
let forge_alias = ForgeAlias::new(forge_alias);
let repo_alias = RepoAlias::new(repo_alias);
let bytes = body.to_vec();
let body = webhook::Body::new(String::from_utf8_lossy(&bytes).to_string());
headers.get("Authorization").map_or_else(
|| {
warn!("No Authorization header");
Err(warp::reject())
},
|authorisation_header| {
info!(
forge = %forge_alias,
repo = %repo_alias,
?authorisation_header,
"Received webhook",
);
// TODO: (#86) Authorization isn't presented consistently, allow each forge
// to parse the authorization from the request
match parse_auth(authorisation_header) {
Ok(authorisation) => {
let message = WebhookMessage::new(
forge_alias,
repo_alias,
authorisation,
body,
);
recipient
.try_send(message)
.map(|_| {
info!("Message sent ok");
warp::reply::with_status("OK", warp::http::StatusCode::OK)
})
.map_err(|e| {
warn!("Unknown error: {:?}", e);
warp::reject()
})
}
Err(e) => {
warn!(?e, "Failed to decode authorization header");
Err(warp::reject())
}
}
},
)
},
);
// Start the server
info!("Starting webhook server: {}", socket_addr);
warp::serve(route).run(socket_addr).await;
}
fn parse_auth(authorization_header: &warp::http::HeaderValue) -> Result<WebhookAuth, Rejection> {
WebhookAuth::new(
authorization_header
.to_str()
.map_err(|e| {
warn!("Invalid non-ascii value in authorization: {:?}", e);
warp::reject()
}) // valid characters
.map(|v| {
info!("raw auth header: {}", v);
v
})?
.strip_prefix("Basic ")
.ok_or_else(|| {
warn!("Authorization must be 'Basic'");
warp::reject()
})?, // must start with "Basic "
)
.map_err(|e| {
warn!(?e, "decode error");
warp::reject()
})
}