No matching definitions.

tur/httpd

stdlib/httpd.tur

Lightweight HTTP/1.0 and HTTP/1.1 server.

Since: Phase H1

defn

httpd-register-tls-impl

(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

defn

httpd-handle

(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)

defn

httpd-worker

(httpd-worker [arg ptr<void>] :)

internal: worker thread function.

Since: Phase H3

defn

httpd-accept-cb

(httpd-accept-cb [env ptr<void> id : int events : int user ptr<void>] :)

internal reactor READ callback for the listen fd.

Since: Phase H2

defn

httpd-new-pool

(httpd-new-pool [port : int workers : int ^fat handler : int])

create an httpd instance with a configurable worker count.

portTCP port to listen on (0 = kernel-assigned)
workersnumber of worker threads in the pool (must be > 0)
handlerfat closure: (fn [conn :ptr<void>] :nil)

ptr<void> -- opaque Httpd handle, or nil on error

Since: Phase H3

defn

httpd-new

(httpd-new [port : int ^fat handler : int])

create a new httpd instance with the default worker pool size (4).

portTCP port to listen on (0 = kernel-assigned)
handlerfat 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

defn

httpd-new-tls

(httpd-new-tls [port : int workers : int ^fat handler : int ctx : int])

create an httpd instance with TLS termination.

portTCP port to listen on (0 = kernel-assigned)
workersnumber of worker threads (must be > 0)
handlerfat closure: (fn [conn :ptr<void>] :nil)
ctxTLS 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

defn

httpd-port

(httpd-port [h ptr<void>] :)

return the port the server is listening on.

hhttpd handle returned by httpd-new

int -- the actual listening port, or -1 on error

Since: Phase H1

defn

httpd-run

(httpd-run [h ptr<void>])

run the listener reactor until httpd-stop is called.

Since: Phase H1

defn

httpd-stop

(httpd-stop [h ptr<void>])

signal the listener to stop accepting new connections.

Since: Phase H1

defn

httpd-free

(httpd-free [h ptr<void>] :)

free all resources after httpd-run returns.

Since: Phase H1

defn

httpd-req-method

(httpd-req-method [conn ptr<void>] :)

return the HTTP method from the parsed request.

Since: Phase H1

defn

httpd-req-path

(httpd-req-path [conn ptr<void>] :)

return the request-target path from the parsed request.

Since: Phase H1

defn

httpd-req-version

(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

defn

httpd-req-body

(httpd-req-body [conn ptr<void>] :)

return the request body (NUL-terminated copy).

cstr -- the request body bytes (NUL-terminated)

Since: Phase H4

defn

httpd-req-body-len

(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

defn

httpd-resp-status-get

(httpd-resp-status-get [conn ptr<void>] :)

return the currently-set response status code.

Since: Phase H6

defn

httpd-resp-status!

(httpd-resp-status! [conn ptr<void> status : int] :)

set the HTTP response status code.

Since: Phase H1

defn

httpd-resp-body!

(httpd-resp-body! [conn ptr<void> body : cstr] :)

set the HTTP response body (copies the string).

Since: Phase H1

defn

httpd-resp-body-bytes!

(httpd-resp-body-bytes! [conn ptr<void> bytes ptr<void> len : int] :)

binary-safe response body setter.

connconnection handle from the worker
bytespointer to raw payload (may contain NUL bytes)
lenbyte count to copy
(httpd-resp-body-bytes! c gzip-data gzip-len)

Since: Phase H8 (M6)

defn

httpd-req-header

(httpd-req-header [conn ptr<void> name : cstr] :)

look up a request header by name (case-insensitive).

connconnection handle from the worker
nameheader 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)

defn

httpd-req-header?

(httpd-req-header? [conn ptr<void> name : cstr] :)

presence test for a request header.

connconnection handle from the worker
nameheader name (case-insensitive)

1 if present, 0 otherwise.

(when (= 1 (httpd-req-header? c "Authorization")) ...)

Since: Phase H8 (M0)

defn

httpd-resp-header!

(httpd-resp-header! [conn ptr<void> name : cstr value : cstr] :)

set (or replace) a response header.

connconnection handle from the worker
nameheader name (case-insensitive on the dedup check)
valueheader value
(httpd-resp-header! c "Content-Type" "application/json")

Since: Phase H8 (M0)

defn

httpd-resp-header-add!

(httpd-resp-header-add! [conn ptr<void> name : cstr value : cstr] :)

append a response header without de-duplication.

connconnection handle from the worker
nameheader name
valueheader 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)

defstruct

CookieOpts

(defstruct CookieOpts [])

fully-specified Set-Cookie attributes.

Since: Phase H8 (M2)

defn

httpd-req-form

(httpd-req-form [conn ptr<void> field : cstr] :)

look up a single URL-encoded form field by name.

connconnection handle from the worker
fieldform 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)

defn

httpd-req-json

(httpd-req-json [conn ptr<void>] :)

lazily parse the request body as JSON.

connconnection 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)

defn

mw-json-body

(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)

defn

httpd-mw-json-body-precheck

(httpd-mw-json-body-precheck [conn ptr<void>] :)

internal: returns 1 when the body is

defstruct

CorsOpts

(defstruct CorsOpts [])

mw-cors configuration.

Since: Phase H8 (M4)

defn

default-cors-opts

(default-cors-opts :)

permissive defaults suitable for development.

Since: Phase H8 (M4)

defn

httpd-mw-cors-is-preflight

(httpd-mw-cors-is-preflight [conn ptr<void>] :)

internal: 1 iff this is a CORS preflight.

defn

httpd-mw-cors-emit-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.

defn

httpd-mw-cors-decorate

(httpd-mw-cors-decorate [conn ptr<void> allow-origin : cstr expose-headers : cstr allow-credentials : int] :)

internal: decorate a normal-flow response.

defn

mw-cors-with

(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)

defn

mw-cors

(mw-cors [^fat next : int] :)

CORS middleware with permissive defaults.

(compose-middleware base mw-cors)

Since: Phase H8 (M4)

defn

mw-cors-opts

(mw-cors-opts [opts : CorsOpts ^fat next : int] :)

CORS middleware factory configured by a CorsOpts struct.

optsa CorsOpts value (see `default-cors-opts`)
nextdownstream 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)

defn

cstr-eq-const-time

(cstr-eq-const-time [a : cstr b : cstr] :)

length-then-constant-time XOR cstring compare.

Since: Phase H8 (M5)

defn

httpd-mw-basic-auth-check

(httpd-mw-basic-auth-check [conn ptr<void> verifier : int] :)

internal: decode + dispatch the verifier.

defn

httpd-mw-basic-auth-challenge

(httpd-mw-basic-auth-challenge [conn ptr<void> realm : cstr] :)

internal: emit 401 + WWW-Authenticate.

defn

mw-basic-auth

(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)

defn

httpd-req-multipart-parse

(httpd-req-multipart-parse [conn ptr<void>] :)

internal: parse the body on first access.

defn

httpd-req-file

(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)

defn

httpd-part-name

(httpd-part-name [part ptr<void>] :)

form field name of a part (or "" if missing).

defn

httpd-part-filename

(httpd-part-filename [part ptr<void>] :)

file name attribute (or "" for non-file parts).

defn

httpd-part-content-type

(httpd-part-content-type [part ptr<void>] :)

per-part Content-Type (or "" if absent).

defn

httpd-part-data

(httpd-part-data [part ptr<void>] :)

raw byte buffer (binary-safe, may contain NUL).

defn

httpd-part-data-len

(httpd-part-data-len [part ptr<void>] :)

byte count of the part's content.

defn

httpd-async-fiber-body

(httpd-async-fiber-body [self ptr<void> user-data ptr<void>] :)

internal: per-request fiber entry point.

defn

httpd-async-accept-cb

(httpd-async-accept-cb [env ptr<void> id : int events : int user ptr<void>] :)

internal: reactor READ callback on the listen fd.

defn

httpd-new-async-with-limit

(httpd-new-async-with-limit [port : int ^fat handler : int max-in-flight : int])

create an async (fiber-per-request) httpd handle.

portTCP port to listen on (0 = kernel-assigned)
handlerfat 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)

defn

httpd-new-async

(httpd-new-async [port : int ^fat handler : int])

create an async server with no in-flight cap.

Since: Phase H8 (A0+A1)

defn

httpd-run-async

(httpd-run-async [h ptr<void>])

drive the async server until stopped.

Since: Phase H8 (A0+A1)

defn

httpd-stop-async

(httpd-stop-async [h ptr<void>])

request a graceful shutdown of the async server.

Since: Phase H8 (A0+A1)

defn

httpd-async-free

(httpd-async-free [h ptr<void>] :)

tear down an async server handle.

Since: Phase H8 (A0+A1)

defn

httpd-async-port

(httpd-async-port [h ptr<void>] :)

read the bound TCP port on an async server.

Since: Phase H8 (A0+A1)

defn

httpd-await-readable

(httpd-await-readable [conn ptr<void>] :)

suspend the current fiber until conn fd is readable.

Since: Phase H8 (A0)

defn

httpd-await-writable

(httpd-await-writable [conn ptr<void>] :)

suspend until conn fd is writable.

defn

httpd-await-timer

(httpd-await-timer [conn ptr<void> ms : int] :)

suspend the current fiber for ms milliseconds.

Since: Phase H8 (A0)

defn

router-new

(router-new :)

create an empty router.

ptr<void> -- opaque Router handle

Since: Phase H6

defn

router-add

(router-add [r ptr<void> method : cstr pattern : cstr ^fat handler : int] :)

register a route with the given method, pattern, and handler.

rrouter handle
methodHTTP method ("GET", "POST", etc.)
patternpath pattern (e.g. "/users/:id")
handlerfat closure: (fn [conn :ptr<void>] :nil)

Since: Phase H6

defn

router-free

(router-free [r ptr<void>] :)

free a router and all registered routes.

rrouter handle

Since: Phase H6

defn

router-dispatch

(router-dispatch [r ptr<void> conn ptr<void>] :)

match the current request against the router's table.

rrouter handle
connconnection handle (the one passed to the httpd handler)

Since: Phase H6

defn

httpd-param

(httpd-param [conn ptr<void> name : cstr] :)

read a captured route parameter set by router-dispatch.

connconnection handle passed to the route handler
nameparameter 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

defn

httpd-call

(httpd-call [handler : int conn ptr<void>] :)

invoke a Turmeric handler closure on a connection.

handlerhandler closure value (int)
connconnection handle from the worker

Since: Phase H7

defmacro

defroute

(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

defn

httpd-mw-mono-ms

(httpd-mw-mono-ms :)

monotonic clock helper (milliseconds). Internal.

defn

httpd-mw-log-emit

(httpd-mw-log-emit [c ptr<void> elapsed-ms : int] :)

emit one log line for a completed request. Internal.

defn

mw-log

(mw-log [^fat next : int] :)

request logging middleware (stdout).

nextdownstream 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)

defmacro

compose-middleware

(compose-middleware [base & mws])

wrap a base handler with multiple middlewares.

basebase handler closure (:int)
mwsvariadic 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)

defn

httpd-mw-apply

(httpd-mw-apply [mw : int cur : int] :)

internal: apply one fat-closure middleware to a handler.

defn

httpd-mw-fold

(httpd-mw-fold [base : int mws : int] :)

internal: right-fold a cons-list of fat-closure

defn

compose-middleware-of

(compose-middleware-of [A])

runtime variadic form of compose-middleware.

basebase handler closure (:int)
mwsvariadic 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

defn

httpd-mw-content-length

(httpd-mw-content-length [conn ptr<void>] :)

internal: read Content-Length as int64, or -1.

defn

mw-body-size

(mw-body-size [max-bytes : int ^fat next : int] :)

reject requests with Content-Length > max-bytes with 413.

max-bytesmaximum accepted Content-Length in bytes (>= 0)
nextdownstream 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)

defn

httpd-set-attr!

(httpd-set-attr! [conn ptr<void> key : cstr val : cstr] :)

attach a key/value pair to the current request.

connhttpd connection pointer
keyattribute name (cstr)
valattribute value (cstr)
(httpd-set-attr! c "user" "alice")

Since: Phase H8 (MW2)

defn

httpd-req-attr

(httpd-req-attr [conn ptr<void> key : cstr] :)

retrieve a previously set request attribute.

connhttpd connection pointer
keyattribute name (cstr, case-sensitive)

The attribute value as a cstr, or "" when absent.

(let [u (httpd-req-attr c "user")] ...)

Since: Phase H8 (MW2)

defn

httpd-req-remote-ip

(httpd-req-remote-ip [conn ptr<void>] :)

return the client's IP address as a cstr.

connhttpd connection pointer

The client IP as a cstr, or "" on failure.

(let [ip (httpd-req-remote-ip c)] ...)

Since: Phase H8 (MW2)

defstruct

RateLimitOpts

(defstruct RateLimitOpts [requests : int window-s : int])

mw-rate-limit configuration.

Since: Phase H8 (MW2)

defn

httpd-mw-ratelimit-new

(httpd-mw-ratelimit-new [requests : int window-s : int] :)

internal: allocate a fresh RL state struct.

defn

httpd-mw-ratelimit-check

(httpd-mw-ratelimit-check [state : int ip : cstr] :)

internal: bump the counter for ip; return

defn

httpd-mw-ratelimit-emit-429

(httpd-mw-ratelimit-emit-429 [conn ptr<void> retry-s : int] :)

internal: write the 429 response.

defn

mw-rate-limit

(mw-rate-limit [opts : RateLimitOpts ^fat next : int] :)

sliding-window IP-based rate limiter middleware.

optsRateLimitOpts
nextdownstream 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)

defn

httpd-mw-static-mime

(httpd-mw-static-mime [path : cstr] :)

internal: map an extension to a Content-Type.

defn

httpd-mw-static-try

(httpd-mw-static-try [conn ptr<void> root : cstr] :)

internal: attempt to serve `path` from `root`

defn

httpd-mw-static-fallback

(httpd-mw-static-fallback [conn ptr<void> root : cstr] :)

serve files from a root directory when next returns 404.

root-dirfilesystem path to serve files from
nextdownstream 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)

defn

mw-static

(mw-static [root-dir : cstr ^fat next : int] :)
Internal definitions