online / endpoints 59 / categories 14 / rate 60/min/ip /

GET /truncate

GET /truncate

Sends a Content-Length header claiming the full payload size, but closes the stream early. Tests how clients handle responses that end before they should.

at Number of bytes to actually send. Range: 1–1000000. Default: 512. Content-Length will claim 2× this value.

control Compare against the well-formed counterpart: not.catastrophic.io/truncate side-by-side

build a request:

expect: Content-Length claims 2× the actual bytes. The stream closes early — clients see a premature EOF, IncompleteRead, or read error.

bash
curl -i https://chaos.catastrophic.io/truncate?at=512
import urllib.request, http.client
with urllib.request.urlopen("https://chaos.catastrophic.io/truncate?at=512") as resp:
    promised = resp.headers["Content-Length"]
    try:
        data = resp.read()
        print(f"Got {len(data)} bytes, promised {promised}")
    except http.client.IncompleteRead as e:
        print(f"Truncated: got {len(e.partial)} of {promised} bytes")
// fetch raises on premature EOF when Content-Length is set.
const url = "https://chaos.catastrophic.io/truncate?at=512";
try {
    const res = await fetch(url);
    const buf = await res.arrayBuffer();
    console.log(`Got ${buf.byteLength} bytes, promised ${res.headers.get("content-length")}`);
} catch (e) {
    console.error("Premature EOF:", e.message);
}
package main

import (
    "fmt"
    "io"
    "net/http"
)

func main() {
    resp, _ := http.Get("https://chaos.catastrophic.io/truncate?at=512")
    defer resp.Body.Close()
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Read error after %d bytes: %v\n", len(body), err)
        return
    }
    fmt.Printf("Got %d bytes, promised %s\n", len(body), resp.Header.Get("Content-Length"))
}
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking"] }
fn main() -> Result<(), Box> {
    let resp = reqwest::blocking::get("https://chaos.catastrophic.io/truncate?at=512")?;
    let promised = resp.headers().get("content-length").cloned();
    match resp.bytes() {
        Ok(b)  => println!("Got {} bytes, promised {:?}", b.len(), promised),
        Err(e) => eprintln!("Read error: {e}"),
    }
    Ok(())
}
import java.net.URI;
import java.net.http.*;

public class Truncate {
    public static void main(String[] args) throws Exception {
        var client = HttpClient.newHttpClient();
        var req = HttpRequest.newBuilder(URI.create("https://chaos.catastrophic.io/truncate?at=512")).build();
        try {
            var resp = client.send(req, HttpResponse.BodyHandlers.ofByteArray());
            System.out.println("Got " + resp.body().length + " bytes, promised "
                + resp.headers().firstValue("Content-Length").orElse(""));
        } catch (java.io.IOException e) {
            System.err.println("Premature EOF: " + e.getMessage());
        }
    }
}
using var client = new HttpClient();
try {
    var resp = await client.GetAsync("https://chaos.catastrophic.io/truncate?at=512");
    var bytes = await resp.Content.ReadAsByteArrayAsync();
    Console.WriteLine($"Got {bytes.Length} bytes, promised {resp.Content.Headers.ContentLength}");
} catch (HttpRequestException e) {
    Console.Error.WriteLine($"Premature EOF: {e.Message}");
}
require "net/http"
uri = URI("https://chaos.catastrophic.io/truncate?at=512")
begin
    res = Net::HTTP.get_response(uri)
    puts "Got #{res.body.bytesize} bytes, promised #{res["Content-Length"]}"
rescue EOFError, IOError => e
    puts "Read error: #{e.message}"
end
# The mismatch between Content-Length and actual bytes received
# will surface as a connection or parsing error in most clients.
$r = Invoke-WebRequest -Uri 'https://chaos.catastrophic.io/truncate?at=512'
Write-Host "Promised: $($r.Headers['X-Chaos-Promised-Bytes'])"
Write-Host "Received: $($r.RawContentLength)"