jetty-9 passing timeout tests

This commit is contained in:
Greg Wilkins 2012-05-23 10:19:23 +02:00
parent b8517abb14
commit 294150f485
7 changed files with 161 additions and 24 deletions

View File

@ -18,6 +18,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
@ -870,7 +871,16 @@ public class HttpParser
return false;
case CLOSED:
BufferUtil.clear(buffer);
int count=0;
while (BufferUtil.hasContent(buffer))
{
byte b=buffer.get();
if (!Character.isWhitespace((char)b) || count++>4)
{
BufferUtil.clear(buffer);
throw new IOException("Illegal characters");
}
}
return false;
}
@ -1049,6 +1059,12 @@ public class HttpParser
}
catch(Exception e)
{
if (isClosed())
{
LOG.debug(e);
throw new IllegalStateException(e);
}
LOG.warn(e);
_handler.badMessage(e.toString());
return true;

View File

@ -393,7 +393,13 @@ public abstract class HttpChannel
if (!_response.isCommitted() && !_request.isHandled())
_response.sendError(404);
// Complete reading the request
_in.consumeAll();
// Complete generating the response
_response.complete();
}
catch(IOException e)
{
@ -497,8 +503,9 @@ public abstract class HttpChannel
@Override
public String toString()
{
return String.format("%s{r=%d,a=%s}",
super.toString(),
return String.format("%s@%x{r=%d,a=%s}",
getClass().getSimpleName(),
hashCode(),
_requests,
_state.getState());
}

View File

@ -35,7 +35,7 @@ import org.eclipse.jetty.util.log.Logger;
*/
public class HttpConnection extends AbstractAsyncConnection
{
private static final Logger LOG = Log.getLogger(HttpConnection.class);
public static final Logger LOG = Log.getLogger(HttpConnection.class);
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<HttpConnection>();
@ -273,7 +273,10 @@ public class HttpConnection extends AbstractAsyncConnection
}
catch(Exception e)
{
LOG.warn(e);
if (_parser.isClosed())
LOG.debug(e);
else
LOG.warn(e);
getEndPoint().close();
}
finally
@ -282,6 +285,14 @@ public class HttpConnection extends AbstractAsyncConnection
}
}
/* ------------------------------------------------------------ */
@Override
public void onReadFail(Throwable cause)
{
// TODO Auto-generated method stub
super.onReadFail(cause);
}
/* ------------------------------------------------------------ */
@Override
public void onClose()
@ -383,15 +394,16 @@ public class HttpConnection extends AbstractAsyncConnection
@Override
protected void completed()
{
LOG.debug("{} completed");
// This is called by HttpChannel#process when it knows that it's handling of the request/response cycle
// is complete. This may be in the original thread dispatched to the connection that has called process from
// the connection#onReadable method, or it may be from a thread dispatched to call process as the result
// of a resumed suspended request.
// At this point the HttpChannel will have completed the generation of any response (although it might remain to
// be asynchronously flushed TBD), but it may not have consumed the entire
// parser should be either complete or can be completed with single call
if (!_parser.isComplete())
{
// TODO make this much more efficient
_httpInput.consumeAll();
}
LOG.debug("{} completed");
// Reset everything for the next cycle.
HttpConnection.this.reset();
// if the onReadable method is not executing
@ -719,15 +731,16 @@ public class HttpConnection extends AbstractAsyncConnection
}
}
protected void consumeAll()
@Override
public void consumeAll()
{
while (true)
{
synchronized (_inputQ)
synchronized (_inputQ.lock())
{
_inputQ.clear();
}
if (_parser.isComplete())
if (_parser.isComplete() || _parser.isClosed())
return;
try
{

View File

@ -21,6 +21,8 @@ import javax.servlet.ServletInputStream;
import org.eclipse.jetty.util.ArrayQueue;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -37,6 +39,8 @@ import org.eclipse.jetty.util.BufferUtil;
*/
public abstract class HttpInput extends ServletInputStream
{
private static final Logger LOG = Log.getLogger(HttpInput.class);
protected final byte[] _oneByte=new byte[1];
protected final ArrayQueue<ByteBuffer> _inputQ=new ArrayQueue<>();
private ByteBuffer _content;
@ -50,7 +54,7 @@ public abstract class HttpInput extends ServletInputStream
/* ------------------------------------------------------------ */
public void recycle()
{
synchronized (_inputQ)
synchronized (_inputQ.lock())
{
_inputEOF=false;
@ -179,4 +183,25 @@ public abstract class HttpInput extends ServletInputStream
_inputEOF=true;
}
}
public void consumeAll()
{
while (true)
{
synchronized (_inputQ.lock())
{
_inputQ.clear();
}
if (_inputEOF)
return;
try
{
blockForContent();
}
catch(IOException e)
{
LOG.warn(e);
}
}
}
}

View File

@ -34,12 +34,13 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.Log;
import org.junit.Assert;
import org.junit.Test;
public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
{
protected static final int MAX_IDLE_TIME=250;
protected static final int MAX_IDLE_TIME=500;
private int sleepTime = MAX_IDLE_TIME + MAX_IDLE_TIME/5;
private int minimumTestRuntime = MAX_IDLE_TIME-MAX_IDLE_TIME/5;
private int maximumTestRuntime = MAX_IDLE_TIME*10;
@ -114,7 +115,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
@Test
public void testMaxIdleWithRequest10NoClientClose() throws Exception
{
{
final Exchanger<EndPoint> endpoint = new Exchanger<EndPoint>();
configureServer(new HelloWorldHandler()
{
@ -150,8 +151,8 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
// Get the server side endpoint
EndPoint endp = endpoint.exchange(null,10,TimeUnit.SECONDS);
if (endp instanceof SslConnection.ApplicationEndPoint)
endp=((SslConnection.ApplicationEndPoint)endp).getEndpoint();
endp=((SslConnection.ApplicationEndPoint)endp).getAsyncConnection().getEndPoint();
// read the response
String result=IO.toString(is);
Assert.assertThat("OK",result,containsString("200 OK"));
@ -160,7 +161,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
assertEquals(-1, is.read());
// wait for idle timeout
TimeUnit.MILLISECONDS.sleep(MAX_IDLE_TIME+MAX_IDLE_TIME/2);
TimeUnit.MILLISECONDS.sleep(3*MAX_IDLE_TIME);
// further writes will get broken pipe or similar
@ -185,6 +186,75 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
Assert.assertFalse(endp.isOpen());
}
@Test
public void testMaxIdleWithRequest10ClientIgnoresClose() throws Exception
{
final Exchanger<EndPoint> endpoint = new Exchanger<EndPoint>();
configureServer(new HelloWorldHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException,
ServletException
{
try
{
endpoint.exchange(baseRequest.getHttpChannel().getConnection().getEndPoint());
}
catch(Exception e)
{}
super.handle(target,baseRequest,request,response);
}
});
Socket client=newSocket(HOST,_connector.getLocalPort());
client.setSoTimeout(10000);
assertFalse(client.isClosed());
OutputStream os=client.getOutputStream();
InputStream is=client.getInputStream();
os.write((
"GET / HTTP/1.0\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"connection: close\r\n"+
"\r\n").getBytes("utf-8"));
os.flush();
// Get the server side endpoint
EndPoint endp = endpoint.exchange(null,10,TimeUnit.SECONDS);
if (endp instanceof SslConnection.ApplicationEndPoint)
endp=((SslConnection.ApplicationEndPoint)endp).getAsyncConnection().getEndPoint();
// read the response
String result=IO.toString(is);
Assert.assertThat("OK",result,containsString("200 OK"));
// check client reads EOF
assertEquals(-1, is.read());
// further writes will get broken pipe or similar
try
{
for (int i=0;i<1000;i++)
{
os.write((
"GET / HTTP/1.0\r\n"+
"host: "+HOST+":"+_connector.getLocalPort()+"\r\n"+
"connection: keep-alive\r\n"+
"\r\n").getBytes("utf-8"));
os.flush();
}
Assert.fail("half close should have timed out");
}
catch(SocketException e)
{
// expected
}
// check the server side is closed
Assert.assertFalse(endp.isOpen());
}
@Test
public void testMaxIdleWithRequest11NoClientClose() throws Exception
{
@ -234,7 +304,7 @@ public abstract class ConnectorTimeoutTest extends HttpServerTestFixture
// check client reads EOF
assertEquals(-1, is.read());
TimeUnit.MILLISECONDS.sleep(MAX_IDLE_TIME+MAX_IDLE_TIME/2);
TimeUnit.MILLISECONDS.sleep(3*MAX_IDLE_TIME);
// further writes will get broken pipe or similar

View File

@ -1014,6 +1014,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
{
public EndPoint _endp;
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
_endp=baseRequest.getHttpChannel().getConnection().getEndPoint();
@ -1032,6 +1033,7 @@ public abstract class HttpServerTestBase extends HttpServerTestFixture
{
public Exchanger<Object> _ex = new Exchanger<Object>();
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);

View File

@ -332,7 +332,7 @@ public class StdErrLog extends AbstractLogger
for (Logger log : Log.getLoggers().values())
{
if (log instanceof StdErrLog)
if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
((StdErrLog)log).setLevel(LEVEL_DEBUG);
}
}
@ -342,7 +342,7 @@ public class StdErrLog extends AbstractLogger
for (Logger log : Log.getLoggers().values())
{
if (log instanceof StdErrLog)
if (log.getName().startsWith(getName()) && log instanceof StdErrLog)
((StdErrLog)log).setLevel(((StdErrLog)log)._configuredLevel);
}
}
@ -573,6 +573,10 @@ public class StdErrLog extends AbstractLogger
// Let Level come from configured Properties instead - sel.setLevel(_level);
logger.setSource(_source);
logger._stderr = this._stderr;
// Force the child to have any programmatic configuration
if (_level!=_configuredLevel)
logger._level=_level;
return logger;
}