--- a/src/java.net.http/share/classes/java/net/http/internal/SSLDelegate.java Wed Feb 07 15:46:30 2018 +0000
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,489 +0,0 @@
-/*
- * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-
-package java.net.http.internal;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.SocketChannel;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import javax.net.ssl.SSLEngineResult.HandshakeStatus;
-import javax.net.ssl.SSLEngineResult.Status;
-import javax.net.ssl.*;
-import java.net.http.internal.common.Log;
-import java.net.http.internal.common.Utils;
-import static javax.net.ssl.SSLEngineResult.HandshakeStatus.*;
-
-/**
- * Implements the mechanics of SSL by managing an SSLEngine object.
- * <p>
- * This class is only used to implement the {@link
- * AbstractAsyncSSLConnection.SSLConnectionChannel} which is handed of
- * to RawChannelImpl when creating a WebSocket.
- */
-class SSLDelegate {
-
- final SSLEngine engine;
- final EngineWrapper wrapper;
- final Lock handshaking = new ReentrantLock();
- final SocketChannel chan;
-
- SSLDelegate(SSLEngine eng, SocketChannel chan)
- {
- this.engine = eng;
- this.chan = chan;
- this.wrapper = new EngineWrapper(chan, engine);
- }
-
- // alpn[] may be null
-// SSLDelegate(SocketChannel chan, HttpClientImpl client, String[] alpn, String sn)
-// throws IOException
-// {
-// serverName = sn;
-// SSLContext context = client.sslContext();
-// engine = context.createSSLEngine();
-// engine.setUseClientMode(true);
-// SSLParameters sslp = client.sslParameters();
-// sslParameters = Utils.copySSLParameters(sslp);
-// if (sn != null) {
-// SNIHostName sni = new SNIHostName(sn);
-// sslParameters.setServerNames(List.of(sni));
-// }
-// if (alpn != null) {
-// sslParameters.setApplicationProtocols(alpn);
-// Log.logSSL("SSLDelegate: Setting application protocols: {0}" + Arrays.toString(alpn));
-// } else {
-// Log.logSSL("SSLDelegate: No application protocols proposed");
-// }
-// engine.setSSLParameters(sslParameters);
-// wrapper = new EngineWrapper(chan, engine);
-// this.chan = chan;
-// this.client = client;
-// }
-
-// SSLParameters getSSLParameters() {
-// return sslParameters;
-// }
-
- static long countBytes(ByteBuffer[] buffers, int start, int number) {
- long c = 0;
- for (int i=0; i<number; i++) {
- c+= buffers[start+i].remaining();
- }
- return c;
- }
-
-
- static class WrapperResult {
- static WrapperResult createOK() {
- WrapperResult r = new WrapperResult();
- r.buf = null;
- r.result = new SSLEngineResult(Status.OK, NOT_HANDSHAKING, 0, 0);
- return r;
- }
- SSLEngineResult result;
-
- ByteBuffer buf; // buffer containing result data
- }
-
- int app_buf_size;
- int packet_buf_size;
-
- enum BufType {
- PACKET,
- APPLICATION
- }
-
- ByteBuffer allocate (BufType type) {
- return allocate (type, -1);
- }
-
- // TODO: Use buffer pool for this
- ByteBuffer allocate (BufType type, int len) {
- assert engine != null;
- synchronized (this) {
- int size;
- if (type == BufType.PACKET) {
- if (packet_buf_size == 0) {
- SSLSession sess = engine.getSession();
- packet_buf_size = sess.getPacketBufferSize();
- }
- if (len > packet_buf_size) {
- packet_buf_size = len;
- }
- size = packet_buf_size;
- } else {
- if (app_buf_size == 0) {
- SSLSession sess = engine.getSession();
- app_buf_size = sess.getApplicationBufferSize();
- }
- if (len > app_buf_size) {
- app_buf_size = len;
- }
- size = app_buf_size;
- }
- return ByteBuffer.allocate (size);
- }
- }
-
- /* reallocates the buffer by :-
- * 1. creating a new buffer double the size of the old one
- * 2. putting the contents of the old buffer into the new one
- * 3. set xx_buf_size to the new size if it was smaller than new size
- *
- * flip is set to true if the old buffer needs to be flipped
- * before it is copied.
- */
- private ByteBuffer realloc (ByteBuffer b, boolean flip, BufType type) {
- // TODO: there should be the linear growth, rather than exponential as
- // we definitely know the maximum amount of space required to unwrap
- synchronized (this) {
- int nsize = 2 * b.capacity();
- ByteBuffer n = allocate (type, nsize);
- if (flip) {
- b.flip();
- }
- n.put(b);
- b = n;
- }
- return b;
- }
-
- /**
- * This is a thin wrapper over SSLEngine and the SocketChannel, which
- * guarantees the ordering of wraps/unwraps with respect to the underlying
- * channel read/writes. It handles the UNDER/OVERFLOW status codes
- * It does not handle the handshaking status codes, or the CLOSED status code
- * though once the engine is closed, any attempt to read/write to it
- * will get an exception. The overall result is returned.
- * It functions synchronously/blocking
- */
- class EngineWrapper {
-
- SocketChannel chan;
- SSLEngine engine;
- final Object wrapLock;
- final Object unwrapLock;
- ByteBuffer unwrap_src, wrap_dst;
- boolean closed = false;
- int u_remaining; // the number of bytes left in unwrap_src after an unwrap()
-
- EngineWrapper (SocketChannel chan, SSLEngine engine) {
- this.chan = chan;
- this.engine = engine;
- wrapLock = new Object();
- unwrapLock = new Object();
- unwrap_src = allocate(BufType.PACKET);
- wrap_dst = allocate(BufType.PACKET);
- }
-
-// void close () throws IOException {
-// }
-
- WrapperResult wrapAndSend(ByteBuffer src, boolean ignoreClose)
- throws IOException
- {
- ByteBuffer[] buffers = new ByteBuffer[1];
- buffers[0] = src;
- return wrapAndSend(buffers, 0, 1, ignoreClose);
- }
-
- /* try to wrap and send the data in src. Handles OVERFLOW.
- * Might block if there is an outbound blockage or if another
- * thread is calling wrap(). Also, might not send any data
- * if an unwrap is needed.
- */
- WrapperResult wrapAndSend(ByteBuffer[] src,
- int offset,
- int len,
- boolean ignoreClose)
- throws IOException
- {
- if (closed && !ignoreClose) {
- throw new IOException ("Engine is closed");
- }
- Status status;
- WrapperResult r = new WrapperResult();
- synchronized (wrapLock) {
- wrap_dst.clear();
- do {
- r.result = engine.wrap (src, offset, len, wrap_dst);
- status = r.result.getStatus();
- if (status == Status.BUFFER_OVERFLOW) {
- wrap_dst = realloc (wrap_dst, true, BufType.PACKET);
- }
- } while (status == Status.BUFFER_OVERFLOW);
- if (status == Status.CLOSED && !ignoreClose) {
- closed = true;
- return r;
- }
- if (r.result.bytesProduced() > 0) {
- wrap_dst.flip();
- int l = wrap_dst.remaining();
- assert l == r.result.bytesProduced();
- while (l>0) {
- l -= chan.write (wrap_dst);
- }
- }
- }
- return r;
- }
-
- /* block until a complete message is available and return it
- * in dst, together with the Result. dst may have been re-allocated
- * so caller should check the returned value in Result
- * If handshaking is in progress then, possibly no data is returned
- */
- WrapperResult recvAndUnwrap(ByteBuffer dst) throws IOException {
- Status status;
- WrapperResult r = new WrapperResult();
- r.buf = dst;
- if (closed) {
- throw new IOException ("Engine is closed");
- }
- boolean needData;
- if (u_remaining > 0) {
- unwrap_src.compact();
- unwrap_src.flip();
- needData = false;
- } else {
- unwrap_src.clear();
- needData = true;
- }
- synchronized (unwrapLock) {
- int x;
- do {
- if (needData) {
- x = chan.read (unwrap_src);
- if (x == -1) {
- throw new IOException ("connection closed for reading");
- }
- unwrap_src.flip();
- }
- r.result = engine.unwrap (unwrap_src, r.buf);
- status = r.result.getStatus();
- if (status == Status.BUFFER_UNDERFLOW) {
- if (unwrap_src.limit() == unwrap_src.capacity()) {
- /* buffer not big enough */
- unwrap_src = realloc (
- unwrap_src, false, BufType.PACKET
- );
- } else {
- /* Buffer not full, just need to read more
- * data off the channel. Reset pointers
- * for reading off SocketChannel
- */
- unwrap_src.position (unwrap_src.limit());
- unwrap_src.limit (unwrap_src.capacity());
- }
- needData = true;
- } else if (status == Status.BUFFER_OVERFLOW) {
- r.buf = realloc (r.buf, true, BufType.APPLICATION);
- needData = false;
- } else if (status == Status.CLOSED) {
- closed = true;
- r.buf.flip();
- return r;
- }
- } while (status != Status.OK);
- }
- u_remaining = unwrap_src.remaining();
- return r;
- }
- }
-
-// WrapperResult sendData (ByteBuffer src) throws IOException {
-// ByteBuffer[] buffers = new ByteBuffer[1];
-// buffers[0] = src;
-// return sendData(buffers, 0, 1);
-// }
-
- /**
- * send the data in the given ByteBuffer. If a handshake is needed
- * then this is handled within this method. When this call returns,
- * all of the given user data has been sent and any handshake has been
- * completed. Caller should check if engine has been closed.
- */
- WrapperResult sendData (ByteBuffer[] src, int offset, int len) throws IOException {
- WrapperResult r = WrapperResult.createOK();
- while (countBytes(src, offset, len) > 0) {
- r = wrapper.wrapAndSend(src, offset, len, false);
- Status status = r.result.getStatus();
- if (status == Status.CLOSED) {
- doClosure ();
- return r;
- }
- HandshakeStatus hs_status = r.result.getHandshakeStatus();
- if (hs_status != HandshakeStatus.FINISHED &&
- hs_status != HandshakeStatus.NOT_HANDSHAKING)
- {
- doHandshake(hs_status);
- }
- }
- return r;
- }
-
- /**
- * read data thru the engine into the given ByteBuffer. If the
- * given buffer was not large enough, a new one is allocated
- * and returned. This call handles handshaking automatically.
- * Caller should check if engine has been closed.
- */
- WrapperResult recvData (ByteBuffer dst) throws IOException {
- /* we wait until some user data arrives */
- int mark = dst.position();
- WrapperResult r = null;
- int pos = dst.position();
- while (dst.position() == pos) {
- r = wrapper.recvAndUnwrap (dst);
- dst = (r.buf != dst) ? r.buf: dst;
- Status status = r.result.getStatus();
- if (status == Status.CLOSED) {
- doClosure ();
- return r;
- }
-
- HandshakeStatus hs_status = r.result.getHandshakeStatus();
- if (hs_status != HandshakeStatus.FINISHED &&
- hs_status != HandshakeStatus.NOT_HANDSHAKING)
- {
- doHandshake (hs_status);
- }
- }
- Utils.flipToMark(dst, mark);
- return r;
- }
-
- /* we've received a close notify. Need to call wrap to send
- * the response
- */
- void doClosure () throws IOException {
- try {
- handshaking.lock();
- ByteBuffer tmp = allocate(BufType.APPLICATION);
- WrapperResult r;
- do {
- tmp.clear();
- tmp.flip ();
- r = wrapper.wrapAndSend(tmp, true);
- } while (r.result.getStatus() != Status.CLOSED);
- } finally {
- handshaking.unlock();
- }
- }
-
- /* do the (complete) handshake after acquiring the handshake lock.
- * If two threads call this at the same time, then we depend
- * on the wrapper methods being idempotent. eg. if wrapAndSend()
- * is called with no data to send then there must be no problem
- */
- @SuppressWarnings("fallthrough")
- void doHandshake (HandshakeStatus hs_status) throws IOException {
- boolean wasBlocking;
- try {
- wasBlocking = chan.isBlocking();
- handshaking.lock();
- chan.configureBlocking(true);
- ByteBuffer tmp = allocate(BufType.APPLICATION);
- while (hs_status != HandshakeStatus.FINISHED &&
- hs_status != HandshakeStatus.NOT_HANDSHAKING)
- {
- WrapperResult r = null;
- switch (hs_status) {
- case NEED_TASK:
- Runnable task;
- while ((task = engine.getDelegatedTask()) != null) {
- /* run in current thread, because we are already
- * running an external Executor
- */
- task.run();
- }
- /* fall thru - call wrap again */
- case NEED_WRAP:
- tmp.clear();
- tmp.flip();
- r = wrapper.wrapAndSend(tmp, false);
- break;
-
- case NEED_UNWRAP:
- tmp.clear();
- r = wrapper.recvAndUnwrap (tmp);
- if (r.buf != tmp) {
- tmp = r.buf;
- }
- assert tmp.position() == 0;
- break;
- }
- hs_status = r.result.getHandshakeStatus();
- }
- Log.logSSL(getSessionInfo());
- if (!wasBlocking) {
- chan.configureBlocking(false);
- }
- } finally {
- handshaking.unlock();
- }
- }
-
-// static void printParams(SSLParameters p) {
-// System.out.println("SSLParameters:");
-// if (p == null) {
-// System.out.println("Null params");
-// return;
-// }
-// for (String cipher : p.getCipherSuites()) {
-// System.out.printf("cipher: %s\n", cipher);
-// }
-// // JDK 8 EXCL START
-// for (String approto : p.getApplicationProtocols()) {
-// System.out.printf("application protocol: %s\n", approto);
-// }
-// // JDK 8 EXCL END
-// for (String protocol : p.getProtocols()) {
-// System.out.printf("protocol: %s\n", protocol);
-// }
-// if (p.getServerNames() != null) {
-// for (SNIServerName sname : p.getServerNames()) {
-// System.out.printf("server name: %s\n", sname.toString());
-// }
-// }
-// }
-
- String getSessionInfo() {
- StringBuilder sb = new StringBuilder();
- String application = engine.getApplicationProtocol();
- SSLSession sess = engine.getSession();
- String cipher = sess.getCipherSuite();
- String protocol = sess.getProtocol();
- sb.append("Handshake complete alpn: ")
- .append(application)
- .append(", Cipher: ")
- .append(cipher)
- .append(", Protocol: ")
- .append(protocol);
- return sb.toString();
- }
-}