Skip to content
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

Upgrade Google HTTP Client to 1.33.0 (Apache v2) #2120

Merged
merged 7 commits into from
Nov 4, 2019
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 4 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,10 @@ subprojects {
// For Google libraries, check <http-client-bom.version>, <google.auth.version>, <guava.version>,
// ... in https://github.com/googleapis/google-cloud-java/blob/master/google-cloud-clients/pom.xml
// for best compatibility.
GOOGLE_HTTP_CLIENT: '1.27.0',
GOOGLE_AUTH_LIBRARY_OAUTH2_HTTP: '0.16.2',
GUAVA: '28.0-jre',
GOOGLE_HTTP_CLIENT: '1.33.0',
GOOGLE_HTTP_CLIENT_APACHE_V2: '1.33.0',
GOOGLE_AUTH_LIBRARY_OAUTH2_HTTP: '0.18.0',
GUAVA: '28.1-jre',

COMMONS_COMPRESS: '1.19',
JACKSON_DATABIND: '2.9.10',
Expand Down
1 change: 1 addition & 0 deletions jib-core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ plugins {

dependencies {
implementation "com.google.http-client:google-http-client:${dependencyVersions.GOOGLE_HTTP_CLIENT}"
implementation "com.google.http-client:google-http-client-apache-v2:${dependencyVersions.GOOGLE_HTTP_CLIENT_APACHE_V2}"
implementation("com.google.auth:google-auth-library-oauth2-http:${dependencyVersions.GOOGLE_AUTH_LIBRARY_OAUTH2_HTTP}") {
chanseokoh marked this conversation as resolved.
Show resolved Hide resolved
exclude group: "com.google.http-client", module: "google-http-client"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,19 @@
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.ApacheHttpTransport;
import com.google.api.client.http.apache.v2.ApacheHttpTransport;
import com.google.api.client.util.SslUtils;
import com.google.cloud.tools.jib.api.LogEvent;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import java.io.IOException;
import java.net.ConnectException;
import java.net.URL;
import java.security.GeneralSecurityException;
import java.util.function.Consumer;
import java.util.function.Supplier;
import javax.net.ssl.SSLException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.HttpClientBuilder;

/**
* Thread-safe HTTP client that can automatically failover from secure HTTPS to insecure HTTPS or
Expand Down Expand Up @@ -86,55 +85,23 @@ private static HttpTransport getSecureHttpTransport() {
//
// A new ApacheHttpTransport needs to be created for each connection because otherwise HTTP
// connection persistence causes the connection to throw NoHttpResponseException.
ApacheHttpTransport transport = new ApacheHttpTransport();
addProxyCredentials(transport);
return transport;
return new ApacheHttpTransport();
}

private static HttpTransport getInsecureHttpTransport() {
try {
ApacheHttpTransport insecureTransport =
new ApacheHttpTransport.Builder().doNotValidateCertificate().build();
addProxyCredentials(insecureTransport);
return insecureTransport;
HttpClientBuilder httpClientBuilder =
ApacheHttpTransport.newDefaultHttpClientBuilder()
.setSSLSocketFactory(null) // creates new factory with the SSLContext given below
.setSSLContext(SslUtils.trustAllSSLContext())
.setSSLHostnameVerifier(new NoopHostnameVerifier());
// Do not use NetHttpTransport. See comments in getConnectionFactory for details.
return new ApacheHttpTransport(httpClientBuilder.build());
} catch (GeneralSecurityException ex) {
throw new RuntimeException("platform does not support TLS protocol", ex);
}
}

/**
* Registers proxy credentials onto transport client, in order to deal with proxies that require
* basic authentication.
*
* @param transport Apache HTTP transport
*/
@VisibleForTesting
static void addProxyCredentials(ApacheHttpTransport transport) {
addProxyCredentials(transport, "https");
addProxyCredentials(transport, "http");
}

private static void addProxyCredentials(ApacheHttpTransport transport, String protocol) {
Preconditions.checkArgument(protocol.equals("http") || protocol.equals("https"));

String proxyHost = System.getProperty(protocol + ".proxyHost");
String proxyUser = System.getProperty(protocol + ".proxyUser");
String proxyPassword = System.getProperty(protocol + ".proxyPassword");
if (proxyHost == null || proxyUser == null || proxyPassword == null) {
return;
}

String defaultProxyPort = protocol.equals("http") ? "80" : "443";
int proxyPort = Integer.parseInt(System.getProperty(protocol + ".proxyPort", defaultProxyPort));

DefaultHttpClient httpClient = (DefaultHttpClient) transport.getHttpClient();
httpClient
.getCredentialsProvider()
.setCredentials(
new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(proxyUser, proxyPassword));
}

private final boolean enableHttpAndInsecureFailover;
private final boolean sendAuthorizationOverHttp;
private final Consumer<LogEvent> logger;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,11 @@ private T call(URL url) throws IOException, RegistryException {
throw new RegistryUnauthorizedException(serverUrl, imageName, responseException);
}

} else if (responseException.getStatusCode()
== HttpStatusCodes.STATUS_CODE_TEMPORARY_REDIRECT
|| responseException.getStatusCode() == HttpStatusCodes.STATUS_CODE_MOVED_PERMANENTLY
|| responseException.getStatusCode() == STATUS_CODE_PERMANENT_REDIRECT) {
// 301 (Moved Permanently), 302 (Found), 303 (See Other), and 307 (Temporary Redirect) are
// automatically followed by Google HTTP Client (setFollowRedirects(true)), but 308 isn't.
// https://github.com/googleapis/google-http-java-client/issues/873
// TODO: remove this when the bug is fixed.
} else if (responseException.getStatusCode() == STATUS_CODE_PERMANENT_REDIRECT) {
// 'Location' header can be relative or absolute.
URL redirectLocation = new URL(url, responseException.getHeaders().getLocation());
return call(redirectLocation);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import javax.net.ssl.SSLException;
import org.hamcrest.CoreMatchers;
Expand Down Expand Up @@ -149,4 +150,43 @@ public void testProxyCredentialProperties()
}
}
}

@Test
public void testVerbatimRedirectionUrls()
throws IOException, InterruptedException, GeneralSecurityException, URISyntaxException {
String url1 = "?id=301&_auth_=exp=1572285389~hmac=f0a387f0";
chanseokoh marked this conversation as resolved.
Show resolved Hide resolved
String url2 = "?id=302&Signature=2wYOD0a%2BDAkK%2F9lQJUOuIpYti8o%3D&Expires=1569997614";
String url3 = "?id=303&_auth_=exp=1572285389~hmac=f0a387f0";
String url4 = "?id=307&Signature=2wYOD0a%2BDAkK%2F9lQJUOuIpYti8o%3D&Expires=1569997614";

String redirect301 =
"HTTP/1.1 301 Moved Permanently\nLocation: " + url1 + "\nContent-Length: 0\n\n";
String redirect302 = "HTTP/1.1 302 Found\nLocation: " + url2 + "\nContent-Length: 0\n\n";
String redirect303 = "HTTP/1.1 303 See Other\nLocation: " + url3 + "\nContent-Length: 0\n\n";
String redirect307 =
"HTTP/1.1 307 Temporary Redirect\nLocation: " + url4 + "\nContent-Length: 0\n\n";
String ok200 = "HTTP/1.1 200 OK\nContent-Length:12\n\nHello World!";
List<String> responses =
Arrays.asList(redirect301, redirect302, redirect303, redirect307, ok200);

FailoverHttpClient httpClient = new FailoverHttpClient(true /*insecure*/, false, logger);
try (TestWebServer server = new TestWebServer(false, responses, 1)) {
httpClient.get(new URL(server.getEndpoint()), request);

Assert.assertThat(
server.getInputRead(),
CoreMatchers.containsString("GET /?id=301&_auth_=exp%3D1572285389~hmac%3Df0a387f0 "));
Assert.assertThat(
server.getInputRead(),
CoreMatchers.containsString(
"GET /?id=302&Signature=2wYOD0a%2BDAkK/9lQJUOuIpYti8o%3D&Expires=1569997614 "));
Assert.assertThat(
server.getInputRead(),
CoreMatchers.containsString("GET /?id=303&_auth_=exp%3D1572285389~hmac%3Df0a387f0 "));
Assert.assertThat(
server.getInputRead(),
CoreMatchers.containsString(
"GET /?id=307&Signature=2wYOD0a%2BDAkK/9lQJUOuIpYti8o%3D&Expires=1569997614 "));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -242,18 +242,22 @@ public void testCall_unknown() throws IOException, RegistryException {
}

@Test
public void testCall_temporaryRedirect() throws IOException, RegistryException {
verifyRetriesWithNewLocation(HttpStatusCodes.STATUS_CODE_TEMPORARY_REDIRECT);
}
public void testCall_permanentRedirect() throws IOException, RegistryException {
ResponseException redirectException =
mockResponseException(
RegistryEndpointCaller.STATUS_CODE_PERMANENT_REDIRECT,
new HttpHeaders().setLocation("https://newlocation"));

@Test
public void testCall_movedPermanently() throws IOException, RegistryException {
verifyRetriesWithNewLocation(HttpStatusCodes.STATUS_CODE_MOVED_PERMANENTLY);
}
// Make httpClient.call() throw first, then succeed.
setUpRegistryResponse(redirectException);
Mockito.when(
mockHttpClient.call(
Mockito.eq("httpMethod"),
Mockito.eq(new URL("https://newlocation")),
Mockito.any()))
.thenReturn(mockResponse);

@Test
public void testCall_permanentRedirect() throws IOException, RegistryException {
verifyRetriesWithNewLocation(RegistryEndpointCaller.STATUS_CODE_PERMANENT_REDIRECT);
Assert.assertEquals("body", endpointCaller.call());
}

@Test
Expand Down Expand Up @@ -471,28 +475,6 @@ private void verifyThrowsRegistryErrorException(int httpStatusCode)
}
}

/**
* Verifies that a response with {@code httpStatusCode} retries the request with the {@code
* Location} header.
*/
private void verifyRetriesWithNewLocation(int httpStatusCode)
throws IOException, RegistryException {
// Mocks a response for temporary redirect to a new location.
ResponseException redirectException =
mockResponseException(httpStatusCode, new HttpHeaders().setLocation("https://newlocation"));

// Make httpClient.call() throw first, then succeed.
setUpRegistryResponse(redirectException);
Mockito.when(
mockHttpClient.call(
Mockito.eq("httpMethod"),
Mockito.eq(new URL("https://newlocation")),
Mockito.any()))
.thenReturn(mockResponse);

Assert.assertEquals("body", endpointCaller.call());
}

private void setUpRegistryResponse(Exception exceptionToThrow)
throws MalformedURLException, IOException {
Mockito.when(
Expand Down