diff --git a/Dockerfile b/Dockerfile index 1659d00b..e01c8911 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,6 +20,7 @@ ARG BABASHKA_LEAN= ARG BABASHKA_MUSL= ARG BABASHKA_FEATURE_CORE_ASYNC= ARG BABASHKA_FEATURE_CSV= +ARG BABASHKA_FEATURE_JAVA_NET_HTTP= ARG BABASHKA_FEATURE_JAVA_NIO= ARG BABASHKA_FEATURE_JAVA_TIME= ARG BABAHSKA_FEATURE_TRANSIT= @@ -37,6 +38,7 @@ ARG BABASHKA_STATIC= ENV BABASHKA_LEAN=$BABASHKA_LEAN ENV BABASHKA_FEATURE_CORE_ASYNC=$BABASHKA_FEATURE_CORE_ASYNC ENV BABASHKA_FEATURE_CSV=$BABASHKA_FEATURE_CSV +ENV BABASHKA_FEATURE_JAVA_NET_HTTP=$BABASHKA_FEATURE_JAVA_NET_HTTP ENV BABASHKA_FEATURE_JAVA_NIO=$BABASHKA_FEATURE_JAVA_NIO ENV BABASHKA_FEATURE_JAVA_TIME=$BABASHKA_FEATURE_JAVA_TIME ENV BABAHSKA_FEATURE_TRANSIT=$BABAHSKA_FEATURE_TRANSIT diff --git a/doc/build.md b/doc/build.md index 2968bf90..18945435 100644 --- a/doc/build.md +++ b/doc/build.md @@ -95,6 +95,7 @@ Babashka supports the following feature flags: |--------|----------------------------------------------|----------| | `BABASHKA_FEATURE_CORE_ASYNC` | Includes the [clojure.core.async](https://github.com/clojure/core.async) library | `true` | | `BABASHKA_FEATURE_CSV` | Includes the [clojure.data.csv](https://github.com/clojure/data.csv) library | `true` | +| `BABASHKA_FEATURE_JAVA_NET_HTTP` | Includes commonly used classes from the `java.net.http` package | `true` | | `BABASHKA_FEATURE_JAVA_NIO` | Includes commonly used classes from the `java.nio` package | `true` | | `BABASHKA_FEATURE_JAVA_TIME` | Includes commonly used classes from the `java.time` package | `true` | | `BABASHKA_FEATURE_TRANSIT` | Includes the [transit-clj](https://github.com/cognitect/transit-clj) library | `true` | diff --git a/script/compile b/script/compile index 8befcb54..b2f3cade 100755 --- a/script/compile +++ b/script/compile @@ -86,6 +86,7 @@ then export BABASHKA_FEATURE_CSV="${BABASHKA_FEATURE_CSV:-false}" export BABAHSKA_FEATURE_TRANSIT="${BABAHSKA_FEATURE_TRANSIT:-false}" export BABASHKA_FEATURE_JAVA_TIME="${BABASHKA_FEATURE_JAVA_TIME:-false}" + export BABASHKA_FEATURE_JAVA_NET_HTTP="${BABASHKA_FEATURE_JAVA_NET_HTTP:-false}" export BABASHKA_FEATURE_JAVA_NIO="${BABASHKA_FEATURE_JAVA_NIO:-false}" export BABASHKA_FEATURE_HTTPKIT_CLIENT="${BABASHKA_FEATURE_HTTPKIT_CLIENT:-false}" export BABASHKA_FEATURE_HTTPKIT_SERVER="${BABASHKA_FEATURE_HTTPKIT_SERVER:-false}" diff --git a/script/uberjar b/script/uberjar index 973d4e52..8baa7681 100755 --- a/script/uberjar +++ b/script/uberjar @@ -18,6 +18,7 @@ then export BABASHKA_FEATURE_CSV="${BABASHKA_FEATURE_CSV:-false}" export BABAHSKA_FEATURE_TRANSIT="${BABAHSKA_FEATURE_TRANSIT:-false}" export BABASHKA_FEATURE_JAVA_TIME="${BABASHKA_FEATURE_JAVA_TIME:-false}" + export BABASHKA_FEATURE_JAVA_NET_HTTP="${BABASHKA_FEATURE_JAVA_NET_HTTP:-false}" export BABASHKA_FEATURE_JAVA_NIO="${BABASHKA_FEATURE_JAVA_NIO:-false}" export BABASHKA_FEATURE_HTTPKIT_CLIENT="${BABASHKA_FEATURE_HTTPKIT_CLIENT:-false}" export BABASHKA_FEATURE_HTTPKIT_SERVER="${BABASHKA_FEATURE_HTTPKIT_SERVER:-false}" diff --git a/src/babashka/impl/classes.clj b/src/babashka/impl/classes.clj index 5b3720bf..39075dc5 100644 --- a/src/babashka/impl/classes.clj +++ b/src/babashka/impl/classes.clj @@ -158,19 +158,12 @@ java.math.BigInteger java.math.MathContext java.math.RoundingMode - java.net.Authenticator java.net.ConnectException - java.net.CookieHandler - java.net.CookieManager - java.net.CookieStore java.net.DatagramSocket java.net.DatagramPacket - java.net.HttpCookie java.net.HttpURLConnection java.net.InetAddress java.net.InetSocketAddress - java.net.PasswordAuthentication - java.net.ProxySelector java.net.ServerSocket java.net.Socket java.net.SocketException @@ -180,28 +173,41 @@ java.net.URLEncoder java.net.URLDecoder ;; java.net.http - jdk.internal.net.http.HttpClientBuilderImpl - jdk.internal.net.http.HttpClientFacade - jdk.internal.net.http.HttpRequestBuilderImpl - jdk.internal.net.http.HttpResponseImpl - jdk.internal.net.http.common.MinimalFuture - jdk.internal.net.http.websocket.BuilderImpl - jdk.internal.net.http.websocket.WebSocketImpl - java.net.http.HttpClient - java.net.http.HttpClient$Builder - java.net.http.HttpClient$Redirect - java.net.http.HttpClient$Version - java.net.http.HttpHeaders - java.net.http.HttpRequest - java.net.http.HttpRequest$BodyPublisher - java.net.http.HttpRequest$BodyPublishers - java.net.http.HttpRequest$Builder - java.net.http.HttpResponse - java.net.http.HttpResponse$BodyHandler - java.net.http.HttpResponse$BodyHandlers - java.net.http.WebSocket - java.net.http.WebSocket$Builder - java.net.http.WebSocket$Listener + ~@(when features/java-net-http? + '[java.net.Authenticator + java.net.CookieHandler + java.net.CookieManager + java.net.CookieStore + java.net.HttpCookie + java.net.PasswordAuthentication + java.net.ProxySelector + java.net.http.HttpClient + java.net.http.HttpClient$Builder + java.net.http.HttpClient$Redirect + java.net.http.HttpClient$Version + java.net.http.HttpHeaders + java.net.http.HttpRequest + java.net.http.HttpRequest$BodyPublisher + java.net.http.HttpRequest$BodyPublishers + java.net.http.HttpRequest$Builder + java.net.http.HttpResponse + java.net.http.HttpResponse$BodyHandler + java.net.http.HttpResponse$BodyHandlers + java.net.http.WebSocket + java.net.http.WebSocket$Builder + java.net.http.WebSocket$Listener + java.security.cert.X509Certificate + javax.net.ssl.SSLContext + javax.net.ssl.SSLParameters + javax.net.ssl.TrustManager + javax.net.ssl.X509TrustManager + jdk.internal.net.http.HttpClientBuilderImpl + jdk.internal.net.http.HttpClientFacade + jdk.internal.net.http.HttpRequestBuilderImpl + jdk.internal.net.http.HttpResponseImpl + jdk.internal.net.http.common.MinimalFuture + jdk.internal.net.http.websocket.BuilderImpl + jdk.internal.net.http.websocket.WebSocketImpl]) ~@(when features/java-nio? '[java.nio.ByteBuffer java.nio.ByteOrder @@ -312,8 +318,6 @@ java.util.zip.ZipInputStream java.util.zip.ZipOutputStream java.util.zip.ZipEntry - javax.net.ssl.SSLContext - javax.net.ssl.SSLParameters ~(symbol "[B") ~(symbol "[I") ~(symbol "[Ljava.lang.Object;") diff --git a/src/babashka/impl/features.clj b/src/babashka/impl/features.clj index 5a015eaa..6bd3dfe6 100644 --- a/src/babashka/impl/features.clj +++ b/src/babashka/impl/features.clj @@ -8,6 +8,7 @@ (def csv? (not= "false" (System/getenv "BABASHKA_FEATURE_CSV"))) (def transit? (not= "false" (System/getenv "BABASHKA_FEATURE_TRANSIT"))) (def java-time? (not= "false" (System/getenv "BABASHKA_FEATURE_JAVA_TIME"))) +(def java-net-http? (not= "false" (System/getenv "BABASHKA_FEATURE_JAVA_NET_HTTP"))) (def java-nio? (not= "false" (System/getenv "BABASHKA_FEATURE_JAVA_NIO"))) (def httpkit-client? (not= "false" (System/getenv "BABASHKA_FEATURE_HTTPKIT_CLIENT"))) (def httpkit-server? (not= "false" (System/getenv "BABASHKA_FEATURE_HTTPKIT_SERVER"))) diff --git a/src/babashka/impl/reify.clj b/src/babashka/impl/reify.clj index 29fc3429..8a3671f3 100644 --- a/src/babashka/impl/reify.clj +++ b/src/babashka/impl/reify.clj @@ -168,4 +168,9 @@ {get [[this]]} java.lang.Comparable - {compareTo [[this other]]}})) + {compareTo [[this other]]} + + javax.net.ssl.X509TrustManager + {checkClientTrusted [[this chain auth-type]] + checkServerTrusted [[this chain auth-type]] + getAcceptedIssuers [[this]]}})) diff --git a/test/babashka/java_http_client_test.clj b/test/babashka/java_net_http_test.clj similarity index 85% rename from test/babashka/java_http_client_test.clj rename to test/babashka/java_net_http_test.clj index 2bf82108..9700ed50 100644 --- a/test/babashka/java_http_client_test.clj +++ b/test/babashka/java_net_http_test.clj @@ -1,4 +1,4 @@ -(ns babashka.java-http-client-test +(ns babashka.java-net-http-test (:require [babashka.test-utils :as test-utils] [clojure.edn :as edn] @@ -9,7 +9,9 @@ (defn bb [expr] (edn/read-string (apply test-utils/bb nil [(str expr)]))) -(deftest java-http-client-test +;; HttpClient + +(deftest send-test (is (= [200 true] (bb '(do (ns net @@ -24,48 +26,91 @@ (.GET) (.build))) - (def client - (-> (HttpClient/newBuilder) - (.build))) + (def client (HttpClient/newHttpClient)) - (def resp (.send client req (HttpResponse$BodyHandlers/ofString))) - [(.statusCode resp) (string? (.body resp))]))))) + (def res (.send client req (HttpResponse$BodyHandlers/ofString))) + [(.statusCode res) (string? (.body res))]))))) -(deftest redirect-test - (let [redirect-prog - (fn [redirect-kind] - (str/replace (str '(do - (ns net - (:import - (java.net.http HttpClient - HttpClient$Redirect - HttpRequest - HttpRequest$BodyPublishers - HttpResponse$BodyHandlers) - (java.net URI))) - (defn log [x] (.println System/err x)) - (let [req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com")) - (.GET) - (.timeout (java.time.Duration/ofSeconds 5)) - (.build)) - client (-> (HttpClient/newBuilder) - (.followRedirects :redirect/kind) - (.build)) - handler (HttpResponse$BodyHandlers/discarding)] - (.statusCode (.send client req handler))))) - ":redirect/kind" - (case redirect-kind - :never - "HttpClient$Redirect/NEVER" - :always - "HttpClient$Redirect/ALWAYS")))] - ;; TODO: make graalvm repro of never-ending request with redirect always on linux aarch64 (+ musl?) - (when-not (and (= "aarch64" (System/getenv "BABASHKA_ARCH")) - (= "linux" (System/getenv "BABASHKA_PLATFORM"))) - (println "Testing redirect always") - (is (= 200 (bb (redirect-prog :always))))) - (println "Testing redirect never") - (is (= 302 (bb (redirect-prog :never)))))) +(deftest send-async-test + (is (= [200 true] + (bb + '(do + (ns net + (:import + (java.net URI) + (java.net.http HttpClient + HttpRequest + HttpResponse$BodyHandlers) + (java.util.function Function))) + + (let [client (HttpClient/newHttpClient) + req (-> (HttpRequest/newBuilder (URI. "https://www.clojure.org")) + (.GET) + (.build))] + (-> (.sendAsync client req (HttpResponse$BodyHandlers/ofString)) + (.thenApply (reify Function (apply [_ res] [(.statusCode res) (string? (.body res))]))) + (deref)))))))) + +;; HttpClient options + +(deftest authenticator-test + (is (= [401 200] + (bb + '(do + (ns net + (:import + (java.net Authenticator + PasswordAuthentication + URI) + (java.net.http HttpClient + HttpRequest + HttpResponse$BodyHandlers))) + + (let [no-auth-client (HttpClient/newHttpClient) + req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com/basic-auth")) + (.build)) + handler (HttpResponse$BodyHandlers/discarding) + no-auth-res (.send no-auth-client req handler) + authenticator (proxy [Authenticator] [] + (getPasswordAuthentication [] + (PasswordAuthentication. "postman" (char-array "password")))) + auth-client (-> (HttpClient/newBuilder) + (.authenticator authenticator) + (.build)) + auth-res (.send auth-client req handler)] + [(.statusCode no-auth-res) (.statusCode auth-res)])))))) + +(deftest cookie-test + (is (= [] + (bb '(do (ns net + (:import [java.net CookieManager])) + (-> (CookieManager.) + (.getCookieStore) + (.getCookies)))))) + (is (= "www.postman-echo.com" + (bb '(do + (ns net + (:import + (java.net CookieManager + URI) + (java.net.http HttpClient + HttpRequest + HttpResponse$BodyHandlers))) + + (let [client (-> (HttpClient/newBuilder) + (.cookieHandler (CookieManager.)) + (.build)) + req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com/get")) + (.GET) + (.build))] + (.send client req (HttpResponse$BodyHandlers/discarding)) + (-> client + (.cookieHandler) + (.get) + (.getCookieStore) + (.getCookies) + first + (.getDomain)))))))) (deftest connect-timeout-test (is (= "java.net.http.HttpConnectTimeoutException" @@ -94,7 +139,7 @@ :type name))))))))) -(deftest executor +(deftest executor-test (is (= 200 (bb '(do @@ -105,6 +150,7 @@ HttpRequest HttpResponse$BodyHandlers) (java.util.concurrent Executors))) + (let [uri (URI. "https://www.postman-echo.com/get") req (-> (HttpRequest/newBuilder uri) (.GET) @@ -115,7 +161,7 @@ res (.send client req (HttpResponse$BodyHandlers/discarding))] (.statusCode res))))))) -(deftest client-proxy +(deftest proxy-test (is (= true (bb '(do @@ -123,6 +169,7 @@ (:import (java.net ProxySelector) (java.net.http HttpClient))) + (let [bespoke-proxy (proxy [ProxySelector] [] (connectFailed [_ _ _]) (select [_ _])) @@ -142,6 +189,7 @@ (java.net.http HttpClient HttpRequest HttpResponse$BodyHandlers))) + (let [uri (URI. "https://www.postman-echo.com/get") req (-> (HttpRequest/newBuilder uri) (.build)) @@ -151,50 +199,135 @@ res (.send client req (HttpResponse$BodyHandlers/discarding))] (.statusCode res))))))) -(deftest ssl-test - (is (= 200 - (bb - '(do - (ns net - (:import - (java.net URI) - (java.net.http HttpClient - HttpRequest - HttpResponse$BodyHandlers) - (javax.net.ssl SSLContext - SSLParameters))) - (let [uri (URI. "https://www.postman-echo.com/get") - req (-> (HttpRequest/newBuilder uri) - (.build)) - ssl-context (doto (SSLContext/getInstance "TLS") - (.init nil nil nil)) - client (-> (HttpClient/newBuilder) - (.sslContext ssl-context) - (.build)) - res (.send client req (HttpResponse$BodyHandlers/discarding))] - (.statusCode res))))))) +(deftest redirect-test + (let [redirect-prog + (fn [redirect-kind] + (str/replace (str '(do + (ns net + (:import + (java.net.http HttpClient + HttpClient$Redirect + HttpRequest + HttpRequest$BodyPublishers + HttpResponse$BodyHandlers) + (java.net URI))) -(deftest send-async-test - (is (= 200 - (bb - '(do - (ns net - (:import - (java.net ProxySelector - URI) - (java.net.http HttpClient - HttpRequest - HttpResponse$BodyHandlers) - (java.time Duration) - (java.util.function Function))) - (let [client (-> (HttpClient/newBuilder) - (.build)) - req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com/get")) - (.GET) - (.build))] - (-> (.sendAsync client req (HttpResponse$BodyHandlers/discarding)) - (.thenApply (reify Function (apply [_ t] (.statusCode t)))) - (deref)))))))) + (defn log [x] (.println System/err x)) + (let [req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com")) + (.GET) + (.timeout (java.time.Duration/ofSeconds 5)) + (.build)) + client (-> (HttpClient/newBuilder) + (.followRedirects :redirect/kind) + (.build)) + handler (HttpResponse$BodyHandlers/discarding)] + (.statusCode (.send client req handler))))) + ":redirect/kind" + (case redirect-kind + :never + "HttpClient$Redirect/NEVER" + :always + "HttpClient$Redirect/ALWAYS")))] + ;; TODO: make graalvm repro of never-ending request with redirect always on linux aarch64 (+ musl?) + (when-not (and (= "aarch64" (System/getenv "BABASHKA_ARCH")) + (= "linux" (System/getenv "BABASHKA_PLATFORM"))) + (println "Testing redirect always") + (is (= 200 (bb (redirect-prog :always))))) + (println "Testing redirect never") + (is (= 302 (bb (redirect-prog :never)))))) + +(deftest ssl-context-test + ;; TODO: investigate aarch64 issue + (when-not + (and (= "aarch64" (System/getenv "BABASHKA_ARCH")) + (= "linux" (System/getenv "BABASHKA_PLATFORM"))) + (is (= {:expired "java.security.cert.CertificateExpiredException" + :revoked 200 ;; TODO: fix, "sun.security.cert.CertificateRevokedException" + :self-signed "sun.security.provider.certpath.SunCertPathBuilderException" + :untrusted-root "sun.security.provider.certpath.SunCertPathBuilderException" + :wrong-host "sun.security.provider.certpath.SunCertPathBuilderException"} + (bb + '(do + (ns net + (:import + (java.net URI) + (java.net.http HttpClient + HttpRequest + HttpResponse$BodyHandlers))) + + (defn send-and-catch [client req handler] + (try + (let [res (.send client req (HttpResponse$BodyHandlers/discarding))] + (.statusCode res)) + (catch Throwable t + (-> (Throwable->map t) :via last :type name)))) + + (let [client (HttpClient/newHttpClient) + handler (HttpResponse$BodyHandlers/discarding) + reqs (->> [:expired + :self-signed + :revoked + :untrusted-root + :wrong-host] + (map (fn [k] + (let [req (-> (URI. (format "https://%s.badssl.com" (name k))) + (HttpRequest/newBuilder) + (.GET) + (.build))] + [k req]))) + (into {}))] + (->> reqs + (map (fn [[k req]] + [k (send-and-catch client req handler)])) + (into {}))))))) + + (is (= {:expired 200 + :self-signed 200 + :untrusted-root 200} + (bb + '(do + (ns net + (:import + (java.net URI) + (java.net.http HttpClient + HttpRequest + HttpResponse$BodyHandlers) + (java.security SecureRandom) + (java.security.cert X509Certificate) + (javax.net.ssl SSLContext + TrustManager + X509TrustManager))) + + (let [insecure-trust-manager (reify X509TrustManager + (checkClientTrusted [_ _ _]) + (checkServerTrusted [_ _ _]) + (getAcceptedIssuers [_] (into-array X509Certificate []))) + insecure-trust-managers (into-array TrustManager [insecure-trust-manager]) + insecure-context (doto (SSLContext/getInstance "TLS") + (.init nil + insecure-trust-managers + (SecureRandom.))) + client (-> (HttpClient/newBuilder) + (.sslContext insecure-context) + (.build)) + handler (HttpResponse$BodyHandlers/discarding) + reqs (->> [:expired + :self-signed + :untrusted-root] + (map (fn [k] + (let [req (-> (URI. (format "https://%s.badssl.com" (name k))) + (HttpRequest/newBuilder) + (.GET) + (.build))] + [k req]))) + (into {}))] + (->> reqs + (map (fn [[k req]] + [k (-> (.send client req handler) + (.statusCode))])) + (into {}))))))))) + +;; HttpRequest (deftest body-publishers-test (is (= true @@ -212,15 +345,16 @@ HttpRequest$BodyPublishers HttpResponse$BodyHandlers) (java.util.function Supplier))) + (let [bp (HttpRequest$BodyPublishers/ofFile (.toPath (io/file "README.md"))) req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com/post")) (.method "POST" bp) (.build)) - client (-> (HttpClient/newBuilder) - (.build)) + client (HttpClient/newHttpClient) res (.send client req (HttpResponse$BodyHandlers/ofString)) body-data (-> (.body res) (json/parse-string true) :data)] (str/includes? body-data "babashka")))))) + (let [body "with love from java.net.http"] (is (= {:of-input-stream body :of-byte-array body @@ -238,6 +372,7 @@ HttpRequest$BodyPublishers HttpResponse$BodyHandlers) (java.util.function Supplier))) + (let [body "with love from java.net.http" publishers {:of-input-stream (HttpRequest$BodyPublishers/ofInputStream (reify Supplier (get [_] (io/input-stream (.getBytes body))))) @@ -272,122 +407,19 @@ HttpResponse$BodyHandlers) (java.nio.charset Charset) (java.util.function Supplier))) + (let [body "おはようございます!" req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com/post")) (.method "POST" (HttpRequest$BodyPublishers/ofString body (Charset/forName "UTF-16"))) (.header "Content-Type" "text/plain; charset=utf-16") (.build)) - client (-> (HttpClient/newBuilder) - (.build)) + client (HttpClient/newHttpClient) res (.send client req (HttpResponse$BodyHandlers/ofString))] (-> (.body res) (json/parse-string true) :data))))))))) -(deftest cookie-test - (is (= [] - (bb '(do (ns net - (:import [java.net CookieManager])) - (-> (CookieManager.) - (.getCookieStore) - (.getCookies)))))) - (is (= "www.postman-echo.com" - (bb '(do - (ns net - (:import - (java.net CookieManager - URI) - (java.net.http HttpClient - HttpRequest - HttpResponse$BodyHandlers))) - (let [client (-> (HttpClient/newBuilder) - (.cookieHandler (CookieManager.)) - (.build)) - req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com/get")) - (.GET) - (.build))] - (.send client req (HttpResponse$BodyHandlers/discarding)) - (-> client - (.cookieHandler) - (.get) - (.getCookieStore) - (.getCookies) - first - (.getDomain)))))))) - -(deftest authenticator-test - (is (= [401 200] - (bb - '(do - (ns net - (:import - (java.net Authenticator - PasswordAuthentication - URI) - (java.net.http HttpClient - HttpRequest - HttpResponse$BodyHandlers))) - (let [no-auth-client (-> (HttpClient/newBuilder) - (.build)) - req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com/basic-auth")) - (.build)) - handler (HttpResponse$BodyHandlers/discarding) - no-auth-res (.send no-auth-client req handler) - authenticator (proxy [Authenticator] [] - (getPasswordAuthentication [] - (PasswordAuthentication. "postman" (char-array "password")))) - auth-client (-> (HttpClient/newBuilder) - (.authenticator authenticator) - (.build)) - auth-res (.send auth-client req handler)] - [(.statusCode no-auth-res) (.statusCode auth-res)])))))) - -(deftest cert-test - ;; TODO: investigate aarch64 issue - (when-not - (and (= "aarch64" (System/getenv "BABASHKA_ARCH")) - (= "linux" (System/getenv "BABASHKA_PLATFORM"))) - (is (= {:expired "java.security.cert.CertificateExpiredException" - :revoked 200 ;; TODO: fix, "sun.security.cert.CertificateRevokedException" - :self-signed "sun.security.provider.certpath.SunCertPathBuilderException" - :untrusted-root "sun.security.provider.certpath.SunCertPathBuilderException" - :wrong-host "sun.security.provider.certpath.SunCertPathBuilderException"} - (bb - '(do - (ns net - (:import - (java.net URI) - (java.net.http HttpClient - HttpRequest - HttpResponse$BodyHandlers))) - - (defn send-and-catch [client req handler] - (try - (let [res (.send client req (HttpResponse$BodyHandlers/discarding))] - (.statusCode res)) - (catch Throwable t - (-> (Throwable->map t) :via last :type name)))) - - (let [client (-> (HttpClient/newBuilder) - (.build)) - handler (HttpResponse$BodyHandlers/discarding) - reqs (->> [:expired - :self-signed - :revoked - :untrusted-root - :wrong-host] - (map (fn [k] - (let [req (-> (URI. (format "https://%s.badssl.com" (name k))) - (HttpRequest/newBuilder) - (.GET) - (.build))] - [k req]))) - (into {}))] - (->> reqs - (map (fn [[k req]] - [k (send-and-catch client req handler)])) - (into {}))))))))) (deftest request-timeout-test (is (= "java.net.http.HttpTimeoutException" @@ -401,8 +433,7 @@ HttpResponse$BodyHandlers) (java.time Duration))) - (let [client (-> (HttpClient/newBuilder) - (.build)) + (let [client (HttpClient/newHttpClient) req (-> (HttpRequest/newBuilder (URI. "https://www.postman-echo.com/delay/1")) (.GET) (.timeout (Duration/ofMillis 200)) @@ -430,6 +461,7 @@ HttpResponse$BodyHandlers) (java.nio.file Files StandardOpenOption) (java.nio.file.attribute FileAttribute))) + (let [client (-> (HttpClient/newBuilder) (.build)) uri (URI. "https://raw.githubusercontent.com/babashka/babashka/master/README.md") @@ -445,6 +477,8 @@ contents (slurp temp-file-path)] (str/includes? contents "babashka"))))))) +;; WebSockets + (defn ws-handler [{:keys [init] :as opts} req] (when init (init req)) (httpkit.server/as-channel @@ -472,6 +506,7 @@ WebSocket$Listener) (java.util.concurrent CompletableFuture) (java.util.function Function))) + (let [p (promise) uri (URI. "ws://localhost:1234") listener (reify WebSocket$Listener