online / endpoints 62 / categories 10 / rate 60/min/ip /

GET /csv

GET /csv alias: /csv

RFC 4180 compliant CSV control. Header + 3 rows, all 3 columns, no BOM, fields containing commas are quoted, CRLF line endings throughout. Build a client against this baseline; flip hostname to chaos.catastrophic.io to exercise the four failure modes.

expect: 200 OK, Content-Type: text/csv; charset=utf-8. CSV parses cleanly under any RFC 4180 parser. X-Chaos-Origin: control. X-Chaos-Counterpart points at chaos.catastrophic.io/csv.

bash
curl -i 'https://not.catastrophic.io/csv'
# stdlib csv.reader is forgiving — it accepts unquoted commas (just
# gets the column count wrong) and embedded newlines (silently splits
# the row). pandas raises on ragged columns by default.
import urllib.request, io, csv

raw = urllib.request.urlopen("https://not.catastrophic.io/csv").read().decode("utf-8")
reader = csv.reader(io.StringIO(raw))
for i, row in enumerate(reader):
    print(f"row {i}: {len(row)} cols -> {row}")
// Papa Parse is the de-facto browser CSV library; it surfaces
// delimiter / column-count errors via the `errors` array.
// Without Papa, parsing CSV correctly in vanilla JS is a footgun
// because of the embedded-newlines mode below.
const res = await fetch("https://not.catastrophic.io/csv");
const raw = await res.text();
console.log("Mode:", res.headers.get("X-Chaos-Csv-Mode"));
const result = Papa.parse(raw, { header: true });
console.log("Errors:", result.errors);
console.log("Rows:", result.data);
package main

import (
    "encoding/csv"
    "fmt"
    "net/http"
    "strings"
    "io"
)

func main() {
    resp, _ := http.Get("https://not.catastrophic.io/csv")
    defer resp.Body.Close()
    raw, _ := io.ReadAll(resp.Body)
    fmt.Println("Mode:", resp.Header.Get("X-Chaos-Csv-Mode"))

    // encoding/csv: FieldsPerRecord=0 enforces the header's count;
    // FieldsPerRecord=-1 disables the check entirely.
    r := csv.NewReader(strings.NewReader(string(raw)))
    r.FieldsPerRecord = 0
    for {
        rec, err := r.Read()
        if err == io.EOF { break }
        if err != nil { fmt.Println("Parse error:", err); break }
        fmt.Println(rec)
    }
}
// Cargo.toml: reqwest = { version = "0.12", features = ["blocking"] }
//             csv = "1"
fn main() -> Result<(), Box> {
    let raw = reqwest::blocking::get("https://not.catastrophic.io/csv")?.text()?;
    let mut rdr = csv::ReaderBuilder::new()
        .has_headers(true)
        .flexible(false)
        .from_reader(raw.as_bytes());
    for result in rdr.records() {
        match result {
            Ok(rec) => println!("{:?}", rec),
            Err(e)  => { eprintln!("Parse error: {e}"); break; }
        }
    }
    Ok(())
}
// Requires Apache Commons CSV: org.apache.commons:commons-csv
import org.apache.commons.csv.*;
import java.net.URI;
import java.net.http.*;
import java.io.StringReader;

public class CsvChaos {
    public static void main(String[] args) throws Exception {
        var client = HttpClient.newHttpClient();
        var req = HttpRequest.newBuilder(URI.create("https://not.catastrophic.io/csv")).build();
        var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
        var fmt = CSVFormat.RFC4180.builder().setHeader().build();
        try (var p = CSVParser.parse(new StringReader(resp.body()), fmt)) {
            for (var record : p) System.out.println(record.toMap());
        } catch (Exception e) {
            System.err.println("Parse error: " + e.getMessage());
        }
    }
}
// Requires CsvHelper.
using CsvHelper;
using CsvHelper.Configuration;
using System.Globalization;
using var client = new HttpClient();
var raw = await client.GetStringAsync("https://not.catastrophic.io/csv");
var config = new CsvConfiguration(CultureInfo.InvariantCulture) {
    HasHeaderRecord = true,
    MissingFieldFound = null,
    BadDataFound = ctx => Console.Error.WriteLine($"Bad data at row {ctx.RawRecord}"),
};
using var reader = new StringReader(raw);
using var csv = new CsvReader(reader, config);
var rows = csv.GetRecords().ToList();
Console.WriteLine($"Rows: {rows.Count}");
# stdlib CSV is RFC-mostly. liberal_parsing: true accepts the unquoted
# comma mode without raising; default settings raise on ragged columns.
require "net/http"
require "csv"

resp = Net::HTTP.get_response(URI("https://not.catastrophic.io/csv"))
raw  = resp.body
puts "Mode: #{resp['X-Chaos-Csv-Mode']}"

begin
    CSV.parse(raw, headers: true).each_with_index do |row, i|
        puts "row #{i}: #{row.length} cols -> #{row.to_h}"
    end
rescue CSV::MalformedCSVError => e
    puts "Refused: #{e.message}"
end
# Import-Csv is strict on column counts by default. Headers come from
# row 1; ragged rows produce $null for missing columns.
$resp = Invoke-WebRequest -Uri 'https://not.catastrophic.io/csv' -SkipHttpErrorCheck
$resp.Headers['X-Chaos-Csv-Mode']
$resp.Content | ConvertFrom-Csv | Format-Table