提交 ef144a90 编写于 作者: M michaelm

6725892: Http server stability issues

Reviewed-by: chegar
上级 24acdd3f
...@@ -91,6 +91,7 @@ public class HttpsConfigurator { ...@@ -91,6 +91,7 @@ public class HttpsConfigurator {
return context; return context;
} }
//BEGIN_TIGER_EXCLUDE
/** /**
* Called by the HttpsServer to configure the parameters * Called by the HttpsServer to configure the parameters
* for a https connection currently being established. * for a https connection currently being established.
...@@ -111,4 +112,5 @@ public class HttpsConfigurator { ...@@ -111,4 +112,5 @@ public class HttpsConfigurator {
public void configure (HttpsParameters params) { public void configure (HttpsParameters params) {
params.setSSLParameters (getSSLContext().getDefaultSSLParameters()); params.setSSLParameters (getSSLContext().getDefaultSSLParameters());
} }
//END_TIGER_EXCLUDE
} }
...@@ -25,7 +25,9 @@ ...@@ -25,7 +25,9 @@
package com.sun.net.httpserver; package com.sun.net.httpserver;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
//BEGIN_TIGER_EXCLUDE
import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLParameters;
//END_TIGER_EXCLUDE
/** /**
* Represents the set of parameters for each https * Represents the set of parameters for each https
...@@ -67,6 +69,7 @@ public abstract class HttpsParameters { ...@@ -67,6 +69,7 @@ public abstract class HttpsParameters {
*/ */
public abstract InetSocketAddress getClientAddress(); public abstract InetSocketAddress getClientAddress();
//BEGIN_TIGER_EXCLUDE
/** /**
* Sets the SSLParameters to use for this HttpsParameters. * Sets the SSLParameters to use for this HttpsParameters.
* The parameters must be supported by the SSLContext contained * The parameters must be supported by the SSLContext contained
...@@ -79,6 +82,7 @@ public abstract class HttpsParameters { ...@@ -79,6 +82,7 @@ public abstract class HttpsParameters {
* invalid or unsupported. * invalid or unsupported.
*/ */
public abstract void setSSLParameters (SSLParameters params); public abstract void setSSLParameters (SSLParameters params);
//END_TIGER_EXCLUDE
/** /**
* Returns a copy of the array of ciphersuites or null if none * Returns a copy of the array of ciphersuites or null if none
......
...@@ -110,6 +110,7 @@ class ChunkedInputStream extends LeftOverInputStream { ...@@ -110,6 +110,7 @@ class ChunkedInputStream extends LeftOverInputStream {
if (remaining == 0) { if (remaining == 0) {
eof = true; eof = true;
consumeCRLF(); consumeCRLF();
t.getServerImpl().requestCompleted (t.getConnection());
return -1; return -1;
} }
needToReadHeader = false; needToReadHeader = false;
......
...@@ -40,5 +40,7 @@ class Event { ...@@ -40,5 +40,7 @@ class Event {
class WriteFinishedEvent extends Event { class WriteFinishedEvent extends Event {
WriteFinishedEvent (ExchangeImpl t) { WriteFinishedEvent (ExchangeImpl t) {
super (t); super (t);
assert !t.writefinished;
t.writefinished = true;
} }
} }
...@@ -38,6 +38,7 @@ class ExchangeImpl { ...@@ -38,6 +38,7 @@ class ExchangeImpl {
Headers reqHdrs, rspHdrs; Headers reqHdrs, rspHdrs;
Request req; Request req;
String method; String method;
boolean writefinished;
URI uri; URI uri;
HttpConnection connection; HttpConnection connection;
long reqContentLen; long reqContentLen;
......
...@@ -56,6 +56,9 @@ class FixedLengthInputStream extends LeftOverInputStream { ...@@ -56,6 +56,9 @@ class FixedLengthInputStream extends LeftOverInputStream {
int n = in.read(b, off, len); int n = in.read(b, off, len);
if (n > -1) { if (n > -1) {
remaining -= n; remaining -= n;
if (remaining == 0) {
t.getServerImpl().requestCompleted (t.getConnection());
}
} }
return n; return n;
} }
......
...@@ -55,10 +55,15 @@ class HttpConnection { ...@@ -55,10 +55,15 @@ class HttpConnection {
SelectionKey selectionKey; SelectionKey selectionKey;
String protocol; String protocol;
long time; long time;
volatile long creationTime; // time this connection was created
volatile long rspStartedTime; // time we started writing the response
int remaining; int remaining;
boolean closed = false; boolean closed = false;
Logger logger; Logger logger;
public enum State {IDLE, REQUEST, RESPONSE};
volatile State state;
public String toString() { public String toString() {
String s = null; String s = null;
if (chan != null) { if (chan != null) {
...@@ -78,6 +83,14 @@ class HttpConnection { ...@@ -78,6 +83,14 @@ class HttpConnection {
context = ctx; context = ctx;
} }
State getState() {
return state;
}
void setState (State s) {
state = s;
}
void setParameters ( void setParameters (
InputStream in, OutputStream rawout, SocketChannel chan, InputStream in, OutputStream rawout, SocketChannel chan,
SSLEngine engine, SSLStreams sslStreams, SSLContext sslContext, String protocol, SSLEngine engine, SSLStreams sslStreams, SSLContext sslContext, String protocol,
......
...@@ -201,32 +201,22 @@ class Request { ...@@ -201,32 +201,22 @@ class Request {
static class ReadStream extends InputStream { static class ReadStream extends InputStream {
SocketChannel channel; SocketChannel channel;
SelectorCache sc;
Selector selector;
ByteBuffer chanbuf; ByteBuffer chanbuf;
SelectionKey key;
int available;
byte[] one; byte[] one;
boolean closed = false, eof = false; private boolean closed = false, eof = false;
ByteBuffer markBuf; /* reads may be satisifed from this buffer */ ByteBuffer markBuf; /* reads may be satisifed from this buffer */
boolean marked; boolean marked;
boolean reset; boolean reset;
int readlimit; int readlimit;
static long readTimeout; static long readTimeout;
ServerImpl server; ServerImpl server;
final static int BUFSIZE = 8 * 1024;
static {
readTimeout = ServerConfig.getReadTimeout();
}
public ReadStream (ServerImpl server, SocketChannel chan) throws IOException { public ReadStream (ServerImpl server, SocketChannel chan) throws IOException {
this.channel = chan; this.channel = chan;
this.server = server; this.server = server;
sc = SelectorCache.getSelectorCache(); chanbuf = ByteBuffer.allocate (BUFSIZE);
selector = sc.getSelector(); chanbuf.clear();
chanbuf = ByteBuffer.allocate (8* 1024);
key = chan.register (selector, SelectionKey.OP_READ);
available = 0;
one = new byte[1]; one = new byte[1];
closed = marked = reset = false; closed = marked = reset = false;
} }
...@@ -255,6 +245,12 @@ class Request { ...@@ -255,6 +245,12 @@ class Request {
return -1; return -1;
} }
assert channel.isBlocking();
if (off < 0 || srclen < 0|| srclen > (b.length-off)) {
throw new IndexOutOfBoundsException ();
}
if (reset) { /* satisfy from markBuf */ if (reset) { /* satisfy from markBuf */
canreturn = markBuf.remaining (); canreturn = markBuf.remaining ();
willreturn = canreturn>srclen ? srclen : canreturn; willreturn = canreturn>srclen ? srclen : canreturn;
...@@ -263,17 +259,19 @@ class Request { ...@@ -263,17 +259,19 @@ class Request {
reset = false; reset = false;
} }
} else { /* satisfy from channel */ } else { /* satisfy from channel */
canreturn = available(); chanbuf.clear ();
while (canreturn == 0 && !eof) { if (srclen < BUFSIZE) {
block (); chanbuf.limit (srclen);
canreturn = available();
} }
if (eof) { do {
willreturn = channel.read (chanbuf);
} while (willreturn == 0);
if (willreturn == -1) {
eof = true;
return -1; return -1;
} }
willreturn = canreturn>srclen ? srclen : canreturn; chanbuf.flip ();
chanbuf.get(b, off, willreturn); chanbuf.get(b, off, willreturn);
available -= willreturn;
if (marked) { /* copy into markBuf */ if (marked) { /* copy into markBuf */
try { try {
...@@ -286,6 +284,11 @@ class Request { ...@@ -286,6 +284,11 @@ class Request {
return willreturn; return willreturn;
} }
public boolean markSupported () {
return true;
}
/* Does not query the OS socket */
public synchronized int available () throws IOException { public synchronized int available () throws IOException {
if (closed) if (closed)
throw new IOException ("Stream is closed"); throw new IOException ("Stream is closed");
...@@ -296,36 +299,7 @@ class Request { ...@@ -296,36 +299,7 @@ class Request {
if (reset) if (reset)
return markBuf.remaining(); return markBuf.remaining();
if (available > 0) return chanbuf.remaining();
return available;
chanbuf.clear ();
available = channel.read (chanbuf);
if (available > 0) {
chanbuf.flip();
} else if (available == -1) {
eof = true;
available = 0;
}
return available;
}
/**
* block() only called when available==0 and buf is empty
*/
private synchronized void block () throws IOException {
long currtime = server.getTime();
long maxtime = currtime + readTimeout;
while (currtime < maxtime) {
if (selector.select (readTimeout) == 1) {
selector.selectedKeys().clear();
available ();
return;
}
currtime = server.getTime();
}
throw new SocketTimeoutException ("no data received");
} }
public void close () throws IOException { public void close () throws IOException {
...@@ -333,8 +307,6 @@ class Request { ...@@ -333,8 +307,6 @@ class Request {
return; return;
} }
channel.close (); channel.close ();
selector.selectNow();
sc.freeSelector(selector);
closed = true; closed = true;
} }
...@@ -362,23 +334,14 @@ class Request { ...@@ -362,23 +334,14 @@ class Request {
SocketChannel channel; SocketChannel channel;
ByteBuffer buf; ByteBuffer buf;
SelectionKey key; SelectionKey key;
SelectorCache sc;
Selector selector;
boolean closed; boolean closed;
byte[] one; byte[] one;
ServerImpl server; ServerImpl server;
static long writeTimeout;
static {
writeTimeout = ServerConfig.getWriteTimeout();
}
public WriteStream (ServerImpl server, SocketChannel channel) throws IOException { public WriteStream (ServerImpl server, SocketChannel channel) throws IOException {
this.channel = channel; this.channel = channel;
this.server = server; this.server = server;
sc = SelectorCache.getSelectorCache(); assert channel.isBlocking();
selector = sc.getSelector();
key = channel.register (selector, SelectionKey.OP_WRITE);
closed = false; closed = false;
one = new byte [1]; one = new byte [1];
buf = ByteBuffer.allocate (4096); buf = ByteBuffer.allocate (4096);
...@@ -411,31 +374,14 @@ class Request { ...@@ -411,31 +374,14 @@ class Request {
l -= n; l -= n;
if (l == 0) if (l == 0)
return; return;
block();
}
}
void block () throws IOException {
long currtime = server.getTime();
long maxtime = currtime + writeTimeout;
while (currtime < maxtime) {
if (selector.select (writeTimeout) == 1) {
selector.selectedKeys().clear ();
return;
} }
currtime = server.getTime();
} }
throw new SocketTimeoutException ("write blocked too long");
}
public void close () throws IOException { public void close () throws IOException {
if (closed) if (closed)
return; return;
//server.logStackTrace ("Request.OS.close: isOpen="+channel.isOpen());
channel.close (); channel.close ();
selector.selectNow();
sc.freeSelector(selector);
closed = true; closed = true;
} }
} }
......
...@@ -53,8 +53,6 @@ class SSLStreams { ...@@ -53,8 +53,6 @@ class SSLStreams {
EngineWrapper wrapper; EngineWrapper wrapper;
OutputStream os; OutputStream os;
InputStream is; InputStream is;
static long readTimeout = ServerConfig.getReadTimeout();
static long writeTimeout = ServerConfig.getWriteTimeout();
/* held by thread doing the hand-shake on this connection */ /* held by thread doing the hand-shake on this connection */
Lock handshaking = new ReentrantLock(); Lock handshaking = new ReentrantLock();
...@@ -77,10 +75,13 @@ class SSLStreams { ...@@ -77,10 +75,13 @@ class SSLStreams {
if (cfg != null) { if (cfg != null) {
Parameters params = new Parameters (cfg, addr); Parameters params = new Parameters (cfg, addr);
cfg.configure (params); cfg.configure (params);
//BEGIN_TIGER_EXCLUDE
SSLParameters sslParams = params.getSSLParameters(); SSLParameters sslParams = params.getSSLParameters();
if (sslParams != null) { if (sslParams != null) {
engine.setSSLParameters (sslParams); engine.setSSLParameters (sslParams);
} else { } else
//END_TIGER_EXCLUDE
{
/* tiger compatibility */ /* tiger compatibility */
if (params.getCipherSuites() != null) { if (params.getCipherSuites() != null) {
try { try {
...@@ -104,7 +105,6 @@ class SSLStreams { ...@@ -104,7 +105,6 @@ class SSLStreams {
class Parameters extends HttpsParameters { class Parameters extends HttpsParameters {
InetSocketAddress addr; InetSocketAddress addr;
SSLParameters params;
HttpsConfigurator cfg; HttpsConfigurator cfg;
Parameters (HttpsConfigurator cfg, InetSocketAddress addr) { Parameters (HttpsConfigurator cfg, InetSocketAddress addr) {
...@@ -117,12 +117,15 @@ class SSLStreams { ...@@ -117,12 +117,15 @@ class SSLStreams {
public HttpsConfigurator getHttpsConfigurator() { public HttpsConfigurator getHttpsConfigurator() {
return cfg; return cfg;
} }
//BEGIN_TIGER_EXCLUDE
SSLParameters params;
public void setSSLParameters (SSLParameters p) { public void setSSLParameters (SSLParameters p) {
params = p; params = p;
} }
SSLParameters getSSLParameters () { SSLParameters getSSLParameters () {
return params; return params;
} }
//END_TIGER_EXCLUDE
} }
/** /**
...@@ -245,9 +248,6 @@ class SSLStreams { ...@@ -245,9 +248,6 @@ class SSLStreams {
SocketChannel chan; SocketChannel chan;
SSLEngine engine; SSLEngine engine;
SelectorCache sc;
Selector write_selector, read_selector;
SelectionKey wkey, rkey;
Object wrapLock, unwrapLock; Object wrapLock, unwrapLock;
ByteBuffer unwrap_src, wrap_dst; ByteBuffer unwrap_src, wrap_dst;
boolean closed = false; boolean closed = false;
...@@ -260,16 +260,9 @@ class SSLStreams { ...@@ -260,16 +260,9 @@ class SSLStreams {
unwrapLock = new Object(); unwrapLock = new Object();
unwrap_src = allocate(BufType.PACKET); unwrap_src = allocate(BufType.PACKET);
wrap_dst = allocate(BufType.PACKET); wrap_dst = allocate(BufType.PACKET);
sc = SelectorCache.getSelectorCache();
write_selector = sc.getSelector();
wkey = chan.register (write_selector, SelectionKey.OP_WRITE);
read_selector = sc.getSelector();
wkey = chan.register (read_selector, SelectionKey.OP_READ);
} }
void close () throws IOException { void close () throws IOException {
sc.freeSelector (write_selector);
sc.freeSelector (read_selector);
} }
/* try to wrap and send the data in src. Handles OVERFLOW. /* try to wrap and send the data in src. Handles OVERFLOW.
...@@ -304,15 +297,7 @@ class SSLStreams { ...@@ -304,15 +297,7 @@ class SSLStreams {
wrap_dst.flip(); wrap_dst.flip();
int l = wrap_dst.remaining(); int l = wrap_dst.remaining();
assert l == r.result.bytesProduced(); assert l == r.result.bytesProduced();
long currtime = time.getTime();
long maxtime = currtime + writeTimeout;
while (l>0) { while (l>0) {
write_selector.select(writeTimeout); // timeout
currtime = time.getTime();
if (currtime > maxtime) {
throw new SocketTimeoutException ("write timed out");
}
write_selector.selectedKeys().clear();
l -= chan.write (wrap_dst); l -= chan.write (wrap_dst);
} }
} }
...@@ -342,20 +327,12 @@ class SSLStreams { ...@@ -342,20 +327,12 @@ class SSLStreams {
needData = true; needData = true;
} }
synchronized (unwrapLock) { synchronized (unwrapLock) {
int x,y; int x;
do { do {
if (needData) { if (needData) {
long currTime = time.getTime();
long maxtime = currTime + readTimeout;
do { do {
if (currTime > maxtime) {
throw new SocketTimeoutException ("read timedout");
}
y = read_selector.select (readTimeout);
currTime = time.getTime();
} while (y != 1);
read_selector.selectedKeys().clear();
x = chan.read (unwrap_src); x = chan.read (unwrap_src);
} while (x == 0);
if (x == -1) { if (x == -1) {
throw new IOException ("connection closed for reading"); throw new IOException ("connection closed for reading");
} }
......
/*
* Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.net.httpserver;
import java.util.*;
import java.nio.*;
import java.net.*;
import java.io.*;
import java.security.*;
import java.nio.channels.*;
/*
* Implements a cache of java.nio.channels.Selector
* where Selectors are allocated on demand and placed
* in a temporary cache for a period of time, so they
* can be reused. If a period of between 2 and 4 minutes
* elapses without being used, then they are closed.
*/
public class SelectorCache {
static SelectorCache cache = null;
private SelectorCache () {
freeSelectors = new LinkedList<SelectorWrapper>();
CacheCleaner c = AccessController.doPrivileged(
new PrivilegedAction<CacheCleaner>() {
public CacheCleaner run() {
CacheCleaner cleaner = new CacheCleaner();
cleaner.setDaemon (true);
return cleaner;
}
});
c.start();
}
/**
* factory method for creating single instance
*/
public static SelectorCache getSelectorCache () {
synchronized (SelectorCache.class) {
if (cache == null) {
cache = new SelectorCache ();
}
}
return cache;
}
private static class SelectorWrapper {
private Selector sel;
private boolean deleteFlag;
private SelectorWrapper (Selector sel) {
this.sel = sel;
this.deleteFlag = false;
}
public Selector getSelector() { return sel;}
public boolean getDeleteFlag () {return deleteFlag;}
public void setDeleteFlag (boolean b) {deleteFlag = b;}
}
/* list of free selectors. Can be re-allocated for a period
* of time, after which if not allocated will be closed
* and removed from the list (by CacheCleaner thread)
*/
LinkedList<SelectorWrapper> freeSelectors;
synchronized Selector getSelector () throws IOException {
SelectorWrapper wrapper = null;
Selector selector;
if (freeSelectors.size() > 0) {
wrapper = freeSelectors.remove();
selector = wrapper.getSelector();
} else {
selector = Selector.open();
}
return selector;
}
synchronized void freeSelector (Selector selector) {
freeSelectors.add (new SelectorWrapper (selector));
}
/* Thread ensures that entries on freeSelector list
* remain there for at least 2 minutes and no longer
* than 4 minutes.
*/
class CacheCleaner extends Thread {
public void run () {
long timeout = ServerConfig.getSelCacheTimeout() * 1000;
while (true) {
try {Thread.sleep (timeout); } catch (Exception e) {}
synchronized (freeSelectors) {
ListIterator<SelectorWrapper> l = freeSelectors.listIterator();
while (l.hasNext()) {
SelectorWrapper w = l.next();
if (w.getDeleteFlag()) {
/* 2nd pass. Close the selector */
try {
w.getSelector().close();
} catch (IOException e) {}
l.remove();
} else {
/* 1st pass. Set the flag */
w.setDeleteFlag (true);
}
}
}
}
}
}
}
...@@ -27,6 +27,8 @@ package sun.net.httpserver; ...@@ -27,6 +27,8 @@ package sun.net.httpserver;
import com.sun.net.httpserver.*; import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*; import com.sun.net.httpserver.spi.*;
import java.util.logging.Logger;
import java.security.PrivilegedAction;
/** /**
* Parameters that users will not likely need to set * Parameters that users will not likely need to set
...@@ -37,23 +39,26 @@ class ServerConfig { ...@@ -37,23 +39,26 @@ class ServerConfig {
static int clockTick; static int clockTick;
static int defaultClockTick = 10000 ; // 10 sec. static final int DEFAULT_CLOCK_TICK = 10000 ; // 10 sec.
/* These values must be a reasonable multiple of clockTick */ /* These values must be a reasonable multiple of clockTick */
static long defaultReadTimeout = 20 ; // 20 sec. static final long DEFAULT_IDLE_INTERVAL = 300 ; // 5 min
static long defaultWriteTimeout = 60 ; // 60 sec. static final int DEFAULT_MAX_IDLE_CONNECTIONS = 200 ;
static long defaultIdleInterval = 300 ; // 5 min
static long defaultSelCacheTimeout = 120 ; // seconds
static int defaultMaxIdleConnections = 200 ;
static long defaultDrainAmount = 64 * 1024; static final long DEFAULT_MAX_REQ_TIME = -1; // default: forever
static final long DEFAULT_MAX_RSP_TIME = -1; // default: forever
static final long DEFAULT_TIMER_MILLIS = 1000;
static final long DEFAULT_DRAIN_AMOUNT = 64 * 1024;
static long readTimeout;
static long writeTimeout;
static long idleInterval; static long idleInterval;
static long selCacheTimeout;
static long drainAmount; // max # of bytes to drain from an inputstream static long drainAmount; // max # of bytes to drain from an inputstream
static int maxIdleConnections; static int maxIdleConnections;
// max time a request or response is allowed to take
static long maxReqTime;
static long maxRspTime;
static long timerMillis;
static boolean debug = false; static boolean debug = false;
static { static {
...@@ -61,49 +66,79 @@ class ServerConfig { ...@@ -61,49 +66,79 @@ class ServerConfig {
idleInterval = ((Long)java.security.AccessController.doPrivileged( idleInterval = ((Long)java.security.AccessController.doPrivileged(
new sun.security.action.GetLongAction( new sun.security.action.GetLongAction(
"sun.net.httpserver.idleInterval", "sun.net.httpserver.idleInterval",
defaultIdleInterval))).longValue() * 1000; DEFAULT_IDLE_INTERVAL))).longValue() * 1000;
clockTick = ((Integer)java.security.AccessController.doPrivileged( clockTick = ((Integer)java.security.AccessController.doPrivileged(
new sun.security.action.GetIntegerAction( new sun.security.action.GetIntegerAction(
"sun.net.httpserver.clockTick", "sun.net.httpserver.clockTick",
defaultClockTick))).intValue(); DEFAULT_CLOCK_TICK))).intValue();
maxIdleConnections = ((Integer)java.security.AccessController.doPrivileged( maxIdleConnections = ((Integer)java.security.AccessController.doPrivileged(
new sun.security.action.GetIntegerAction( new sun.security.action.GetIntegerAction(
"sun.net.httpserver.maxIdleConnections", "sun.net.httpserver.maxIdleConnections",
defaultMaxIdleConnections))).intValue(); DEFAULT_MAX_IDLE_CONNECTIONS))).intValue();
readTimeout = ((Long)java.security.AccessController.doPrivileged( drainAmount = ((Long)java.security.AccessController.doPrivileged(
new sun.security.action.GetLongAction( new sun.security.action.GetLongAction(
"sun.net.httpserver.readTimeout", "sun.net.httpserver.drainAmount",
defaultReadTimeout))).longValue()* 1000; DEFAULT_DRAIN_AMOUNT))).longValue();
selCacheTimeout = ((Long)java.security.AccessController.doPrivileged( maxReqTime = ((Long)java.security.AccessController.doPrivileged(
new sun.security.action.GetLongAction( new sun.security.action.GetLongAction(
"sun.net.httpserver.selCacheTimeout", "sun.net.httpserver.maxReqTime",
defaultSelCacheTimeout))).longValue()* 1000; DEFAULT_MAX_REQ_TIME))).longValue();
writeTimeout = ((Long)java.security.AccessController.doPrivileged( maxRspTime = ((Long)java.security.AccessController.doPrivileged(
new sun.security.action.GetLongAction( new sun.security.action.GetLongAction(
"sun.net.httpserver.writeTimeout", "sun.net.httpserver.maxRspTime",
defaultWriteTimeout))).longValue()* 1000; DEFAULT_MAX_RSP_TIME))).longValue();
drainAmount = ((Long)java.security.AccessController.doPrivileged( timerMillis = ((Long)java.security.AccessController.doPrivileged(
new sun.security.action.GetLongAction( new sun.security.action.GetLongAction(
"sun.net.httpserver.drainAmount", "sun.net.httpserver.timerMillis",
defaultDrainAmount))).longValue(); DEFAULT_TIMER_MILLIS))).longValue();
debug = ((Boolean)java.security.AccessController.doPrivileged( debug = ((Boolean)java.security.AccessController.doPrivileged(
new sun.security.action.GetBooleanAction( new sun.security.action.GetBooleanAction(
"sun.net.httpserver.debug"))).booleanValue(); "sun.net.httpserver.debug"))).booleanValue();
} }
static long getReadTimeout () {
return readTimeout;
}
static long getSelCacheTimeout () { static void checkLegacyProperties (final Logger logger) {
return selCacheTimeout;
// legacy properties that are no longer used
// print a warning to logger if they are set.
java.security.AccessController.doPrivileged(
new PrivilegedAction<Void>() {
public Void run () {
if (System.getProperty("sun.net.httpserver.readTimeout")
!=null)
{
logger.warning ("sun.net.httpserver.readTimeout "+
"property is no longer used. "+
"Use sun.net.httpserver.maxReqTime instead."
);
}
if (System.getProperty("sun.net.httpserver.writeTimeout")
!=null)
{
logger.warning ("sun.net.httpserver.writeTimeout "+
"property is no longer used. Use "+
"sun.net.httpserver.maxRspTime instead."
);
}
if (System.getProperty("sun.net.httpserver.selCacheTimeout")
!=null)
{
logger.warning ("sun.net.httpserver.selCacheTimeout "+
"property is no longer used."
);
}
return null;
}
}
);
} }
static boolean debugEnabled () { static boolean debugEnabled () {
...@@ -122,11 +157,19 @@ class ServerConfig { ...@@ -122,11 +157,19 @@ class ServerConfig {
return maxIdleConnections; return maxIdleConnections;
} }
static long getWriteTimeout () {
return writeTimeout;
}
static long getDrainAmount () { static long getDrainAmount () {
return drainAmount; return drainAmount;
} }
static long getMaxReqTime () {
return maxReqTime;
}
static long getMaxRspTime () {
return maxRspTime;
}
static long getTimerMillis () {
return timerMillis;
}
} }
...@@ -37,6 +37,7 @@ import java.util.logging.Level; ...@@ -37,6 +37,7 @@ import java.util.logging.Level;
import javax.net.ssl.*; import javax.net.ssl.*;
import com.sun.net.httpserver.*; import com.sun.net.httpserver.*;
import com.sun.net.httpserver.spi.*; import com.sun.net.httpserver.spi.*;
import sun.net.httpserver.HttpConnection.State;
/** /**
* Provides implementation for both HTTP and HTTPS * Provides implementation for both HTTP and HTTPS
...@@ -55,6 +56,12 @@ class ServerImpl implements TimeSource { ...@@ -55,6 +56,12 @@ class ServerImpl implements TimeSource {
private SelectionKey listenerKey; private SelectionKey listenerKey;
private Set<HttpConnection> idleConnections; private Set<HttpConnection> idleConnections;
private Set<HttpConnection> allConnections; private Set<HttpConnection> allConnections;
/* following two are used to keep track of the times
* when a connection/request is first received
* and when we start to send the response
*/
private Set<HttpConnection> reqConnections;
private Set<HttpConnection> rspConnections;
private List<Event> events; private List<Event> events;
private Object lolock = new Object(); private Object lolock = new Object();
private volatile boolean finished = false; private volatile boolean finished = false;
...@@ -62,14 +69,19 @@ class ServerImpl implements TimeSource { ...@@ -62,14 +69,19 @@ class ServerImpl implements TimeSource {
private boolean bound = false; private boolean bound = false;
private boolean started = false; private boolean started = false;
private volatile long time; /* current time */ private volatile long time; /* current time */
private volatile long subticks = 0;
private volatile long ticks; /* number of clock ticks since server started */ private volatile long ticks; /* number of clock ticks since server started */
private HttpServer wrapper; private HttpServer wrapper;
final static int CLOCK_TICK = ServerConfig.getClockTick(); final static int CLOCK_TICK = ServerConfig.getClockTick();
final static long IDLE_INTERVAL = ServerConfig.getIdleInterval(); final static long IDLE_INTERVAL = ServerConfig.getIdleInterval();
final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections(); final static int MAX_IDLE_CONNECTIONS = ServerConfig.getMaxIdleConnections();
final static long TIMER_MILLIS = ServerConfig.getTimerMillis ();
final static long MAX_REQ_TIME=getTimeMillis(ServerConfig.getMaxReqTime());
final static long MAX_RSP_TIME=getTimeMillis(ServerConfig.getMaxRspTime());
final static boolean timer1Enabled = MAX_REQ_TIME != -1 || MAX_RSP_TIME != -1;
private Timer timer; private Timer timer, timer1;
private Logger logger; private Logger logger;
ServerImpl ( ServerImpl (
...@@ -79,6 +91,7 @@ class ServerImpl implements TimeSource { ...@@ -79,6 +91,7 @@ class ServerImpl implements TimeSource {
this.protocol = protocol; this.protocol = protocol;
this.wrapper = wrapper; this.wrapper = wrapper;
this.logger = Logger.getLogger ("com.sun.net.httpserver"); this.logger = Logger.getLogger ("com.sun.net.httpserver");
ServerConfig.checkLegacyProperties (logger);
https = protocol.equalsIgnoreCase ("https"); https = protocol.equalsIgnoreCase ("https");
this.address = addr; this.address = addr;
contexts = new ContextList(); contexts = new ContextList();
...@@ -94,9 +107,18 @@ class ServerImpl implements TimeSource { ...@@ -94,9 +107,18 @@ class ServerImpl implements TimeSource {
dispatcher = new Dispatcher(); dispatcher = new Dispatcher();
idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>()); idleConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>()); allConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
reqConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
rspConnections = Collections.synchronizedSet (new HashSet<HttpConnection>());
time = System.currentTimeMillis(); time = System.currentTimeMillis();
timer = new Timer ("server-timer", true); timer = new Timer ("server-timer", true);
timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK); timer.schedule (new ServerTimerTask(), CLOCK_TICK, CLOCK_TICK);
if (timer1Enabled) {
timer1 = new Timer ("server-timer1", true);
timer1.schedule (new ServerTimerTask1(),TIMER_MILLIS,TIMER_MILLIS);
logger.config ("HttpServer timer1 enabled period in ms: "+TIMER_MILLIS);
logger.config ("MAX_REQ_TIME: "+MAX_REQ_TIME);
logger.config ("MAX_RSP_TIME: "+MAX_RSP_TIME);
}
events = new LinkedList<Event>(); events = new LinkedList<Event>();
logger.config ("HttpServer created "+protocol+" "+ addr); logger.config ("HttpServer created "+protocol+" "+ addr);
} }
...@@ -181,6 +203,9 @@ class ServerImpl implements TimeSource { ...@@ -181,6 +203,9 @@ class ServerImpl implements TimeSource {
allConnections.clear(); allConnections.clear();
idleConnections.clear(); idleConnections.clear();
timer.cancel(); timer.cancel();
if (timer1Enabled) {
timer1.cancel();
}
} }
Dispatcher dispatcher; Dispatcher dispatcher;
...@@ -236,13 +261,6 @@ class ServerImpl implements TimeSource { ...@@ -236,13 +261,6 @@ class ServerImpl implements TimeSource {
} }
} }
int resultSize () {
synchronized (lolock) {
return events.size ();
}
}
/* main server listener task */ /* main server listener task */
class Dispatcher implements Runnable { class Dispatcher implements Runnable {
...@@ -257,7 +275,7 @@ class ServerImpl implements TimeSource { ...@@ -257,7 +275,7 @@ class ServerImpl implements TimeSource {
if (terminating && exchanges == 0) { if (terminating && exchanges == 0) {
finished = true; finished = true;
} }
SocketChannel chan = c.getChannel(); responseCompleted (c);
LeftOverInputStream is = t.getOriginalInputStream(); LeftOverInputStream is = t.getOriginalInputStream();
if (!is.isEOF()) { if (!is.isEOF()) {
t.close = true; t.close = true;
...@@ -268,17 +286,10 @@ class ServerImpl implements TimeSource { ...@@ -268,17 +286,10 @@ class ServerImpl implements TimeSource {
} else { } else {
if (is.isDataBuffered()) { if (is.isDataBuffered()) {
/* don't re-enable the interestops, just handle it */ /* don't re-enable the interestops, just handle it */
requestStarted (c);
handle (c.getChannel(), c); handle (c.getChannel(), c);
} else { } else {
/* re-enable interestops */ connsToRegister.add (c);
SelectionKey key = c.getSelectionKey();
if (key.isValid()) {
key.interestOps (
key.interestOps()|SelectionKey.OP_READ
);
}
c.time = getTime() + IDLE_INTERVAL;
idleConnections.add (c);
} }
} }
} }
...@@ -290,21 +301,50 @@ class ServerImpl implements TimeSource { ...@@ -290,21 +301,50 @@ class ServerImpl implements TimeSource {
} }
} }
final LinkedList<HttpConnection> connsToRegister =
new LinkedList<HttpConnection>();
void reRegister (HttpConnection c) {
/* re-register with selector */
try {
SocketChannel chan = c.getChannel();
chan.configureBlocking (false);
SelectionKey key = chan.register (selector, SelectionKey.OP_READ);
key.attach (c);
c.selectionKey = key;
c.time = getTime() + IDLE_INTERVAL;
idleConnections.add (c);
} catch (IOException e) {
dprint(e);
logger.log(Level.FINER, "Dispatcher(8)", e);
c.close();
}
}
public void run() { public void run() {
while (!finished) { while (!finished) {
try { try {
ListIterator<HttpConnection> li =
connsToRegister.listIterator();
for (HttpConnection c : connsToRegister) {
reRegister(c);
}
connsToRegister.clear();
/* process the events list first */ List<Event> list = null;
selector.select(1000);
while (resultSize() > 0) {
Event r;
synchronized (lolock) { synchronized (lolock) {
r = events.remove(0); if (events.size() > 0) {
handleEvent (r); list = events;
events = new LinkedList<Event>();
} }
} }
selector.select(1000); if (list != null) {
for (Event r: list) {
handleEvent (r);
}
}
/* process the selected list now */ /* process the selected list now */
...@@ -327,6 +367,7 @@ class ServerImpl implements TimeSource { ...@@ -327,6 +367,7 @@ class ServerImpl implements TimeSource {
c.selectionKey = newkey; c.selectionKey = newkey;
c.setChannel (chan); c.setChannel (chan);
newkey.attach (c); newkey.attach (c);
requestStarted (c);
allConnections.add (c); allConnections.add (c);
} else { } else {
try { try {
...@@ -334,27 +375,44 @@ class ServerImpl implements TimeSource { ...@@ -334,27 +375,44 @@ class ServerImpl implements TimeSource {
boolean closed; boolean closed;
SocketChannel chan = (SocketChannel)key.channel(); SocketChannel chan = (SocketChannel)key.channel();
HttpConnection conn = (HttpConnection)key.attachment(); HttpConnection conn = (HttpConnection)key.attachment();
// interestOps will be restored at end of read
key.interestOps (0); key.cancel();
chan.configureBlocking (true);
if (idleConnections.remove(conn)) {
// was an idle connection so add it
// to reqConnections set.
requestStarted (conn);
}
handle (chan, conn); handle (chan, conn);
} else { } else {
assert false; assert false;
} }
} catch (CancelledKeyException e) {
handleException(key, null);
} catch (IOException e) { } catch (IOException e) {
HttpConnection conn = (HttpConnection)key.attachment(); handleException(key, e);
logger.log (
Level.FINER, "Dispatcher (2)", e
);
conn.close();
} }
} }
} }
// call the selector just to process the cancelled keys
selector.selectNow();
} catch (IOException e) {
logger.log (Level.FINER, "Dispatcher (4)", e);
} catch (Exception e) { } catch (Exception e) {
logger.log (Level.FINER, "Dispatcher (3)", e); e.printStackTrace();
logger.log (Level.FINER, "Dispatcher (7)", e);
} }
} }
} }
private void handleException (SelectionKey key, Exception e) {
HttpConnection conn = (HttpConnection)key.attachment();
if (e != null) {
logger.log (Level.FINER, "Dispatcher (2)", e);
}
closeConnection(conn);
}
public void handle (SocketChannel chan, HttpConnection conn) public void handle (SocketChannel chan, HttpConnection conn)
throws IOException throws IOException
{ {
...@@ -363,10 +421,10 @@ class ServerImpl implements TimeSource { ...@@ -363,10 +421,10 @@ class ServerImpl implements TimeSource {
executor.execute (t); executor.execute (t);
} catch (HttpError e1) { } catch (HttpError e1) {
logger.log (Level.FINER, "Dispatcher (4)", e1); logger.log (Level.FINER, "Dispatcher (4)", e1);
conn.close(); closeConnection(conn);
} catch (IOException e) { } catch (IOException e) {
logger.log (Level.FINER, "Dispatcher (5)", e); logger.log (Level.FINER, "Dispatcher (5)", e);
conn.close(); closeConnection(conn);
} }
} }
} }
...@@ -390,6 +448,25 @@ class ServerImpl implements TimeSource { ...@@ -390,6 +448,25 @@ class ServerImpl implements TimeSource {
return logger; return logger;
} }
private void closeConnection(HttpConnection conn) {
conn.close();
allConnections.remove(conn);
switch (conn.getState()) {
case REQUEST:
reqConnections.remove(conn);
break;
case RESPONSE:
rspConnections.remove(conn);
break;
case IDLE:
idleConnections.remove(conn);
break;
}
assert !reqConnections.remove(conn);
assert !rspConnections.remove(conn);
assert !idleConnections.remove(conn);
}
/* per exchange task */ /* per exchange task */
class Exchange implements Runnable { class Exchange implements Runnable {
...@@ -450,8 +527,7 @@ class ServerImpl implements TimeSource { ...@@ -450,8 +527,7 @@ class ServerImpl implements TimeSource {
requestLine = req.requestLine(); requestLine = req.requestLine();
if (requestLine == null) { if (requestLine == null) {
/* connection closed */ /* connection closed */
connection.close(); closeConnection(connection);
allConnections.remove(connection);
return; return;
} }
int space = requestLine.indexOf (' '); int space = requestLine.indexOf (' ');
...@@ -482,6 +558,9 @@ class ServerImpl implements TimeSource { ...@@ -482,6 +558,9 @@ class ServerImpl implements TimeSource {
if (s != null) { if (s != null) {
clen = Long.parseLong(s); clen = Long.parseLong(s);
} }
if (clen == 0) {
requestCompleted (connection);
}
} }
ctx = contexts.findContext (protocol, uri.getPath()); ctx = contexts.findContext (protocol, uri.getPath());
if (ctx == null) { if (ctx == null) {
...@@ -560,7 +639,7 @@ class ServerImpl implements TimeSource { ...@@ -560,7 +639,7 @@ class ServerImpl implements TimeSource {
} catch (IOException e1) { } catch (IOException e1) {
logger.log (Level.FINER, "ServerImpl.Exchange (1)", e1); logger.log (Level.FINER, "ServerImpl.Exchange (1)", e1);
connection.close(); closeConnection(connection);
} catch (NumberFormatException e3) { } catch (NumberFormatException e3) {
reject (Code.HTTP_BAD_REQUEST, reject (Code.HTTP_BAD_REQUEST,
requestLine, "NumberFormatException thrown"); requestLine, "NumberFormatException thrown");
...@@ -569,7 +648,7 @@ class ServerImpl implements TimeSource { ...@@ -569,7 +648,7 @@ class ServerImpl implements TimeSource {
requestLine, "URISyntaxException thrown"); requestLine, "URISyntaxException thrown");
} catch (Exception e4) { } catch (Exception e4) {
logger.log (Level.FINER, "ServerImpl.Exchange (2)", e4); logger.log (Level.FINER, "ServerImpl.Exchange (2)", e4);
connection.close(); closeConnection(connection);
} }
} }
...@@ -591,47 +670,60 @@ class ServerImpl implements TimeSource { ...@@ -591,47 +670,60 @@ class ServerImpl implements TimeSource {
rejected = true; rejected = true;
logReply (code, requestStr, message); logReply (code, requestStr, message);
sendReply ( sendReply (
code, true, "<h1>"+code+Code.msg(code)+"</h1>"+message code, false, "<h1>"+code+Code.msg(code)+"</h1>"+message
); );
/* connection is already closed by sendReply, now remove it */ closeConnection(connection);
allConnections.remove(connection);
} }
void sendReply ( void sendReply (
int code, boolean closeNow, String text) int code, boolean closeNow, String text)
{ {
try { try {
String s = "HTTP/1.1 " + code + Code.msg(code) + "\r\n"; StringBuilder builder = new StringBuilder (512);
builder.append ("HTTP/1.1 ")
.append (code).append (Code.msg(code)).append ("\r\n");
if (text != null && text.length() != 0) { if (text != null && text.length() != 0) {
s = s + "Content-Length: "+text.length()+"\r\n"; builder.append ("Content-Length: ")
s = s + "Content-Type: text/html\r\n"; .append (text.length()).append ("\r\n")
.append ("Content-Type: text/html\r\n");
} else { } else {
s = s + "Content-Length: 0\r\n"; builder.append ("Content-Length: 0\r\n");
text = ""; text = "";
} }
if (closeNow) { if (closeNow) {
s = s + "Connection: close\r\n"; builder.append ("Connection: close\r\n");
} }
s = s + "\r\n" + text; builder.append ("\r\n").append (text);
String s = builder.toString();
byte[] b = s.getBytes("ISO8859_1"); byte[] b = s.getBytes("ISO8859_1");
rawout.write (b); rawout.write (b);
rawout.flush(); rawout.flush();
if (closeNow) { if (closeNow) {
connection.close(); closeConnection(connection);
} }
} catch (IOException e) { } catch (IOException e) {
logger.log (Level.FINER, "ServerImpl.sendReply", e); logger.log (Level.FINER, "ServerImpl.sendReply", e);
connection.close(); closeConnection(connection);
} }
} }
} }
void logReply (int code, String requestStr, String text) { void logReply (int code, String requestStr, String text) {
if (!logger.isLoggable(Level.FINE)) {
return;
}
if (text == null) { if (text == null) {
text = ""; text = "";
} }
String message = requestStr + " [" + code + " " + String r;
if (requestStr.length() > 80) {
r = requestStr.substring (0, 80) + "<TRUNCATED>";
} else {
r = requestStr;
}
String message = r + " [" + code + " " +
Code.msg(code) + "] ("+text+")"; Code.msg(code) + "] ("+text+")";
logger.fine (message); logger.fine (message);
} }
...@@ -667,6 +759,34 @@ class ServerImpl implements TimeSource { ...@@ -667,6 +759,34 @@ class ServerImpl implements TimeSource {
return wrapper; return wrapper;
} }
void requestStarted (HttpConnection c) {
c.creationTime = getTime();
c.setState (State.REQUEST);
reqConnections.add (c);
}
// called after a request has been completely read
// by the server. This stops the timer which would
// close the connection if the request doesn't arrive
// quickly enough. It then starts the timer
// that ensures the client reads the response in a timely
// fashion.
void requestCompleted (HttpConnection c) {
assert c.getState() == State.REQUEST;
reqConnections.remove (c);
c.rspStartedTime = getTime();
rspConnections.add (c);
c.setState (State.RESPONSE);
}
// called after response has been sent
void responseCompleted (HttpConnection c) {
assert c.getState() == State.RESPONSE;
rspConnections.remove (c);
c.setState (State.IDLE);
}
/** /**
* TimerTask run every CLOCK_TICK ms * TimerTask run every CLOCK_TICK ms
*/ */
...@@ -689,4 +809,62 @@ class ServerImpl implements TimeSource { ...@@ -689,4 +809,62 @@ class ServerImpl implements TimeSource {
} }
} }
} }
class ServerTimerTask1 extends TimerTask {
// runs every TIMER_MILLIS
public void run () {
LinkedList<HttpConnection> toClose = new LinkedList<HttpConnection>();
time = System.currentTimeMillis();
synchronized (reqConnections) {
if (MAX_REQ_TIME != -1) {
for (HttpConnection c : reqConnections) {
if (c.creationTime + TIMER_MILLIS + MAX_REQ_TIME <= time) {
toClose.add (c);
}
}
for (HttpConnection c : toClose) {
logger.log (Level.FINE, "closing: no request: " + c);
reqConnections.remove (c);
allConnections.remove (c);
c.close();
}
}
}
toClose = new LinkedList<HttpConnection>();
synchronized (rspConnections) {
if (MAX_RSP_TIME != -1) {
for (HttpConnection c : rspConnections) {
if (c.rspStartedTime + TIMER_MILLIS +MAX_RSP_TIME <= time) {
toClose.add (c);
}
}
for (HttpConnection c : toClose) {
logger.log (Level.FINE, "closing: no response: " + c);
rspConnections.remove (c);
allConnections.remove (c);
c.close();
}
}
}
}
}
void logStackTrace (String s) {
logger.finest (s);
StringBuilder b = new StringBuilder ();
StackTraceElement[] e = Thread.currentThread().getStackTrace();
for (int i=0; i<e.length; i++) {
b.append (e[i].toString()).append("\n");
}
logger.finest (b.toString());
}
static long getTimeMillis(long secs) {
if (secs == -1) {
return -1;
} else {
return secs * 1000;
}
}
} }
...@@ -22,8 +22,20 @@ ...@@ -22,8 +22,20 @@
*/ */
import com.sun.net.httpserver.*; import com.sun.net.httpserver.*;
import java.util.logging.*;
public class Test { public class Test {
static Logger logger;
static void enableLogging() {
logger = Logger.getLogger("com.sun.net.httpserver");
Handler h = new ConsoleHandler();
h.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
logger.addHandler(h);
}
static void delay () { static void delay () {
try { try {
Thread.sleep (1000); Thread.sleep (1000);
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
* @test * @test
* @bug 6270015 * @bug 6270015
* @run main/othervm Test1 * @run main/othervm Test1
* @run main/othervm -Dsun.net.httpserver.maxReqTime=10 Test1
* @summary Light weight HTTP server * @summary Light weight HTTP server
*/ */
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
import com.sun.net.httpserver.*; import com.sun.net.httpserver.*;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.logging.*;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
...@@ -45,12 +46,19 @@ public class Test13 extends Test { ...@@ -45,12 +46,19 @@ public class Test13 extends Test {
static SSLContext ctx; static SSLContext ctx;
final static int NUM = 32; // was 32
static boolean fail = false; static boolean fail = false;
public static void main (String[] args) throws Exception { public static void main (String[] args) throws Exception {
HttpServer s1 = null; HttpServer s1 = null;
HttpsServer s2 = null; HttpsServer s2 = null;
ExecutorService executor=null; ExecutorService executor=null;
Logger l = Logger.getLogger ("com.sun.net.httpserver");
Handler ha = new ConsoleHandler();
ha.setLevel(Level.ALL);
l.setLevel(Level.ALL);
l.addHandler(ha);
try { try {
String root = System.getProperty ("test.src")+ "/docs"; String root = System.getProperty ("test.src")+ "/docs";
System.out.print ("Test13: "); System.out.print ("Test13: ");
...@@ -70,10 +78,10 @@ public class Test13 extends Test { ...@@ -70,10 +78,10 @@ public class Test13 extends Test {
int port = s1.getAddress().getPort(); int port = s1.getAddress().getPort();
int httpsport = s2.getAddress().getPort(); int httpsport = s2.getAddress().getPort();
Runner r[] = new Runner[64]; Runner r[] = new Runner[NUM*2];
for (int i=0; i<32; i++) { for (int i=0; i<NUM; i++) {
r[i] = new Runner (true, "http", root+"/test1", port, "smallfile.txt", 23); r[i] = new Runner (true, "http", root+"/test1", port, "smallfile.txt", 23);
r[i+32] = new Runner (true, "https", root+"/test1", port, "smallfile.txt", 23); r[i+NUM] = new Runner (true, "https", root+"/test1", httpsport, "smallfile.txt", 23);
} }
start (r); start (r);
join (r); join (r);
...@@ -91,6 +99,7 @@ public class Test13 extends Test { ...@@ -91,6 +99,7 @@ public class Test13 extends Test {
static void start (Runner[] x) { static void start (Runner[] x) {
for (int i=0; i<x.length; i++) { for (int i=0; i<x.length; i++) {
if (x[i] != null)
x[i].start(); x[i].start();
} }
} }
...@@ -98,6 +107,7 @@ public class Test13 extends Test { ...@@ -98,6 +107,7 @@ public class Test13 extends Test {
static void join (Runner[] x) { static void join (Runner[] x) {
for (int i=0; i<x.length; i++) { for (int i=0; i<x.length; i++) {
try { try {
if (x[i] != null)
x[i].join(); x[i].join();
} catch (InterruptedException e) {} } catch (InterruptedException e) {}
} }
......
/*
* Copyright (c) 2005, 2006, 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.
*/
/**
* @test
* @bug 6725892
* @run main/othervm -Dsun.net.httpserver.maxReqTime=2 Test
* @summary
*/
import com.sun.net.httpserver.*;
import java.util.concurrent.*;
import java.util.logging.*;
import java.io.*;
import java.net.*;
import javax.net.ssl.*;
public class Test {
static HttpServer s1;
static int port;
static URL url;
static final String RESPONSE_BODY = "response";
static boolean failed = false;
static class Handler implements HttpHandler {
public void handle (HttpExchange t)
throws IOException
{
InputStream is = t.getRequestBody();
InetSocketAddress rem = t.getRemoteAddress();
System.out.println ("Request from: " + rem);
while (is.read () != -1) ;
is.close();
String requrl = t.getRequestURI().toString();
OutputStream os = t.getResponseBody();
t.sendResponseHeaders (200, RESPONSE_BODY.length());
os.write (RESPONSE_BODY.getBytes());
t.close();
}
}
public static void main (String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
//Logger log = Logger.getLogger ("com.sun.net.httpserver");
//log.setLevel(Level.ALL);
//ConsoleHandler hg = new ConsoleHandler();
//hg.setLevel (Level.ALL);
//log.addHandler(hg);
sun.net.httpserver.HttpServerImpl x = null;
try {
InetSocketAddress addr = new InetSocketAddress (0);
s1 = HttpServer.create (addr, 0);
HttpHandler h = new Handler ();
HttpContext c1 = s1.createContext ("/", h);
s1.setExecutor(exec);
s1.start();
port = s1.getAddress().getPort();
System.out.println ("Server on port " + port);
url = new URL ("http://rialto.ireland:"+port+"/foo");
test1();
test2();
test3();
x = (sun.net.httpserver.HttpServerImpl)s1;
Thread.sleep (2000);
} catch (Exception e) {
e.printStackTrace();
System.out.println ("FAIL");
throw new RuntimeException ();
} finally {
s1.stop(0);
System.out.println ("After Shutdown");
exec.shutdown();
}
}
// open TCP connection without sending anything. Check server closes it.
static void test1() throws IOException {
failed = false;
Socket s = new Socket ("127.0.0.1", port);
InputStream is = s.getInputStream();
// server should close connection after 2 seconds. We wait up to 10
s.setSoTimeout (10000);
try {
is.read();
} catch (SocketTimeoutException e) {
failed = true;
}
s.close();
if (failed) {
System.out.println ("test1: FAIL");
throw new RuntimeException ();
} else {
System.out.println ("test1: OK");
}
}
// send request and don't read response. Check server closes connection
static void test2() throws IOException {
HttpURLConnection urlc = (HttpURLConnection) url.openConnection();
urlc.setReadTimeout (20 * 1000);
InputStream is = urlc.getInputStream();
// we won't read response and check if it times out
// on server. If it timesout at client then there is a problem
try {
Thread.sleep (10 * 1000);
while (is.read() != -1) ;
} catch (InterruptedException e) {
System.out.println (e);
System.out.println ("test2: FAIL");
throw new RuntimeException ("unexpected error");
} catch (SocketTimeoutException e1) {
System.out.println (e1);
System.out.println ("test2: FAIL");
throw new RuntimeException ("client timedout");
} finally {
is.close();
}
System.out.println ("test2: OK");
}
// same as test2, but repeated with multiple connections
// including a number of valid request/responses
// Worker: a thread opens a connection to the server in one of three modes.
// NORMAL - sends a request, waits for response, and checks valid response
// REQUEST - sends a partial request, and blocks, to see if
// server closes the connection.
// RESPONSE - sends a request, partially reads response and blocks,
// to see if server closes the connection.
static class Worker extends Thread {
CountDownLatch latch;
Mode mode;
enum Mode {
REQUEST, // block during sending of request
RESPONSE, // block during reading of response
NORMAL // don't block
};
Worker (CountDownLatch latch, Mode mode) {
this.latch = latch;
this.mode = mode;
}
void fail(String msg) {
System.out.println (msg);
failed = true;
}
public void run () {
HttpURLConnection urlc;
InputStream is = null;
try {
urlc = (HttpURLConnection) url.openConnection();
urlc.setReadTimeout (20 * 1000);
urlc.setDoOutput(true);
} catch (IOException e) {
fail("Worker: failed to connect to server");
latch.countDown();
return;
}
try {
OutputStream os = urlc.getOutputStream();
os.write ("foo".getBytes());
if (mode == Mode.REQUEST) {
Thread.sleep (3000);
}
os.close();
is = urlc.getInputStream();
if (mode == Mode.RESPONSE) {
Thread.sleep (3000);
}
if (!checkResponse (is, RESPONSE_BODY)) {
fail ("Worker: response");
}
is.close();
return;
} catch (InterruptedException e0) {
fail("Worker: timedout");
} catch (SocketTimeoutException e1) {
fail("Worker: timedout");
} catch (IOException e2) {
switch (mode) {
case NORMAL:
fail ("Worker: " + e2.getMessage());
break;
case RESPONSE:
if (is == null) {
fail ("Worker: " + e2.getMessage());
break;
}
// default: is ok
}
} finally {
latch.countDown();
}
}
}
static final int NUM = 20;
static void test3() throws Exception {
failed = false;
CountDownLatch l = new CountDownLatch (NUM*3);
Worker[] workers = new Worker[NUM*3];
for (int i=0; i<NUM; i++) {
workers[i*3] = new Worker (l, Worker.Mode.NORMAL);
workers[i*3+1] = new Worker (l, Worker.Mode.REQUEST);
workers[i*3+2] = new Worker (l, Worker.Mode.RESPONSE);
workers[i*3].start();
workers[i*3+1].start();
workers[i*3+2].start();
}
l.await();
for (int i=0; i<NUM*3; i++) {
workers[i].join();
}
if (failed) {
throw new RuntimeException ("test3: failed");
}
System.out.println ("test3: OK");
}
static boolean checkResponse (InputStream is, String resp) {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] buf = new byte [64];
int c;
while ((c=is.read(buf)) != -1) {
bos.write (buf, 0, c);
}
bos.close();
if (!bos.toString().equals(resp)) {
System.out.println ("Wrong response: " + bos.toString());
return false;
}
} catch (IOException e) {
System.out.println (e);
return false;
}
return true;
}
}
...@@ -83,7 +83,7 @@ public class B6401598 { ...@@ -83,7 +83,7 @@ public class B6401598 {
server = HttpServer.create(new InetSocketAddress(0), 400); server = HttpServer.create(new InetSocketAddress(0), 400);
server.createContext("/server/", new MyHandler()); server.createContext("/server/", new MyHandler());
exec = Executors.newFixedThreadPool(3); exec = Executors.newFixedThreadPool(3);
server.setExecutor(null); server.setExecutor(exec);
port = server.getAddress().getPort(); port = server.getAddress().getPort();
server.start(); server.start();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册