Skip to content

Commit

Permalink
Merge pull request #69 from sunng87/feature/http-3
Browse files Browse the repository at this point in the history
feat: implement http3 connector
  • Loading branch information
sunng87 authored Apr 6, 2022
2 parents 9d09b8b + 82b7daa commit 9418187
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 14 deletions.
4 changes: 3 additions & 1 deletion .github/workflows/clojure.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ jobs:
graalvm-version: '21.0.0.java11'
- uses: DeLaGuardo/setup-clojure@master
with:
lein: 2.9.5
lein: 2.9.8
- name: Install modules
run: cd http3; lein install
- name: Run tests
run: lein test
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# ring-jetty9-adapter (rj9a)

Ring adapter for Jetty 9 and above versions, with HTTP2 and WebSocket support.
Ring adapter for Jetty 10 and above versions, with HTTP/3, WebSocket and
Expiremental HTTP/3 support.

This is a simple and plain wrapper on Jetty 9. It doesn't introduce
additional thread model or anything else (no unofficial ring variance,
Expand Down Expand Up @@ -80,6 +81,37 @@ options to `run-jetty` like:
:keystore-type "jks"})
```

### HTTP/3

From 10.0.9, Jetty ships a usable expiremental HTTP/3 implementation
based on [the quiche
library](https://github.com/cloudflare/quiche). rj9a made it an
optional feature. To enable HTTP/3 support, you will need to:

* Install libquiche on your system and make sure `libquiche.so` can be
loaded from the Clojure(Java) application.
* In addition to rj9a, add dependency
`[info.sunng/ring-jetty9-adapter-http3 "0.1.0"]` to your clojure
project to bring in HTTP/3 staff.
* Provide certficate and key just like HTTPs setup because HTTP/3 is
secure by default. There is no plaintext fallback for now.
* Provide option `:http3? true` to `run-jetty` to enable HTTP/3
protocol.

```clojure
(jetty/run-jetty dummy-app {:port 5000 ;; default clear-text http/1.1 port
:http3 true ;; enable http/3 support
:ssl-port 5443 ;; ssl-port is used by http/3
:keystore "dev-resources/keystore.jks"
:key-password "111111"
:keystore-type "jks"})
```

Since HTTP/3 runs on UDP, it could share same port with TCP based
protocol like HTTP/2 or 1.1.

An example is available in `examples` folder.

### WebSocket

You can define following handlers for websocket events.
Expand Down Expand Up @@ -168,6 +200,7 @@ You can find examples in `examples` folder. To run example:
* async: `lein with-profile example-async run` ring 1.6 async
handler example
* http2 `lein with-profile example-http2 run`
* http3 `lein with-profile example-http3 run`
* websocket: `lein with-profile example-websocket run`

## Contributors
Expand Down
1 change: 1 addition & 0 deletions dev-resources/simplelogger.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
org.slf4j.simpleLogger.defaultLogLevel=debug
12 changes: 12 additions & 0 deletions examples/rj9a/http3.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(ns rj9a.http3
(:gen-class)
(:require [ring.adapter.jetty9 :as jetty]))

(defn dummy-app [req] {:body "It works" :status 200})

(defn -main [& args]
(jetty/run-jetty dummy-app {:port 5000 :http false :http3? true :ssl-port 5443
:keystore "dev-resources/keystore.jks"
:key-password "111111"
:keystore-type "jks"
:sni-host-check? false}))
17 changes: 17 additions & 0 deletions http3/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/target
/lib
/classes
/checkouts
pom.xml
pom.xml.asc
*.jar
*.class
.lein-deps-sum
.lein-failures
.lein-plugins
.lein-repl-history
.lein-env

.idea/
*.iml
/.nrepl-port
10 changes: 10 additions & 0 deletions http3/project.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
(def jetty-version "10.0.9")

(defproject info.sunng/ring-jetty9-adapter-http3 "0.1.0-SNAPSHOT"
:description "Ring adapter for jetty 9 and above, meta package for http3"
:url "http://github.com/sunng87/ring-jetty9-adapter"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:deploy-repositories {"releases" :clojars}
:global-vars {*warn-on-reflection* true}
:dependencies [[org.eclipse.jetty.http3/http3-server ~jetty-version]])
14 changes: 14 additions & 0 deletions http3/src/ring/adapter/jetty9/http3.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
(ns ring.adapter.jetty9.http3
(:import [org.eclipse.jetty.server
HttpConfiguration SecureRequestCustomizer]
[org.eclipse.jetty.http3.server
HTTP3ServerConnectionFactory HTTP3ServerConnector]
[org.eclipse.jetty.http3.api Session$Server$Listener]))

(defn http3-connector [server http-configuration ssl-context-factory port host]
(let [connection-factory (HTTP3ServerConnectionFactory. http-configuration)
connector (HTTP3ServerConnector. server ssl-context-factory
(into-array HTTP3ServerConnectionFactory [connection-factory]))]
(doto connector
(.setPort port)
(.setHost host))))
9 changes: 6 additions & 3 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
(def jetty-version "10.0.8")
(def jetty-version "10.0.9")

(defproject info.sunng/ring-jetty9-adapter "0.17.6-SNAPSHOT"
:description "Ring adapter for jetty9, which supports websocket and spdy"
:url "http://github.com/sunng87/ring-jetty9-adapter"
:license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.10.3"]
[ring/ring-servlet "1.9.4"]
[ring/ring-servlet "1.9.5" :exclusions [commons-io]]
[info.sunng/ring-jetty9-adapter-http3 "0.1.0-SNAPSHOT" :optional true]
[org.eclipse.jetty/jetty-server ~jetty-version]
[org.eclipse.jetty.websocket/websocket-jetty-api ~jetty-version]
[org.eclipse.jetty.websocket/websocket-jetty-server ~jetty-version]
Expand All @@ -19,10 +20,12 @@
:jvm-args ["-Xmx128m"]
:profiles {:dev {:dependencies [[clj-http "3.12.3"]
[less-awful-ssl "1.0.6"]
[org.slf4j/slf4j-simple "2.0.0-alpha5"]
[org.slf4j/slf4j-simple "2.0.0-alpha6"]
#_[stylefruits/gniazdo "1.1.4"]]}
:example-http2 {:source-paths ["examples/"]
:main ^:skip-aot rj9a.http2}
:example-http3 {:source-paths ["examples/"]
:main ^:skip-aot rj9a.http3}
:example-websocket {:source-paths ["examples/"]
:main ^:skip-aot rj9a.websocket}
:example-http {:source-paths ["examples/"]
Expand Down
25 changes: 16 additions & 9 deletions src/ring/adapter/jetty9.clj
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
(ns ring.adapter.jetty9
"Adapter for the Jetty 9 server, with websocket support.
"Adapter for the Jetty 10 server, with websocket support.
Derived from ring.adapter.jetty"
(:import [org.eclipse.jetty.server
Server Request ServerConnector
Server Request ServerConnector Connector
HttpConfiguration HttpConnectionFactory
ConnectionFactory SecureRequestCustomizer
ProxyConnectionFactory]
Expand All @@ -14,8 +14,7 @@
[org.eclipse.jetty.websocket.server.config JettyWebSocketServletContainerInitializer]
[javax.servlet.http HttpServletRequest HttpServletResponse]
[javax.servlet AsyncContext]
[org.eclipse.jetty.http2
HTTP2Cipher]
[org.eclipse.jetty.http2 HTTP2Cipher]
[org.eclipse.jetty.http2.server
HTTP2CServerConnectionFactory HTTP2ServerConnectionFactory]
[org.eclipse.jetty.alpn.server ALPNServerConnectionFactory]
Expand Down Expand Up @@ -218,12 +217,18 @@
(.setHost host)
(.setIdleTimeout max-idle-time))))

(defn- http3-connector [& args]
;; load http3 module dynamically
(require 'ring.adapter.jetty9.http3)
(let [http3-connector* @(ns-resolve 'ring.adapter.jetty9.http3 'http3-connector)]
(apply http3-connector* args)))

(defn- create-server
"Construct a Jetty Server instance."
[{:as options
:keys [port max-threads min-threads threadpool-idle-timeout job-queue
daemon? max-idle-time host ssl? ssl-port h2? h2c? http? proxy?
thread-pool]
thread-pool http3?]
:or {port 80
max-threads 50
min-threads 8
Expand All @@ -245,11 +250,13 @@
(.addBean (ScheduledExecutorScheduler.)))
http-configuration (http-config options)
ssl? (or ssl? ssl-port)
ssl-factory (ssl-context-factory options)
connectors (cond-> []
ssl? (conj (https-connector server http-configuration (ssl-context-factory options)
ssl? (conj (https-connector server http-configuration ssl-factory
h2? ssl-port host max-idle-time))
http? (conj (http-connector server http-configuration h2c? port host max-idle-time proxy?)))]
(.setConnectors server (into-array connectors))
http? (conj (http-connector server http-configuration h2c? port host max-idle-time proxy?))
http3? (conj (http3-connector server http-configuration ssl-factory ssl-port host)))]
(.setConnectors server (into-array Connector connectors))
server))

(defn ^Server run-jetty
Expand Down Expand Up @@ -300,7 +307,7 @@
:wrap-jetty-handler - a wrapper fn that wraps default jetty handler into another, default to `identity`, not that it's not a ring middleware
:sni-required? - require sni for secure connection, default to false
:sni-host-check? - enable host check for secure connection, default to true
"
:http3? - enable http3 protocol"
[handler {:as options
:keys [configurator join? async?
allow-null-path-info wrap-jetty-handler]
Expand Down

0 comments on commit 9418187

Please sign in to comment.