|
16 | 16 | package com.vaadin.flow.server.frontend.installer;
|
17 | 17 |
|
18 | 18 | import java.io.File;
|
19 |
| -import java.io.FileOutputStream; |
20 | 19 | import java.io.IOException;
|
| 20 | +import java.net.Authenticator; |
| 21 | +import java.net.InetSocketAddress; |
| 22 | +import java.net.PasswordAuthentication; |
| 23 | +import java.net.ProxySelector; |
21 | 24 | import java.net.URI;
|
22 |
| -import java.net.URL; |
23 |
| -import java.nio.channels.Channels; |
24 |
| -import java.nio.channels.ReadableByteChannel; |
| 25 | +import java.net.http.HttpClient; |
| 26 | +import java.net.http.HttpClient.Redirect; |
| 27 | +import java.net.http.HttpClient.Version; |
| 28 | +import java.net.http.HttpRequest; |
| 29 | +import java.net.http.HttpResponse; |
| 30 | +import java.net.http.HttpResponse.BodyHandlers; |
| 31 | +import java.nio.file.Path; |
| 32 | +import java.time.Duration; |
25 | 33 |
|
26 | 34 | import org.apache.commons.io.FileUtils;
|
27 |
| -import org.apache.commons.io.FilenameUtils; |
28 |
| -import org.apache.http.HttpEntity; |
29 |
| -import org.apache.http.HttpHost; |
30 |
| -import org.apache.http.auth.AuthScope; |
31 |
| -import org.apache.http.auth.UsernamePasswordCredentials; |
32 |
| -import org.apache.http.client.AuthCache; |
33 |
| -import org.apache.http.client.CredentialsProvider; |
34 |
| -import org.apache.http.client.config.RequestConfig; |
35 |
| -import org.apache.http.client.methods.CloseableHttpResponse; |
36 |
| -import org.apache.http.client.methods.HttpGet; |
37 |
| -import org.apache.http.client.protocol.HttpClientContext; |
38 |
| -import org.apache.http.impl.auth.BasicScheme; |
39 |
| -import org.apache.http.impl.client.BasicAuthCache; |
40 |
| -import org.apache.http.impl.client.BasicCredentialsProvider; |
41 |
| -import org.apache.http.impl.client.CloseableHttpClient; |
42 |
| -import org.apache.http.impl.client.HttpClients; |
43 | 35 | import org.slf4j.Logger;
|
44 | 36 | import org.slf4j.LoggerFactory;
|
45 | 37 |
|
|
53 | 45 | * @since
|
54 | 46 | */
|
55 | 47 | public final class DefaultFileDownloader implements FileDownloader {
|
| 48 | + |
56 | 49 | public static final String HTTPS_PROTOCOLS = "https.protocols";
|
57 | 50 |
|
58 | 51 | private final ProxyConfig proxyConfig;
|
@@ -99,110 +92,69 @@ public void download(URI downloadURI, File destination, String userName,
|
99 | 92 |
|
100 | 93 | private void downloadFile(File destination, URI downloadUri)
|
101 | 94 | throws IOException, DownloadException {
|
102 |
| - CloseableHttpResponse response = execute(downloadUri); |
103 |
| - int statusCode = response.getStatusLine().getStatusCode(); |
104 |
| - if (statusCode != 200) { |
105 |
| - throw new DownloadException( |
106 |
| - "Got error code " + statusCode + " from the server."); |
107 |
| - } |
108 |
| - new File( |
109 |
| - FilenameUtils.getFullPathNoEndSeparator(destination.toString())) |
110 |
| - .mkdirs(); |
111 |
| - |
112 |
| - HttpEntity responseEntity = response.getEntity(); |
113 |
| - long expected = responseEntity.getContentLength(); |
114 |
| - try (ReadableByteChannel rbc = Channels |
115 |
| - .newChannel(responseEntity.getContent()); |
116 |
| - FileOutputStream fos = new FileOutputStream(destination)) { |
117 |
| - long transferred = fos.getChannel().transferFrom(rbc, 0, |
118 |
| - Long.MAX_VALUE); |
119 |
| - if (expected > 0 && transferred != expected) { |
120 |
| - // Download failed and channel.transferFrom does not rethrow the |
121 |
| - // exception |
122 |
| - throw new DownloadException( |
123 |
| - "Error downloading from " + downloadUri + ". Expected " |
124 |
| - + expected + " bytes but got " + transferred); |
125 |
| - } |
126 |
| - } |
127 | 95 |
|
128 |
| - } |
| 96 | + HttpClient.Builder clientBuilder = HttpClient.newBuilder() |
| 97 | + .version(Version.HTTP_1_1).followRedirects(Redirect.NORMAL); |
129 | 98 |
|
130 |
| - private CloseableHttpResponse execute(URI requestUri) throws IOException { |
131 |
| - CloseableHttpResponse response; |
132 | 99 | ProxyConfig.Proxy proxy = proxyConfig
|
133 |
| - .getProxyForUrl(requestUri.toString()); |
| 100 | + .getProxyForUrl(downloadUri.toString()); |
| 101 | + |
134 | 102 | if (proxy != null) {
|
135 |
| - getLogger().info("Downloading via proxy {}", proxy.toString()); |
136 |
| - return executeViaProxy(proxy, requestUri); |
| 103 | + getLogger().debug("Downloading via proxy {}", proxy.toString()); |
| 104 | + clientBuilder = clientBuilder.proxy(ProxySelector |
| 105 | + .of(new InetSocketAddress(proxy.host, proxy.port))); |
| 106 | + clientBuilder = clientBuilder.authenticator(new Authenticator() { |
| 107 | + @Override |
| 108 | + protected PasswordAuthentication getPasswordAuthentication() { |
| 109 | + if (getRequestorType() == RequestorType.PROXY) { |
| 110 | + return new PasswordAuthentication(proxy.username, |
| 111 | + proxy.password.toCharArray()); |
| 112 | + } |
| 113 | + return new PasswordAuthentication(userName, |
| 114 | + password.toCharArray()); |
| 115 | + } |
| 116 | + }); |
137 | 117 | } else {
|
138 |
| - getLogger().info("No proxy was configured, downloading directly"); |
| 118 | + getLogger().debug("No proxy was configured, downloading directly"); |
139 | 119 | if (userName != null && !userName.isEmpty() && password != null
|
140 | 120 | && !password.isEmpty()) {
|
141 | 121 | getLogger().info("Using credentials ({})", userName);
|
142 |
| - // Auth target host |
143 |
| - URL aURL = requestUri.toURL(); |
144 |
| - HttpClientContext localContext = makeLocalContext(aURL); |
145 |
| - CredentialsProvider credentialsProvider = makeCredentialsProvider( |
146 |
| - aURL.getHost(), aURL.getPort(), userName, password); |
147 |
| - response = buildHttpClient(credentialsProvider) |
148 |
| - .execute(new HttpGet(requestUri), localContext); |
149 |
| - } else { |
150 |
| - response = buildHttpClient(null) |
151 |
| - .execute(new HttpGet(requestUri)); |
152 |
| - } |
153 |
| - } |
154 |
| - return response; |
155 |
| - } |
| 122 | + clientBuilder = clientBuilder |
| 123 | + .authenticator(new Authenticator() { |
| 124 | + @Override |
| 125 | + protected PasswordAuthentication getPasswordAuthentication() { |
| 126 | + return new PasswordAuthentication(userName, |
| 127 | + password.toCharArray()); |
| 128 | + } |
| 129 | + }); |
156 | 130 |
|
157 |
| - private CloseableHttpResponse executeViaProxy(ProxyConfig.Proxy proxy, |
158 |
| - URI requestUri) throws IOException { |
159 |
| - final CloseableHttpClient proxyClient; |
160 |
| - if (proxy.useAuthentication()) { |
161 |
| - proxyClient = buildHttpClient(makeCredentialsProvider(proxy.host, |
162 |
| - proxy.port, proxy.username, proxy.password)); |
163 |
| - } else { |
164 |
| - proxyClient = buildHttpClient(null); |
| 131 | + } |
165 | 132 | }
|
166 | 133 |
|
167 |
| - final HttpHost proxyHttpHost = new HttpHost(proxy.host, proxy.port); |
168 |
| - |
169 |
| - final RequestConfig requestConfig = RequestConfig.custom() |
170 |
| - .setProxy(proxyHttpHost).build(); |
171 |
| - |
172 |
| - final HttpGet request = new HttpGet(requestUri); |
173 |
| - request.setConfig(requestConfig); |
| 134 | + HttpClient client = clientBuilder.build(); |
| 135 | + HttpRequest request = HttpRequest.newBuilder().uri(downloadUri).GET() |
| 136 | + .build(); |
174 | 137 |
|
175 |
| - return proxyClient.execute(request); |
176 |
| - } |
177 |
| - |
178 |
| - private CloseableHttpClient buildHttpClient( |
179 |
| - CredentialsProvider credentialsProvider) { |
180 |
| - return HttpClients.custom().disableContentCompression() |
181 |
| - .useSystemProperties() |
182 |
| - .setDefaultCredentialsProvider(credentialsProvider).build(); |
183 |
| - } |
| 138 | + try { |
| 139 | + HttpResponse<Path> response = client.send(request, |
| 140 | + BodyHandlers.ofFile(destination.toPath())); |
| 141 | + if (response.statusCode() != 200) { |
| 142 | + throw new DownloadException("Got error code " |
| 143 | + + response.statusCode() + " from the server."); |
| 144 | + } |
| 145 | + long expected = response.headers() |
| 146 | + .firstValueAsLong("Content-Length").getAsLong(); |
| 147 | + if (destination.length() != expected) { |
| 148 | + throw new DownloadException("Error downloading from " |
| 149 | + + downloadUri + ". Expected " + expected |
| 150 | + + " bytes but got " + destination.length()); |
| 151 | + } |
184 | 152 |
|
185 |
| - private CredentialsProvider makeCredentialsProvider(String host, int port, |
186 |
| - String username, String password) { |
187 |
| - CredentialsProvider credentialsProvider = new BasicCredentialsProvider(); |
188 |
| - credentialsProvider.setCredentials(new AuthScope(host, port), |
189 |
| - new UsernamePasswordCredentials(username, password)); |
190 |
| - return credentialsProvider; |
191 |
| - } |
| 153 | + } catch (InterruptedException ex) { |
| 154 | + Thread.currentThread().interrupt(); |
| 155 | + throw new RuntimeException(ex); |
| 156 | + } |
192 | 157 |
|
193 |
| - private HttpClientContext makeLocalContext(URL requestUrl) { |
194 |
| - // Auth target host |
195 |
| - HttpHost target = new HttpHost(requestUrl.getHost(), |
196 |
| - requestUrl.getPort(), requestUrl.getProtocol()); |
197 |
| - // Create AuthCache instance |
198 |
| - AuthCache authCache = new BasicAuthCache(); |
199 |
| - // Generate BASIC scheme object and add it to the local auth cache |
200 |
| - BasicScheme basicAuth = new BasicScheme(); |
201 |
| - authCache.put(target, basicAuth); |
202 |
| - // Add AuthCache to the execution context |
203 |
| - HttpClientContext localContext = HttpClientContext.create(); |
204 |
| - localContext.setAuthCache(authCache); |
205 |
| - return localContext; |
206 | 158 | }
|
207 | 159 |
|
208 | 160 | private Logger getLogger() {
|
|
0 commit comments