Move Proxy parsing logic from connectors to a common class

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java
index 81e94b4..60c5e83 100644
--- a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java
+++ b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java
@@ -29,6 +29,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 import java.util.concurrent.atomic.AtomicLong;
@@ -51,6 +52,7 @@
 import org.glassfish.jersey.client.ClientRequest;
 import org.glassfish.jersey.client.ClientResponse;
 import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.client.innate.ClientProxy;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
@@ -279,28 +281,20 @@
             clientBuilder.setRetryHandler((HttpRequestRetryHandler) retryHandler);
         }
 
-        final Object proxyUri;
-        proxyUri = config.getProperty(ClientProperties.PROXY_URI);
-        if (proxyUri != null) {
-            final URI u = getProxyUri(proxyUri);
-            final HttpHost proxy = new HttpHost(u.getHost(), u.getPort(), u.getScheme());
-            final String userName;
-            userName = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_USERNAME, String.class);
-            if (userName != null) {
-                final String password;
-                password = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_PASSWORD, String.class);
-
-                if (password != null) {
-                    final CredentialsProvider credsProvider = new BasicCredentialsProvider();
-                    credsProvider.setCredentials(
-                            new AuthScope(u.getHost(), u.getPort()),
-                            new UsernamePasswordCredentials(userName, password)
-                    );
-                    clientBuilder.setDefaultCredentialsProvider(credsProvider);
-                }
+        final Optional<ClientProxy> proxy = ClientProxy.proxyFromConfiguration(config);
+        proxy.ifPresent(clientProxy -> {
+            final URI u = clientProxy.uri();
+            final HttpHost proxyHost = new HttpHost(u.getHost(), u.getPort(), u.getScheme());
+            if (clientProxy.userName() != null && clientProxy.password() != null) {
+                final CredentialsProvider credsProvider = new BasicCredentialsProvider();
+                credsProvider.setCredentials(
+                        new AuthScope(u.getHost(), u.getPort()),
+                        new UsernamePasswordCredentials(clientProxy.userName(), clientProxy.password())
+                );
+                clientBuilder.setDefaultCredentialsProvider(credsProvider);
             }
-            clientBuilder.setProxy(proxy);
-        }
+            clientBuilder.setProxy(proxyHost);
+        });
 
         final Boolean preemptiveBasicAuthProperty = (Boolean) config.getProperties()
                 .get(ApacheClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION);
@@ -452,16 +446,6 @@
         return cookieStore;
     }
 
-    private static URI getProxyUri(final Object proxy) {
-        if (proxy instanceof URI) {
-            return (URI) proxy;
-        } else if (proxy instanceof String) {
-            return URI.create((String) proxy);
-        } else {
-            throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(ClientProperties.PROXY_URI));
-        }
-    }
-
     @Override
     public ClientResponse apply(final ClientRequest clientRequest) throws ProcessingException {
         final HttpUriRequest request = getUriHttpRequest(clientRequest);
diff --git a/connectors/apache-connector/src/main/resources/org/glassfish/jersey/apache/connector/localization.properties b/connectors/apache-connector/src/main/resources/org/glassfish/jersey/apache/connector/localization.properties
index f0cc55b..3b06461 100644
--- a/connectors/apache-connector/src/main/resources/org/glassfish/jersey/apache/connector/localization.properties
+++ b/connectors/apache-connector/src/main/resources/org/glassfish/jersey/apache/connector/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2020 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License v. 2.0, which is available at
@@ -19,7 +19,5 @@
 failed.to.stop.client=Failed to stop the client.
 # {0} - property name, e.g. jersey.config.client.httpclient.connectionManager; {1}, {2} - full class name
 ignoring.value.of.property=Ignoring value of property "{0}" ("{1}") - not instance of "{2}".
-# {0} - property name - jersey.config.client.httpclient.proxyUri
-wrong.proxy.uri.type=The proxy URI ("{0}") property MUST be an instance of String or URI.
 invalid.configurable.component.type=The supplied component "{0}" is not assignable from JerseyClient or JerseyWebTarget.
 expected.connector.provider.not.used=The supplied component is not configured to use a ApacheConnectorProvider.
diff --git a/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java b/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
index 9d10105..da7ccdd 100644
--- a/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
+++ b/connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java
@@ -30,6 +30,7 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 import java.util.logging.Level;
@@ -93,6 +94,7 @@
 import org.glassfish.jersey.client.ClientRequest;
 import org.glassfish.jersey.client.ClientResponse;
 import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.client.innate.ClientProxy;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
@@ -280,28 +282,20 @@
             clientBuilder.setRetryStrategy((HttpRequestRetryStrategy) retryHandler);
         }
 
-        final Object proxyUri;
-        proxyUri = config.getProperty(ClientProperties.PROXY_URI);
-        if (proxyUri != null) {
-            final URI u = getProxyUri(proxyUri);
-            final HttpHost proxy = new HttpHost(u.getScheme(), u.getHost(), u.getPort());
-            final String userName;
-            userName = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_USERNAME, String.class);
-            if (userName != null) {
-                final String password;
-                password = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_PASSWORD, String.class);
-
-                if (password != null) {
-                    final CredentialsStore credsProvider = new BasicCredentialsProvider();
-                    credsProvider.setCredentials(
-                            new AuthScope(u.getHost(), u.getPort()),
-                            new UsernamePasswordCredentials(userName, password.toCharArray())
-                    );
-                    clientBuilder.setDefaultCredentialsProvider(credsProvider);
-                }
+        final Optional<ClientProxy> proxy = ClientProxy.proxyFromConfiguration(config);
+        proxy.ifPresent(clientProxy -> {
+            final URI u = clientProxy.uri();
+            final HttpHost proxyHost = new HttpHost(u.getScheme(), u.getHost(), u.getPort());
+            if (clientProxy.userName() != null && clientProxy.password() != null) {
+                final CredentialsStore credsProvider = new BasicCredentialsProvider();
+                credsProvider.setCredentials(
+                        new AuthScope(u.getHost(), u.getPort()),
+                        new UsernamePasswordCredentials(clientProxy.userName(), clientProxy.password().toCharArray())
+                );
+                clientBuilder.setDefaultCredentialsProvider(credsProvider);
             }
-            clientBuilder.setProxy(proxy);
-        }
+            clientBuilder.setProxy(proxyHost);
+        });
 
         final Boolean preemptiveBasicAuthProperty = (Boolean) config.getProperties()
                 .get(Apache5ClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION);
@@ -456,16 +450,6 @@
         return cookieStore;
     }
 
-    private static URI getProxyUri(final Object proxy) {
-        if (proxy instanceof URI) {
-            return (URI) proxy;
-        } else if (proxy instanceof String) {
-            return URI.create((String) proxy);
-        } else {
-            throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(ClientProperties.PROXY_URI));
-        }
-    }
-
     @Override
     public ClientResponse apply(final ClientRequest clientRequest) throws ProcessingException {
         final HttpUriRequest request = getUriHttpRequest(clientRequest);
diff --git a/connectors/apache5-connector/src/main/resources/org/glassfish/jersey/apache5/connector/localization.properties b/connectors/apache5-connector/src/main/resources/org/glassfish/jersey/apache5/connector/localization.properties
index 16dbc30..1f6e4a6 100644
--- a/connectors/apache5-connector/src/main/resources/org/glassfish/jersey/apache5/connector/localization.properties
+++ b/connectors/apache5-connector/src/main/resources/org/glassfish/jersey/apache5/connector/localization.properties
@@ -19,7 +19,5 @@
 failed.to.stop.client=Failed to stop the client.
 # {0} - property name, e.g. jersey.config.client.httpclient.connectionManager; {1}, {2} - full class name
 ignoring.value.of.property=Ignoring value of property "{0}" ("{1}") - not instance of "{2}".
-# {0} - property name - jersey.config.client.httpclient.proxyUri
-wrong.proxy.uri.type=The proxy URI ("{0}") property MUST be an instance of String or URI.
 invalid.configurable.component.type=The supplied component "{0}" is not assignable from JerseyClient or JerseyWebTarget.
 expected.connector.provider.not.used=The supplied component is not configured to use a ApacheConnectorProvider.
diff --git a/connectors/grizzly-connector/src/main/java/org/glassfish/jersey/grizzly/connector/GrizzlyConnector.java b/connectors/grizzly-connector/src/main/java/org/glassfish/jersey/grizzly/connector/GrizzlyConnector.java
index ced8f43..14a3e30 100644
--- a/connectors/grizzly-connector/src/main/java/org/glassfish/jersey/grizzly/connector/GrizzlyConnector.java
+++ b/connectors/grizzly-connector/src/main/java/org/glassfish/jersey/grizzly/connector/GrizzlyConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2010, 2019 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -22,6 +22,7 @@
 import java.net.URI;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Properties;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -39,6 +40,7 @@
 import org.glassfish.jersey.client.ClientRequest;
 import org.glassfish.jersey.client.ClientResponse;
 import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.client.innate.ClientProxy;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
 import org.glassfish.jersey.internal.Version;
@@ -83,7 +85,7 @@
     GrizzlyConnector(final Client client,
                      final Configuration config,
                      final GrizzlyConnectorProvider.AsyncClientCustomizer asyncClientCustomizer) {
-        AsyncHttpClientConfig.Builder builder = new AsyncHttpClientConfig.Builder();
+        final AsyncHttpClientConfig.Builder builder = new AsyncHttpClientConfig.Builder();
 
         ExecutorService executorService;
         if (config != null) {
@@ -95,7 +97,7 @@
                 executorService = Executors.newCachedThreadPool();
             }
 
-            builder = builder.setExecutorService(executorService);
+            builder.setExecutorService(executorService);
 
             builder.setConnectTimeout(ClientProperties.getValue(config.getProperties(),
                                                                 ClientProperties.CONNECT_TIMEOUT, 10000));
@@ -103,29 +105,23 @@
             builder.setRequestTimeout(ClientProperties.getValue(config.getProperties(),
                                                                 ClientProperties.READ_TIMEOUT, 10000));
 
-            Object proxyUri;
-            proxyUri = config.getProperty(ClientProperties.PROXY_URI);
-            if (proxyUri != null) {
-                final URI u = getProxyUri(proxyUri);
+            final Optional<ClientProxy> proxy = ClientProxy.proxyFromConfiguration(config);
+            proxy.ifPresent(clientProxy -> {
+                final URI u = clientProxy.uri();
                 final Properties proxyProperties = new Properties();
                 proxyProperties.setProperty(ProxyUtils.PROXY_PROTOCOL, u.getScheme());
                 proxyProperties.setProperty(ProxyUtils.PROXY_HOST, u.getHost());
                 proxyProperties.setProperty(ProxyUtils.PROXY_PORT, String.valueOf(u.getPort()));
 
-                final String userName = ClientProperties.getValue(
-                        config.getProperties(), ClientProperties.PROXY_USERNAME, String.class);
-                if (userName != null) {
-                    proxyProperties.setProperty(ProxyUtils.PROXY_USER, userName);
-
-                    final String password = ClientProperties.getValue(
-                            config.getProperties(), ClientProperties.PROXY_PASSWORD, String.class);
-                    if (password != null) {
-                        proxyProperties.setProperty(ProxyUtils.PROXY_PASSWORD, password);
+                if (clientProxy.userName() != null) {
+                    proxyProperties.setProperty(ProxyUtils.PROXY_USER, clientProxy.userName());
+                    if (clientProxy.password() != null) {
+                        proxyProperties.setProperty(ProxyUtils.PROXY_PASSWORD, clientProxy.password());
                     }
                 }
                 ProxyServerSelector proxyServerSelector = ProxyUtils.createProxyServerSelector(proxyProperties);
                 builder.setProxyServerSelector(proxyServerSelector);
-            }
+            });
         } else {
             executorService = Executors.newCachedThreadPool();
             builder.setExecutorService(executorService);
@@ -140,7 +136,7 @@
         }
 
         if (asyncClientCustomizer != null) {
-            builder = asyncClientCustomizer.customize(client, config, builder);
+            asyncClientCustomizer.customize(client, config, builder);
         }
 
         AsyncHttpClientConfig asyncClientConfig = builder.build();
@@ -148,17 +144,6 @@
         this.grizzlyClient = new AsyncHttpClient(new GrizzlyAsyncHttpProvider(asyncClientConfig), asyncClientConfig);
     }
 
-    @SuppressWarnings("ChainOfInstanceofChecks")
-    private static URI getProxyUri(final Object proxy) {
-        if (proxy instanceof URI) {
-            return (URI) proxy;
-        } else if (proxy instanceof String) {
-            return URI.create((String) proxy);
-        } else {
-            throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(ClientProperties.PROXY_URI));
-        }
-    }
-
     /**
      * Get the underlying Grizzly {@link com.ning.http.client.AsyncHttpClient} instance.
      *
diff --git a/connectors/grizzly-connector/src/main/resources/org/glassfish/jersey/grizzly/connector/localization.properties b/connectors/grizzly-connector/src/main/resources/org/glassfish/jersey/grizzly/connector/localization.properties
index 7fb050e..2d577c6 100644
--- a/connectors/grizzly-connector/src/main/resources/org/glassfish/jersey/grizzly/connector/localization.properties
+++ b/connectors/grizzly-connector/src/main/resources/org/glassfish/jersey/grizzly/connector/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,5 +16,4 @@
 
 error.buffering.entity=Error buffering the entity.
 expected.connector.provider.not.used=The supplied component is not configured to use a GrizzlyConnectorProvider.
-invalid.configurable.component.type=The supplied component "{0}" is not assignable from JerseyClient or JerseyWebTarget.
-wrong.proxy.uri.type=The proxy URI ("{0}") property MUST be an instance of String or URI.
+invalid.configurable.component.type=The supplied component "{0}" is not assignable from JerseyClient or JerseyWebTarget.
\ No newline at end of file
diff --git a/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
index 780adc1..1456d4b 100644
--- a/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
+++ b/connectors/jetty-connector/src/main/java/org/glassfish/jersey/jetty/connector/JettyConnector.java
@@ -31,10 +31,8 @@
 import java.util.Optional;
 import java.util.concurrent.CancellationException;
 import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.logging.Level;
@@ -54,6 +52,7 @@
 import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.ClientRequest;
 import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.client.innate.ClientProxy;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
 import org.glassfish.jersey.internal.util.collection.ByteBufferInputStream;
@@ -187,19 +186,17 @@
             auth.addAuthentication((BasicAuthentication) basicAuthProvider);
         }
 
-        final Object proxyUri = config.getProperties().get(ClientProperties.PROXY_URI);
-        if (proxyUri != null) {
-            final URI u = getProxyUri(proxyUri);
+        final Optional<ClientProxy> proxy = ClientProxy.proxyFromConfiguration(config);
+        proxy.ifPresent(clientProxy -> {
             final ProxyConfiguration proxyConfig = client.getProxyConfiguration();
+            final URI u = clientProxy.uri();
             proxyConfig.getProxies().add(new HttpProxy(u.getHost(), u.getPort()));
 
-            final Object proxyUsername = config.getProperties().get(ClientProperties.PROXY_USERNAME);
-            if (proxyUsername != null) {
-                final Object proxyPassword = config.getProperties().get(ClientProperties.PROXY_PASSWORD);
+            if (clientProxy.userName() != null) {
                 auth.addAuthentication(new BasicAuthentication(u, "<<ANY_REALM>>",
-                        String.valueOf(proxyUsername), String.valueOf(proxyPassword)));
+                        clientProxy.userName(), clientProxy.password()));
             }
-        }
+        });
 
         if (disableCookies) {
             client.setCookieStore(new HttpCookieStore.Empty());
@@ -223,17 +220,6 @@
         this.cookieStore = client.getCookieStore();
     }
 
-    @SuppressWarnings("ChainOfInstanceofChecks")
-    private static URI getProxyUri(final Object proxy) {
-        if (proxy instanceof URI) {
-            return (URI) proxy;
-        } else if (proxy instanceof String) {
-            return URI.create((String) proxy);
-        } else {
-            throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(ClientProperties.PROXY_URI));
-        }
-    }
-
     /**
      * Get the {@link HttpClient}.
      *
diff --git a/connectors/jetty-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties b/connectors/jetty-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties
index 15d9708..88cbe10 100644
--- a/connectors/jetty-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties
+++ b/connectors/jetty-connector/src/main/resources/org/glassfish/jersey/jetty/connector/localization.properties
@@ -1,5 +1,5 @@
 #
-# Copyright (c) 2013, 2018 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
 #
 # This program and the accompanying materials are made available under the
 # terms of the Eclipse Public License v. 2.0, which is available at
@@ -16,7 +16,5 @@
 
 # {0} - HTTP method, e.g. GET, DELETE
 method.not.supported=Method {0} not supported.
-# {0} - property name - jersey.config.client.proxyUri
-wrong.proxy.uri.type=The proxy URI ("{0}") property MUST be an instance of String or URI.
 invalid.configurable.component.type=The supplied component "{0}" is not assignable from JerseyClient or JerseyWebTarget.
 expected.connector.provider.not.used=The supplied component is not configured to use a JettyConnectorProvider.
diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
index 6d67c9d..8bb117a 100644
--- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
+++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java
@@ -19,8 +19,6 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.ProxySelector;
 import java.net.SocketAddress;
 import java.net.URI;
 import java.util.ArrayList;
@@ -29,6 +27,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
@@ -48,7 +47,6 @@
 import io.netty.channel.Channel;
 import io.netty.channel.ChannelDuplexHandler;
 import io.netty.channel.ChannelHandlerContext;
-import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.channel.ChannelInitializer;
 import io.netty.channel.ChannelOption;
 import io.netty.channel.ChannelPipeline;
@@ -69,7 +67,6 @@
 import io.netty.handler.codec.http.HttpUtil;
 import io.netty.handler.codec.http.HttpVersion;
 import io.netty.handler.proxy.HttpProxyHandler;
-import io.netty.handler.proxy.ProxyConnectionEvent;
 import io.netty.handler.proxy.ProxyHandler;
 import io.netty.handler.ssl.ApplicationProtocolConfig;
 import io.netty.handler.ssl.ClientAuth;
@@ -80,10 +77,12 @@
 import io.netty.handler.timeout.IdleState;
 import io.netty.handler.timeout.IdleStateEvent;
 import io.netty.handler.timeout.IdleStateHandler;
+import io.netty.resolver.NoopAddressResolverGroup;
 import io.netty.util.concurrent.GenericFutureListener;
 import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.ClientRequest;
 import org.glassfish.jersey.client.ClientResponse;
+import org.glassfish.jersey.client.innate.ClientProxy;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
 import org.glassfish.jersey.message.internal.OutboundMessageContext;
@@ -235,6 +234,18 @@
 
             if (chan == null) {
                Bootstrap b = new Bootstrap();
+
+               // http proxy
+               Optional<ClientProxy> proxy = ClientProxy.proxyFromRequest(jerseyRequest);
+               if (!proxy.isPresent()) {
+                   proxy = ClientProxy.proxyFromProperties(requestUri);
+               }
+               proxy.ifPresent(clientProxy -> {
+                   b.resolver(NoopAddressResolverGroup.INSTANCE); // request hostname resolved by the HTTP proxy
+               });
+
+               final Optional<ClientProxy> handlerProxy = proxy;
+
                b.group(group)
                 .channel(NioSocketChannel.class)
                 .handler(new ChannelInitializer<SocketChannel>() {
@@ -245,41 +256,14 @@
                      Configuration config = jerseyRequest.getConfiguration();
 
                      // http proxy
-                     final Object proxyUri = config.getProperties().get(ClientProperties.PROXY_URI);
-                     if (proxyUri != null) {
-                         final URI u = getProxyUri(proxyUri);
-
-                         final String userName = ClientProperties.getValue(
-                                 config.getProperties(), ClientProperties.PROXY_USERNAME, String.class);
-                         final String password = ClientProperties.getValue(
-                                 config.getProperties(), ClientProperties.PROXY_PASSWORD, String.class);
-
+                     handlerProxy.ifPresent(clientProxy -> {
+                         final URI u = clientProxy.uri();
                          InetSocketAddress proxyAddr = new InetSocketAddress(u.getHost(),
-                                                                             u.getPort() == -1 ? 8080 : u.getPort());
-                         ProxyHandler proxy = createProxyHandler(jerseyRequest, proxyAddr, userName, password, connectTimeout);
-                         p.addLast(proxy);
-                     } else {
-                         ProxySelector sel = ProxySelector.getDefault();
-                         for (Proxy proxy: sel.select(requestUri)) {
-                             if (Proxy.Type.HTTP.equals(proxy.type())) {
-                                 SocketAddress proxyAddress = proxy.address();
-                                 if (InetSocketAddress.class.isInstance(proxy.address())) {
-                                     InetSocketAddress proxyAddr = (InetSocketAddress) proxyAddress;
-                                     if (proxyAddr.isUnresolved()
-                                             && proxyAddr.getHostName() != null
-                                             && proxyAddr.getHostName().startsWith("http://")) {
-                                         proxyAddress = new InetSocketAddress(
-                                                 proxyAddr.getHostString().substring(7), proxyAddr.getPort()
-                                         );
-                                     }
-                                 }
-                                 ProxyHandler proxyHandler
-                                         = createProxyHandler(jerseyRequest, proxyAddress, null, null, connectTimeout);
-                                 p.addLast(proxyHandler);
-                                 break;
-                             }
-                         }
-                     }
+                                 u.getPort() == -1 ? 8080 : u.getPort());
+                         ProxyHandler proxy1 = createProxyHandler(jerseyRequest, proxyAddr,
+                                 clientProxy.userName(), clientProxy.password(), connectTimeout);
+                         p.addLast(proxy1);
+                     });
 
                      // Enable HTTPS if necessary.
                      if ("https".equals(requestUri.getScheme())) {
@@ -471,17 +455,6 @@
         executorService.shutdown();
     }
 
-    @SuppressWarnings("ChainOfInstanceofChecks")
-    private static URI getProxyUri(final Object proxy) {
-        if (proxy instanceof URI) {
-            return (URI) proxy;
-        } else if (proxy instanceof String) {
-            return URI.create((String) proxy);
-        } else {
-            throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(ClientProperties.PROXY_URI));
-        }
-    }
-
     protected static class PruneIdlePool extends ChannelDuplexHandler {
        HashMap<String, ArrayList<Channel>> connections;
        String key;
diff --git a/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties b/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties
index 9055d62..ba91c4f 100644
--- a/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties
+++ b/connectors/netty-connector/src/main/resources/org/glassfish/jersey/netty/connector/localization.properties
@@ -14,7 +14,6 @@
 # SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 #
 
-wrong.proxy.uri.type=The proxy URI ("{0}") property MUST be an instance of String or URI.
 wrong.read.timeout=Unexpected ("{0}") READ_TIMEOUT.
 wrong.max.pool.size=Unexpected ("{0}") maximum number of connections per destination.
 wrong.max.pool.total=Unexpected ("{0}") maximum number of connections total.
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
index 4b42f10..e5600ce 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java
@@ -520,19 +520,4 @@
     public static <T> T getValue(final Map<String, ?> properties, final String key, final Class<T> type) {
         return PropertiesHelper.getValue(properties, key, type, null);
     }
-
-    /**
-     * Get the value of the specified property. If null, it will obtain it from System property.
-     * <p/>
-     * If the property is not set the method will return {@code null}.
-     *
-     * @param properties Map of properties to get the property value from.
-     * @param key        Name of the property.
-     * @param systemKey  Name of the System property.
-     * @return Value of the property or {@code null}.
-     * @since 2.37
-     */
-    public static String getValue(Map<String, ?> properties, String key, String systemKey) {
-        return PropertiesHelper.getValue(properties, key, System.getProperty(systemKey), String.class, null);
-    }
 }
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/ClientProxy.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/ClientProxy.java
new file mode 100644
index 0000000..00d4078
--- /dev/null
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/ClientProxy.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+package org.glassfish.jersey.client.innate;
+
+import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.ClientRequest;
+import org.glassfish.jersey.client.internal.LocalizationMessages;
+
+import javax.ws.rs.ProcessingException;
+import javax.ws.rs.core.Configuration;
+import javax.ws.rs.core.MultivaluedMap;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.Locale;
+import java.util.Optional;
+
+/**
+ * Default client Proxy information internal object. It is used for parsing the proxy information in all connectors.
+ */
+public abstract class ClientProxy {
+
+    private ClientProxy() {
+      // do not instantiate
+    };
+
+    public static Optional<ClientProxy> proxyFromRequest(ClientRequest request) {
+        return getProxy(request);
+    }
+
+    public static Optional<ClientProxy> proxyFromProperties(URI requestUri) {
+        return getSystemPropertiesProxy(requestUri);
+    }
+
+    public static Optional<ClientProxy> proxyFromConfiguration(Configuration configuration) {
+        return getProxy(configuration);
+    }
+
+    public static ClientProxy proxy(Proxy proxy) {
+        return new ProxyClientProxy(proxy);
+    }
+
+    public static void setBasicAuthorizationHeader(MultivaluedMap<String, Object> headers, ClientProxy proxy) {
+        if (proxy.userName() != null) {
+            StringBuilder auth = new StringBuilder().append(proxy.userName()).append(":");
+            if (proxy.password() != null) {
+                auth.append(proxy.password());
+            }
+            String encoded = "Basic " + Base64.getEncoder().encodeToString(auth.toString().getBytes());
+            headers.put("Proxy-Authorization", Arrays.asList(encoded));
+        }
+    }
+
+    protected String userName;
+    protected String password;
+
+    private static ClientProxy toProxy(Object proxy) {
+        if (proxy instanceof String) {
+            return new UriClientProxy(URI.create((String) proxy));
+        } else if (proxy instanceof URI) {
+            return new UriClientProxy((URI) proxy);
+        } else if (Proxy.class.isInstance(proxy)) {
+            Proxy netProxy = Proxy.class.cast(proxy);
+            if (Proxy.Type.HTTP.equals(netProxy.type())) {
+                return new ProxyClientProxy(Proxy.class.cast(proxy));
+            } else {
+                return null;
+            }
+        } else {
+            throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(ClientProperties.PROXY_URI));
+        }
+    }
+
+    public abstract Proxy proxy();
+
+    public abstract URI uri();
+
+    public abstract Proxy.Type type();
+
+    public String password() {
+        return password;
+    }
+
+    public String userName() {
+        return userName;
+    };
+
+    private static Optional<ClientProxy> getProxy(ClientRequest request) {
+        Object proxyUri = request.resolveProperty(ClientProperties.PROXY_URI, Object.class);
+        if (proxyUri != null) {
+            ClientProxy proxy = toProxy(proxyUri);
+            if (proxy != null) {
+                proxy.userName = request.resolveProperty(ClientProperties.PROXY_USERNAME, String.class);
+                proxy.password = request.resolveProperty(ClientProperties.PROXY_PASSWORD, String.class);
+                return Optional.of(proxy);
+            } else {
+                return Optional.empty();
+            }
+        }
+        return Optional.empty();
+    }
+
+    private static Optional<ClientProxy> getProxy(Configuration config) {
+        Object proxyUri = config.getProperties().get(ClientProperties.PROXY_URI);
+        if (proxyUri != null) {
+            ClientProxy proxy = toProxy(proxyUri);
+            if (proxy != null) {
+                proxy.userName = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_USERNAME, String.class);
+                proxy.password = ClientProperties.getValue(config.getProperties(), ClientProperties.PROXY_PASSWORD, String.class);
+                return Optional.of(proxy);
+            } else {
+                return Optional.empty();
+            }
+        }
+        return Optional.empty();
+    }
+
+    private static Optional<ClientProxy> getSystemPropertiesProxy(URI requestUri) {
+        ProxySelector sel = ProxySelector.getDefault();
+        for (Proxy proxy: sel.select(requestUri)) {
+            if (Proxy.Type.HTTP.equals(proxy.type())) {
+                return Optional.of(new ProxyClientProxy(proxy));
+            }
+        }
+        return Optional.empty();
+    }
+
+    private static final class ProxyClientProxy extends ClientProxy {
+
+        private final Proxy proxy;
+
+        private ProxyClientProxy(Proxy proxy) {
+            this.proxy = proxy;
+        }
+
+        @Override
+        public Proxy proxy() {
+            return proxy;
+        }
+
+        @Override
+        public Proxy.Type type() {
+            return proxy.type();
+        }
+
+        @Override
+        public URI uri() {
+            URI uri = null;
+            if (Proxy.Type.HTTP.equals(proxy.type())) {
+                SocketAddress proxyAddress = proxy.address();
+                if (InetSocketAddress.class.isInstance(proxy.address())) {
+                    InetSocketAddress proxyAddr = (InetSocketAddress) proxyAddress;
+                    try {
+                        if (proxyAddr.isUnresolved()
+                                && proxyAddr.getHostName() != null
+                                && proxyAddr.getHostName().toLowerCase(Locale.ROOT).startsWith("http://")) {
+                            String hostString = proxyAddr.getHostString().substring(7);
+                            uri = new URI("http", null, hostString, proxyAddr.getPort(), null, null, null);
+                        } else {
+                            uri = new URI("http", null, proxyAddr.getHostString(), proxyAddr.getPort(), null, null, null);
+                        }
+                    } catch (URISyntaxException e) {
+                        throw new ProcessingException(e);
+                    }
+                }
+            }
+            return uri;
+        }
+    }
+
+    private static final class UriClientProxy extends ClientProxy {
+        private final URI uri;
+
+        private UriClientProxy(URI uri) {
+            this.uri = uri;
+        }
+
+        @Override
+        public Proxy proxy() {
+            return new Proxy(type(), new InetSocketAddress(uri.getHost(), uri.getPort()));
+        }
+
+        @Override
+        public Proxy.Type type() {
+            return Proxy.Type.HTTP;
+        }
+
+        @Override
+        public URI uri() {
+            return uri;
+        }
+    }
+}
+
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/package-info.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/package-info.java
new file mode 100644
index 0000000..016eea7
--- /dev/null
+++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/package-info.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0, which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * This Source Code may also be made available under the following Secondary
+ * Licenses when the conditions for such availability set forth in the
+ * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
+ * version 2 with the GNU Classpath Exception, which is available at
+ * https://www.gnu.org/software/classpath/license.html.
+ *
+ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
+ */
+
+/**
+ * Jersey client MOST INTERNAL classes/interfaces.
+ * Shall not be used outside of Jersey.
+ */
+package org.glassfish.jersey.client.innate;
\ No newline at end of file
diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
index ef85fa8..dc8af25 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java
@@ -21,28 +21,23 @@
 import java.io.InputStream;
 import java.lang.reflect.Field;
 import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
 import java.net.ProtocolException;
-import java.net.Proxy;
-import java.net.Proxy.Type;
 import java.net.SocketTimeoutException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.security.AccessController;
 import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
-import java.util.Arrays;
-import java.util.Base64;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 import java.util.logging.Level;
 import java.util.logging.Logger;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
 import javax.net.ssl.HostnameVerifier;
@@ -50,17 +45,16 @@
 import javax.net.ssl.SSLSocketFactory;
 import javax.ws.rs.ProcessingException;
 import javax.ws.rs.client.Client;
-import javax.ws.rs.core.Configuration;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 
-import org.glassfish.jersey.ExternalProperties;
 import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.ClientRequest;
 import org.glassfish.jersey.client.ClientResponse;
 import org.glassfish.jersey.client.HttpUrlConnectorProvider;
 import org.glassfish.jersey.client.JerseyClient;
 import org.glassfish.jersey.client.RequestEntityProcessing;
+import org.glassfish.jersey.client.innate.ClientProxy;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
 import org.glassfish.jersey.internal.util.PropertiesHelper;
@@ -321,37 +315,11 @@
         }
     }
 
-    private static URI getProxyUriValue(Object proxy) {
-        if (proxy instanceof URI) {
-            return (URI) proxy;
-        } else if (proxy instanceof String) {
-            return URI.create((String) proxy);
-        } else {
-            throw new ProcessingException(LocalizationMessages.WRONG_PROXY_URI_TYPE(ClientProperties.PROXY_URI));
-        }
-    }
-
     private ClientResponse _apply(final ClientRequest request) throws IOException {
         final HttpURLConnection uc;
-        Proxy proxy = null;
-        Object proxyUri = request.getConfiguration().getProperties().get(ClientProperties.PROXY_URI);
-        if (proxyUri != null) {
-            URI uri = getProxyUriValue(proxyUri);
-            String username = ClientProperties.getValue(request.getConfiguration().getProperties(),
-                    ClientProperties.PROXY_USERNAME, ExternalProperties.HTTP_PROXY_USER);
-            String password = ClientProperties.getValue(request.getConfiguration().getProperties(),
-                    ClientProperties.PROXY_PASSWORD, ExternalProperties.HTTP_PROXY_PASSWORD);
-            if (username != null) {
-                StringBuilder auth = new StringBuilder().append(username).append(":");
-                if (password != null) {
-                    auth.append(password);
-                }
-                String encoded = "Basic " + Base64.getEncoder().encodeToString(auth.toString().getBytes());
-                request.getHeaders().put("Proxy-Authorization", Arrays.asList(encoded));
-            }
-            proxy = new Proxy(Type.HTTP, new InetSocketAddress(uri.getHost(), uri.getPort()));
-        }
-        uc = this.connectionFactory.getConnection(request.getUri().toURL(), proxy);
+        Optional<ClientProxy> proxy = ClientProxy.proxyFromRequest(request);
+        proxy.ifPresent(clientProxy -> ClientProxy.setBasicAuthorizationHeader(request.getHeaders(), proxy.get()));
+        uc = this.connectionFactory.getConnection(request.getUri().toURL(), proxy.isPresent() ? proxy.get().proxy() : null);
         uc.setDoInput(true);
 
         final String httpMethod = request.getMethod();
diff --git a/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties b/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
index 8e3823c..e66ceab 100644
--- a/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
+++ b/core-client/src/main/resources/org/glassfish/jersey/client/internal/localization.properties
@@ -84,4 +84,5 @@
 error.listener.init=ClientLifecycleListener {0} failed to initialize properly.
 error.listener.close=ClientLifecycleListener {0} failed to close properly.
 error.shutdownhook.close=Client shutdown hook {0} failed.
+# {0} - property name - jersey.config.client.httpclient.proxyUri
 wrong.proxy.uri.type=The proxy URI ("{0}") property MUST be an instance of String or URI.
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java
index 4580fac..a813e54 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxySelectorTest.java
@@ -61,9 +61,8 @@
     @Parameterized.Parameters(name = "{index}: {0}")
     public static List<Object[]> testData() {
         return Arrays.asList(new Object[][]{
-//                {ApacheConnectorProvider.class},
-//                {Apache5ConnectorProvider.class},
-//                {JettyConnectorProvider.class},
+                // Apache, Grizzly, Jetty have the proxy set on constructor, i.e. ProxySelector cannot choose based on URI.
+                // HttpUrlConnector ignores proxy on localhost.
                 {NettyConnectorProvider.class},
         });
     }
diff --git a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java
index b30d39e..69be5f6 100644
--- a/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java
+++ b/tests/e2e-client/src/test/java/org/glassfish/jersey/tests/e2e/client/connector/proxy/ProxyTest.java
@@ -27,6 +27,7 @@
 import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.HttpUrlConnectorProvider;
 import org.glassfish.jersey.client.spi.ConnectorProvider;
+import org.glassfish.jersey.grizzly.connector.GrizzlyConnectorProvider;
 import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
 import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
 import org.junit.AfterClass;
@@ -71,6 +72,7 @@
         return Arrays.asList(new Object[][]{
                 {ApacheConnectorProvider.class},
                 {Apache5ConnectorProvider.class},
+                {GrizzlyConnectorProvider.class},
                 {JettyConnectorProvider.class},
                 {NettyConnectorProvider.class},
                 {HttpUrlConnectorProvider.class},
@@ -98,9 +100,11 @@
 
     @Test
     public void testGet407() {
+        // Grizzly sends (String)null password and username
+        int expected = GrizzlyConnectorProvider.class.isInstance(connectorProvider) ? 400 : 407;
         client().property(ClientProperties.PROXY_URI, ProxyTest.PROXY_URI);
         try (Response response = target("proxyTest").request().get()) {
-            assertEquals(407, response.getStatus());
+            assertEquals(expected, response.getStatus());
         } catch (ProcessingException pe) {
             Assert.assertTrue(pe.getMessage().contains("407")); // netty
         }