6354758: rename old test HttpServer classes
Reviewed-by: chegar, michaelm, khazra
Contributed-by: John Zavgren <john.zavgren@oracle.com>
--- a/jdk/test/java/net/Authenticator/B4678055.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/Authenticator/B4722333.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/Authenticator/B4759514.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/Authenticator/B4769350.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/Authenticator/B4921848.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/Authenticator/B4933582.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/Authenticator/B4962064.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/CookieHandler/CookieManagerTest.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/ProxySelector/LoopbackAddresses.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/ProxySelector/ProxyTest.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/URL/PerConnectionProxy.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/java/net/URLConnection/B5052093.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/sun/net/www/AuthHeaderTest.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/sun/net/www/http/ChunkedInputStream/ChunkedEncodingWithProgressMonitorTest.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/sun/net/www/http/KeepAliveCache/B5045306.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/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/jdk/test/sun/net/www/httptest/HttpTransaction.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/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/jdk/test/sun/net/www/protocol/http/B6296310.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/sun/net/www/protocol/http/B6299712.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/sun/net/www/protocol/http/RelativeRedirect.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/sun/net/www/protocol/http/ResponseCacheStream.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/sun/net/www/protocol/http/SetChunkedStreamingMode.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/test/sun/security/ssl/sun/net/www/http/ChunkedOutputStream/Test.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/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/jdk/test/sun/security/ssl/sun/net/www/httpstest/HttpTransaction.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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/jdk/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/jdk/test/sun/security/ssl/sun/net/www/protocol/https/HttpsURLConnection/B6216082.java Fri Sep 07 14:00:31 2012 +0100
+++ b/jdk/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);
}