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