1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.transport.jetty;
20
21 import javax.net.ssl.SSLContext;
22
23 import java.io.IOException;
24 import java.io.InputStream;
25 import java.net.URI;
26 import java.net.URISyntaxException;
27 import java.nio.file.Files;
28 import java.nio.file.Path;
29 import java.nio.file.StandardCopyOption;
30 import java.security.NoSuchAlgorithmException;
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.HashMap;
34 import java.util.Map;
35 import java.util.TreeSet;
36 import java.util.concurrent.ExecutionException;
37 import java.util.concurrent.TimeUnit;
38 import java.util.concurrent.atomic.AtomicBoolean;
39 import java.util.concurrent.atomic.AtomicReference;
40 import java.util.function.Function;
41 import java.util.regex.Matcher;
42
43 import org.eclipse.aether.ConfigurationProperties;
44 import org.eclipse.aether.ConfigurationProperties.HttpVersion;
45 import org.eclipse.aether.RepositorySystemSession;
46 import org.eclipse.aether.repository.AuthenticationContext;
47 import org.eclipse.aether.repository.RemoteRepository;
48 import org.eclipse.aether.spi.connector.transport.AbstractTransporter;
49 import org.eclipse.aether.spi.connector.transport.GetTask;
50 import org.eclipse.aether.spi.connector.transport.PeekTask;
51 import org.eclipse.aether.spi.connector.transport.PutTask;
52 import org.eclipse.aether.spi.connector.transport.TransportTask;
53 import org.eclipse.aether.spi.connector.transport.http.ChecksumExtractor;
54 import org.eclipse.aether.spi.connector.transport.http.HttpTransportPropertiesBuilder;
55 import org.eclipse.aether.spi.connector.transport.http.HttpTransporter;
56 import org.eclipse.aether.spi.connector.transport.http.HttpTransporterException;
57 import org.eclipse.aether.spi.io.PathProcessor;
58 import org.eclipse.aether.transfer.HttpTransportProperty;
59 import org.eclipse.aether.transfer.NoTransporterException;
60 import org.eclipse.aether.transfer.TransferCancelledException;
61 import org.eclipse.aether.transfer.TransferEvent;
62 import org.eclipse.aether.util.ConfigUtils;
63 import org.eclipse.aether.util.connector.transport.http.HttpTransporterUtils;
64 import org.eclipse.jetty.client.Authentication;
65 import org.eclipse.jetty.client.BasicAuthentication;
66 import org.eclipse.jetty.client.HttpClient;
67 import org.eclipse.jetty.client.HttpProxy;
68 import org.eclipse.jetty.client.InputStreamResponseListener;
69 import org.eclipse.jetty.client.Request;
70 import org.eclipse.jetty.client.Response;
71 import org.eclipse.jetty.client.transport.HttpClientConnectionFactory;
72 import org.eclipse.jetty.client.transport.HttpClientTransportDynamic;
73 import org.eclipse.jetty.http.HttpField;
74 import org.eclipse.jetty.http.HttpHeader;
75 import org.eclipse.jetty.http2.client.HTTP2Client;
76 import org.eclipse.jetty.http2.client.transport.ClientConnectionFactoryOverHTTP2;
77 import org.eclipse.jetty.http3.client.HTTP3Client;
78 import org.eclipse.jetty.http3.client.HTTP3ClientQuicConfiguration;
79 import org.eclipse.jetty.http3.client.transport.ClientConnectionFactoryOverHTTP3;
80 import org.eclipse.jetty.io.ClientConnectionFactory;
81 import org.eclipse.jetty.io.ClientConnector;
82 import org.eclipse.jetty.io.EndPoint.SslSessionData;
83 import org.eclipse.jetty.quic.quiche.client.QuicheClientQuicConfiguration;
84 import org.eclipse.jetty.quic.quiche.client.QuicheTransport;
85 import org.eclipse.jetty.util.ssl.SslContextFactory;
86
87 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.ACCEPT_ENCODING;
88 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.CONTENT_LENGTH;
89 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.CONTENT_RANGE;
90 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.CONTENT_RANGE_PATTERN;
91 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.IF_UNMODIFIED_SINCE;
92 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.LAST_MODIFIED;
93 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.MULTIPLE_CHOICES;
94 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.PRECONDITION_FAILED;
95 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.RANGE;
96 import static org.eclipse.aether.spi.connector.transport.http.HttpConstants.USER_AGENT;
97
98
99
100
101
102
103 final class JettyTransporter extends AbstractTransporter implements HttpTransporter {
104 private static final long MODIFICATION_THRESHOLD = 60L * 1000L;
105
106 private final RepositorySystemSession session;
107
108 private final RemoteRepository repository;
109
110 private final ChecksumExtractor checksumExtractor;
111
112 private final PathProcessor pathProcessor;
113
114 private final URI baseUri;
115
116 private final HttpClient client;
117
118 private final int connectTimeout;
119
120 private final int requestTimeout;
121
122 private final Map<String, String> headers;
123
124 private final boolean preemptiveAuth;
125
126 private final boolean preemptivePutAuth;
127
128 private final boolean sendRfc9457Accept;
129
130 private final boolean insecure;
131
132 private final AtomicReference<BasicAuthentication.BasicResult> basicServerAuthenticationResult;
133
134 private final AtomicReference<BasicAuthentication.BasicResult> basicProxyAuthenticationResult;
135
136 JettyTransporter(
137 RepositorySystemSession session,
138 RemoteRepository repository,
139 ChecksumExtractor checksumExtractor,
140 PathProcessor pathProcessor)
141 throws NoTransporterException {
142 this.session = session;
143 this.repository = repository;
144 this.checksumExtractor = checksumExtractor;
145 this.pathProcessor = pathProcessor;
146 try {
147 this.baseUri = HttpTransporterUtils.getBaseUri(repository);
148 } catch (URISyntaxException e) {
149 throw new NoTransporterException(repository, e.getMessage(), e);
150 }
151
152 HashMap<String, String> headers = new HashMap<>();
153 String userAgent = HttpTransporterUtils.getUserAgent(session, repository);
154 if (userAgent != null) {
155 headers.put(USER_AGENT, userAgent);
156 }
157 Map<String, String> configuredHeaders = HttpTransporterUtils.getHttpHeaders(session, repository);
158 if (configuredHeaders != null) {
159 headers.putAll(configuredHeaders);
160 }
161
162 this.headers = headers;
163
164 this.connectTimeout = HttpTransporterUtils.getHttpConnectTimeout(session, repository);
165 this.requestTimeout = HttpTransporterUtils.getHttpRequestTimeout(session, repository);
166 this.preemptiveAuth = HttpTransporterUtils.isHttpPreemptiveAuth(session, repository);
167 this.preemptivePutAuth = HttpTransporterUtils.isHttpPreemptivePutAuth(session, repository);
168 this.sendRfc9457Accept = HttpTransporterUtils.isHttpSendRfc9457Accept(session, repository);
169 final String httpsSecurityMode = HttpTransporterUtils.getHttpsSecurityMode(session, repository);
170 this.insecure = ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(httpsSecurityMode);
171
172 this.basicServerAuthenticationResult = new AtomicReference<>(null);
173 this.basicProxyAuthenticationResult = new AtomicReference<>(null);
174 this.client = createClient();
175 }
176
177 private void mayApplyPreemptiveAuth(Request request) {
178 if (basicServerAuthenticationResult.get() != null) {
179 basicServerAuthenticationResult.get().apply(request);
180 }
181 if (basicProxyAuthenticationResult.get() != null) {
182 basicProxyAuthenticationResult.get().apply(request);
183 }
184 }
185
186 private URI resolve(TransportTask task) {
187 return baseUri.resolve(task.getLocation());
188 }
189
190 @Override
191 protected void implPeek(PeekTask task) throws Exception {
192 Request request = client.newRequest(resolve(task)).method("HEAD");
193 request.headers(m -> headers.forEach(m::add));
194 if (preemptiveAuth) {
195 mayApplyPreemptiveAuth(request);
196 }
197
198 Map<String, HttpField> rawResponseHeaders = new HashMap<>();
199 request.onResponseHeader((r, field) -> {
200 rawResponseHeaders.put(field.getLowerCaseName(), field);
201 return true;
202 });
203 Response response = request.send();
204 Map<TransferEvent.TransportPropertyKey, Object> transportProperties =
205 createTransportProperties(request, rawResponseHeaders);
206 task.getListener().transportPropertiesAvailable(transportProperties);
207 if (response.getStatus() >= MULTIPLE_CHOICES) {
208 throw new HttpTransporterException(response.getStatus());
209 }
210 }
211
212 @Override
213 protected void implGet(GetTask task) throws Exception {
214 boolean resume = task.getResumeOffset() > 0L && task.getDataPath() != null;
215 Response response;
216 InputStreamResponseListener listener;
217
218 while (true) {
219 Request request = client.newRequest(resolve(task)).method("GET");
220 request.headers(m -> headers.forEach(m::add));
221 if (preemptiveAuth) {
222 mayApplyPreemptiveAuth(request);
223 }
224 if (sendRfc9457Accept) {
225 JettyRFC9457Reporter.INSTANCE.prepareRequest(request);
226 }
227 if (resume) {
228 long resumeOffset = task.getResumeOffset();
229 long lastModified =
230 Files.getLastModifiedTime(task.getDataPath()).toMillis();
231 request.headers(h -> {
232 h.add(RANGE, "bytes=" + resumeOffset + '-');
233 h.addDateField(IF_UNMODIFIED_SINCE, lastModified - MODIFICATION_THRESHOLD);
234 h.remove(HttpHeader.ACCEPT_ENCODING);
235 h.add(ACCEPT_ENCODING, "identity");
236 });
237 }
238
239
240 Map<String, HttpField> rawResponseHeaders = new HashMap<>();
241 request.onResponseHeader((r, field) -> {
242 rawResponseHeaders.put(field.getLowerCaseName(), field);
243 return true;
244 });
245 listener = new InputStreamResponseListener();
246 request.send(listener);
247 try {
248 response = listener.get(requestTimeout, TimeUnit.MILLISECONDS);
249 } catch (ExecutionException e) {
250 Throwable t = e.getCause();
251 if (t instanceof Exception) {
252 throw (Exception) t;
253 } else {
254 throw new RuntimeException(t);
255 }
256 }
257 Map<TransferEvent.TransportPropertyKey, Object> transportProperties =
258 createTransportProperties(request, rawResponseHeaders);
259 task.getListener().transportPropertiesAvailable(transportProperties);
260 if (response.getStatus() >= MULTIPLE_CHOICES) {
261 if (resume && response.getStatus() == PRECONDITION_FAILED) {
262 resume = false;
263 continue;
264 }
265 JettyRFC9457Reporter.INSTANCE.generateException(listener, (statusCode, reasonPhrase) -> {
266 throw new HttpTransporterException(statusCode);
267 });
268 }
269 break;
270 }
271
272 long offset = 0L, length = response.getHeaders().getLongField(CONTENT_LENGTH);
273 if (resume) {
274 String range = response.getHeaders().get(CONTENT_RANGE);
275 if (range != null) {
276 Matcher m = CONTENT_RANGE_PATTERN.matcher(range);
277 if (!m.matches()) {
278 throw new IOException("Invalid Content-Range header for partial download: " + range);
279 }
280 offset = Long.parseLong(m.group(1));
281 length = Long.parseLong(m.group(2)) + 1L;
282 if (offset < 0L || offset >= length || (offset > 0L && offset != task.getResumeOffset())) {
283 throw new IOException("Invalid Content-Range header for partial download from offset "
284 + task.getResumeOffset() + ": " + range);
285 }
286 }
287 }
288
289 final boolean downloadResumed = offset > 0L;
290 final Path dataFile = task.getDataPath();
291 if (dataFile == null) {
292 try (InputStream is = listener.getInputStream()) {
293 utilGet(task, is, true, length, downloadResumed);
294 }
295 } else {
296 try (PathProcessor.CollocatedTempFile tempFile = pathProcessor.newTempFile(dataFile)) {
297 task.setDataPath(tempFile.getPath(), downloadResumed);
298 if (downloadResumed && Files.isRegularFile(dataFile)) {
299 try (InputStream inputStream = Files.newInputStream(dataFile)) {
300 Files.copy(inputStream, tempFile.getPath(), StandardCopyOption.REPLACE_EXISTING);
301 }
302 }
303 try (InputStream is = listener.getInputStream()) {
304 utilGet(task, is, true, length, downloadResumed);
305 }
306 tempFile.move();
307 } finally {
308 task.setDataPath(dataFile);
309 }
310 }
311 if (task.getDataPath() != null && response.getHeaders().getDateField(LAST_MODIFIED) != -1) {
312 long lastModified =
313 response.getHeaders().getDateField(LAST_MODIFIED);
314 if (lastModified != -1) {
315 pathProcessor.setLastModified(
316 task.getDataPath(), HttpTransporterUtils.clampRemoteLastModified(lastModified));
317 }
318 }
319 Map<String, String> checksums = checksumExtractor.extractChecksums(headerGetter(response));
320 if (checksums != null && !checksums.isEmpty()) {
321 checksums.forEach(task::setChecksum);
322 }
323 }
324
325 private Map<TransferEvent.TransportPropertyKey, Object> createTransportProperties(
326 Request request, Map<String, HttpField> rawResponseHeaders) {
327 HttpTransportPropertiesBuilder builder =
328 new HttpTransportPropertiesBuilder(toHttpVersion(request.getVersion()));
329 SslSessionData sslSessionData = request.getConnection().getSslSessionData();
330 if (sslSessionData != null && sslSessionData.sslSession() != null) {
331 builder.withSslProtocol(sslSessionData.sslSession().getProtocol());
332 builder.withSslCipherSuite(sslSessionData.sslSession().getCipherSuite());
333 }
334 if (rawResponseHeaders.containsKey("content-encoding")) {
335 builder.withContentCoding(rawResponseHeaders.get("content-encoding").getValue());
336 }
337 return builder.build();
338 }
339
340 static HttpTransportProperty.HttpVersion toHttpVersion(org.eclipse.jetty.http.HttpVersion version) {
341 switch (version) {
342 case HTTP_1_0:
343 return HttpTransportProperty.HttpVersion.HTTP_1_0;
344 case HTTP_1_1:
345 return HttpTransportProperty.HttpVersion.HTTP_1_1;
346 case HTTP_2:
347 return HttpTransportProperty.HttpVersion.HTTP_2;
348 case HTTP_3:
349 return HttpTransportProperty.HttpVersion.HTTP_3;
350 default:
351 throw new IllegalArgumentException("Unknown version " + version.toString());
352 }
353 }
354
355 private static Function<String, String> headerGetter(Response response) {
356 return s -> response.getHeaders().get(s);
357 }
358
359 @Override
360 protected void implPut(PutTask task) throws Exception {
361 Request request = client.newRequest(resolve(task)).method("PUT");
362 request.headers(m -> headers.forEach(m::add));
363 if (sendRfc9457Accept) {
364 JettyRFC9457Reporter.INSTANCE.prepareRequest(request);
365 }
366 if (preemptiveAuth || preemptivePutAuth) {
367 mayApplyPreemptiveAuth(request);
368 }
369 request.body(PutTaskRequestContent.from(task));
370
371 Map<String, HttpField> rawResponseHeaders = new HashMap<>();
372 request.onResponseHeader((r, field) -> {
373 rawResponseHeaders.put(field.getLowerCaseName(), field);
374 return true;
375 });
376 AtomicBoolean started = new AtomicBoolean(false);
377 Response response;
378 try (InputStreamResponseListener listener = new InputStreamResponseListener()) {
379 request.onRequestCommit(r -> {
380 if (task.getDataLength() == 0) {
381 if (started.compareAndSet(false, true)) {
382 try {
383 task.getListener().transportStarted(0, task.getDataLength());
384 } catch (TransferCancelledException e) {
385 r.abort(e);
386 }
387 }
388 }
389 })
390 .onRequestContent((r, b) -> {
391 if (started.compareAndSet(false, true)) {
392 try {
393 task.getListener().transportStarted(0, task.getDataLength());
394 } catch (TransferCancelledException e) {
395 r.abort(e);
396 return;
397 }
398 }
399 try {
400 task.getListener().transportProgressed(b);
401 } catch (TransferCancelledException e) {
402 r.abort(e);
403 }
404 })
405 .send(listener);
406 response = listener.get(requestTimeout, TimeUnit.MILLISECONDS);
407 task.getListener().transportPropertiesAvailable(createTransportProperties(request, rawResponseHeaders));
408 if (response.getStatus() >= MULTIPLE_CHOICES) {
409 JettyRFC9457Reporter.INSTANCE.generateException(listener, (statusCode, reasonPhrase) -> {
410 throw new HttpTransporterException(statusCode);
411 });
412 }
413 } catch (ExecutionException e) {
414 Throwable t = e.getCause();
415 if (t instanceof IOException ioex) {
416 if (ioex.getCause() instanceof TransferCancelledException) {
417 throw (TransferCancelledException) ioex.getCause();
418 } else {
419 throw ioex;
420 }
421 } else if (t instanceof Exception) {
422 throw (Exception) t;
423 } else {
424 throw new RuntimeException(t);
425 }
426 }
427 }
428
429 @Override
430 protected void implClose() {
431 try {
432 this.client.stop();
433 } catch (Exception e) {
434 throw new RuntimeException(e);
435 }
436 }
437
438 @SuppressWarnings("checkstyle:methodlength")
439 private HttpClient createClient() throws RuntimeException {
440 BasicAuthentication.BasicResult serverAuth = null;
441 BasicAuthentication.BasicResult proxyAuth = null;
442 SSLContext sslContext = null;
443 BasicAuthentication basicAuthentication = null;
444 try (AuthenticationContext repoAuthContext = AuthenticationContext.forRepository(session, repository)) {
445 if (repoAuthContext != null) {
446 sslContext = repoAuthContext.get(AuthenticationContext.SSL_CONTEXT, SSLContext.class);
447
448 String username = repoAuthContext.get(AuthenticationContext.USERNAME);
449 String password = repoAuthContext.get(AuthenticationContext.PASSWORD);
450
451 URI uri = URI.create(repository.getUrl());
452 basicAuthentication = new BasicAuthentication(uri, Authentication.ANY_REALM, username, password);
453 if (preemptiveAuth || preemptivePutAuth) {
454 serverAuth = new BasicAuthentication.BasicResult(uri, HttpHeader.AUTHORIZATION, username, password);
455 }
456 }
457 }
458
459 SslContextFactory.Client sslContextFactory = new SslContextFactory.Client();
460 if (insecure) {
461
462 sslContextFactory.setEndpointIdentificationAlgorithm(null);
463 sslContextFactory.setHostnameVerifier((name, context) -> true);
464 sslContextFactory.setTrustAll(true);
465 } else {
466 try {
467 if (sslContext == null) {
468
469
470 sslContext = SSLContext.getDefault();
471 }
472 } catch (NoSuchAlgorithmException e) {
473 throw new IllegalStateException("SSL Context setup failure", e);
474 }
475 }
476
477 if (sslContext != null) {
478
479 sslContextFactory.setSslContext(sslContext);
480 }
481
482 ClientConnector clientConnector = new ClientConnector();
483 clientConnector.setSslContextFactory(sslContextFactory);
484
485 Collection<ClientConnectionFactory.Info> connectors = new ArrayList<>();
486 if ("https".equalsIgnoreCase(repository.getProtocol())) {
487 HttpVersion httpVersion = HttpTransporterUtils.getHttpVersion(session, repository);
488 switch (httpVersion) {
489 case MAXIMUM:
490 case HTTP_3:
491 QuicheClientQuicConfiguration clientQuicConfig =
492 HTTP3ClientQuicConfiguration.configure(new QuicheClientQuicConfiguration());
493 HTTP3Client http3Client = new HTTP3Client(clientQuicConfig, clientConnector);
494 QuicheTransport transport = new QuicheTransport(clientQuicConfig);
495 ClientConnectionFactoryOverHTTP3.HTTP3 http3 =
496 new ClientConnectionFactoryOverHTTP3.HTTP3(http3Client, transport);
497 connectors.add(http3);
498 break;
499 case HTTP_2:
500 case DEFAULT:
501 HTTP2Client http2Client = new HTTP2Client(clientConnector);
502 ClientConnectionFactoryOverHTTP2.HTTP2 http2 =
503 new ClientConnectionFactoryOverHTTP2.HTTP2(http2Client);
504 connectors.add(http2);
505 break;
506 default:
507 break;
508 }
509 }
510 connectors.add(HttpClientConnectionFactory.HTTP11);
511
512 HttpClientTransportDynamic dynamicTransport = new HttpClientTransportDynamic(
513 clientConnector, connectors.toArray(new ClientConnectionFactory.Info[0]));
514 HttpClient httpClient = new HttpClient(dynamicTransport);
515 httpClient.setConnectTimeout(connectTimeout);
516 httpClient.setIdleTimeout(requestTimeout);
517 httpClient.setFollowRedirects(ConfigUtils.getBoolean(
518 session,
519 JettyTransporterConfigurationKeys.DEFAULT_FOLLOW_REDIRECTS,
520 JettyTransporterConfigurationKeys.CONFIG_PROP_FOLLOW_REDIRECTS));
521 httpClient.setMaxRedirects(ConfigUtils.getInteger(
522 session,
523 JettyTransporterConfigurationKeys.DEFAULT_MAX_REDIRECTS,
524 JettyTransporterConfigurationKeys.CONFIG_PROP_MAX_REDIRECTS));
525
526 boolean followInsecureRedirects = ConfigUtils.getBoolean(
527 session,
528 JettyTransporterConfigurationKeys.DEFAULT_FOLLOW_INSECURE_REDIRECTS,
529 JettyTransporterConfigurationKeys.CONFIG_PROP_FOLLOW_INSECURE_REDIRECTS + "." + repository.getId(),
530 JettyTransporterConfigurationKeys.CONFIG_PROP_FOLLOW_INSECURE_REDIRECTS);
531
532 boolean originScopedHeaders = ConfigUtils.getBoolean(
533 session,
534 JettyTransporterConfigurationKeys.DEFAULT_ORIGIN_SCOPED_HEADERS,
535 JettyTransporterConfigurationKeys.CONFIG_PROP_ORIGIN_SCOPED_HEADERS + "." + repository.getId(),
536 JettyTransporterConfigurationKeys.CONFIG_PROP_ORIGIN_SCOPED_HEADERS);
537
538 httpClient.setUserAgentField(null);
539
540 if (basicAuthentication != null) {
541 httpClient.getAuthenticationStore().addAuthentication(basicAuthentication);
542 }
543
544 if (repository.getProxy() != null) {
545 HttpProxy proxy = new HttpProxy(
546 repository.getProxy().getHost(), repository.getProxy().getPort());
547
548 httpClient.getProxyConfiguration().addProxy(proxy);
549 try (AuthenticationContext proxyAuthContext = AuthenticationContext.forProxy(session, repository)) {
550 if (proxyAuthContext != null) {
551 String username = proxyAuthContext.get(AuthenticationContext.USERNAME);
552 String password = proxyAuthContext.get(AuthenticationContext.PASSWORD);
553
554 BasicAuthentication proxyAuthentication =
555 new BasicAuthentication(proxy.getURI(), Authentication.ANY_REALM, username, password);
556
557 httpClient.getAuthenticationStore().addAuthentication(proxyAuthentication);
558 if (preemptiveAuth || preemptivePutAuth) {
559 proxyAuth = new BasicAuthentication.BasicResult(
560 proxy.getURI(), HttpHeader.PROXY_AUTHORIZATION, username, password);
561 }
562 }
563 }
564 }
565 if (serverAuth != null) {
566 this.basicServerAuthenticationResult.set(serverAuth);
567 }
568 if (proxyAuth != null) {
569 this.basicProxyAuthenticationResult.set(proxyAuth);
570 }
571
572 try {
573 httpClient.start();
574
575
576
577 if (!followInsecureRedirects && "https".equalsIgnoreCase(repository.getProtocol())) {
578
579
580 httpClient.getRequestListeners().addQueuedListener(new InsecureRedirectGuard());
581 }
582 if (originScopedHeaders) {
583
584
585
586
587
588
589
590
591
592 Map<String, String> configuredHeaders = HttpTransporterUtils.getHttpHeaders(session, repository);
593 boolean hasPreemptiveAuth = basicAuthentication != null;
594 boolean hasConfiguredHeaders = configuredHeaders != null && !configuredHeaders.isEmpty();
595 if (hasPreemptiveAuth || hasConfiguredHeaders) {
596 TreeSet<String> scopedHeaderNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
597 scopedHeaderNames.add(HttpHeader.AUTHORIZATION.asString());
598 if (hasConfiguredHeaders) {
599 scopedHeaderNames.addAll(configuredHeaders.keySet());
600 }
601 httpClient
602 .getRequestListeners()
603 .addHeadersListener(new OriginScopedHeadersListener(baseUri, scopedHeaderNames));
604 }
605 }
606
607 return httpClient;
608 } catch (Exception e) {
609 if (e instanceof RuntimeException) {
610 throw (RuntimeException) e;
611 } else {
612 throw new IllegalStateException("Jetty client start failure", e);
613 }
614 }
615 }
616 }