HTTP interface¶
The contract for iiiris's HTTP surface: the routes it serves, the middleware
every request passes through, the status codes it returns, and the
caching/CORS/conditional-GET semantics on every response. This is the
transport-layer contract — distinct from the IIIF parameter grammar
(iiif-compliance.md), the decode pipeline
(image-decode.md), and the auth service shapes
(auth.md).
What it does¶
A stdlib net/http.ServeMux routes requests through a fixed middleware chain
to version-specific IIIF handlers (plus health, readiness, and admin
endpoints). The IIIF handlers reconstruct the request path and delegate
grammar parsing to iiif/v{2,3}.ParsePath, then run the
redirect → auth → cache → render flow. Every IIIF response is emitted through
http.ServeContent so conditional GETs, ranges, and ETags work uniformly.
Surface¶
Routes¶
| Route | Handler |
|---|---|
GET /{$} |
Landing page (HTML). |
GET /health |
Liveness — always 200 while the process is up. |
GET /ready |
Readiness — runs every source's Healthchecker in parallel; 200 if all pass, 503 if any fails. |
GET /iiif/3/{rest...} · GET /iiif/2/{rest...} |
Catch-all image + info.json requests; the handler splits {rest} into identifier + IIIF tail and calls the v3/v2 parser. |
/admin/ |
Operator UI (HTTP Basic). Returns 404 when admin credentials are unset (the subsystem is off, not merely locked). |
/iiif/auth/... |
The IIIF Auth 2.0/1.0 services — mounted only when auth profiles are configured (auth.md). |
Middleware chain (outer → inner)¶
collapseSlashes → request counter + per-route stats → request log →
optional per-IP concurrency cap → panic recoverer → handler.
collapseSlashesfolds runs of/in the path before routing, so//iiif///3/...routes identically.- Per-route stats feed the
/admin/dashboard (count + latency percentiles per classified route). - Per-IP cap is wired only when
image.max_concurrent_per_ip > 0; it sits inside count+log (so a rejection is counted and logged) and outside the recoverer. - Recoverer turns a handler panic into a
500(logged), never a dropped connection.
Contracts¶
Status codes¶
| Condition | Status |
|---|---|
Path-syntax or parameter-value rejection (bad region/size/rotation/quality, region origin outside source, upscale without ^) |
400 |
image.max_pixel_area (source) or max_output_area (output) guard exceeded |
400 |
| Auth denied (hard deny) | 401, or as supplied by the Authorizer |
| Identifier not found in the source | 404 |
| Per-IP concurrency cap exceeded | 429 |
| Decode / encode / source-I/O failure | 500 (also a recovered panic) |
A source healthcheck fails (on /ready) |
503 |
Hook.Redirect returns a URL, or an auth Decision.Redirect |
303 |
If-None-Match / If-Modified-Since matches the served entry |
304 |
Status mapping is single-sourced in the handlers; the parameter-level detail
behind a 400 is in iiif-compliance.md.
Response semantics (every /iiif/... response)¶
- Cache + conditional GET. Each response carries
Cache-Controland anETag, and is written viahttp.ServeContent, soIf-None-Match/If-Modified-Sinceyield304. - CORS.
Access-Control-Allow-Origin: *on every IIIF response. - Content negotiation.
info.jsonhonoursAccept: application/ld+json(vsapplication/json), with the matching JSON-LDContent-Type. - Redirect precedence.
Hook.Redirectis consulted at the top of the image and info handlers — before auth and parameter parsing — so a non-empty URL fires a303immediately (content relocation), distinct from an auth-conditional303.
Test coverage¶
In internal/server/ (integration tests against an httptest server):
collapse_slashes_test.go— slash folding before routing.content_neg_test.go—info.jsonAcceptnegotiation + JSON-LD type.cache_integration_test.go—ETag/ conditional-GET304.iplimit_test.go— the per-IP cap returns429.hook_redirect_test.go—Hook.Redirectfires a303before parsing/auth.stats_test.go— per-route classification for the dashboard.server_integration_test.go— end-to-end routing + status mapping.
Out of scope¶
- IIIF parameter grammar + supported features — region/size/rotation/
quality/format coverage and the info.json field set live in
iiif-compliance.md; the grammar itself is iniiif/v{2,3}.ParsePath. - The decode/render pipeline —
image-decode.md. - Auth service request/response shapes —
auth.md. - Admin UI internals (pages, SSE, config editor) —
admin.md.