Skip to content

Commit d7550cb

Browse files
fix(relay): rewrite bare git 413 bodies with pack-limit guidance
Use axum middleware to replace RequestBodyLimitLayer's empty 413 with a message that names BUZZ_GIT_MAX_PACK_BYTES. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 36fff3c commit d7550cb

1 file changed

Lines changed: 26 additions & 27 deletions

File tree

crates/buzz-relay/src/api/git/transport.rs

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,17 @@ use std::time::{Duration, Instant};
1515

1616
use axum::{
1717
body::Body,
18-
error_handling::HandleErrorLayer,
1918
extract::{Path as AxumPath, Query, State},
2019
http::{header, StatusCode},
20+
middleware::{from_fn, Next},
2121
response::{IntoResponse, Response},
2222
routing::{get, post},
23-
BoxError,
2423
Router,
2524
};
2625
use base64::Engine;
2726
use hex;
2827
use serde::Deserialize;
2928
use tokio::process::Command;
30-
use tower::ServiceBuilder;
3129
use tower_http::limit::RequestBodyLimitLayer;
3230
use tracing::{error, info, warn};
3331

@@ -1917,33 +1915,36 @@ pub fn git_router(state: Arc<AppState>) -> Router {
19171915
.route("/git/{owner}/{repo}/info/refs", get(info_refs))
19181916
.route("/git/{owner}/{repo}/git-upload-pack", post(upload_pack))
19191917
.route("/git/{owner}/{repo}/git-receive-pack", post(receive_pack))
1920-
.layer(
1921-
ServiceBuilder::new()
1922-
.layer(HandleErrorLayer::new(move |err: BoxError| async move {
1923-
git_body_limit_response(err, max_bytes)
1924-
}))
1925-
.layer(RequestBodyLimitLayer::new(body_limit)),
1926-
)
1918+
.layer(from_fn(move |req, next| {
1919+
rewrite_git_body_limit_413(req, next, max_bytes)
1920+
}))
1921+
.layer(RequestBodyLimitLayer::new(body_limit))
19271922
.with_state(state)
19281923
}
19291924

1930-
fn git_body_limit_response(err: BoxError, max_bytes: u64) -> Response {
1931-
let msg = err.to_string();
1932-
if msg.contains("length limit") || msg.contains("LengthLimitError") {
1925+
/// `RequestBodyLimitLayer` returns a bare 413; rewrite the body so operators
1926+
/// see which env var to raise (and so proxies don't look like TLS aborts).
1927+
async fn rewrite_git_body_limit_413(
1928+
req: axum::extract::Request,
1929+
next: Next,
1930+
max_bytes: u64,
1931+
) -> Response {
1932+
let response = next.run(req).await;
1933+
if response.status() == StatusCode::PAYLOAD_TOO_LARGE {
19331934
return (
19341935
StatusCode::PAYLOAD_TOO_LARGE,
1935-
format!(
1936-
"git pack body exceeds relay limit ({max_bytes} bytes; \
1937-
self-hosted relays: BUZZ_GIT_MAX_PACK_BYTES)"
1938-
),
1936+
git_body_limit_message(max_bytes),
19391937
)
19401938
.into_response();
19411939
}
1942-
(
1943-
StatusCode::INTERNAL_SERVER_ERROR,
1944-
"failed to read git request body",
1940+
response
1941+
}
1942+
1943+
fn git_body_limit_message(max_bytes: u64) -> String {
1944+
format!(
1945+
"git pack body exceeds relay limit ({max_bytes} bytes; \
1946+
self-hosted relays: BUZZ_GIT_MAX_PACK_BYTES)"
19451947
)
1946-
.into_response()
19471948
}
19481949

19491950
#[cfg(test)]
@@ -1985,12 +1986,10 @@ mod track_c_tests {
19851986
}
19861987

19871988
#[test]
1988-
fn git_body_limit_response_maps_length_errors_to_413() {
1989-
let response = git_body_limit_response(
1990-
Box::new(std::io::Error::other("LengthLimitError")),
1991-
5_242_880,
1992-
);
1993-
assert_eq!(response.status(), StatusCode::PAYLOAD_TOO_LARGE);
1989+
fn git_body_limit_message_names_env_var() {
1990+
let msg = git_body_limit_message(5_242_880);
1991+
assert!(msg.contains("5242880"));
1992+
assert!(msg.contains("BUZZ_GIT_MAX_PACK_BYTES"));
19941993
}
19951994

19961995
/// Without a gzip `Content-Encoding`, the body is passed through byte

0 commit comments

Comments
 (0)