Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8251312

HttpClient send throws InterruptedException when interrupted but does not cancel request

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Approved
    • Icon: P3 P3
    • 16
    • core-libs
    • None
    • behavioral
    • low
    • CompletableFuture::cancel specify that the `mayInterruptIfRunning` parameter is ignored. The `CompletableFuture` objects returned by the `HttpClient` override and redefine this behavior. This could be surprising for existing callers.
    • Java API
    • SE

      Summary

      There is no easy way to cancel a request sent through the java.net.http.HttpClient. This change proposes to handle thread interruption (when in synchronous mode) and CompletableFuture.cancel(true) (when in asynchronous mode) to release resources associated with an inflight request when the caller is no longer interested in the response.

      Problem

      There is no easy way to cancel a request sent through the java.net.http.HttpClient. A natural way may seem to call Thread.interrupt() on the thread waiting for the response, or to cancel the CompletableFuture returned by the HttpClient, but although both will cause an exception to be thrown, the actual request will typically continue in the background as if nothing had happened. This is typically an issue when using virtual threads, as HttpClient::send will throw an InterruptedException when interrupted but will not cancel the request.

      Solution

      This change proposes to handle thread interruption (when in synchronous mode) and CompletableFuture.cancel(true) (when in asynchronous mode) to release resources associated with an inflight request when the caller is no longer interested in the response.

      Specification

      The concepts of default HttpClient implementation and cancelable futures are introduced.

      src/java.net.http/share/classes/java/net/http/HttpClient.java

      A clarification is added to the class level API documentation:

        *
        * <p> An {@code HttpClient} can be used to send {@linkplain HttpRequest
        * requests} and retrieve their {@linkplain HttpResponse responses}. An {@code
      - * HttpClient} is created through a {@link HttpClient#newBuilder() builder}. The
      - * builder can be used to configure per-client state, like: the preferred
      + * HttpClient} is created through a {@link HttpClient.Builder builder}.
      + * The {@link #newBuilder() newBuilder} method returns a builder that creates
      + * instances of the default {@code HttpClient} implementation.
      + * The builder can be used to configure per-client state, like: the preferred
        * protocol version ( HTTP/1.1 or HTTP/2 ), whether to follow redirects, a
        * proxy, an authenticator, etc. Once built, an {@code HttpClient} is immutable,
        * and can be used to send multiple requests.

      A paragraph is added to the API documentation of the newBuilder method:

           /**
            * Creates a new {@code HttpClient} builder.
            *
      +     * <p> Builders returned by this method create instances
      +     * of the default {@code HttpClient} implementation.
      +     *
            * @return an {@code HttpClient.Builder}
            */
           public static Builder newBuilder() {

      A paragraph is added to the API documentation of the HttpClient::send method:

      +     * <p> If the operation is interrupted, the default {@code HttpClient}
      +     * implementation attempts to cancel the HTTP exchange and
      +     * {@link InterruptedException} is thrown.
      +     * No guarantee is made as to exactly <em>when</em> the cancellation request
      +     * may be taken into account. In particular, the request might still get sent
      +     * to the server, as its processing might already have started asynchronously
      +     * in another thread, and the underlying resources may only be released
      +     * asynchronously.
      +     * <ul>
      +     *     <li>With HTTP/1.1, an attempt to cancel may cause the underlying
      +     *         connection to be closed abruptly.
      +     *     <li>With HTTP/2, an attempt to cancel may cause the stream to be reset,
      +     *         or in certain circumstances, may also cause the connection to be
      +     *         closed abruptly, if, for instance, the thread is currently trying
      +     *         to write to the underlying socket.
      +     * </ul>
      +     *

      A paragraph is added to the API documentation of the three args HttpClient::sendAsync method:

      +     * <p> The default {@code HttpClient} implementation returns
      +     * {@code CompletableFuture} objects that are <em>cancelable</em>.
      +     * {@code CompletableFuture} objects {@linkplain CompletableFuture#newIncompleteFuture()
      +     * derived} from cancelable futures are themselves <em>cancelable</em>.
      +     * Invoking {@linkplain CompletableFuture#cancel(boolean) cancel(true)}
      +     * on a cancelable future that is not completed, attempts to cancel the HTTP exchange
      +     * in an effort to release underlying resources as soon as possible.
      +     * No guarantee is made as to exactly <em>when</em> the cancellation request
      +     * may be taken into account. In particular, the request might still get sent
      +     * to the server, as its processing might already have started asynchronously
      +     * in another thread, and the underlying resources may only be released
      +     * asynchronously.
      +     * <ul>
      +     *     <li>With HTTP/1.1, an attempt to cancel may cause the underlying connection
      +     *         to be closed abruptly.
      +     *     <li>With HTTP/2, an attempt to cancel may cause the stream to be reset.
      +     * </ul>
      +     *

            dfuchs Daniel Fuchs
            alanb Alan Bateman
            Alan Bateman, Chris Hegarty, Michael McMahon
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: