Documentation/Reference

HTTP and HTTPS

On this page

Implemented in BASIL, with ordinary suspending functions and owned TCP connections. The protocol follows RFC 9110 and RFC 9112 .

A small server

router=HTTPRouter()
RouteGet(BYREF router,"/hello/{name}",Hello)
MountDirectory(BYREF router,"/static/","/var/web")
listener=ListenTCP("127.0.0.1",8080)
WHILE TRUE
    connection=Accept(listener)
    SPAWN ServeHTTP(TAKE connection,router)
WEND

FUNCTION Hello(request AS HTTPRequest) AS HTTPResponse
    RETURN TextResponse("Hello "+Get(request.Parameters,"name",""))
END FUNCTION

Each child owns its connection and an independent router snapshot. Scope exit cancels and joins children and closes connections. No handshake or status channel is needed. ServeHTTP(listener,router) also supplies the accept/spawn loop. Serving errors are translated to HTTP responses when possible; disconnected peers simply end that connection. ServeHTTPChecked(connection,router,options) exposes operational/protocol and original application errors to a supervising function. Mutable router slots explicitly admit Error, so a checked server caller must handle or declare that open family. Convenience servers recover handler failures and report concrete error names. Ordinary handler signatures and ownership remain checked statically.

Route accepts an explicit method; RouteGet and RoutePost are conveniences. Literal segments beat {parameter} segments, which beat final {*remainder}. Duplicate method/pattern shapes are rejected. Unknown paths return 404; wrong methods return 405 with Allow. HEAD falls back to GET and suppresses body bytes. OPTIONS is supported. Paths are percent-decoded once; malformed encodings, dot segments, NUL, backslash and encoded slashes are rejected. Query strings remain separate; HTTPQueryValues decodes form-style query parameters.

Buffered handlers receive request.Body as LIST OF BYTE and request.Trailers. TextResponse, BytesResponse and RedirectResponse construct independent values. Streaming handlers are SUBs taking (request AS HTTPRequest, connection AS TCPConnection, BYREF exchange AS HTTPExchange) and declaring FAILS IoError,HTTPError,IndexError,StringError. Use ReadHTTPBody, StartHTTPResponse, WriteHTTPBody, FinishHTTPResponse; alternatively use WriteHTTPResponse. Read chunks until empty before reusing a connection. ReadHTTPBodyAll is a bounded convenience. Chunked output is selected when no Content-Length is supplied. Explicit lengths must match actual bytes. Trailers are exposed separately from headers; application-defined trailer fields must permit trailer use. The library rejects core framing/routing/authentication fields rather than merging them into initial headers.

UseHTTP registers streaming middleware; writing a response ends dispatch. exchange.Context carries string context to subsequent middleware/handlers through request.Context. Buffered request bodies are read before middleware runs. HTTPFallback installs a streaming fallback. These are checked noncapturing function values, not escaping closures. Route configuration may carry independent string values.

HTTPS and clients

ServeHTTPS(connection,router,tlsOptions [,httpOptions]) upgrades the same owned connection using TLSServerOptions. A listener overload supplies the accept loop. Certificates and keys use the TLS interfaces documented in networking ; https-server.bas is a complete example. ALPN is restricted to http/1.1. Verification remains enabled for HTTPS clients.

response=HTTPGet("https://example.com/")
PRINT response.Status
PRINT DecodeUtf8(response.Body)

HTTPPost accepts STRING or LIST OF BYTE. RequestHTTP accepts arbitrary methods. HTTPClientOptions combines HTTPOptions and TLSClientOptions. Redirects are bounded; cross-origin redirects strip authorization/cookies, and HTTPS downgrade is forbidden by default. A redirect limit of zero disables following. HTTPGetPlain and RequestPlainHTTP reject HTTPS and allow the static linker to omit TLS entirely. Generic scheme-selecting clients necessarily retain both transports.

For explicit streaming/reuse: ParseHTTPURL, ConnectHTTP, HTTPExchange(options), StartHTTPRequest, WriteHTTPBody, FinishHTTPRequest, ReadHTTPResponse, then ReadHTTPBody until empty. Finish/drain one exchange before starting another. Errors require abandoning that connection; do not reuse damaged framing state. The buffered client does this automatically. There is no automatic connection pool, proxy, cookie jar, decompression, HTTP/2, WebSocket upgrade or CONNECT tunnel in this slice.

Directory serving and file workers

listener=ListenTCP("127.0.0.1",8080)
WHILE TRUE
    connection=Accept(listener)
    SPAWN ServeDirectory(TAKE connection,"/var/web")
WEND

The same handler backs MountDirectory. GET/HEAD, index.html, MIME types, weak ETag, If-None-Match, Last-Modified and single byte ranges are supported. Multi-range requests are ignored; If-Range falls back to a full response. Listings are opt-in with HTTPOptions().DirectoryListings configured through a local options value; HTML and URL components are escaped. Hidden files are disabled by default. Linux openat2 containment rejects child symlinks and traversal, including symlinks within the root. Unsupported kernels fail explicitly; there is no unsafe path-prefix fallback. Files stream in 32 KiB chunks. Listings are bounded snapshots, not unbounded traversal.

Ordinary open/read/stat/directory/seek APIs now park their coroutine while four bounded filesystem workers execute the operation. Cancellation removes queued work immediately; running kernel operations must acknowledge completion before resources/buffers are released. A stuck kernel operation can therefore delay cancellation. File writes, flush, atomic save and other mutation APIs have not all moved to workers yet.

Limits and protocol policy

HTTPOptions defaults: idle 30 s, headers 10 s, body 30 s, writes 30 s; start line 8192 bytes, headers 32768 bytes/100 fields, body 16 MiB, chunks 65536, requests per connection 1000, redirects 5. Each phase uses its deadline; arbitrary handler CPU work is not preempted. Streaming still enforces body limits. Header values must be UTF-8. Strict CRLF and framing checks reject duplicate Content-Length, TE+CL, unsupported transfer coding, malformed chunks/trailers and premature EOF. Request header folding is rejected; response folding is normalized. Expect: 100-continue is supported. HEAD/204/304 suppress bodies. A partial response is never followed by a second status. Allocation failure remains fatal, as elsewhere in the language.

Typed JSON helpers

Typed URLs and shared parsing

URL values/builders provide ParseURL, component access, JoinURLPath(base,path,Query=parameters), repeated query values and explicit RFC reference resolution. HTTPGet/HTTPPost and request helpers accept typed URL values. ParseHTTPURL and Host-header adaptation use that parser with the existing stricter HTTP authority/port/transport policy. Redirects now use the shared RFC resolver; encoded delimiters retain their meaning and fragment data is not sent as a target.

From the BASIL documentation. View source ↗

Find your way around BASIL

Type to search the manual.