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

chore: use SDK http client instead of a separate http-client dependency (#16897) (CP: 24.1) #17656

Merged
merged 1 commit into from
Sep 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions flow-client/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,25 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
<exclusions>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
</exclusions>
<scope>provided</scope>
</dependency>
<!-- Overrides the dependency version of httpclient -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.gwt</groupId>
<artifactId>gwt-user</artifactId>
Expand Down
17 changes: 0 additions & 17 deletions flow-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,23 +130,6 @@
<artifactId>commons-compress</artifactId>
<version>1.24.0</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.14</version>
<exclusions>
<exclusion>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Overrides the dependency version of httpclient -->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
<!-- TESTING DEPENDENCIES -->
<dependency>
<groupId>com.vaadin</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,30 +16,22 @@
package com.vaadin.flow.server.frontend.installer;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.net.http.HttpClient;
import java.net.http.HttpClient.Redirect;
import java.net.http.HttpClient.Version;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import java.nio.file.Path;
import java.time.Duration;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.AuthCache;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.BasicAuthCache;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand All @@ -53,6 +45,7 @@
* @since
*/
public final class DefaultFileDownloader implements FileDownloader {

public static final String HTTPS_PROTOCOLS = "https.protocols";

private final ProxyConfig proxyConfig;
Expand Down Expand Up @@ -99,110 +92,69 @@ public void download(URI downloadURI, File destination, String userName,

private void downloadFile(File destination, URI downloadUri)
throws IOException, DownloadException {
CloseableHttpResponse response = execute(downloadUri);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != 200) {
throw new DownloadException(
"Got error code " + statusCode + " from the server.");
}
new File(
FilenameUtils.getFullPathNoEndSeparator(destination.toString()))
.mkdirs();

HttpEntity responseEntity = response.getEntity();
long expected = responseEntity.getContentLength();
try (ReadableByteChannel rbc = Channels
.newChannel(responseEntity.getContent());
FileOutputStream fos = new FileOutputStream(destination)) {
long transferred = fos.getChannel().transferFrom(rbc, 0,
Long.MAX_VALUE);
if (expected > 0 && transferred != expected) {
// Download failed and channel.transferFrom does not rethrow the
// exception
throw new DownloadException(
"Error downloading from " + downloadUri + ". Expected "
+ expected + " bytes but got " + transferred);
}
}

}
HttpClient.Builder clientBuilder = HttpClient.newBuilder()
.version(Version.HTTP_1_1).followRedirects(Redirect.NORMAL);

private CloseableHttpResponse execute(URI requestUri) throws IOException {
CloseableHttpResponse response;
ProxyConfig.Proxy proxy = proxyConfig
.getProxyForUrl(requestUri.toString());
.getProxyForUrl(downloadUri.toString());

if (proxy != null) {
getLogger().info("Downloading via proxy {}", proxy.toString());
return executeViaProxy(proxy, requestUri);
getLogger().debug("Downloading via proxy {}", proxy.toString());
clientBuilder = clientBuilder.proxy(ProxySelector
.of(new InetSocketAddress(proxy.host, proxy.port)));
clientBuilder = clientBuilder.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication(proxy.username,
proxy.password.toCharArray());
}
return new PasswordAuthentication(userName,
password.toCharArray());
}
});
} else {
getLogger().info("No proxy was configured, downloading directly");
getLogger().debug("No proxy was configured, downloading directly");
if (userName != null && !userName.isEmpty() && password != null
&& !password.isEmpty()) {
getLogger().info("Using credentials ({})", userName);
// Auth target host
URL aURL = requestUri.toURL();
HttpClientContext localContext = makeLocalContext(aURL);
CredentialsProvider credentialsProvider = makeCredentialsProvider(
aURL.getHost(), aURL.getPort(), userName, password);
response = buildHttpClient(credentialsProvider)
.execute(new HttpGet(requestUri), localContext);
} else {
response = buildHttpClient(null)
.execute(new HttpGet(requestUri));
}
}
return response;
}
clientBuilder = clientBuilder
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(userName,
password.toCharArray());
}
});

private CloseableHttpResponse executeViaProxy(ProxyConfig.Proxy proxy,
URI requestUri) throws IOException {
final CloseableHttpClient proxyClient;
if (proxy.useAuthentication()) {
proxyClient = buildHttpClient(makeCredentialsProvider(proxy.host,
proxy.port, proxy.username, proxy.password));
} else {
proxyClient = buildHttpClient(null);
}
}

final HttpHost proxyHttpHost = new HttpHost(proxy.host, proxy.port);

final RequestConfig requestConfig = RequestConfig.custom()
.setProxy(proxyHttpHost).build();

final HttpGet request = new HttpGet(requestUri);
request.setConfig(requestConfig);
HttpClient client = clientBuilder.build();
HttpRequest request = HttpRequest.newBuilder().uri(downloadUri).GET()
.build();

return proxyClient.execute(request);
}

private CloseableHttpClient buildHttpClient(
CredentialsProvider credentialsProvider) {
return HttpClients.custom().disableContentCompression()
.useSystemProperties()
.setDefaultCredentialsProvider(credentialsProvider).build();
}
try {
HttpResponse<Path> response = client.send(request,
BodyHandlers.ofFile(destination.toPath()));
if (response.statusCode() != 200) {
throw new DownloadException("Got error code "
+ response.statusCode() + " from the server.");
}
long expected = response.headers()
.firstValueAsLong("Content-Length").getAsLong();
if (destination.length() != expected) {
throw new DownloadException("Error downloading from "
+ downloadUri + ". Expected " + expected
+ " bytes but got " + destination.length());
}

private CredentialsProvider makeCredentialsProvider(String host, int port,
String username, String password) {
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(new AuthScope(host, port),
new UsernamePasswordCredentials(username, password));
return credentialsProvider;
}
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
throw new RuntimeException(ex);
}

private HttpClientContext makeLocalContext(URL requestUrl) {
// Auth target host
HttpHost target = new HttpHost(requestUrl.getHost(),
requestUrl.getPort(), requestUrl.getProtocol());
// Create AuthCache instance
AuthCache authCache = new BasicAuthCache();
// Generate BASIC scheme object and add it to the local auth cache
BasicScheme basicAuth = new BasicScheme();
authCache.put(target, basicAuth);
// Add AuthCache to the execution context
HttpClientContext localContext = HttpClientContext.create();
localContext.setAuthCache(authCache);
return localContext;
}

private Logger getLogger() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ protected Stream<String> getExcludedPatterns() {
// Node downloader classes
"com\\.vaadin\\.flow\\.server\\.frontend\\.installer\\.DefaultArchiveExtractor",
"com\\.vaadin\\.flow\\.server\\.frontend\\.installer\\.ArchiveExtractor",
"com\\.vaadin\\.flow\\.server\\.frontend\\.installer\\.DefaultFileDownloader",
"com\\.vaadin\\.flow\\.server\\.frontend\\.installer\\.DefaultFileDownloader(\\$.*)?",
"com\\.vaadin\\.flow\\.server\\.frontend\\.installer\\.FileDownloader",
"com\\.vaadin\\.flow\\.server\\.frontend\\.installer\\.NodeInstaller",
"com\\.vaadin\\.flow\\.server\\.frontend\\.installer\\.NodeInstaller\\$InstallData",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
import java.util.UUID;
import java.util.stream.Stream;

import org.apache.commons.codec.binary.Hex;
import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -107,14 +106,26 @@ static String createHash(String string) {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(string.getBytes(StandardCharsets.UTF_8));
byte[] digest = md.digest();
return Hex.encodeHexString(digest);
return toHexString(digest);
} catch (Exception e) {
getLogger().debug("Missing hash algorithm", e);
}
}
return StatisticsConstants.MISSING_DATA;
}

private static String toHexString(byte[] bytes) {
StringBuilder hexString = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(0xFF & bytes[i]);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
}

/**
* Get the source URL for the project.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,11 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -281,20 +278,21 @@ String sendStatistics(ObjectNode json) {
private static ObjectNode postData(String postUrl, String data) {
ObjectNode result;
try {
HttpPost post = new HttpPost(postUrl);
post.addHeader("Content-Type", "application/json");
post.setEntity(new StringEntity(data));

HttpClient client = HttpClientBuilder.create().build();
HttpResponse response = client.execute(post);
String responseStatus = response.getStatusLine().getStatusCode()
+ ": " + response.getStatusLine().getReasonPhrase();
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(postUrl))
.POST(HttpRequest.BodyPublishers.ofString(data))
.header("Content-Type", "application/json").build();

HttpResponse<String> response = client.send(request,
HttpResponse.BodyHandlers.ofString());

int statusCode = response.statusCode();

JsonNode jsonResponse = null;
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
String responseString = EntityUtils
.toString(response.getEntity());
if (statusCode == 200) {
jsonResponse = JsonHelpers.getJsonMapper()
.readTree(responseString);
.readTree(response.body());
}

if (jsonResponse != null && jsonResponse.isObject()) {
Expand All @@ -304,11 +302,15 @@ private static ObjectNode postData(String postUrl, String data) {
result = JsonHelpers.getJsonMapper().createObjectNode();
}
// Update the status and return the results
result.put(StatisticsConstants.FIELD_LAST_STATUS, responseStatus);
result.put(StatisticsConstants.FIELD_LAST_STATUS,
statusCode + ": "); // TODO is reason phrase needed here
return result;

} catch (IOException e) {
getLogger().debug("Failed to send statistics.", e);
} catch (IOException ex) {
getLogger().debug("Failed to send statistics.", ex);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
getLogger().debug("Failed to send statistics.", ex);
}

// Fallback
Expand Down