A cross-platform HTTP server & client for Clojure using Java NIO.
(require '[zeph.server :as server])
(def stop (server/run-server
(fn [req] {:status 200 :body "Hello!"})
{:port 8080}))
;; Stop
(stop)
(require '[zeph.client :as http])
@(http/get "https://example.com")
;; => {:status 200 :headers {...} :body "..."}
;; deps.edn
{:deps {io.gitlab.myst3m/zeph {:mvn/version "0.2.1"}}}
;; Leiningen
[io.gitlab.myst3m/zeph "0.2.1"]
(require '[zeph.server :as server])
(defn handler [req]
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello, World!"})
(def stop (server/run-server handler {:port 8080}))
;; Stop server
(stop)
| Option | Default | Description |
|---|---|---|
:port | 8080 | Port number |
:ip | "0.0.0.0" | Bind address |
:thread | CPU cores | Worker threads |
:ssl? | false | Enable HTTPS |
:cert | nil | Certificate PEM path |
:key | nil | Private key PEM path |
:keystore | nil | PKCS12/JKS keystore path |
:keystore-password | nil | Keystore password |
:static-root | nil | Static files directory |
;; Built-in localhost certificate
(server/run-server handler {:port 8443 :ssl? true})
;; PEM files
(server/run-server handler {:port 8443
:ssl? true
:cert "cert.pem"
:key "key.pem"})
;; PKCS12 keystore
(server/run-server handler {:port 8443
:ssl? true
:keystore "keystore.p12"
:keystore-password "secret"})
HTTP/2 is automatically enabled:
Test with curl:
# HTTP/2 over TLS (h2)
curl -v --http2 https://localhost:8443/
# HTTP/2 cleartext (h2c)
curl -v --http2 http://localhost:8080/
Look for HTTP/2 200 in the output to confirm HTTP/2 is working.
Large files are streamed efficiently without loading into memory:
(require '[clojure.java.io :as io])
;; File - sets Content-Length automatically
{:status 200 :body (io/file "/path/to/large.bin")}
;; InputStream - uses chunked encoding
{:status 200 :body (io/input-stream "/path/to/large.bin")}
(server/run-server handler {:port 8080
:static-root "public"})
;; Serves files from ./public directory
;; Example: GET /css/style.css -> ./public/css/style.css
{:server-port 8080
:server-name "localhost"
:remote-addr "127.0.0.1"
:uri "/path"
:query-string "foo=bar"
:scheme :http
:request-method :get
:protocol "HTTP/1.1"
:headers {"host" "localhost"}
:body nil}
(require '[zeph.client :as http])
;; GET
@(http/get "https://example.com")
;; POST with JSON
@(http/post "https://api.example.com/data"
{:body "{\"key\":\"value\"}"
:headers {"Content-Type" "application/json"}})
;; Async with callback
(http/get "https://example.com"
(fn [{:keys [status body error]}]
(println status)))
| Option | Default | Description |
|---|---|---|
:method | :get | HTTP method |
:headers | nil | Request headers |
:body | nil | Request body |
:query-params | nil | Query parameters |
:form-params | nil | Form parameters |
:timeout | 30000 | Timeout (ms) |
:follow-redirects | true | Follow redirects |
:max-redirects | 5 | Max redirects |
:insecure? | false | Skip SSL verification |
:basic-auth | nil | ["user" "pass"] |
;; Blocking
@(http/get url)
;; With timeout
(deref (http/get url) 5000 :timeout)
;; Check completion
(realized? (http/get url))
;; Success
{:status 200 :headers {...} :body "..."}
;; Error
{:error #<Exception ...>}
Zeph includes an HTTPie-style CLI. Client is the default command.
# Basic requests (client is default - no subcommand needed)
zeph httpbin.org/get
zeph POST httpbin.org/post name=John age:=30
# Nested JSON syntax
zeph httpbin.org/post user.name=John user.email=john@example.com
# => {"user": {"name": "John", "email": "john@example.com"}}
zeph httpbin.org/post items.0=apple items.1=banana
# => {"items": ["apple", "banana"]}
# Options
zeph -X httpbin.org/get # trace with headers/body
zeph -V httpbin.org/get # trace summary
zeph -1 httpbin.org/get # force HTTP/1.1
zeph -k https://self-signed/api # skip SSL verify
# Server
zeph s -p 8080
zeph s -p 8443 --ssl
| Syntax | Description | Example |
|---|---|---|
key=value | JSON string | name=John -> {"name": "John"} |
key:=value | Raw JSON | age:=30 -> {"age": 30} |
a.b=value | Nested object | user.name=John -> {"user": {"name": "John"}} |
a.0=value | Array element | items.0=x -> {"items": ["x"]} |
Header:Value | HTTP header | Authorization:Bearer token |
clojure -T:build compile-java
clojure -X:test
clojure -M:dev
clojure -T:build uberjar
java -jar target/zeph-*-standalone.jar server -p 8080
GRAALVM_HOME=/path/to/graalvm clojure -T:build native-image
./target/zeph server -p 8080
+---------------------------------------------------------------+
| Zeph Server |
+---------------------------------------------------------------+
| HttpServer Interface |
| | |
| v |
| HttpServerNio |
| (Cross-platform) |
| | |
| v |
| +---------------+ |
| | Selector | |
| | Workers | |
| | (NIO + | |
| | sendfile) | |
| +---------------+ |
+---------------------------------------------------------------+
| Java NIO Selector |
+---------------------------------------------------------------+
EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
Can you improve this documentation?Edit on GitLab
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |