37
37
import java .time .Duration ;
38
38
import java .util .*;
39
39
import java .util .concurrent .CompletableFuture ;
40
+ import java .util .concurrent .CompletionException ;
40
41
import java .util .concurrent .TimeUnit ;
41
42
import java .util .function .Consumer ;
42
43
import java .util .function .Function ;
@@ -557,7 +558,7 @@ public static Builder contentTypeHeader(Builder builder, String contentType) {
557
558
* @return HttpResponse
558
559
*/
559
560
public static HttpResponse <InputStream > execute (HttpClient httpClient , HttpRequest httpRequest ) {
560
- return execute ( httpClient , httpRequest , BodyHandlers .ofInputStream ());
561
+ return AsyncHttpRDF . getOrElseThrow ( executeAsync ( httpClient , httpRequest , BodyHandlers .ofInputStream () ));
561
562
}
562
563
563
564
/**
@@ -577,13 +578,35 @@ public static HttpResponse<InputStream> execute(HttpClient httpClient, HttpReque
577
578
*
578
579
* @param httpClient
579
580
* @param httpRequest
580
- * @param bodyHandler
581
581
* @return HttpResponse
582
582
*/
583
- /*package*/ static <X > HttpResponse <X > execute (HttpClient httpClient , HttpRequest httpRequest , BodyHandler <X > bodyHandler ) {
583
+ public static CompletableFuture <HttpResponse <InputStream >> executeAsync (HttpClient httpClient , HttpRequest httpRequest ) {
584
+ return executeAsync (httpClient , httpRequest , BodyHandlers .ofInputStream ());
585
+ }
586
+
587
+ /**
588
+ * Execute a request, return a {@code HttpResponse<X>} which
589
+ * can be passed to {@link #handleHttpStatusCode(HttpResponse)} which will
590
+ * convert non-2xx status code to {@link HttpException HttpExceptions}.
591
+ * <p>
592
+ * This function applies the HTTP authentication challenge support
593
+ * and will repeat the request if necessary with added authentication.
594
+ * <p>
595
+ * See {@link AuthEnv} for authentication registration.
596
+ * <br/>
597
+ * See {@link #executeJDK} to execute exactly once without challenge response handling.
598
+ *
599
+ * @see AuthEnv AuthEnv for authentic registration
600
+ * @see #executeJDK executeJDK to execute exacly once.
601
+ *
602
+ * @param httpClient
603
+ * @param httpRequest
604
+ * @param bodyHandler
605
+ * @return HttpResponse
606
+ */ /*package*/ static <X > CompletableFuture <HttpResponse <X >> executeAsync (HttpClient httpClient , HttpRequest httpRequest , BodyHandler <X > bodyHandler ) {
584
607
// To run with no jena-supplied authentication handling.
585
608
if ( false )
586
- return executeJDK (httpClient , httpRequest , bodyHandler );
609
+ return executeJDKAsync (httpClient , httpRequest , bodyHandler );
587
610
URI uri = httpRequest .uri ();
588
611
URI key = null ;
589
612
@@ -599,29 +622,16 @@ public static HttpResponse<InputStream> execute(HttpClient httpClient, HttpReque
599
622
authEnv .registerUsernamePassword (key , userpasswd [0 ], userpasswd [1 ]);
600
623
}
601
624
}
602
- try {
603
- return AuthLib .authExecute (httpClient , httpRequest , bodyHandler );
604
- } finally {
605
- if ( key != null )
606
- // The AuthEnv is "per tenant".
607
- // Temporary registration within the AuthEnv of the
608
- // user:password is acceptable.
609
- authEnv .unregisterUsernamePassword (key );
610
- }
611
- }
612
625
613
- /**
614
- * Execute request and return a {@code HttpResponse<InputStream>} response.
615
- * Status codes have not been handled. The response can be passed to
616
- * {@link #handleResponseInputStream(HttpResponse)} which will convert non-2xx
617
- * status code to {@link HttpException HttpExceptions}.
618
- *
619
- * @param httpClient
620
- * @param httpRequest
621
- * @return HttpResponse
622
- */
623
- public static HttpResponse <InputStream > executeJDK (HttpClient httpClient , HttpRequest httpRequest ) {
624
- return execute (httpClient , httpRequest , BodyHandlers .ofInputStream ());
626
+ URI finalKey = key ;
627
+ return AuthLib .authExecuteAsync (httpClient , httpRequest , bodyHandler )
628
+ .whenComplete ((httpResponse , throwable ) -> {
629
+ if ( finalKey != null )
630
+ // The AuthEnv is "per tenant".
631
+ // Temporary registration within the AuthEnv of the
632
+ // user:password is acceptable.
633
+ authEnv .unregisterUsernamePassword (finalKey );
634
+ });
625
635
}
626
636
627
637
/**
@@ -637,25 +647,53 @@ public static HttpResponse<InputStream> executeJDK(HttpClient httpClient, HttpRe
637
647
* @return HttpResponse
638
648
*/
639
649
public static <T > HttpResponse <T > executeJDK (HttpClient httpClient , HttpRequest httpRequest , BodyHandler <T > bodyHandler ) {
640
- try {
641
- // This is the one place all HTTP requests go through.
642
- logRequest (httpRequest );
643
- HttpResponse <T > httpResponse = httpClient .send (httpRequest , bodyHandler );
644
- logResponse (httpResponse );
645
- return httpResponse ;
646
- //} catch (HttpTimeoutException ex) {
647
- } catch (IOException | InterruptedException ex ) {
648
- if ( ex .getMessage () != null ) {
649
- // This is silly.
650
- // Rather than an HTTP exception, bad authentication becomes IOException("too many authentication attempts");
651
- // or IOException("No credentials provided") if the authenticator decides to return null.
652
- if ( ex .getMessage ().contains ("too many authentication attempts" ) ||
653
- ex .getMessage ().contains ("No credentials provided" ) ) {
654
- throw new HttpException (401 , HttpSC .getMessage (401 ));
650
+ return AsyncHttpRDF .getOrElseThrow (executeJDKAsync (httpClient , httpRequest , bodyHandler ));
651
+ }
652
+
653
+ /**
654
+ * Execute request and return a {@code HttpResponse<InputStream>} response.
655
+ * Status codes have not been handled. The response can be passed to
656
+ * {@link #handleResponseInputStream(HttpResponse)} which will convert non-2xx
657
+ * status code to {@link HttpException HttpExceptions}.
658
+ *
659
+ * @param httpClient
660
+ * @param httpRequest
661
+ * @return HttpResponse
662
+ */
663
+ public static CompletableFuture <HttpResponse <InputStream >> executeJDKAsync (HttpClient httpClient , HttpRequest httpRequest ) {
664
+ return executeAsync (httpClient , httpRequest , BodyHandlers .ofInputStream ());
665
+ }
666
+
667
+ public static <T > CompletableFuture <HttpResponse <T >> executeJDKAsync (HttpClient httpClient , HttpRequest httpRequest , BodyHandler <T > bodyHandler ) {
668
+ // This is the one place all HTTP requests go through.
669
+ logRequest (httpRequest );
670
+ CompletableFuture <HttpResponse <T >> future = httpClient .sendAsync (httpRequest , bodyHandler )
671
+ .thenApply (httpResponse -> {
672
+ logResponse (httpResponse );
673
+ return httpResponse ;
674
+ })
675
+ .exceptionally (ex -> {
676
+ Throwable cause = ex instanceof CompletionException ? ex .getCause () : ex ;
677
+ if (cause instanceof IOException ) {
678
+ if ( ex .getMessage () != null ) {
679
+ // This is silly.
680
+ // Rather than an HTTP exception, bad authentication becomes IOException("too many authentication attempts");
681
+ // or IOException("No credentials provided") if the authenticator decides to return null.
682
+ if ( ex .getMessage ().contains ("too many authentication attempts" ) ||
683
+ ex .getMessage ().contains ("No credentials provided" ) ) {
684
+ throw new HttpException (401 , HttpSC .getMessage (401 ));
685
+ }
686
+ }
687
+ // Note: Can't reuse AsyncHttpRDF.handleRuntimeException because of this HttpException.
688
+ throw new HttpException (httpRequest .method ()+" " +httpRequest .uri ().toString (), cause );
689
+ } else if (cause instanceof RuntimeException re ) {
690
+ re .addSuppressed (new RuntimeException ("Passed through here." ));
691
+ throw re ;
692
+ } else {
693
+ throw new RuntimeException (cause );
655
694
}
656
- }
657
- throw new HttpException (httpRequest .method ()+" " +httpRequest .uri ().toString (), ex );
658
- }
695
+ });
696
+ return future ;
659
697
}
660
698
661
699
/*package*/ static CompletableFuture <HttpResponse <InputStream >> asyncExecute (HttpClient httpClient , HttpRequest httpRequest ) {
0 commit comments