Repository: camunda/connectors
Pull request #4320: refactor(rest): refactor the proxy support for the REST Connector
Author: login: johnBgood
Labels: name: deploy-preview
name: qa:required
description: Will trigger the QA workflow
Created: 2025-03-14T17:47:52Z | Merged: 2025-03-21T10:54:44Z

## Description

This PR changes the previous behavior about how we configure a proxy (and non-proxy hosts).
- We can now set up a proxy for REST-like connectors **only** (REST, Github, etc). This is done using a specific set of **environment variables**: 


| Env. Var | Description | Default |
|--------|--------|--------|
| CONNECTOR_HTTP(S)_PROXY_SCHEME | The proxy **scheme** (`http`, or `https`). This allows the user to use https proxies which is not possible with other clients | `http` |
| CONNECTOR_HTTP(S)_PROXY_HOST | The **host** part of the proxy URL (do not include `http://`)  | |
| CONNECTOR_HTTP(S)_PROXY_PORT | The **port** part of the proxy URL | |
| CONNECTOR_HTTP(S)_PROXY_USER | If you need to authenticate, use this variable to set the **user** | |
| CONNECTOR_HTTP(S)_PROXY_PASSWORD | If you need to authenticate, use this variable to set the **password** | | 
| CONNECTOR_HTTP_NON_PROXY_HOSTS | Similar to the [Java](https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html) `http.nonProxyHosts` system property | 

- Note that these values are **not copied** to the System Properties anymore.
- If you want a global behavior, don't use these variables but use System Properties instead.

## Checklist

- [x] PR has a **milestone** or the `no milestone` label.




Diff:
diff --git a/.ci/preview-environments/charts/c8sm/values.yaml b/.ci/preview-environments/charts/c8sm/values.yaml
index 3a29af0d62..228cf74ebb 100644
--- a/.ci/preview-environments/charts/c8sm/values.yaml
+++ b/.ci/preview-environments/charts/c8sm/values.yaml
@@ -95,9 +95,9 @@ camunda-platform:
         value: ${ZEEBE_AUTHORIZATION_SERVER_URL}
       - name: CAMUNDA_CONNECTOR_POLLING_ENABLED
         value: "false"
-      - name: CAMUNDA_CLIENT_ZEEBE_REST-ADDRESS
+      - name: CAMUNDA_CLIENT_REST-ADDRESS
         value: http://zeebe-cluster-gateway:9600
-      - name: CAMUNDA_CLIENT_ZEEBE_GRPC-ADDRESS
+      - name: CAMUNDA_CLIENT_GRPC-ADDRESS
         value: http://zeebe-cluster-gateway:26500
     resources:
       limits:
diff --git a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/HttpService.java b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/HttpService.java
index 6bc34c3df8..f2385db375 100644
--- a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/HttpService.java
+++ b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/HttpService.java
@@ -21,7 +21,6 @@
 import io.camunda.connector.http.base.blocklist.HttpBlockListManager;
 import io.camunda.connector.http.base.client.HttpClient;
 import io.camunda.connector.http.base.client.apache.CustomApacheHttpClient;
-import io.camunda.connector.http.base.client.apache.ProxyHandler;
 import io.camunda.connector.http.base.cloudfunction.CloudFunctionService;
 import io.camunda.connector.http.base.model.HttpCommonRequest;
 import io.camunda.connector.http.base.model.HttpCommonResult;
@@ -35,12 +34,10 @@ public class HttpService {
 
   private final CloudFunctionService cloudFunctionService;
 
-  private final HttpClient httpClient = CustomApacheHttpClient.getDefault();
+  private final HttpClient httpClient = new CustomApacheHttpClient();
 
   private final HttpBlockListManager httpBlocklistManager = new DefaultHttpBlocklistManager();
 
-  private final ProxyHandler proxyHandler = new ProxyHandler();
-
   public HttpService() {
     this(new CloudFunctionService());
   }
@@ -73,7 +70,7 @@ public HttpCommonResult executeConnectorRequest(
   private HttpCommonResult executeRequest(
       HttpCommonRequest request, @Nullable ExecutionEnvironment executionEnvironment) {
     try {
-      HttpCommonResult jsonResult = httpClient.execute(request, proxyHandler, executionEnvironment);
+      HttpCommonResult jsonResult = httpClient.execute(request, executionEnvironment);
       LOGGER.debug("Connector returned result: {}", jsonResult);
       return jsonResult;
     } catch (ConnectorException e) {
diff --git a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/HttpClient.java b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/HttpClient.java
index 7dd3dc6597..e9464065d5 100644
--- a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/HttpClient.java
+++ b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/HttpClient.java
@@ -17,7 +17,6 @@
 package io.camunda.connector.http.base.client;
 
 import io.camunda.connector.http.base.ExecutionEnvironment;
-import io.camunda.connector.http.base.client.apache.ProxyHandler;
 import io.camunda.connector.http.base.model.HttpCommonRequest;
 import io.camunda.connector.http.base.model.HttpCommonResult;
 import javax.annotation.Nullable;
@@ -33,9 +32,7 @@ public interface HttpClient {
    * @return the result of the request as a {@link HttpCommonResult}
    */
   HttpCommonResult execute(
-      HttpCommonRequest request,
-      ProxyHandler proxyHandler,
-      @Nullable ExecutionEnvironment executionEnvironment);
+      HttpCommonRequest request, @Nullable ExecutionEnvironment executionEnvironment);
 
   /**
    * Executes the given {@link HttpCommonRequest} and returns the result as a {@link
@@ -43,9 +40,9 @@ HttpCommonResult execute(
    *
    * @param request the {@link HttpCommonRequest} to execute
    * @return the result of the request as a {@link HttpCommonResult}
-   * @see #execute(HttpCommonRequest, ProxyHandler, ExecutionEnvironment)
+   * @see #execute(HttpCommonRequest, ExecutionEnvironment)
    */
   default HttpCommonResult execute(HttpCommonRequest request) {
-    return execute(request, new ProxyHandler(), null);
+    return execute(request, null);
   }
 }
diff --git a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClient.java b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClient.java
index d5c762ee5b..c2eba583c6 100644
--- a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClient.java
+++ b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClient.java
@@ -20,103 +20,49 @@
 import io.camunda.connector.http.base.ExecutionEnvironment;
 import io.camunda.connector.http.base.client.HttpClient;
 import io.camunda.connector.http.base.client.HttpStatusHelper;
+import io.camunda.connector.http.base.client.apache.proxy.ProxyAwareHttpClient;
 import io.camunda.connector.http.base.exception.ConnectorExceptionMapper;
 import io.camunda.connector.http.base.model.HttpCommonRequest;
 import io.camunda.connector.http.base.model.HttpCommonResult;
 import java.io.IOException;
 import javax.annotation.Nullable;
 import org.apache.hc.client5.http.ClientProtocolException;
-import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
 import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.io.SocketConfig;
-import org.apache.hc.core5.util.Timeout;
 
 public class CustomApacheHttpClient implements HttpClient {
 
-  public static final CustomApacheHttpClient DEFAULT =
-      new CustomApacheHttpClient(createHttpClientBuilder());
-
-  private final HttpClientBuilder httpClientBuilder;
-
-  private CustomApacheHttpClient(HttpClientBuilder httpClientBuilder) {
-    this.httpClientBuilder = httpClientBuilder;
-  }
-
-  /**
-   * Returns the default instance of {@link CustomApacheHttpClient}. The default instance is
-   * configured with a {@link HttpClientBuilder} that has a {@link
-   * PoolingHttpClientConnectionManager} with a maximum of {@link Integer#MAX_VALUE} connections.
-   *
-   * @return the default instance of {@link CustomApacheHttpClient}
-   */
-  public static CustomApacheHttpClient getDefault() {
-    return DEFAULT;
-  }
-
-  /**
-   * Creates a new instance of {@link CustomApacheHttpClient} with the given {@link
-   * HttpClientBuilder}. Use this method if you want to customize the {@link HttpClientBuilder}. See
-   * {@link #getDefault()} for the default instance.
-   *
-   * @param httpClientBuilder the {@link HttpClientBuilder} to use
-   * @return a new instance of {@link CustomApacheHttpClient}
-   * @see HttpClients#custom()
-   */
-  public static CustomApacheHttpClient create(HttpClientBuilder httpClientBuilder) {
-    return new CustomApacheHttpClient(httpClientBuilder);
-  }
-
-  private static HttpClientBuilder createHttpClientBuilder() {
-    return HttpClients.custom()
-        .setConnectionManager(createConnectionManager())
-        .disableRedirectHandling();
-  }
-
-  private static PoolingHttpClientConnectionManager createConnectionManager() {
-    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
-    connectionManager.setMaxTotal(Integer.MAX_VALUE);
-    connectionManager.setDefaultMaxPerRoute(Integer.MAX_VALUE);
-
-    // Socket config
-    connectionManager.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).build());
-
-    return connectionManager;
-  }
-
   /**
    * Converts the given {@link HttpCommonRequest} to an Apache {@link
    * org.apache.hc.core5.http.ClassicHttpRequest} and executes it.
    *
+   * <p>This method is thread-safe.
+   *
    * @param request the request to execute
    * @param executionEnvironment the {@link ExecutionEnvironment} we are in
    * @return the {@link HttpCommonResult}
    */
   @Override
   public HttpCommonResult execute(
-      HttpCommonRequest request,
-      ProxyHandler proxyHandler,
-      @Nullable ExecutionEnvironment executionEnvironment) {
+      HttpCommonRequest request, @Nullable ExecutionEnvironment executionEnvironment) {
     try {
       var apacheRequest = ApacheRequestFactory.get().createHttpRequest(request);
-      var result =
-          httpClientBuilder
-              .setDefaultRequestConfig(getRequestConfig(request))
-              .setDefaultCredentialsProvider(
-                  proxyHandler.getCredentialsProvider(apacheRequest.getScheme()))
-              .useSystemProperties() // Will fallback on system properties for unset values,
-              // e.g. http.keepAlive, http.agent
-              .build()
-              .execute(
-                  apacheRequest,
-                  new HttpCommonResultResponseHandler(
-                      executionEnvironment, request.isStoreResponse()));
-      if (HttpStatusHelper.isError(result.status())) {
-        throw ConnectorExceptionMapper.from(result);
+      var host = apacheRequest.getAuthority().getHostName();
+      var scheme = apacheRequest.getScheme();
+      try (var client =
+          new ProxyAwareHttpClient(
+              new ProxyAwareHttpClient.TimeoutConfiguration(
+                  request.getConnectionTimeoutInSeconds(), request.getReadTimeoutInSeconds()),
+              new ProxyAwareHttpClient.ProxyContext(scheme, host))) {
+        var result =
+            client.execute(
+                apacheRequest,
+                new HttpCommonResultResponseHandler(
+                    executionEnvironment, request.isStoreResponse()));
+        if (HttpStatusHelper.isError(result.status())) {
+          throw ConnectorExceptionMapper.from(result);
+        }
+        return result;
       }
-      return result;
     } catch (ClientProtocolException e) {
       throw new ConnectorException(
           String.valueOf(HttpStatus.SC_SERVER_ERROR),
@@ -129,11 +75,4 @@ public HttpCommonResult execute(
           e);
     }
   }
-
-  private RequestConfig getRequestConfig(HttpCommonRequest request) {
-    return RequestConfig.custom()
-        .setConnectionRequestTimeout(Timeout.ofSeconds(request.getConnectionTimeoutInSeconds()))
-        .setResponseTimeout(Timeout.ofSeconds(request.getReadTimeoutInSeconds()))
-        .build();
-  }
 }
diff --git a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
index 5f55f107a5..b6db2a8232 100644
--- a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
+++ b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
@@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
     return oAuthService.extractTokenFromResponse(response.body());
   }
 }
diff --git a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyAwareHttpClient.java b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyAwareHttpClient.java
new file mode 100644
index 0000000000..c677632395
--- /dev/null
+++ b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyAwareHttpClient.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
+ * under one or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. Camunda licenses this file to you under the Apache License,
+ * Version 2.0; you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.camunda.connector.http.base.client.apache.proxy;
+
+import java.io.Closeable;
+import java.io.IOException;
+import org.apache.hc.client5.http.config.RequestConfig;
+import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
+import org.apache.hc.client5.http.impl.classic.HttpClients;
+import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.io.HttpClientResponseHandler;
+import org.apache.hc.core5.http.io.SocketConfig;
+import org.apache.hc.core5.util.Timeout;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Builder for creating a {@link CloseableHttpClient} with proxy support. This class is thread-safe.
+ */
+public class ProxyAwareHttpClient implements Closeable {
+  private static final Logger LOG = LoggerFactory.getLogger(ProxyAwareHttpClient.class);
+
+  private final ProxyHandler proxyHandler = new ProxyHandler();
+  private final TimeoutConfiguration timeoutConfiguration;
+  private final ProxyContext proxyContext;
+  private final CloseableHttpClient client;
+
+  public record TimeoutConfiguration(int connectionTimeoutInSeconds, int readTimeoutInSeconds) {}
+
+  public record ProxyContext(String scheme, String host) {}
+
+  public ProxyAwareHttpClient(
+      TimeoutConfiguration timeoutConfiguration, ProxyContext proxyContext) {
+    this.timeoutConfiguration = timeoutConfiguration;
+    this.proxyContext = proxyContext;
+    this.client = createClient();
+  }
+
+  @Override
+  public void close() throws IOException {
+    if (client != null) {
+      client.close();
+    }
+  }
+
+  public <T> T execute(ClassicHttpRequest request, HttpClientResponseHandler<T> responseHandler)
+      throws IOException {
+    return client.execute(request, responseHandler);
+  }
+
+  private CloseableHttpClient createClient() {
+    var builder = createHttpClientBuilder();
+
+    setProxyIfConfigured(proxyContext, builder);
+
+    builder
+        .setDefaultRequestConfig(getRequestTimeoutConfig(timeoutConfiguration))
+        .useSystemProperties();
+
+    return builder.build();
+  }
+
+  private void setProxyIfConfigured(ProxyContext proxyContext, HttpClientBuilder builder) {
+    proxyHandler
+        .getProxyDetails(proxyContext.scheme())
+        .ifPresent(
+            proxyDetails -> {
+              HttpHost proxyHttpHost =
+                  new HttpHost(proxyDetails.scheme(), proxyDetails.host(), proxyDetails.port());
+              LOG.debug(
+                  "Using proxy for target scheme [{}] and host [{}] => [{}]",
+                  proxyContext.scheme(),
+                  proxyContext.host(),
+                  proxyHttpHost);
+              builder.setDefaultCredentialsProvider(
+                  proxyHandler.getCredentialsProvider(proxyContext.scheme()));
+              builder.setRoutePlanner(new ProxyRoutePlanner(proxyHttpHost));
+            });
+  }
+
+  private HttpClientBuilder createHttpClientBuilder() {
+    return HttpClients.custom()
+        .setConnectionManager(createConnectionManager())
+        .disableRedirectHandling();
+  }
+
+  private PoolingHttpClientConnectionManager createConnectionManager() {
+    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
+    connectionManager.setMaxTotal(Integer.MAX_VALUE);
+    connectionManager.setDefaultMaxPerRoute(Integer.MAX_VALUE);
+
+    // Socket config
+    connectionManager.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).build());
+
+    return connectionManager;
+  }
+
+  private RequestConfig getRequestTimeoutConfig(TimeoutConfiguration timeoutConfiguration) {
+    return RequestConfig.custom()
+        .setConnectionRequestTimeout(
+            Timeout.ofSeconds(timeoutConfiguration.connectionTimeoutInSeconds()))
+        .setResponseTimeout(Timeout.ofSeconds(timeoutConfiguration.readTimeoutInSeconds()))
+        .build();
+  }
+}
diff --git a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/ProxyHandler.java b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyHandler.java
similarity index 54%
rename from connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/ProxyHandler.java
rename to connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyHandler.java
index 9d01925651..d271bd37b0 100644
--- a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/ProxyHandler.java
+++ b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyHandler.java
@@ -14,10 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package io.camunda.connector.http.base.client.apache;
+package io.camunda.connector.http.base.client.apache.proxy;
 
 import io.camunda.connector.api.error.ConnectorInputException;
-import java.net.*;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -28,36 +27,52 @@
 import org.apache.hc.client5.http.auth.UsernamePasswordCredentials;
 import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
 
+/**
+ * This class is responsible for handling proxy configuration. It reads the proxy configuration from
+ * environment variables and provides the proxy details and credentials provider for a given
+ * protocol. Here's the list of environment variables that are used to configure the proxy:
+ *
+ * <ul>
+ *   <li>CONNECTOR_HTTP(S)_PROXY_SCHEME (default: http)
+ *   <li>CONNECTOR_HTTP(S)_PROXY_HOST
+ *   <li>CONNECTOR_HTTP(S)_PROXY_PORT
+ *   <li>CONNECTOR_HTTP(S)_PROXY_USER
+ *   <li>CONNECTOR_HTTP(S)_PROXY_PASSWORD
+ *   <li>CONNECTOR_HTTP_NON_PROXY_HOSTS
+ * </ul>
+ *
+ * The proxy configuration can be set for both HTTP and HTTPS protocols (for the target URL),
+ * allowing different proxy configurations for each protocol.
+ */
 public class ProxyHandler {
-  record ProxyDetails(
-      String protocol,
-      String host,
-      int port,
-      String user,
-      String password,
-      String nonProxyHosts,
-      boolean sourceIsSystemProperties) {}
+  public record ProxyDetails(String scheme, String host, int port, String user, String password) {}
 
-  private static final List<String> PROTOCOLS = List.of("http", "https");
+  public static final String CONNECTOR_HTTP_NON_PROXY_HOSTS_ENV_VAR =
+      "CONNECTOR_HTTP_NON_PROXY_HOSTS";
+  public static final String HTTP = "http";
+  public static final String HTTPS = "https";
+  private static final String DEFAULT_SCHEME = HTTP;
+  private static final List<String> PROTOCOLS = List.of(HTTP, HTTPS);
   private Map<String, ProxyDetails> proxyConfigForProtocols = new HashMap<>();
   private Map<String, CredentialsProvider> credentialsProvidersForProtocols = new HashMap<>();
 
   public ProxyHandler() {
     this.proxyConfigForProtocols = loadProxyConfig();
     this.credentialsProvidersForProtocols = initializeCredentialsProviders();
-    this.syncEnvVarsToSystemProperties();
   }
 
   private Map<String, ProxyDetails> loadProxyConfig() {
     Map<String, ProxyDetails> config = new HashMap<>();
     for (String protocol : PROTOCOLS) {
-      getConfigFromSystemProperties(protocol)
-          .or(() -> getConfigFromEnvVars(protocol))
-          .ifPresent(d -> config.put(protocol, d));
+      getConfigFromEnvVars(protocol).ifPresent(d -> config.put(protocol, d));
     }
     return config;
   }
 
+  public Optional<ProxyDetails> getProxyDetails(String protocol) {
+    return Optional.ofNullable(proxyConfigForProtocols.get(protocol));
+  }
+
   private Map<String, CredentialsProvider> initializeCredentialsProviders() {
     Map<String, CredentialsProvider> providers = new HashMap<>();
     for (Map.Entry<String, ProxyDetails> entry : proxyConfigForProtocols.entrySet()) {
@@ -80,16 +95,17 @@ private Optional<ProxyDetails> getConfigFromEnvVars(String protocol) {
       try {
         return Optional.of(
             new ProxyDetails(
-                protocol,
+                System.getenv()
+                    .getOrDefault(
+                        "CONNECTOR_" + protocol.toUpperCase() + "_PROXY_SCHEME", DEFAULT_SCHEME),
                 System.getenv("CONNECTOR_" + protocol.toUpperCase() + "_PROXY_HOST"),
                 Integer.parseInt(
                     System.getenv("CONNECTOR_" + protocol.toUpperCase() + "_PROXY_PORT")),
                 System.getenv()
                     .getOrDefault("CONNECTOR_" + protocol.toUpperCase() + "_PROXY_USER", null),
                 System.getenv()
-                    .getOrDefault("CONNECTOR_" + protocol.toUpperCase() + "_PROXY_PASSWORD", null),
-                System.getenv("CONNECTOR_HTTP_PROXY_NON_PROXY_HOSTS"),
-                false));
+                    .getOrDefault(
+                        "CONNECTOR_" + protocol.toUpperCase() + "_PROXY_PASSWORD", null)));
       } catch (NumberFormatException e) {
         throw new ConnectorInputException("Invalid proxy port in environment variables", e);
       }
@@ -97,56 +113,7 @@ private Optional<ProxyDetails> getConfigFromEnvVars(String protocol) {
     return Optional.empty();
   }
 
-  private Optional<ProxyDetails> getConfigFromSystemProperties(String protocol) {
-    if (StringUtils.isNotBlank(System.getProperty(protocol + ".proxyHost"))
-        && StringUtils.isNotBlank(System.getProperty(protocol + ".proxyPort"))) {
-      try {
-        return Optional.of(
-            new ProxyDetails(
-                protocol,
-                System.getProperty(protocol + ".proxyHost"),
-                Integer.parseInt(System.getProperty(protocol + ".proxyPort")),
-                System.getProperty(protocol + ".proxyUser"),
-                System.getProperty(protocol + ".proxyPassword"),
-                System.getProperty(protocol + ".nonProxyHosts"),
-                true));
-      } catch (NumberFormatException e) {
-        throw new ConnectorInputException(
-            "Invalid proxy port in system properties for " + protocol, e);
-      }
-    }
-    return Optional.empty();
-  }
-
   public CredentialsProvider getCredentialsProvider(String protocol) {
     return credentialsProvidersForProtocols.getOrDefault(protocol, new BasicCredentialsProvider());
   }
-
-  /*
-   Set the system properties for the proxy settings from env vars, if needed, because
-   the default proxy selector does enforce a set of System Properties related to proxy settings.
-   https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/ProxySelector.html
-  */
-  private void syncEnvVarsToSystemProperties() {
-    for (Map.Entry<String, ProxyDetails> entry : proxyConfigForProtocols.entrySet()) {
-      ProxyDetails p = entry.getValue();
-      if (!p.sourceIsSystemProperties()) {
-        setSystemPropertyIfUnset(p.protocol + ".proxyHost", p.host());
-        setSystemPropertyIfUnset(p.protocol + ".proxyPort", String.valueOf(p.port()));
-        setSystemPropertyIfUnset(p.protocol + ".proxyUser", p.user());
-        setSystemPropertyIfUnset(p.protocol + ".proxyPassword", p.password());
-        setSystemPropertyIfUnset(
-            "http.nonProxyHosts",
-            p.nonProxyHosts()); // The HTTPS protocol handler will use the same nonProxyHosts
-        // property as the HTTP protocol. See here:
-        // https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/doc-files/net-properties.html#Proxies
-      }
-    }
-  }
-
-  private void setSystemPropertyIfUnset(String name, String value) {
-    if (StringUtils.isBlank(System.getProperty(name)) && StringUtils.isNotBlank(value)) {
-      System.setProperty(name, value);
-    }
-  }
 }
diff --git a/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyRoutePlanner.java b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyRoutePlanner.java
new file mode 100644
index 0000000000..2105f70203
--- /dev/null
+++ b/connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/proxy/ProxyRoutePlanner.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
+ * under one or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. Camunda licenses this file to you under the Apache License,
+ * Version 2.0; you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.camunda.connector.http.base.client.apache.proxy;
+
+import static io.camunda.connector.http.base.client.apache.proxy.ProxyHandler.CONNECTOR_HTTP_NON_PROXY_HOSTS_ENV_VAR;
+
+import java.util.Objects;
+import java.util.stream.Stream;
+import org.apache.hc.client5.http.impl.routing.DefaultProxyRoutePlanner;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class ProxyRoutePlanner extends DefaultProxyRoutePlanner {
+  private static final Logger LOG = LoggerFactory.getLogger(ProxyRoutePlanner.class.getName());
+
+  public ProxyRoutePlanner(HttpHost proxy) {
+    super(proxy);
+  }
+
+  @Override
+  protected HttpHost determineProxy(HttpHost target, HttpContext context) throws HttpException {
+    if (getNonProxyHosts()
+        .filter(Objects::nonNull)
+        .anyMatch(
+            nonProxyHosts ->
+                target.getHostName().matches(sanitizedNonProxyHostsRegex(nonProxyHosts)))) {
+      LOG.debug(
+          "Not using proxy for target host [{}] as it matched either system properties (http.nonProxyHosts) or environment variables ({})",
+          target.getHostName(),
+          CONNECTOR_HTTP_NON_PROXY_HOSTS_ENV_VAR);
+      return null;
+    }
+    var proxy = super.determineProxy(target, context);
+    LOG.debug("Using proxy for target host [{}] => [{}]", target.getHostName(), proxy);
+    return proxy;
+  }
+
+  private String sanitizedNonProxyHostsRegex(String nonProxyHosts) {
+    return nonProxyHosts != null
+        ? nonProxyHosts.replace(
+            "*", ".*") // This is required as the nonProxyHosts property uses * as a wildcard
+        : null;
+  }
+
+  private Stream<String> getNonProxyHosts() {
+    return Stream.of(
+        System.getProperty("http.nonProxyHosts"),
+        System.getenv(CONNECTOR_HTTP_NON_PROXY_HOSTS_ENV_VAR));
+  }
+}
diff --git a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/ApacheRequestFactoryTest.java b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/ApacheRequestFactoryTest.java
index 444fdc53dc..c09d88c707 100644
--- a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/ApacheRequestFactoryTest.java
+++ b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/ApacheRequestFactoryTest.java
@@ -20,9 +20,7 @@
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 import io.camunda.connector.api.json.ConnectorsObjectMapperSupplier;
 import io.camunda.connector.http.base.authentication.Base64Helper;
@@ -53,7 +51,7 @@
 import org.junit.jupiter.params.provider.EnumSource;
 import org.junit.jupiter.params.provider.MethodSource;
 import org.junit.jupiter.params.provider.ValueSource;
-import org.mockito.MockedStatic;
+import org.mockito.MockedConstruction;
 
 public class ApacheRequestFactoryTest {
 
@@ -116,12 +114,11 @@ public void shouldSetOAuthAuthentication_whenProvided() throws Exception {
       request.setAuthentication(
           new OAuthAuthentication(
               "url", "clientId", "secret", "audience", OAuthConstants.CREDENTIALS_BODY, "scopes"));
-      var mockedClient = mock(CustomApacheHttpClient.class);
-      try (MockedStatic<CustomApacheHttpClient> mockedClientSupplier =
-          mockStatic(CustomApacheHttpClient.class)) {
-        mockedClientSupplier.when(CustomApacheHttpClient::getDefault).thenReturn(mockedClient);
-        when(mockedClient.execute(any(HttpCommonRequest.class))).thenReturn(result);
-
+      try (MockedConstruction<CustomApacheHttpClient> mocked =
+          mockConstruction(
+              CustomApacheHttpClient.class,
+              (mock, context) ->
+                  when(mock.execute(any(HttpCommonRequest.class))).thenReturn(result))) {
         // when
         ClassicHttpRequest httpRequest = ApacheRequestFactory.get().createHttpRequest(request);
 
diff --git a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClientTest.java b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClientTest.java
index e1b22399f0..4809e1cbd6 100644
--- a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClientTest.java
+++ b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClientTest.java
@@ -51,7 +51,6 @@
 import java.util.Map;
 import java.util.stream.Stream;
 import org.apache.commons.text.StringEscapeUtils;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.HttpStatus;
@@ -70,7 +69,7 @@
 @WireMockTest
 public class CustomApacheHttpClientTest {
 
-  private final CustomApacheHttpClient customApacheHttpClient = CustomApacheHttpClient.getDefault();
+  private final CustomApacheHttpClient customApacheHttpClient = new CustomApacheHttpClient();
   private final ObjectMapper objectMapper = ConnectorsObjectMapperSupplier.getCopy();
   private final InMemoryDocumentStore store = InMemoryDocumentStore.INSTANCE;
 
@@ -96,9 +95,7 @@ public void shouldStoreDocument_whenStoreResponseEnabled(WireMockRuntimeInfo wmR
       request.setUrl(wmRuntimeInfo.getHttpBaseUrl() + "/download");
       HttpCommonResult result =
           customApacheHttpClient.execute(
-              request,
-              new ProxyHandler(),
-              new ExecutionEnvironment.SelfManaged(new TestDocumentFactory()));
+              request, new ExecutionEnvironment.SelfManaged(new TestDocumentFactory()));
       assertThat(result).isNotNull();
       assertThat(result.status()).isEqualTo(200);
       assertThat(result.headers().get(HttpHeaders.CONTENT_TYPE))
@@ -234,7 +231,7 @@ public static void setUp() {
       Testcontainers.exposeHostPorts(proxy.port());
       proxyContainer.withAccessToHost(true);
       proxyContainer.start();
-      proxiedApacheHttpClient = CustomApacheHttpClient.create(HttpClients.custom());
+      proxiedApacheHttpClient = new CustomApacheHttpClient();
     }
 
     private static void setAllSystemProperties() {
@@ -383,19 +380,21 @@ public void shouldThrowException_whenAuthenticationRequiredAndNotProvidedAsEnvVa
     }
 
     @Test
-    public void shouldUseSystemProperties_WhenEnvVarAndSystemPropertiesAreProvided(
+    public void shouldAlwaysUseEnvVars_WhenEnvVarAndSystemPropertiesAreProvided(
         WireMockRuntimeInfo wmRuntimeInfo) throws Exception {
       restoreSystemProperties(
           () -> {
             withEnvironmentVariables(
-                    "CONNECTOR_HTTPS_PROXY_HOST",
+                    "CONNECTOR_HTTP_PROXY_HOST",
                     "localhost",
-                    "CONNECTOR_HTTPS_PROXY_PORT",
+                    "CONNECTOR_HTTP_PROXY_PORT",
                     proxyContainer.getMappedPort(3128).toString(),
-                    "CONNECTOR_HTTPS_PROXY_USER",
-                    "my-user",
-                    "CONNECTOR_HTTPS_PROXY_PASSWORD",
-                    "demo")
+                    "CONNECTOR_HTTP_PROXY_USER",
+                    // Fake user/password to ensure this is used
+                    // The system properties contain the correct user/password
+                    "WRONG_USER",
+                    "CONNECTOR_HTTP_PROXY_PASSWORD",
+                    "WRONG_PASSWORD")
                 .execute(
                     () -> {
                       proxy.stubFor(get("/protected").willReturn(ok().withBody("Hello, world!")));
@@ -404,12 +403,11 @@ public void shouldUseSystemProperties_WhenEnvVarAndSystemPropertiesAreProvided(
                       HttpCommonRequest request = new HttpCommonRequest();
                       request.setMethod(HttpMethod.GET);
                       request.setUrl(getWireMockBaseUrlWithPath(wmRuntimeInfo, "/protected"));
-                      HttpCommonResult result = proxiedApacheHttpClient.execute(request);
-                      assertThat(result).isNotNull();
-                      assertThat(result.status()).isEqualTo(200);
-                      assertThat(result.body()).isEqualTo("Hello, world!");
-                      assertThat(result.headers().get("Via")).asString().contains("squid");
-                      proxy.verify(getRequestedFor(urlEqualTo("/protected")));
+                      ConnectorException e =
+                          assertThrows(
+                              ConnectorException.class,
+                              () -> proxiedApacheHttpClient.execute(request));
+                      assertThat(e.getMessage()).isEqualTo("Proxy Authentication Required");
                     });
           });
     }
diff --git a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/ProxyHandlerTest.java b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/ProxyHandlerTest.java
index bb1b1c4641..badb3f2f2f 100644
--- a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/ProxyHandlerTest.java
+++ b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/ProxyHandlerTest.java
@@ -22,6 +22,7 @@
 import static uk.org.webcompere.systemstubs.SystemStubs.withEnvironmentVariables;
 
 import io.camunda.connector.api.error.ConnectorInputException;
+import io.camunda.connector.http.base.client.apache.proxy.ProxyHandler;
 import org.apache.hc.client5.http.auth.CredentialsProvider;
 import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
 import org.junit.jupiter.api.AfterAll;
@@ -55,14 +56,6 @@ public static void unsetAllSystemProperties() {
     System.clearProperty("https.proxyPassword");
   }
 
-  @Test
-  public void shouldThrowException_whenProxyPortInvalidInSystemProperties() {
-    System.setProperty("http.proxyHost", "localhost");
-    System.setProperty("http.proxyPort", "invalid");
-
-    assertThrows(ConnectorInputException.class, () -> new ProxyHandler());
-  }
-
   @Test
   public void shouldThrowException_whenProxyPortInvalidInEnvVars() throws Exception {
     restoreSystemProperties(
@@ -96,70 +89,6 @@ public void shouldReturnDefaultCredentialsProvider_whenNotConfigured() {
     assertThat(provider).isInstanceOf(BasicCredentialsProvider.class);
   }
 
-  @Test
-  public void shouldSetSystemProperties_whenProxySettingsEnvVars() throws Exception {
-    restoreSystemProperties(
-        () -> {
-          withEnvironmentVariables(
-                  "CONNECTOR_HTTPS_PROXY_HOST",
-                  "localhost",
-                  "CONNECTOR_HTTPS_PROXY_PORT",
-                  "3128",
-                  "CONNECTOR_HTTPS_PROXY_USER",
-                  "my-user",
-                  "CONNECTOR_HTTPS_PROXY_PASSWORD",
-                  "demo",
-                  "CONNECTOR_HTTP_PROXY_NON_PROXY_HOSTS",
-                  "www.test.de")
-              .execute(
-                  () -> {
-                    assertThat(System.getProperty("https.proxyHost")).isNull();
-                    assertThat(System.getProperty("https.proxyPort")).isNull();
-                    assertThat(System.getProperty("https.proxyUser")).isNull();
-                    assertThat(System.getProperty("https.proxyPassword")).isNull();
-                    assertThat(System.getProperty("http.nonProxyHosts")).isNull();
-
-                    new ProxyHandler();
-
-                    assertThat(System.getProperty("https.proxyHost")).isEqualTo("localhost");
-                    assertThat(System.getProperty("https.proxyPort")).isEqualTo("3128");
-                    assertThat(System.getProperty("https.proxyUser")).isEqualTo("my-user");
-                    assertThat(System.getProperty("https.proxyPassword")).isEqualTo("demo");
-                    assertThat(System.getProperty("http.nonProxyHosts")).isEqualTo("www.test.de");
-                  });
-        });
-  }
-
-  @Test
-  public void shouldNotSetAuthSystemProperties_whenProxySettingsEnvVarsNoAuth() throws Exception {
-    restoreSystemProperties(
-        () -> {
-          withEnvironmentVariables(
-                  "CONNECTOR_HTTP_PROXY_HOST",
-                  "localhost",
-                  "CONNECTOR_HTTP_PROXY_PORT",
-                  "3128",
-                  "CONNECTOR_HTTP_PROXY_NON_PROXY_HOSTS",
-                  "www.test.de")
-              .execute(
-                  () -> {
-                    assertThat(System.getProperty("http.proxyHost")).isNull();
-                    assertThat(System.getProperty("http.proxyPort")).isNull();
-                    assertThat(System.getProperty("http.proxyUser")).isNull();
-                    assertThat(System.getProperty("http.proxyPassword")).isNull();
-                    assertThat(System.getProperty("http.nonProxyHosts")).isNull();
-
-                    new ProxyHandler();
-
-                    assertThat(System.getProperty("http.proxyHost")).isEqualTo("localhost");
-                    assertThat(System.getProperty("http.proxyPort")).isEqualTo("3128");
-                    assertThat(System.getProperty("http.proxyUser")).isEqualTo(null);
-                    assertThat(System.getProperty("http.proxyPassword")).isEqualTo(null);
-                    assertThat(System.getProperty("http.nonProxyHosts")).isEqualTo("www.test.de");
-                  });
-        });
-  }
-
   @Test
   public void shouldNotOverwriteSystemProperties_whenProxySettingsEnvVarsAndSystemProperties()
       throws Exception {
diff --git a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/proxy/ProxyRoutePlannerTest.java b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/proxy/ProxyRoutePlannerTest.java
new file mode 100644
index 0000000000..981e134207
--- /dev/null
+++ b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/client/apache/proxy/ProxyRoutePlannerTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH
+ * under one or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. Camunda licenses this file to you under the Apache License,
+ * Version 2.0; you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package io.camunda.connector.http.base.client.apache.proxy;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.stream.Stream;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpHost;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+public class ProxyRoutePlannerTest {
+
+  HttpHost proxy = new HttpHost("theproxyhost", 4750);
+
+  private static Stream<Arguments> provideNonProxyHostTestData() {
+    return Stream.of(
+        // nonProxyHosts, requestUrl, skipProxy
+        Arguments.of("example.de", "http://example.de", true),
+        Arguments.of("example.de", "http://www.example.de", false),
+        Arguments.of("www.example.de", "http://www.example.de", true),
+        Arguments.of("example.de|www.camunda.de", "http://www.camunda.io", false),
+        Arguments.of("example.de", "http://www.example.de", false),
+        Arguments.of("example.de", "http://api.example.de", false),
+        Arguments.of("example.de", "http://another.example.de", false),
+        Arguments.of("example.de", "http://example.com", false),
+        Arguments.of("example.de|camunda.de", "http://www.example.de", false),
+        Arguments.of("example.de|camunda.de", "http://www.camunda.de", false),
+        Arguments.of("example.de|camunda.de", "http://www.google.de", false),
+        Arguments.of("*.example.de", "http://api.example.de", true),
+        Arguments.of("*.example.de", "http://www.example.de", true),
+        Arguments.of("*.example.de", "http://example.de", false),
+        Arguments.of("*.example.de", "http://example.com", false),
+        Arguments.of("*.example.de|*.camunda.io", "http://sub.example.de", true),
+        Arguments.of("*.example.de|*.camunda.io", "http://api.camunda.io", true),
+        Arguments.of("*.example.de|*.camunda.io", "http://www.google.com", false));
+  }
+
+  @AfterEach
+  public void clearSystemProperties() {
+    System.clearProperty("http.nonProxyHosts");
+  }
+
+  @ParameterizedTest
+  @MethodSource("provideNonProxyHostTestData")
+  public void shouldUseProxy_whenRequestUrlMatches(
+      String nonProxyHosts, String requestUrl, Boolean skipProxy)
+      throws HttpException, URISyntaxException {
+    // given
+    HttpHost target = HttpHost.create(requestUrl);
+    System.setProperty("http.nonProxyHosts", nonProxyHosts);
+    ProxyRoutePlanner proxyRoutePlanner = new ProxyRoutePlanner(proxy);
+    // Tests with the Java default proxy selector
+    System.setProperty("http.proxyHost", "theproxyhost");
+    System.setProperty("http.proxyPort", "4750");
+    var javaDefaultProxies = ProxySelector.getDefault().select(URI.create(requestUrl));
+
+    // when
+    var route = proxyRoutePlanner.determineProxy(target, null);
+
+    // then
+    assertThat(javaDefaultProxies.size()).isEqualTo(1);
+    var javaProxy = javaDefaultProxies.getFirst();
+
+    if (skipProxy) {
+      assertThat(route).isNull();
+      assertThat(javaProxy.type()).isEqualTo(Proxy.Type.DIRECT);
+    } else {
+      assertThat(route).isNotNull();
+      assertThat(route).isEqualTo(proxy);
+      assertThat(javaProxy.type()).isEqualTo(Proxy.Type.HTTP);
+    }
+  }
+}
diff --git a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/cloudfunction/CloudFunctionServiceTest.java b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/cloudfunction/CloudFunctionServiceTest.java
index b125d9df0a..38a7e0efcd 100644
--- a/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/cloudfunction/CloudFunctionServiceTest.java
+++ b/connectors/http/http-base/src/test/java/io/camunda/connector/http/base/cloudfunction/CloudFunctionServiceTest.java
@@ -64,11 +64,7 @@ public void shouldConvertToCloudFunctionRequestWithDocumentContent_whenBodyConta
     request.setUrl("theUrl");
     request.setMethod(HttpMethod.POST);
     request.setHeaders(
-        Map.of(
-            "header",
-            "value",
-            "Content-Type",
-            ContentType.APPLICATION_FORM_URLENCODED.getMimeType()));
+        Map.of("header", "value", "Content-Type", ContentType.MULTIPART_FORM_DATA.getMimeType()));
     request.setBody(Map.of("bodyKey", "bodyValue", "myDocument", document));
     request.setConnectionTimeoutInSeconds(50);
     request.setReadTimeoutInSeconds(60);
@@ -89,9 +85,7 @@ public void shouldConvertToCloudFunctionRequestWithDocumentContent_whenBodyConta
     assertThat(body).containsEntry("url", "theUrl");
     assertThat(body).containsEntry("method", "POST");
     assertThat(body)
-        .containsEntry(
-            "headers",
-            Map.of("header", "value", "Content-Type", "application/x-www-form-urlencoded"));
+        .containsEntry("headers", Map.of("header", "value", "Content-Type", "multipart/form-data"));
     assertThat(body)
         .containsEntry(
             "body",
diff --git a/connectors/http/rest/src/test/java/io/camunda/connector/http/rest/HttpServiceTest.java b/connectors/http/rest/src/test/java/io/camunda/connector/http/rest/HttpServiceTest.java
index 9c696c6729..d70dafc8d1 100644
--- a/connectors/http/rest/src/test/java/io/camunda/connector/http/rest/HttpServiceTest.java
+++ b/connectors/http/rest/src/test/java/io/camunda/connector/http/rest/HttpServiceTest.java
@@ -19,14 +19,14 @@
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.when;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.*;
 
 import io.camunda.connector.http.base.authentication.OAuthConstants;
 import io.camunda.connector.http.base.authentication.OAuthService;
 import io.camunda.connector.http.base.client.apache.ApacheRequestFactory;
 import io.camunda.connector.http.base.client.apache.CustomApacheHttpClient;
+import io.camunda.connector.http.base.model.HttpCommonRequest;
 import io.camunda.connector.http.base.model.HttpCommonResult;
 import io.camunda.connector.http.base.model.auth.OAuthAuthentication;
 import io.camunda.connector.http.rest.model.HttpJsonRequest;
@@ -36,7 +36,7 @@
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
-import org.mockito.MockedStatic;
+import org.mockito.MockedConstruction;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 @ExtendWith(MockitoExtension.class)
@@ -68,10 +68,11 @@ void checkIfOAuthBearerTokenIsAddedOnTheRequestHeader(final String input) throws
     HttpCommonResult oauthResult =
         new HttpCommonResult(200, null, Map.of(OAuthConstants.ACCESS_TOKEN, ACCESS_TOKEN));
     var mockedClient = mock(CustomApacheHttpClient.class);
-    try (MockedStatic<CustomApacheHttpClient> mockedClientSupplier =
-        mockStatic(CustomApacheHttpClient.class)) {
-      mockedClientSupplier.when(CustomApacheHttpClient::getDefault).thenReturn(mockedClient);
-      when(mockedClient.execute(oauthRequest)).thenReturn(oauthResult);
+    try (MockedConstruction<CustomApacheHttpClient> mocked =
+        mockConstruction(
+            CustomApacheHttpClient.class,
+            (mock, ctx) ->
+                when(mock.execute(any(HttpCommonRequest.class))).thenReturn(oauthResult))) {
       // when
       String bearerToken = oAuthService.extractTokenFromResponse(oauthResult.body());
       var apacheRequest = ApacheRequestFactory.get().createHttpRequest(httpJsonRequest);


Reviews:
user: login: ztefanie
body: Feel free to re-add these tests for matching non proxy hosts properly:
https://github.com/camunda/connectors/pull/3894/files#diff-0d190eecc67fcce2f29db41ca7e5fbe821ff94693ab1ab045393c42e148abb91R91-R126
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-17T08:26:29Z
user: login: johnBgood
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-17T08:35:34Z
user: login: ztefanie
body: Thanks for this PR and fixing the proxy issues :green_heart: 

I reviewed it, the code looks good :+1: 

I tested this, but the nonProxyHost setting gets ignored for me
These are my settings `CONNECTOR_HTTP_PROXY_HOST=localhost;CONNECTOR_HTTP_PROXY_PORT=3128;CONNECTOR_HTTP_PROXY_NON_PROXY_HOSTS=www.gmx.de;CONNECTOR_HTTP_PROXY_USER=my-user;CONNECTOR_HTTP_PROXY_PASSWORD=demo` but requests to http://www.gmx.de still get proxied.

Please also do not forget to adjust the documentation, plus I think we need to mention that we introduced breaking changes, because now a setup with proxy settings with system variables will no longer work, right?

Please also do not forget to backport this to 8.7


state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-19T06:23:38Z
user: login: sbuettner
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T08:01:54Z
user: login: sbuettner
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T08:03:08Z
user: login: johnBgood
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-19T08:35:14Z
user: login: sbuettner
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T08:38:01Z
user: login: johnBgood
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-19T08:58:22Z
user: login: johnBgood
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-19T08:59:44Z
user: login: ztefanie
body: Tested again, works as expected :rocket: 
state: DISMISSED
author_association: COLLABORATOR
submitted_at: 2025-03-19T09:05:26Z
user: login: sbuettner
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-19T09:15:33Z
user: login: Szik
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-21T10:44:48Z
user: login: ztefanie
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-21T10:51:45Z

Review comments:
diff_hunk: @@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
user: login: ztefanie
body: Could we create two clients (one for using a proxy and one for non-proxy) and then select the correct one for a request instead of creating new ones for every request?
created_at: 2025-03-17T08:26:26Z
author_association: COLLABORATOR
diff_hunk: @@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
user: login: johnBgood
body: Sadly not really, the timeouts are configured when creating the client and they are based on the request values :/ 
```java
  private RequestConfig getRequestTimeoutConfig(TimeoutConfiguration timeoutConfiguration) {
    return RequestConfig.custom()
        .setConnectionRequestTimeout(
            Timeout.ofSeconds(timeoutConfiguration.connectionTimeoutInSeconds()))
        .setResponseTimeout(Timeout.ofSeconds(timeoutConfiguration.readTimeoutInSeconds()))
        .build();
  }
```
If you find another way, I'll be more than happy though
created_at: 2025-03-17T08:35:34Z
author_association: COLLABORATOR
diff_hunk: @@ -20,103 +20,49 @@
 import io.camunda.connector.http.base.ExecutionEnvironment;
 import io.camunda.connector.http.base.client.HttpClient;
 import io.camunda.connector.http.base.client.HttpStatusHelper;
+import io.camunda.connector.http.base.client.apache.proxy.ProxyAwareHttpClient;
 import io.camunda.connector.http.base.exception.ConnectorExceptionMapper;
 import io.camunda.connector.http.base.model.HttpCommonRequest;
 import io.camunda.connector.http.base.model.HttpCommonResult;
 import java.io.IOException;
 import javax.annotation.Nullable;
 import org.apache.hc.client5.http.ClientProtocolException;
-import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
 import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.io.SocketConfig;
-import org.apache.hc.core5.util.Timeout;
 
 public class CustomApacheHttpClient implements HttpClient {
 
-  public static final CustomApacheHttpClient DEFAULT =
-      new CustomApacheHttpClient(createHttpClientBuilder());
-
-  private final HttpClientBuilder httpClientBuilder;
-
-  private CustomApacheHttpClient(HttpClientBuilder httpClientBuilder) {
-    this.httpClientBuilder = httpClientBuilder;
-  }
-
-  /**
-   * Returns the default instance of {@link CustomApacheHttpClient}. The default instance is
-   * configured with a {@link HttpClientBuilder} that has a {@link
-   * PoolingHttpClientConnectionManager} with a maximum of {@link Integer#MAX_VALUE} connections.
-   *
-   * @return the default instance of {@link CustomApacheHttpClient}
-   */
-  public static CustomApacheHttpClient getDefault() {
-    return DEFAULT;
-  }
-
-  /**
-   * Creates a new instance of {@link CustomApacheHttpClient} with the given {@link
-   * HttpClientBuilder}. Use this method if you want to customize the {@link HttpClientBuilder}. See
-   * {@link #getDefault()} for the default instance.
-   *
-   * @param httpClientBuilder the {@link HttpClientBuilder} to use
-   * @return a new instance of {@link CustomApacheHttpClient}
-   * @see HttpClients#custom()
-   */
-  public static CustomApacheHttpClient create(HttpClientBuilder httpClientBuilder) {
-    return new CustomApacheHttpClient(httpClientBuilder);
-  }
-
-  private static HttpClientBuilder createHttpClientBuilder() {
-    return HttpClients.custom()
-        .setConnectionManager(createConnectionManager())
-        .disableRedirectHandling();
-  }
-
-  private static PoolingHttpClientConnectionManager createConnectionManager() {
-    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
-    connectionManager.setMaxTotal(Integer.MAX_VALUE);
-    connectionManager.setDefaultMaxPerRoute(Integer.MAX_VALUE);
-
-    // Socket config
-    connectionManager.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).build());
-
-    return connectionManager;
-  }
-
   /**
    * Converts the given {@link HttpCommonRequest} to an Apache {@link
    * org.apache.hc.core5.http.ClassicHttpRequest} and executes it.
    *
+   * <p>This method is thread-safe.
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClient.java
user: login: ztefanie
body: is this `<p>` tag ever closed?
created_at: 2025-03-19T06:09:07Z
author_association: COLLABORATOR
diff_hunk: @@ -20,103 +20,49 @@
 import io.camunda.connector.http.base.ExecutionEnvironment;
 import io.camunda.connector.http.base.client.HttpClient;
 import io.camunda.connector.http.base.client.HttpStatusHelper;
+import io.camunda.connector.http.base.client.apache.proxy.ProxyAwareHttpClient;
 import io.camunda.connector.http.base.exception.ConnectorExceptionMapper;
 import io.camunda.connector.http.base.model.HttpCommonRequest;
 import io.camunda.connector.http.base.model.HttpCommonResult;
 import java.io.IOException;
 import javax.annotation.Nullable;
 import org.apache.hc.client5.http.ClientProtocolException;
-import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
 import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.io.SocketConfig;
-import org.apache.hc.core5.util.Timeout;
 
 public class CustomApacheHttpClient implements HttpClient {
 
-  public static final CustomApacheHttpClient DEFAULT =
-      new CustomApacheHttpClient(createHttpClientBuilder());
-
-  private final HttpClientBuilder httpClientBuilder;
-
-  private CustomApacheHttpClient(HttpClientBuilder httpClientBuilder) {
-    this.httpClientBuilder = httpClientBuilder;
-  }
-
-  /**
-   * Returns the default instance of {@link CustomApacheHttpClient}. The default instance is
-   * configured with a {@link HttpClientBuilder} that has a {@link
-   * PoolingHttpClientConnectionManager} with a maximum of {@link Integer#MAX_VALUE} connections.
-   *
-   * @return the default instance of {@link CustomApacheHttpClient}
-   */
-  public static CustomApacheHttpClient getDefault() {
-    return DEFAULT;
-  }
-
-  /**
-   * Creates a new instance of {@link CustomApacheHttpClient} with the given {@link
-   * HttpClientBuilder}. Use this method if you want to customize the {@link HttpClientBuilder}. See
-   * {@link #getDefault()} for the default instance.
-   *
-   * @param httpClientBuilder the {@link HttpClientBuilder} to use
-   * @return a new instance of {@link CustomApacheHttpClient}
-   * @see HttpClients#custom()
-   */
-  public static CustomApacheHttpClient create(HttpClientBuilder httpClientBuilder) {
-    return new CustomApacheHttpClient(httpClientBuilder);
-  }
-
-  private static HttpClientBuilder createHttpClientBuilder() {
-    return HttpClients.custom()
-        .setConnectionManager(createConnectionManager())
-        .disableRedirectHandling();
-  }
-
-  private static PoolingHttpClientConnectionManager createConnectionManager() {
-    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
-    connectionManager.setMaxTotal(Integer.MAX_VALUE);
-    connectionManager.setDefaultMaxPerRoute(Integer.MAX_VALUE);
-
-    // Socket config
-    connectionManager.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).build());
-
-    return connectionManager;
-  }
-
   /**
    * Converts the given {@link HttpCommonRequest} to an Apache {@link
    * org.apache.hc.core5.http.ClassicHttpRequest} and executes it.
    *
+   * <p>This method is thread-safe.
+   *
    * @param request the request to execute
    * @param executionEnvironment the {@link ExecutionEnvironment} we are in
    * @return the {@link HttpCommonResult}
    */
   @Override
   public HttpCommonResult execute(
-      HttpCommonRequest request,
-      ProxyHandler proxyHandler,
-      @Nullable ExecutionEnvironment executionEnvironment) {
+      HttpCommonRequest request, @Nullable ExecutionEnvironment executionEnvironment) {
     try {
       var apacheRequest = ApacheRequestFactory.get().createHttpRequest(request);
-      var result =
-          httpClientBuilder
-              .setDefaultRequestConfig(getRequestConfig(request))
-              .setDefaultCredentialsProvider(
-                  proxyHandler.getCredentialsProvider(apacheRequest.getScheme()))
-              .useSystemProperties() // Will fallback on system properties for unset values,
-              // e.g. http.keepAlive, http.agent
-              .build()
-              .execute(
-                  apacheRequest,
-                  new HttpCommonResultResponseHandler(
-                      executionEnvironment, request.isStoreResponse()));
-      if (HttpStatusHelper.isError(result.status())) {
-        throw ConnectorExceptionMapper.from(result);
+      var host = apacheRequest.getAuthority().getHostName();
+      var scheme = apacheRequest.getScheme();
+      try (var client =
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClient.java
user: login: ztefanie
body: Nice :rocket: 
created_at: 2025-03-19T06:09:47Z
author_association: COLLABORATOR
diff_hunk: @@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
user: login: sbuettner
body: @johnBgood Curious, can this be overriden on a request level like this:
```
final RequestConfig params = RequestConfig.custom().setConnectTimeout(3000).setSocketTimeout(3000).build();
httpGet.setConfig(params);
```
?
created_at: 2025-03-19T08:01:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
user: login: sbuettner
body: It seems like the proxy config can also be passed via the request config: https://hc.apache.org/httpcomponents-client-4.5.x/current/httpclient/apidocs/org/apache/http/client/config/RequestConfig.html
created_at: 2025-03-19T08:03:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
user: login: johnBgood
body: > @johnBgood Curious, can this be overriden on a request level like this:

Yes, but this is not how we create requests (we don't use HttpGet, HttpPost etc). This is because it induced other limitations or less generic code I don't remember exactly
created_at: 2025-03-19T08:35:14Z
author_association: COLLABORATOR
diff_hunk: @@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
user: login: sbuettner
body: Ok, should we maybe tackle it in a separate PR? As our Connectors are essentially request based we could potentially benefit from a more request focused configuration. 
created_at: 2025-03-19T08:38:01Z
author_association: CONTRIBUTOR
diff_hunk: @@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
user: login: johnBgood
body: Yes we can have a look in another PR, I took a look and it's probably not that many changes 
created_at: 2025-03-19T08:58:22Z
author_association: COLLABORATOR
diff_hunk: @@ -20,103 +20,49 @@
 import io.camunda.connector.http.base.ExecutionEnvironment;
 import io.camunda.connector.http.base.client.HttpClient;
 import io.camunda.connector.http.base.client.HttpStatusHelper;
+import io.camunda.connector.http.base.client.apache.proxy.ProxyAwareHttpClient;
 import io.camunda.connector.http.base.exception.ConnectorExceptionMapper;
 import io.camunda.connector.http.base.model.HttpCommonRequest;
 import io.camunda.connector.http.base.model.HttpCommonResult;
 import java.io.IOException;
 import javax.annotation.Nullable;
 import org.apache.hc.client5.http.ClientProtocolException;
-import org.apache.hc.client5.http.config.RequestConfig;
-import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
-import org.apache.hc.client5.http.impl.classic.HttpClients;
-import org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager;
 import org.apache.hc.core5.http.HttpStatus;
-import org.apache.hc.core5.http.io.SocketConfig;
-import org.apache.hc.core5.util.Timeout;
 
 public class CustomApacheHttpClient implements HttpClient {
 
-  public static final CustomApacheHttpClient DEFAULT =
-      new CustomApacheHttpClient(createHttpClientBuilder());
-
-  private final HttpClientBuilder httpClientBuilder;
-
-  private CustomApacheHttpClient(HttpClientBuilder httpClientBuilder) {
-    this.httpClientBuilder = httpClientBuilder;
-  }
-
-  /**
-   * Returns the default instance of {@link CustomApacheHttpClient}. The default instance is
-   * configured with a {@link HttpClientBuilder} that has a {@link
-   * PoolingHttpClientConnectionManager} with a maximum of {@link Integer#MAX_VALUE} connections.
-   *
-   * @return the default instance of {@link CustomApacheHttpClient}
-   */
-  public static CustomApacheHttpClient getDefault() {
-    return DEFAULT;
-  }
-
-  /**
-   * Creates a new instance of {@link CustomApacheHttpClient} with the given {@link
-   * HttpClientBuilder}. Use this method if you want to customize the {@link HttpClientBuilder}. See
-   * {@link #getDefault()} for the default instance.
-   *
-   * @param httpClientBuilder the {@link HttpClientBuilder} to use
-   * @return a new instance of {@link CustomApacheHttpClient}
-   * @see HttpClients#custom()
-   */
-  public static CustomApacheHttpClient create(HttpClientBuilder httpClientBuilder) {
-    return new CustomApacheHttpClient(httpClientBuilder);
-  }
-
-  private static HttpClientBuilder createHttpClientBuilder() {
-    return HttpClients.custom()
-        .setConnectionManager(createConnectionManager())
-        .disableRedirectHandling();
-  }
-
-  private static PoolingHttpClientConnectionManager createConnectionManager() {
-    PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
-    connectionManager.setMaxTotal(Integer.MAX_VALUE);
-    connectionManager.setDefaultMaxPerRoute(Integer.MAX_VALUE);
-
-    // Socket config
-    connectionManager.setDefaultSocketConfig(SocketConfig.custom().setSoKeepAlive(true).build());
-
-    return connectionManager;
-  }
-
   /**
    * Converts the given {@link HttpCommonRequest} to an Apache {@link
    * org.apache.hc.core5.http.ClassicHttpRequest} and executes it.
    *
+   * <p>This method is thread-safe.
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/CustomApacheHttpClient.java
user: login: johnBgood
body: no need to, it's usually not closed in Javadoc
created_at: 2025-03-19T08:59:43Z
author_association: COLLABORATOR
diff_hunk: @@ -63,7 +63,7 @@ public void build(ClassicRequestBuilder builder, HttpCommonRequest request) {
 
   String fetchOAuthToken(OAuthAuthentication authentication) {
     HttpCommonRequest oAuthRequest = oAuthService.createOAuthRequestFrom(authentication);
-    HttpCommonResult response = CustomApacheHttpClient.getDefault().execute(oAuthRequest);
+    HttpCommonResult response = new CustomApacheHttpClient().execute(oAuthRequest);
path: connectors/http/http-base/src/main/java/io/camunda/connector/http/base/client/apache/builder/parts/ApacheRequestAuthenticationBuilder.java
user: login: sbuettner
body: Created a follow up issue here: https://github.com/camunda/connectors/issues/4371
created_at: 2025-03-19T09:15:33Z
author_association: CONTRIBUTOR

PR comments:
user: login: johnBgood
created_at: 2025-03-19T09:13:34Z
body: # QA information
## Test Environment
- Test required in (you can check both):
  - [x] SaaS
  - [x] SM
- [ ] Create a new test for this feature in our [Pre Release Tests](https://modeler.ultrawombat.com/projects/017779a8-9459-4feb-8b47-eeef8b2717ef--connectors-pre-release-test) for @Szik to be able to test

## Test Scope
- When using env vars only: test that we can now configure a Proxy for **REST-like connectors only** (REST, Github, GraphQL,...), other connectors or the runtime should not be affected. For instance, we shouldn't have to exclude `zeebe-service` from the proxied hosts now.
- When using env vars + the `http.nonProxyHosts` System property:  ensure that the REST Connector uses it.
- When using env vars + system properties: env vars should be applied to the REST Connector (only the `http.nonProxyHosts` is also used).
- When using only system properties: the whole application is affected by these settings.

## Test Data
- See the env variables to be used for the configuration in this ticket's description.
author_association: COLLABORATOR
user: login: Szik
created_at: 2025-03-21T10:49:05Z
body: /backport
author_association: CONTRIBUTOR
user: login: team-connectors-int-automation[bot]
created_at: 2025-03-21T10:49:22Z
body: Only merged pull requests can be backported.
author_association: CONTRIBUTOR
user: login: team-connectors-int-automation[bot]
created_at: 2025-03-21T10:55:03Z
body: Backport failed for `release/8.7`, because it was unable to cherry-pick the commit(s).

Please cherry-pick the changes locally and resolve any conflicts.
```bash
git fetch origin release/8.7
git worktree add -d .worktree/backport-4320-to-release/8.7 origin/release/8.7
cd .worktree/backport-4320-to-release/8.7
git switch --create backport-4320-to-release/8.7
git cherry-pick -x 54b1889022ce8a53d3bfa164630bf895f1907e62
```
author_association: CONTRIBUTOR