Support ClientProperties.PROXY_URI & al in HttpUrlConnector (#5091)
* Support for proxy in HttpUrlConnector
Signed-off-by: Jorge Bescos Gascon <jorge.bescos.gascon@oracle.com>
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 7f1752e..4b42f10 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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
@@ -520,4 +520,19 @@
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/HttpUrlConnectorProvider.java b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
index 7389781..25a6d78 100644
--- a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
+++ b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2013, 2019 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
@@ -18,6 +18,7 @@
import java.io.IOException;
import java.net.HttpURLConnection;
+import java.net.Proxy;
import java.net.URL;
import java.util.Map;
import java.util.logging.Logger;
@@ -267,6 +268,23 @@
* @throws java.io.IOException in case the connection cannot be provided.
*/
public HttpURLConnection getConnection(URL url) throws IOException;
+
+ /**
+ * Get a {@link java.net.HttpURLConnection} for a given URL.
+ * <p>
+ * Implementation of the method MUST be thread-safe and MUST ensure that
+ * a dedicated {@link java.net.HttpURLConnection} instance is returned for concurrent
+ * requests.
+ * </p>
+ *
+ * @param url the endpoint URL.
+ * @param proxy the configured proxy or null.
+ * @return the {@link java.net.HttpURLConnection}.
+ * @throws java.io.IOException in case the connection cannot be provided.
+ */
+ default HttpURLConnection getConnection(URL url, Proxy proxy) throws IOException {
+ return (proxy == null) ? getConnection(url) : (HttpURLConnection) url.openConnection(proxy);
+ }
}
private static class DefaultConnectionFactory implements ConnectionFactory {
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 443c857..ef85fa8 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
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2011, 2021 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 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
@@ -21,13 +21,18 @@
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;
@@ -37,17 +42,19 @@
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.ws.rs.ProcessingException;
-import javax.ws.rs.client.Client;
-import javax.ws.rs.core.MultivaluedMap;
-import javax.ws.rs.core.Response;
-
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
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;
@@ -314,10 +321,37 @@
}
}
+ 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;
-
- uc = this.connectionFactory.getConnection(request.getUri().toURL());
+ 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);
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 4bc0ae8..8e3823c 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
@@ -1,5 +1,5 @@
#
-# Copyright (c) 2012, 2019 Oracle and/or its affiliates. All rights reserved.
+# Copyright (c) 2012, 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
@@ -84,3 +84,4 @@
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.
+wrong.proxy.uri.type=The proxy URI ("{0}") property MUST be an instance of String or URI.
diff --git a/core-client/src/test/java/org/glassfish/jersey/client/HttpUrlConnectorTest.java b/core-client/src/test/java/org/glassfish/jersey/client/HttpUrlConnectorTest.java
index 7cd9121..5bb7a78 100644
--- a/core-client/src/test/java/org/glassfish/jersey/client/HttpUrlConnectorTest.java
+++ b/core-client/src/test/java/org/glassfish/jersey/client/HttpUrlConnectorTest.java
@@ -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
diff --git a/core-common/src/main/java/org/glassfish/jersey/ExternalProperties.java b/core-common/src/main/java/org/glassfish/jersey/ExternalProperties.java
index 04ef1bd..a8f6ec6 100644
--- a/core-common/src/main/java/org/glassfish/jersey/ExternalProperties.java
+++ b/core-common/src/main/java/org/glassfish/jersey/ExternalProperties.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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
@@ -32,12 +32,22 @@
public static final String HTTP_PROXY_PORT = "http.proxyPort";
/**
- * Property used to indicates the hosts that should be accessed
+ * Property used to indicate the hosts that should be accessed
* without going through the proxy.
*/
public static final String HTTP_NON_PROXY_HOSTS = "http.nonProxyHosts";
/**
+ * Property used to specify the user name to authenticate with the proxy.
+ */
+ public static final String HTTP_PROXY_USER = "http.proxyUser";
+
+ /**
+ * Property used to specify the password to authenticate with the proxy.
+ */
+ public static final String HTTP_PROXY_PASSWORD = "http.proxyPassword";
+
+ /**
* Prevent instantiation.
*/
private ExternalProperties() {
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 93e69c2..b30d39e 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
@@ -25,6 +25,7 @@
import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.client.ClientProperties;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
import org.glassfish.jersey.client.spi.ConnectorProvider;
import org.glassfish.jersey.jetty.connector.JettyConnectorProvider;
import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
@@ -72,6 +73,7 @@
{Apache5ConnectorProvider.class},
{JettyConnectorProvider.class},
{NettyConnectorProvider.class},
+ {HttpUrlConnectorProvider.class},
});
}
@@ -110,7 +112,8 @@
client().property(ClientProperties.PROXY_USERNAME, ProxyTest.PROXY_USERNAME);
client().property(ClientProperties.PROXY_PASSWORD, ProxyTest.PROXY_PASSWORD);
Response response = target("proxyTest").request().get();
- assertEquals(200, response.getStatus());
+ response.bufferEntity();
+ assertEquals(response.readEntity(String.class), 200, response.getStatus());
}
private static Server server;
diff --git a/tests/integration/externalproperties/src/test/java/org/glassfish/jersey/tests/externalproperties/HttpProxyTest.java b/tests/integration/externalproperties/src/test/java/org/glassfish/jersey/tests/externalproperties/HttpProxyTest.java
index 9410029..f74a55d 100644
--- a/tests/integration/externalproperties/src/test/java/org/glassfish/jersey/tests/externalproperties/HttpProxyTest.java
+++ b/tests/integration/externalproperties/src/test/java/org/glassfish/jersey/tests/externalproperties/HttpProxyTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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
diff --git a/tests/integration/jersey-4003/src/test/java/org/glassfish/jersey/tests/integration/jersey4003/LostResponseTest.java b/tests/integration/jersey-4003/src/test/java/org/glassfish/jersey/tests/integration/jersey4003/LostResponseTest.java
index 2ba8042..1780d05 100644
--- a/tests/integration/jersey-4003/src/test/java/org/glassfish/jersey/tests/integration/jersey4003/LostResponseTest.java
+++ b/tests/integration/jersey-4003/src/test/java/org/glassfish/jersey/tests/integration/jersey4003/LostResponseTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2020 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2020, 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,16 +16,18 @@
package org.glassfish.jersey.tests.integration.jersey4003;
-import org.glassfish.jersey.client.ClientConfig;
-import org.glassfish.jersey.client.HttpUrlConnectorProvider;
-import org.glassfish.jersey.client.JerseyClientBuilder;
-import org.glassfish.jersey.client.JerseyCompletionStageRxInvoker;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Test;
-
-import org.mockito.Mockito;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.URL;
+import java.util.concurrent.CompletionStage;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.Entity;
@@ -34,17 +36,15 @@
import javax.ws.rs.core.GenericType;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.URL;
-import java.util.concurrent.CompletionStage;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicReference;
+
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.client.HttpUrlConnectorProvider;
+import org.glassfish.jersey.client.JerseyClientBuilder;
+import org.glassfish.jersey.client.JerseyCompletionStageRxInvoker;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
public class LostResponseTest {
@@ -59,7 +59,7 @@
HttpUrlConnectorProvider.ConnectionFactory connectionFactory =
Mockito.mock(HttpUrlConnectorProvider.ConnectionFactory.class);
HttpURLConnection connection = Mockito.mock(HttpURLConnection.class);
- Mockito.when(connectionFactory.getConnection(Mockito.any(URL.class))).thenReturn(connection);
+ Mockito.when(connectionFactory.getConnection(Mockito.any(URL.class), Mockito.any())).thenReturn(connection);
OutputStream outputStream = Mockito.mock(OutputStream.class);
Mockito.when(connection.getOutputStream()).thenReturn(outputStream);