The Machines · Runtime stack

seedhttps

A reckoner seed: https, a reply taken apart — machines/seeds/seedhttps.shoddy

the seedhttps machine's icon

Summary

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>…"

Why It's Useful

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.

User's Guide

> "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 Reference

WordDescription
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.

Who Uses It

UserHow
halifaxThe 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.
sparkySparky 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.

The Machines It Uses

MachineWhy
cuttleThe Cell type replies and headers cross the bridge as.
httpsThe domain this seed bridges: HttpStatus, HttpReason, HttpBody, HttpHeaders and HttpHeader bridged straight through, and HttpRequestText to build what goes on the wire.
reckonerRckReg and the argument readers every registered word is built from.
seqPair and the list plumbing under the header conversions.