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 jdk.incubator.http.internal.common.ByteBufferReference; 29 import jdk.incubator.http.internal.common.MinimalFuture; 30 import jdk.incubator.http.HttpResponse.BodyHandler; 31 32 import java.io.IOException; 33 import java.net.InetSocketAddress; 34 import java.nio.ByteBuffer; 35 import java.nio.channels.SocketChannel; 36 import java.util.concurrent.CompletableFuture; 37 import java.util.function.Consumer; 38 import java.util.function.Supplier; 39 40 /** 41 * A plain text socket tunnel through a proxy. Uses "CONNECT" but does not 42 * encrypt. Used by WebSocket, as well as HTTP over SSL + Proxy. 43 * Wrapped in SSLTunnelConnection or AsyncSSLTunnelConnection for encryption. 44 */ 45 class PlainTunnelingConnection extends HttpConnection implements AsyncConnection { 46 47 final PlainHttpConnection delegate; 48 protected final InetSocketAddress proxyAddr; 49 private volatile boolean connected; 50 51 @Override 52 public CompletableFuture<Void> connectAsync() { 53 return delegate.connectAsync() 54 .thenCompose((Void v) -> { 55 HttpRequestImpl req = new HttpRequestImpl("CONNECT", client, address); 56 MultiExchange<Void,Void> mconnectExchange = new MultiExchange<>(req, client, this::ignore); 57 return mconnectExchange.responseAsync() 58 .thenCompose((HttpResponseImpl<Void> resp) -> { 59 CompletableFuture<Void> cf = new MinimalFuture<>(); 60 if (resp.statusCode() != 200) { 61 cf.completeExceptionally(new IOException("Tunnel failed")); 62 } else { 63 connected = true; 64 cf.complete(null); 65 } 66 return cf; 67 }); 68 }); 69 } 70 71 private HttpResponse.BodyProcessor<Void> ignore(int status, HttpHeaders hdrs) { 72 return HttpResponse.BodyProcessor.discard((Void)null); 73 } 74 75 @Override 76 public void connect() throws IOException, InterruptedException { 77 delegate.connect(); 78 HttpRequestImpl req = new HttpRequestImpl("CONNECT", client, address); 79 MultiExchange<Void,Void> mul = new MultiExchange<>(req, client, BodyHandler.<Void>discard(null)); 80 Exchange<Void> connectExchange = new Exchange<>(req, mul); 81 Response r = connectExchange.responseImpl(delegate); 82 if (r.statusCode() != 200) { 83 throw new IOException("Tunnel failed"); 84 } 85 connected = true; 86 } 87 88 @Override 89 boolean connected() { 90 return connected; 91 } 92 93 protected PlainTunnelingConnection(InetSocketAddress addr, 94 InetSocketAddress proxy, 95 HttpClientImpl client) { 96 super(addr, client); 97 this.proxyAddr = proxy; 98 delegate = new PlainHttpConnection(proxy, client); 99 } 100 101 @Override 102 SocketChannel channel() { 103 return delegate.channel(); 104 } 105 106 @Override 107 ConnectionPool.CacheKey cacheKey() { 108 return new ConnectionPool.CacheKey(null, proxyAddr); 109 } 110 111 @Override 112 long write(ByteBuffer[] buffers, int start, int number) throws IOException { 113 return delegate.write(buffers, start, number); 114 } 115 116 @Override 117 long write(ByteBuffer buffer) throws IOException { 118 return delegate.write(buffer); 119 } 120 121 @Override 122 public void writeAsync(ByteBufferReference[] buffers) throws IOException { 123 delegate.writeAsync(buffers); 124 } 125 126 @Override 127 public void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException { 128 delegate.writeAsyncUnordered(buffers); 129 } 130 131 @Override 132 public void flushAsync() throws IOException { 133 delegate.flushAsync(); 134 } 135 136 @Override 137 public void close() { 138 delegate.close(); 139 connected = false; 140 } 141 142 @Override 143 void shutdownInput() throws IOException { 144 delegate.shutdownInput(); 145 } 146 147 @Override 148 void shutdownOutput() throws IOException { 149 delegate.shutdownOutput(); 150 } 151 152 @Override 153 CompletableFuture<Void> whenReceivingResponse() { 154 return delegate.whenReceivingResponse(); 155 } 156 157 @Override 158 protected ByteBuffer readImpl() throws IOException { 159 return delegate.readImpl(); 160 } 161 162 @Override 163 boolean isSecure() { 164 return false; 165 } 166 167 @Override 168 boolean isProxied() { 169 return true; 170 } 171 172 @Override 173 public void setAsyncCallbacks(Consumer<ByteBufferReference> asyncReceiver, 174 Consumer<Throwable> errorReceiver, 175 Supplier<ByteBufferReference> readBufferSupplier) { 176 delegate.setAsyncCallbacks(asyncReceiver, errorReceiver, readBufferSupplier); 177 } 178 179 @Override 180 public void startReading() { 181 delegate.startReading(); 182 } 183 184 @Override 185 public void stopAsyncReading() { 186 delegate.stopAsyncReading(); 187 } 188 189 @Override 190 public void enableCallback() { 191 delegate.enableCallback(); 192 } 193 194 @Override 195 synchronized void configureMode(Mode mode) throws IOException { 196 super.configureMode(mode); 197 delegate.configureMode(mode); 198 } 199 } | 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 } |