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