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 26package jdk.incubator.http; 27 28import javax.net.ssl.SSLParameters; 29import java.io.Closeable; 30import java.io.IOException; 31import java.net.InetSocketAddress; 32import java.nio.ByteBuffer; 33import java.nio.channels.SocketChannel; 34import java.util.concurrent.CompletableFuture; 35 36import jdk.incubator.http.internal.common.ByteBufferReference; 37import jdk.incubator.http.internal.common.Utils; 38 39/** 40 * Wraps socket channel layer and takes care of SSL also. 41 * 42 * Subtypes are: 43 * PlainHttpConnection: regular direct TCP connection to server 44 * PlainProxyConnection: plain text proxy connection 45 * PlainTunnelingConnection: opens plain text (CONNECT) tunnel to server 46 * SSLConnection: TLS channel direct to server 47 * SSLTunnelConnection: TLS channel via (CONNECT) proxy tunnel 48 */ 49abstract class HttpConnection implements Closeable { 50 51 enum Mode { 52 BLOCKING, 53 NON_BLOCKING, 54 ASYNC 55 } 56 57 protected Mode mode; 58 59 // address we are connected to. Could be a server or a proxy 60 final InetSocketAddress address; 61 final HttpClientImpl client; 62 63 HttpConnection(InetSocketAddress address, HttpClientImpl client) { 64 this.address = address; 65 this.client = client; 66 } 67 68 /** 69 * Public API to this class. addr is the ultimate destination. Any proxies 70 * etc are figured out from the request. Returns an instance of one of the 71 * following 72 * PlainHttpConnection 73 * PlainTunnelingConnection 74 * SSLConnection 75 * SSLTunnelConnection 76 * 77 * When object returned, connect() or connectAsync() must be called, which 78 * when it returns/completes, the connection is usable for requests. 79 */ 80 public static HttpConnection getConnection( 81 InetSocketAddress addr, HttpClientImpl client, HttpRequestImpl request) 82 { 83 return getConnectionImpl(addr, client, request, false); 84 } 85 86 /** 87 * Called specifically to get an async connection for HTTP/2 over SSL. 88 */ 89 public static HttpConnection getConnection(InetSocketAddress addr, 90 HttpClientImpl client, HttpRequestImpl request, boolean isHttp2) { 91 92 return getConnectionImpl(addr, client, request, isHttp2); 93 } 94 95 public abstract void connect() throws IOException, InterruptedException; 96 97 public abstract CompletableFuture<Void> connectAsync(); 98 99 /** 100 * Returns whether this connection is connected to its destination 101 */ 102 abstract boolean connected(); 103 104 abstract boolean isSecure(); 105 106 abstract boolean isProxied(); 107 108 /** 109 * Completes when the first byte of the response is available to be read. 110 */ 111 abstract CompletableFuture<Void> whenReceivingResponse(); 112 113 final boolean isOpen() { 114 return channel().isOpen(); 115 } 116 117 /* Returns either a plain HTTP connection or a plain tunnelling connection 118 * for proxied WebSocket */ 119 private static HttpConnection getPlainConnection(InetSocketAddress addr, 120 InetSocketAddress proxy, 121 HttpRequestImpl request, 122 HttpClientImpl client) { 123 if (request.isWebSocket() && proxy != null) { 124 return new PlainTunnelingConnection(addr, proxy, client); 125 } else { 126 if (proxy == null) { 127 return new PlainHttpConnection(addr, client); 128 } else { 129 return new PlainProxyConnection(proxy, client); 130 } 131 } 132 } 133 134 private static HttpConnection getSSLConnection(InetSocketAddress addr, 135 InetSocketAddress proxy, HttpRequestImpl request, 136 String[] alpn, boolean isHttp2, HttpClientImpl client) 137 { 138 if (proxy != null) { 139 return new SSLTunnelConnection(addr, client, proxy); 140 } else if (!isHttp2) { 141 return new SSLConnection(addr, client, alpn); 142 } else { 143 return new AsyncSSLConnection(addr, client, alpn); 144 } 145 } 146 147 /** 148 * Main factory method. Gets a HttpConnection, either cached or new if 149 * none available. 150 */ 151 private static HttpConnection getConnectionImpl(InetSocketAddress addr, 152 HttpClientImpl client, 153 HttpRequestImpl request, boolean isHttp2) 154 { 155 HttpConnection c = null; 156 InetSocketAddress proxy = request.proxy(client); 157 boolean secure = request.secure(); 158 ConnectionPool pool = client.connectionPool(); 159 String[] alpn = null; 160 161 if (secure && isHttp2) { 162 alpn = new String[2]; 163 alpn[0] = "h2"; 164 alpn[1] = "http/1.1"; 165 } 166 167 if (!secure) { 168 c = pool.getConnection(false, addr, proxy); 169 if (c != null) { 170 return c; 171 } else { 172 return getPlainConnection(addr, proxy, request, client); 173 } 174 } else { 175 if (!isHttp2) { // if http2 we don't cache connections 176 c = pool.getConnection(true, addr, proxy); 177 } 178 if (c != null) { 179 return c; 180 } else { 181 return getSSLConnection(addr, proxy, request, alpn, isHttp2, client); 182 } 183 } 184 } 185 186 void returnToCache(HttpHeaders hdrs) { 187 if (hdrs == null) { 188 // the connection was closed by server 189 close(); 190 return; 191 } 192 if (!isOpen()) { 193 return; 194 } 195 ConnectionPool pool = client.connectionPool(); 196 boolean keepAlive = hdrs.firstValue("Connection") 197 .map((s) -> !s.equalsIgnoreCase("close")) 198 .orElse(true); 199 200 if (keepAlive) { 201 pool.returnToPool(this); 202 } else { 203 close(); 204 } 205 } 206 207 /** 208 * Also check that the number of bytes written is what was expected. This 209 * could be different if the buffer is user-supplied and its internal 210 * pointers were manipulated in a race condition. 211 */ 212 final void checkWrite(long expected, ByteBuffer buffer) throws IOException { 213 long written = write(buffer); 214 if (written != expected) { 215 throw new IOException("incorrect number of bytes written"); 216 } 217 } 218 219 final void checkWrite(long expected, 220 ByteBuffer[] buffers, 221 int start, 222 int length) 223 throws IOException 224 { 225 long written = write(buffers, start, length); 226 if (written != expected) { 227 throw new IOException("incorrect number of bytes written"); 228 } 229 } 230 231 abstract SocketChannel channel(); 232 233 final InetSocketAddress address() { 234 return address; 235 } 236 237 synchronized void configureMode(Mode mode) throws IOException { 238 this.mode = mode; 239 if (mode == Mode.BLOCKING) { 240 channel().configureBlocking(true); 241 } else { 242 channel().configureBlocking(false); 243 } 244 } 245 246 synchronized Mode getMode() { 247 return mode; 248 } 249 250 abstract ConnectionPool.CacheKey cacheKey(); 251 252 // overridden in SSL only 253 SSLParameters sslParameters() { 254 return null; 255 } 256 257 // Methods to be implemented for Plain TCP and SSL 258 259 abstract long write(ByteBuffer[] buffers, int start, int number) 260 throws IOException; 261 262 abstract long write(ByteBuffer buffer) throws IOException; 263 264 // Methods to be implemented for Plain TCP (async mode) and AsyncSSL 265 266 /** 267 * In {@linkplain Mode#ASYNC async mode}, this method puts buffers at the 268 * end of the send queue; Otherwise, it is equivalent to {@link 269 * #write(ByteBuffer[], int, int) write(buffers, 0, buffers.length)}. 270 * When in async mode, calling this method should later be followed by 271 * subsequent flushAsync invocation. 272 * That allows multiple threads to put buffers into the queue while some other 273 * thread is writing. 274 */ 275 abstract void writeAsync(ByteBufferReference[] buffers) throws IOException; 276 277 /** 278 * In {@linkplain Mode#ASYNC async mode}, this method may put 279 * buffers at the beginning of send queue, breaking frames sequence and 280 * allowing to write these buffers before other buffers in the queue; 281 * Otherwise, it is equivalent to {@link 282 * #write(ByteBuffer[], int, int) write(buffers, 0, buffers.length)}. 283 * When in async mode, calling this method should later be followed by 284 * subsequent flushAsync invocation. 285 * That allows multiple threads to put buffers into the queue while some other 286 * thread is writing. 287 */ 288 abstract void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException; 289 290 /** 291 * This method should be called after any writeAsync/writeAsyncUnordered 292 * invocation. 293 * If there is a race to flushAsync from several threads one thread 294 * (race winner) capture flush operation and write the whole queue content. 295 * Other threads (race losers) exits from the method (not blocking) 296 * and continue execution. 297 */ 298 abstract void flushAsync() throws IOException; 299 300 /** 301 * Closes this connection, by returning the socket to its connection pool. 302 */ 303 @Override 304 public abstract void close(); 305 306 abstract void shutdownInput() throws IOException; 307 308 abstract void shutdownOutput() throws IOException; 309 310 /** 311 * Puts position to limit and limit to capacity so we can resume reading 312 * into this buffer, but if required > 0 then limit may be reduced so that 313 * no more than required bytes are read next time. 314 */ 315 static void resumeChannelRead(ByteBuffer buf, int required) { 316 int limit = buf.limit(); 317 buf.position(limit); 318 int capacity = buf.capacity() - limit; 319 if (required > 0 && required < capacity) { 320 buf.limit(limit + required); 321 } else { 322 buf.limit(buf.capacity()); 323 } 324 } 325 326 final ByteBuffer read() throws IOException { 327 ByteBuffer b = readImpl(); 328 return b; 329 } 330 331 /* 332 * Returns a ByteBuffer with the data available at the moment, or null if 333 * reached EOF. 334 */ 335 protected abstract ByteBuffer readImpl() throws IOException; 336 337 @Override 338 public String toString() { 339 return "HttpConnection: " + channel().toString(); 340 } 341} 342