moonfetch
moonbitstack/moonfetch
The client, the request it sends, the response it reads, and the values both are made of.
fnframes
The frames an HTTP/2 request is written as: the connection preface, this side's settings, and
the HEADERS and DATA frames of the exchange.
The connection is a fresh one, so the field section is encoded against the static table alone,
which is what a first request on a connection looks like.
fn frames(@method.Method, StringView, query? : Array[(String, String)], headers? : Array[(String, String)], body? : Body) -> Wire raise
letidle
How many idle HTTP/1.1 connections are kept per origin: urllib3's 10.
let idle : Int32
letlimits
The bounds a client uses when none are given: a 64 KiB head, 256 fields, and a 32 MiB body.
No client of the common ones bounds a body by default; a library that returns a Response
with the whole body in memory has to, or one answer can exhaust the process. 32 MiB covers
the API payloads a buffered call is for; anything larger is read as a Stream, which is not
bounded.
let limits : Limits
letredirects
How many redirects are followed when nothing else is said: reqwest's and requests' 10.
A host that hands the redirect to its own stack follows as many as it allows; this is the
number requests say.
let redirects : Int32
lettimeout
How long a request may take when nothing else is said, in milliseconds: httpx's 5 seconds.
let timeout : Int32
fnwire
The octets an HTTP/1.1 request is written as, for a caller that wants to see them: the head
with its framing, then the body.
A body produced while the request goes out is not read here — taking its pieces would spend
them — so only the head is written, with the framing that says a body follows.
Refused::Url for a URL no request can name, and the state machine's own refusal for a field
it cannot write.
fn wire(@method.Method, StringView, query? : Array[(String, String)], headers? : Array[(String, String)], body? : Body) -> Wire raise
suberrorRefused
What moonfetch itself refuses. Errors from below pass through as they are: a socket that
will not connect, a TLS alert, a response that does not parse.
suberror Refused {
- Status(status~ : Int32, url~ : String)
- Redirects(limit~ : Int32)
- Reset(code~ : Int32)
- Timeout(limit~ : Int32)
- Url(String)
- Exceeded(limit~ : Int32, got~ : Int32)
- Proxy(String)
Implements Show.
enumBody
A request body. One value, so a request cannot carry two bodies for the library to choose
between.
enum Body {
- Empty
- Text(String)
- Bytes(Bytes)
- Json(@json.Json)
- Form(Array[(String, String)])
- Multipart(Array[Part])
- Stream(Source)
enumChunk
A piece of a response body as it arrives.
enum Chunk {
- Data(Bytes)
- End(Array[(String, String)])
typeClient
An HTTP client over the host's fetch.
base_url is the prefix relative URLs are joined to, as the client the other host has joins
them. headers go on every request. timeout bounds each request, redirects included;
None waits for ever. redirects caps how many are followed; 0 follows none. credentials
is what the host may do with its cookies. limits bounds a buffered response.
The host owns the connections, the cookie store, the proxy settings and the content codings,
so there is no pool, jar or proxy here: close has nothing to close.
type Client
fn Client::close(Self) -> Unit
Close every connection the client keeps.
fn Client::new(base_url? : String, headers? : Array[(String, String)], timeout? : Int32?, redirects? : Int32, http2? : Bool, compression? : Bool, http3? : Bool, idle? : Int32, h2c? : Bool, limits? : Limits, jar? : Jar, proxy? : Proxy, tls? : @tls.Client) -> Self raise Refused
A client.
base_url is the prefix relative URLs are joined to, as httpx joins them: its path is kept,
so /users under https://a.example/api is https://a.example/api/users. headers go on
every request. timeout bounds each request, redirects included; None waits for ever.
redirects caps how many are followed; 0 follows none. http2 offers HTTP/2 by ALPN;
h2c speaks HTTP/2 with prior knowledge over http, where there is no ALPN to ask.
compression asks for gzip or deflate and decodes what comes back, as httpx and reqwest do;
a request that names its own accept-encoding gets the body as sent.
http3 speaks HTTP/3 over QUIC where it can, and falls back to TCP for an origin that does
not answer on the datagram, as a browser's Alt-Svc does without the cache.
limits bounds what a buffered response may hold. jar keeps cookies across requests.
proxy sends the connection through an HTTP or SOCKS5 proxy.
tls judges servers' certificates, the Mozilla roots when not given; its ALPN list is set
by http2.
structJar
A cookie store for a Client (RFC 6265bis).
One jar may be shared by several clients and by tasks: every change goes through a lock.
Persistence is not part of it — a jar lives as long as the process does.
struct Jar {
fn Jar::clear(Self) -> Unit
fn Jar::new() -> Self
An empty jar.
fn Jar::size(Self) -> Int32
How many cookies the jar holds.
structLimits
What a client reads of a response before it refuses it.
head bounds the status line and header block, fields the number of header fields, and
body the body as it arrives, before any content coding is undone. The body bound is
checked as the octets come in, so a server that would send more is stopped early rather
than after the allocation.
struct Limits {
- head : Int32
- fields : Int32
- body : Int32
fn Limits::new(head? : Int32, fields? : Int32, body? : Int32) -> Self
The same bounds with some replaced.
structPart
One part of a multipart/form-data body (RFC 7578): a form field when filename is None,
a file otherwise.
struct Part {
- name : String
- content : Bytes
- filename : String?
- content_type : String?
fn Part::content(Self) -> Bytes
fn Part::content_type(Self) -> String?
fn Part::field(StringView, StringView) -> Self
A form field.
fn Part::file(StringView, StringView, Bytes, content_type? : StringView) -> Self
A file part. content_type defaults to application/octet-stream, as browsers do.
fn Part::filename(Self) -> String?
fn Part::name(Self) -> String
structProxy
A proxy a client sends its connections through.
http and https are the CONNECT method of RFC 9110 §9.3.6 — an https proxy speaks TLS
to the proxy itself — and socks5 is RFC 1928, with the host sent as a name so the proxy
resolves it. The user and password, when present, authenticate to the proxy: Basic for
CONNECT, username and password for SOCKS5.
struct Proxy {
- scheme : String
- host : String
- port : Int32
- user : String?
- password : String?
fn Proxy::parse(StringView) -> Self raise Refused
A proxy from a URL: http://user:pass@host:1080, socks5://host:1080.
structResponse
A response, read whole. Header names are lower case, as HTTP/2 writes them, and keep the
order they arrived in.
struct Response {
- status : Int32
- reason : String
- version : String
- headers : Array[(String, String)]
- url : String
- body : Bytes
fn Response::checked(Self) -> Self raise Refused
The response itself, or Status for a 4xx or 5xx, as reqwest's error_for_status does.
fn Response::is_success(Self) -> Bool
Whether the status is 2xx.
fn Response::json(Self) -> @json.Json raise @moonjson.Refused
The body parsed as JSON, in the strict reading of RFC 8259: no comment, no trailing comma,
and nothing after the one value.
fn Response::text(Self) -> String
The body as UTF-8 text; a malformed sequence reads as U+FFFD.
fn Response::values(Self, StringView) -> Array[String]
Every value of the header name, in order: what set-cookie needs.
structSource
A request body that is not known when the request is made: the steps are called as the
connection has room for more.
struct Source {
- length : Int64?
- step : async () -> Bytes?
Implements Eq.
fn Source::length(Self) -> Int64?
The length the source promises, when it promises one.
fn Source::new(async () -> Bytes?) -> Self
A body whose length is not known in advance; HTTP/1.1 sends it chunked, HTTP/2 as data
frames.
fn Source::sized(Int64, async () -> Bytes?) -> Self
A body of exactly length octets: HTTP/1.1 and HTTP/2 both declare the length, so no
chunking and no close-delimited body. A source that ends early or runs long fails the
request.
structStream
A response whose body is read a piece at a time, for a body too large to hold or one that
never ends: a server-sent event feed, a download that is written as it comes.
One task reads one stream. close gives up the rest; a program that just drops it leaves
the exchange unfinished until the client is closed.
struct Stream {
fn Stream::close(Self) -> Unit
Give up the rest of the body: an HTTP/2 stream is reset, an HTTP/1.1 connection is closed.
fn Stream::reason(Self) -> String
fn Stream::status(Self) -> Int32
fn Stream::url(Self) -> String
fn Stream::version(Self) -> String
Empty on this host: it does not say which version answered.
structWire
How a request would go on the wire: the version that would carry it, and the pieces it is
written as, in order.
The pieces of an HTTP/1.1 request are its head and then its body; those of an HTTP/2 one are
the connection preface, this side's settings, and then one entry per frame, labelled with the
frame's type and the flags it carries.
struct Wire {
- version : String
- pieces : Array[(String, Bytes)]