From 3fff1d871f08c2d2000b8ee904f7516f6b20f8ff Mon Sep 17 00:00:00 2001 From: Loukas Agorgianitis Date: Thu, 26 Sep 2024 10:51:07 +0300 Subject: [PATCH] Add unix domain socket support Signed-off-by: Loukas Agorgianitis --- ring-jetty-adapter/project.clj | 2 ++ ring-jetty-adapter/src/ring/adapter/jetty.clj | 19 ++++++++++++- .../test/ring/adapter/test/jetty.clj | 28 ++++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/ring-jetty-adapter/project.clj b/ring-jetty-adapter/project.clj index 3706922d..f9da0587 100644 --- a/ring-jetty-adapter/project.clj +++ b/ring-jetty-adapter/project.clj @@ -8,6 +8,7 @@ [ring/ring-core "1.12.2"] [org.ring-clojure/ring-jakarta-servlet "1.12.2"] [org.eclipse.jetty/jetty-server "11.0.21"] + [org.eclipse.jetty/jetty-unixdomain-server "11.0.21"] [org.eclipse.jetty.websocket/websocket-jetty-server "11.0.21"]] :aliases {"test-all" ["with-profile" "default:+1.10:+1.11:+1.12" "test"]} :profiles @@ -15,6 +16,7 @@ [less-awful-ssl "1.0.6"] [hato "0.9.0"]] :jvm-opts ["-Dorg.eclipse.jetty.server.HttpChannelState.DEFAULT_TIMEOUT=500"]} + :test {:dependencies [[org.eclipse.jetty/jetty-client "11.0.21"]]} :1.10 {:dependencies [[org.clojure/clojure "1.10.3"]]} :1.11 {:dependencies [[org.clojure/clojure "1.11.2"]]} :1.12 {:dependencies [[org.clojure/clojure "1.12.0-alpha9"]]}}) diff --git a/ring-jetty-adapter/src/ring/adapter/jetty.clj b/ring-jetty-adapter/src/ring/adapter/jetty.clj index 3e37f667..14f3cf8a 100644 --- a/ring-jetty-adapter/src/ring/adapter/jetty.clj +++ b/ring-jetty-adapter/src/ring/adapter/jetty.clj @@ -2,7 +2,8 @@ "A Ring adapter that uses the Jetty 9 embedded web server. Adapters are used to convert Ring handlers into running web servers." - (:require [ring.util.jakarta.servlet :as servlet] + (:require [clojure.java.io :as io] + [ring.util.jakarta.servlet :as servlet] [ring.websocket :as ws] [ring.websocket.protocols :as wsp]) (:import [java.nio ByteBuffer] @@ -15,6 +16,7 @@ HttpConnectionFactory SslConnectionFactory SecureRequestCustomizer] + [org.eclipse.jetty.unixdomain.server UnixDomainServerConnector] [org.eclipse.jetty.servlet ServletContextHandler ServletHandler] [org.eclipse.jetty.util BlockingArrayQueue] [org.eclipse.jetty.util.thread ThreadPool QueuedThreadPool] @@ -161,6 +163,10 @@ (ServerConnector. server #^"[Lorg.eclipse.jetty.server.ConnectionFactory;" (into-array ConnectionFactory factories))) +(defn- unix-domain-server-connector ^UnixDomainServerConnector [^Server server & factories] + (UnixDomainServerConnector. server #^"[Lorg.eclipse.jetty.server.ConnectionFactory;" + (into-array ConnectionFactory factories))) + (defn- http-config ^HttpConfiguration [options] (doto (HttpConfiguration.) (.setSendDateHeader (:send-date-header? options true)) @@ -229,6 +235,14 @@ (.setHost (options :host)) (.setIdleTimeout (options :max-idle-time 200000))))) +(defn- unix-socket-connector ^ServerConnector [server options] + (let [http-factory (HttpConnectionFactory. (http-config options)) + socket (io/file (options :unix-socket))] + (.deleteOnExit socket) + (doto (unix-domain-server-connector server http-factory) + (.setUnixDomainPath (.toPath socket)) + (.setIdleTimeout (options :max-idle-time 200000))))) + (defn- create-threadpool [options] (let [min-threads (options :min-threads 8) max-threads (options :max-threads 50) @@ -253,6 +267,8 @@ (.addConnector server (http-connector server options))) (when (or (options :ssl?) (options :ssl-port)) (.addConnector server (ssl-connector server options))) + (when (options :unix-socket) + (.addConnector server (unix-socket-connector server options))) server)) (defn run-jetty @@ -266,6 +282,7 @@ :async-timeout-handler - an async handler to handle an async context timeout :port - the port to listen on (defaults to 80) :host - the hostname to listen on + :unix-socket - the unix domain docket path to listen on :join? - blocks the thread until server ends (defaults to true) :daemon? - use daemon threads (defaults to false) diff --git a/ring-jetty-adapter/test/ring/adapter/test/jetty.clj b/ring-jetty-adapter/test/ring/adapter/test/jetty.clj index 0d3f7517..e0a75673 100644 --- a/ring-jetty-adapter/test/ring/adapter/test/jetty.clj +++ b/ring-jetty-adapter/test/ring/adapter/test/jetty.clj @@ -8,10 +8,15 @@ [ring.core.protocols :as p] [ring.websocket :as ws] [ring.websocket.protocols :as wsp]) - (:import [java.nio ByteBuffer] + (:import [java.io File] + [java.nio ByteBuffer] + [java.nio.file Paths] [org.eclipse.jetty.util.thread QueuedThreadPool] [org.eclipse.jetty.server Server Request SslConnectionFactory] [org.eclipse.jetty.server.handler AbstractHandler] + [org.eclipse.jetty.io ClientConnector] + [org.eclipse.jetty.client HttpClient] + [org.eclipse.jetty.client.http HttpClientTransportOverHTTP] [java.net ServerSocket ConnectException] [java.security KeyStore] [java.io SequenceInputStream ByteArrayInputStream InputStream @@ -81,6 +86,11 @@ (def test-ssl-url (str "https://localhost:" test-ssl-port)) +(def test-unix-domain-socket + (let [sock-file (File/createTempFile "ring-jetty-" ".sock")] + (.delete sock-file) + (.getAbsolutePath sock-file))) + (def nil-keystore (doto (KeyStore/getInstance (KeyStore/getDefaultType)) (.load nil))) @@ -102,6 +112,22 @@ "text/plain")) (is (= (:body response) "Hello World"))))) + (let [java-version (->> (System/getProperty "java.version") + (re-find #"\A\d+") + (Integer/parseInt))] + (when (>= java-version 16) + (testing "UNIX Socket server" + (with-server hello-world {:http? false + :unix-socket test-unix-domain-socket} + (let [path (Paths/get test-unix-domain-socket (make-array String 0)) + connector (ClientConnector/forUnixDomain path) + transport (HttpClientTransportOverHTTP. connector) + client (doto (HttpClient. transport) (.start)) + response (.GET client "http://localhost")] + (is (= (.getStatus response) 200)) + (is (.getMediaType response) "text/plain") + (is (= (.getContentAsString response) "Hello World"))))))) + (testing "HTTPS server" (with-server hello-world {:port test-port :ssl-port test-ssl-port