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