src/java.net.http/share/classes/jdk/internal/net/http/MultiExchange.java
changeset 52196 420445d16008
parent 51364 31d9e82b2e64
child 53300 54aa3ea04fe8
child 53700 4ce47bc1fb92
equal deleted inserted replaced
52195:f08c1d7a5c53 52196:420445d16008
    24  */
    24  */
    25 
    25 
    26 package jdk.internal.net.http;
    26 package jdk.internal.net.http;
    27 
    27 
    28 import java.io.IOException;
    28 import java.io.IOException;
       
    29 import java.io.UncheckedIOException;
    29 import java.net.ConnectException;
    30 import java.net.ConnectException;
    30 import java.net.http.HttpConnectTimeoutException;
    31 import java.net.http.HttpConnectTimeoutException;
    31 import java.util.Iterator;
    32 import java.util.Iterator;
    32 import java.util.LinkedList;
    33 import java.util.LinkedList;
    33 import java.security.AccessControlContext;
    34 import java.security.AccessControlContext;
    34 import java.util.concurrent.CompletableFuture;
    35 import java.util.concurrent.CompletableFuture;
       
    36 import java.util.concurrent.CompletionStage;
    35 import java.util.concurrent.CompletionException;
    37 import java.util.concurrent.CompletionException;
    36 import java.util.concurrent.ExecutionException;
    38 import java.util.concurrent.ExecutionException;
    37 import java.util.concurrent.Executor;
    39 import java.util.concurrent.Executor;
       
    40 import java.util.concurrent.Flow;
    38 import java.util.concurrent.atomic.AtomicInteger;
    41 import java.util.concurrent.atomic.AtomicInteger;
    39 import java.util.function.Function;
    42 import java.util.function.Function;
    40 
    43 
    41 import java.net.http.HttpClient;
    44 import java.net.http.HttpClient;
       
    45 import java.net.http.HttpHeaders;
    42 import java.net.http.HttpRequest;
    46 import java.net.http.HttpRequest;
    43 import java.net.http.HttpResponse;
    47 import java.net.http.HttpResponse;
       
    48 import java.net.http.HttpResponse.BodySubscriber;
    44 import java.net.http.HttpResponse.PushPromiseHandler;
    49 import java.net.http.HttpResponse.PushPromiseHandler;
    45 import java.net.http.HttpTimeoutException;
    50 import java.net.http.HttpTimeoutException;
    46 import jdk.internal.net.http.common.Log;
    51 import jdk.internal.net.http.common.Log;
    47 import jdk.internal.net.http.common.Logger;
    52 import jdk.internal.net.http.common.Logger;
    48 import jdk.internal.net.http.common.MinimalFuture;
    53 import jdk.internal.net.http.common.MinimalFuture;
   198         CompletableFuture<HttpResponse<T>> cf = responseAsync0(start);
   203         CompletableFuture<HttpResponse<T>> cf = responseAsync0(start);
   199         start.completeAsync( () -> null, executor); // trigger execution
   204         start.completeAsync( () -> null, executor); // trigger execution
   200         return cf;
   205         return cf;
   201     }
   206     }
   202 
   207 
       
   208     // return true if the response is a type where a response body is never possible
       
   209     // and therefore doesn't have to include header information which indicates no
       
   210     // body is present. This is distinct from responses that also do not contain
       
   211     // response bodies (possibly ever) but which are required to have content length
       
   212     // info in the header (eg 205). Those cases do not have to be handled specially
       
   213 
       
   214     private static boolean bodyNotPermitted(Response r) {
       
   215         return r.statusCode == 204;
       
   216     }
       
   217 
       
   218     private boolean bodyIsPresent(Response r) {
       
   219         HttpHeaders headers = r.headers();
       
   220         if (headers.firstValue("Content-length").isPresent())
       
   221             return true;
       
   222         if (headers.firstValue("Transfer-encoding").isPresent())
       
   223             return true;
       
   224         return false;
       
   225     }
       
   226 
       
   227     // Call the user's body handler to get an empty body object
       
   228 
       
   229     private CompletableFuture<HttpResponse<T>> handleNoBody(Response r, Exchange<T> exch) {
       
   230         BodySubscriber<T> bs = responseHandler.apply(new ResponseInfoImpl(r.statusCode(),
       
   231                 r.headers(), r.version()));
       
   232         CompletionStage<T> cs = bs.getBody();
       
   233         bs.onSubscribe(new NullSubscription());
       
   234         bs.onComplete();
       
   235         MinimalFuture<HttpResponse<T>> result = new MinimalFuture<>();
       
   236         cs.whenComplete((nullBody, exception) -> {
       
   237             if (exception != null)
       
   238                 result.completeExceptionally(exception);
       
   239             else {
       
   240                 this.response =
       
   241                         new HttpResponseImpl<>(r.request(), r, this.response, nullBody, exch);
       
   242                 result.complete(this.response);
       
   243             }
       
   244         });
       
   245         return result;
       
   246     }
       
   247 
   203     private CompletableFuture<HttpResponse<T>>
   248     private CompletableFuture<HttpResponse<T>>
   204     responseAsync0(CompletableFuture<Void> start) {
   249     responseAsync0(CompletableFuture<Void> start) {
   205         return start.thenCompose( v -> responseAsyncImpl())
   250         return start.thenCompose( v -> responseAsyncImpl())
   206                     .thenCompose((Response r) -> {
   251                     .thenCompose((Response r) -> {
   207                         Exchange<T> exch = getExchange();
   252                         Exchange<T> exch = getExchange();
       
   253                         if (bodyNotPermitted(r)) {
       
   254                             if (bodyIsPresent(r)) {
       
   255                                 IOException ioe = new IOException(
       
   256                                     "unexpected content length header with 204 response");
       
   257                                 exch.cancel(ioe);
       
   258                                 return MinimalFuture.failedFuture(ioe);
       
   259                             } else
       
   260                                 return handleNoBody(r, exch);
       
   261                         }
   208                         return exch.readBodyAsync(responseHandler)
   262                         return exch.readBodyAsync(responseHandler)
   209                             .thenApply((T body) -> {
   263                             .thenApply((T body) -> {
   210                                 this.response =
   264                                 this.response =
   211                                     new HttpResponseImpl<>(r.request(), r, this.response, body, exch);
   265                                     new HttpResponseImpl<>(r.request(), r, this.response, body, exch);
   212                                 return this.response;
   266                                 return this.response;
   213                             });
   267                             });
   214                     });
   268                     });
       
   269     }
       
   270 
       
   271     static class NullSubscription implements Flow.Subscription {
       
   272         @Override
       
   273         public void request(long n) {
       
   274         }
       
   275 
       
   276         @Override
       
   277         public void cancel() {
       
   278         }
   215     }
   279     }
   216 
   280 
   217     private CompletableFuture<Response> responseAsyncImpl() {
   281     private CompletableFuture<Response> responseAsyncImpl() {
   218         CompletableFuture<Response> cf;
   282         CompletableFuture<Response> cf;
   219         if (attempts.incrementAndGet() > max_attempts) {
   283         if (attempts.incrementAndGet() > max_attempts) {