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 java.io.IOException;
29import java.io.UncheckedIOException;
30import java.net.InetSocketAddress;
31import java.nio.ByteBuffer;
32import java.nio.channels.SocketChannel;
33import java.util.concurrent.CompletableFuture;
34import javax.net.ssl.SSLEngineResult.Status;
35import javax.net.ssl.SSLParameters;
36import jdk.incubator.http.SSLDelegate.WrapperResult;
37
38import jdk.incubator.http.internal.common.ByteBufferReference;
39import jdk.incubator.http.internal.common.Utils;
40
41/**
42 * An SSL tunnel built on a Plain (CONNECT) TCP tunnel.
43 */
44class SSLTunnelConnection extends HttpConnection {
45
46    final PlainTunnelingConnection delegate;
47    protected SSLDelegate sslDelegate;
48    private volatile boolean connected;
49
50    @Override
51    public void connect() throws IOException, InterruptedException {
52        delegate.connect();
53        this.sslDelegate = new SSLDelegate(delegate.channel(), client, null);
54        connected = true;
55    }
56
57    @Override
58    boolean connected() {
59        return connected && delegate.connected();
60    }
61
62    @Override
63    public CompletableFuture<Void> connectAsync() {
64        return delegate.connectAsync()
65            .thenAccept((Void v) -> {
66                try {
67                    // can this block?
68                    this.sslDelegate = new SSLDelegate(delegate.channel(),
69                                                       client,
70                                                       null);
71                    connected = true;
72                } catch (IOException e) {
73                    throw new UncheckedIOException(e);
74                }
75            });
76    }
77
78    SSLTunnelConnection(InetSocketAddress addr,
79                        HttpClientImpl client,
80                        InetSocketAddress proxy)
81    {
82        super(addr, client);
83        delegate = new PlainTunnelingConnection(addr, proxy, client);
84    }
85
86    @Override
87    SSLParameters sslParameters() {
88        return sslDelegate.getSSLParameters();
89    }
90
91    @Override
92    public String toString() {
93        return "SSLTunnelConnection: " + super.toString();
94    }
95
96    private static long countBytes(ByteBuffer[] buffers, int start, int number) {
97        long c = 0;
98        for (int i=0; i<number; i++) {
99            c+= buffers[start+i].remaining();
100        }
101        return c;
102    }
103
104    @Override
105    ConnectionPool.CacheKey cacheKey() {
106        return ConnectionPool.cacheKey(address, delegate.proxyAddr);
107    }
108
109    @Override
110    long write(ByteBuffer[] buffers, int start, int number) throws IOException {
111        //debugPrint("Send", buffers, start, number);
112        long l = countBytes(buffers, start, number);
113        WrapperResult r = sslDelegate.sendData(buffers, start, number);
114        if (r.result.getStatus() == Status.CLOSED) {
115            if (l > 0) {
116                throw new IOException("SSLHttpConnection closed");
117            }
118        }
119        return l;
120    }
121
122    @Override
123    long write(ByteBuffer buffer) throws IOException {
124        //debugPrint("Send", buffer);
125        long l = buffer.remaining();
126        WrapperResult r = sslDelegate.sendData(buffer);
127        if (r.result.getStatus() == Status.CLOSED) {
128            if (l > 0) {
129                throw new IOException("SSLHttpConnection closed");
130            }
131        }
132        return l;
133    }
134
135    @Override
136    void writeAsync(ByteBufferReference[] buffers) throws IOException {
137        write(ByteBufferReference.toBuffers(buffers), 0, buffers.length);
138    }
139
140    @Override
141    void writeAsyncUnordered(ByteBufferReference[] buffers) throws IOException {
142        write(ByteBufferReference.toBuffers(buffers), 0, buffers.length);
143    }
144
145    @Override
146    void flushAsync() throws IOException {
147        // nothing to do
148    }
149
150    @Override
151    public void close() {
152        Utils.close(delegate.channel());
153    }
154
155    @Override
156    void shutdownInput() throws IOException {
157        delegate.channel().shutdownInput();
158    }
159
160    @Override
161    void shutdownOutput() throws IOException {
162        delegate.channel().shutdownOutput();
163    }
164
165    @Override
166    protected ByteBuffer readImpl() throws IOException {
167        ByteBuffer buf = Utils.getBuffer();
168
169        WrapperResult r = sslDelegate.recvData(buf);
170        return r.buf;
171    }
172
173    @Override
174    SocketChannel channel() {
175        return delegate.channel();
176    }
177
178    @Override
179    CompletableFuture<Void> whenReceivingResponse() {
180        return delegate.whenReceivingResponse();
181    }
182
183    @Override
184    boolean isSecure() {
185        return true;
186    }
187
188    @Override
189    boolean isProxied() {
190        return true;
191    }
192}
193