Skip to content

Commit

Permalink
Jetty 12 early WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
d-t-w committed Jan 9, 2025
1 parent 9255c8d commit dcf6534
Show file tree
Hide file tree
Showing 24 changed files with 1,015 additions and 0 deletions.
1 change: 1 addition & 0 deletions slipway-jetty12/.clj-kondo/config.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{:linters {:refer-all {:exclude [clojure.test]}}}
15 changes: 15 additions & 0 deletions slipway-jetty12/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/target
/classes
/checkouts
profiles.clj
pom.xml
pom.xml.asc
*.jar
*.class
/.lein-*
/.nrepl-port
/.prepl-port
.hgignore
.hg/
.idea/
*.iml
1 change: 1 addition & 0 deletions slipway-jetty12/common
6 changes: 6 additions & 0 deletions slipway-jetty12/dependency-check-suppressions.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<suppressions xmlns="https://jeremylong.github.io/DependencyCheck/dependency-suppression.1.3.xsd">

<!-- Only include legitimate suppressions of false-positives in this file -->

</suppressions>
Binary file added slipway-jetty12/dev-resources/my-keystore.jks
Binary file not shown.
Binary file added slipway-jetty12/dev-resources/my-truststore.jks
Binary file not shown.
39 changes: 39 additions & 0 deletions slipway-jetty12/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
(defproject io.factorhouse/slipway-jetty12 "1.1.18"

:description "A Clojure Companion for Jetty 12"

:url "https://github.com/factorhouse/slipway"

:license {:name "Apache 2.0 License"
:url "https://github.com/factorhouse/slipway/blob/main/LICENSE"}

:profiles {:dev {:dependencies [[com.fasterxml.jackson.core/jackson-core "2.18.2"] ;; required for internal inconsistency within clj-kondo, kept at latest for CVE avoidance
[clj-kondo "2024.11.14"]
[clj-http "3.13.0"]
[ch.qos.logback/logback-classic "1.3.14"] ;; Logback 1.3.x supports the Java EE edition whereas logback 1.4.x supports Jakarta EE, otherwise the two versions are feature identical. The 1.5.x continues the 1.4.x series but with logback-access relocated to its own repository.
[ring/ring-anti-forgery "1.3.1"]
[metosin/reitit-ring "0.7.2" :exclusions [ring/ring-core]]]
:resource-paths ["dev-resources" "common/dev-resources"]
:plugins [[lein-cljfmt "0.9.2"]]}
:smoke {:pedantic? :abort}}

:aliases {"check" ["with-profile" "+smoke" "check"]
"kondo" ["with-profile" "+smoke" "run" "-m" "clj-kondo.main" "--lint" "common/src:common-jetty1x/src:test:common/test" "--parallel"]
"fmt" ["with-profile" "+smoke" "cljfmt" "check"]}

:dependencies [[org.clojure/clojure "1.12.0"]
[org.clojure/tools.logging "1.3.0"]
[commons-io "2.16.1"] ;; replaces old version with CVE in ring-servlet, remove when ring bumped to latest
[org.ring-clojure/ring-jakarta-servlet "1.13.0"]
[com.taoensso/sente "1.17.0"]
[org.eclipse.jetty.websocket/jetty-websocket-jetty-api "12.0.16"]
[org.eclipse.jetty.websocket/jetty-websocket-jetty-server "12.0.16" :exclusions [org.slf4j/slf4j-api]]
[org.eclipse.jetty/jetty-server "12.0.16" :exclusions [org.slf4j/slf4j-api]]
[org.eclipse.jetty/jetty-security "12.0.16" :exclusions [org.slf4j/slf4j-api]]
[org.eclipse.jetty.ee10/jetty-ee10-servlet "12.0.16"]
[org.slf4j/slf4j-api "2.0.16"]]

:source-paths ["src" "common/src" "common-jetty1x/src" "common-javax/src"]
:test-paths ["common/test"]

:javac-options ["--release" "17"])
111 changes: 111 additions & 0 deletions slipway-jetty12/src/slipway.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
(ns slipway
(:require [clojure.tools.logging :as log]
[slipway.connector.http]
[slipway.connector.https]
[slipway.handler]
[slipway.security :as security]
[slipway.server :as server]
[slipway.user])
(:import (org.eclipse.jetty.server Handler Server)))

(comment
#:slipway.handler.gzip{:enabled? "is gzip enabled? default true"
:included-mime-types "mime types to include (without charset or other parameters), leave nil for default types"
:excluded-mime-types "mime types to exclude (replacing any previous exclusion set)"
:min-gzip-size "min response size to trigger dynamic compression (in bytes, default 1024)"}

#: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"
: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 "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"
:proxy-protocol? "if true, add the ProxyConnectionFactor. See Jetty Proxy Protocol docs"
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
:configurator "a fn taking the final connector as argument, allowing further configuration"
:keystore "keystore to use, either path (String) or concrete KeyStore"
:keystore-type "type of keystore, e.g. JKS"
:keystore-password "password of the keystore"
:key-manager-password "password for the specific key within the keystore"
:truststore "truststore to use, either path (String) or concrete KeyStore"
:truststore-password "password of the truststore"
:truststore-type "type of the truststore, eg. JKS"
:include-protocols "a list of protocol name patterns to include in SSLEngine"
:exclude-protocols "a list of protocol name patterns to exclude from SSLEngine"
:replace-exclude-protocols? "if true will replace existing exclude-protocols, otherwise will add them"
:exclude-ciphers "a list of cipher suite names to exclude from SSLEngine"
:replace-exclude-ciphers? "if true will replace existing exclude-ciphers, otherwise will add them"
:security-provider "the security provider name"
:client-auth "either :need or :want to set the corresponding need/wantClientAuth field"
:ssl-context "a concrete pre-configured SslContext"
:sni-required? "true if SNI is required, else requests will be rejected with 400 response, default false"
:sni-host-check? "true if the SNI Host name must match when there is an SNI certificate, default false"
:sts-max-age "set the Strict-Transport-Security max age in seconds, default -1"
:sts-include-subdomains? "true if a include subdomain property is sent with any Strict-Transport-Security header"}

#: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."
: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 "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"
:proxy-protocol? "if true, add the ProxyConnectionFactory. See Jetty Proxy Protocol docs"
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
:configurator "a fn taking the final connector as argument, allowing further configuration"}

#: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 list of concrete Jetty ConstraintMapping"}

#:slipway.session{:secure-request-only? "set the secure flag on session cookies (default true)"
:http-only? "set the http-only flag on session cookies (default true)"
:same-site "set session cookie same-site policy to :none, :lax, or :strict (default :strict)"
:max-inactive-interval "max session idle time (in s, default -1)"
:tracking-modes "a set (colloection) of #{:cookie, :ssl, or :url}"
:cookie-name "the name of the session cookie"
:session-id-manager "the meta manager used for cross context session management"
:refresh-cookie-age "max time before a session cookie is re-set (in s)"
:path-parameter-name "name of path parameter used for URL session tracking"}

;; Jetty 10 / Jetty 11 Websockets
#:slipway.websockets{:idle-timeout "max websocket idle time (in ms), default 500000"
:input-buffer-size "max websocket input buffer size (in bytes)"
:output-buffer-size "max websocket output buffer size (in bytes)"
:max-text-message-size "max websocket text message size (in bytes, default 65536)"
:max-binary-message-size "max websocket binary message size (in bytes)"
:max-frame-size "max websocket frame size (in bytes)"
:auto-fragment "websocket auto fragment (boolean)"}

;; Jetty 9 Websockets
#:slipway.websockets{:idle-timeout "max websocket idle time (in ms), default 500000"
:input-buffer-size "max websocket input buffer size"
:max-text-message-size "max websocket text message size"
:max-binary-message-size "max websocket binary message size"}

#: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.server{:handler "the base Jetty handler implementation (:default defmethod impl found in slipway.handler)"
:connectors "the connectors supported by this server"
:thread-pool "the thread-pool used by this server (leave null for reasonable defaults)"
:error-handler "the error-handler used by this server for Jetty level errors"}

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

(defn start ^Server
[ring-handler {::keys [join?] :as opts}]
(log/info "starting slipway server")
(let [server (server/create-server opts)
login-service (security/login-service opts)]
(.setHandler server ^Handler (server/handler ring-handler login-service opts))
(some->> login-service (.addBean server))
(.start server)
(when join?
(log/info "joining jetty thread")
(.join server))
server))

(defn stop
[^Server server]
(log/info "stopping slipway server")
(.stop server))
37 changes: 37 additions & 0 deletions slipway-jetty12/src/slipway/common/websockets.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
(ns slipway.common.websockets
(:require [clojure.string :as string]))

(defprotocol WebSockets
(send! [this msg] [this msg callback])
(ping! [this] [this msg])
(close! [this] [this status-code reason])
(remote-addr [this])
(idle-timeout! [this ms])
(connected? [this])
(req-of [this]))

(defprotocol WebSocketSend
(-send! [x ws] [x ws callback]))

(defprotocol WebSocketPing
(-ping! [x ws]))

(defn upgrade-request?
[{:keys [headers]}]
(let [connection (or (get headers "Connection") (get headers "connection"))
upgrade (or (get headers "Upgrade") (get headers "upgrade"))]
(and (some? upgrade)
(some? connection)
(string/includes? (string/lower-case upgrade) "websocket")
(string/includes? (string/lower-case connection) "upgrade"))))

(defn upgrade-response?
[{:keys [status ws] :as resp}]
(and (= 101 status) (map? ws) (upgrade-request? resp)))

(defn upgrade-response
[ws-handler]
{:status 101
:headers {"Connection" "Upgrade"
"Upgrade" "Websocket"}
:ws ws-handler})
42 changes: 42 additions & 0 deletions slipway-jetty12/src/slipway/connector/http.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
(ns slipway.connector.http
(:require [clojure.tools.logging :as log]
[slipway.server :as server])
(:import (org.eclipse.jetty.server ConnectionFactory ForwardedRequestCustomizer HttpConfiguration
HttpConnectionFactory ProxyConnectionFactory Server ServerConnector)))

(defn default-config ^HttpConfiguration
[{::keys [http-forwarded? send-server-version? send-date-header?]
:or {send-server-version? false
send-date-header? false}}]
(let [config (doto (HttpConfiguration.)
(.setSendServerVersion send-server-version?)
(.setSendDateHeader send-date-header?))]
(when http-forwarded? (.addCustomizer config (ForwardedRequestCustomizer.)))
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"
: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 "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"
:proxy-protocol? "if true, add the ProxyConnectionFactor. See Jetty Proxy Protocol docs"
:http-config "a concrete HttpConfiguration object to replace the default config entirely"
:configurator "a fn taking the final connector as argument, allowing further configuration"
:send-server-version? "if true, send the Server header in responses"
:send-date-header? "if true, send the Date header in responses"})

(defmethod server/connector ::connector
[^Server server {::keys [host port idle-timeout proxy-protocol? http-forwarded? configurator http-config]
:or {idle-timeout 200000
port 80}
:as opts}]
(log/infof (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)))
factories (->> (if proxy-protocol? [(ProxyConnectionFactory.) http-factory] [http-factory])
(into-array ConnectionFactory))
connector (ServerConnector. ^Server server ^"[Lorg.eclipse.jetty.server.ConnectionFactory;" factories)]
(.setHost connector host)
(.setPort connector port)
(.setIdleTimeout connector idle-timeout)
(when configurator (configurator connector))
connector))
Loading

0 comments on commit dcf6534

Please sign in to comment.