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