From 697e98eade2983b465f7d59cb084ae35cc69a70c Mon Sep 17 00:00:00 2001 From: Jan Seeger Date: Tue, 21 Apr 2026 16:31:01 +0200 Subject: [PATCH 1/5] Add support for constructing custom proxy routing. - Modified `->ProxySelector` to take a vector of handlers and proxy parameters in addition to the "simple" host/port config argument. - Added functions to construct handler/proxy pairs. - Added some tests for proxy selection - Updated changelog. --- CHANGELOG.md | 4 ++ src/babashka/http_client.clj | 38 ++++++++++-- src/babashka/http_client/internal.clj | 88 +++++++++++++++++++++++++++ test/babashka/http_client_test.clj | 12 +++- 4 files changed, 137 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d729c7..28ff510 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Babashka [http-client](https://github.com/babashka/http-client): HTTP client for Clojure and babashka built on java.net.http +## Unreleased + +- [#80](https://github.com/babashka/http-client/issues/80): add idiomatic proxy configuration functionality + ## 0.4.23 (2025-06-06) - [#75](https://github.com/babashka/http-client/issues/75): override existing content type header in multipart request diff --git a/src/babashka/http_client.clj b/src/babashka/http_client.clj index 36dbb84..d0b8734 100644 --- a/src/babashka/http_client.clj +++ b/src/babashka/http_client.clj @@ -6,13 +6,43 @@ "Options used to create the (implicit) default client." i/default-client-opts) +(defn proxy-scheme + "Return a handler that will use the proxy described by `opts` for all URLs with a scheme equal to `scheme`." + [scheme opts] + (i/proxy-scheme scheme opts)) + +(defn proxy-exclude-urls + "Return a handler pair that will use a direct proxy for all URLs that have a hostname ending with any of the excluded hostnames." + [excluded-urls] + (i/proxy-exclude-urls excluded-urls)) + +(defn proxy-all + "Return a handler that will always use the provided proxy. Use `->ProxySelector` with the `:host` and `:port` arguments if you + do not combine this with other handlers." + [opts] + (i/proxy-all opts)) + +(defn proxy-host + "Return a handler that will handle a single hostname with the provided proxy." + [hostname opts] + (i/proxy-host hostname opts)) + (defn ->ProxySelector - "Constructs a `java.net.ProxySelector`. + "Constructs a `java.net.ProxySelector`. Can either take a map of `:host` and `:port` actions, or a list of predicate/proxy + pairs for more complex configuration. + Options: * `:host` - string - * `:port` - long" - [opts] - (i/->ProxySelector opts)) + * `:port` - long + Handlers: + * `pred`: A one-argument function receiving a java.net.URI argument. If it returns truthy, the corresponding proxy is returned. + * `proxy`: A map of proxy options containing `:host`, `:port` and `:type` keys. `:host` is a string, `:port` a long and `:type` can + be `:direct`, `:http` or `:socks`. Use the `proxy-scheme`, `proxy-exclude-urls`, `proxy-all` or `proxy-host` functions + to build handler pairs." + [opts-or-handlers] + (if (vector? opts-or-handlers) + (i/make-proxy-selector opts-or-handlers) + (i/->ProxySelector opts-or-handlers))) (defn ->SSLContext "Constructs a `javax.net.ssl.SSLContext`. diff --git a/src/babashka/http_client/internal.clj b/src/babashka/http_client/internal.clj index dea6807..02450b3 100644 --- a/src/babashka/http_client/internal.clj +++ b/src/babashka/http_client/internal.clj @@ -36,6 +36,12 @@ :never HttpClient$Redirect/NEVER :normal HttpClient$Redirect/NORMAL)) +(defn- ->proxy-type [type] + (case type + :direct java.net.Proxy$Type/DIRECT + :socks java.net.Proxy$Type/SOCKS + :http java.net.Proxy$Type/HTTP)) + (defn- version-keyword->version-enum [version] (case version :http1.1 HttpClient$Version/HTTP_1_1 @@ -105,6 +111,88 @@ (cond (and host port) (java.net.ProxySelector/of (java.net.InetSocketAddress. ^String host ^long port)))))) +(defn ->Proxy + [opts] + (if (instance? java.net.Proxy opts) + opts + (let [{:keys [host port type]} opts] + (cond + (= type :direct) + java.net.Proxy/NO_PROXY + (and host port type) + (java.net.Proxy. (->proxy-type type) (java.net.InetSocketAddress. ^String host ^long port)))))) + +(defn- case-insensitive-env-var [name] + (or (System/getenv (str/lower-case name)) + (System/getenv (str/upper-case name)))) + +(defn- no-proxy-value [value] + (cond + (nil? value) + #{} + (= value "*") + :all + :else + (into #{} + (map + (fn [entry] (str/trim entry)) + (str/split value #","))))) + +(defn- env->proxy-opts [value] + (when value + (let [uri (java.net.URI. value) + base-opts {:host (.getHost uri) :port (.getPort uri)}] + (condp contains? (.getScheme uri) + #{"http" "https"} (assoc base-opts :type :http) + #{"socks4" "socks4a" "socks5" "socks5a"} (assoc base-opts :type :socks))))) + +(defn make-proxy-selector [handlers] + (proxy [java.net.ProxySelector] [] + (connectFailed [_ _ _]) + (select [^URI uri] + (loop [[[pred proxy] & rest] handlers] + (cond + (nil? pred) + [java.net.Proxy/NO_PROXY] + (pred uri) + [proxy] + :else + (recur rest)))))) + +(defn proxy-scheme [scheme proxy-opts] + [(fn [^java.net.URI uri] + (= (.getScheme uri) scheme)) + (->Proxy proxy-opts)]) + +(defn proxy-exclude-urls [excluded-urls] + [(fn [^java.net.URI uri] + (some (fn [excluded-url] (str/ends-with? (.getHost uri) excluded-url)) excluded-urls)) + java.net.Proxy/NO_PROXY]) + +(defn proxy-all [proxy-opts] + [(constantly true) + (->Proxy proxy-opts)]) + +(defn proxy-host [hostname proxy-opts] + [(fn [^java.net.URI uri] + (= (.getHost uri) hostname)) + (->Proxy proxy-opts)]) + +(defn proxy-selector-from-env-vars [] + (let [http-proxy-value (env->proxy-opts (case-insensitive-env-var "http_proxy")) + https-proxy-value (env->proxy-opts (case-insensitive-env-var "https_proxy")) + no-proxy-value (no-proxy-value (case-insensitive-env-var "no_proxy"))] + (cond + ;; No proxy defined + (and (nil? http-proxy-value) (nil? https-proxy-value)) + nil + ;; Star in no_proxy variable + (= no-proxy-value :all) + nil + :else + (make-proxy-selector [(proxy-exclude-urls no-proxy-value) + (proxy-scheme "http" (or http-proxy-value https-proxy-value)) + (proxy-scheme "https" (or https-proxy-value http-proxy-value))])))) (defn ->Authenticator [v] (if (instance? Authenticator v) diff --git a/test/babashka/http_client_test.clj b/test/babashka/http_client_test.clj index 9a6a924..6f362a1 100644 --- a/test/babashka/http_client_test.clj +++ b/test/babashka/http_client_test.clj @@ -473,7 +473,17 @@ (deftest proxy-selector (is (instance? java.net.ProxySelector (http/->ProxySelector {:host "https://clojure.org" - :port 1337})))) + :port 1337}))) + (let [complex-proxy-selector (http/->ProxySelector [(http/proxy-exclude-urls #{"localhost"}) + (http/proxy-scheme "http" {:host "example.org" :port 8080 :type :http})])] + (is (instance? java.net.ProxySelector complex-proxy-selector)) + (let [proxies-for-http (.select complex-proxy-selector (java.net.URI. "http://www.example.org")) + excluded-proxies (.select complex-proxy-selector (java.net.URI. "http://localhost")) + proxies-for-https (.select complex-proxy-selector (java.net.URI. "https://www.example.org"))] + (is (= (count proxies-for-http) (count proxies-for-https) (count excluded-proxies) 1)) + (is (= (.type (get proxies-for-http 0)) java.net.Proxy$Type/HTTP)) + (is (= (.type (get excluded-proxies 0)) java.net.Proxy$Type/DIRECT)) + (is (= (.type (get proxies-for-https 0)) java.net.Proxy$Type/DIRECT))))) (deftest cookie-handler-test (testing "nil passthrough" From 34dfe2390ce9544fc1a294d9e13d7026f18967e1 Mon Sep 17 00:00:00 2001 From: Jan Seeger Date: Thu, 23 Apr 2026 10:01:43 +0200 Subject: [PATCH 2/5] Reworked handler/proxy interface into single function interface. --- src/babashka/http_client.clj | 49 ++++------- src/babashka/http_client/internal.clj | 85 ++++++++++--------- .../http_client/internal/helpers_test.clj | 2 +- test/babashka/http_client_test.clj | 23 +++-- 4 files changed, 82 insertions(+), 77 deletions(-) diff --git a/src/babashka/http_client.clj b/src/babashka/http_client.clj index d0b8734..63c96c3 100644 --- a/src/babashka/http_client.clj +++ b/src/babashka/http_client.clj @@ -6,43 +6,30 @@ "Options used to create the (implicit) default client." i/default-client-opts) -(defn proxy-scheme - "Return a handler that will use the proxy described by `opts` for all URLs with a scheme equal to `scheme`." - [scheme opts] - (i/proxy-scheme scheme opts)) - -(defn proxy-exclude-urls - "Return a handler pair that will use a direct proxy for all URLs that have a hostname ending with any of the excluded hostnames." - [excluded-urls] - (i/proxy-exclude-urls excluded-urls)) - -(defn proxy-all - "Return a handler that will always use the provided proxy. Use `->ProxySelector` with the `:host` and `:port` arguments if you - do not combine this with other handlers." - [opts] - (i/proxy-all opts)) +(defn ->ProxySelector + "Constructs a `java.net.ProxySelector`. Can either take a map of `:host` and `:port` actions, or a proxy + creation function that takes a java.net.URI and returns a proxy configuration for ->Proxy. -(defn proxy-host - "Return a handler that will handle a single hostname with the provided proxy." - [hostname opts] - (i/proxy-host hostname opts)) + Options: + * `:host` - string + * `:port` - long + Proxy function: + * `fn`: A one-argument function receiving a java.net.URI argument and returning a proxy configuration for ->Proxy or nil + (meaning don't use a proxy)." + [opts-or-fn] + (if (map? opts-or-fn) + (i/->ProxySelector opts-or-fn) + (i/fn->ProxySelector opts-or-fn))) -(defn ->ProxySelector - "Constructs a `java.net.ProxySelector`. Can either take a map of `:host` and `:port` actions, or a list of predicate/proxy - pairs for more complex configuration. +(defn ->Proxy + "Constructs a `java.net.Proxy`. Options: * `:host` - string * `:port` - long - Handlers: - * `pred`: A one-argument function receiving a java.net.URI argument. If it returns truthy, the corresponding proxy is returned. - * `proxy`: A map of proxy options containing `:host`, `:port` and `:type` keys. `:host` is a string, `:port` a long and `:type` can - be `:direct`, `:http` or `:socks`. Use the `proxy-scheme`, `proxy-exclude-urls`, `proxy-all` or `proxy-host` functions - to build handler pairs." - [opts-or-handlers] - (if (vector? opts-or-handlers) - (i/make-proxy-selector opts-or-handlers) - (i/->ProxySelector opts-or-handlers))) + * `:type` - One of `:direct` (no proxy), `:socks` (socks proxy) or `:http` (application level proxy)." + [opts] + (i/->Proxy opts)) (defn ->SSLContext "Constructs a `javax.net.ssl.SSLContext`. diff --git a/src/babashka/http_client/internal.clj b/src/babashka/http_client/internal.clj index 02450b3..0dce33a 100644 --- a/src/babashka/http_client/internal.clj +++ b/src/babashka/http_client/internal.clj @@ -138,50 +138,46 @@ (fn [entry] (str/trim entry)) (str/split value #","))))) +(defn- uri-port-with-default [^URI uri] + (if (not= (.getPort uri) -1) + (.getPort uri) + (cond + (= (.getScheme uri) "http") 80 + (= (.getScheme uri) "https") 443 + :else -1))) + (defn- env->proxy-opts [value] - (when value + (when (and value (not (str/blank? value))) (let [uri (java.net.URI. value) - base-opts {:host (.getHost uri) :port (.getPort uri)}] + base-opts {:host (.getHost uri) :port (uri-port-with-default uri)}] (condp contains? (.getScheme uri) #{"http" "https"} (assoc base-opts :type :http) #{"socks4" "socks4a" "socks5" "socks5a"} (assoc base-opts :type :socks))))) -(defn make-proxy-selector [handlers] +(defn fn->ProxySelector [proxy-fn] (proxy [java.net.ProxySelector] [] (connectFailed [_ _ _]) (select [^URI uri] - (loop [[[pred proxy] & rest] handlers] - (cond - (nil? pred) - [java.net.Proxy/NO_PROXY] - (pred uri) - [proxy] - :else - (recur rest)))))) - -(defn proxy-scheme [scheme proxy-opts] - [(fn [^java.net.URI uri] - (= (.getScheme uri) scheme)) - (->Proxy proxy-opts)]) - -(defn proxy-exclude-urls [excluded-urls] - [(fn [^java.net.URI uri] - (some (fn [excluded-url] (str/ends-with? (.getHost uri) excluded-url)) excluded-urls)) - java.net.Proxy/NO_PROXY]) - -(defn proxy-all [proxy-opts] - [(constantly true) - (->Proxy proxy-opts)]) - -(defn proxy-host [hostname proxy-opts] - [(fn [^java.net.URI uri] - (= (.getHost uri) hostname)) - (->Proxy proxy-opts)]) - -(defn proxy-selector-from-env-vars [] - (let [http-proxy-value (env->proxy-opts (case-insensitive-env-var "http_proxy")) - https-proxy-value (env->proxy-opts (case-insensitive-env-var "https_proxy")) - no-proxy-value (no-proxy-value (case-insensitive-env-var "no_proxy"))] + ;; Only allow the proxy function to return a single proxy. + ;; I don't really see the use case for multiple. + [(if-let [proxy-opts (proxy-fn uri)] + (->Proxy proxy-opts) + java.net.Proxy/NO_PROXY)]))) + +(defn- proxy-scheme [scheme proxy-opts] + (fn [^java.net.URI uri] + (when (= (.getScheme uri) scheme) + (->Proxy proxy-opts)))) + +(defn- proxy-exclude-urls [excluded-urls] + (fn [^java.net.URI uri] + (when (some (fn [excluded-url] (str/ends-with? (.getHost uri) excluded-url)) excluded-urls) + java.net.Proxy/NO_PROXY))) + +(defn proxy-selector-from-curl-vars [http-proxy-env https-proxy-env no-proxy-env] + (let [http-proxy-value (env->proxy-opts http-proxy-env) + https-proxy-value (env->proxy-opts https-proxy-env) + no-proxy-value (no-proxy-value no-proxy-env)] (cond ;; No proxy defined (and (nil? http-proxy-value) (nil? https-proxy-value)) @@ -190,9 +186,19 @@ (= no-proxy-value :all) nil :else - (make-proxy-selector [(proxy-exclude-urls no-proxy-value) - (proxy-scheme "http" (or http-proxy-value https-proxy-value)) - (proxy-scheme "https" (or https-proxy-value http-proxy-value))])))) + (fn->ProxySelector (fn [^java.net.URI uri] + (some (fn [proxy-fn] (proxy-fn uri)) + (cond-> [] + no-proxy-value (conj (proxy-exclude-urls no-proxy-value)) + https-proxy-value (conj (proxy-scheme "https" https-proxy-value)) + http-proxy-value (conj (proxy-scheme "http" http-proxy-value))))))))) + +(defn proxy-selector-from-env [] + (proxy-selector-from-curl-vars + (case-insensitive-env-var "http_proxy") + (case-insensitive-env-var "https_proxy") + (case-insensitive-env-var "no_proxy"))) + (defn ->Authenticator [v] (if (instance? Authenticator v) @@ -278,7 +284,8 @@ :user-agent (str "babashka.http-client/" iv/version)}}}) (def default-client - (delay (client default-client-opts))) + (let [default-proxy-selector (proxy-selector-from-env)] + (delay (client (merge default-client-opts (when default-proxy-selector {:proxy default-proxy-selector})))))) (defn- method-keyword->str [method] (str/upper-case (name method))) diff --git a/test/babashka/http_client/internal/helpers_test.clj b/test/babashka/http_client/internal/helpers_test.clj index ef03e82..6562c5b 100644 --- a/test/babashka/http_client/internal/helpers_test.clj +++ b/test/babashka/http_client/internal/helpers_test.clj @@ -4,5 +4,5 @@ [clojure.test :as t])) (t/deftest ->uri-tests - (let [uri (h/->uri {:scheme "https" :host "example.com" :path "/foo"})] + (let [^java.net.URI uri (h/->uri {:scheme "https" :host "example.com" :path "/foo"})] (t/is (= (.getPort uri) -1)))) diff --git a/test/babashka/http_client_test.clj b/test/babashka/http_client_test.clj index 6f362a1..20a4852 100644 --- a/test/babashka/http_client_test.clj +++ b/test/babashka/http_client_test.clj @@ -3,6 +3,7 @@ [babashka.fs :as fs] [babashka.http-client :as http] [babashka.http-client.interceptors :as i] + [babashka.http-client.internal :as internal] [babashka.http-client.internal.version :as iv] [borkdude.deflet :refer [deflet]] [cheshire.core :as json] @@ -474,16 +475,26 @@ (is (instance? java.net.ProxySelector (http/->ProxySelector {:host "https://clojure.org" :port 1337}))) - (let [complex-proxy-selector (http/->ProxySelector [(http/proxy-exclude-urls #{"localhost"}) - (http/proxy-scheme "http" {:host "example.org" :port 8080 :type :http})])] + (let [^java.net.ProxySelector complex-proxy-selector (http/->ProxySelector (fn [^java.net.URI uri] + (when (= (.getScheme uri) "http") + {:host "http://www.example.org" + :port 128 + :type :http})))] (is (instance? java.net.ProxySelector complex-proxy-selector)) (let [proxies-for-http (.select complex-proxy-selector (java.net.URI. "http://www.example.org")) - excluded-proxies (.select complex-proxy-selector (java.net.URI. "http://localhost")) proxies-for-https (.select complex-proxy-selector (java.net.URI. "https://www.example.org"))] - (is (= (count proxies-for-http) (count proxies-for-https) (count excluded-proxies) 1)) + (is (= (count proxies-for-http) (count proxies-for-https) 1)) (is (= (.type (get proxies-for-http 0)) java.net.Proxy$Type/HTTP)) - (is (= (.type (get excluded-proxies 0)) java.net.Proxy$Type/DIRECT)) - (is (= (.type (get proxies-for-https 0)) java.net.Proxy$Type/DIRECT))))) + (is (= (.type (get proxies-for-https 0)) java.net.Proxy$Type/DIRECT)))) + (let [environment-proxy-selector (internal/proxy-selector-from-curl-vars "" "https://www.example.org" "localhost")] + (is (instance? java.net.ProxySelector environment-proxy-selector)) + (let [http-proxies (.select environment-proxy-selector (java.net.URI. "http://example.org/index.html")) + https-proxies (.select environment-proxy-selector (java.net.URI. "https://example.org/index.html")) + excluded-proxies (.select environment-proxy-selector (java.net.URI. "http://localhost:8080/do"))] + (is (= (count http-proxies) (count https-proxies) (count excluded-proxies))) + (is (= (get http-proxies 0) java.net.Proxy/NO_PROXY)) + (is (= (get https-proxies 0) (http/->Proxy {:host "example.org" :type :http :port 443}))) + (is (= (get excluded-proxies 0) java.net.Proxy/NO_PROXY))))) (deftest cookie-handler-test (testing "nil passthrough" From f8c5b1405cdc576f68ee746584854ce2253d56ba Mon Sep 17 00:00:00 2001 From: Jan Seeger Date: Thu, 23 Apr 2026 14:08:42 +0200 Subject: [PATCH 3/5] PR changes. --- src/babashka/http_client.clj | 7 ++++--- test/babashka/http_client_test.clj | 7 ++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/babashka/http_client.clj b/src/babashka/http_client.clj index 63c96c3..53b5380 100644 --- a/src/babashka/http_client.clj +++ b/src/babashka/http_client.clj @@ -17,9 +17,10 @@ * `fn`: A one-argument function receiving a java.net.URI argument and returning a proxy configuration for ->Proxy or nil (meaning don't use a proxy)." [opts-or-fn] - (if (map? opts-or-fn) - (i/->ProxySelector opts-or-fn) - (i/fn->ProxySelector opts-or-fn))) + (cond + (instance? java.net.ProxySelector opts-or-fn) opts-or-fn + (map? opts-or-fn) (i/->ProxySelector opts-or-fn) + :else (i/fn->ProxySelector opts-or-fn))) (defn ->Proxy "Constructs a `java.net.Proxy`. diff --git a/test/babashka/http_client_test.clj b/test/babashka/http_client_test.clj index 20a4852..74d7dab 100644 --- a/test/babashka/http_client_test.clj +++ b/test/babashka/http_client_test.clj @@ -475,6 +475,11 @@ (is (instance? java.net.ProxySelector (http/->ProxySelector {:host "https://clojure.org" :port 1337}))) + ;; Check passthrough behavior. + (is (instance? java.net.ProxySelector + (http/->ProxySelector + (http/->ProxySelector {:host "https://clojure.org" + :port 1337})))) (let [^java.net.ProxySelector complex-proxy-selector (http/->ProxySelector (fn [^java.net.URI uri] (when (= (.getScheme uri) "http") {:host "http://www.example.org" @@ -493,7 +498,7 @@ excluded-proxies (.select environment-proxy-selector (java.net.URI. "http://localhost:8080/do"))] (is (= (count http-proxies) (count https-proxies) (count excluded-proxies))) (is (= (get http-proxies 0) java.net.Proxy/NO_PROXY)) - (is (= (get https-proxies 0) (http/->Proxy {:host "example.org" :type :http :port 443}))) + (is (= (get https-proxies 0) (http/->Proxy {:host "www.example.org" :type :http :port 443}))) (is (= (get excluded-proxies 0) java.net.Proxy/NO_PROXY))))) (deftest cookie-handler-test From 5245b3a0ddb49d128e252ca53410e04b7d0497e7 Mon Sep 17 00:00:00 2001 From: Jan Seeger Date: Thu, 7 May 2026 11:57:19 +0200 Subject: [PATCH 4/5] Removed usage of environment variables for default proxy. --- src/babashka/http_client/internal.clj | 67 --------------------------- test/babashka/http_client_test.clj | 11 +---- 2 files changed, 1 insertion(+), 77 deletions(-) diff --git a/src/babashka/http_client/internal.clj b/src/babashka/http_client/internal.clj index 0dce33a..0022f8e 100644 --- a/src/babashka/http_client/internal.clj +++ b/src/babashka/http_client/internal.clj @@ -122,38 +122,6 @@ (and host port type) (java.net.Proxy. (->proxy-type type) (java.net.InetSocketAddress. ^String host ^long port)))))) -(defn- case-insensitive-env-var [name] - (or (System/getenv (str/lower-case name)) - (System/getenv (str/upper-case name)))) - -(defn- no-proxy-value [value] - (cond - (nil? value) - #{} - (= value "*") - :all - :else - (into #{} - (map - (fn [entry] (str/trim entry)) - (str/split value #","))))) - -(defn- uri-port-with-default [^URI uri] - (if (not= (.getPort uri) -1) - (.getPort uri) - (cond - (= (.getScheme uri) "http") 80 - (= (.getScheme uri) "https") 443 - :else -1))) - -(defn- env->proxy-opts [value] - (when (and value (not (str/blank? value))) - (let [uri (java.net.URI. value) - base-opts {:host (.getHost uri) :port (uri-port-with-default uri)}] - (condp contains? (.getScheme uri) - #{"http" "https"} (assoc base-opts :type :http) - #{"socks4" "socks4a" "socks5" "socks5a"} (assoc base-opts :type :socks))))) - (defn fn->ProxySelector [proxy-fn] (proxy [java.net.ProxySelector] [] (connectFailed [_ _ _]) @@ -164,41 +132,6 @@ (->Proxy proxy-opts) java.net.Proxy/NO_PROXY)]))) -(defn- proxy-scheme [scheme proxy-opts] - (fn [^java.net.URI uri] - (when (= (.getScheme uri) scheme) - (->Proxy proxy-opts)))) - -(defn- proxy-exclude-urls [excluded-urls] - (fn [^java.net.URI uri] - (when (some (fn [excluded-url] (str/ends-with? (.getHost uri) excluded-url)) excluded-urls) - java.net.Proxy/NO_PROXY))) - -(defn proxy-selector-from-curl-vars [http-proxy-env https-proxy-env no-proxy-env] - (let [http-proxy-value (env->proxy-opts http-proxy-env) - https-proxy-value (env->proxy-opts https-proxy-env) - no-proxy-value (no-proxy-value no-proxy-env)] - (cond - ;; No proxy defined - (and (nil? http-proxy-value) (nil? https-proxy-value)) - nil - ;; Star in no_proxy variable - (= no-proxy-value :all) - nil - :else - (fn->ProxySelector (fn [^java.net.URI uri] - (some (fn [proxy-fn] (proxy-fn uri)) - (cond-> [] - no-proxy-value (conj (proxy-exclude-urls no-proxy-value)) - https-proxy-value (conj (proxy-scheme "https" https-proxy-value)) - http-proxy-value (conj (proxy-scheme "http" http-proxy-value))))))))) - -(defn proxy-selector-from-env [] - (proxy-selector-from-curl-vars - (case-insensitive-env-var "http_proxy") - (case-insensitive-env-var "https_proxy") - (case-insensitive-env-var "no_proxy"))) - (defn ->Authenticator [v] (if (instance? Authenticator v) diff --git a/test/babashka/http_client_test.clj b/test/babashka/http_client_test.clj index 74d7dab..1807486 100644 --- a/test/babashka/http_client_test.clj +++ b/test/babashka/http_client_test.clj @@ -490,16 +490,7 @@ proxies-for-https (.select complex-proxy-selector (java.net.URI. "https://www.example.org"))] (is (= (count proxies-for-http) (count proxies-for-https) 1)) (is (= (.type (get proxies-for-http 0)) java.net.Proxy$Type/HTTP)) - (is (= (.type (get proxies-for-https 0)) java.net.Proxy$Type/DIRECT)))) - (let [environment-proxy-selector (internal/proxy-selector-from-curl-vars "" "https://www.example.org" "localhost")] - (is (instance? java.net.ProxySelector environment-proxy-selector)) - (let [http-proxies (.select environment-proxy-selector (java.net.URI. "http://example.org/index.html")) - https-proxies (.select environment-proxy-selector (java.net.URI. "https://example.org/index.html")) - excluded-proxies (.select environment-proxy-selector (java.net.URI. "http://localhost:8080/do"))] - (is (= (count http-proxies) (count https-proxies) (count excluded-proxies))) - (is (= (get http-proxies 0) java.net.Proxy/NO_PROXY)) - (is (= (get https-proxies 0) (http/->Proxy {:host "www.example.org" :type :http :port 443}))) - (is (= (get excluded-proxies 0) java.net.Proxy/NO_PROXY))))) + (is (= (.type (get proxies-for-https 0)) java.net.Proxy$Type/DIRECT))))) (deftest cookie-handler-test (testing "nil passthrough" From 222d492d5bce7b41680a7accd76ec03df70b6c76 Mon Sep 17 00:00:00 2001 From: Jan Seeger Date: Tue, 12 May 2026 08:00:54 +0200 Subject: [PATCH 5/5] Removed leftover default proxy functionality. --- src/babashka/http_client/internal.clj | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/babashka/http_client/internal.clj b/src/babashka/http_client/internal.clj index 0022f8e..889921b 100644 --- a/src/babashka/http_client/internal.clj +++ b/src/babashka/http_client/internal.clj @@ -217,8 +217,7 @@ :user-agent (str "babashka.http-client/" iv/version)}}}) (def default-client - (let [default-proxy-selector (proxy-selector-from-env)] - (delay (client (merge default-client-opts (when default-proxy-selector {:proxy default-proxy-selector})))))) + (delay (client default-client-opts))) (defn- method-keyword->str [method] (str/upper-case (name method)))