Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
7b8ea95
include jetty-openid dependency
d-t-w Jun 11, 2026
7541551
make slipway.websockets/path-spec correctly configurable
d-t-w Jun 11, 2026
98caf54
Merge branch 'main' into open-id-support
d-t-w Jun 11, 2026
3c0fd31
move construction of login-handler and server-handler to within handl…
d-t-w Jun 12, 2026
084731c
minor refactor
d-t-w Jun 12, 2026
6af8aa4
moving towards a generalized security-handler (vs login-handler) appr…
d-t-w Jun 12, 2026
5d3260a
rework security handler implementations into separate namespaces
d-t-w Jun 15, 2026
6981a1c
fix tests
d-t-w Jun 15, 2026
9e99dbc
update documentated configuration
d-t-w Jun 15, 2026
a061f9b
fix format
d-t-w Jun 15, 2026
caa0035
fix kondo
d-t-w Jun 15, 2026
98ea40f
fix tests
d-t-w Jun 15, 2026
4b41800
better use of mixed namespace maps
d-t-w Jun 16, 2026
6816657
rm :pre check
d-t-w Jun 16, 2026
61fcd7b
explicit context-handler as default, and support ContextHandlerCollec…
d-t-w Jun 16, 2026
57f54d7
fix kondo
d-t-w Jun 16, 2026
e5e2578
refactor compression ns
d-t-w Jun 16, 2026
3f36706
fix format
d-t-w Jun 16, 2026
57cfc43
update README.md
d-t-w Jun 16, 2026
124cb7e
add error-handler to context-handler
d-t-w Jun 16, 2026
785efbe
refactor SyncHandler to simpler ns
d-t-w Jun 17, 2026
fba449c
::websockets/enabled? false by default, refactor context ns
d-t-w Jun 17, 2026
24fba00
minor nit
d-t-w Jun 17, 2026
b578a8b
fix tests
d-t-w Jun 17, 2026
79361d3
fix tests
d-t-w Jun 17, 2026
4f341dd
split tests
d-t-w Jun 17, 2026
4b803b7
break out server handler/connector configuration, remove ContextHandl…
d-t-w Jun 22, 2026
49b2510
fix format
d-t-w Jun 22, 2026
04b5a37
kondo and README.md
d-t-w Jun 22, 2026
ad354b4
in-memory hash testing
d-t-w Jun 22, 2026
1bc3079
fix kondo
d-t-w Jun 22, 2026
be1afb5
bump clojure setup ci task
d-t-w Jun 22, 2026
e6959b8
kondo paths
d-t-w Jun 22, 2026
4890a8d
intial context-collection tests
d-t-w Jun 22, 2026
a59bfab
context collection tests
d-t-w Jun 22, 2026
cd8a938
fix tests
d-t-w Jun 22, 2026
2898661
virtual hosts
d-t-w Jun 22, 2026
12e6708
first cut OpenIDLoginModule support
d-t-w Jun 23, 2026
e78a374
fix kondo
d-t-w Jun 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
java-version: '25'

- name: Install clojure tools
uses: DeLaGuardo/setup-clojure@13.6.0
uses: DeLaGuardo/setup-clojure@13.6.1
with:
lein: 'latest'
github-token: ${{ secrets.GITHUB_TOKEN }}
Expand Down
232 changes: 142 additions & 90 deletions README.md

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions project.clj
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
[ring/ring-anti-forgery "1.4.0"]
[metosin/reitit-ring "0.10.1"]]
:resource-paths ["dev-resources"]
:plugins [[dev.weavejester/lein-cljfmt "0.16.3"]]}
:plugins [[dev.weavejester/lein-cljfmt "0.16.4"]]}
:pedantic {:pedantic? :abort}}

:aliases {"check" ["with-profile" "+pedantic" "check"]
"kondo" ["with-profile" "+pedantic" "run" "-m" "clj-kondo.main" "--lint" "src:test" "--parallel"]
"kondo" ["with-profile" "+pedantic" "run" "-m" "clj-kondo.main" "--lint" "src:test/integration:test/unit" "--parallel"]
"fmt" ["with-profile" "+pedantic" "cljfmt" "check"]
"fmtfix" ["with-profile" "+pedantic" "cljfmt" "fix"]}

:aot [slipway.handler.sync-handler]
:aot [slipway.sync-handler]

:dependencies [[org.clojure/clojure "1.12.5"]
[org.clojure/tools.logging "1.3.1"]
Expand All @@ -34,10 +34,11 @@
[org.eclipse.jetty/jetty-server "12.1.10"]
[org.eclipse.jetty/jetty-session "12.1.10"]
[org.eclipse.jetty/jetty-security "12.1.10"]
[org.eclipse.jetty/jetty-openid "12.1.10"]
[org.eclipse.jetty.compression/jetty-compression-server "12.1.10"]
[org.eclipse.jetty.compression/jetty-compression-gzip "12.1.10"]]

:source-paths ["src"]
:test-paths ["test"]
:test-paths ["test/unit" "test/integration"]

:javac-options ["--release" "17"])
70 changes: 40 additions & 30 deletions src/slipway.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,22 @@
(:require [clojure.tools.logging :as log]
[slipway.connector.http]
[slipway.connector.https]
[slipway.handler]
[slipway.security :as security]
[slipway.context]
[slipway.security]
[slipway.server :as server]
[slipway.user]
[slipway.websockets])
(:import (org.eclipse.jetty.server Handler Server)))
(:import (org.eclipse.jetty.server Server)))

(comment
#:slipway.handler.compression{:enabled? "is compression handler enabled? default true"
:path-spec "the compression path-spec, default '/*'"
:format "compression format, defaults to :gzip"
:compress-min-bytes "min response size to trigger compression (default 1024 bytes)"
:compression-config "a concrete Jetty CompressConfig instance (nil for default configuration)"}
#:slipway.compression{:enabled? "is a compression handler enabled? default true"
:path-spec "the compression path-spec, default '/*'"
:format "compression format, defaults to :gzip"
:compress-min-bytes "min response size to trigger compression (default 1024 bytes)"
:compression-config "a concrete Jetty CompressionConfig instance (nil for default configuration)"}

#:slipway.connector.https{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
#:slipway.connector.https{:name "the name of this connector (useful for VirtualHosts configuration)"
:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(). default 443"
:idle-timeout-ms "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
Expand Down Expand Up @@ -45,9 +46,10 @@
:send-server-version? "if true, send the Server header in responses"
:send-date-header? "if true, send the Date header in responses"
:relative-redirect-allowed? "if true, allow relative redirects, default false"
:http-compliance "set 'RFC2616' to support reduced HttpCompliance, default is Jetty HttpCompliance/default"}
:http-compliance "set the HttpCompliance mode, defaults to HttpCompliance/RFC9110"}

#:slipway.connector.http{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
#:slipway.connector.http{:name "the name of this connector (useful for VirtualHosts configuration)"
:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 80"
:idle-timeout-ms "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
Expand All @@ -57,14 +59,21 @@
:send-server-version? "if true, send the Server header in responses"
:send-date-header? "if true, send the Date header in responses"
:relative-redirect-allowed? "if true, allow relative redirects, default false"
:http-compliance "set 'RFC2616' to support reduced HttpCompliance, default is Jetty HttpCompliance/default"}
:http-compliance "set the HttpCompliance mode, defaults to HttpCompliance/RFC9110"}

#:slipway.security{:realm "the Jetty authentication realm"
:hash-user-file "the path to a Jetty Hash User File"
:login-service "a Jetty LoginService identifier, 'jaas' and 'hash' supported by default"
:identity-service "a concrete Jetty IdentityService"
:authenticator "a concrete Jetty Authenticator (e.g. FormAuthenticator or BasicAuthenticator)"
:constraint-mappings "a vector of [^String pathSpec, org.eclipse.jetty.security.Constraint]"}
#:slipway.security{:handler "identifies a SecurityHandler impl, 'jaas', 'hash', and 'openid' supported by default"}

#:slipway.security.hash{:realm "an (optional) Jetty authentication realm"
:user-file "the path to a Jetty hash-user file"
:users "a sequence of [^String user-name, ^String credential, ^String[] [roles]]"
:authenticator "a concrete Jetty Authenticator (e.g. FormAuthenticator or BasicAuthenticator)"
:constraint-mappings "a vector of [^String pathSpec, org.eclipse.jetty.security.Constraint]"
:identity-service "an (optional) concrete Jetty IdentityService"}

#:slipway.security.jaas{:realm "the Jetty authentication realm"
:authenticator "a concrete Jetty Authenticator (e.g. FormAuthenticator or BasicAuthenticator)"
:constraint-mappings "a vector of [^String pathSpec, org.eclipse.jetty.security.Constraint]"
:identity-service "an (optional) concrete Jetty IdentityService"}

#:slipway.session{:secure-request-only? "set the secure flag on session cookies"
:http-only? "set the http-only flag on session cookies"
Expand All @@ -79,7 +88,7 @@

#:slipway.sente{:options "A map of options passed directly to sente/make-channel-socket-server!"}

#:slipway.websockets{:enabled? "are websockets enabled? default true"
#:slipway.websockets{:enabled? "are websockets enabled? default false"
:path-spec "the websocket path-spec, default '/chsk'"
:idle-timeout-ms "max websocket idle time, default 300000"
:input-buffer-bytes "max websocket input buffer size"
Expand All @@ -90,26 +99,27 @@
:max-outgoing-frames "max websocket frames waiting to be sent per session, default -1"
:auto-fragment "websocket auto fragment (boolean), default true"}

#:slipway.handler{:context-path "the root context path, default '/'"
:ws-path "the path serving the websocket upgrade handler, default '/chsk'"
:null-path-info? "true if /path is not redirected to /path/, default true"}
#:slipway.context{:path "the context path, default '/'"
:ring-handler "the ring-handler descendant of this context-handler"
:null-path-info? "true if /path is not redirected to /path/, default true"
:virtual-hosts "a list of ^String virtual hosts for the context"
:error-handler "the error-handler used by this context-handler for context level errors"
:handlers "a sequence of [:slipway.context], when used with ::server/handler of ::context/handler-collection"}

#:slipway.server{:handler "the base Jetty handler implementation (:default defmethod impl found in slipway.handler)"
:connectors "the connectors supported by this server"
#:slipway.server{:connector "the connector supported by this server"
:connectors "the connectors supported by this server (when many connectors supported)"
:handler "the handler for this server, dispatches on :slipway.server/handler-type, :default is slipway.context/handler"
:thread-pool "the thread-pool used by this server (nil for default behaviour)"
:scheduler "the scheduler used by this server (nil for default behaviour)"
:buffer-pool "the buffer-pool used by this server (nil for default behaviour)"
:error-handler "the error-handler used by this server for Jetty level errors"}
:error-handler "the error-handler used by this server for Jetty level errors (nil for default behaviour)"}

#:slipway{:join? "join the Jetty threadpool, blocks the calling thread until jetty exits, default false"})

(defn start ^Server
[ring-handler {::keys [join?] :as opts}]
[{::keys [join?] :as opts}]
(log/debugf "starting jetty server %s" opts)
(let [server (server/create-server opts)
login-service (security/login-service opts)
handler (server/handler server ring-handler login-service opts)]
(.setHandler server ^Handler handler)
(let [server (server/create-server opts)]
(.start server)
(when join?
(log/debug "joining jetty thread")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
(ns slipway.handler.compression
(ns slipway.compression
(:refer-clojure :exclude [format])
(:require [clojure.tools.logging :as log])
(:import (org.eclipse.jetty.compression.gzip GzipCompression)
(org.eclipse.jetty.compression.server CompressionConfig CompressionHandler)))

(comment
#:slipway.handler.compression{:enabled? "is compression handler enabled? default true"
:path-spec "the compression path-spec, default '/*'"
:format "compression format, defaults to :gzip"
:compress-min-bytes "min response size to trigger compression (default 1024 bytes)"
:compression-config "a concrete Jetty CompressConfig instance (nil for default configuration)"})
#:slipway.compression{:enabled? "is a compression handler enabled? default true"
:path-spec "the compression path-spec, default '/*'"
:format "compression format, defaults to :gzip"
:compress-min-bytes "min response size to trigger compression (default 1024 bytes)"
:compression-config "a concrete Jetty CompressionConfig instance (nil for default configuration)"})

(defmulti format ::format)

Expand Down
13 changes: 7 additions & 6 deletions src/slipway/connector/http.clj
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
config))

(comment
#:slipway.connector.http{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
#:slipway.connector.http{:name "the name of this connector (useful for VirtualHosts configuration)"
:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(), default 80"
:idle-timeout-ms "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
:idle-timeout-ms "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 30000 ms"
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
:proxy-protocol? "if true, add the ProxyConnectionFactory. See Jetty Proxy Protocol docs"
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
Expand All @@ -40,9 +41,8 @@
:http-compliance "set the HttpCompliance mode, defaults to HttpCompliance/RFC9110"})

(defmethod server/connector ::connector
[^Server server {::keys [host port idle-timeout-ms proxy-protocol? http-forwarded? configurator http-config]
:or {idle-timeout-ms 200000
port 80}
[^Server server {::keys [host port name idle-timeout-ms proxy-protocol? http-forwarded? configurator http-config]
:or {port 80}
:as opts}]
(log/debugf (str "starting " (when proxy-protocol? "proxied ") "HTTP connector on %s:%s" (when http-forwarded? " with http-forwarded support")) (or host "all-interfaces") port)
(let [http-factory (HttpConnectionFactory. (or http-config (default-config opts)))
Expand All @@ -51,6 +51,7 @@
connector (ServerConnector. ^Server server ^"[Lorg.eclipse.jetty.server.ConnectionFactory;" factories)]
(.setHost connector host)
(.setPort connector port)
(.setIdleTimeout connector idle-timeout-ms)
(some->> name (.setName connector))
(some->> idle-timeout-ms (.setIdleTimeout connector))
(when configurator (configurator connector))
connector))
19 changes: 9 additions & 10 deletions src/slipway/connector/https.clj
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,8 @@
(when key-manager-password
(.setKeyManagerPassword context-factory key-manager-password))
(cond
(string? truststore)
(.setTrustStorePath context-factory truststore)
(instance? KeyStore truststore)
(.setTrustStore context-factory ^KeyStore truststore))
(string? truststore) (.setTrustStorePath context-factory truststore)
(instance? KeyStore truststore) (.setTrustStore context-factory ^KeyStore truststore))
(when truststore-password
(.setTrustStorePassword context-factory truststore-password))
(when truststore-type
Expand Down Expand Up @@ -91,9 +89,10 @@
(ServerConnector. server (context-factory opts) ^"[Lorg.eclipse.jetty.server.ConnectionFactory;" (into-array ConnectionFactory [http-factory])))

(comment
#:slipway.connector.https{:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
#:slipway.connector.https{:name "the name of this connector (useful for VirtualHosts configuration)"
:host "the network interface this connector binds to as an IP address or a hostname. If null or 0.0.0.0, then bind to all interfaces. Default null/all interfaces"
:port "port this connector listens on. If set to 0 a random port is assigned which may be obtained with getLocalPort(). default 443"
:idle-timeout-ms "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 200000 ms"
:idle-timeout-ms "max idle time for a connection, roughly translates to the Socket.setSoTimeout. Default 30000 ms"
:http-forwarded? "if true, add the ForwardRequestCustomizer. See Jetty Forward HTTP docs"
:proxy-protocol? "if true, add the ProxyConnectionFactory. See Jetty Proxy Protocol docs"
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
Expand Down Expand Up @@ -123,14 +122,14 @@
:http-compliance "set the HttpCompliance mode, defaults to HttpCompliance/RFC9110"})

(defmethod server/connector ::connector
[^Server server {::keys [host port idle-timeout-ms proxy-protocol? http-config configurator]
:or {idle-timeout-ms 200000
port 443}
[^Server server {::keys [host port name idle-timeout-ms proxy-protocol? http-config configurator]
:or {port 443}
:as opts}]
(let [http-factory (HttpConnectionFactory. (or http-config (default-config opts)))
connector (if proxy-protocol? (proxied-connector server http-factory opts) (standard-connector server http-factory opts))]
(.setHost connector host)
(.setPort connector port)
(.setIdleTimeout connector idle-timeout-ms)
(some->> name (.setName connector))
(some->> idle-timeout-ms (.setIdleTimeout connector))
(when configurator (configurator connector))
connector))
Loading