From 252fcc4c8b85156da995d0cb785d71746f75b42e Mon Sep 17 00:00:00 2001 From: jansupol Date: Fri, 8 Jul 2022 14:24:08 +0200 Subject: [PATCH] Move Proxy parsing logic from connectors to a common class Signed-off-by: jansupol --- .../apache/connector/ApacheConnector.java | 46 ++-- .../apache/connector/localization.properties | 4 +- .../apache5/connector/Apache5Connector.java | 46 ++-- .../apache5/connector/localization.properties | 2 - .../grizzly/connector/GrizzlyConnector.java | 43 ++-- .../grizzly/connector/localization.properties | 5 +- .../jetty/connector/JettyConnector.java | 28 +-- .../jetty/connector/localization.properties | 4 +- .../netty/connector/NettyConnector.java | 71 ++---- .../netty/connector/localization.properties | 1 - .../jersey/client/ClientProperties.java | 15 -- .../jersey/client/innate/ClientProxy.java | 212 ++++++++++++++++++ .../jersey/client/innate/package-info.java | 21 ++ .../client/internal/HttpUrlConnector.java | 42 +--- .../client/internal/localization.properties | 1 + .../connector/proxy/ProxySelectorTest.java | 5 +- .../e2e/client/connector/proxy/ProxyTest.java | 6 +- 17 files changed, 323 insertions(+), 229 deletions(-) create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/innate/ClientProxy.java create mode 100644 core-client/src/main/java/org/glassfish/jersey/client/innate/package-info.java 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 81e94b4b17..60c5e83399 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 @@ class ApacheConnector implements Connector { 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 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 @@ public CookieStore getCookieStore() { 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 f0cc55b834..3b06461a3f 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 @@ error.reading.httpentity.stream=Error reading InputStream from HttpEntity: "{0}" 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 9d101055f2..da7ccdd5ce 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 @@ class Apache5Connector implements Connector { 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 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 @@ public CookieStore getCookieStore() { 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 16dbc30eca..1f6e4a6f9b 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 @@ error.reading.httpentity.stream=Error reading InputStream from HttpEntity: "{0}" 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 ced8f4376e..14a3e307b6 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 @@ class GrizzlyConnector implements Connector { 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 @@ class GrizzlyConnector implements Connector { executorService = Executors.newCachedThreadPool(); } - builder = builder.setExecutorService(executorService); + builder.setExecutorService(executorService); builder.setConnectTimeout(ClientProperties.getValue(config.getProperties(), ClientProperties.CONNECT_TIMEOUT, 10000)); @@ -103,29 +105,23 @@ class GrizzlyConnector implements Connector { 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 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 @@ class GrizzlyConnector implements Connector { } if (asyncClientCustomizer != null) { - builder = asyncClientCustomizer.customize(client, config, builder); + asyncClientCustomizer.customize(client, config, builder); } AsyncHttpClientConfig asyncClientConfig = builder.build(); @@ -148,17 +144,6 @@ class GrizzlyConnector implements Connector { 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 7fb050ec9d..2d577c6e9a 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 780adc1e8a..1456d4b5bd 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 @@ class JettyConnector implements Connector { auth.addAuthentication((BasicAuthentication) basicAuthProvider); } - final Object proxyUri = config.getProperties().get(ClientProperties.PROXY_URI); - if (proxyUri != null) { - final URI u = getProxyUri(proxyUri); + final Optional 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, "<>", - String.valueOf(proxyUsername), String.valueOf(proxyPassword))); + clientProxy.userName(), clientProxy.password())); } - } + }); if (disableCookies) { client.setCookieStore(new HttpCookieStore.Empty()); @@ -223,17 +220,6 @@ class JettyConnector implements Connector { 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 15d9708e91..88cbe10eab 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 6d67c9d867..8bb117a966 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 @@ protected void execute(final ClientRequest jerseyRequest, final Set redirec if (chan == null) { Bootstrap b = new Bootstrap(); + + // http proxy + Optional 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 handlerProxy = proxy; + b.group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer() { @@ -245,41 +256,14 @@ protected void initChannel(SocketChannel ch) throws Exception { 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 @@ public void close() { 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> 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 9055d6277a..ba91c4f649 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 4b42f10297..e5600ce382 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 getValue(final Map properties, final String key, public static T getValue(final Map properties, final String key, final Class type) { return PropertiesHelper.getValue(properties, key, type, null); } - - /** - * Get the value of the specified property. If null, it will obtain it from System property. - *

- * 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 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 0000000000..00d407814a --- /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 proxyFromRequest(ClientRequest request) { + return getProxy(request); + } + + public static Optional proxyFromProperties(URI requestUri) { + return getSystemPropertiesProxy(requestUri); + } + + public static Optional proxyFromConfiguration(Configuration configuration) { + return getProxy(configuration); + } + + public static ClientProxy proxy(Proxy proxy) { + return new ProxyClientProxy(proxy); + } + + public static void setBasicAuthorizationHeader(MultivaluedMap 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 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 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 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 0000000000..016eea7163 --- /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 ef85fa8eaf..dc8af256b1 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 @@ protected void secureConnection(final JerseyClient client, final HttpURLConnecti } } - 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 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 8e3823cde3..e66ceab304 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.request.cancelled=Request cancelled by the client call. 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 4580facf24..a813e545c6 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 @@ public class ProxySelectorTest { @Parameterized.Parameters(name = "{index}: {0}") public static List 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 b30d39e29c..69be5f6591 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 @@ public static List testData() { return Arrays.asList(new Object[][]{ {ApacheConnectorProvider.class}, {Apache5ConnectorProvider.class}, + {GrizzlyConnectorProvider.class}, {JettyConnectorProvider.class}, {NettyConnectorProvider.class}, {HttpUrlConnectorProvider.class}, @@ -98,9 +100,11 @@ public void testGetNoPass() { @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 }