Skip to content

fix #2458 add HTTP proxy support to UrlConnectionHttpClient #2986

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed

fix #2458 add HTTP proxy support to UrlConnectionHttpClient #2986

wants to merge 2 commits into from

Conversation

MikeDombo
Copy link

@MikeDombo MikeDombo commented Jan 24, 2022

Motivation and Context

Resolves #2458. The goal of this change is to add full proxy support to the UrlConnectionHttpClient so that our project can remove the Apache HTTP Client and instead rely on Java builtins. This change must support HTTP and HTTPS proxies with username/password authorization.

Modifications

Adds HTTP proxy support to the UrlConnectionHttpClient by using largely the same ProxyConfiguration class as is in the Apache HTTP Client. If the user configures a proxy using the builder, and the request isn't going to a non-proxy host, then the request will go through the application proxy using Java's builtin Proxy class.

Testing

Manually tested using a local squid proxy via HTTP without authentication.

Screenshots (if appropriate)

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING document
  • Local run of mvn install succeeds
  • My code follows the code style of this project
  • My change requires a change to the Javadoc documentation
  • I have updated the Javadoc documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed
  • I have added a changelog entry. Adding a new entry must be accomplished by running the scripts/new-change script and following the instructions. Commit the new file created by the script in .changes/next-release with your changes.
  • My change is to implement 1.11 parity feature and I have updated LaunchChangelog

License

  • I confirm that this pull request can be released under the Apache 2 license

@sonarqubecloud
Copy link

SonarCloud Quality Gate failed.    Quality Gate failed

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 2 Code Smells

8.2% 8.2% Coverage
38.4% 38.4% Duplication

@MikeDombo MikeDombo marked this pull request as ready for review January 27, 2022 21:58
@MikeDombo MikeDombo requested a review from a team as a code owner January 27, 2022 21:58
@debora-ito
Copy link
Member

@MikeDombo can you please add unit tests?

* Returns the Java system property for nonProxyHosts as set of Strings.
* See http://docs.oracle.com/javase/7/docs/api/java/net/doc-files/net-properties.html.
*/
private Set<String> parseNonProxyHostsProperty() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is Duplicate function same as software.amazon.awssdk.http.apache.ProxyConfiguration can we move this to Utils like in function ApacheUtils ?

import software.amazon.awssdk.utils.builder.ToCopyableBuilder;

/**
* Configuration that defines how to communicate via an HTTP proxy.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have more descriptive Java Doc for this class similar to one ProxyConfiguration of NettyNioAsyncHttpClient

String authToken = String.format("%s:%s", this.proxyConfiguration.username(), this.proxyConfiguration.password());
String authB64 = Base64.getEncoder().encodeToString(authToken.getBytes(StandardCharsets.UTF_8));
connection.addRequestProperty("proxy-authorization", String.format("Basic %s", authB64));
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a way to test this in junit or Integration tests ?
Did you get a chance to test this one , please let us know the steps.
We can discuss this offline on slack,

/**
* Configuration that defines how to communicate via an HTTP proxy.
*/
Builder proxyConfiguration(ProxyConfiguration proxyConfiguration);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add Builder with consumer also ?

@@ -70,12 +75,16 @@

private static final Logger log = Logger.loggerFor(UrlConnectionHttpClient.class);
private static final String CLIENT_NAME = "UrlConnection";
private static final AttributeMap.Key<ProxyConfiguration> PROXY_CONFIGURATION =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To be consistent with other http client lets assign the ProxyConfiguration directly to the class Field as done in other client constructors , I didnot get the benefit of assigning it to a AttributeMap and then doing a get.

Can we do something similar to ApacheHttpClient ?

@@ -148,7 +157,17 @@ private HttpURLConnection createAndConfigureConnection(HttpExecuteRequest reques
}

private HttpURLConnection createDefaultConnection(URI uri, SSLSocketFactory socketFactory) {
HttpURLConnection connection = invokeSafely(() -> (HttpURLConnection) uri.toURL().openConnection());
HttpURLConnection connection;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we simplify the code as below ?

    private HttpURLConnection createDefaultConnection(URI uri, SSLSocketFactory socketFactory) {

        Optional<Proxy> proxy = determineProxy(uri);

        HttpURLConnection connection = !proxy.isPresent() ?
                                       invokeSafely(() -> (HttpURLConnection) uri.toURL().openConnection())
                                                          :
                                       invokeSafely(() -> (HttpURLConnection) uri.toURL().openConnection(proxy.get()));

        if (connection instanceof HttpsURLConnection) {
            HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;

            if (options.get(SdkHttpConfigurationOption.TRUST_ALL_CERTIFICATES)) {
                httpsConnection.setHostnameVerifier(NoOpHostNameVerifier.INSTANCE);
            }
            httpsConnection.setSSLSocketFactory(socketFactory);
        }

        if (proxy.isPresent() && shouldProxyAuthorize()){
            connection.addRequestProperty("proxy-authorization", String.format("Basic %s", encodedAuthToken(proxyConfiguration)));
        }
        connection.setConnectTimeout(saturatedCast(options.get(SdkHttpConfigurationOption.CONNECTION_TIMEOUT).toMillis()));
        connection.setReadTimeout(saturatedCast(options.get(SdkHttpConfigurationOption.READ_TIMEOUT).toMillis()));

        return connection;
    }

    private boolean shouldProxyAuthorize() {
        return this.proxyConfiguration != null 
               && StringUtils.isEmpty(this.proxyConfiguration.username()) 
               && StringUtils.isEmpty(this.proxyConfiguration.password());
    }


    /**
     * If a proxy is configured with username+password, then set the proxy-authorization header to authorize ourselves with the
     * proxy
     */
    private static String encodedAuthToken(ProxyConfiguration proxyConfiguration) {

        String authToken = String.format("%s:%s", proxyConfiguration.username(), proxyConfiguration.password());
        return Base64.getEncoder().encodeToString(authToken.getBytes(StandardCharsets.UTF_8));
    }

    private Optional<Proxy> determineProxy(URI uri) {
        if (isProxyEnabled(proxyConfiguration) && !isProxyHostExcluded(uri)) {
            return Optional.of(
                new Proxy(Proxy.Type.HTTP,
                          new InetSocketAddress(this.proxyConfiguration.host(), this.proxyConfiguration.port())));
        }
        return Optional.empty();
    }

@debora-ito
Copy link
Member

Superseded by #3112. Closing.

@debora-ito debora-ito closed this Apr 18, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

UrlConnectionHttpClient should have builder methods for setting a Proxy
3 participants