diff --git a/test/jdk/sun/net/www/protocol/https/AbstractCallback.java b/test/jdk/sun/net/www/protocol/https/AbstractCallback.java deleted file mode 100644 index 4fc60def96a..00000000000 --- a/test/jdk/sun/net/www/protocol/https/AbstractCallback.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2002, 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.util.*; -import java.io.IOException; - -/** - * This class provides a partial implementation of the HttpCallback - * interface. Use this class if you want to use the requestURI as a means - * of tracking multiple invocations of a request (on the server). - * In this case, you implement the modified request() method, which includes - * an integer count parameter. This parameter indicates the number of times - * (starting at zero) the request URI has been received. - */ - -public abstract class AbstractCallback implements HttpCallback { - - Map requests; - - static class Request { - URI uri; - int count; - - Request (URI u) { - uri = u; - count = 0; - } - } - - AbstractCallback () { - requests = Collections.synchronizedMap (new HashMap()); - } - - /** - * handle the given request and generate an appropriate response. - * @param msg the transaction containing the request from the - * client and used to send the response - */ - public void request (HttpTransaction msg) { - URI uri = msg.getRequestURI(); - Request req = (Request) requests.get (uri); - if (req == null) { - req = new Request (uri); - requests.put (uri, req); - } - request (msg, req.count++); - } - - /** - * Same as HttpCallback interface except that the integer n - * is provided to indicate sequencing of repeated requests using - * the same request URI. n starts at zero and is incremented - * for each successive call. - * - * @param msg the transaction containing the request from the - * client and used to send the response - * @param n value is 0 at first call, and is incremented by 1 for - * each subsequent call using the same request URI. - */ - abstract public void request (HttpTransaction msg, int n); -} diff --git a/test/jdk/sun/net/www/protocol/https/ChunkedOutputStream.java b/test/jdk/sun/net/www/protocol/https/ChunkedOutputStream.java index c376ef1fedd..4a29be60860 100644 --- a/test/jdk/sun/net/www/protocol/https/ChunkedOutputStream.java +++ b/test/jdk/sun/net/www/protocol/https/ChunkedOutputStream.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2021, 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 @@ -24,8 +24,7 @@ /** * @test * @bug 5026745 - * @modules java.base/sun.net.www - * @build TestHttpsServer HttpCallback + * @library /test/lib * @run main/othervm ChunkedOutputStream * @run main/othervm -Djava.net.preferIPv6Addresses=true ChunkedOutputStream * @@ -34,12 +33,35 @@ * @summary Cannot flush output stream when writing to an HttpUrlConnection */ -import java.io.*; -import java.net.*; -import javax.net.ssl.*; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.HttpRetryException; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.SocketException; +import java.net.URL; +import java.nio.charset.Charset; +import java.security.KeyStore; +import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicInteger; -public class ChunkedOutputStream implements HttpCallback { +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManagerFactory; + +import com.sun.net.httpserver.HttpExchange; +import com.sun.net.httpserver.HttpHandler; +import com.sun.net.httpserver.HttpsConfigurator; +import com.sun.net.httpserver.HttpsServer; + +public class ChunkedOutputStream implements HttpHandler { /* * Where do we find the keystores for ssl? */ @@ -57,98 +79,100 @@ public class ChunkedOutputStream implements HttpCallback { static final String str2 = "Helloworld1234567890abcdefghijklmnopqrstuvwxyz"+ "1234567890"; - public void request(HttpTransaction req) { - try { - // this is needed (count++ doesn't work), 'cause we - // are doing concurrent tests - String path = req.getRequestURI().getPath(); - if (path.equals("/d0")) { - count = 0; - } else if (path.equals("/d01")) { - count = 1; - } else if (path.equals("/d3")) { - count = 2; - } else if (path.equals("/d4") || path.equals("/d5")) { - count = 3; - } else if (path.equals("/d6")) { - count = 3; - } else if (path.equals("/d7")) { - count = 4; - } else if (path.equals("/d8")) { - count = 5; - } + private static String getAuthority() { + InetAddress address = server.getAddress().getAddress(); + String hostaddr = address.getHostAddress(); + if (address.isAnyLocalAddress()) hostaddr = "localhost"; + if (hostaddr.indexOf(':') > -1) hostaddr = "[" + hostaddr + "]"; + return hostaddr + ":" + server.getAddress().getPort(); + } - switch (count) { + public void handle(HttpExchange req) throws IOException { + // this is needed (count++ doesn't work), 'cause we + // are doing concurrent tests + System.out.println("Request Received"); + String path = req.getRequestURI().getPath(); + if (path.equals("/d0")) { + count = 0; + } else if (path.equals("/d01")) { + count = 1; + } else if (path.equals("/d3")) { + count = 2; + } else if (path.equals("/d4") || path.equals("/d5")) { + count = 3; + } else if (path.equals("/d6")) { + count = 3; + } else if (path.equals("/d7")) { + count = 4; + } else if (path.equals("/d8")) { + count = 5; + } + + switch (count) { case 0: /* test1 -- keeps conn alive */ case 1: /* test2 -- closes conn */ - String reqbody = req.getRequestEntityBody(); + + String reqbody = ""; + try(InputStream inputStream = req.getRequestBody()) { + reqbody = new String(inputStream.readAllBytes(), Charset.forName("ISO8859_1")); + } if (!reqbody.equals(str1)) { - req.sendResponse(500, "Internal server error"); - req.orderlyClose(); + req.sendResponseHeaders(500, -1); + break; } - String chunk = req.getRequestHeader("Transfer-encoding"); + String chunk = req.getRequestHeaders().getFirst("Transfer-encoding"); if (!"chunked".equals(chunk)) { - req.sendResponse(501, "Internal server error"); - req.orderlyClose(); + req.sendResponseHeaders(501, -1); + break; } - req.setResponseEntityBody(reqbody); if (count == 1) { - req.setResponseHeader("Connection", "close"); + req.getResponseHeaders().set("Connection", "close"); } - req.sendResponse(200, "OK"); - if (count == 1) { - req.orderlyClose(); + req.sendResponseHeaders(200, 0); + try (OutputStream os = req.getResponseBody()) { + os.write(reqbody.getBytes(Charset.forName("ISO8859_1"))); } break; case 2: /* test 3 */ - reqbody = req.getRequestEntityBody(); + reqbody = new String(req.getRequestBody().readAllBytes(), Charset.forName("ISO8859_1")); if (!reqbody.equals(str2)) { - req.sendResponse(500, "Internal server error"); - req.orderlyClose(); + req.sendResponseHeaders(500, -1); + break; } - int clen = Integer.parseInt ( - req.getRequestHeader("Content-length")); + int clen = Integer.parseInt (req.getRequestHeaders().getFirst("Content-length")); if (clen != str2.length()) { - req.sendResponse(501, "Internal server error"); - req.orderlyClose(); + req.sendResponseHeaders(501, -1); + break; + } + req.getResponseHeaders().set("Connection", "close"); + req.sendResponseHeaders(200, 0); + try (OutputStream os = req.getResponseBody()) { + os.write(reqbody.getBytes(Charset.forName("ISO8859_1"))); } - req.setResponseEntityBody (reqbody); - req.setResponseHeader("Connection", "close"); - req.sendResponse(200, "OK"); - req.orderlyClose(); break; case 3: /* test 6 */ - req.setResponseHeader("Location", "https://foo.bar/"); - req.setResponseHeader("Connection", "close"); - req.sendResponse(307, "Temporary Redirect"); - req.orderlyClose(); + if (path.equals("/d6")) { + reqbody = new String(req.getRequestBody().readAllBytes(), Charset.forName("ISO8859_1")); + } + req.getResponseHeaders().set("Location", "https://foo.bar/"); + req.getResponseHeaders().set("Connection", "close"); + req.sendResponseHeaders(307, -1); break; case 4: /* test 7 */ case 5: /* test 8 */ - reqbody = req.getRequestEntityBody(); + reqbody = new String(req.getRequestBody().readAllBytes(), Charset.forName("ISO8859_1")); if (reqbody != null && !"".equals(reqbody)) { - req.sendResponse(501, "Internal server error"); - req.orderlyClose(); + req.sendResponseHeaders(501, -1); + break; } - req.setResponseHeader("Connection", "close"); - req.sendResponse(200, "OK"); - req.orderlyClose(); + req.getResponseHeaders().set("Connection", "close"); + req.sendResponseHeaders(200, -1); break; default: - req.sendResponse(404, "Not Found"); - req.orderlyClose(); + req.sendResponseHeaders(404, -1); break; - } - } catch (IOException e) { - e.printStackTrace(); } - } - - public boolean dropPlainTextConnections() { - System.out.println("Unrecognized SSL message, plaintext connection?"); - System.out.println("TestHttpsServer receveived rogue connection: ignoring it."); - rogueCount.incrementAndGet(); - return true; + req.close(); } static void readAndCompare(InputStream is, String cmp) throws IOException { @@ -179,9 +203,6 @@ public class ChunkedOutputStream implements HttpCallback { } catch (SocketException x) { // we expect that the server will drop the connection and // close the accepted socket, so we should get a SocketException - // on the client side, and confirm that this::dropPlainTextConnections - // has ben called. - if (rogueCount.get() == rogue) throw x; System.out.println("Got expected exception: " + x); } } @@ -196,7 +217,7 @@ public class ChunkedOutputStream implements HttpCallback { urlc.setDoOutput(true); urlc.setRequestMethod("POST"); OutputStream os = urlc.getOutputStream(); - os.write(str1.getBytes()); + os.write(str1.getBytes(Charset.forName("ISO8859_1"))); os.close(); InputStream is = urlc.getInputStream(); readAndCompare(is, str1); @@ -213,7 +234,7 @@ public class ChunkedOutputStream implements HttpCallback { urlc.setDoOutput(true); urlc.setRequestMethod("POST"); OutputStream os = urlc.getOutputStream(); - os.write (str2.getBytes()); + os.write (str2.getBytes(Charset.forName("ISO8859_1"))); os.close(); InputStream is = urlc.getInputStream(); readAndCompare(is, str2); @@ -230,7 +251,7 @@ public class ChunkedOutputStream implements HttpCallback { urlc.setDoOutput(true); urlc.setRequestMethod("POST"); OutputStream os = urlc.getOutputStream(); - os.write(str2.getBytes()); + os.write(str2.getBytes(Charset.forName("ISO8859_1"))); try { os.close(); throw new Exception("should have thrown IOException"); @@ -248,7 +269,7 @@ public class ChunkedOutputStream implements HttpCallback { urlc.setRequestMethod("POST"); OutputStream os = urlc.getOutputStream(); try { - os.write(str2.getBytes()); + os.write(str2.getBytes(Charset.forName("ISO8859_1"))); throw new Exception("should have thrown IOException"); } catch (IOException e) {} } @@ -263,7 +284,7 @@ public class ChunkedOutputStream implements HttpCallback { urlc.setDoOutput(true); urlc.setRequestMethod("POST"); OutputStream os = urlc.getOutputStream(); - os.write(str1.getBytes()); + os.write(str1.getBytes(Charset.forName("ISO8859_1"))); os.close(); try { InputStream is = urlc.getInputStream(); @@ -310,9 +331,10 @@ public class ChunkedOutputStream implements HttpCallback { } } - static TestHttpsServer server; + static HttpsServer server; public static void main(String[] args) throws Exception { + ChunkedOutputStream chunkedOutputStream = new ChunkedOutputStream(); // setup properties to do ssl String keyFilename = System.getProperty("test.src", "./") + "/" + pathToStores + @@ -333,26 +355,48 @@ public class ChunkedOutputStream implements HttpCallback { HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier()); try { - server = new TestHttpsServer( - new ChunkedOutputStream(), 1, 10, loopback, 0); - System.out.println("Server started: listening on: " + server.getAuthority()); - testPlainText(server.getAuthority()); + // 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); + + SSLContext sslCtx = SSLContext.getInstance("TLS"); + + sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + server = HttpsServer.create(new InetSocketAddress(loopback, 0), 10); + server.setHttpsConfigurator(new HttpsConfigurator(sslCtx)); + server.createContext("/", chunkedOutputStream); + server.setExecutor(Executors.newSingleThreadExecutor()); + server.start(); + + System.out.println("Server started: listening on: " + getAuthority()); + testPlainText(getAuthority()); // the test server doesn't support keep-alive yet // test1("http://" + server.getAuthority() + "/d0"); - test1("https://" + server.getAuthority() + "/d01"); - test3("https://" + server.getAuthority() + "/d3"); - test4("https://" + server.getAuthority() + "/d4"); - test5("https://" + server.getAuthority() + "/d5"); - test6("https://" + server.getAuthority() + "/d6"); - test7("https://" + server.getAuthority() + "/d7"); - test8("https://" + server.getAuthority() + "/d8"); + test1("https://" + getAuthority() + "/d01"); + test3("https://" + getAuthority() + "/d3"); + test4("https://" + getAuthority() + "/d4"); + test5("https://" + getAuthority() + "/d5"); + test6("https://" + getAuthority() + "/d6"); + test7("https://" + getAuthority() + "/d7"); + test8("https://" + getAuthority() + "/d8"); } catch (Exception e) { if (server != null) { - server.terminate(); + server.stop(1); } throw e; } - server.terminate(); + server.stop(1); } finally { HttpsURLConnection.setDefaultHostnameVerifier(reservedHV); } @@ -365,7 +409,7 @@ public class ChunkedOutputStream implements HttpCallback { } public static void except(String s) { - server.terminate(); + server.stop(1); throw new RuntimeException(s); } } diff --git a/test/jdk/sun/net/www/protocol/https/HttpCallback.java b/test/jdk/sun/net/www/protocol/https/HttpCallback.java deleted file mode 100644 index 3aa74ce163b..00000000000 --- a/test/jdk/sun/net/www/protocol/https/HttpCallback.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2002, 2019, 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. - */ - -/** - * This interface is implemented by classes that wish to handle incoming HTTP - * requests and generate responses. This could be a general purpose HTTP server - * or a test case that expects specific requests from a client. - *
- * The incoming request fields can be examined via the {@link HttpTransaction} - * object, and a response can also be generated and sent via the request object. - */ -public interface HttpCallback { - /** - * handle the given request and generate an appropriate response. - * @param msg the transaction containing the request from the - * client and used to send the response - */ - void request (HttpTransaction msg); - - /** - * Tells whether the server should simply close the - * connection and ignore the request when the first - * byte received by the server looks like a plain - * text connection. - * @return true if the request should be ignored. - **/ - default boolean dropPlainTextConnections() { - return false; - } -} diff --git a/test/jdk/sun/net/www/protocol/https/HttpTransaction.java b/test/jdk/sun/net/www/protocol/https/HttpTransaction.java deleted file mode 100644 index ea90c267301..00000000000 --- a/test/jdk/sun/net/www/protocol/https/HttpTransaction.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (c) 2002, 2012, 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.io.*; -import java.nio.*; -import java.nio.channels.*; -import java.net.*; -import sun.net.www.MessageHeader; - -/** - * This class encapsulates a HTTP request received and a response to be - * generated in one transaction. It provides methods for examaining the - * request from the client, and for building and sending a reply. - */ - -public class HttpTransaction { - - String command; - URI requesturi; - TestHttpsServer.ServerWorker server; - MessageHeader reqheaders, reqtrailers; - String reqbody; - byte[] rspbody; - MessageHeader rspheaders, rsptrailers; - SocketChannel ch; - int rspbodylen; - boolean rspchunked; - - HttpTransaction (TestHttpsServer.ServerWorker server, String command, - URI requesturi, MessageHeader headers, - String body, MessageHeader trailers, SocketChannel ch) { - this.command = command; - this.requesturi = requesturi; - this.reqheaders = headers; - this.reqbody = body; - this.reqtrailers = trailers; - this.ch = ch; - this.server = server; - } - - /** - * Get the value of a request header whose name is specified by the - * String argument. - * - * @param key the name of the request header - * @return the value of the header or null if it does not exist - */ - public String getRequestHeader (String key) { - return reqheaders.findValue (key); - } - - /** - * Get the value of a response header whose name is specified by the - * String argument. - * - * @param key the name of the response header - * @return the value of the header or null if it does not exist - */ - public String getResponseHeader (String key) { - return rspheaders.findValue (key); - } - - /** - * Get the request URI - * - * @return the request URI - */ - public URI getRequestURI () { - return requesturi; - } - - public String toString () { - StringBuffer buf = new StringBuffer(); - buf.append ("Request from: ").append (ch.toString()).append("\r\n"); - buf.append ("Command: ").append (command).append("\r\n"); - buf.append ("Request URI: ").append (requesturi).append("\r\n"); - buf.append ("Headers: ").append("\r\n"); - buf.append (reqheaders.toString()).append("\r\n"); - buf.append ("Body: ").append (reqbody).append("\r\n"); - buf.append ("---------Response-------\r\n"); - buf.append ("Headers: ").append("\r\n"); - if (rspheaders != null) { - buf.append (rspheaders.toString()).append("\r\n"); - } - String rbody = rspbody == null? "": new String (rspbody); - buf.append ("Body: ").append (rbody).append("\r\n"); - return new String (buf); - } - - /** - * Get the value of a request trailer whose name is specified by - * the String argument. - * - * @param key the name of the request trailer - * @return the value of the trailer or null if it does not exist - */ - public String getRequestTrailer (String key) { - return reqtrailers.findValue (key); - } - - /** - * Add a response header to the response. Multiple calls with the same - * key value result in multiple header lines with the same key identifier - * @param key the name of the request header to add - * @param val the value of the header - */ - public void addResponseHeader (String key, String val) { - if (rspheaders == null) - rspheaders = new MessageHeader (); - rspheaders.add (key, val); - } - - /** - * Set a response header. Searches for first header with named key - * and replaces its value with val - * @param key the name of the request header to add - * @param val the value of the header - */ - public void setResponseHeader (String key, String val) { - if (rspheaders == null) - rspheaders = new MessageHeader (); - rspheaders.set (key, val); - } - - /** - * Add a response trailer to the response. Multiple calls with the same - * key value result in multiple trailer lines with the same key identifier - * @param key the name of the request trailer to add - * @param val the value of the trailer - */ - public void addResponseTrailer (String key, String val) { - if (rsptrailers == null) - rsptrailers = new MessageHeader (); - rsptrailers.add (key, val); - } - - /** - * Get the request method - * - * @return the request method - */ - public String getRequestMethod (){ - return command; - } - - /** - * Perform an orderly close of the TCP connection associated with this - * request. This method guarantees that any response already sent will - * not be reset (by this end). The implementation does a shutdownOutput() - * of the TCP connection and for a period of time consumes and discards - * data received on the reading side of the connection. This happens - * in the background. After the period has expired the - * connection is completely closed. - */ - - public void orderlyClose () { - try { - server.orderlyCloseChannel (ch); - } catch (IOException e) { - System.out.println (e); - } - } - - /** - * Do an immediate abortive close of the TCP connection associated - * with this request. - */ - public void abortiveClose () { - try { - server.abortiveCloseChannel(ch); - } catch (IOException e) { - System.out.println (e); - } - } - - /** - * Get the SocketChannel associated with this request - * - * @return the socket channel - */ - public SocketChannel channel() { - return ch; - } - - /** - * Get the request entity body associated with this request - * as a single String. - * - * @return the entity body in one String - */ - public String getRequestEntityBody (){ - return reqbody; - } - - /** - * Set the entity response body with the given string - * The content length is set to the length of the string - * @param body the string to send in the response - */ - public void setResponseEntityBody (String body){ - rspbody = body.getBytes(); - rspbodylen = body.length(); - rspchunked = false; - addResponseHeader ("Content-length", Integer.toString (rspbodylen)); - } - /** - * Set the entity response body with the given byte[] - * The content length is set to the gven length - * @param body the string to send in the response - */ - public void setResponseEntityBody (byte[] body, int len){ - rspbody = body; - rspbodylen = len; - rspchunked = false; - addResponseHeader ("Content-length", Integer.toString (rspbodylen)); - } - - - /** - * Set the entity response body by reading the given inputstream - * - * @param is the inputstream from which to read the body - */ - public void setResponseEntityBody (InputStream is) throws IOException { - byte[] buf = new byte [2048]; - byte[] total = new byte [2048]; - int total_len = 2048; - int c, len=0; - while ((c=is.read (buf)) != -1) { - if (len+c > total_len) { - byte[] total1 = new byte [total_len * 2]; - System.arraycopy (total, 0, total1, 0, len); - total = total1; - total_len = total_len * 2; - } - System.arraycopy (buf, 0, total, len, c); - len += c; - } - setResponseEntityBody (total, len); - } - - /* chunked */ - - /** - * Set the entity response body with the given array of strings - * The content encoding is set to "chunked" and each array element - * is sent as one chunk. - * @param body the array of string chunks to send in the response - */ - public void setResponseEntityBody (String[] body) { - StringBuffer buf = new StringBuffer (); - int len = 0; - for (int i=0; i
TunnelProxy 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
*/
@@ -55,8 +69,6 @@ public class TunnelProxy {
* Create a TunnelProxy 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
@@ -74,8 +86,6 @@ public class TunnelProxy {
* 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
@@ -95,8 +105,6 @@ public class TunnelProxy {
* 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
@@ -249,7 +257,6 @@ public class TunnelProxy {
/* return true if the connection is closed, false otherwise */
private boolean read (SocketChannel chan, SelectionKey key) {
- HttpTransaction msg;
boolean res;
try {
InputStream is = new BufferedInputStream (new NioInputStream (chan));
diff --git a/test/jdk/sun/net/www/protocol/https/TestHttpsServer.java b/test/jdk/sun/net/www/protocol/https/TestHttpsServer.java
deleted file mode 100644
index c18909da53a..00000000000
--- a/test/jdk/sun/net/www/protocol/https/TestHttpsServer.java
+++ /dev/null
@@ -1,983 +0,0 @@
-/*
- * Copyright (c) 2002, 2019, 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 {
- this(cb, threads, cperthread, null, port);
- }
-
- /**
- * 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 address the InetAddress to bind to. {@code Null} means the
- * wildcard address.
- * @param port the port number to bind the server to. {@code Zero}
- * means choose any free port.
- */
-
- public TestHttpsServer(HttpCallback cb, int threads, int cperthread, InetAddress address, int port)
- throws IOException {
- schan = ServerSocketChannel.open();
- InetSocketAddress addr = new InetSocketAddress(address, 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 -1) hostaddr = "[" + hostaddr + "]";
- return hostaddr + ":" + getLocalPort();
- }
-
- static class Server extends Thread {
-
- ServerSocketChannel schan;
- Selector selector;
- SelectionKey listenerKey;
- SelectionKey key; /* the current key being processed */
- HttpCallback cb;
- ByteBuffer consumeBuffer;
- int maxconn;
- int nconn;
- ClosedChannelList clist;
- boolean shutdown;
-
- Server(HttpCallback cb, ServerSocketChannel schan, int maxconn) {
- this.schan = schan;
- this.maxconn = maxconn;
- this.cb = cb;
- nconn = 0;
- consumeBuffer = ByteBuffer.allocate(512);
- clist = new ClosedChannelList();
- try {
- selector = Selector.open();
- schan.configureBlocking(false);
- listenerKey = schan.register(selector, SelectionKey.OP_ACCEPT);
- } catch (IOException e) {
- System.err.println("Server could not start: " + e);
- }
- }
-
- /* Stop the thread as soon as possible */
- public synchronized void terminate() {
- shutdown = true;
- }
-
- public void run() {
- try {
- while (true) {
- selector.select(1000);
- Set selected = selector.selectedKeys();
- Iterator iter = selected.iterator();
- while (iter.hasNext()) {
- key = (SelectionKey)iter.next();
- if (key.equals (listenerKey)) {
- SocketChannel sock = schan.accept();
- if (sock == null) {
- /* false notification */
- iter.remove();
- continue;
- }
- sock.configureBlocking(true);
- SSLEngine sslEng = sslCtx.createSSLEngine();
- sslEng.setUseClientMode(false);
- new ServerWorker(cb, sock, sslEng).start();
- nconn ++;
- if (nconn == maxconn) {
- /* deregister */
- listenerKey.cancel();
- listenerKey = null;
- }
- } else {
- if (key.isReadable()) {
- boolean closed = false;
- SocketChannel chan = (SocketChannel)key.channel();
- if (key.attachment() != null) {
- closed = consume(chan);
- }
-
- if (closed) {
- chan.close();
- key.cancel();
- if (nconn == maxconn) {
- listenerKey = schan.register(selector, SelectionKey.OP_ACCEPT);
- }
- nconn --;
- }
- }
- }
- iter.remove();
- }
- clist.check();
-
- synchronized (this) {
- if (shutdown) {
- clist.terminate();
- return;
- }
- }
- }
- } catch (IOException e) {
- System.out.println("Server exception: " + e);
- // TODO finish
- }
- }
-
- /* read all the data off the channel without looking at it
- * return true if connection closed
- */
- boolean consume(SocketChannel chan) {
- try {
- consumeBuffer.clear();
- int c = chan.read(consumeBuffer);
- if (c == -1)
- return true;
- } catch (IOException e) {
- return true;
- }
- return false;
- }
- }
-
- static class ServerWorker extends Thread {
- private ByteBuffer inNetBB;
- private ByteBuffer outNetBB;
- private ByteBuffer inAppBB;
- private ByteBuffer outAppBB;
-
- SSLEngine sslEng;
- SocketChannel schan;
- HttpCallback cb;
- HandshakeStatus currentHSStatus;
- boolean initialHSComplete;
- boolean handshakeStarted;
- /*
- * All inbound data goes through this buffer.
- *
- * It might be nice to use a cache of ByteBuffers so we're
- * not alloc/dealloc'ing all over the place.
- */
-
- /*
- * Application buffers, also used for handshaking
- */
- private int appBBSize;
-
- ServerWorker(HttpCallback cb, SocketChannel schan, SSLEngine sslEng) {
- this.sslEng = sslEng;
- this.schan = schan;
- this.cb = cb;
- currentHSStatus = HandshakeStatus.NEED_UNWRAP;
- initialHSComplete = false;
- int netBBSize = sslEng.getSession().getPacketBufferSize();
- inNetBB = ByteBuffer.allocate(netBBSize);
- outNetBB = ByteBuffer.allocate(netBBSize);
- appBBSize = sslEng.getSession().getApplicationBufferSize();
- inAppBB = ByteBuffer.allocate(appBBSize);
- outAppBB = ByteBuffer.allocate(appBBSize);
- }
-
- public SSLEngine getSSLEngine() {
- return sslEng;
- }
-
- public ByteBuffer outNetBB() {
- return outNetBB;
- }
-
- public ByteBuffer outAppBB() {
- return outAppBB;
- }
-
- public void run () {
- try {
- SSLEngineResult result;
-
- while (!initialHSComplete) {
-
- switch (currentHSStatus) {
-
- case NEED_UNWRAP:
- int bytes = schan.read(inNetBB);
- if (!handshakeStarted && bytes > 0) {
- handshakeStarted = true;
- int byte0 = inNetBB.get(0);
- if (byte0 != 0x16) {
- // first byte of a TLS connection is supposed to be
- // 0x16. If not it may be a plain text connection.
- //
- // Sometime a rogue client may try to open a plain
- // connection with our server. Calling this method
- // gives a chance to the test logic to ignore such
- // rogue connections.
- //
- if (cb.dropPlainTextConnections()) {
- try { schan.close(); } catch (IOException x) { };
- return;
- }
- // else sslEng.unwrap will throw later on...
- }
- }
-
-needIO:
- while (currentHSStatus == HandshakeStatus.NEED_UNWRAP) {
- /*
- * Don't need to resize requestBB, since no app data should
- * be generated here.
- */
- inNetBB.flip();
- result = sslEng.unwrap(inNetBB, inAppBB);
- inNetBB.compact();
- currentHSStatus = result.getHandshakeStatus();
-
- switch (result.getStatus()) {
-
- case OK:
- switch (currentHSStatus) {
- case NOT_HANDSHAKING:
- throw new IOException(
- "Not handshaking during initial handshake");
-
- case NEED_TASK:
- Runnable task;
- while ((task = sslEng.getDelegatedTask()) != null) {
- task.run();
- currentHSStatus = sslEng.getHandshakeStatus();
- }
- break;
- }
-
- break;
-
- case BUFFER_UNDERFLOW:
- break needIO;
-
- default: // BUFFER_OVERFLOW/CLOSED:
- throw new IOException("Received" + result.getStatus() +
- "during initial handshaking");
- }
- }
-
- /*
- * Just transitioned from read to write.
- */
- if (currentHSStatus != HandshakeStatus.NEED_WRAP) {
- break;
- }
-
- // Fall through and fill the write buffer.
-
- case NEED_WRAP:
- /*
- * The flush above guarantees the out buffer to be empty
- */
- outNetBB.clear();
- result = sslEng.wrap(inAppBB, outNetBB);
- outNetBB.flip();
- schan.write (outNetBB);
- outNetBB.compact();
- currentHSStatus = result.getHandshakeStatus();
-
- switch (result.getStatus()) {
- case OK:
-
- if (currentHSStatus == HandshakeStatus.NEED_TASK) {
- Runnable task;
- while ((task = sslEng.getDelegatedTask()) != null) {
- task.run();
- currentHSStatus = sslEng.getHandshakeStatus();
- }
- }
-
- break;
-
- default: // BUFFER_OVERFLOW/BUFFER_UNDERFLOW/CLOSED:
- throw new IOException("Received" + result.getStatus() +
- "during initial handshaking");
- }
- break;
-
- case FINISHED:
- initialHSComplete = true;
- break;
- default: // NOT_HANDSHAKING/NEED_TASK
- throw new RuntimeException("Invalid Handshaking State" +
- currentHSStatus);
- } // switch
- }
- // read the application data; using non-blocking mode
- schan.configureBlocking(false);
- read(schan, sslEng);
- } catch (Exception ex) {
- throw new RuntimeException(ex);
- }
- }
-
- /* return true if the connection is closed, false otherwise */
-
- private boolean read(SocketChannel chan, SSLEngine sslEng) {
- HttpTransaction msg;
- boolean res;
- try {
- InputStream is = new BufferedInputStream(new NioInputStream(chan, sslEng, inNetBB, inAppBB));
- String requestline = readLine(is);
- MessageHeader mhead = new MessageHeader(is);
- String clen = mhead.findValue("Content-Length");
- String trferenc = mhead.findValue("Transfer-Encoding");
- String data = null;
- if (trferenc != null && trferenc.equals("chunked"))
- data = new String(readChunkedData(is));
- else if (clen != null)
- data = new String(readNormalData(is, Integer.parseInt(clen)));
- String[] req = requestline.split(" ");
- if (req.length < 2) {
- /* invalid request line */
- return false;
- }
- String cmd = req[0];
- URI uri = null;
- try {
- uri = new URI(req[1]);
- msg = new HttpTransaction(this, cmd, uri, mhead, data, null, chan);
- cb.request(msg);
- } catch (URISyntaxException e) {
- System.err.println ("Invalid URI: " + e);
- msg = new HttpTransaction(this, cmd, null, null, null, null, chan);
- msg.sendResponse(501, "Whatever");
- }
- res = false;
- } catch (IOException e) {
- res = true;
- }
- return res;
- }
-
- byte[] readNormalData(InputStream is, int len) throws IOException {
- byte[] buf = new byte[len];
- int c, off=0, remain=len;
- while (remain > 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);
- }
- }
- }
- }
-}