GET /compression
Returns responses where Content-Encoding lies about the body's actual compression state, or where the body is gzip-compressed but the header doesn't say so. Tests how clients and proxies handle disagreement between encoding metadata and bytes.
mode
gzip-header-plain-body (default; Content-Encoding: gzip but body is plain text), gzip-body-no-header (body is actually gzipped but no Content-Encoding header — looks like binary garbage), wrong-encoding-name (Content-Encoding: superzip — unknown), chained-claim (Content-Encoding: gzip, br — multiple encodings claimed, body is plain text).
build a request:
expect: 200 OK. gzip-header-plain-body and chained-claim send plain JSON with a lying Content-Encoding — most clients will attempt to gunzip and fail. gzip-body-no-header sends real gzip bytes without the header, so the body looks like binary garbage. wrong-encoding-name uses an unknown encoding name.
# --compressed tells curl to honour Content-Encoding; will surface the lie
curl -i --compressed 'https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body'
import urllib.request
resp = urllib.request.urlopen("https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body")
print(resp.status, "Content-Encoding:", resp.headers.get("Content-Encoding"))
raw = resp.read()
print(f"Got {len(raw)} bytes")
print(raw[:200])
// Browsers auto-decompress when Content-Encoding is recognised. fetch will
// throw on a malformed gzip stream when the header claims gzip.
try {
const res = await fetch("https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body");
console.log("status:", res.status, "Content-Encoding:", res.headers.get("content-encoding"));
console.log("body:", await res.text());
} catch (e) {
console.error("Decode error:", e.message);
}
package main
import (
"fmt"
"io"
"net/http"
)
func main() {
// Don't let the transport auto-decompress so we see exactly what's sent.
tr := &http.Transport{DisableCompression: true}
resp, _ := (&http.Client{Transport: tr}).Get("https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body")
defer resp.Body.Close()
raw, _ := io.ReadAll(resp.Body)
fmt.Println("Content-Encoding:", resp.Header.Get("Content-Encoding"))
fmt.Printf("Got %d bytes\n", len(raw))
fmt.Println(string(raw[:min(200, len(raw))]))
}
func min(a, b int) int { if a < b { return a }; return b }
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking"] }
// (reqwest does NOT auto-decompress unless the "gzip" feature is enabled)
fn main() -> Result<(), Box> {
let resp = reqwest::blocking::get("https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body")?;
println!("Content-Encoding: {:?}", resp.headers().get("content-encoding"));
let bytes = resp.bytes()?;
println!("Got {} bytes", bytes.len());
println!("{:?}", &bytes[..200.min(bytes.len())]);
Ok(())
}
// Java 11+ HttpClient. ofString assumes plain text — wrong encoding will
// surface as garbled output.
import java.net.URI;
import java.net.http.*;
public class CompressionChaos {
public static void main(String[] args) throws Exception {
var client = HttpClient.newHttpClient();
var req = HttpRequest.newBuilder(URI.create("https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body")).build();
var resp = client.send(req, HttpResponse.BodyHandlers.ofByteArray());
System.out.println("Content-Encoding: " +
resp.headers().firstValue("Content-Encoding").orElse(""));
System.out.println("Got " + resp.body().length + " bytes");
System.out.println(new String(resp.body(),
0, Math.min(200, resp.body().length)));
}
}
// Disable auto-decompression so we see the raw bytes.
using var handler = new HttpClientHandler { AutomaticDecompression = System.Net.DecompressionMethods.None };
using var client = new HttpClient(handler);
var resp = await client.GetAsync("https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body");
Console.WriteLine($"Content-Encoding: {resp.Content.Headers.ContentEncoding}");
var bytes = await resp.Content.ReadAsByteArrayAsync();
Console.WriteLine($"Got {bytes.Length} bytes");
Console.WriteLine(System.Text.Encoding.UTF8.GetString(bytes, 0, Math.Min(200, bytes.Length)));
require "net/http"
uri = URI("https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body")
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
req = Net::HTTP::Get.new(uri)
req["Accept-Encoding"] = "identity" # ask for no decompression
res = http.request(req)
puts "Content-Encoding: #{res['Content-Encoding']}"
puts "Got #{res.body.bytesize} bytes"
puts res.body[0, 200]
end
# Invoke-WebRequest auto-decompresses gzip; the chaos surfaces as a decode error.
try {
$r = Invoke-WebRequest -Uri 'https://chaos.catastrophic.io/compression?mode=gzip-header-plain-body'
"Content-Encoding: $($r.Headers['Content-Encoding'])"
"Content length: $($r.RawContentLength)"
$r.Content.Substring(0, [Math]::Min(200, $r.Content.Length))
} catch {
"Decode error: $($_.Exception.Message)"
}
headers
body