Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Transfer, compression, ranges and images

Compression

/gzip, /deflate, /brotli and /zstd return a small JSON echo (gzipped, deflated, brotli or zstd set to true, plus method, headers and origin) compressed with that encoding and labelled with Content-Encoding (gzip, deflate in the zlib format, br, zstd) and Vary: Accept-Encoding. They do so whatever Accept-Encoding says, so you can show a gateway decompressing for a client that cannot, or passing the encoding through.

# Always compressed, whatever Accept-Encoding says: a gateway can be shown
# decompressing (or passing through) the body.
GET {{base_url}}/gzip
HTTP 200
[Asserts]
header "Content-Encoding" == "gzip"
header "Vary" contains "Accept-Encoding"
jsonpath "$.gzipped" == true
jsonpath "$.method" == "GET"
GET {{base_url}}/brotli
HTTP 200
[Asserts]
header "Content-Encoding" == "br"
jsonpath "$.brotli" == true
GET {{base_url}}/deflate
HTTP 200
[Asserts]
header "Content-Encoding" == "deflate"

GET {{base_url}}/zstd
HTTP 200
[Asserts]
header "Content-Encoding" == "zstd"

GET /encoding/utf8 is an HTML page of multi-script UTF-8 text (Latin with diacritics, Greek, Cyrillic, Hebrew, Arabic, CJK, Thai, math symbols, emoji) for charset and transcoding tests.

GET {{base_url}}/encoding/utf8
HTTP 200
[Asserts]
header "Content-Type" == "text/html; charset=utf-8"
body contains "Grüße"

Ranges

GET /range/{n} returns n bytes (abc...z repeated, 1 to 102400; 10240 in public mode) with Accept-Ranges: bytes and a strong ETag: "range<n>". A single range (bytes=a-b, bytes=a-, bytes=-n) gets 206 with Content-Range; an unsatisfiable or malformed one gets 416 with Content-Range: bytes */<n>. Multiple ranges and unknown units get the full 200 body (allowed by RFC 9110). If-Range with a different ETag also returns the full body.

GET {{base_url}}/range/100
Range: bytes=0-9
HTTP 206
[Asserts]
header "Content-Range" == "bytes 0-9/100"
header "Accept-Ranges" == "bytes"
header "ETag" == "\"range100\""
body == "abcdefghij"
GET {{base_url}}/range/100
Range: bytes=-4
HTTP 206
[Asserts]
header "Content-Range" == "bytes 96-99/100"
body == "stuv"
# If-Range with a different ETag: the full body comes back.
GET {{base_url}}/range/100
Range: bytes=0-9
If-Range: "something-else"
HTTP 200
[Asserts]
bytes count == 100
GET {{base_url}}/range/100
Range: bytes=500-
HTTP 416
[Asserts]
header "Content-Range" == "bytes */100"

Data transfer

RouteBehaviour
GET /bytes/{n}n random bytes (application/octet-stream); ?seed= makes them reproducible
GET /stream-bytes/{n}The same, streamed in chunks of ?chunk_size= bytes (default 10240)
GET /stream/{n}n JSON lines (url, args, headers, origin, id), streamed
GET /drip?numbytes= bytes (default 10) of * spread over ?duration= seconds (default 2), after ?delay= seconds (default 0), with status ?code= (default 200)
GET /links/{n}Redirects to /links/{n}/0
GET /links/{n}/{offset}An HTML page with n links (at most 200), the current one unlinked
GET /base64/{value}Decodes standard or URL-safe base64 (padding optional); text comes back as text/plain, anything else as application/octet-stream

Caps (normal / public mode): /bytes and /stream-bytes 102400 / 10240 bytes, /stream 100 / 20 lines, /drip 10240 / 1024 bytes. Counts above the cap are clamped (like httpbin) and the response says so in X-Rustybin-Capped: <cap>; /drip instead rejects too many bytes with 400. /drip duration and delay are capped by the instance delay limit (30 s, 10 s in public mode).

GET {{base_url}}/bytes/16?seed=42
HTTP 200
[Captures]
first: bytes
[Asserts]
header "Content-Type" == "application/octet-stream"
bytes count == 16

# Same seed, same bytes.
GET {{base_url}}/bytes/16?seed=42
HTTP 200
[Asserts]
bytes == {{first}}
GET {{base_url}}/bytes/500000
HTTP 200
[Asserts]
header "X-Rustybin-Capped" == "102400"
bytes count == 102400
GET {{base_url}}/stream/3?trace=on
HTTP 200
[Asserts]
header "Content-Type" == "application/json"
body split "\n" count == 4
body contains "\"id\":2"
body contains "\"args\":{\"trace\":\"on\"}"
GET {{base_url}}/stream-bytes/4096?chunk_size=1024&seed=1
HTTP 200
[Asserts]
bytes count == 4096

/drip sends its headers (with the final Content-Length) right away and then the body slowly: use it for streaming and idle-timeout policies.

GET {{base_url}}/drip?duration=0.5&numbytes=5&delay=0.1&code=202
HTTP 202
[Asserts]
header "Content-Length" == "5"
body == "*****"
duration >= 500
GET {{base_url}}/links/3
HTTP 302
[Asserts]
header "Location" == "/links/3/0"

GET {{base_url}}/links/3/1
HTTP 200
[Asserts]
header "Content-Type" contains "text/html"
body contains "<a href='/links/3/0'>0</a>"
GET {{base_url}}/base64/SFRUUEJJTiBpcyBhd2Vzb21l
HTTP 200
[Asserts]
body == "HTTPBIN is awesome"

Client information

RouteReturns
GET /ip{"ipv4": ..., "ipv6": ...} (the other family is null)
GET /ip/v4, GET /ip/v6One family only
GET /date, GET /date/{timezone}Today's date, in UTC or an IANA timezone (/date/America/New_York)
GET /time, GET /time/{timezone}The current time as RFC 3339; unknown timezones get 404

The client IP honours proxy headers only with RUSTYBIN_TRUST_FORWARD=true (see Configuration), which makes /ip the quickest check of what a gateway forwards.

GET {{base_url}}/ip
HTTP 200
[Asserts]
jsonpath "$.ipv4" == "127.0.0.1"
jsonpath "$.ipv6" == null
GET {{base_url}}/time/Europe/London
HTTP 200
[Asserts]
jsonpath "$.timezone" == "Europe/London"
jsonpath "$.time" isIsoDate

GET {{base_url}}/date/Mars/Olympus_Mons
HTTP 404
[Asserts]
jsonpath "$.error" == "unknown_timezone"

Random values

/uuid (v4), /guuid (a braced GUID), /random (a bundle), /random/int, /random/int/{lower}/{upper} (inclusive; lower must be smaller than upper), /random/uint, /random/lorem-ipsum and /random/lorem-ipsum/{count} (1 to 32 paragraphs).

GET {{base_url}}/uuid
HTTP 200
[Asserts]
jsonpath "$.uuid" matches /^[0-9a-f-]{36}$/

GET {{base_url}}/random/int/1/6
HTTP 200
[Asserts]
jsonpath "$.value" >= 1
jsonpath "$.value" <= 6

GET {{base_url}}/random/lorem-ipsum/2
HTTP 200
[Asserts]
jsonpath "$.paragraphs" count == 2

Images

Small, valid images for content-type routing, transformation and caching tests: /image/png (16x16), /image/jpeg, /image/gif and /image/webp (8x8), /image/svg. GET /image picks the format from Accept (webp, svg, jpeg, png, gif in that order of preference; */* gives webp, no Accept header gives png) and answers 406 when nothing matches.

GET {{base_url}}/image
Accept: image/png
HTTP 200
[Asserts]
header "Content-Type" == "image/png"

GET {{base_url}}/image/svg
HTTP 200
[Asserts]
header "Content-Type" contains "image/svg+xml"

GET {{base_url}}/image
Accept: text/plain
HTTP 406