jetty-9 simplified error handling

This commit is contained in:
Greg Wilkins 2012-08-23 17:32:10 +10:00
parent cd719bf979
commit d59a47d376
14 changed files with 315 additions and 346 deletions

View File

@ -168,7 +168,7 @@ public class HttpGenerator
}
/* ------------------------------------------------------------ */
public Result generateRequest(RequestInfo info, ByteBuffer headerOrChunk, ByteBuffer content, boolean last)
public Result generateRequest(RequestInfo info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last)
{
switch(_state)
{
@ -178,7 +178,7 @@ public class HttpGenerator
return Result.NEED_INFO;
// Do we need a request header
if (headerOrChunk==null || headerOrChunk.capacity()<=CHUNK_SIZE)
if (header==null)
return Result.NEED_HEADER;
// If we have not been told our persistence, set the default
@ -186,7 +186,6 @@ public class HttpGenerator
_persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
// prepare the header
ByteBuffer header = headerOrChunk;
int pos=BufferUtil.flipToFill(header);
try
{
@ -232,9 +231,8 @@ public class HttpGenerator
if (isChunking())
{
// Do we need a chunk buffer?
if (headerOrChunk==null || headerOrChunk.capacity()>CHUNK_SIZE)
if (chunk==null)
return Result.NEED_CHUNK;
ByteBuffer chunk = headerOrChunk;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,len);
BufferUtil.flipToFlush(chunk,0);
@ -259,9 +257,8 @@ public class HttpGenerator
if (isChunking())
{
// Do we need a chunk buffer?
if (headerOrChunk==null || headerOrChunk.capacity()>CHUNK_SIZE)
if (chunk==null)
return Result.NEED_CHUNK;
ByteBuffer chunk=headerOrChunk;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,0);
BufferUtil.flipToFlush(chunk,0);
@ -284,7 +281,7 @@ public class HttpGenerator
}
/* ------------------------------------------------------------ */
public Result generateResponse(ResponseInfo info, ByteBuffer headerOrChunk, ByteBuffer content, boolean last)
public Result generateResponse(ResponseInfo info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last)
{
switch(_state)
{
@ -306,7 +303,7 @@ public class HttpGenerator
}
// Do we need a response header
if (headerOrChunk==null || headerOrChunk.capacity()<=CHUNK_SIZE)
if (header==null)
return Result.NEED_HEADER;
// If we have not been told our persistence, set the default
@ -314,7 +311,6 @@ public class HttpGenerator
_persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
// prepare the header
ByteBuffer header = headerOrChunk;
int pos=BufferUtil.flipToFill(header);
try
{
@ -383,18 +379,15 @@ public class HttpGenerator
// handle the content.
if (len>0)
{
// Do we need a chunk buffer?
if (isChunking() && BufferUtil.space(headerOrChunk) < CHUNK_SIZE)
return Result.NEED_CHUNK;
ByteBuffer chunk = headerOrChunk;
_contentPrepared+=len;
if (isChunking())
{
if (chunk==null)
return Result.NEED_CHUNK;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,len);
BufferUtil.flipToFlush(chunk,0);
}
_contentPrepared+=len;
}
if (last)
@ -420,11 +413,10 @@ public class HttpGenerator
if (isChunking())
{
// Do we need a chunk buffer?
if (BufferUtil.space(headerOrChunk) < CHUNK_SIZE)
if (chunk==null)
return Result.NEED_CHUNK;
// Write the last chunk
ByteBuffer chunk=headerOrChunk;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,0);
BufferUtil.flipToFlush(chunk,0);

View File

@ -1114,7 +1114,7 @@ public class HttpParser
LOG.warn("badMessage: "+e.toString()+" for "+_handler);
LOG.debug(e);
badMessage(buffer,HttpStatus.BAD_REQUEST_400,e.toString());
badMessage(buffer,HttpStatus.BAD_REQUEST_400,null);
return true;
}
}

View File

@ -175,14 +175,15 @@ public class HttpTester
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteBuffer header=null;
ByteBuffer chunk=null;
ByteBuffer content=_content==null?null:ByteBuffer.wrap(_content.toByteArray());
loop: while(!generator.isEnd())
{
HttpGenerator.Result result = info instanceof RequestInfo
?generator.generateRequest((RequestInfo)info,header,content,true)
:generator.generateResponse((ResponseInfo)info,header,content,true);
?generator.generateRequest((RequestInfo)info,header,chunk,content,true)
:generator.generateResponse((ResponseInfo)info,header,chunk,content,true);
switch(result)
{
case NEED_HEADER:
@ -190,7 +191,7 @@ public class HttpTester
continue;
case NEED_CHUNK:
header=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case NEED_INFO:
@ -202,6 +203,11 @@ public class HttpTester
out.write(BufferUtil.toArray(header));
BufferUtil.clear(header);
}
if (BufferUtil.hasContent(chunk))
{
out.write(BufferUtil.toArray(chunk));
BufferUtil.clear(chunk);
}
if (BufferUtil.hasContent(content))
{
out.write(BufferUtil.toArray(content));

View File

@ -54,7 +54,7 @@ public class HttpGeneratorClientTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generateRequest(null,null,null,true);
result=gen.generateRequest(null,null,null,null, true);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.START,gen.getState());
@ -63,18 +63,18 @@ public class HttpGeneratorClientTest
info.getHttpFields().add("User-Agent","test");
assertTrue(!gen.isChunking());
result=gen.generateRequest(info,null,null,true);
result=gen.generateRequest(info,null,null,null, true);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateRequest(info,header,null,true);
result=gen.generateRequest(info,header,null,null, true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertTrue(!gen.isChunking());
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
result=gen.generateResponse(null,null,null,false);
result=gen.generateResponse(null,null,null,null, false);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertTrue(!gen.isChunking());
@ -94,7 +94,7 @@ public class HttpGeneratorClientTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generateRequest(null,null,content0,true);
result=gen.generateRequest(null,null,null,content0, true);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.START,gen.getState());
@ -102,11 +102,11 @@ public class HttpGeneratorClientTest
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
result=gen.generateRequest(info,null,content0,true);
result=gen.generateRequest(info,null,null,content0, true);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateRequest(info,header,content0,true);
result=gen.generateRequest(info,header,null,content0, true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertTrue(!gen.isChunking());
@ -115,7 +115,7 @@ public class HttpGeneratorClientTest
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generateResponse(null,null,null,false);
result=gen.generateResponse(null,null,null,null, false);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertTrue(!gen.isChunking());
@ -140,7 +140,7 @@ public class HttpGeneratorClientTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generateRequest(null,null,content0,false);
result=gen.generateRequest(null,null,null,content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.START,gen.getState());
@ -148,11 +148,11 @@ public class HttpGeneratorClientTest
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
result=gen.generateRequest(info,null,content0,false);
result=gen.generateRequest(info,null,null,content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateRequest(info,header,content0,false);
result=gen.generateRequest(info,header,null,content0, false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(gen.isChunking());
@ -161,11 +161,11 @@ public class HttpGeneratorClientTest
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generateRequest(null,header,content1,false);
result=gen.generateRequest(null,header,null,content1, false);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.generateRequest(null,chunk,content1,false);
result=gen.generateRequest(null,null,chunk,content1, false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(gen.isChunking());
@ -174,19 +174,19 @@ public class HttpGeneratorClientTest
out+=BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generateResponse(null,chunk,null,true);
result=gen.generateResponse(null,null,chunk,null, true);
assertEquals(HttpGenerator.Result.CONTINUE,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertTrue(gen.isChunking());
result=gen.generateResponse(null,chunk,null,true);
result=gen.generateResponse(null,null,chunk,null, true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
out+=BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
assertTrue(!gen.isChunking());
result=gen.generateResponse(null,chunk,null,true);
result=gen.generateResponse(null,null,chunk,null, true);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
@ -212,7 +212,7 @@ public class HttpGeneratorClientTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generateRequest(null,null,content0,false);
result=gen.generateRequest(null,null,null,content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.START,gen.getState());
@ -220,11 +220,11 @@ public class HttpGeneratorClientTest
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
result=gen.generateRequest(info,null,content0,false);
result=gen.generateRequest(info,null,null,content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateRequest(info,header,content0,false);
result=gen.generateRequest(info,header,null,content0, false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(!gen.isChunking());
@ -233,19 +233,19 @@ public class HttpGeneratorClientTest
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generateRequest(null,null,content1,false);
result=gen.generateRequest(null,null,null,content1, false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(!gen.isChunking());
out+=BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generateResponse(null,null,null,true);
result=gen.generateResponse(null,null,null,null, true);
assertEquals(HttpGenerator.Result.CONTINUE,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertTrue(!gen.isChunking());
result=gen.generateResponse(null,null,null,true);
result=gen.generateResponse(null,null,null,null, true);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
out+=BufferUtil.toString(chunk);

View File

@ -149,6 +149,7 @@ public class HttpGeneratorServerTest
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
}
ByteBuffer header = null;
ByteBuffer chunk = null;
HttpGenerator.ResponseInfo info = null;
loop:
@ -164,22 +165,7 @@ public class HttpGeneratorServerTest
// Generate
boolean last = !BufferUtil.hasContent(content);
/*
System.err.printf("generate(%s,%s,%s,%s,%s)@%s%n",
BufferUtil.toSummaryString(header),
BufferUtil.toSummaryString(chunk),
BufferUtil.toSummaryString(buffer),
BufferUtil.toSummaryString(content),
action,gen.getState());
*/
HttpGenerator.Result result = gen.generateResponse(info, header, content, last);
/*System.err.printf("%s (%s,%s,%s,%s,%s)@%s%n",
result,
BufferUtil.toSummaryString(header),
BufferUtil.toSummaryString(chunk),
BufferUtil.toSummaryString(buffer),
BufferUtil.toSummaryString(content),
action,gen.getState());*/
HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last);
switch (result)
{
@ -192,7 +178,7 @@ public class HttpGeneratorServerTest
continue;
case NEED_CHUNK:
header = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case FLUSH:
@ -201,6 +187,11 @@ public class HttpGeneratorServerTest
response += BufferUtil.toString(header);
header.position(header.limit());
}
if (BufferUtil.hasContent(chunk))
{
response += BufferUtil.toString(chunk);
chunk.position(chunk.limit());
}
if (BufferUtil.hasContent(content))
{
response += BufferUtil.toString(content);
@ -322,23 +313,23 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result = gen.generateResponse(null, null, null, true);
result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
result = gen.generateResponse(info, null, null, true);
result = gen.generateResponse(info, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
result = gen.generateResponse(info, header, null, true);
result = gen.generateResponse(info, header, null, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
result = gen.generateResponse(null, null, null, false);
result = gen.generateResponse(null, null, null, null, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -356,7 +347,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result = gen.generateResponse(null, null, null, true);
result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -365,13 +356,13 @@ public class HttpGeneratorServerTest
info.getHttpFields().add("Connection", "Upgrade");
info.getHttpFields().add("Sec-WebSocket-Accept", "123456789==");
result = gen.generateResponse(info, header, null, true);
result = gen.generateResponse(info, header, null, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
result = gen.generateResponse(info, null, null, false);
result = gen.generateResponse(info, null, null, null, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -391,17 +382,17 @@ public class HttpGeneratorServerTest
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, content0, false);
HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
result = gen.generateResponse(info, null, content0, false);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, header, content0, false);
result = gen.generateResponse(info, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -410,7 +401,7 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, chunk, content1, false);
result = gen.generateResponse(null,null,chunk, content1, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(chunk);
@ -418,17 +409,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result = gen.generateResponse(null, chunk, null, true);
result = gen.generateResponse(null,null,chunk, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, chunk, null, true);
result = gen.generateResponse(null,null,chunk, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
out += BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
result = gen.generateResponse(null, chunk, null, true);
result = gen.generateResponse(null,null,chunk, null, true);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -453,17 +444,17 @@ public class HttpGeneratorServerTest
HttpGenerator.Result
result = gen.generateResponse(null, null, content0, false);
result = gen.generateResponse(null, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
result = gen.generateResponse(info, null, content0, false);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, header, content0, false);
result = gen.generateResponse(info, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -472,17 +463,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, null, content1, false);
result = gen.generateResponse(null, null, null, content1, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result = gen.generateResponse(null, null, null, true);
result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, null, null, true);
result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -504,33 +495,33 @@ public class HttpGeneratorServerTest
HttpGenerator.Result
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, false);
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, header, null, false);
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, header, null, null, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING_1XX, gen.getState());
String out = BufferUtil.toString(header);
result = gen.generateResponse(null, null, null, false);
result = gen.generateResponse(null, null, null, null, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.START, gen.getState());
assertThat(out, containsString("HTTP/1.1 100 Continue"));
result = gen.generateResponse(null, null, content0, false);
result = gen.generateResponse(null, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970);
result = gen.generateResponse(info, null, content0, false);
result = gen.generateResponse(info, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, header, content0, false);
result = gen.generateResponse(info, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -539,17 +530,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, null, content1, false);
result = gen.generateResponse(null, null, null, content1, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result = gen.generateResponse(null, null, null, true);
result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, null, null, true);
result = gen.generateResponse(null, null, null, null, true);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());

View File

@ -253,7 +253,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable
LOG.warn(String.valueOf(_uri), e);
_state.error(e);
_request.setHandled(true);
handleError(e);
handleException(e);
}
finally
{
@ -312,45 +312,6 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable
}
}
protected boolean complete()
{
LOG.debug("{} complete", this);
if (_state.isCompleting())
{
_state.completed();
if (isExpecting100Continue())
{
LOG.debug("100-Continue response not sent");
// We didn't send 100 continues, but the latest interpretation
// of the spec (see httpbis) is that the client will either
// send the body anyway, or close. So we no longer need to
// do anything special here other than make the connection not persistent
_expect100Continue = false;
if (!isCommitted())
_response.addHeader(HttpHeader.CONNECTION.toString(), HttpHeaderValue.CLOSE.toString());
else
LOG.warn("Cannot send 'Connection: close' for 100-Continue, response is already committed");
}
if (!_response.isCommitted() && !_request.isHandled())
_response.sendError(Response.SC_NOT_FOUND, null, null);
_request.setHandled(true);
try
{
_response.getHttpOutput().close();
}
catch (IOException x)
{
// We cannot write the response, so there is no point in calling
// response.sendError() since that writes, and we already know we cannot write.
LOG.debug("Could not write response", x);
}
}
return _request.getHttpInput().isShutdown();
}
/**
* <p>Sends an error 500, performing a special logic to detect whether the request is suspended,
* to avoid concurrent writes from the application.</p>
@ -360,11 +321,11 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable
*
* @param x the Throwable that caused the problem
*/
private void handleError(Throwable x)
protected void handleException(Throwable x)
{
if (_state.isSuspended())
try
{
try
if (_state.isSuspended())
{
HttpFields fields = new HttpFields();
ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, Response.SC_INTERNAL_SERVER_ERROR, null, _request.isHead());
@ -372,112 +333,20 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable
if (!committed)
LOG.warn("Could not send response error 500, response is already committed");
}
catch (IOException e)
else
{
// We tried our best, just log
LOG.debug("Could not commit response error 500", e);
}
_request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,x);
_request.setAttribute(RequestDispatcher.ERROR_EXCEPTION_TYPE,x.getClass());
_response.sendError(500, x.getMessage());
}
}
else
catch (IOException e)
{
_response.sendError(500, null, x.getMessage());
// We tried our best, just log
LOG.debug("Could not commit response error 500", e);
}
}
protected void sendError(ResponseInfo info, String extraContent)
{
int status = info.getStatus();
try
{
String reason = info.getReason();
if (reason == null)
reason = HttpStatus.getMessage(status);
// If we are allowed to have a body
if (status != Response.SC_NO_CONTENT &&
status != Response.SC_NOT_MODIFIED &&
status != Response.SC_PARTIAL_CONTENT &&
status >= Response.SC_OK)
{
ErrorHandler errorHandler = null;
ContextHandler.Context context = _request.getContext();
if (context != null)
errorHandler = context.getContextHandler().getErrorHandler();
if (errorHandler == null)
errorHandler = getServer().getBean(ErrorHandler.class);
if (errorHandler != null)
{
_request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, new Integer(status));
_request.setAttribute(RequestDispatcher.ERROR_MESSAGE, reason);
_request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, _request.getRequestURI());
_request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME, _request.getServletName());
errorHandler.handle(null, _request, _request, _response);
}
else
{
HttpFields fields = info.getHttpFields();
fields.put(HttpHeader.CACHE_CONTROL, "must-revalidate,no-cache,no-store");
fields.put(HttpHeader.CONTENT_TYPE, MimeTypes.Type.TEXT_HTML_8859_1.toString());
reason = escape(reason);
String uri = escape(_request.getRequestURI());
extraContent = escape(extraContent);
StringBuilder writer = new StringBuilder(2048);
writer.append("<html>\n");
writer.append("<head>\n");
writer.append("<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n");
writer.append("<title>Error ").append(Integer.toString(status)).append(' ').append(reason).append("</title>\n");
writer.append("</head>\n");
writer.append("<body>\n");
writer.append("<h2>HTTP ERROR: ").append(Integer.toString(status)).append("</h2>\n");
writer.append("<p>Problem accessing ").append(uri).append(". Reason:\n");
writer.append("<pre>").append(reason).append("</pre></p>");
if (extraContent != null)
writer.append("<p>").append(extraContent).append("</p>");
writer.append("<hr /><i><small>Powered by Jetty://</small></i>\n");
writer.append("</body>\n");
writer.append("</html>");
byte[] bytes = writer.toString().getBytes(StringUtil.__ISO_8859_1);
fields.put(HttpHeader.CONTENT_LENGTH, String.valueOf(bytes.length));
_response.getOutputStream().write(bytes);
}
}
else if (status != Response.SC_PARTIAL_CONTENT)
{
// TODO: not sure why we need to modify the request when writing an error ?
// TODO: or modify the response if the error code cannot have a body ?
// _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE);
// _channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH);
// _characterEncoding = null;
// _mimeType = null;
}
complete();
// TODO: is this needed ?
if (_state.isIdle())
_state.complete();
_request.getHttpInput().shutdown();
}
catch (IOException x)
{
// We failed to write the error, bail out
LOG.debug("Could not write error response " + status, x);
}
}
private String escape(String reason)
{
if (reason != null)
{
reason = reason.replaceAll("&", "&amp;");
reason = reason.replaceAll("<", "&lt;");
reason = reason.replaceAll(">", "&gt;");
}
return reason;
}
public boolean isExpecting100Continue()
{
return _expect100Continue;
@ -634,7 +503,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable
if (_expect)
{
_response.sendError(Response.SC_EXPECTATION_FAILED, null, null);
badMessage(HttpStatus.EXPECTATION_FAILED_417,null);
return true;
}
@ -653,9 +522,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable
public boolean content(ByteBuffer ref)
{
if (LOG.isDebugEnabled())
{
LOG.debug("{} content {}", this, BufferUtil.toDetailString(ref));
}
_request.getHttpInput().content(ref);
return true;
}

View File

@ -125,46 +125,6 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
}
}
protected boolean readAndParse() throws IOException
{
// If there is a request buffer, we are re-entering here
if (_requestBuffer == null)
{
_requestBuffer = _bufferPool.acquire(_configuration.getRequestHeaderSize(), false);
int filled = getEndPoint().fill(_requestBuffer);
LOG.debug("{} filled {}", this, filled);
// If we failed to fill
if (filled == 0)
{
// Somebody wanted to read, we didn't so schedule another attempt
releaseRequestBuffer();
fillInterested();
return false;
}
else if (filled < 0)
{
_parser.inputShutdown();
// We were only filling if fully consumed, so if we have
// read -1 then we have nothing to parse and thus nothing that
// will generate a response. If we had a suspended request pending
// a response or a request waiting in the buffer, we would not be here.
if (getEndPoint().isOutputShutdown())
getEndPoint().close();
else
getEndPoint().shutdownOutput();
// buffer must be empty and the channel must be idle, so we can release.
releaseRequestBuffer();
return false;
}
}
// Parse the buffer
return _parser.parseNext(_requestBuffer);
}
/**
* <p>Parses and handles HTTP messages.</p>
* <p>This method is called when this {@link Connection} is ready to read bytes from the {@link EndPoint}.
@ -183,7 +143,43 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
{
while (true)
{
if (readAndParse())
// If there is a request buffer, we are re-entering here
if (BufferUtil.isEmpty(_requestBuffer))
{
if (_requestBuffer == null)
_requestBuffer = _bufferPool.acquire(_configuration.getRequestHeaderSize(), false);
int filled = getEndPoint().fill(_requestBuffer);
LOG.debug("{} filled {}", this, filled);
// If we failed to fill
if (filled == 0)
{
// Somebody wanted to read, we didn't so schedule another attempt
releaseRequestBuffer();
fillInterested();
return;
}
else if (filled < 0)
{
_parser.inputShutdown();
// We were only filling if fully consumed, so if we have
// read -1 then we have nothing to parse and thus nothing that
// will generate a response. If we had a suspended request pending
// a response or a request waiting in the buffer, we would not be here.
if (getEndPoint().isOutputShutdown())
getEndPoint().close();
else
getEndPoint().shutdownOutput();
// buffer must be empty and the channel must be idle, so we can release.
releaseRequestBuffer();
return;
}
}
// Parse the buffer
if (_parser.parseNext(_requestBuffer))
{
// The parser returned true, which indicates the channel is ready to handle a request.
// Call the channel and this will either handle the request/response to completion OR,
@ -250,9 +246,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
// TODO This is always blocking! One of the important use-cases is to be able to write large static content without a thread
ByteBuffer header = null;
ByteBuffer chunk = null;
out: while (true)
{
HttpGenerator.Result result = _generator.generateResponse(info, header, content, lastContent);
HttpGenerator.Result result = _generator.generateResponse(info, header, chunk, content, lastContent);
if (LOG.isDebugEnabled())
LOG.debug("{} generate: {} ({},{},{})@{}",
this,
@ -266,34 +263,40 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
{
case NEED_HEADER:
{
if (header != null)
_bufferPool.release(header);
header = _bufferPool.acquire(_configuration.getResponseHeaderSize(), false);
continue;
}
case NEED_CHUNK:
{
if (header != null)
_bufferPool.release(header);
header = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
continue;
}
case FLUSH:
{
// Don't write the chunk or the content if this is a HEAD response
if (_channel.getRequest().isHead())
{
BufferUtil.clear(chunk);
BufferUtil.clear(content);
if (BufferUtil.hasContent(header))
blockingWrite(header);
}
else if (BufferUtil.hasContent(header))
// If we have a header
if (BufferUtil.hasContent(header))
{
// we know there will not be a chunk, so write either header+content or just the header
if (BufferUtil.hasContent(content))
blockingWrite(header, content);
else
blockingWrite(header);
}
else if (BufferUtil.hasContent(content))
else if (BufferUtil.hasContent(chunk))
{
if (BufferUtil.hasContent(content))
blockingWrite(chunk,content);
else
blockingWrite(chunk);
}
else if (BufferUtil.hasContent(content))
{
blockingWrite(content);
}
@ -306,6 +309,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
}
case DONE:
{
if (header!=null)
_bufferPool.release(header);
if (chunk!=null)
_bufferPool.release(chunk);
break out;
}
case CONTINUE:
@ -433,17 +440,49 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
@Override
protected void blockForContent() throws IOException
{
/* We extend the blockForContent method to replace the
default implementation of a blocking queue with an implementation
that uses the calling thread to block on a readable callback and
then to do the parsing before before attempting the read.
*/
try
{
while (true)
{
FutureCallback<Void> callback = new FutureCallback<>();
getEndPoint().fillInterested(null, callback);
callback.get();
if (readAndParse())
break;
else
releaseRequestBuffer();
// Can the parser progress (even with an empty buffer)
boolean event=_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
// If there is more content to parse, leep so we can queue all content from this buffer now without the
// need to call blockForContent again
while (BufferUtil.hasContent(_requestBuffer) && _parser.inContentState())
event|=_parser.parseNext(_requestBuffer);
// If we have an event, return
if (event)
return;
// Do we have content ready to parse?
if (BufferUtil.isEmpty(_requestBuffer))
{
// Wait until we can read
FutureCallback<Void> block=new FutureCallback<>();
getEndPoint().fillInterested(null,block);
LOG.debug("{} block readable on {}",this,block);
block.get();
// We will need a buffer to read into
if (_requestBuffer==null)
_requestBuffer=_bufferPool.acquire(_configuration.getRequestBufferSize(),false);
// read some data
int filled=getEndPoint().fill(_requestBuffer);
LOG.debug("{} block filled {}",this,filled);
if (filled<0)
{
_parser.inputShutdown();
return;
}
}
}
}
catch (InterruptedException x)
@ -510,6 +549,13 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
_generator.setPersistent(false);
return result;
}
@Override
protected void handleException(Throwable x)
{
_generator.setPersistent(false);
super.handleException(x);
}
}

View File

@ -238,7 +238,10 @@ public class LocalConnector extends AbstractConnector
if (!_closed.await(idleFor,units))
{
if (size==getOutput().remaining())
{
LOG.debug("idle for {} {}",idleFor,units);
return;
}
size=getOutput().remaining();
}
}

View File

@ -25,6 +25,8 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@ -41,6 +43,9 @@ import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.util.ByteArrayISO8859Writer;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
@ -297,36 +302,102 @@ public class Response implements HttpServletResponse
@Override
public void sendError(int code, String message) throws IOException
{
sendError(code, message, null);
}
protected void sendError(int code, String message, String content)
{
if (isIncluding())
return;
if (isCommitted())
{
LOG.warn("Could not commit error {} {}, response already committed", code, message);
return;
}
LOG.warn("Committed before "+code+" "+message);
resetBuffer();
_characterEncoding = null;
setHeader(HttpHeader.EXPIRES, null);
setHeader(HttpHeader.LAST_MODIFIED, null);
setHeader(HttpHeader.CACHE_CONTROL, null);
setHeader(HttpHeader.CONTENT_TYPE, null);
setHeader(HttpHeader.CONTENT_LENGTH, null);
if (message == null)
message = HttpStatus.getMessage(code);
setStatus(code, message);
_characterEncoding=null;
setHeader(HttpHeader.EXPIRES,null);
setHeader(HttpHeader.LAST_MODIFIED,null);
setHeader(HttpHeader.CACHE_CONTROL,null);
setHeader(HttpHeader.CONTENT_TYPE,null);
setHeader(HttpHeader.CONTENT_LENGTH,null);
_outputType = OutputType.NONE;
setStatus(code);
_reason=message;
_channel.sendError(newResponseInfo(), content);
if (message==null)
message=HttpStatus.getMessage(code);
// If we are allowed to have a body
if (code!=SC_NO_CONTENT &&
code!=SC_NOT_MODIFIED &&
code!=SC_PARTIAL_CONTENT &&
code>=SC_OK)
{
Request request = _channel.getRequest();
ErrorHandler error_handler = null;
ContextHandler.Context context = request.getContext();
if (context!=null)
error_handler=context.getContextHandler().getErrorHandler();
if (error_handler==null)
error_handler = _channel.getServer().getBean(ErrorHandler.class);
if (error_handler!=null)
{
request.setAttribute(RequestDispatcher.ERROR_STATUS_CODE,new Integer(code));
request.setAttribute(RequestDispatcher.ERROR_MESSAGE, message);
request.setAttribute(RequestDispatcher.ERROR_REQUEST_URI, request.getRequestURI());
request.setAttribute(RequestDispatcher.ERROR_SERVLET_NAME,request.getServletName());
error_handler.handle(null,_channel.getRequest(),_channel.getRequest(),this );
}
else
{
setHeader(HttpHeader.CACHE_CONTROL, "must-revalidate,no-cache,no-store");
setContentType(MimeTypes.Type.TEXT_HTML_8859_1.toString());
ByteArrayISO8859Writer writer= new ByteArrayISO8859Writer(2048);
if (message != null)
{
message= StringUtil.replace(message, "&", "&amp;");
message= StringUtil.replace(message, "<", "&lt;");
message= StringUtil.replace(message, ">", "&gt;");
}
String uri= request.getRequestURI();
if (uri!=null)
{
uri= StringUtil.replace(uri, "&", "&amp;");
uri= StringUtil.replace(uri, "<", "&lt;");
uri= StringUtil.replace(uri, ">", "&gt;");
}
writer.write("<html>\n<head>\n<meta http-equiv=\"Content-Type\" content=\"text/html;charset=ISO-8859-1\"/>\n");
writer.write("<title>Error ");
writer.write(Integer.toString(code));
writer.write(' ');
if (message==null)
message=HttpStatus.getMessage(code);
writer.write(message);
writer.write("</title>\n</head>\n<body>\n<h2>HTTP ERROR: ");
writer.write(Integer.toString(code));
writer.write("</h2>\n<p>Problem accessing ");
writer.write(uri);
writer.write(". Reason:\n<pre> ");
writer.write(message);
writer.write("</pre>");
writer.write("</p>\n<hr /><i><small>Powered by Jetty://</small></i>");
writer.write("\n</body>\n</html>\n");
writer.flush();
setContentLength(writer.size());
writer.writeTo(getOutputStream());
writer.destroy();
}
}
else if (code!=SC_PARTIAL_CONTENT)
{
// TODO work out why this is required?
_channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_TYPE);
_channel.getRequest().getHttpFields().remove(HttpHeader.CONTENT_LENGTH);
_characterEncoding=null;
_mimeType=null;
}
complete();
}
/**
@ -530,7 +601,10 @@ public class Response implements HttpServletResponse
@Override
public void setStatus(int sc)
{
setStatus(sc, null);
if (sc <= 0)
throw new IllegalArgumentException();
if (!isIncluding())
_status = sc;
}
@Override

View File

@ -351,10 +351,13 @@ public class Server extends HandlerWrapper implements Attributes
for (Connector connector : _connectors)
{
while (connector.getStatistics().isRunning() && connector.getStatistics().getConnectionsOpen()>0 && System.currentTimeMillis()<stop_by)
{
System.err.println(((Dumpable)connector).dump());
Thread.sleep(100);
if (connector.getStatistics().isRunning() && connector.getStatistics().getConnectionsOpen()>0 && System.currentTimeMillis()<stop_by)
{
if (LOG.isDebugEnabled())
System.err.println(((Dumpable)connector).dump());
LOG.info("Waiting for connections to close on {}...",connector);
while (connector.getStatistics().isRunning() && connector.getStatistics().getConnectionsOpen()>0 && System.currentTimeMillis()<stop_by)
Thread.sleep(100);
}
}
}

View File

@ -321,7 +321,8 @@ public class HttpConnectionTest
int offset=0;
offset=0;
requests="GET /R1?read=1&error=500 HTTP/1.1\n"+
requests=
"GET /R1?read=1&error=500 HTTP/1.1\n"+
"Host: localhost\n"+
"Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+
@ -340,12 +341,12 @@ public class HttpConnectionTest
"abcdefghij\n";
response=connector.getResponses(requests);
offset = checkContains(response,offset,"HTTP/1.1 500");
offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"/R2");
offset = checkContains(response,offset,"encoding=UTF-8");
offset = checkContains(response,offset,"abcdefghij");
}
@Test
@ -376,7 +377,7 @@ public class HttpConnectionTest
Logger logger = Log.getLogger(HttpChannel.class);
try
{
logger.info("EXPECTING: java.lang.IllegalStateException...");
logger.info("EXPECTING: java.lang.IllegalStateException...");
((StdErrLog)logger).setHideStacks(true);
response=connector.getResponses(requests);
offset = checkContains(response,offset,"HTTP/1.1 500");

View File

@ -379,6 +379,7 @@ public class RequestTest
}
};
String content="";
for (int l=0;l<1025;l++)
@ -390,7 +391,9 @@ public class RequestTest
"Connection: close\r\n"+
"\r\n"+
content;
Log.getRootLogger().debug("test l={}",l);
String response = _connector.getResponses(request);
Log.getRootLogger().debug(response);
assertEquals(l,length[0]);
if (l>0)
assertEquals(l,_handler._content.length());

View File

@ -47,6 +47,7 @@ import org.eclipse.jetty.server.session.HashSessionIdManager;
import org.eclipse.jetty.server.session.HashSessionManager;
import org.eclipse.jetty.server.session.HashedSession;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.IO;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
@ -103,35 +104,14 @@ public class ResponseTest
}
};
_channel = new HttpChannel(_connector,null,endp,null,null)
_channel = new HttpChannel(_connector,new HttpConfiguration(null,false),endp,null,null)
{
// @Override
protected void flush(ByteBuffer content, boolean last) throws IOException
{
content.clear();
}
// @Override
protected FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException
{
content.clear();
FutureCallback<Void> fcb = new FutureCallback<>();
fcb.completed(null);
return fcb;
}
@Override
public ScheduledExecutorService getScheduler()
{
return null;
}
@Override
public HttpConfiguration getHttpConfiguration()
{
return null;
}
@Override
protected void execute(Runnable task)
{

View File

@ -104,8 +104,11 @@ public class BufferUtil
*/
public static void clear(ByteBuffer buffer)
{
buffer.position(0);
buffer.limit(0);
if (buffer!=null)
{
buffer.position(0);
buffer.limit(0);
}
}
/* ------------------------------------------------------------ */