1 /* 2 * Copyright (c) 2015, 2017, 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.incubator.http; 27 28 import java.io.IOException; 29 import java.lang.System.Logger.Level; 30 import java.util.concurrent.CompletableFuture; 31 import java.util.concurrent.Executor; 32 import java.util.function.Function; 33 import jdk.incubator.http.internal.common.MinimalFuture; 34 import static jdk.incubator.http.HttpClient.Version.HTTP_1_1; 35 import jdk.incubator.http.internal.common.Utils; 36 37 /** 38 * Splits request so that headers and body can be sent separately with optional 39 * (multiple) responses in between (e.g. 100 Continue). Also request and 40 * response always sent/received in different calls. 41 * 42 * Synchronous and asynchronous versions of each method are provided. 43 * 44 * Separate implementations of this class exist for HTTP/1.1 and HTTP/2 45 * Http1Exchange (HTTP/1.1) 46 * Stream (HTTP/2) 47 * 48 * These implementation classes are where work is allocated to threads. 49 */ 50 abstract class ExchangeImpl<T> { 51 52 static final boolean DEBUG = Utils.DEBUG; // Revisit: temporary dev flag. 53 private static final System.Logger DEBUG_LOGGER = 54 Utils.getDebugLogger("ExchangeImpl"::toString, DEBUG); 55 56 final Exchange<T> exchange; 57 58 ExchangeImpl(Exchange<T> e) { 59 // e == null means a http/2 pushed stream 60 this.exchange = e; 61 } 62 63 final Exchange<T> getExchange() { 64 return exchange; 65 } 66 67 68 /** 69 * Returns the {@link HttpConnection} instance to which this exchange is 70 * assigned. 71 */ 72 abstract HttpConnection connection(); 73 74 /** 75 * Initiates a new exchange and assigns it to a connection if one exists 76 * already. connection usually null. 77 */ 78 static <U> CompletableFuture<? extends ExchangeImpl<U>> 79 get(Exchange<U> exchange, HttpConnection connection) 80 { 81 if (exchange.version() == HTTP_1_1) { 82 DEBUG_LOGGER.log(Level.DEBUG, "get: HTTP/1.1: new Http1Exchange"); 83 return createHttp1Exchange(exchange, connection); 84 } else { 85 Http2ClientImpl c2 = exchange.client().client2(); // TODO: improve 86 HttpRequestImpl request = exchange.request(); 87 CompletableFuture<Http2Connection> c2f = c2.getConnectionFor(request); 88 DEBUG_LOGGER.log(Level.DEBUG, "get: Trying to get HTTP/2 connection"); 89 return c2f.handle((h2c, t) -> createExchangeImpl(h2c, t, exchange, connection)) 90 .thenCompose(Function.identity()); 91 } 92 } 93 94 private static <U> CompletableFuture<? extends ExchangeImpl<U>> 95 createExchangeImpl(Http2Connection c, 96 Throwable t, 97 Exchange<U> exchange, 98 HttpConnection connection) 99 { 100 DEBUG_LOGGER.log(Level.DEBUG, "handling HTTP/2 connection creation result"); 101 if (t != null) { 102 DEBUG_LOGGER.log(Level.DEBUG, 103 "handling HTTP/2 connection creation failed: %s", 104 (Object)t); 105 t = Utils.getCompletionCause(t); 106 if (t instanceof Http2Connection.ALPNException) { 107 Http2Connection.ALPNException ee = (Http2Connection.ALPNException)t; 108 AbstractAsyncSSLConnection as = ee.getConnection(); 109 DEBUG_LOGGER.log(Level.DEBUG, "downgrading to HTTP/1.1 with: %s", as); 110 CompletableFuture<? extends ExchangeImpl<U>> ex = 111 createHttp1Exchange(exchange, as); 112 return ex; 113 } else { 114 DEBUG_LOGGER.log(Level.DEBUG, "HTTP/2 connection creation failed " 115 + "with unexpected exception: %s", (Object)t); 116 return CompletableFuture.failedFuture(t); 117 } 118 } 119 if (c == null) { 120 // no existing connection. Send request with HTTP 1 and then 121 // upgrade if successful 122 DEBUG_LOGGER.log(Level.DEBUG, "new Http1Exchange, try to upgrade"); 123 return createHttp1Exchange(exchange, connection) 124 .thenApply((e) -> { 125 exchange.h2Upgrade(); 126 return e; 127 }); 128 } else { 129 DEBUG_LOGGER.log(Level.DEBUG, "creating HTTP/2 streams"); 130 Stream<U> s = c.createStream(exchange); 131 CompletableFuture<? extends ExchangeImpl<U>> ex = MinimalFuture.completedFuture(s); 132 return ex; 133 } 134 } 135 136 private static <T> CompletableFuture<Http1Exchange<T>> 137 createHttp1Exchange(Exchange<T> ex, HttpConnection as) 138 { 139 try { 140 return MinimalFuture.completedFuture(new Http1Exchange<>(ex, as)); 141 } catch (Throwable e) { 142 return MinimalFuture.failedFuture(e); 143 } 144 } 145 146 /* The following methods have separate HTTP/1.1 and HTTP/2 implementations */ 147 148 abstract CompletableFuture<ExchangeImpl<T>> sendHeadersAsync(); 149 150 /** Sends a request body, after request headers have been sent. */ 151 abstract CompletableFuture<ExchangeImpl<T>> sendBodyAsync(); 152 153 abstract CompletableFuture<T> readBodyAsync(HttpResponse.BodyHandler<T> handler, 154 boolean returnConnectionToPool, 155 Executor executor); 156 157 /** 158 * Ignore/consume the body. 159 */ 160 abstract CompletableFuture<Void> ignoreBody(); 161 162 /** Gets the response headers. Completes before body is read. */ 163 abstract CompletableFuture<Response> getResponseAsync(Executor executor); 164 165 166 /** Cancels a request. Not currently exposed through API. */ 167 abstract void cancel(); 168 169 /** 170 * Cancels a request with a cause. Not currently exposed through API. 171 */ 172 abstract void cancel(IOException cause); 173 174 /** 175 * Called when the exchange is released, so that cleanup actions may be 176 * performed - such as deregistering callbacks. 177 * Typically released is called during upgrade, when an HTTP/2 stream 178 * takes over from an Http1Exchange, or when a new exchange is created 179 * during a multi exchange before the final response body was received. 180 */ 181 abstract void released(); 182 183 /** 184 * Called when the exchange is completed, so that cleanup actions may be 185 * performed - such as deregistering callbacks. 186 * Typically, completed is called at the end of the exchange, when the 187 * final response body has been received (or an error has caused the 188 * completion of the exchange). 189 */ 190 abstract void completed(); 191 192 /** 193 * Returns true if this exchange was canceled. 194 * @return true if this exchange was canceled. 195 */ 196 abstract boolean isCanceled(); 197 198 /** 199 * Returns the cause for which this exchange was canceled, if available. 200 * @return the cause for which this exchange was canceled, if available. 201 */ 202 abstract Throwable getCancelCause(); 203 }