httpd-part-content-type
(httpd-part-content-type [part ptr<void>] :)
per-part Content-Type (or "" if absent).
No matching definitions.
Lightweight HTTP/1.0 and HTTP/1.1 server.
Since: Phase H1
(httpd-register-tls-impl [wrap-fd : int handshake : int read-fn : int write-fn : int shutdown : int free-fn : int] :)
install TLS function pointers into the H5 hook table.
| wrap-fd | (int64_t)(intptr_t)&tls-wrap-fd | |
| handshake | (int64_t)(intptr_t)&tls-handshake | |
| read-fn | (int64_t)(intptr_t)&tls-read | |
| write-fn | (int64_t)(intptr_t)&tls-write | |
| shutdown | (int64_t)(intptr_t)&tls-shutdown | |
| free-fn | (int64_t)(intptr_t)&tls-free |
Since: Phase H5
(httpd-handle [fd : int handler : int tls : int] :)
internal: serve one or more HTTP requests on fd, then
Since: Phase H1 (TLS path: Phase H5)
(httpd-worker [arg ptr<void>] :)
internal: worker thread function.
Since: Phase H3
(httpd-accept-cb [env ptr<void> id : int events : int user ptr<void>] :)
internal reactor READ callback for the listen fd.
Since: Phase H2
(httpd-new-pool [port : int workers : int ^fat handler : int])
create an httpd instance with a configurable worker count.
| port | TCP port to listen on (0 = kernel-assigned) | |
| workers | number of worker threads in the pool (must be > 0) | |
| handler | fat closure: (fn [conn :ptr<void>] :nil) |
ptr<void> -- opaque Httpd handle, or nil on error
Since: Phase H3
(httpd-new [port : int ^fat handler : int])
create a new httpd instance with the default worker pool size (4).
| port | TCP port to listen on (0 = kernel-assigned) | |
| handler | fat closure: (fn [conn :ptr<void>] :nil) |
ptr<void> -- opaque Httpd handle, or nil on error
(let [h (httpd-new 0 (fn [conn :ptr<void>] :nil
(httpd-resp-status! conn 200)
(httpd-resp-body! conn "OK")))]
(httpd-run h)
(httpd-free h))
Since: Phase H1
(httpd-new-tls [port : int workers : int ^fat handler : int ctx : int])
create an httpd instance with TLS termination.
| port | TCP port to listen on (0 = kernel-assigned) | |
| workers | number of worker threads (must be > 0) | |
| handler | fat closure: (fn [conn :ptr<void>] :nil) | |
| ctx | TLS context handle from tls-ctx-new with cert + key loaded |
ptr<void> -- opaque Httpd handle, or nil if ctx is 0 or TLS ops have not been registered.
(tls-httpd-init)
(let [ctx (tls-ctx-new)]
(tls-ctx-load-cert-pem ctx "cert.pem")
(tls-ctx-load-key-pem ctx "key.pem")
(let [h (httpd-new-tls 8443 4 my-handler ctx)]
(httpd-run h)
(httpd-free h)
(tls-ctx-free ctx)))
Since: Phase H5
(httpd-port [h ptr<void>] :)
return the port the server is listening on.
| h | httpd handle returned by httpd-new |
int -- the actual listening port, or -1 on error
Since: Phase H1
(httpd-run [h ptr<void>])
run the listener reactor until httpd-stop is called.
Since: Phase H1
(httpd-stop [h ptr<void>])
signal the listener to stop accepting new connections.
Since: Phase H1
(httpd-free [h ptr<void>] :)
free all resources after httpd-run returns.
Since: Phase H1
(httpd-req-method [conn ptr<void>] :)
return the HTTP method from the parsed request.
Since: Phase H1
(httpd-req-path [conn ptr<void>] :)
return the request-target path from the parsed request.
Since: Phase H1
(httpd-req-version [conn ptr<void>] :)
return the HTTP version from the parsed request line.
cstr -- "HTTP/1.0", "HTTP/1.1", etc., or "" if unparsed
Since: Phase H4
(httpd-req-body [conn ptr<void>] :)
return the request body (NUL-terminated copy).
cstr -- the request body bytes (NUL-terminated)
Since: Phase H4
(httpd-req-body-len [conn ptr<void>] :)
return the request body length in bytes.
int -- number of bytes in the body (0 if no body)
Since: Phase H4
(httpd-resp-status-get [conn ptr<void>] :)
return the currently-set response status code.
Since: Phase H6
(httpd-resp-status! [conn ptr<void> status : int] :)
set the HTTP response status code.
Since: Phase H1
(httpd-resp-body! [conn ptr<void> body : cstr] :)
set the HTTP response body (copies the string).
Since: Phase H1
(httpd-resp-body-bytes! [conn ptr<void> bytes ptr<void> len : int] :)
binary-safe response body setter.
| conn | connection handle from the worker | |
| bytes | pointer to raw payload (may contain NUL bytes) | |
| len | byte count to copy |
(httpd-resp-body-bytes! c gzip-data gzip-len)
Since: Phase H8 (M6)
(httpd-req-header [conn ptr<void> name : cstr] :)
look up a request header by name (case-insensitive).
| conn | connection handle from the worker | |
| name | header name to look up (case-insensitive) |
The header value as a cstr (pointer into per-request storage; valid only for the duration of the handler call). Empty string if absent.
(let [ua (httpd-req-header c "User-Agent")] ...)
Since: Phase H8 (M0)
(httpd-req-header? [conn ptr<void> name : cstr] :)
presence test for a request header.
| conn | connection handle from the worker | |
| name | header name (case-insensitive) |
1 if present, 0 otherwise.
(when (= 1 (httpd-req-header? c "Authorization")) ...)
Since: Phase H8 (M0)
(httpd-resp-header! [conn ptr<void> name : cstr value : cstr] :)
set (or replace) a response header.
| conn | connection handle from the worker | |
| name | header name (case-insensitive on the dedup check) | |
| value | header value |
(httpd-resp-header! c "Content-Type" "application/json")
Since: Phase H8 (M0)
(httpd-resp-header-add! [conn ptr<void> name : cstr value : cstr] :)
append a response header without de-duplication.
| conn | connection handle from the worker | |
| name | header name | |
| value | header value |
(httpd-resp-header-add! c "Set-Cookie" "session=abc; Path=/") (httpd-resp-header-add! c "Set-Cookie" "csrf=xyz; Path=/")
Since: Phase H8 (M0)
(httpd-req-cookie [conn ptr<void> name : cstr] :)
read a single cookie value from the Cookie: header.
| conn | connection handle from the worker | |
| name | cookie name to look up (case-sensitive) |
The cookie's value as a heap-allocated cstr, OR the empty string "" if the header is absent or the cookie is not present. NOTE: when the cookie IS found, the returned cstr is a fresh malloc that the runtime treats as per-call ownership; do not free. When absent the returned pointer is a literal "" and also need not be freed.
(let [sid (httpd-req-cookie c "session")]
(if (= (cstr-len sid) 0) ...))
Since: Phase H8 (M2)
(defstruct CookieOpts [])
fully-specified Set-Cookie attributes.
Since: Phase H8 (M2)
(cookie [name : cstr value : cstr] :)
construct a CookieOpts with sensible defaults.
| name | cookie name | |
| value | cookie value |
(httpd-set-cookie! c (cookie "session" "abc123"))
Since: Phase H8 (M2)
(cookie-full [name : cstr value : cstr path : cstr domain : cstr max-age : int secure : int http-only : int same-site : cstr] :)
construct a CookieOpts with every attribute explicit.
(httpd-set-cookie! c (cookie-full "tracking" "t1" "/api" "example.com"
3600 1 0 "None"))
Since: Phase H8 (M2)
(httpd-set-cookie! [conn ptr<void> opts : CookieOpts] :)
emit a Set-Cookie response header from a CookieOpts.
| conn | connection handle from the worker | |
| opts | CookieOpts value (typically built by `cookie` or `cookie-full`) |
(httpd-set-cookie! c (cookie "session" "abc123"))
Since: Phase H8 (M2); requires the DS1 fix in
(httpd-req-form [conn ptr<void> field : cstr] :)
look up a single URL-encoded form field by name.
| conn | connection handle from the worker | |
| field | form field name (case-sensitive, plain ASCII) |
The decoded value as a heap-allocated cstr (per-call ownership; do not free explicitly -- the runtime drops it after the handler returns). Empty string "" when the field is absent or the body is empty.
(let [user (httpd-req-form c "username")
pw (httpd-req-form c "password")] ...)
Since: Phase H8 (M3)
(httpd-req-json [conn ptr<void>] :)
lazily parse the request body as JSON.
| conn | connection handle from the worker |
The parsed JSON node tree as an :int (the json stdlib carrier), or 0 when there is no body / parse failed.
(let [doc (httpd-req-json c)
nm (json/get-string (json/get! doc "name"))]
(httpd-resp-body! c nm))
Since: Phase H8 (M3)
(mw-json-body [^fat next : int] :)
pre-parse the JSON body, 400 on failure.
(let [composed (compose-middleware base mw-json-body)]
(httpd-new 0 composed))
Since: Phase H8 (M3)
(httpd-mw-json-body-precheck [conn ptr<void>] :)
internal: returns 1 when the body is
(defstruct CorsOpts [])
mw-cors configuration.
Since: Phase H8 (M4)
(default-cors-opts :)
permissive defaults suitable for development.
Since: Phase H8 (M4)
(httpd-mw-cors-is-preflight [conn ptr<void>] :)
internal: 1 iff this is a CORS preflight.
(httpd-mw-cors-emit-preflight [conn ptr<void> allow-origin : cstr allow-methods : cstr allow-headers : cstr allow-credentials : int max-age : int] :)
internal: write the preflight response.
(httpd-mw-cors-decorate [conn ptr<void> allow-origin : cstr expose-headers : cstr allow-credentials : int] :)
internal: decorate a normal-flow response.
(mw-cors-with [allow-origin : cstr allow-methods : cstr allow-headers : cstr expose-headers : cstr allow-credentials : int max-age : int ^fat next : int] :)
CORS middleware factory (positional args).
(compose-middleware base
(mw-cors-with "https://app.example.com"
"GET, POST, OPTIONS"
"Content-Type, Authorization"
"" 1 60))
Since: Phase H8 (M4)
(mw-cors [^fat next : int] :)
CORS middleware with permissive defaults.
(compose-middleware base mw-cors)
Since: Phase H8 (M4)
(mw-cors-opts [opts : CorsOpts ^fat next : int] :)
CORS middleware factory configured by a CorsOpts struct.
| opts | a CorsOpts value (see `default-cors-opts`) | |
| next | downstream handler closure (fat-closure :int) |
A wrapped handler closure (:ptr<void>) suitable for httpd-new / compose-middleware.
(let [opts (default-cors-opts)
composed (compose-middleware base (mw-cors-opts opts))]
(httpd-new 0 composed))
Since: Phase H8 (M4)
(cstr-eq-const-time [a : cstr b : cstr] :)
length-then-constant-time XOR cstring compare.
Since: Phase H8 (M5)
(httpd-mw-basic-auth-check [conn ptr<void> verifier : int] :)
internal: decode + dispatch the verifier.
(httpd-mw-basic-auth-challenge [conn ptr<void> realm : cstr] :)
internal: emit 401 + WWW-Authenticate.
(mw-basic-auth [realm : cstr ^fat verifier : int ^fat next : int] :)
HTTP Basic Auth middleware factory.
(let [verify (fn [u :cstr p :cstr] :int ; captures nothing -- fine
(if (= 1 (cstr-eq-const-time u "admin"))
(cstr-eq-const-time p "s3cret")
0))
composed (compose-middleware base (mw-basic-auth "app" verify))]
(httpd-new 0 composed))
Since: Phase H8 (M5)
(httpd-req-multipart-parse [conn ptr<void>] :)
internal: parse the body on first access.
(httpd-req-file [conn ptr<void> field : cstr] :)
look up a multipart part by form field name.
(let [p (httpd-req-file c "upload")]
(when (not= 0 p)
(println (httpd-part-filename p))
(println (int->cstr (httpd-part-data-len p)))))
Since: Phase H8 (M7)
(httpd-part-name [part ptr<void>] :)
form field name of a part (or "" if missing).
(httpd-part-filename [part ptr<void>] :)
file name attribute (or "" for non-file parts).
(httpd-part-content-type [part ptr<void>] :)
per-part Content-Type (or "" if absent).
(httpd-part-data [part ptr<void>] :)
raw byte buffer (binary-safe, may contain NUL).
(httpd-part-data-len [part ptr<void>] :)
byte count of the part's content.
(httpd-async-fiber-body [self ptr<void> user-data ptr<void>] :)
internal: per-request fiber entry point.
(httpd-async-accept-cb [env ptr<void> id : int events : int user ptr<void>] :)
internal: reactor READ callback on the listen fd.
(httpd-new-async-with-limit [port : int ^fat handler : int max-in-flight : int])
create an async (fiber-per-request) httpd handle.
| port | TCP port to listen on (0 = kernel-assigned) | |
| handler | fat closure: (fn [conn :ptr<void>] :nil) |
ptr<void> -- HttpdAsync handle (or NULL on error) Lifecycle: (let [h (httpd-new-async 0 handler)] (httpd-run-async h) ;; blocks until httpd-stop-async fires (httpd-async-free h))
Since: Phase H8 (A0+A1)
(httpd-new-async [port : int ^fat handler : int])
create an async server with no in-flight cap.
Since: Phase H8 (A0+A1)
(httpd-run-async [h ptr<void>])
drive the async server until stopped.
Since: Phase H8 (A0+A1)
(httpd-stop-async [h ptr<void>])
request a graceful shutdown of the async server.
Since: Phase H8 (A0+A1)
(httpd-async-free [h ptr<void>] :)
tear down an async server handle.
Since: Phase H8 (A0+A1)
(httpd-async-port [h ptr<void>] :)
read the bound TCP port on an async server.
Since: Phase H8 (A0+A1)
(httpd-await-readable [conn ptr<void>] :)
suspend the current fiber until conn fd is readable.
Since: Phase H8 (A0)
(httpd-await-writable [conn ptr<void>] :)
suspend until conn fd is writable.
(httpd-await-timer [conn ptr<void> ms : int] :)
suspend the current fiber for ms milliseconds.
Since: Phase H8 (A0)
(router-new :)
create an empty router.
ptr<void> -- opaque Router handle
Since: Phase H6
(router-add [r ptr<void> method : cstr pattern : cstr ^fat handler : int] :)
register a route with the given method, pattern, and handler.
| r | router handle | |
| method | HTTP method ("GET", "POST", etc.) | |
| pattern | path pattern (e.g. "/users/:id") | |
| handler | fat closure: (fn [conn :ptr<void>] :nil) |
Since: Phase H6
(router-free [r ptr<void>] :)
free a router and all registered routes.
| r | router handle |
Since: Phase H6
(router-dispatch [r ptr<void> conn ptr<void>] :)
match the current request against the router's table.
| r | router handle | |
| conn | connection handle (the one passed to the httpd handler) |
Since: Phase H6
(httpd-param [conn ptr<void> name : cstr] :)
read a captured route parameter set by router-dispatch.
| conn | connection handle passed to the route handler | |
| name | parameter name (e.g. "id" for a ":id" pattern segment) |
cstr -- the captured value, or "" if no parameter with that name
;; pattern "/users/:id" matched against "/users/42" (httpd-param conn "id") ; => "42"
Since: Phase H6
(httpd-call [handler : int conn ptr<void>] :)
invoke a Turmeric handler closure on a connection.
| handler | handler closure value (int) | |
| conn | connection handle from the worker |
Since: Phase H7
(defroute [router method pattern handler])
macro sugar for (router-add router method pattern handler).
(defroute r "GET" "/" (fn [c :ptr<void>] :nil ...))
Since: Phase H6
(httpd-mw-mono-ms :)
monotonic clock helper (milliseconds). Internal.
(httpd-mw-log-emit [c ptr<void> elapsed-ms : int] :)
emit one log line for a completed request. Internal.
(mw-log [^fat next : int] :)
request logging middleware (stdout).
| next | downstream handler closure (fat-closure :int) |
A wrapped handler closure (:int) with the same calling convention as handlers passed to httpd-new / httpd-new-pool.
(let [base (fn [c :ptr<void>] :nil
(httpd-resp-status! c 200)
(httpd-resp-body! c "hi"))
composed (mw-log base)]
(httpd-new 0 composed))
Since: Phase H8 (M1)
(compose-middleware [base & mws])
wrap a base handler with multiple middlewares.
| base | base handler closure (:int) | |
| mws | variadic middleware closures (each `(fn [next :int] :int)`), | |
| applied right-to-left so the leftmost ends up outermost. |
The fully-wrapped handler closure (:int).
(let [composed (compose-middleware base mw-log)]
(httpd-new 0 composed))
;; Multiple middlewares (mw-log is outermost):
(let [composed (compose-middleware base mw-log mw-cors mw-auth)]
(httpd-new 0 composed))
Since: Phase H8 (M8 core)
(httpd-mw-apply [mw : int cur : int] :)
internal: apply one fat-closure middleware to a handler.
(httpd-mw-fold [base : int mws : int] :)
internal: right-fold a cons-list of fat-closure
(compose-middleware-of [A])
runtime variadic form of compose-middleware.
| base | base handler closure (:int) | |
| mws | variadic fat-closure middlewares; type variable A unifies | |
| to whatever closure type the call site uses. |
The fully-wrapped handler closure (:int).
Since: Phase H8 (M8 core); requires the V1 cast fix in
(httpd-mw-content-length [conn ptr<void>] :)
internal: read Content-Length as int64, or -1.
(mw-body-size [max-bytes : int ^fat next : int] :)
reject requests with Content-Length > max-bytes with 413.
| max-bytes | maximum accepted Content-Length in bytes (>= 0) | |
| next | downstream handler closure (fat-closure :int) |
A wrapped handler closure (:ptr<void>) suitable for httpd-new / compose-middleware.
(let [composed (mw-body-size 1048576 base)] ; 1 MiB cap
(httpd-new 0 composed))
Since: Phase H8 (MW1)
(httpd-set-attr! [conn ptr<void> key : cstr val : cstr] :)
attach a key/value pair to the current request.
| conn | httpd connection pointer | |
| key | attribute name (cstr) | |
| val | attribute value (cstr) |
(httpd-set-attr! c "user" "alice")
Since: Phase H8 (MW2)
(httpd-req-attr [conn ptr<void> key : cstr] :)
retrieve a previously set request attribute.
| conn | httpd connection pointer | |
| key | attribute name (cstr, case-sensitive) |
The attribute value as a cstr, or "" when absent.
(let [u (httpd-req-attr c "user")] ...)
Since: Phase H8 (MW2)
(httpd-req-remote-ip [conn ptr<void>] :)
return the client's IP address as a cstr.
| conn | httpd connection pointer |
The client IP as a cstr, or "" on failure.
(let [ip (httpd-req-remote-ip c)] ...)
Since: Phase H8 (MW2)
(defstruct RateLimitOpts [requests : int window-s : int])
mw-rate-limit configuration.
Since: Phase H8 (MW2)
(httpd-mw-ratelimit-new [requests : int window-s : int] :)
internal: allocate a fresh RL state struct.
(httpd-mw-ratelimit-check [state : int ip : cstr] :)
internal: bump the counter for ip; return
(httpd-mw-ratelimit-emit-429 [conn ptr<void> retry-s : int] :)
internal: write the 429 response.
(mw-rate-limit [opts : RateLimitOpts ^fat next : int] :)
sliding-window IP-based rate limiter middleware.
| opts | RateLimitOpts | |
| next | downstream handler closure (fat-closure :int) |
A wrapped handler closure (:ptr<void>) suitable for httpd-new / compose-middleware.
(let [composed (mw-rate-limit (make-struct RateLimitOpts 60 60) base)]
(httpd-new 0 composed))
Since: Phase H8 (MW2)
(httpd-mw-static-mime [path : cstr] :)
internal: map an extension to a Content-Type.
(httpd-mw-static-try [conn ptr<void> root : cstr] :)
internal: attempt to serve `path` from `root`
(httpd-mw-static-fallback [conn ptr<void> root : cstr] :)
serve files from a root directory when next returns 404.
| root-dir | filesystem path to serve files from | |
| next | downstream handler closure (fat-closure :int) |
A wrapped handler closure (:ptr<void>).
(let [composed (mw-static "./public" router-handler)]
(httpd-new 0 composed))
Since: Phase H8 (MW2)
(mw-static [root-dir : cstr ^fat next : int] :)
httpd/autolink-hint-- ensure programs using httpd link against libturi + pthreads.