Path: blob/master/src/java.net.http/share/classes/jdk/internal/net/http/ExchangeImpl.java
41171 views
/*1* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package jdk.internal.net.http;2627import java.io.IOException;28import java.net.http.HttpClient;29import java.net.http.HttpResponse;30import java.util.concurrent.CompletableFuture;31import java.util.concurrent.Executor;3233import jdk.internal.net.http.common.Logger;34import jdk.internal.net.http.common.MinimalFuture;35import jdk.internal.net.http.common.Utils;3637import static java.net.http.HttpClient.Version.HTTP_1_1;3839/**40* Splits request so that headers and body can be sent separately with optional41* (multiple) responses in between (e.g. 100 Continue). Also request and42* response always sent/received in different calls.43*44* Synchronous and asynchronous versions of each method are provided.45*46* Separate implementations of this class exist for HTTP/1.1 and HTTP/247* Http1Exchange (HTTP/1.1)48* Stream (HTTP/2)49*50* These implementation classes are where work is allocated to threads.51*/52abstract class ExchangeImpl<T> {5354private static final Logger debug =55Utils.getDebugLogger("ExchangeImpl"::toString, Utils.DEBUG);5657final Exchange<T> exchange;5859ExchangeImpl(Exchange<T> e) {60// e == null means a http/2 pushed stream61this.exchange = e;62}6364final Exchange<T> getExchange() {65return exchange;66}6768HttpClient client() {69return exchange.client();70}7172/**73* Returns the {@link HttpConnection} instance to which this exchange is74* assigned.75*/76abstract HttpConnection connection();7778/**79* Initiates a new exchange and assigns it to a connection if one exists80* already. connection usually null.81*/82static <U> CompletableFuture<? extends ExchangeImpl<U>>83get(Exchange<U> exchange, HttpConnection connection)84{85if (exchange.version() == HTTP_1_1) {86if (debug.on())87debug.log("get: HTTP/1.1: new Http1Exchange");88return createHttp1Exchange(exchange, connection);89} else {90Http2ClientImpl c2 = exchange.client().client2(); // #### improve91HttpRequestImpl request = exchange.request();92CompletableFuture<Http2Connection> c2f = c2.getConnectionFor(request, exchange);93if (debug.on())94debug.log("get: Trying to get HTTP/2 connection");95// local variable required here; see JDK-822355396CompletableFuture<CompletableFuture<? extends ExchangeImpl<U>>> fxi =97c2f.handle((h2c, t) -> createExchangeImpl(h2c, t, exchange, connection));98return fxi.thenCompose(x->x);99}100}101102private static <U> CompletableFuture<? extends ExchangeImpl<U>>103createExchangeImpl(Http2Connection c,104Throwable t,105Exchange<U> exchange,106HttpConnection connection)107{108if (debug.on())109debug.log("handling HTTP/2 connection creation result");110boolean secure = exchange.request().secure();111if (t != null) {112if (debug.on())113debug.log("handling HTTP/2 connection creation failed: %s",114(Object)t);115t = Utils.getCompletionCause(t);116if (t instanceof Http2Connection.ALPNException) {117Http2Connection.ALPNException ee = (Http2Connection.ALPNException)t;118AbstractAsyncSSLConnection as = ee.getConnection();119if (debug.on())120debug.log("downgrading to HTTP/1.1 with: %s", as);121CompletableFuture<? extends ExchangeImpl<U>> ex =122createHttp1Exchange(exchange, as);123return ex;124} else {125if (debug.on())126debug.log("HTTP/2 connection creation failed "127+ "with unexpected exception: %s", (Object)t);128return MinimalFuture.failedFuture(t);129}130}131if (secure && c== null) {132if (debug.on())133debug.log("downgrading to HTTP/1.1 ");134CompletableFuture<? extends ExchangeImpl<U>> ex =135createHttp1Exchange(exchange, null);136return ex;137}138if (c == null) {139// no existing connection. Send request with HTTP 1 and then140// upgrade if successful141if (debug.on())142debug.log("new Http1Exchange, try to upgrade");143return createHttp1Exchange(exchange, connection)144.thenApply((e) -> {145exchange.h2Upgrade();146return e;147});148} else {149if (debug.on()) debug.log("creating HTTP/2 streams");150Stream<U> s = c.createStream(exchange);151CompletableFuture<? extends ExchangeImpl<U>> ex = MinimalFuture.completedFuture(s);152return ex;153}154}155156private static <T> CompletableFuture<Http1Exchange<T>>157createHttp1Exchange(Exchange<T> ex, HttpConnection as)158{159try {160return MinimalFuture.completedFuture(new Http1Exchange<>(ex, as));161} catch (Throwable e) {162return MinimalFuture.failedFuture(e);163}164}165166// Called for 204 response - when no body is permitted167void nullBody(HttpResponse<T> resp, Throwable t) {168// Needed for HTTP/1.1 to close the connection or return it to the pool169// Needed for HTTP/2 to subscribe a dummy subscriber and close the stream170}171172/* The following methods have separate HTTP/1.1 and HTTP/2 implementations */173174abstract CompletableFuture<ExchangeImpl<T>> sendHeadersAsync();175176/** Sends a request body, after request headers have been sent. */177abstract CompletableFuture<ExchangeImpl<T>> sendBodyAsync();178179abstract CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler,180boolean returnConnectionToPool,181Executor executor);182183/**184* Ignore/consume the body.185*/186abstract CompletableFuture<Void> ignoreBody();187188189/** Gets the response headers. Completes before body is read. */190abstract CompletableFuture<Response> getResponseAsync(Executor executor);191192193/** Cancels a request. Not currently exposed through API. */194abstract void cancel();195196/**197* Cancels a request with a cause. Not currently exposed through API.198*/199abstract void cancel(IOException cause);200201/**202* Called when the exchange is released, so that cleanup actions may be203* performed - such as deregistering callbacks.204* Typically released is called during upgrade, when an HTTP/2 stream205* takes over from an Http1Exchange, or when a new exchange is created206* during a multi exchange before the final response body was received.207*/208abstract void released();209210/**211* Called when the exchange is completed, so that cleanup actions may be212* performed - such as deregistering callbacks.213* Typically, completed is called at the end of the exchange, when the214* final response body has been received (or an error has caused the215* completion of the exchange).216*/217abstract void completed();218219/**220* Returns true if this exchange was canceled.221* @return true if this exchange was canceled.222*/223abstract boolean isCanceled();224225/**226* Returns the cause for which this exchange was canceled, if available.227* @return the cause for which this exchange was canceled, if available.228*/229abstract Throwable getCancelCause();230231// Mark the exchange as upgraded232// Needed to handle cancellation during the upgrade from233// Http1Exchange to Stream234void upgraded() { }235}236237238