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