changeset 5846:3857114d8255

6354758: rename old test HttpServer classes Reviewed-by: chegar, michaelm, khazra Contributed-by: John Zavgren <john.zavgren@oracle.com>
author chegar
date Fri, 07 Sep 2012 15:30:17 +0100
parents d5d24c08f0dc
children 7f081e14364e
files test/java/net/Authenticator/B4678055.java test/java/net/Authenticator/B4722333.java test/java/net/Authenticator/B4759514.java test/java/net/Authenticator/B4769350.java test/java/net/Authenticator/B4921848.java test/java/net/Authenticator/B4933582.java test/java/net/Authenticator/B4962064.java test/java/net/CookieHandler/CookieManagerTest.java test/java/net/ProxySelector/LoopbackAddresses.java test/java/net/ProxySelector/ProxyTest.java test/java/net/URL/PerConnectionProxy.java test/java/net/URLConnection/B5052093.java test/sun/net/www/AuthHeaderTest.java test/sun/net/www/http/ChunkedInputStream/ChunkedEncodingWithProgressMonitorTest.java test/sun/net/www/http/KeepAliveCache/B5045306.java test/sun/net/www/httptest/HttpServer.java test/sun/net/www/httptest/HttpTransaction.java test/sun/net/www/httptest/TestHttpServer.java test/sun/net/www/protocol/http/B6296310.java test/sun/net/www/protocol/http/B6299712.java test/sun/net/www/protocol/http/RelativeRedirect.java test/sun/net/www/protocol/http/ResponseCacheStream.java test/sun/net/www/protocol/http/SetChunkedStreamingMode.java test/sun/security/ssl/sun/net/www/http/ChunkedOutputStream/Test.java test/sun/security/ssl/sun/net/www/httpstest/HttpServer.java test/sun/security/ssl/sun/net/www/httpstest/HttpTransaction.java test/sun/security/ssl/sun/net/www/httpstest/TestHttpsServer.java test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java
diffstat 28 files changed, 1745 insertions(+), 1746 deletions(-) [+]
line wrap: on
line diff
--- a/test/java/net/Authenticator/B4678055.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/Authenticator/B4678055.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 4678055
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main B4678055
  * @summary Basic Authentication fails with multiple realms
  */
@@ -119,13 +119,13 @@
         is.close();
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main (String[] args) throws Exception {
         MyAuthenticator auth = new MyAuthenticator ();
         Authenticator.setDefault (auth);
         try {
-            server = new HttpServer (new B4678055(), 1, 10, 0);
+            server = new TestHttpServer (new B4678055(), 1, 10, 0);
             System.out.println ("Server: listening on port: " + server.getLocalPort());
             client ("http://localhost:"+server.getLocalPort()+"/d1/foo.html");
             client ("http://localhost:"+server.getLocalPort()+"/d2/foo.html");
--- a/test/java/net/Authenticator/B4722333.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/Authenticator/B4722333.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 4722333
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main B4722333
  * @summary JRE Proxy Authentication Not Working with ISA2000
  */
@@ -114,13 +114,13 @@
         is.close();
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main (String[] args) throws Exception {
         MyAuthenticator auth = new MyAuthenticator ();
         Authenticator.setDefault (auth);
         try {
-            server = new HttpServer (new B4722333(), 1, 10, 0);
+            server = new TestHttpServer (new B4722333(), 1, 10, 0);
             System.out.println ("Server started: listening on port: " + server.getLocalPort());
             client ("http://localhost:"+server.getLocalPort()+"/d1/d2/d3/foo.html");
             client ("http://localhost:"+server.getLocalPort()+"/ASD/d3/x.html");
--- a/test/java/net/Authenticator/B4759514.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/Authenticator/B4759514.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 4759514
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main B4759514
  * @summary Digest Authentication is erroniously quoting the nc value, contrary to RFC 2617
  */
@@ -91,13 +91,13 @@
         is.close();
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main (String[] args) throws Exception {
         MyAuthenticator auth = new MyAuthenticator ();
         Authenticator.setDefault (auth);
         try {
-            server = new HttpServer (new B4759514(), 1, 10, 0);
+            server = new TestHttpServer (new B4759514(), 1, 10, 0);
             System.out.println ("Server: listening on port: " + server.getLocalPort());
             client ("http://localhost:"+server.getLocalPort()+"/d1/foo.html");
         } catch (Exception e) {
--- a/test/java/net/Authenticator/B4769350.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/Authenticator/B4769350.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 4769350
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction AbstractCallback
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction AbstractCallback
  * @run main/othervm B4769350 server
  * @run main/othervm B4769350 proxy
  * @summary proxy authentication username and password caching only works in serial case
@@ -142,10 +142,10 @@
             switch (count) {
             case 0:
                 errorReply (req, "Basic realm=\"realm1\"");
-                HttpServer.rendezvous ("one", 2);
+                TestHttpServer.rendezvous ("one", 2);
                 break;
             case 1:
-                HttpServer.waitForCondition ("cond2");
+                TestHttpServer.waitForCondition ("cond2");
                 okReply (req);
                 break;
             default:
@@ -158,11 +158,11 @@
             switch (count) {
             case 0:
                 errorReply (req, "Basic realm=\"realm2\"");
-                HttpServer.rendezvous ("one", 2);
-                HttpServer.setCondition ("cond1");
+                TestHttpServer.rendezvous ("one", 2);
+                TestHttpServer.setCondition ("cond1");
                 break;
             case 1:
-                HttpServer.waitForCondition ("cond2");
+                TestHttpServer.waitForCondition ("cond2");
                 okReply (req);
                 break;
             default:
@@ -174,7 +174,7 @@
             switch (count) {
             case 0:
                 errorReply (req, "Basic realm=\"realm1\"");
-                HttpServer.rendezvous ("two", 2);
+                TestHttpServer.rendezvous ("two", 2);
                 break;
             case 1:
                 okReply (req);
@@ -188,8 +188,8 @@
             switch (count) {
             case 0:
                 errorReply (req, "Basic realm=\"realm2\"");
-                HttpServer.rendezvous ("two", 2);
-                HttpServer.setCondition ("cond2");
+                TestHttpServer.rendezvous ("two", 2);
+                TestHttpServer.setCondition ("cond2");
                 break;
             case 1:
                 okReply (req);
@@ -207,7 +207,7 @@
         void doT2a (HttpTransaction req, int count) throws IOException {
             /* This will be called several times */
             if (count == 1) {
-                HttpServer.setCondition ("T2cond1");
+                TestHttpServer.setCondition ("T2cond1");
             }
             errorReply (req, "Basic realm=\"realm3\"");
         }
@@ -233,7 +233,7 @@
             switch (count) {
             case 0:
                 proxyReply (req, "Basic realm=\"proxy\"");
-                HttpServer.setCondition ("T3cond1");
+                TestHttpServer.setCondition ("T3cond1");
                 break;
             case 1:
                 errorReply (req, "Basic realm=\"realm4\"");
@@ -260,7 +260,7 @@
         }
     };
 
-    static HttpServer server;
+    static TestHttpServer server;
     static MyAuthenticator auth = new MyAuthenticator ();
 
     static int redirects = 4;
@@ -276,7 +276,7 @@
         c4 = new Client (authority, "/test/realm2/t1d", false);
 
         c1.start(); c2.start();
-        HttpServer.waitForCondition ("cond1");
+        TestHttpServer.waitForCondition ("cond1");
         c3.start(); c4.start();
         c1.join(); c2.join(); c3.join(); c4.join();
 
@@ -294,7 +294,7 @@
         c5 = new Client (authority, "/test/realm3/t2a", true);
         c6 = new Client (authority, "/test/realm3/t2b", false);
         c5.start ();
-        HttpServer.waitForCondition ("T2cond1");
+        TestHttpServer.waitForCondition ("T2cond1");
         c6.start ();
         c5.join(); c6.join();
 
@@ -313,7 +313,7 @@
         c8 = new Client (authority, "/test/realm4/t3b", false);
         c9 = new Client (authority, "/test/realm4/t3c", false);
         c7.start ();
-        HttpServer.waitForCondition ("T3cond1");
+        TestHttpServer.waitForCondition ("T3cond1");
         c8.start ();
         c9.start ();
         c7.join(); c8.join(); c9.join();
@@ -333,7 +333,7 @@
         Authenticator.setDefault (auth);
         boolean proxy = args[0].equals ("proxy");
         try {
-            server = new HttpServer (new CallBack(), 10, 1, 0);
+            server = new TestHttpServer (new CallBack(), 10, 1, 0);
             System.out.println ("Server: listening on port: " + server.getLocalPort());
             if (proxy) {
                 System.setProperty ("http.proxyHost", "localhost");
--- a/test/java/net/Authenticator/B4921848.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/Authenticator/B4921848.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 4921848
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main/othervm -Dhttp.auth.preference=basic B4921848
  * @summary Allow user control over authentication schemes
  */
@@ -82,13 +82,13 @@
         is.close();
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main (String[] args) throws Exception {
         MyAuthenticator auth = new MyAuthenticator ();
         Authenticator.setDefault (auth);
         try {
-            server = new HttpServer (new B4921848(), 1, 10, 0);
+            server = new TestHttpServer (new B4921848(), 1, 10, 0);
             System.out.println ("Server started: listening on port: " + server.getLocalPort());
             client ("http://localhost:"+server.getLocalPort()+"/d1/d2/d3/foo.html");
         } catch (Exception e) {
--- a/test/java/net/Authenticator/B4933582.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/Authenticator/B4933582.java	Fri Sep 07 15:30:17 2012 +0100
@@ -119,7 +119,7 @@
         is.close();
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main (String[] args) throws Exception {
         firstTime = args[0].equals ("first");
@@ -128,11 +128,11 @@
         CacheImpl cache;
         try {
             if (firstTime) {
-                server = new HttpServer (new B4933582(), 1, 10, 0);
+                server = new TestHttpServer (new B4933582(), 1, 10, 0);
                 cache = new CacheImpl (server.getLocalPort());
             } else {
                 cache = new CacheImpl ();
-                server = new HttpServer(new B4933582(), 1, 10, cache.getPort());
+                server = new TestHttpServer(new B4933582(), 1, 10, cache.getPort());
             }
             AuthCacheValue.setAuthCache (cache);
             System.out.println ("Server: listening on port: " + server.getLocalPort());
--- a/test/java/net/Authenticator/B4962064.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/Authenticator/B4962064.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 4962064
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main/othervm B4962064
  * @summary Extend Authenticator to provide access to request URI and server/proxy
  */
@@ -85,12 +85,12 @@
         is.close();
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
     static URL urlsave;
 
     public static void main (String[] args) throws Exception {
         try {
-            server = new HttpServer (new B4962064(), 1, 10, 0);
+            server = new TestHttpServer (new B4962064(), 1, 10, 0);
             int port = server.getLocalPort();
             System.setProperty ("http.proxyHost", "localhost");
             System.setProperty ("http.proxyPort", Integer.toString (port));
--- a/test/java/net/CookieHandler/CookieManagerTest.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/CookieHandler/CookieManagerTest.java	Fri Sep 07 15:30:17 2012 +0100
@@ -26,7 +26,7 @@
  * @summary Unit test for java.net.CookieManager
  * @bug 6244040
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main/othervm -ea CookieManagerTest
  * @author Edward Wang
  */
@@ -38,7 +38,7 @@
 
 public class CookieManagerTest {
     static CookieHttpTransaction httpTrans;
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main(String[] args) throws Exception {
         startHttpServer();
@@ -52,7 +52,7 @@
     public static void startHttpServer() {
         try {
             httpTrans = new CookieHttpTransaction();
-            server = new HttpServer(httpTrans, 1, 1, 0);
+            server = new TestHttpServer(httpTrans, 1, 1, 0);
         } catch (IOException e) {
             e.printStackTrace();
         }
--- a/test/java/net/ProxySelector/LoopbackAddresses.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/ProxySelector/LoopbackAddresses.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @bug 4924226
  * @summary PIT: Can no launch jnlp application via 127.0.0.1 address on the web server
  * @library ../../../sun/net/www/httptest/
- * @build ClosedChannelList HttpServer HttpTransaction HttpCallback
+ * @build ClosedChannelList TestHttpServer HttpTransaction HttpCallback
  * @compile LoopbackAddresses.java
  * @run main/othervm LoopbackAddresses
  */
@@ -39,7 +39,7 @@
  */
 
 public class LoopbackAddresses implements HttpCallback {
-    static HttpServer server;
+    static TestHttpServer server;
 
     public void request (HttpTransaction req) {
         req.setResponseEntityBody ("Hello .");
@@ -52,7 +52,7 @@
 
     public static void main(String[] args) {
         try {
-            server = new HttpServer (new LoopbackAddresses(), 1, 10, 0);
+            server = new TestHttpServer (new LoopbackAddresses(), 1, 10, 0);
             ProxyServer pserver = new ProxyServer(InetAddress.getByName("localhost"), server.getLocalPort());
             // start proxy server
             new Thread(pserver).start();
--- a/test/java/net/ProxySelector/ProxyTest.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/ProxySelector/ProxyTest.java	Fri Sep 07 15:30:17 2012 +0100
@@ -26,7 +26,7 @@
  * @bug 4696512
  * @summary HTTP client: Improve proxy server configuration and selection
  * @library ../../../sun/net/www/httptest/
- * @build ClosedChannelList HttpServer HttpTransaction HttpCallback
+ * @build ClosedChannelList TestHttpServer HttpTransaction HttpCallback
  * @compile ProxyTest.java
  * @run main/othervm -Dhttp.proxyHost=inexistant -Dhttp.proxyPort=8080 ProxyTest
  */
@@ -36,7 +36,7 @@
 import java.util.ArrayList;
 
 public class ProxyTest implements HttpCallback {
-    static HttpServer server;
+    static TestHttpServer server;
 
     public ProxyTest() {
     }
@@ -74,7 +74,7 @@
             throw new RuntimeException("Default ProxySelector is null");
         ProxySelector.setDefault(new MyProxySelector());
         try {
-            server = new HttpServer (new ProxyTest(), 1, 10, 0);
+            server = new TestHttpServer (new ProxyTest(), 1, 10, 0);
             URL url = new URL("http://localhost:"+server.getLocalPort());
             System.out.println ("client opening connection to: " + url);
             HttpURLConnection urlc = (HttpURLConnection)url.openConnection ();
--- a/test/java/net/URL/PerConnectionProxy.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/URL/PerConnectionProxy.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @bug 4920526
  * @summary Needs per connection proxy support for URLs
  * @library ../../../sun/net/www/httptest/
- * @build ClosedChannelList HttpServer HttpTransaction HttpCallback
+ * @build ClosedChannelList TestHttpServer HttpTransaction HttpCallback
  * @compile PerConnectionProxy.java
  * @run main/othervm -Dhttp.proxyHost=inexistant -Dhttp.proxyPort=8080 PerConnectionProxy
  */
@@ -35,7 +35,7 @@
 import sun.net.www.*;
 
 public class PerConnectionProxy implements HttpCallback {
-    static HttpServer server;
+    static TestHttpServer server;
 
     public void request (HttpTransaction req) {
         req.setResponseEntityBody ("Hello .");
@@ -48,7 +48,7 @@
 
     public static void main(String[] args) {
         try {
-            server = new HttpServer (new PerConnectionProxy(), 1, 10, 0);
+            server = new TestHttpServer (new PerConnectionProxy(), 1, 10, 0);
             ProxyServer pserver = new ProxyServer(InetAddress.getByName("localhost"), server.getLocalPort());
             // start proxy server
             new Thread(pserver).start();
--- a/test/java/net/URLConnection/B5052093.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/java/net/URLConnection/B5052093.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 5052093
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main B5052093
  * @summary URLConnection doesn't support large files
  */
@@ -34,7 +34,7 @@
 import sun.net.www.protocol.file.FileURLConnection;
 
 public class B5052093 implements HttpCallback {
-    private static HttpServer server;
+    private static TestHttpServer server;
     private static long testSize = ((long) (Integer.MAX_VALUE)) + 2;
 
     public static class LargeFile extends File {
@@ -63,7 +63,7 @@
     }
 
     public static void main(String[] args) throws Exception {
-        server = new HttpServer(new B5052093(), 1, 10, 0);
+        server = new TestHttpServer(new B5052093(), 1, 10, 0);
         try {
             URL url = new URL("http://localhost:"+server.getLocalPort()+"/foo");
             URLConnection conn = url.openConnection();
--- a/test/sun/net/www/AuthHeaderTest.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/AuthHeaderTest.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 4804309
  * @library ../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main AuthHeaderTest
  * @summary AuthHeaderTest bug
  */
@@ -90,13 +90,13 @@
         is.close();
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main (String[] args) throws Exception {
         MyAuthenticator auth = new MyAuthenticator ();
         Authenticator.setDefault (auth);
         try {
-            server = new HttpServer (new AuthHeaderTest(), 1, 10, 0);
+            server = new TestHttpServer (new AuthHeaderTest(), 1, 10, 0);
             System.out.println ("Server: listening on port: " + server.getLocalPort());
             client ("http://localhost:"+server.getLocalPort()+"/d1/foo.html");
         } catch (Exception e) {
--- a/test/sun/net/www/http/ChunkedInputStream/ChunkedEncodingWithProgressMonitorTest.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/http/ChunkedInputStream/ChunkedEncodingWithProgressMonitorTest.java	Fri Sep 07 15:30:17 2012 +0100
@@ -24,8 +24,6 @@
 /**
  * @test
  * @bug 4333920 4994372
- * @library ../../../../../sun/net/www/httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
  * @run main ChunkedEncodingWithProgressMonitorTest
  * @summary ChunkedEncoding unit test; MeteredStream/ProgressData problem
  */
--- a/test/sun/net/www/http/KeepAliveCache/B5045306.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/http/KeepAliveCache/B5045306.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 5045306 6356004 6993490
  * @library ../../httptest/
- * @build HttpCallback HttpServer HttpTransaction
+ * @build HttpCallback TestHttpServer HttpTransaction
  * @run main/othervm B5045306
  * @summary Http keep-alive implementation is not efficient
  */
@@ -50,7 +50,7 @@
 public class B5045306
 {
     static SimpleHttpTransaction httpTrans;
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main(String[] args) throws Exception {
         startHttpServer();
@@ -60,7 +60,7 @@
     public static void startHttpServer() {
         try {
             httpTrans = new SimpleHttpTransaction();
-            server = new HttpServer(httpTrans, 1, 10, 0);
+            server = new TestHttpServer(httpTrans, 1, 10, 0);
         } catch (IOException e) {
             e.printStackTrace();
         }
--- a/test/sun/net/www/httptest/HttpServer.java	Fri Sep 07 14:00:31 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,728 +0,0 @@
-/*
- * Copyright (c) 2002, 2010, 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.*;
-
-/**
- * This class implements a simple HTTP server. It uses multiple threads to
- * handle connections in parallel, and also multiple connections/requests
- * can be handled per thread.
- * <p>
- * It must be instantiated with a {@link HttpCallback} object to which
- * requests are given and must be handled.
- * <p>
- * 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 changes are made here, please sure they are propagated to
- * the HTTPS equivalent in the JSSE regression test suite.
- *
- * NOTE NOTE NOTE NOTE NOTE NOTE NOTE
- */
-
-public class HttpServer {
-
-    ServerSocketChannel schan;
-    int threads;
-    int cperthread;
-    HttpCallback cb;
-    Server[] servers;
-
-    /**
-     * Create a <code>HttpServer<code> 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 <code>HttpServer<code> 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 <code>HttpServer<code> 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. <code>Zero</code>
-     *  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;
-        servers = new Server [threads];
-        for (int i=0; i<threads; i++) {
-            servers[i] = new Server (cb, schan, cperthread);
-            servers[i].start();
-        }
-    }
-
-    /** Tell all threads in the server to exit within 5 seconds.
-     *  This is an abortive termination. Just prior to the thread exiting
-     *  all channels in that thread waiting to be closed are forceably closed.
-     */
-
-    public void terminate () {
-        for (int i=0; i<threads; i++) {
-            servers[i].terminate ();
-        }
-    }
-
-    /**
-     * return the local port number to which the server is bound.
-     * @return the local port number
-     */
-
-    public int getLocalPort () {
-        return schan.socket().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 (false);
-                            sock.register (selector, SelectionKey.OP_READ);
-                            nconn ++;
-                            System.out.println("SERVER: new connection. chan[" + sock + "]");
-                            if (nconn == maxconn) {
-                                /* deregister */
-                                listenerKey.cancel ();
-                                listenerKey = null;
-                            }
-                        } else {
-                            if (key.isReadable()) {
-                                boolean closed;
-                                SocketChannel chan = (SocketChannel) key.channel();
-                                System.out.println("SERVER: connection readable. chan[" + chan + "]");
-                                if (key.attachment() != null) {
-                                    System.out.println("Server: comsume");
-                                    closed = consume (chan);
-                                } else {
-                                    closed = read (chan, key);
-                                }
-                                if (closed) {
-                                    chan.close ();
-                                    key.cancel ();
-                                    if (nconn == maxconn) {
-                                        listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
-                                    }
-                                    nconn --;
-                                }
-                            }
-                        }
-                        iter.remove();
-                    }
-                    clist.check();
-                    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;
-        }
-
-        /* 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));
-                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, key);
-                    cb.request (msg);
-                } catch (URISyntaxException e) {
-                    System.err.println ("Invalid URI: " + e);
-                    msg = new HttpTransaction (this, cmd, null, null, null, null, key);
-                    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 <CR><LF>:  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 (SelectionKey key) throws IOException {
-            SocketChannel ch = (SocketChannel)key.channel ();
-            System.out.println("SERVER: orderlyCloseChannel chan[" + ch + "]");
-            ch.socket().shutdownOutput();
-            key.attach (this);
-            clist.add (key);
-        }
-
-        synchronized void abortiveCloseChannel (SelectionKey key) throws IOException {
-            SocketChannel ch = (SocketChannel)key.channel ();
-            System.out.println("SERVER: abortiveCloseChannel chan[" + ch + "]");
-
-            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 {
-        SocketChannel channel;
-        Selector selector;
-        ByteBuffer chanbuf;
-        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) throws IOException {
-            this.channel = chan;
-            selector = Selector.open();
-            chanbuf = ByteBuffer.allocate (1024);
-            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;
-                chanbuf.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;
-
-            chanbuf.clear ();
-            available = channel.read (chanbuf);
-            if (available > 0)
-                chanbuf.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 {
-        SocketChannel channel;
-        ByteBuffer buf;
-        SelectionKey key;
-        Selector selector;
-        boolean closed;
-        byte[] one;
-
-        public NioOutputStream (SocketChannel channel) throws IOException {
-            this.channel = channel;
-            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");
-
-            buf = ByteBuffer.allocate (len);
-            buf.put (b, off, len);
-            buf.flip ();
-            int n;
-            while ((n = channel.write (buf)) < len) {
-                len -= n;
-                if (len == 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.
-     * <P>
-     * 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.
-     * <p>
-     * 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).
-     * <P>
-     * 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);
-                }
-            }
-        }
-    }
-}
--- a/test/sun/net/www/httptest/HttpTransaction.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/httptest/HttpTransaction.java	Fri Sep 07 15:30:17 2012 +0100
@@ -37,7 +37,7 @@
 
     String command;
     URI requesturi;
-    HttpServer.Server server;
+    TestHttpServer.Server server;
     MessageHeader reqheaders, reqtrailers;
     String reqbody;
     byte[] rspbody;
@@ -46,7 +46,7 @@
     int rspbodylen;
     boolean rspchunked;
 
-    HttpTransaction (HttpServer.Server server, String command,
+    HttpTransaction (TestHttpServer.Server server, String command,
                         URI requesturi, MessageHeader headers,
                         String body, MessageHeader trailers, SelectionKey  key) {
         this.command = command;
@@ -290,7 +290,7 @@
      * @param rTag the response string to send with the response code
      */
     public void sendResponse (int rCode, String rTag) throws IOException {
-        OutputStream os = new HttpServer.NioOutputStream(channel());
+        OutputStream os = new TestHttpServer.NioOutputStream(channel());
         PrintStream ps = new PrintStream (os);
         ps.print ("HTTP/1.1 " + rCode + " " + rTag + "\r\n");
         if (rspheaders != null) {
@@ -314,7 +314,7 @@
     /* sends one byte less than intended */
 
     public void sendPartialResponse (int rCode, String rTag)throws IOException {
-        OutputStream os = new HttpServer.NioOutputStream(channel());
+        OutputStream os = new TestHttpServer.NioOutputStream(channel());
         PrintStream ps = new PrintStream (os);
         ps.print ("HTTP/1.1 " + rCode + " " + rTag + "\r\n");
         ps.flush();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/net/www/httptest/TestHttpServer.java	Fri Sep 07 15:30:17 2012 +0100
@@ -0,0 +1,728 @@
+/*
+ * Copyright (c) 2002, 2010, 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.*;
+
+/**
+ * This class implements a simple HTTP server. It uses multiple threads to
+ * handle connections in parallel, and also multiple connections/requests
+ * can be handled per thread.
+ * <p>
+ * It must be instantiated with a {@link HttpCallback} object to which
+ * requests are given and must be handled.
+ * <p>
+ * 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 changes are made here, please sure they are propagated to
+ * the HTTPS equivalent in the JSSE regression test suite.
+ *
+ * NOTE NOTE NOTE NOTE NOTE NOTE NOTE
+ */
+
+public class TestHttpServer {
+
+    ServerSocketChannel schan;
+    int threads;
+    int cperthread;
+    HttpCallback cb;
+    Server[] servers;
+
+    /**
+     * Create a <code>TestHttpServer<code> 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 TestHttpServer (HttpCallback cb) throws IOException {
+        this (cb, 1, 10, 0);
+    }
+
+    /**
+     * Create a <code>TestHttpServer<code> 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 TestHttpServer (HttpCallback cb, int threads, int cperthread)
+        throws IOException {
+        this (cb, threads, cperthread, 0);
+    }
+
+    /**
+     * Create a <code>TestHttpServer<code> 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. <code>Zero</code>
+     *  means choose any free port.
+     */
+
+    public TestHttpServer (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;
+        servers = new Server [threads];
+        for (int i=0; i<threads; i++) {
+            servers[i] = new Server (cb, schan, cperthread);
+            servers[i].start();
+        }
+    }
+
+    /** Tell all threads in the server to exit within 5 seconds.
+     *  This is an abortive termination. Just prior to the thread exiting
+     *  all channels in that thread waiting to be closed are forceably closed.
+     */
+
+    public void terminate () {
+        for (int i=0; i<threads; i++) {
+            servers[i].terminate ();
+        }
+    }
+
+    /**
+     * return the local port number to which the server is bound.
+     * @return the local port number
+     */
+
+    public int getLocalPort () {
+        return schan.socket().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 (false);
+                            sock.register (selector, SelectionKey.OP_READ);
+                            nconn ++;
+                            System.out.println("SERVER: new connection. chan[" + sock + "]");
+                            if (nconn == maxconn) {
+                                /* deregister */
+                                listenerKey.cancel ();
+                                listenerKey = null;
+                            }
+                        } else {
+                            if (key.isReadable()) {
+                                boolean closed;
+                                SocketChannel chan = (SocketChannel) key.channel();
+                                System.out.println("SERVER: connection readable. chan[" + chan + "]");
+                                if (key.attachment() != null) {
+                                    System.out.println("Server: comsume");
+                                    closed = consume (chan);
+                                } else {
+                                    closed = read (chan, key);
+                                }
+                                if (closed) {
+                                    chan.close ();
+                                    key.cancel ();
+                                    if (nconn == maxconn) {
+                                        listenerKey = schan.register (selector, SelectionKey.OP_ACCEPT);
+                                    }
+                                    nconn --;
+                                }
+                            }
+                        }
+                        iter.remove();
+                    }
+                    clist.check();
+                    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;
+        }
+
+        /* 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));
+                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, key);
+                    cb.request (msg);
+                } catch (URISyntaxException e) {
+                    System.err.println ("Invalid URI: " + e);
+                    msg = new HttpTransaction (this, cmd, null, null, null, null, key);
+                    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 <CR><LF>:  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 (SelectionKey key) throws IOException {
+            SocketChannel ch = (SocketChannel)key.channel ();
+            System.out.println("SERVER: orderlyCloseChannel chan[" + ch + "]");
+            ch.socket().shutdownOutput();
+            key.attach (this);
+            clist.add (key);
+        }
+
+        synchronized void abortiveCloseChannel (SelectionKey key) throws IOException {
+            SocketChannel ch = (SocketChannel)key.channel ();
+            System.out.println("SERVER: abortiveCloseChannel chan[" + ch + "]");
+
+            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 {
+        SocketChannel channel;
+        Selector selector;
+        ByteBuffer chanbuf;
+        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) throws IOException {
+            this.channel = chan;
+            selector = Selector.open();
+            chanbuf = ByteBuffer.allocate (1024);
+            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;
+                chanbuf.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;
+
+            chanbuf.clear ();
+            available = channel.read (chanbuf);
+            if (available > 0)
+                chanbuf.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 {
+        SocketChannel channel;
+        ByteBuffer buf;
+        SelectionKey key;
+        Selector selector;
+        boolean closed;
+        byte[] one;
+
+        public NioOutputStream (SocketChannel channel) throws IOException {
+            this.channel = channel;
+            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");
+
+            buf = ByteBuffer.allocate (len);
+            buf.put (b, off, len);
+            buf.flip ();
+            int n;
+            while ((n = channel.write (buf)) < len) {
+                len -= n;
+                if (len == 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.
+     * <P>
+     * 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.
+     * <p>
+     * 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).
+     * <P>
+     * 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);
+                }
+            }
+        }
+    }
+}
--- a/test/sun/net/www/protocol/http/B6296310.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/protocol/http/B6296310.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 6296310
  * @library ../../httptest/
- * @build HttpCallback HttpServer HttpTransaction
+ * @build HttpCallback TestHttpServer HttpTransaction
  * @run main/othervm B6296310
  * @summary  REGRESSION: AppletClassLoader.getResourceAsStream() behaviour is wrong in some cases
  */
@@ -42,7 +42,7 @@
 public class B6296310
 {
    static SimpleHttpTransaction httpTrans;
-   static HttpServer server;
+   static TestHttpServer server;
 
    public static void main(String[] args)
    {
@@ -55,7 +55,7 @@
    public static void startHttpServer() {
       try {
          httpTrans = new SimpleHttpTransaction();
-         server = new HttpServer(httpTrans, 1, 10, 0);
+         server = new TestHttpServer(httpTrans, 1, 10, 0);
       } catch (IOException e) {
          e.printStackTrace();
       }
--- a/test/sun/net/www/protocol/http/B6299712.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/protocol/http/B6299712.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 6299712
  * @library ../../httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main/othervm B6299712
  * @summary  NullPointerException in sun.net.www.protocol.http.HttpURLConnection.followRedirect
  */
@@ -49,7 +49,7 @@
  */
 public class B6299712 {
     static SimpleHttpTransaction httpTrans;
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main(String[] args) throws Exception {
         ResponseCache.setDefault(new DeployCacheHandler());
@@ -61,7 +61,7 @@
     public static void startHttpServer() {
         try {
             httpTrans = new SimpleHttpTransaction();
-            server = new HttpServer(httpTrans, 1, 10, 0);
+            server = new TestHttpServer(httpTrans, 1, 10, 0);
         } catch (IOException e) {
             e.printStackTrace();
         }
--- a/test/sun/net/www/protocol/http/RelativeRedirect.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/protocol/http/RelativeRedirect.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 4726087
  * @library ../../httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main RelativeRedirect
  * @summary URLConnection cannot handle redirects
  */
@@ -35,7 +35,7 @@
 
 public class RelativeRedirect implements HttpCallback {
     static int count = 0;
-    static HttpServer server;
+    static TestHttpServer server;
 
     static class MyAuthenticator extends Authenticator {
         public MyAuthenticator () {
@@ -89,7 +89,7 @@
         MyAuthenticator auth = new MyAuthenticator ();
         Authenticator.setDefault (auth);
         try {
-            server = new HttpServer (new RelativeRedirect(), 1, 10, 0);
+            server = new TestHttpServer (new RelativeRedirect(), 1, 10, 0);
             System.out.println ("Server: listening on port: " + server.getLocalPort());
             URL url = new URL("http://localhost:"+server.getLocalPort());
             System.out.println ("client opening connection to: " + url);
--- a/test/sun/net/www/protocol/http/ResponseCacheStream.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/protocol/http/ResponseCacheStream.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 6262486
  * @library ../../httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+ * @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main/othervm -Dhttp.keepAlive=false ResponseCacheStream
  * @summary COMPATIBILITY: jagex_com - Monkey Puzzle applet fails to load
  */
@@ -91,13 +91,13 @@
         }
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main(String[] args) throws Exception {
         MyResponseCache cache = new MyResponseCache();
         try {
             ResponseCache.setDefault(cache);
-            server = new HttpServer (new ResponseCacheStream());
+            server = new TestHttpServer (new ResponseCacheStream());
             System.out.println ("Server: listening on port: " + server.getLocalPort());
             URL url = new URL ("http://127.0.0.1:"+server.getLocalPort()+"/");
             System.out.println ("Client: connecting to " + url);
--- a/test/sun/net/www/protocol/http/SetChunkedStreamingMode.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/net/www/protocol/http/SetChunkedStreamingMode.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 5049976
  * @library ../../httptest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction
+  @build HttpCallback TestHttpServer ClosedChannelList HttpTransaction
  * @run main SetChunkedStreamingMode
  * @summary Unspecified NPE is thrown when streaming output mode is enabled
  */
@@ -60,11 +60,11 @@
         System.out.println ("finished reading");
     }
 
-    static HttpServer server;
+    static TestHttpServer server;
 
     public static void main (String[] args) throws Exception {
         try {
-            server = new HttpServer (new SetChunkedStreamingMode(), 1, 10, 0);
+            server = new TestHttpServer (new SetChunkedStreamingMode(), 1, 10, 0);
             System.out.println ("Server: listening on port: " + server.getLocalPort());
             URL url = new URL ("http://127.0.0.1:"+server.getLocalPort()+"/");
             System.out.println ("Client: connecting to " + url);
--- a/test/sun/security/ssl/sun/net/www/http/ChunkedOutputStream/Test.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/security/ssl/sun/net/www/http/ChunkedOutputStream/Test.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,6 +25,7 @@
  * @test
  * @bug 5026745
  * @library ../../httpstest/
+ * @build TestHttpsServer HttpCallback
  * @run main/othervm Test
  *
  *     SunJSSE does not support dynamic system properties, no way to re-use
@@ -275,7 +276,7 @@
         }
     }
 
-    static HttpServer server;
+    static TestHttpsServer server;
 
     public static void main (String[] args) throws Exception {
         // setup properties to do ssl
@@ -296,7 +297,7 @@
             HttpsURLConnection.setDefaultHostnameVerifier(new NameVerifier());
 
             try {
-                server = new HttpServer (new Test(), 1, 10, 0);
+                server = new TestHttpsServer (new Test(), 1, 10, 0);
                 System.out.println ("Server started: listening on port: " + server.getLocalPort());
                 // the test server doesn't support keep-alive yet
                 // test1("http://localhost:"+server.getLocalPort()+"/d0");
--- a/test/sun/security/ssl/sun/net/www/httpstest/HttpServer.java	Fri Sep 07 14:00:31 2012 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -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)
- * <p>
- * It must be instantiated with a {@link HttpCallback} object to which
- * requests are given and must be handled.
- * <p>
- * 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 <code>HttpServer<code> 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 <code>HttpServer<code> 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 <code>HttpServer<code> 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. <code>Zero</code>
-     *  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<threads; i++) {
-                servers[i] = new Server (cb, schan, cperthread);
-                servers[i].start();
-            }
-        } catch (Exception ex) {
-            throw new RuntimeException("test failed. cause: "+ex.getMessage());
-        }
-    }
-
-    /** Tell all threads in the server to exit within 5 seconds.
-     *  This is an abortive termination. Just prior to the thread exiting
-     *  all channels in that thread waiting to be closed are forceably closed.
-     */
-
-    public void terminate () {
-        for (int i=0; i<threads; i++) {
-            servers[i].terminate ();
-        }
-    }
-
-    /**
-     * return the local port number to which the server is bound.
-     * @return the local port number
-     */
-
-    public int getLocalPort () {
-        return schan.socket().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;
-        /*
-         * 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);
-
-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 <CR><LF>:  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.
-     * <P>
-     * 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.
-     * <p>
-     * 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).
-     * <P>
-     * 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);
-                }
-            }
-        }
-    }
-}
--- a/test/sun/security/ssl/sun/net/www/httpstest/HttpTransaction.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/security/ssl/sun/net/www/httpstest/HttpTransaction.java	Fri Sep 07 15:30:17 2012 +0100
@@ -37,7 +37,7 @@
 
     String command;
     URI requesturi;
-    HttpServer.ServerWorker server;
+    TestHttpsServer.ServerWorker server;
     MessageHeader reqheaders, reqtrailers;
     String reqbody;
     byte[] rspbody;
@@ -46,7 +46,7 @@
     int rspbodylen;
     boolean rspchunked;
 
-    HttpTransaction (HttpServer.ServerWorker server, String command,
+    HttpTransaction (TestHttpsServer.ServerWorker server, String command,
                         URI requesturi, MessageHeader headers,
                         String body, MessageHeader trailers, SocketChannel ch) {
         this.command = command;
@@ -290,7 +290,7 @@
      * @param rTag the response string to send with the response code
      */
     public void sendResponse (int rCode, String rTag) throws IOException {
-        OutputStream os = new HttpServer.NioOutputStream(channel(), server.getSSLEngine(), server.outNetBB(), server.outAppBB());
+        OutputStream os = new TestHttpsServer.NioOutputStream(channel(), server.getSSLEngine(), server.outNetBB(), server.outAppBB());
         PrintStream ps = new PrintStream (os);
         ps.print ("HTTP/1.1 " + rCode + " " + rTag + "\r\n");
         if (rspheaders != null) {
@@ -314,7 +314,7 @@
     /* sends one byte less than intended */
 
     public void sendPartialResponse (int rCode, String rTag)throws IOException {
-        OutputStream os = new HttpServer.NioOutputStream(channel(), server.getSSLEngine(), server.outNetBB(), server.outAppBB());
+        OutputStream os = new TestHttpsServer.NioOutputStream(channel(), server.getSSLEngine(), server.outNetBB(), server.outAppBB());
         PrintStream ps = new PrintStream (os);
         ps.print ("HTTP/1.1 " + rCode + " " + rTag + "\r\n");
         ps.flush();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/sun/net/www/httpstest/TestHttpsServer.java	Fri Sep 07 15:30:17 2012 +0100
@@ -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)
+ * <p>
+ * It must be instantiated with a {@link HttpCallback} object to which
+ * requests are given and must be handled.
+ * <p>
+ * 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 <code>TestHttpsServer<code> 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 <code>TestHttpsServer<code> 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 <code>TestHttpsServer<code> 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. <code>Zero</code>
+     *  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<threads; i++) {
+                servers[i] = new Server (cb, schan, cperthread);
+                servers[i].start();
+            }
+        } catch (Exception ex) {
+            throw new RuntimeException("test failed. cause: "+ex.getMessage());
+        }
+    }
+
+    /** Tell all threads in the server to exit within 5 seconds.
+     *  This is an abortive termination. Just prior to the thread exiting
+     *  all channels in that thread waiting to be closed are forceably closed.
+     */
+
+    public void terminate () {
+        for (int i=0; i<threads; i++) {
+            servers[i].terminate ();
+        }
+    }
+
+    /**
+     * return the local port number to which the server is bound.
+     * @return the local port number
+     */
+
+    public int getLocalPort () {
+        return schan.socket().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;
+        /*
+         * 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);
+
+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 <CR><LF>:  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.
+     * <P>
+     * 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.
+     * <p>
+     * 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).
+     * <P>
+     * 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);
+                }
+            }
+        }
+    }
+}
--- a/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java	Fri Sep 07 14:00:31 2012 +0100
+++ b/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java	Fri Sep 07 15:30:17 2012 +0100
@@ -25,7 +25,7 @@
  * @test
  * @bug 6216082
  * @library ../../../httpstest/
- * @build HttpCallback HttpServer ClosedChannelList HttpTransaction TunnelProxy
+ * @build HttpCallback TestHttpsServer ClosedChannelList HttpTransaction TunnelProxy
  * @summary  Redirect problem with HttpsURLConnection using a proxy
  *     SunJSSE does not support dynamic system properties, no way to re-use
  *     system properties in samevm/agentvm mode.
@@ -39,7 +39,7 @@
 
 public class B6216082 {
     static SimpleHttpTransaction httpTrans;
-    static HttpServer server;
+    static TestHttpsServer server;
     static TunnelProxy proxy;
 
     // it seems there's no proxy ever if a url points to 'localhost',
@@ -133,7 +133,7 @@
         // Both the https server and the proxy let the
         // system pick up an ephemeral port.
         httpTrans = new SimpleHttpTransaction();
-        server = new HttpServer(httpTrans, 1, 10, 0);
+        server = new TestHttpsServer(httpTrans, 1, 10, 0);
         proxy = new TunnelProxy(1, 10, 0);
     }