From caec22001dbfd52a11f2b4c39bc238805ff95803 Mon Sep 17 00:00:00 2001 From: Lilleman auf Larv Date: Sun, 20 Sep 2026 00:32:42 +0200 Subject: [PATCH] Ship the methods a request carries, as booleans a consumer can read --- CHANGELOG.md | 9 +++---- README.md | 20 ++++++++++++---- data-import/httpmethod.py | 24 ++++++++++++------- data/misc/httpmethod.tsv | 49 +++++++-------------------------------- todo.md | 5 ++++ 5 files changed, 51 insertions(+), 56 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab23529..4d990bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -44,10 +44,11 @@ replacement, and each removed path, column or flag. `device` and `os` columns. `misc.car` keeps its makes and models in a `make` and a `model` column. - `misc.httpmethod`, `misc.protocol` and `misc.port` are IANA's registries as tables. - `misc.httpmethod` carries `safe` and `idempotent` beside the method; `misc.protocol` - the keyword, its `number` and its `name`, selectable by either; `misc.port` renders a - TCP port number, with the IANA `service` name beside it, keeping the first service the - registry describes for a port so a number selects one row. + `misc.httpmethod` is the nine methods the HTTP core specification defines, carrying + `safe` and `idempotent` as `true` or `false`; `misc.protocol` the keyword, its + `number` and its `name`, selectable by either; `misc.port` renders a TCP port number, + with the IANA `service` name beside it, keeping the first service the registry + describes for a port so a number selects one row. - `geo.SE` and `geo.US`: five linked tables per country, `region`, `municipality`, `locality`, `postal-code` and `street`, weighted by population and address counts and built from SCB, GeoNames, Trafikverket NVDB and the US Census Bureau, and an diff --git a/README.md b/README.md index 1a841ff..722e5c3 100644 --- a/README.md +++ b/README.md @@ -170,10 +170,18 @@ which `--list` shows. `car`, `currency`, `httpmethod`, `httpstatus`, `language`, `mimetype`, `port`, `protocol`, `territory`, `timezone` and `useragent` are [tables](#table), so `misc.territory[SE].capital` and `misc.currency[Euro].symbol` select a row; `car` and `useragent` carry no key or name, so they are drawn from rather -than selected in. `misc.httpmethod`, `misc.protocol` and `misc.port` are IANA's -registries: a method carries the register's `yes` or `no` for `safe` and `idempotent`, -a protocol its `number` and its spelled-out `name`, and `misc.port` renders the number -a port field holds, with the IANA `service` name beside it. +than selected in. + +`misc.httpmethod`, `misc.protocol` and `misc.port` are IANA's registries. +`misc.httpmethod` is the nine methods the HTTP core specification defines, not the +WebDAV extensions the register also holds, and its `safe` and `idempotent` are `true` +or `false`, so a Go `bool` reads them. `misc.protocol` carries a `number` and a `name`, +the keyword itself where the register spells none out, and selects by either spelling. +`misc.port` is the TCP assignments, rendering the number a port field holds with the +IANA `service` beside it, and selecting by number alone. A draw spans the whole +register, so pin `misc.port[443]` where a fixture needs a port a reader recognises; +it stops at 49150, IANA assigning nothing above, so an ephemeral source port is +`{int(49152,65535)}`. [`DATA-LICENSES.md`](DATA-LICENSES.md) names each table's source and licence. `misc.timezone` is every zone tzdb gives a shipped territory, from one apiece for most @@ -1188,6 +1196,10 @@ the Development section below, and who ships a register the four above then draw worth more than the last rows of a table. Where no such link can hold the fact stays a column. Layer your own `misc.territory` over the shipped one and you must layer `misc.timezone` too, or the link fails at load naming the row. +- **`misc.port` selects by number, and carries no `name`.** 29 of its services sit on + more than one port, `http-alt` on three, so `misc.port[http-alt]` could name no one + row. The number is what a port field holds anyway, and `misc.port[443].service` + reads the other way. - **`misc.car` is one flat table, not a make linked to its models.** A row is a make and a model drawn together, so no render pairs a Volvo with a RAV4. Two linked tables would reach the same pairs and add a selector nothing asks for; a make alone diff --git a/data-import/httpmethod.py b/data-import/httpmethod.py index 0e9e110..585c3b9 100644 --- a/data-import/httpmethod.py +++ b/data-import/httpmethod.py @@ -3,8 +3,10 @@ data-import/httpmethod.py [--source URL_OR_FILE] [--cache DIR] [--out FILE] -A row needs a name of letters and hyphens, which drops the registry's `*`, and both of -the answers the registry gives for it. +A method ships when the register cites the HTTP core specification for it, RFC 9110 +section 9.3 or RFC 5789, which is the nine a request carries; the WebDAV and DeltaV +extensions the register also holds do not. The register's `yes` and `no` ship as `true` +and `false`, which a consumer's boolean reads. """ import argparse import csv @@ -20,19 +22,23 @@ SOURCE = "https://www.iana.org/assignments/http-methods/methods.csv" OUT = Path(__file__).resolve().parent.parent / "data" / "misc" / "httpmethod.tsv" CACHE = Path(__file__).resolve().parent / "cache" COLUMNS = ["idempotent", "method", "safe"] -ANSWERS = ("yes", "no") +CORE = re.compile(r"\[RFC9110, Section 9\.3\.|\[RFC5789, Section 2\]") +BOOLEAN = {"yes": "true", "no": "false"} +EXPECTED = 9 def rows(text): for r in csv.DictReader(io.StringIO(text)): method = (r["Method Name"] or "").strip() - safe, idempotent = (r["Safe"] or "").strip(), (r["Idempotent"] or "").strip() - if not re.fullmatch(r"[A-Za-z][A-Za-z-]*", method) or not safe or not idempotent: + if not CORE.search(r["Reference"] or ""): continue - for column, answer in (("Safe", safe), ("Idempotent", idempotent)): - if answer not in ANSWERS: + answers = {} + for column in ("Safe", "Idempotent"): + answer = (r[column] or "").strip() + if answer not in BOOLEAN: sys.exit(f"{method}: {column} {answer!r} is neither yes nor no") - yield {"idempotent": idempotent, "method": method, "safe": safe} + answers[column] = BOOLEAN[answer] + yield {"idempotent": answers["Idempotent"], "method": method, "safe": answers["Safe"]} def main(): @@ -42,6 +48,8 @@ def main(): p.add_argument("--out", default=str(OUT)) a = p.parse_args() table = sorted(rows(source.fetch(a.source, a.cache, "http-methods.csv").decode("utf-8")), key=lambda r: r["method"]) + if len(table) != EXPECTED: + sys.exit(f"{len(table)} methods cite the core specification, not {EXPECTED}; the register's Reference column has moved") tsv.write(a.out, COLUMNS, table) diff --git a/data/misc/httpmethod.tsv b/data/misc/httpmethod.tsv index 872ec7c..b9e4108 100644 --- a/data/misc/httpmethod.tsv +++ b/data/misc/httpmethod.tsv @@ -1,41 +1,10 @@ idempotent method safe -yes ACL no -yes BASELINE-CONTROL no -yes BIND no -yes CHECKIN no -yes CHECKOUT no -no CONNECT no -yes COPY no -yes DELETE no -yes GET yes -yes HEAD yes -yes LABEL no -yes LINK no -no LOCK no -yes MERGE no -yes MKACTIVITY no -yes MKCALENDAR no -yes MKCOL no -yes MKREDIRECTREF no -yes MKWORKSPACE no -yes MOVE no -yes OPTIONS yes -yes ORDERPATCH no -no PATCH no -no POST no -yes PRI yes -yes PROPFIND yes -yes PROPPATCH no -yes PUT no -yes QUERY yes -yes REBIND no -yes REPORT yes -yes SEARCH yes -yes TRACE yes -yes UNBIND no -yes UNCHECKOUT no -yes UNLINK no -yes UNLOCK no -yes UPDATE no -yes UPDATEREDIRECTREF no -yes VERSION-CONTROL no +false CONNECT false +true DELETE false +true GET true +true HEAD true +true OPTIONS true +false PATCH false +false POST false +true PUT false +true TRACE true diff --git a/todo.md b/todo.md index ba184dd..30529c7 100644 --- a/todo.md +++ b/todo.md @@ -103,6 +103,11 @@ by a `data-import/` script, as README goal 10 asks. - Add `{base64url(n)}` for JWT shapes. - Add a `unix` layout to `date()` once something needs it. +- Name a spelling that works when a row selector misses: `misc.protocol[tcp]`, + `misc.httpmethod[get]` and `misc.territory[se]` all answer "no row … has key or name" + and stop there, where a case-insensitive match could name the row that exists, and a + table with no `name` column could say it selects by key alone. + ### Open questions to settle - Decide whether `misc.browser` becomes a parent of `misc.useragent`, so