/** Take a Throwable and return a suitable CompletableFuture that is completed exceptionally. */ private CompletableFuture<HttpResponseImpl> getExceptionalCF(Throwable t) { if ((t instanceof CompletionException) || (t instanceof ExecutionException)) { if (t.getCause() != null) { t = t.getCause(); } } if (cancelled && t instanceof IOException) { t = new HttpTimeoutException("request timed out"); } return CompletableFuture.failedFuture(t); }
public CompletableFuture<HttpResponseImpl> responseAsync(Void v) { CompletableFuture<HttpResponseImpl> cf; if (++attempts > max_attempts) { cf = CompletableFuture.failedFuture(new IOException("Too many retries")); } else { if (currentreq.timeval() != 0) { // set timer td = new TimedEvent(currentreq.timeval()); client.registerTimer(td); } Exchange exch = getExchange(); cf = requestFiltersAsync(currentreq) .thenCompose(exch::responseAsync) .thenCompose(this::responseFiltersAsync) .thenCompose( (Pair<HttpResponse, HttpRequestImpl> pair) -> { HttpResponseImpl resp = (HttpResponseImpl) pair.first; if (resp != null) { if (attempts > 1) { Log.logError("Succeeded on attempt: " + attempts); } return CompletableFuture.completedFuture(resp); } else { currentreq = pair.second; Exchange previous = exch; setExchange(new Exchange(currentreq, currentreq.getAccessControlContext())); // reads body off previous, and then waits for next response return previous .responseBodyAsync(HttpResponse.ignoreBody()) .thenCompose(this::responseAsync); } }) .handle( (BiFunction<HttpResponse, Throwable, Pair<HttpResponse, Throwable>>) Pair::new) .thenCompose( (Pair<HttpResponse, Throwable> obj) -> { HttpResponseImpl response = (HttpResponseImpl) obj.first; if (response != null) { return CompletableFuture.completedFuture(response); } // all exceptions thrown are handled here CompletableFuture<HttpResponseImpl> error = getExceptionalCF(obj.second); if (error == null) { cancelTimer(); return responseAsync(null); } else { return error; } }); } return cf; }