diff --git a/jersey/connector/src/main/java/io/helidon/jersey/connector/HelidonConnector.java b/jersey/connector/src/main/java/io/helidon/jersey/connector/HelidonConnector.java index a9141e146ac..704c8e2efb7 100644 --- a/jersey/connector/src/main/java/io/helidon/jersey/connector/HelidonConnector.java +++ b/jersey/connector/src/main/java/io/helidon/jersey/connector/HelidonConnector.java @@ -37,6 +37,7 @@ import io.helidon.config.Config; import io.helidon.nima.common.tls.Tls; import io.helidon.nima.http.media.ReadableEntity; +import io.helidon.nima.webclient.Proxy; import io.helidon.nima.webclient.WebClient; import io.helidon.nima.webclient.http1.Http1; import io.helidon.nima.webclient.http1.Http1Client; @@ -73,6 +74,7 @@ class HelidonConnector implements Connector { private final Client client; private final Http1Client httpClient; + private Proxy proxy; HelidonConnector(Client client, Configuration config) { this.client = client; @@ -84,6 +86,9 @@ class HelidonConnector implements Connector { // use config for client builder.config(helidonConfig(config).orElse(Config.empty())); + // proxy support + proxy = ProxyBuilder.createProxy(config).orElse(Proxy.noProxy()); + // possibly override config with properties if (properties.containsKey(CONNECT_TIMEOUT)) { builder.connectTimeout(Duration.ofMillis(getValue(properties, CONNECT_TIMEOUT, DEFAULT_TIMEOUT))); @@ -104,9 +109,14 @@ class HelidonConnector implements Connector { * @return the mapped request */ private Http1ClientRequest mapRequest(ClientRequest request) { + // possibly override proxy in request + Proxy requestProxy = ProxyBuilder.createProxy(request).orElse(proxy); + + // create WebClient request URI uri = request.getUri(); Http1ClientRequest httpRequest = httpClient .method(Http.Method.create(request.getMethod())) + .proxy(requestProxy) .uri(uri); // map query parameters @@ -264,6 +274,10 @@ Http1Client client() { return httpClient; } + Proxy proxy() { + return proxy; + } + /** * Returns the Helidon Connector configuration, if available. * diff --git a/jersey/connector/src/main/java/io/helidon/jersey/connector/ProxyBuilder.java b/jersey/connector/src/main/java/io/helidon/jersey/connector/ProxyBuilder.java new file mode 100644 index 00000000000..2988db118eb --- /dev/null +++ b/jersey/connector/src/main/java/io/helidon/jersey/connector/ProxyBuilder.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.jersey.connector; + +import java.net.URI; +import java.util.Locale; +import java.util.Optional; + +import io.helidon.nima.webclient.Proxy; + +import jakarta.ws.rs.ProcessingException; +import jakarta.ws.rs.core.Configuration; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.ClientRequest; + +import static org.glassfish.jersey.client.ClientProperties.PROXY_PASSWORD; +import static org.glassfish.jersey.client.ClientProperties.PROXY_URI; +import static org.glassfish.jersey.client.ClientProperties.PROXY_USERNAME; + +class ProxyBuilder { + + static Optional createProxy(Configuration config) { + Object proxyUri = config.getProperty(PROXY_URI); + String userName = ClientProperties.getValue(config.getProperties(), PROXY_USERNAME, String.class); + String password = ClientProperties.getValue(config.getProperties(), PROXY_PASSWORD, String.class); + return createProxy(proxyUri, userName, password); + } + + static Optional createProxy(ClientRequest clientRequest) { + Object proxyUri = clientRequest.resolveProperty(PROXY_URI, Object.class); + String userName = clientRequest.resolveProperty(PROXY_USERNAME, String.class); + String password = clientRequest.resolveProperty(PROXY_PASSWORD, String.class); + return createProxy(proxyUri, userName, password); + } + + private ProxyBuilder() { + } + + private static Optional createProxy(Object proxyUri, String userName, String password) { + if (proxyUri != null) { + URI u = getProxyUri(proxyUri); + Proxy.Builder builder = Proxy.builder(); + if (u.getScheme().toUpperCase(Locale.ROOT).equals("DIRECT")) { + builder.type(Proxy.ProxyType.NONE); + } else { + builder.host(u.getHost()).port(u.getPort()); + if ("HTTP".equals(u.getScheme().toUpperCase(Locale.ROOT))) { + builder.type(Proxy.ProxyType.HTTP); + } else { + HelidonConnector.LOGGER.warning(String.format("Proxy schema %s not supported.", u.getScheme())); + return Optional.empty(); + } + } + if (userName != null) { + builder.username(userName); + if (password != null) { + builder.password(password.toCharArray()); + } + } + return Optional.of(builder.build()); + } else { + return Optional.empty(); + } + } + + 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("The proxy URI '" + proxy + "' MUST be String or URI"); + } + } +} diff --git a/jersey/connector/src/test/java/io/helidon/jersey/connector/ConfigTest.java b/jersey/connector/src/test/java/io/helidon/jersey/connector/ConfigTest.java index 6459de740dc..841a043ca63 100644 --- a/jersey/connector/src/test/java/io/helidon/jersey/connector/ConfigTest.java +++ b/jersey/connector/src/test/java/io/helidon/jersey/connector/ConfigTest.java @@ -16,8 +16,11 @@ package io.helidon.jersey.connector; +import java.util.Optional; + import io.helidon.config.Config; import io.helidon.config.ConfigSources; +import io.helidon.nima.webclient.Proxy; import io.helidon.nima.webclient.http1.Http1ClientRequest; import jakarta.ws.rs.client.Client; import jakarta.ws.rs.client.ClientBuilder; @@ -25,6 +28,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.CoreMatchers.is; @@ -64,4 +68,26 @@ void testConfigPropertyOverride() { Http1ClientRequest request = connector.client().get(); assertThat(request.followRedirects(), is(false)); } + + @Test + void testConfigNoProxy() { + Client client = ClientBuilder.newClient(); + HelidonConnector connector = new HelidonConnector(client, client.getConfiguration()); + assertThat(connector.proxy(), is(Proxy.noProxy())); + } + + @Test + void testConfigProxy() { + Client client = ClientBuilder.newBuilder() + .property(ClientProperties.PROXY_URI, "http://localhost:8080") + .property(ClientProperties.PROXY_USERNAME, "user") + .property(ClientProperties.PROXY_PASSWORD, "pass") + .build(); + HelidonConnector connector = new HelidonConnector(client, client.getConfiguration()); + assertThat(connector.proxy().host(), is("localhost")); + assertThat(connector.proxy().port(), is(8080)); + assertThat(connector.proxy().type(), is(Proxy.ProxyType.HTTP)); + assertThat(connector.proxy().username(), is(Optional.of("user"))); + assertThat(connector.proxy().password(), notNullValue()); + } } diff --git a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/Proxy.java b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/Proxy.java index 9df20ee4e62..f7a3393dba4 100644 --- a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/Proxy.java +++ b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/Proxy.java @@ -320,6 +320,24 @@ public Optional address(UriHelper uri) { } } + /** + * Returns the port. + * + * @return proxy port + */ + public int port() { + return port; + } + + /** + * Returns the host. + * + * @return proxy host + */ + public String host() { + return host; + } + /** * Returns an Optional with the username. * @return the username