1 /*
2  * Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package jdk.internal.net.http;
27 
28 import java.io.IOException;
29 import java.net.http.HttpClient;
30 import java.net.http.HttpResponse;
31 import java.util.concurrent.CompletableFuture;
32 import java.util.concurrent.Executor;
33 
34 import jdk.internal.net.http.common.Logger;
35 import jdk.internal.net.http.common.MinimalFuture;
36 import jdk.internal.net.http.common.Utils;
37 
38 import static java.net.http.HttpClient.Version.HTTP_1_1;
39 
40 /**
41  * Splits request so that headers and body can be sent separately with optional
42  * (multiple) responses in between (e.g. 100 Continue). Also request and
43  * response always sent/received in different calls.
44  *
45  * Synchronous and asynchronous versions of each method are provided.
46  *
47  * Separate implementations of this class exist for HTTP/1.1 and HTTP/2
48  *      Http1Exchange   (HTTP/1.1)
49  *      Stream          (HTTP/2)
50  *
51  * These implementation classes are where work is allocated to threads.
52  */
53 abstract class ExchangeImpl<T> {
54 
55     private static final Logger debug =
56             Utils.getDebugLogger("ExchangeImpl"::toString, Utils.DEBUG);
57 
58     final Exchange<T> exchange;
59 
ExchangeImpl(Exchange<T> e)60     ExchangeImpl(Exchange<T> e) {
61         // e == null means a http/2 pushed stream
62         this.exchange = e;
63     }
64 
getExchange()65     final Exchange<T> getExchange() {
66         return exchange;
67     }
68 
client()69     HttpClient client() {
70         return exchange.client();
71     }
72 
73     /**
74      * Returns the {@link HttpConnection} instance to which this exchange is
75      * assigned.
76      */
connection()77     abstract HttpConnection connection();
78 
79     /**
80      * Initiates a new exchange and assigns it to a connection if one exists
81      * already. connection usually null.
82      */
83     static <U> CompletableFuture<? extends ExchangeImpl<U>>
get(Exchange<U> exchange, HttpConnection connection)84     get(Exchange<U> exchange, HttpConnection connection)
85     {
86         if (exchange.version() == HTTP_1_1) {
87             if (debug.on())
88                 debug.log("get: HTTP/1.1: new Http1Exchange");
89             return createHttp1Exchange(exchange, connection);
90         } else {
91             Http2ClientImpl c2 = exchange.client().client2(); // #### improve
92             HttpRequestImpl request = exchange.request();
93             CompletableFuture<Http2Connection> c2f = c2.getConnectionFor(request, exchange);
94             if (debug.on())
95                 debug.log("get: Trying to get HTTP/2 connection");
96             // local variable required here; see JDK-8223553
97             CompletableFuture<CompletableFuture<? extends ExchangeImpl<U>>> fxi =
98                 c2f.handle((h2c, t) -> createExchangeImpl(h2c, t, exchange, connection));
99             return fxi.thenCompose(x->x);
100         }
101     }
102 
103     private static <U> CompletableFuture<? extends ExchangeImpl<U>>
createExchangeImpl(Http2Connection c, Throwable t, Exchange<U> exchange, HttpConnection connection)104     createExchangeImpl(Http2Connection c,
105                        Throwable t,
106                        Exchange<U> exchange,
107                        HttpConnection connection)
108     {
109         if (debug.on())
110             debug.log("handling HTTP/2 connection creation result");
111         boolean secure = exchange.request().secure();
112         if (t != null) {
113             if (debug.on())
114                 debug.log("handling HTTP/2 connection creation failed: %s",
115                                  (Object)t);
116             t = Utils.getCompletionCause(t);
117             if (t instanceof Http2Connection.ALPNException) {
118                 Http2Connection.ALPNException ee = (Http2Connection.ALPNException)t;
119                 AbstractAsyncSSLConnection as = ee.getConnection();
120                 if (debug.on())
121                     debug.log("downgrading to HTTP/1.1 with: %s", as);
122                 CompletableFuture<? extends ExchangeImpl<U>> ex =
123                         createHttp1Exchange(exchange, as);
124                 return ex;
125             } else {
126                 if (debug.on())
127                     debug.log("HTTP/2 connection creation failed "
128                                      + "with unexpected exception: %s", (Object)t);
129                 return MinimalFuture.failedFuture(t);
130             }
131         }
132         if (secure && c== null) {
133             if (debug.on())
134                 debug.log("downgrading to HTTP/1.1 ");
135             CompletableFuture<? extends ExchangeImpl<U>> ex =
136                     createHttp1Exchange(exchange, null);
137             return ex;
138         }
139         if (c == null) {
140             // no existing connection. Send request with HTTP 1 and then
141             // upgrade if successful
142             if (debug.on())
143                 debug.log("new Http1Exchange, try to upgrade");
144             return createHttp1Exchange(exchange, connection)
145                     .thenApply((e) -> {
146                         exchange.h2Upgrade();
147                         return e;
148                     });
149         } else {
150             if (debug.on()) debug.log("creating HTTP/2 streams");
151             Stream<U> s = c.createStream(exchange);
152             CompletableFuture<? extends ExchangeImpl<U>> ex = MinimalFuture.completedFuture(s);
153             return ex;
154         }
155     }
156 
157     private static <T> CompletableFuture<Http1Exchange<T>>
createHttp1Exchange(Exchange<T> ex, HttpConnection as)158     createHttp1Exchange(Exchange<T> ex, HttpConnection as)
159     {
160         try {
161             return MinimalFuture.completedFuture(new Http1Exchange<>(ex, as));
162         } catch (Throwable e) {
163             return MinimalFuture.failedFuture(e);
164         }
165     }
166 
167     // Called for 204 response - when no body is permitted
nullBody(HttpResponse<T> resp, Throwable t)168     void nullBody(HttpResponse<T> resp, Throwable t) {
169         // Needed for HTTP/1.1 to close the connection or return it to the pool
170         // Needed for HTTP/2 to subscribe a dummy subscriber and close the stream
171     }
172 
173     /* The following methods have separate HTTP/1.1 and HTTP/2 implementations */
174 
sendHeadersAsync()175     abstract CompletableFuture<ExchangeImpl<T>> sendHeadersAsync();
176 
177     /** Sends a request body, after request headers have been sent. */
sendBodyAsync()178     abstract CompletableFuture<ExchangeImpl<T>> sendBodyAsync();
179 
readBodyAsync(HttpResponse.BodyHandler<T> handler, boolean returnConnectionToPool, Executor executor)180     abstract CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler,
181                                                 boolean returnConnectionToPool,
182                                                 Executor executor);
183 
184     /**
185      * Ignore/consume the body.
186      */
ignoreBody()187     abstract CompletableFuture<Void> ignoreBody();
188 
189 
190     /** Gets the response headers. Completes before body is read. */
getResponseAsync(Executor executor)191     abstract CompletableFuture<Response> getResponseAsync(Executor executor);
192 
193 
194     /** Cancels a request.  Not currently exposed through API. */
cancel()195     abstract void cancel();
196 
197     /**
198      * Cancels a request with a cause.  Not currently exposed through API.
199      */
cancel(IOException cause)200     abstract void cancel(IOException cause);
201 
202     /**
203      * Called when the exchange is released, so that cleanup actions may be
204      * performed - such as deregistering callbacks.
205      * Typically released is called during upgrade, when an HTTP/2 stream
206      * takes over from an Http1Exchange, or when a new exchange is created
207      * during a multi exchange before the final response body was received.
208      */
released()209     abstract void released();
210 
211     /**
212      * Called when the exchange is completed, so that cleanup actions may be
213      * performed - such as deregistering callbacks.
214      * Typically, completed is called at the end of the exchange, when the
215      * final response body has been received (or an error has caused the
216      * completion of the exchange).
217      */
completed()218     abstract void completed();
219 
220     /**
221      * Returns true if this exchange was canceled.
222      * @return true if this exchange was canceled.
223      */
isCanceled()224     abstract boolean isCanceled();
225 
226     /**
227      * Returns the cause for which this exchange was canceled, if available.
228      * @return the cause for which this exchange was canceled, if available.
229      */
getCancelCause()230     abstract Throwable getCancelCause();
231 
232     // Mark the exchange as upgraded
233     // Needed to handle cancellation during the upgrade from
234     // Http1Exchange to Stream
upgraded()235     void upgraded() { }
236 }
237