5252import java .time .ZonedDateTime ;
5353import java .time .format .DateTimeFormatter ;
5454import java .time .format .DateTimeParseException ;
55+ import java .util .Base64 ;
5556import java .util .Collections ;
5657import java .util .HashMap ;
5758import java .util .Locale ;
7980import org .slf4j .Logger ;
8081import org .slf4j .LoggerFactory ;
8182
83+ import static java .nio .charset .StandardCharsets .ISO_8859_1 ;
8284import static org .eclipse .aether .spi .connector .transport .http .HttpConstants .ACCEPT_ENCODING ;
8385import static org .eclipse .aether .spi .connector .transport .http .HttpConstants .CACHE_CONTROL ;
8486import static org .eclipse .aether .spi .connector .transport .http .HttpConstants .CONTENT_LENGTH ;
@@ -135,6 +137,12 @@ final class JdkTransporter extends AbstractTransporter implements HttpTransporte
135137
136138 private final Semaphore maxConcurrentRequests ;
137139
140+ private boolean preemptivePutAuth ;
141+
142+ private boolean preemptiveAuth ;
143+
144+ private PasswordAuthentication serverAuthentication ;
145+
138146 JdkTransporter (
139147 RepositorySystemSession session ,
140148 RemoteRepository repository ,
@@ -227,6 +235,17 @@ final class JdkTransporter extends AbstractTransporter implements HttpTransporte
227235 CONFIG_PROP_MAX_CONCURRENT_REQUESTS + "." + repository .getId (),
228236 CONFIG_PROP_MAX_CONCURRENT_REQUESTS ));
229237
238+ this .preemptiveAuth = ConfigUtils .getBoolean (
239+ session ,
240+ ConfigurationProperties .DEFAULT_HTTP_PREEMPTIVE_AUTH ,
241+ ConfigurationProperties .HTTP_PREEMPTIVE_AUTH + "." + repository .getId (),
242+ ConfigurationProperties .HTTP_PREEMPTIVE_AUTH );
243+ this .preemptivePutAuth = ConfigUtils .getBoolean (
244+ session ,
245+ ConfigurationProperties .DEFAULT_HTTP_PREEMPTIVE_PUT_AUTH ,
246+ ConfigurationProperties .HTTP_PREEMPTIVE_PUT_AUTH + "." + repository .getId (),
247+ ConfigurationProperties .HTTP_PREEMPTIVE_PUT_AUTH );
248+
230249 this .headers = headers ;
231250 this .client = createClient (session , repository , insecure );
232251 }
@@ -255,6 +274,8 @@ protected void implPeek(PeekTask task) throws Exception {
255274 HttpRequest .Builder request =
256275 HttpRequest .newBuilder ().uri (resolve (task )).method ("HEAD" , HttpRequest .BodyPublishers .noBody ());
257276 headers .forEach (request ::setHeader );
277+
278+ prepare (request );
258279 try {
259280 HttpResponse <Void > response = send (request .build (), HttpResponse .BodyHandlers .discarding ());
260281 if (response .statusCode () >= MULTIPLE_CHOICES ) {
@@ -286,6 +307,7 @@ protected void implGet(GetTask task) throws Exception {
286307 request .header (ACCEPT_ENCODING , "identity" );
287308 }
288309
310+ prepare (request );
289311 try {
290312 response = send (request .build (), HttpResponse .BodyHandlers .ofInputStream ());
291313 if (response .statusCode () >= MULTIPLE_CHOICES ) {
@@ -397,7 +419,7 @@ protected void implPut(PutTask task) throws Exception {
397419 try (PathProcessor .TempFile tempFile = pathProcessor .newTempFile ()) {
398420 utilPut (task , Files .newOutputStream (tempFile .getPath ()), true );
399421 request .PUT (HttpRequest .BodyPublishers .ofFile (tempFile .getPath ()));
400-
422+ prepare ( request );
401423 try {
402424 HttpResponse <Void > response = send (request .build (), HttpResponse .BodyHandlers .discarding ());
403425 if (response .statusCode () >= MULTIPLE_CHOICES ) {
@@ -409,6 +431,24 @@ protected void implPut(PutTask task) throws Exception {
409431 }
410432 }
411433
434+ private void prepare (HttpRequest .Builder requestBuilder ) {
435+ if (serverAuthentication != null
436+ && (preemptiveAuth
437+ || (preemptivePutAuth && requestBuilder .build ().method ().equals ("PUT" )))) {
438+ // https://stackoverflow.com/a/58612586
439+ requestBuilder .setHeader (
440+ "Authorization" ,
441+ getBasicAuthValue (serverAuthentication .getUserName (), serverAuthentication .getPassword ()));
442+ }
443+ }
444+
445+ static String getBasicAuthValue (String username , char [] password ) {
446+ StringBuilder sb = new StringBuilder (128 );
447+ sb .append (username ).append (':' ).append (password );
448+ // Java's HTTP client uses ISO-8859-1 for Basic auth encoding
449+ return "Basic " + Base64 .getEncoder ().encodeToString (sb .toString ().getBytes (ISO_8859_1 ));
450+ }
451+
412452 private <T > HttpResponse <T > send (HttpRequest request , HttpResponse .BodyHandler <T > responseBodyHandler )
413453 throws Exception {
414454 maxConcurrentRequests .acquire ();
@@ -437,10 +477,8 @@ private HttpClient createClient(RepositorySystemSession session, RemoteRepositor
437477
438478 String username = repoAuthContext .get (AuthenticationContext .USERNAME );
439479 String password = repoAuthContext .get (AuthenticationContext .PASSWORD );
440-
441- authentications .put (
442- Authenticator .RequestorType .SERVER ,
443- new PasswordAuthentication (username , password .toCharArray ()));
480+ serverAuthentication = new PasswordAuthentication (username , password .toCharArray ());
481+ authentications .put (Authenticator .RequestorType .SERVER , serverAuthentication );
444482 }
445483 }
446484
@@ -519,7 +557,6 @@ public X509Certificate[] getAcceptedIssuers() {
519557 }
520558 }
521559 }
522-
523560 if (!authentications .isEmpty ()) {
524561 builder .authenticator (new Authenticator () {
525562 @ Override
@@ -528,7 +565,6 @@ protected PasswordAuthentication getPasswordAuthentication() {
528565 }
529566 });
530567 }
531-
532568 return builder .build ();
533569 }
534570
0 commit comments