|
28 | 28 | import java.io.IOException; |
29 | 29 | import java.io.UncheckedIOException; |
30 | 30 | import java.net.InetAddress; |
| 31 | +import java.net.http.HttpResponse.BodyHandlers; |
31 | 32 | import java.nio.channels.Selector; |
32 | 33 | import java.net.Authenticator; |
33 | 34 | import java.net.CookieHandler; |
|
38 | 39 | import java.security.AccessController; |
39 | 40 | import java.security.PrivilegedAction; |
40 | 41 | import java.time.Duration; |
| 42 | +import java.util.Objects; |
41 | 43 | import java.util.Optional; |
42 | 44 | import java.util.concurrent.CompletableFuture; |
43 | 45 | import java.util.concurrent.Executor; |
44 | 46 | import javax.net.ssl.SSLContext; |
45 | 47 | import javax.net.ssl.SSLParameters; |
46 | 48 | import java.net.http.HttpResponse.BodyHandler; |
47 | 49 | import java.net.http.HttpResponse.PushPromiseHandler; |
| 50 | +import java.util.concurrent.Flow.Subscription; |
| 51 | + |
48 | 52 | import jdk.internal.net.http.HttpClientBuilderImpl; |
49 | 53 |
|
50 | 54 | /** |
|
61 | 65 | * and can be used to send multiple requests. |
62 | 66 | * |
63 | 67 | * <p> An {@code HttpClient} provides configuration information, and resource |
64 | | - * sharing, for all requests sent through it. |
| 68 | + * sharing, for all requests sent through it. An {@code HttpClient} instance |
| 69 | + * typically manages its own pools of connections, which it may then reuse |
| 70 | + * as and when necessary. Connection pools are typically not shared between |
| 71 | + * {@code HttpClient} instances. Creating a new client for each operation, |
| 72 | + * though possible, will usually prevent reusing such connections. |
65 | 73 | * |
66 | 74 | * <p> A {@link BodyHandler BodyHandler} must be supplied for each {@link |
67 | 75 | * HttpRequest} sent. The {@code BodyHandler} determines how to handle the |
|
119 | 127 | * proxying) and a {@code URL} string of the form {@code "socket://host:port"} |
120 | 128 | * where host and port specify the proxy's address. |
121 | 129 | * |
122 | | - * @implNote If an explicit {@linkplain HttpClient.Builder#executor(Executor) |
| 130 | + * @apiNote |
| 131 | + * Resources allocated by the {@code HttpClient} may be |
| 132 | + * reclaimed early by {@linkplain #close() closing} the client. |
| 133 | + * |
| 134 | + * @implNote |
| 135 | + * <p id="closing"> |
| 136 | + * The JDK built-in implementation of the {@code HttpClient} overrides |
| 137 | + * {@link #close()}, {@link #shutdown()}, {@link #shutdownNow()}, |
| 138 | + * {@link #awaitTermination(Duration)}, and {@link #isTerminated()} to |
| 139 | + * provide a best effort implementation. Failing to close, cancel, or |
| 140 | + * read returned streams to exhaustion, such as streams provided when using |
| 141 | + * {@link BodyHandlers#ofInputStream()}, {@link BodyHandlers#ofLines()}, or |
| 142 | + * {@link BodyHandlers#ofPublisher()}, may prevent requests submitted |
| 143 | + * before an {@linkplain #shutdown() orderly shutdown} |
| 144 | + * to run to completion. Likewise, failing to |
| 145 | + * {@linkplain Subscription#request(long) request data} or {@linkplain |
| 146 | + * Subscription#cancel() cancel subscriptions} from a custom {@linkplain |
| 147 | + * java.net.http.HttpResponse.BodySubscriber BodySubscriber} may stop |
| 148 | + * delivery of data and {@linkplain #awaitTermination(Duration) stall an |
| 149 | + * orderly shutdown}. |
| 150 | + * |
| 151 | + * <p> |
| 152 | + * If an explicit {@linkplain HttpClient.Builder#executor(Executor) |
123 | 153 | * executor} has not been set for an {@code HttpClient}, and a security manager |
124 | 154 | * has been installed, then the default executor will execute asynchronous and |
125 | 155 | * dependent tasks in a context that is granted no permissions. Custom |
|
132 | 162 | * |
133 | 163 | * @since 11 |
134 | 164 | */ |
135 | | -public abstract class HttpClient { |
| 165 | +public abstract class HttpClient implements AutoCloseable { |
136 | 166 |
|
137 | 167 | /** |
138 | 168 | * Creates an HttpClient. |
@@ -599,7 +629,8 @@ public enum Redirect { |
599 | 629 | * @param request the request |
600 | 630 | * @param responseBodyHandler the response body handler |
601 | 631 | * @return the response |
602 | | - * @throws IOException if an I/O error occurs when sending or receiving |
| 632 | + * @throws IOException if an I/O error occurs when sending or receiving, or |
| 633 | + * the client has {@linkplain ##closing shut down} |
603 | 634 | * @throws InterruptedException if the operation is interrupted |
604 | 635 | * @throws IllegalArgumentException if the {@code request} argument is not |
605 | 636 | * a request that could have been validly built as specified by {@link |
@@ -646,7 +677,8 @@ public enum Redirect { |
646 | 677 | * |
647 | 678 | * <p> The returned completable future completes exceptionally with: |
648 | 679 | * <ul> |
649 | | - * <li>{@link IOException} - if an I/O error occurs when sending or receiving</li> |
| 680 | + * <li>{@link IOException} - if an I/O error occurs when sending or receiving, |
| 681 | + * or the client has {@linkplain ##closing shut down}.</li> |
650 | 682 | * <li>{@link SecurityException} - If a security manager has been installed |
651 | 683 | * and it denies {@link java.net.URLPermission access} to the |
652 | 684 | * URL in the given request, or proxy if one is configured. |
@@ -730,4 +762,154 @@ public enum Redirect { |
730 | 762 | public WebSocket.Builder newWebSocketBuilder() { |
731 | 763 | throw new UnsupportedOperationException(); |
732 | 764 | } |
| 765 | + |
| 766 | + /** |
| 767 | + * Initiates an orderly shutdown in which requests previously |
| 768 | + * submitted with {@code send} or {@code sendAsync} |
| 769 | + * are run to completion, but no new request will be accepted. |
| 770 | + * Running a request to completion may involve running several |
| 771 | + * operations in the background, including {@linkplain ##closing |
| 772 | + * waiting for responses to be delivered}, which will all have to |
| 773 | + * run to completion until the request is considered completed. |
| 774 | + * |
| 775 | + * Invocation has no additional effect if already shut down. |
| 776 | + * |
| 777 | + * <p>This method does not wait for previously submitted request |
| 778 | + * to complete execution. Use {@link #awaitTermination(Duration) |
| 779 | + * awaitTermination} or {@link #close() close} to do that. |
| 780 | + * |
| 781 | + * @implSpec |
| 782 | + * The default implementation of this method does nothing. Subclasses should |
| 783 | + * override this method to implement the appropriate behavior. |
| 784 | + * |
| 785 | + * @see ##closing Implementation Note on closing the HttpClient |
| 786 | + * |
| 787 | + * @since 21 |
| 788 | + */ |
| 789 | + public void shutdown() { } |
| 790 | + |
| 791 | + /** |
| 792 | + * Blocks until all operations have completed execution after a shutdown |
| 793 | + * request, or the {@code duration} elapses, or the current thread is |
| 794 | + * {@linkplain Thread#interrupt() interrupted}, whichever happens first. |
| 795 | + * Operations are any tasks required to run a request previously |
| 796 | + * submitted with {@code send} or {@code sendAsync} to completion. |
| 797 | + * |
| 798 | + * <p> This method does not wait if the duration to wait is less than or |
| 799 | + * equal to zero. In this case, the method just tests if the thread has |
| 800 | + * terminated. |
| 801 | + * |
| 802 | + * @implSpec |
| 803 | + * The default implementation of this method checks for null arguments, but |
| 804 | + * otherwise does nothing and returns true. |
| 805 | + * Subclasses should override this method to implement the proper behavior. |
| 806 | + * |
| 807 | + * @param duration the maximum time to wait |
| 808 | + * @return {@code true} if this client terminated and |
| 809 | + * {@code false} if the timeout elapsed before termination |
| 810 | + * @throws InterruptedException if interrupted while waiting |
| 811 | + * |
| 812 | + * @see ##closing Implementation Note on closing the HttpClient |
| 813 | + * |
| 814 | + * @since 21 |
| 815 | + */ |
| 816 | + public boolean awaitTermination(Duration duration) throws InterruptedException { |
| 817 | + Objects.requireNonNull(duration); |
| 818 | + return true; |
| 819 | + } |
| 820 | + |
| 821 | + /** |
| 822 | + * Returns {@code true} if all operations have completed following |
| 823 | + * a shutdown. |
| 824 | + * Operations are any tasks required to run a request previously |
| 825 | + * submitted with {@code send} or {@code sendAsync} to completion. |
| 826 | + * <p> Note that {@code isTerminated} is never {@code true} unless |
| 827 | + * either {@code shutdown} or {@code shutdownNow} was called first. |
| 828 | + * |
| 829 | + * @implSpec |
| 830 | + * The default implementation of this method does nothing and returns false. |
| 831 | + * Subclasses should override this method to implement the proper behavior. |
| 832 | + * |
| 833 | + * @return {@code true} if all tasks have completed following a shutdown |
| 834 | + * |
| 835 | + * @see ##closing Implementation Note on closing the HttpClient |
| 836 | + * |
| 837 | + * @since 21 |
| 838 | + */ |
| 839 | + public boolean isTerminated() { |
| 840 | + return false; |
| 841 | + } |
| 842 | + |
| 843 | + /** |
| 844 | + * This method attempts to initiate an immediate shutdown. |
| 845 | + * An implementation of this method may attempt to |
| 846 | + * interrupt operations that are actively running. |
| 847 | + * Operations are any tasks required to run a request previously |
| 848 | + * submitted with {@code send} or {@code sendAsync} to completion. |
| 849 | + * The behavior of actively running operations when interrupted |
| 850 | + * is undefined. In particular, there is no guarantee that |
| 851 | + * interrupted operations will terminate, or that code waiting |
| 852 | + * on these operations will ever be notified. |
| 853 | + * |
| 854 | + * @implSpec |
| 855 | + * The default implementation of this method simply calls {@link #shutdown()}. |
| 856 | + * Subclasses should override this method to implement the appropriate |
| 857 | + * behavior. |
| 858 | + * |
| 859 | + * @see ##closing Implementation Note on closing the HttpClient |
| 860 | + * |
| 861 | + * @since 21 |
| 862 | + */ |
| 863 | + public void shutdownNow() { |
| 864 | + shutdown(); |
| 865 | + } |
| 866 | + |
| 867 | + /** |
| 868 | + * Initiates an orderly shutdown in which requests previously |
| 869 | + * submitted to {@code send} or {@code sendAsync} |
| 870 | + * are run to completion, but no new request will be accepted. |
| 871 | + * Running a request to completion may involve running several |
| 872 | + * operations in the background, including {@linkplain ##closing |
| 873 | + * waiting for responses to be delivered}. |
| 874 | + * This method waits until all operations have completed execution |
| 875 | + * and the client has terminated. |
| 876 | + * |
| 877 | + * <p> If interrupted while waiting, this method may attempt to stop all |
| 878 | + * operations by calling {@link #shutdownNow()}. It then continues to wait |
| 879 | + * until all actively executing operations have completed. |
| 880 | + * The interrupt status will be re-asserted before this method returns. |
| 881 | + * |
| 882 | + * <p> If already terminated, invoking this method has no effect. |
| 883 | + * |
| 884 | + * @implSpec |
| 885 | + * The default implementation invokes {@code shutdown()} and waits for tasks |
| 886 | + * to complete execution with {@code awaitTermination}. |
| 887 | + * |
| 888 | + * @see ##closing Implementation Note on closing the HttpClient |
| 889 | + * |
| 890 | + * @since 21 |
| 891 | + */ |
| 892 | + @Override |
| 893 | + public void close() { |
| 894 | + boolean terminated = isTerminated(); |
| 895 | + if (!terminated) { |
| 896 | + shutdown(); |
| 897 | + boolean interrupted = false; |
| 898 | + while (!terminated) { |
| 899 | + try { |
| 900 | + terminated = awaitTermination(Duration.ofDays(1L)); |
| 901 | + } catch (InterruptedException e) { |
| 902 | + if (!interrupted) { |
| 903 | + interrupted = true; |
| 904 | + shutdownNow(); |
| 905 | + if (isTerminated()) break; |
| 906 | + } |
| 907 | + } |
| 908 | + } |
| 909 | + if (interrupted) { |
| 910 | + Thread.currentThread().interrupt(); |
| 911 | + } |
| 912 | + } |
| 913 | + } |
| 914 | + |
733 | 915 | } |
0 commit comments