--- old/test/sun/security/ssl/sun/net/www/httpstest/HttpServer.java 2012-09-05 15:21:18.178224224 -0400 +++ /dev/null 2012-08-28 07:24:16.656816964 -0400 @@ -1,933 +0,0 @@ -/* - * Copyright (c) 2002, 2004, 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. - * - * 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. - */ - -import java.net.*; -import java.io.*; -import java.nio.*; -import java.nio.channels.*; -import sun.net.www.MessageHeader; -import java.util.*; -import javax.net.ssl.*; -import javax.net.ssl.SSLEngineResult.*; -import java.security.*; - -/** - * This class implements a simple HTTPS server. It uses multiple threads to - * handle connections in parallel, and will spin off a new thread to handle - * each request. (this is easier to implement with SSLEngine) - *

- * It must be instantiated with a {@link HttpCallback} object to which - * requests are given and must be handled. - *

- * Simple synchronization between the client(s) and server can be done - * using the {@link #waitForCondition(String)}, {@link #setCondition(String)} and - * {@link #rendezvous(String,int)} methods. - * - * NOTE NOTE NOTE NOTE NOTE NOTE NOTE - * - * If you make a change in here, please don't forget to make the - * corresponding change in the J2SE equivalent. - * - * NOTE NOTE NOTE NOTE NOTE NOTE NOTE - */ - -public class HttpServer { - - ServerSocketChannel schan; - int threads; - int cperthread; - HttpCallback cb; - Server[] servers; - - // ssl related fields - static SSLContext sslCtx; - - /** - * Create a HttpServer instance with the specified callback object - * for handling requests. One thread is created to handle requests, - * and up to ten TCP connections will be handled simultaneously. - * @param cb the callback object which is invoked to handle each - * incoming request - */ - - public HttpServer (HttpCallback cb) throws IOException { - this (cb, 1, 10, 0); - } - - /** - * Create a HttpServer instance with the specified number of - * threads and maximum number of connections per thread. This functions - * the same as the 4 arg constructor, where the port argument is set to zero. - * @param cb the callback object which is invoked to handle each - * incoming request - * @param threads the number of threads to create to handle requests - * in parallel - * @param cperthread the number of simultaneous TCP connections to - * handle per thread - */ - - public HttpServer (HttpCallback cb, int threads, int cperthread) - throws IOException { - this (cb, threads, cperthread, 0); - } - - /** - * Create a HttpServer instance with the specified number - * of threads and maximum number of connections per thread and running on - * the specified port. The specified number of threads are created to - * handle incoming requests, and each thread is allowed - * to handle a number of simultaneous TCP connections. - * @param cb the callback object which is invoked to handle - * each incoming request - * @param threads the number of threads to create to handle - * requests in parallel - * @param cperthread the number of simultaneous TCP connections - * to handle per thread - * @param port the port number to bind the server to. Zero - * means choose any free port. - */ - - public HttpServer (HttpCallback cb, int threads, int cperthread, int port) - throws IOException { - schan = ServerSocketChannel.open (); - InetSocketAddress addr = new InetSocketAddress (port); - schan.socket().bind (addr); - this.threads = threads; - this.cb = cb; - this.cperthread = cperthread; - - try { - // create and initialize a SSLContext - KeyStore ks = KeyStore.getInstance("JKS"); - KeyStore ts = KeyStore.getInstance("JKS"); - char[] passphrase = "passphrase".toCharArray(); - - ks.load(new FileInputStream(System.getProperty("javax.net.ssl.keyStore")), passphrase); - ts.load(new FileInputStream(System.getProperty("javax.net.ssl.trustStore")), passphrase); - - KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); - kmf.init(ks, passphrase); - - TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); - tmf.init(ts); - - sslCtx = SSLContext.getInstance("TLS"); - - sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); - - servers = new Server [threads]; - for (int i=0; i 0 && ((c=is.read (buf, off, remain))>0)) { - remain -= c; - off += c; - } - return buf; - } - - private void readCRLF(InputStream is) throws IOException { - int cr = is.read(); - int lf = is.read(); - - if (((cr & 0xff) != 0x0d) || - ((lf & 0xff) != 0x0a)) { - throw new IOException( - "Expected : got '" + cr + "/" + lf + "'"); - } - } - - byte[] readChunkedData (InputStream is) throws IOException { - LinkedList l = new LinkedList (); - int total = 0; - for (int len=readChunkLen(is); len!=0; len=readChunkLen(is)) { - l.add (readNormalData(is, len)); - total += len; - readCRLF(is); // CRLF at end of chunk - } - readCRLF(is); // CRLF at end of Chunked Stream. - byte[] buf = new byte [total]; - Iterator i = l.iterator(); - int x = 0; - while (i.hasNext()) { - byte[] b = (byte[])i.next(); - System.arraycopy (b, 0, buf, x, b.length); - x += b.length; - } - return buf; - } - - private int readChunkLen (InputStream is) throws IOException { - int c, len=0; - boolean done=false, readCR=false; - while (!done) { - c = is.read (); - if (c == '\n' && readCR) { - done = true; - } else { - if (c == '\r' && !readCR) { - readCR = true; - } else { - int x=0; - if (c >= 'a' && c <= 'f') { - x = c - 'a' + 10; - } else if (c >= 'A' && c <= 'F') { - x = c - 'A' + 10; - } else if (c >= '0' && c <= '9') { - x = c - '0'; - } - len = len * 16 + x; - } - } - } - return len; - } - - private String readLine (InputStream is) throws IOException { - boolean done=false, readCR=false; - byte[] b = new byte [512]; - int c, l = 0; - - while (!done) { - c = is.read (); - if (c == '\n' && readCR) { - done = true; - } else { - if (c == '\r' && !readCR) { - readCR = true; - } else { - b[l++] = (byte)c; - } - } - } - return new String (b); - } - - /** close the channel associated with the current key by: - * 1. shutdownOutput (send a FIN) - * 2. mark the key so that incoming data is to be consumed and discarded - * 3. After a period, close the socket - */ - - synchronized void orderlyCloseChannel (SocketChannel ch) throws IOException { - ch.socket().shutdownOutput(); - } - - synchronized void abortiveCloseChannel (SocketChannel ch) throws IOException { - Socket s = ch.socket (); - s.setSoLinger (true, 0); - ch.close(); - } - } - - - /** - * Implements blocking reading semantics on top of a non-blocking channel - */ - - static class NioInputStream extends InputStream { - SSLEngine sslEng; - SocketChannel channel; - Selector selector; - ByteBuffer inNetBB; - ByteBuffer inAppBB; - SelectionKey key; - int available; - byte[] one; - boolean closed; - ByteBuffer markBuf; /* reads may be satisifed from this buffer */ - boolean marked; - boolean reset; - int readlimit; - - public NioInputStream (SocketChannel chan, SSLEngine sslEng, ByteBuffer inNetBB, ByteBuffer inAppBB) throws IOException { - this.sslEng = sslEng; - this.channel = chan; - selector = Selector.open(); - this.inNetBB = inNetBB; - this.inAppBB = inAppBB; - key = chan.register (selector, SelectionKey.OP_READ); - available = 0; - one = new byte[1]; - closed = marked = reset = false; - } - - public synchronized int read (byte[] b) throws IOException { - return read (b, 0, b.length); - } - - public synchronized int read () throws IOException { - return read (one, 0, 1); - } - - public synchronized int read (byte[] b, int off, int srclen) throws IOException { - - int canreturn, willreturn; - - if (closed) - return -1; - - if (reset) { /* satisfy from markBuf */ - canreturn = markBuf.remaining (); - willreturn = canreturn>srclen ? srclen : canreturn; - markBuf.get(b, off, willreturn); - if (canreturn == willreturn) { - reset = false; - } - } else { /* satisfy from channel */ - canreturn = available(); - if (canreturn == 0) { - block (); - canreturn = available(); - } - willreturn = canreturn>srclen ? srclen : canreturn; - inAppBB.get(b, off, willreturn); - available -= willreturn; - - if (marked) { /* copy into markBuf */ - try { - markBuf.put (b, off, willreturn); - } catch (BufferOverflowException e) { - marked = false; - } - } - } - return willreturn; - } - - public synchronized int available () throws IOException { - if (closed) - throw new IOException ("Stream is closed"); - - if (reset) - return markBuf.remaining(); - - if (available > 0) - return available; - - inAppBB.clear (); - int bytes = channel.read (inNetBB); - - int needed = sslEng.getSession().getApplicationBufferSize(); - if (needed > inAppBB.remaining()) { - inAppBB = ByteBuffer.allocate(needed); - } - inNetBB.flip(); - SSLEngineResult result = sslEng.unwrap(inNetBB, inAppBB); - inNetBB.compact(); - available = result.bytesProduced(); - - if (available > 0) - inAppBB.flip(); - else if (available == -1) - throw new IOException ("Stream is closed"); - return available; - } - - /** - * block() only called when available==0 and buf is empty - */ - private synchronized void block () throws IOException { - //assert available == 0; - int n = selector.select (); - //assert n == 1; - selector.selectedKeys().clear(); - available (); - } - - public void close () throws IOException { - if (closed) - return; - channel.close (); - closed = true; - } - - public synchronized void mark (int readlimit) { - if (closed) - return; - this.readlimit = readlimit; - markBuf = ByteBuffer.allocate (readlimit); - marked = true; - reset = false; - } - - public synchronized void reset () throws IOException { - if (closed ) - return; - if (!marked) - throw new IOException ("Stream not marked"); - marked = false; - reset = true; - markBuf.flip (); - } - } - - static class NioOutputStream extends OutputStream { - SSLEngine sslEng; - SocketChannel channel; - ByteBuffer outNetBB; - ByteBuffer outAppBB; - SelectionKey key; - Selector selector; - boolean closed; - byte[] one; - - public NioOutputStream (SocketChannel channel, SSLEngine sslEng, ByteBuffer outNetBB, ByteBuffer outAppBB) throws IOException { - this.sslEng = sslEng; - this.channel = channel; - this.outNetBB = outNetBB; - this.outAppBB = outAppBB; - selector = Selector.open (); - key = channel.register (selector, SelectionKey.OP_WRITE); - closed = false; - one = new byte [1]; - } - - public synchronized void write (int b) throws IOException { - one[0] = (byte)b; - write (one, 0, 1); - } - - public synchronized void write (byte[] b) throws IOException { - write (b, 0, b.length); - } - - public synchronized void write (byte[] b, int off, int len) throws IOException { - if (closed) - throw new IOException ("stream is closed"); - - outAppBB = ByteBuffer.allocate (len); - outAppBB.put (b, off, len); - outAppBB.flip (); - int n; - outNetBB.clear(); - int needed = sslEng.getSession().getPacketBufferSize(); - if (outNetBB.capacity() < needed) { - outNetBB = ByteBuffer.allocate(needed); - } - SSLEngineResult ret = sslEng.wrap(outAppBB, outNetBB); - outNetBB.flip(); - int newLen = ret.bytesProduced(); - while ((n = channel.write (outNetBB)) < newLen) { - newLen -= n; - if (newLen == 0) - return; - selector.select (); - selector.selectedKeys().clear (); - } - } - - public void close () throws IOException { - if (closed) - return; - channel.close (); - closed = true; - } - } - - /** - * Utilities for synchronization. A condition is - * identified by a string name, and is initialized - * upon first use (ie. setCondition() or waitForCondition()). Threads - * are blocked until some thread calls (or has called) setCondition() for the same - * condition. - *

- * A rendezvous built on a condition is also provided for synchronizing - * N threads. - */ - - private static HashMap conditions = new HashMap(); - - /* - * Modifiable boolean object - */ - private static class BValue { - boolean v; - } - - /* - * Modifiable int object - */ - private static class IValue { - int v; - IValue (int i) { - v =i; - } - } - - - private static BValue getCond (String condition) { - synchronized (conditions) { - BValue cond = (BValue) conditions.get (condition); - if (cond == null) { - cond = new BValue(); - conditions.put (condition, cond); - } - return cond; - } - } - - /** - * Set the condition to true. Any threads that are currently blocked - * waiting on the condition, will be unblocked and allowed to continue. - * Threads that subsequently call waitForCondition() will not block. - * If the named condition did not exist prior to the call, then it is created - * first. - */ - - public static void setCondition (String condition) { - BValue cond = getCond (condition); - synchronized (cond) { - if (cond.v) { - return; - } - cond.v = true; - cond.notifyAll(); - } - } - - /** - * If the named condition does not exist, then it is created and initialized - * to false. If the condition exists or has just been created and its value - * is false, then the thread blocks until another thread sets the condition. - * If the condition exists and is already set to true, then this call returns - * immediately without blocking. - */ - - public static void waitForCondition (String condition) { - BValue cond = getCond (condition); - synchronized (cond) { - if (!cond.v) { - try { - cond.wait(); - } catch (InterruptedException e) {} - } - } - } - - /* conditions must be locked when accessing this */ - static HashMap rv = new HashMap(); - - /** - * Force N threads to rendezvous (ie. wait for each other) before proceeding. - * The first thread(s) to call are blocked until the last - * thread makes the call. Then all threads continue. - *

- * All threads that call with the same condition name, must use the same value - * for N (or the results may be not be as expected). - *

- * Obviously, if fewer than N threads make the rendezvous then the result - * will be a hang. - */ - - public static void rendezvous (String condition, int N) { - BValue cond; - IValue iv; - String name = "RV_"+condition; - - /* get the condition */ - - synchronized (conditions) { - cond = (BValue)conditions.get (name); - if (cond == null) { - /* we are first caller */ - if (N < 2) { - throw new RuntimeException ("rendezvous must be called with N >= 2"); - } - cond = new BValue (); - conditions.put (name, cond); - iv = new IValue (N-1); - rv.put (name, iv); - } else { - /* already initialised, just decrement the counter */ - iv = (IValue) rv.get (name); - iv.v --; - } - } - - if (iv.v > 0) { - waitForCondition (name); - } else { - setCondition (name); - synchronized (conditions) { - clearCondition (name); - rv.remove (name); - } - } - } - - /** - * If the named condition exists and is set then remove it, so it can - * be re-initialized and used again. If the condition does not exist, or - * exists but is not set, then the call returns without doing anything. - * Note, some higher level synchronization - * may be needed between clear and the other operations. - */ - - public static void clearCondition(String condition) { - BValue cond; - synchronized (conditions) { - cond = (BValue) conditions.get (condition); - if (cond == null) { - return; - } - synchronized (cond) { - if (cond.v) { - conditions.remove (condition); - } - } - } - } -} --- /dev/null 2012-08-28 07:24:16.656816964 -0400 +++ new/test/sun/security/ssl/sun/net/www/httpstest/TestHttpsServer.java 2012-09-05 15:21:17.942224228 -0400 @@ -0,0 +1,933 @@ +/* + * Copyright (c) 2002, 2004, 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. + * + * 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. + */ + +import java.net.*; +import java.io.*; +import java.nio.*; +import java.nio.channels.*; +import sun.net.www.MessageHeader; +import java.util.*; +import javax.net.ssl.*; +import javax.net.ssl.SSLEngineResult.*; +import java.security.*; + +/** + * This class implements a simple HTTPS server. It uses multiple threads to + * handle connections in parallel, and will spin off a new thread to handle + * each request. (this is easier to implement with SSLEngine) + *

+ * It must be instantiated with a {@link HttpCallback} object to which + * requests are given and must be handled. + *

+ * Simple synchronization between the client(s) and server can be done + * using the {@link #waitForCondition(String)}, {@link #setCondition(String)} and + * {@link #rendezvous(String,int)} methods. + * + * NOTE NOTE NOTE NOTE NOTE NOTE NOTE + * + * If you make a change in here, please don't forget to make the + * corresponding change in the J2SE equivalent. + * + * NOTE NOTE NOTE NOTE NOTE NOTE NOTE + */ + +public class TestHttpsServer { + + ServerSocketChannel schan; + int threads; + int cperthread; + HttpCallback cb; + Server[] servers; + + // ssl related fields + static SSLContext sslCtx; + + /** + * Create a TestHttpsServer instance with the specified callback object + * for handling requests. One thread is created to handle requests, + * and up to ten TCP connections will be handled simultaneously. + * @param cb the callback object which is invoked to handle each + * incoming request + */ + + public TestHttpsServer (HttpCallback cb) throws IOException { + this (cb, 1, 10, 0); + } + + /** + * Create a TestHttpsServer instance with the specified number of + * threads and maximum number of connections per thread. This functions + * the same as the 4 arg constructor, where the port argument is set to zero. + * @param cb the callback object which is invoked to handle each + * incoming request + * @param threads the number of threads to create to handle requests + * in parallel + * @param cperthread the number of simultaneous TCP connections to + * handle per thread + */ + + public TestHttpsServer (HttpCallback cb, int threads, int cperthread) + throws IOException { + this (cb, threads, cperthread, 0); + } + + /** + * Create a TestHttpsServer instance with the specified number + * of threads and maximum number of connections per thread and running on + * the specified port. The specified number of threads are created to + * handle incoming requests, and each thread is allowed + * to handle a number of simultaneous TCP connections. + * @param cb the callback object which is invoked to handle + * each incoming request + * @param threads the number of threads to create to handle + * requests in parallel + * @param cperthread the number of simultaneous TCP connections + * to handle per thread + * @param port the port number to bind the server to. Zero + * means choose any free port. + */ + + public TestHttpsServer (HttpCallback cb, int threads, int cperthread, int port) + throws IOException { + schan = ServerSocketChannel.open (); + InetSocketAddress addr = new InetSocketAddress (port); + schan.socket().bind (addr); + this.threads = threads; + this.cb = cb; + this.cperthread = cperthread; + + try { + // create and initialize a SSLContext + KeyStore ks = KeyStore.getInstance("JKS"); + KeyStore ts = KeyStore.getInstance("JKS"); + char[] passphrase = "passphrase".toCharArray(); + + ks.load(new FileInputStream(System.getProperty("javax.net.ssl.keyStore")), passphrase); + ts.load(new FileInputStream(System.getProperty("javax.net.ssl.trustStore")), passphrase); + + KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); + kmf.init(ks, passphrase); + + TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); + tmf.init(ts); + + sslCtx = SSLContext.getInstance("TLS"); + + sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + servers = new Server [threads]; + for (int i=0; i 0 && ((c=is.read (buf, off, remain))>0)) { + remain -= c; + off += c; + } + return buf; + } + + private void readCRLF(InputStream is) throws IOException { + int cr = is.read(); + int lf = is.read(); + + if (((cr & 0xff) != 0x0d) || + ((lf & 0xff) != 0x0a)) { + throw new IOException( + "Expected : got '" + cr + "/" + lf + "'"); + } + } + + byte[] readChunkedData (InputStream is) throws IOException { + LinkedList l = new LinkedList (); + int total = 0; + for (int len=readChunkLen(is); len!=0; len=readChunkLen(is)) { + l.add (readNormalData(is, len)); + total += len; + readCRLF(is); // CRLF at end of chunk + } + readCRLF(is); // CRLF at end of Chunked Stream. + byte[] buf = new byte [total]; + Iterator i = l.iterator(); + int x = 0; + while (i.hasNext()) { + byte[] b = (byte[])i.next(); + System.arraycopy (b, 0, buf, x, b.length); + x += b.length; + } + return buf; + } + + private int readChunkLen (InputStream is) throws IOException { + int c, len=0; + boolean done=false, readCR=false; + while (!done) { + c = is.read (); + if (c == '\n' && readCR) { + done = true; + } else { + if (c == '\r' && !readCR) { + readCR = true; + } else { + int x=0; + if (c >= 'a' && c <= 'f') { + x = c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + x = c - 'A' + 10; + } else if (c >= '0' && c <= '9') { + x = c - '0'; + } + len = len * 16 + x; + } + } + } + return len; + } + + private String readLine (InputStream is) throws IOException { + boolean done=false, readCR=false; + byte[] b = new byte [512]; + int c, l = 0; + + while (!done) { + c = is.read (); + if (c == '\n' && readCR) { + done = true; + } else { + if (c == '\r' && !readCR) { + readCR = true; + } else { + b[l++] = (byte)c; + } + } + } + return new String (b); + } + + /** close the channel associated with the current key by: + * 1. shutdownOutput (send a FIN) + * 2. mark the key so that incoming data is to be consumed and discarded + * 3. After a period, close the socket + */ + + synchronized void orderlyCloseChannel (SocketChannel ch) throws IOException { + ch.socket().shutdownOutput(); + } + + synchronized void abortiveCloseChannel (SocketChannel ch) throws IOException { + Socket s = ch.socket (); + s.setSoLinger (true, 0); + ch.close(); + } + } + + + /** + * Implements blocking reading semantics on top of a non-blocking channel + */ + + static class NioInputStream extends InputStream { + SSLEngine sslEng; + SocketChannel channel; + Selector selector; + ByteBuffer inNetBB; + ByteBuffer inAppBB; + SelectionKey key; + int available; + byte[] one; + boolean closed; + ByteBuffer markBuf; /* reads may be satisifed from this buffer */ + boolean marked; + boolean reset; + int readlimit; + + public NioInputStream (SocketChannel chan, SSLEngine sslEng, ByteBuffer inNetBB, ByteBuffer inAppBB) throws IOException { + this.sslEng = sslEng; + this.channel = chan; + selector = Selector.open(); + this.inNetBB = inNetBB; + this.inAppBB = inAppBB; + key = chan.register (selector, SelectionKey.OP_READ); + available = 0; + one = new byte[1]; + closed = marked = reset = false; + } + + public synchronized int read (byte[] b) throws IOException { + return read (b, 0, b.length); + } + + public synchronized int read () throws IOException { + return read (one, 0, 1); + } + + public synchronized int read (byte[] b, int off, int srclen) throws IOException { + + int canreturn, willreturn; + + if (closed) + return -1; + + if (reset) { /* satisfy from markBuf */ + canreturn = markBuf.remaining (); + willreturn = canreturn>srclen ? srclen : canreturn; + markBuf.get(b, off, willreturn); + if (canreturn == willreturn) { + reset = false; + } + } else { /* satisfy from channel */ + canreturn = available(); + if (canreturn == 0) { + block (); + canreturn = available(); + } + willreturn = canreturn>srclen ? srclen : canreturn; + inAppBB.get(b, off, willreturn); + available -= willreturn; + + if (marked) { /* copy into markBuf */ + try { + markBuf.put (b, off, willreturn); + } catch (BufferOverflowException e) { + marked = false; + } + } + } + return willreturn; + } + + public synchronized int available () throws IOException { + if (closed) + throw new IOException ("Stream is closed"); + + if (reset) + return markBuf.remaining(); + + if (available > 0) + return available; + + inAppBB.clear (); + int bytes = channel.read (inNetBB); + + int needed = sslEng.getSession().getApplicationBufferSize(); + if (needed > inAppBB.remaining()) { + inAppBB = ByteBuffer.allocate(needed); + } + inNetBB.flip(); + SSLEngineResult result = sslEng.unwrap(inNetBB, inAppBB); + inNetBB.compact(); + available = result.bytesProduced(); + + if (available > 0) + inAppBB.flip(); + else if (available == -1) + throw new IOException ("Stream is closed"); + return available; + } + + /** + * block() only called when available==0 and buf is empty + */ + private synchronized void block () throws IOException { + //assert available == 0; + int n = selector.select (); + //assert n == 1; + selector.selectedKeys().clear(); + available (); + } + + public void close () throws IOException { + if (closed) + return; + channel.close (); + closed = true; + } + + public synchronized void mark (int readlimit) { + if (closed) + return; + this.readlimit = readlimit; + markBuf = ByteBuffer.allocate (readlimit); + marked = true; + reset = false; + } + + public synchronized void reset () throws IOException { + if (closed ) + return; + if (!marked) + throw new IOException ("Stream not marked"); + marked = false; + reset = true; + markBuf.flip (); + } + } + + static class NioOutputStream extends OutputStream { + SSLEngine sslEng; + SocketChannel channel; + ByteBuffer outNetBB; + ByteBuffer outAppBB; + SelectionKey key; + Selector selector; + boolean closed; + byte[] one; + + public NioOutputStream (SocketChannel channel, SSLEngine sslEng, ByteBuffer outNetBB, ByteBuffer outAppBB) throws IOException { + this.sslEng = sslEng; + this.channel = channel; + this.outNetBB = outNetBB; + this.outAppBB = outAppBB; + selector = Selector.open (); + key = channel.register (selector, SelectionKey.OP_WRITE); + closed = false; + one = new byte [1]; + } + + public synchronized void write (int b) throws IOException { + one[0] = (byte)b; + write (one, 0, 1); + } + + public synchronized void write (byte[] b) throws IOException { + write (b, 0, b.length); + } + + public synchronized void write (byte[] b, int off, int len) throws IOException { + if (closed) + throw new IOException ("stream is closed"); + + outAppBB = ByteBuffer.allocate (len); + outAppBB.put (b, off, len); + outAppBB.flip (); + int n; + outNetBB.clear(); + int needed = sslEng.getSession().getPacketBufferSize(); + if (outNetBB.capacity() < needed) { + outNetBB = ByteBuffer.allocate(needed); + } + SSLEngineResult ret = sslEng.wrap(outAppBB, outNetBB); + outNetBB.flip(); + int newLen = ret.bytesProduced(); + while ((n = channel.write (outNetBB)) < newLen) { + newLen -= n; + if (newLen == 0) + return; + selector.select (); + selector.selectedKeys().clear (); + } + } + + public void close () throws IOException { + if (closed) + return; + channel.close (); + closed = true; + } + } + + /** + * Utilities for synchronization. A condition is + * identified by a string name, and is initialized + * upon first use (ie. setCondition() or waitForCondition()). Threads + * are blocked until some thread calls (or has called) setCondition() for the same + * condition. + *

+ * A rendezvous built on a condition is also provided for synchronizing + * N threads. + */ + + private static HashMap conditions = new HashMap(); + + /* + * Modifiable boolean object + */ + private static class BValue { + boolean v; + } + + /* + * Modifiable int object + */ + private static class IValue { + int v; + IValue (int i) { + v =i; + } + } + + + private static BValue getCond (String condition) { + synchronized (conditions) { + BValue cond = (BValue) conditions.get (condition); + if (cond == null) { + cond = new BValue(); + conditions.put (condition, cond); + } + return cond; + } + } + + /** + * Set the condition to true. Any threads that are currently blocked + * waiting on the condition, will be unblocked and allowed to continue. + * Threads that subsequently call waitForCondition() will not block. + * If the named condition did not exist prior to the call, then it is created + * first. + */ + + public static void setCondition (String condition) { + BValue cond = getCond (condition); + synchronized (cond) { + if (cond.v) { + return; + } + cond.v = true; + cond.notifyAll(); + } + } + + /** + * If the named condition does not exist, then it is created and initialized + * to false. If the condition exists or has just been created and its value + * is false, then the thread blocks until another thread sets the condition. + * If the condition exists and is already set to true, then this call returns + * immediately without blocking. + */ + + public static void waitForCondition (String condition) { + BValue cond = getCond (condition); + synchronized (cond) { + if (!cond.v) { + try { + cond.wait(); + } catch (InterruptedException e) {} + } + } + } + + /* conditions must be locked when accessing this */ + static HashMap rv = new HashMap(); + + /** + * Force N threads to rendezvous (ie. wait for each other) before proceeding. + * The first thread(s) to call are blocked until the last + * thread makes the call. Then all threads continue. + *

+ * All threads that call with the same condition name, must use the same value + * for N (or the results may be not be as expected). + *

+ * Obviously, if fewer than N threads make the rendezvous then the result + * will be a hang. + */ + + public static void rendezvous (String condition, int N) { + BValue cond; + IValue iv; + String name = "RV_"+condition; + + /* get the condition */ + + synchronized (conditions) { + cond = (BValue)conditions.get (name); + if (cond == null) { + /* we are first caller */ + if (N < 2) { + throw new RuntimeException ("rendezvous must be called with N >= 2"); + } + cond = new BValue (); + conditions.put (name, cond); + iv = new IValue (N-1); + rv.put (name, iv); + } else { + /* already initialised, just decrement the counter */ + iv = (IValue) rv.get (name); + iv.v --; + } + } + + if (iv.v > 0) { + waitForCondition (name); + } else { + setCondition (name); + synchronized (conditions) { + clearCondition (name); + rv.remove (name); + } + } + } + + /** + * If the named condition exists and is set then remove it, so it can + * be re-initialized and used again. If the condition does not exist, or + * exists but is not set, then the call returns without doing anything. + * Note, some higher level synchronization + * may be needed between clear and the other operations. + */ + + public static void clearCondition(String condition) { + BValue cond; + synchronized (conditions) { + cond = (BValue) conditions.get (condition); + if (cond == null) { + return; + } + synchronized (cond) { + if (cond.v) { + conditions.remove (condition); + } + } + } + } +}