A reckoner seed: https, a reply taken apart — machines/seeds/seedhttps.shoddy
seednet fetches a URL and hands back a wall of text. This seed is the half that makes the wall useful.
> "example.com" "/" { { "user-agent" "shoddy" } } HTTPSGET "R" STO
> "R" RCL HTTPSTATUS
x: 200
> "R" RCL "content-type" "?" HTTPHEADER
x: "text/html; charset=utf-8"
> "R" RCL HTTPBODY
x: "<!doctype html>…"
The easiest seed in the directory, and
https earned that. The machine has not
one Error in it. Its header states the rule this seed
leans on: “Pure where it can be… Building a request and
parsing a reply are functions on Strings… Only
HttpsGet and HttpsRequest reach the
network.”
So five of the seven words below are the machine's own, bridged
straight through with nothing but an argument check. The sixth and
seventh replace HttpsRequest, and only because it sends
through net's RequestTls and the
aborting socket builtins under it — builtins that end the
whole session on failure. The request text is still
HttpRequestText's, byte for byte. Only the sending
is done differently.
A redirect is reported, not chased. (A redirect is a
reply, status 301 among others, that says the page has moved and where.)
That is https's own policy and this seed keeps
it. Chasing a 301 means deciding about loops, cross-host hops and method
rewriting, and the machine calls those the caller's policy rather than a
library's. A session is a caller like any other: HTTPSTATUS
says 301 and HTTPHEADER finds the location.
Text that is not a reply answers rather than refusing.
The parsing words answer a status of 0, an empty body, an
empty dict. That is the right answer for a caller holding something
unexpected, and better than a refusal they cannot act on.
The guards that matter are the smuggling ones. A line break in a header, a host, a path or a method does not make a malformed request. It makes two requests, the second written by whoever supplied the string. Nothing downstream would notice. So each is refused here, and each has its own assertion in the test suite. A space in a path is the same fault by another route: it ends the request line early.
NETBODY is gone, and this is where it went.
seednet carried one that split a reply at the
blank line, written before this seed existed. HTTPBODY is
https's own and does the job properly. Keeping
both would have been the second spelling of a word the dictionary already
had — the thing seedbuiltin's own
rules exist to prevent.
Headers are a dict, in and out, which is
seeddict's shape. (A header is one named line
of a request or reply, such as content-type.) Names come
back lowercased, because HTTP header names are case-insensitive
and https normalises them at the boundary. So
DGET finds content-type whichever way the
server wrote it. On the way in, a header may be a pair or a two-element
list. { { "accept" "text/html" } } is what somebody types
before they have met the pair syntax, and it is unambiguous.
> "example.com" "/" { { "user-agent" "shoddy" } } HTTPSGET "R" STO
ok: R
> "R" RCL HTTPSTATUS
x: 200
> "R" RCL HTTPREASON
x: "OK"
> "R" RCL HTTPHEADERS "server" DGET
x: "ECAcc (dcd/7D5A)"
> "google.com" "/" { } HTTPSGET "D" STO
> "D" RCL HTTPSTATUS
x: 301 ' reported, not chased
> "D" RCL "location" "none" HTTPHEADER
x: "https://www.google.com/"
Without --allow-net the two fetching words refuse and
name the remedy. The five parsing words work regardless, because they
never touch a socket (a network connection). A good many hosts answer 403
("forbidden") to a request with no user-agent, which is why the examples
send one.
| Word | Description |
|---|---|
| HTTPSGET ( host path headers -- reply ) | Fetch a path from a host over TLS (the encryption behind https) with headers of your own, and answer the whole raw reply. Use { } for none. |
| HTTPSREQUEST ( method host path headers -- reply ) | The same for any method — GET, HEAD, DELETE. No body is sent, which is what HTTP/1.0 makes straightforward. |
| HTTPSTATUS ( reply -- n ) | The status number — 200, 404, 301 — and 0 for something that is not a reply at all. |
| HTTPREASON ( reply -- str ) | The reason phrase beside it: "OK", "Not Found". |
| HTTPBODY ( reply -- str ) | Everything after the blank line that ends the headers. Bytes as they arrived, one per character. |
| HTTPHEADERS ( reply -- dict ) | Every header as a dict, names lowercased. DGET reads one out. |
| HTTPHEADER ( reply name dflt -- str ) | One header by name, or dflt when it is not there. Matched without regard to case. |
The machine's limits are this seed's too, and they are
stated rather than worked around: HTTP/1.0, no redirects followed, no
compression. The body is bytes — a UTF-8 page arrives as its bytes,
which WRITEFILE reproduces exactly.
| User | How | |
|---|---|---|
| halifax | The calculator's structured fetch — a reply taken apart into a status, headers and a body, and the words that do it work with no network at all. | |
| sparky | Sparky folds it too, so a model calling eval reaches the same words halifax puts at a prompt. |
A mill claims this seed by folding RckSeedHttps over its
reckoner state, which is all halifax does.
| Machine | Why | |
|---|---|---|
| cuttle | The Cell type replies and headers cross the bridge as. | |
| https | The domain this seed bridges: HttpStatus, HttpReason, HttpBody, HttpHeaders and HttpHeader bridged straight through, and HttpRequestText to build what goes on the wire. | |
| reckoner | RckReg and the argument readers every registered word is built from. | |
| seq | Pair and the list plumbing under the header conversions. |