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.net.InetSocketAddress; 31 import java.nio.ByteBuffer; 32 import java.nio.channels.SocketChannel; 33 import java.util.concurrent.CompletableFuture; 34 import jdk.incubator.http.internal.common.FlowTube; 35 import jdk.incubator.http.internal.common.MinimalFuture; 36 import static jdk.incubator.http.HttpResponse.BodyHandler.discard; 37 38 /** 39 * A plain text socket tunnel through a proxy. Uses "CONNECT" but does not 40 * encrypt. Used by WebSocket, as well as HTTP over SSL + Proxy. 41 * Wrapped in SSLTunnelConnection or AsyncSSLTunnelConnection for encryption. 42 */ 43 final class PlainTunnelingConnection extends HttpConnection { 44 45 final PlainHttpConnection delegate; 46 protected final InetSocketAddress proxyAddr; 47 private volatile boolean connected; 48 49 protected PlainTunnelingConnection(InetSocketAddress addr, 50 InetSocketAddress proxy, 51 HttpClientImpl client) { 52 super(addr, client); 53 this.proxyAddr = proxy; 54 delegate = new PlainHttpConnection(proxy, client); 55 } 56 57 @Override 58 public CompletableFuture<Void> connectAsync() { 59 debug.log(Level.DEBUG, "Connecting plain connection"); 60 return delegate.connectAsync() 61 .thenCompose((Void v) -> { 62 debug.log(Level.DEBUG, "sending HTTP/1.1 CONNECT"); 63 HttpClientImpl client = client(); 64 assert client != null; 65 HttpRequestImpl req = new HttpRequestImpl("CONNECT", address); 66 MultiExchange<Void,Void> mulEx = new MultiExchange<>(null, req, client, discard(null), null); 67 Exchange<Void> connectExchange = new Exchange<>(req, mulEx); 68 69 return connectExchange 70 .responseAsyncImpl(delegate) 71 .thenCompose((Response resp) -> { 72 CompletableFuture<Void> cf = new MinimalFuture<>(); 73 debug.log(Level.DEBUG, "got response: %d", resp.statusCode()); 74 if (resp.statusCode() != 200) { 75 cf.completeExceptionally(new IOException( 76 "Tunnel failed, got: "+ resp.statusCode())); 77 } else { 78 // get the initial/remaining bytes 79 ByteBuffer b = ((Http1Exchange<?>)connectExchange.exchImpl).drainLeftOverBytes(); 80 int remaining = b.remaining(); 81 assert remaining == 0: "Unexpected remaining: " + remaining; 82 connected = true; 83 cf.complete(null); 84 } 85 return cf; 86 }); 87 }); 88 } 89 90 @Override 91 HttpPublisher publisher() { return delegate.publisher(); } 92 93 @Override 94 boolean connected() { 95 return connected; 96 } 97 98 @Override 99 SocketChannel channel() { 100 return delegate.channel(); 101 } 102 103 @Override 104 FlowTube getConnectionFlow() { 105 return delegate.getConnectionFlow(); 106 } 107 108 @Override 109 ConnectionPool.CacheKey cacheKey() { 110 return new ConnectionPool.CacheKey(null, proxyAddr); 111 } 112 113 @Override 114 public void close() { 115 delegate.close(); 116 connected = false; 117 } 118 119 @Override 120 void shutdownInput() throws IOException { 121 delegate.shutdownInput(); 122 } 123 124 @Override 125 void shutdownOutput() throws IOException { 126 delegate.shutdownOutput(); 127 } 128 129 @Override 130 boolean isSecure() { 131 return false; 132 } 133 134 @Override 135 boolean isProxied() { 136 return true; 137 } 138 139 // Support for WebSocket/RawChannelImpl which unfortunately 140 // still depends on synchronous read/writes. 141 // It should be removed when RawChannelImpl moves to using asynchronous APIs. 142 @Override 143 DetachedConnectionChannel detachChannel() { 144 return delegate.detachChannel(); 145 } 146 }