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 jdk.incubator.http.internal.common.ByteBufferReference;
29import jdk.incubator.http.internal.common.MinimalFuture;
30import jdk.incubator.http.HttpResponse.BodyHandler;
31
32import java.io.IOException;
33import java.net.InetSocketAddress;
34import java.nio.ByteBuffer;
35import java.nio.channels.SocketChannel;
36import java.util.concurrent.CompletableFuture;
37import java.util.function.Consumer;
38import 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 */
45class 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}
200