mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
fix(http): decompress gzip-encoded git request bodies
Modern git clients send Content-Encoding: gzip on POST requests to /git-upload-pack for efficiency. Without decompression, the compressed binary data was passed directly to git upload-pack, which expected pkt-line format, causing: fatal: protocol error: bad line length character: ?? error: RPC failed; HTTP 500 This was discovered in production when git clone requests consistently failed with HTTP 500 errors. The fix extracts the Content-Encoding header and uses flate2::GzDecoder to decompress gzip bodies before passing them to the git subprocess.
This commit is contained in:
+39
-3
@@ -159,14 +159,23 @@ impl Service<Request<Incoming>> for HttpService {
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
// Extract Content-Encoding header to handle gzip-compressed request bodies
|
||||
// Modern git clients send gzip-compressed POST bodies for efficiency
|
||||
let content_encoding = req
|
||||
.headers()
|
||||
.get("content-encoding")
|
||||
.and_then(|v| v.to_str().ok())
|
||||
.map(|s| s.to_lowercase());
|
||||
|
||||
tracing::debug!(
|
||||
"Git request: {} {} (npub={}, id={}, subpath={}, protocol={:?})",
|
||||
"Git request: {} {} (npub={}, id={}, subpath={}, protocol={:?}, encoding={:?})",
|
||||
method,
|
||||
path,
|
||||
npub,
|
||||
identifier,
|
||||
subpath,
|
||||
git_protocol
|
||||
git_protocol,
|
||||
content_encoding
|
||||
);
|
||||
|
||||
let repo_path = git::resolve_repo_path(&git_data_path, &npub, &identifier);
|
||||
@@ -175,12 +184,39 @@ impl Service<Request<Incoming>> for HttpService {
|
||||
|
||||
return Box::pin(async move {
|
||||
// Collect request body once before the match statement
|
||||
let body_bytes = req
|
||||
let raw_body = req
|
||||
.collect()
|
||||
.await
|
||||
.map(|collected| collected.to_bytes())
|
||||
.unwrap_or_else(|_| Bytes::new());
|
||||
|
||||
// Decompress gzip-encoded request bodies
|
||||
// Git clients send Content-Encoding: gzip for POST requests
|
||||
let body_bytes = if content_encoding.as_deref() == Some("gzip") {
|
||||
use flate2::read::GzDecoder;
|
||||
use std::io::Read;
|
||||
|
||||
let mut decoder = GzDecoder::new(&raw_body[..]);
|
||||
let mut decompressed = Vec::new();
|
||||
match decoder.read_to_end(&mut decompressed) {
|
||||
Ok(_) => {
|
||||
tracing::debug!(
|
||||
"Decompressed gzip body: {} -> {} bytes",
|
||||
raw_body.len(),
|
||||
decompressed.len()
|
||||
);
|
||||
Bytes::from(decompressed)
|
||||
}
|
||||
Err(e) => {
|
||||
tracing::warn!("Failed to decompress gzip body: {}", e);
|
||||
// Fall back to raw body (might work if not actually gzip)
|
||||
raw_body
|
||||
}
|
||||
}
|
||||
} else {
|
||||
raw_body
|
||||
};
|
||||
|
||||
let result = match (method.as_ref(), subpath.as_str()) {
|
||||
// GET /info/refs?service=git-upload-pack or git-receive-pack
|
||||
(m, sp) if m == Method::GET && sp.starts_with("info/refs") => {
|
||||
|
||||
Reference in New Issue
Block a user