mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-22 03:54:33 +02:00
8211437: java.net.http.HttpClient hangs on 204 reply without Content-length 0
Reviewed-by: chegar, dfuchs
This commit is contained in:
parent
4abb6861ec
commit
b9279ef850
5 changed files with 396 additions and 4 deletions
|
@ -26,21 +26,26 @@
|
|||
package jdk.internal.net.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.http.HttpConnectTimeoutException;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.security.AccessControlContext;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Flow;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpHeaders;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.net.http.HttpResponse.BodySubscriber;
|
||||
import java.net.http.HttpResponse.PushPromiseHandler;
|
||||
import java.net.http.HttpTimeoutException;
|
||||
import jdk.internal.net.http.common.Log;
|
||||
|
@ -200,11 +205,60 @@ class MultiExchange<T> {
|
|||
return cf;
|
||||
}
|
||||
|
||||
// return true if the response is a type where a response body is never possible
|
||||
// and therefore doesn't have to include header information which indicates no
|
||||
// body is present. This is distinct from responses that also do not contain
|
||||
// response bodies (possibly ever) but which are required to have content length
|
||||
// info in the header (eg 205). Those cases do not have to be handled specially
|
||||
|
||||
private static boolean bodyNotPermitted(Response r) {
|
||||
return r.statusCode == 204;
|
||||
}
|
||||
|
||||
private boolean bodyIsPresent(Response r) {
|
||||
HttpHeaders headers = r.headers();
|
||||
if (headers.firstValue("Content-length").isPresent())
|
||||
return true;
|
||||
if (headers.firstValue("Transfer-encoding").isPresent())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call the user's body handler to get an empty body object
|
||||
|
||||
private CompletableFuture<HttpResponse<T>> handleNoBody(Response r, Exchange<T> exch) {
|
||||
BodySubscriber<T> bs = responseHandler.apply(new ResponseInfoImpl(r.statusCode(),
|
||||
r.headers(), r.version()));
|
||||
CompletionStage<T> cs = bs.getBody();
|
||||
bs.onSubscribe(new NullSubscription());
|
||||
bs.onComplete();
|
||||
MinimalFuture<HttpResponse<T>> result = new MinimalFuture<>();
|
||||
cs.whenComplete((nullBody, exception) -> {
|
||||
if (exception != null)
|
||||
result.completeExceptionally(exception);
|
||||
else {
|
||||
this.response =
|
||||
new HttpResponseImpl<>(r.request(), r, this.response, nullBody, exch);
|
||||
result.complete(this.response);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
private CompletableFuture<HttpResponse<T>>
|
||||
responseAsync0(CompletableFuture<Void> start) {
|
||||
return start.thenCompose( v -> responseAsyncImpl())
|
||||
.thenCompose((Response r) -> {
|
||||
Exchange<T> exch = getExchange();
|
||||
if (bodyNotPermitted(r)) {
|
||||
if (bodyIsPresent(r)) {
|
||||
IOException ioe = new IOException(
|
||||
"unexpected content length header with 204 response");
|
||||
exch.cancel(ioe);
|
||||
return MinimalFuture.failedFuture(ioe);
|
||||
} else
|
||||
return handleNoBody(r, exch);
|
||||
}
|
||||
return exch.readBodyAsync(responseHandler)
|
||||
.thenApply((T body) -> {
|
||||
this.response =
|
||||
|
@ -214,6 +268,16 @@ class MultiExchange<T> {
|
|||
});
|
||||
}
|
||||
|
||||
static class NullSubscription implements Flow.Subscription {
|
||||
@Override
|
||||
public void request(long n) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
}
|
||||
|
||||
private CompletableFuture<Response> responseAsyncImpl() {
|
||||
CompletableFuture<Response> cf;
|
||||
if (attempts.incrementAndGet() > max_attempts) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue