- Scheme 75%
- Shell 17.4%
- Rust 4.5%
- Makefile 3%
- Common Lisp 0.1%
| .builds | ||
| .jerboa | ||
| docs | ||
| fuzz | ||
| lib/jerboa-https/sandbox | ||
| scripts | ||
| src | ||
| support | ||
| tests | ||
| wasm | ||
| .gitignore | ||
| AGENTS.md | ||
| chez-https.md | ||
| dependencies.lock | ||
| jpkg.sexp | ||
| LICENSE | ||
| Makefile | ||
| README.md | ||
| SECURITY.md | ||
chez-https
Native HTTP/1.1 client over TLS for Chez Scheme. No subprocesses. No curl. Pure in-process HTTPS.
Dependencies
jerboa-ssl— TLS transport layer, locked by commit and tree independencies.lock- Chez Scheme 10.x
- OpenSSL (
libssl,libcrypto)
Installation
# From this checkout, fetch the reviewed dependency revision.
support/locked-dependency.sh fetch dependencies.lock jerboa-ssl ~/mine/jerboa-ssl
make
CI uses the same lock verifier. Updating jerboa-ssl requires a reviewed
commit and tree-hash change in dependencies.lock; default branches are never
consumed by the build.
Usage
(import (chez-https))
;; Simple GET
(let ([req (http-get "https://example.com/")])
(display (request-status req)) ;; 200
(display (request-text req)) ;; HTML body as string
(request-close req))
;; With headers and query parameters (S3-compatible format)
(let ([headers (list (list "Authorization" ':: "AWS4-HMAC-SHA256 ...")
'::
(list "Host" ':: "bucket.s3.amazonaws.com")
(list "x-amz-date" ':: "20260306T120000Z"))]
[params (list (list "list-type" ':: "2")
(list "prefix" ':: "AWSLogs/"))])
(let ([req (http-get url 'headers: headers 'params: params)])
(display (request-status req))
(request-close req)))
;; PUT with body
(let ([req (http-put url
'headers: headers
'params: #f
'data: (string->utf8 "request body"))])
(display (request-status req))
(request-close req))
API
HTTP Methods
(http-get url ['headers: hdrs] ['params: params])
(http-post url ['headers: hdrs] ['params: params] ['data: body])
(http-put url ['headers: hdrs] ['params: params] ['data: body])
(http-delete url ['headers: hdrs] ['params: params])
(http-head url ['headers: hdrs] ['params: params])
Response Accessors
| Function | Returns | Description |
|---|---|---|
(request-status req) |
integer | HTTP status code (200, 404, etc.) |
(request-text req) |
string | Response body as UTF-8 string |
(request-content req) |
bytevector | Response body as raw bytes |
(request-headers req) |
alist | Response headers (lowercase keys) |
(request-header req name) |
string or #f |
Single header value lookup |
(request-close req) |
void | No-op (connection already closed) |
Utilities
| Function | Description |
|---|---|
(parse-url url) |
Returns (values host port path) |
(url-encode str) |
RFC 3986 percent-encoding |
(flatten-request-headers hdrs) |
Flatten (name :: value) format to alist |
(build-query-string params) |
Build URL query string from params |
Response policy
(http-client-config) returns the active bounded response policy. Set startup
limits with options such as max-header-bytes:, max-headers:, max-body:,
max-chunk:, max-chunked-wire:, idle-timeout:, and total-timeout:.
Values must be positive exact integers. Configure the policy before issuing
concurrent requests.
Features
- HTTPS via OpenSSL (TLS 1.2/1.3)
- SNI and hostname verification
- Streaming, bounded chunked transfer decoding
- Strict duplicate/conflicting response-framing rejection
- Header, decoded-body, raw-chunked-body, chunk-size, idle, and total limits
- RFC-token/control-character validation before request serialization
- 100 Continue handling
- Binary response body preservation (critical for gzip/binary downloads)
- Configurable HTTPD worker count, listen backlog, queue capacity, request timeout, response timeout, header count, and body size limits
- Bounded TLS handshake workers, queue, short handshake deadline, and global/per-IP pre-authentication limits
- Descriptor-pinned static-file serving with canonical containment and inode identity verification
- S3-compatible header format (
::separator convention) - URL encoding (RFC 3986)
- Automatic SSL initialization
Testing
make test
The tests include a local self-signed TLS peer for hostile response framing and
deadline cases. Interpreter-based development explicitly opts into loading
jerboa-ssl from its canonical absolute directory; production binaries should
statically register the TLS shim.
Release Evidence
The HTTPD surface is private until the daemon release gate is complete. Run:
make release-evidence
The release bundle includes SBOM, reproducibility, TLS dependency, HTTPD
fuzz-evidence, and HTTPD soak status under dist/release-evidence/. Default
fuzz evidence records fixed adversarial cases, a 2048-case generated
deterministic HTTPD corpus, and a present cargo-fuzz harness for the sandboxed
HTTP parser. Run with JHTTPS_RUN_COVERAGE_FUZZ=1 to record bounded local
coverage evidence; production release still requires longer release-host fuzz
evidence or a signed exception. Default local soak evidence records load and queue-exhaustion
soak as blocked/not-run. Opt-in local soak evidence records deterministic
slow-client coverage plus bounded load/queue smokes, but production release
still requires target-host sustained load evidence.
HTTPD TLS/proxy policy is documented in docs/tls-proxy-policy.md and copied
into dist/release-evidence/tls-proxy-policy.txt.
Architecture
chez-https (this library)
│
└── jerboa-ssl (TLS transport)
│
└── OpenSSL (libssl, libcrypto)
chez-https is Scheme on top of chez-ssl for I/O. The HTTPD parser boundary
uses a small Rust/WASM sandbox for request lines, header lines, and chunk sizes
when the Jerboa WASM sandbox is available; JHTTPS_REQUIRE_WASM_HTTP=1 makes
that boundary fail closed for production deployments.