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

View File

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

View File

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

View File

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

View File

@ -149,6 +149,7 @@ public class HttpGeneratorServerTest
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content)); // System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
} }
ByteBuffer header = null; ByteBuffer header = null;
ByteBuffer chunk = null;
HttpGenerator.ResponseInfo info = null; HttpGenerator.ResponseInfo info = null;
loop: loop:
@ -164,22 +165,7 @@ public class HttpGeneratorServerTest
// Generate // Generate
boolean last = !BufferUtil.hasContent(content); boolean last = !BufferUtil.hasContent(content);
/* HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last);
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());*/
switch (result) switch (result)
{ {
@ -192,7 +178,7 @@ public class HttpGeneratorServerTest
continue; continue;
case NEED_CHUNK: case NEED_CHUNK:
header = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE); chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue; continue;
case FLUSH: case FLUSH:
@ -201,6 +187,11 @@ public class HttpGeneratorServerTest
response += BufferUtil.toString(header); response += BufferUtil.toString(header);
header.position(header.limit()); header.position(header.limit());
} }
if (BufferUtil.hasContent(chunk))
{
response += BufferUtil.toString(chunk);
chunk.position(chunk.limit());
}
if (BufferUtil.hasContent(content)) if (BufferUtil.hasContent(content))
{ {
response += BufferUtil.toString(content); response += BufferUtil.toString(content);
@ -322,23 +313,23 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator(); HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result 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.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false); ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970); 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); 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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState()); assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String head = BufferUtil.toString(header); String head = BufferUtil.toString(header);
BufferUtil.clear(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.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState()); assertEquals(HttpGenerator.State.END, gen.getState());
@ -356,7 +347,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator(); HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result 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.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
@ -365,13 +356,13 @@ public class HttpGeneratorServerTest
info.getHttpFields().add("Connection", "Upgrade"); info.getHttpFields().add("Connection", "Upgrade");
info.getHttpFields().add("Sec-WebSocket-Accept", "123456789=="); 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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState()); assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String head = BufferUtil.toString(header); String head = BufferUtil.toString(header);
BufferUtil.clear(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.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState()); 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. "); ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator(); 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.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false); ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970); 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.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState()); 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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState()); assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -410,7 +401,7 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0); out += BufferUtil.toString(content0);
BufferUtil.clear(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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState()); assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(chunk); out += BufferUtil.toString(chunk);
@ -418,17 +409,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content1); out += BufferUtil.toString(content1);
BufferUtil.clear(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.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState()); 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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState()); assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
out += BufferUtil.toString(chunk); out += BufferUtil.toString(chunk);
BufferUtil.clear(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.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState()); assertEquals(HttpGenerator.State.END, gen.getState());
@ -453,17 +444,17 @@ public class HttpGeneratorServerTest
HttpGenerator.Result 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.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false); ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970); 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.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState()); 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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState()); assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -472,17 +463,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0); out += BufferUtil.toString(content0);
BufferUtil.clear(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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState()); assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(content1); out += BufferUtil.toString(content1);
BufferUtil.clear(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.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState()); 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.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState()); assertEquals(HttpGenerator.State.END, gen.getState());
@ -504,33 +495,33 @@ public class HttpGeneratorServerTest
HttpGenerator.Result 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.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState()); 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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING_1XX, gen.getState()); assertEquals(HttpGenerator.State.COMPLETING_1XX, gen.getState());
String out = BufferUtil.toString(header); 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.Result.DONE, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
assertThat(out, containsString("HTTP/1.1 100 Continue")); 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.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState()); assertEquals(HttpGenerator.State.START, gen.getState());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false); ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), 59, 200, null, false);
info.getHttpFields().add("Last-Modified", HttpFields.__01Jan1970); 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.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState()); 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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState()); assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -539,17 +530,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0); out += BufferUtil.toString(content0);
BufferUtil.clear(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.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState()); assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(content1); out += BufferUtil.toString(content1);
BufferUtil.clear(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.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState()); 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.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState()); 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); LOG.warn(String.valueOf(_uri), e);
_state.error(e); _state.error(e);
_request.setHandled(true); _request.setHandled(true);
handleError(e); handleException(e);
} }
finally 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, * <p>Sends an error 500, performing a special logic to detect whether the request is suspended,
* to avoid concurrent writes from the application.</p> * 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 * @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(); HttpFields fields = new HttpFields();
ResponseInfo info = new ResponseInfo(_request.getHttpVersion(), fields, 0, Response.SC_INTERNAL_SERVER_ERROR, null, _request.isHead()); 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) if (!committed)
LOG.warn("Could not send response error 500, response is already committed"); LOG.warn("Could not send response error 500, response is already committed");
} }
catch (IOException e) else
{ {
// We tried our best, just log _request.setAttribute(RequestDispatcher.ERROR_EXCEPTION,x);
LOG.debug("Could not commit response error 500", e); _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() public boolean isExpecting100Continue()
{ {
return _expect100Continue; return _expect100Continue;
@ -634,7 +503,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable
if (_expect) if (_expect)
{ {
_response.sendError(Response.SC_EXPECTATION_FAILED, null, null); badMessage(HttpStatus.EXPECTATION_FAILED_417,null);
return true; return true;
} }
@ -653,9 +522,7 @@ public class HttpChannel implements HttpParser.RequestHandler, Runnable
public boolean content(ByteBuffer ref) public boolean content(ByteBuffer ref)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
{
LOG.debug("{} content {}", this, BufferUtil.toDetailString(ref)); LOG.debug("{} content {}", this, BufferUtil.toDetailString(ref));
}
_request.getHttpInput().content(ref); _request.getHttpInput().content(ref);
return true; 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>Parses and handles HTTP messages.</p>
* <p>This method is called when this {@link Connection} is ready to read bytes from the {@link EndPoint}. * <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) 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. // 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, // 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 // 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 header = null;
ByteBuffer chunk = null;
out: while (true) 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()) if (LOG.isDebugEnabled())
LOG.debug("{} generate: {} ({},{},{})@{}", LOG.debug("{} generate: {} ({},{},{})@{}",
this, this,
@ -266,34 +263,40 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
{ {
case NEED_HEADER: case NEED_HEADER:
{ {
if (header != null)
_bufferPool.release(header);
header = _bufferPool.acquire(_configuration.getResponseHeaderSize(), false); header = _bufferPool.acquire(_configuration.getResponseHeaderSize(), false);
continue; continue;
} }
case NEED_CHUNK: case NEED_CHUNK:
{ {
if (header != null) chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
_bufferPool.release(header);
header = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, false);
continue; continue;
} }
case FLUSH: case FLUSH:
{ {
// Don't write the chunk or the content if this is a HEAD response
if (_channel.getRequest().isHead()) if (_channel.getRequest().isHead())
{ {
BufferUtil.clear(chunk);
BufferUtil.clear(content); 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)) if (BufferUtil.hasContent(content))
blockingWrite(header, content); blockingWrite(header, content);
else else
blockingWrite(header); 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); blockingWrite(content);
} }
@ -306,6 +309,10 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
} }
case DONE: case DONE:
{ {
if (header!=null)
_bufferPool.release(header);
if (chunk!=null)
_bufferPool.release(chunk);
break out; break out;
} }
case CONTINUE: case CONTINUE:
@ -433,17 +440,49 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
@Override @Override
protected void blockForContent() throws IOException 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 try
{ {
while (true) while (true)
{ {
FutureCallback<Void> callback = new FutureCallback<>(); // Can the parser progress (even with an empty buffer)
getEndPoint().fillInterested(null, callback); boolean event=_parser.parseNext(_requestBuffer==null?BufferUtil.EMPTY_BUFFER:_requestBuffer);
callback.get();
if (readAndParse()) // If there is more content to parse, leep so we can queue all content from this buffer now without the
break; // need to call blockForContent again
else while (BufferUtil.hasContent(_requestBuffer) && _parser.inContentState())
releaseRequestBuffer(); 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) catch (InterruptedException x)
@ -510,6 +549,13 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
_generator.setPersistent(false); _generator.setPersistent(false);
return result; 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 (!_closed.await(idleFor,units))
{ {
if (size==getOutput().remaining()) if (size==getOutput().remaining())
{
LOG.debug("idle for {} {}",idleFor,units);
return; return;
}
size=getOutput().remaining(); size=getOutput().remaining();
} }
} }

View File

@ -25,6 +25,8 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Locale; import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletOutputStream; import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie; import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse; 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.HttpURI;
import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MimeTypes; 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.StringUtil;
import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
@ -297,36 +302,102 @@ public class Response implements HttpServletResponse
@Override @Override
public void sendError(int code, String message) throws IOException public void sendError(int code, String message) throws IOException
{
sendError(code, message, null);
}
protected void sendError(int code, String message, String content)
{ {
if (isIncluding()) if (isIncluding())
return; return;
if (isCommitted()) if (isCommitted())
{ LOG.warn("Committed before "+code+" "+message);
LOG.warn("Could not commit error {} {}, response already committed", code, message);
return;
}
resetBuffer(); resetBuffer();
_characterEncoding = null; _characterEncoding=null;
setHeader(HttpHeader.EXPIRES, null); setHeader(HttpHeader.EXPIRES,null);
setHeader(HttpHeader.LAST_MODIFIED, null); setHeader(HttpHeader.LAST_MODIFIED,null);
setHeader(HttpHeader.CACHE_CONTROL, null); setHeader(HttpHeader.CACHE_CONTROL,null);
setHeader(HttpHeader.CONTENT_TYPE, null); setHeader(HttpHeader.CONTENT_TYPE,null);
setHeader(HttpHeader.CONTENT_LENGTH, null); setHeader(HttpHeader.CONTENT_LENGTH,null);
if (message == null)
message = HttpStatus.getMessage(code);
setStatus(code, message);
_outputType = OutputType.NONE; _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 @Override
public void setStatus(int sc) public void setStatus(int sc)
{ {
setStatus(sc, null); if (sc <= 0)
throw new IllegalArgumentException();
if (!isIncluding())
_status = sc;
} }
@Override @Override

View File

@ -351,10 +351,13 @@ public class Server extends HandlerWrapper implements Attributes
for (Connector connector : _connectors) for (Connector connector : _connectors)
{ {
while (connector.getStatistics().isRunning() && connector.getStatistics().getConnectionsOpen()>0 && System.currentTimeMillis()<stop_by) if (connector.getStatistics().isRunning() && connector.getStatistics().getConnectionsOpen()>0 && System.currentTimeMillis()<stop_by)
{ {
System.err.println(((Dumpable)connector).dump()); if (LOG.isDebugEnabled())
Thread.sleep(100); 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; int offset=0;
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"+ "Host: localhost\n"+
"Transfer-Encoding: chunked\n"+ "Transfer-Encoding: chunked\n"+
"Content-Type: text/plain; charset=utf-8\n"+ "Content-Type: text/plain; charset=utf-8\n"+
@ -340,12 +341,12 @@ public class HttpConnectionTest
"abcdefghij\n"; "abcdefghij\n";
response=connector.getResponses(requests); response=connector.getResponses(requests);
offset = checkContains(response,offset,"HTTP/1.1 500"); offset = checkContains(response,offset,"HTTP/1.1 500");
offset = checkContains(response,offset,"HTTP/1.1 200"); offset = checkContains(response,offset,"HTTP/1.1 200");
offset = checkContains(response,offset,"/R2"); offset = checkContains(response,offset,"/R2");
offset = checkContains(response,offset,"encoding=UTF-8"); offset = checkContains(response,offset,"encoding=UTF-8");
offset = checkContains(response,offset,"abcdefghij"); offset = checkContains(response,offset,"abcdefghij");
} }
@Test @Test
@ -376,7 +377,7 @@ public class HttpConnectionTest
Logger logger = Log.getLogger(HttpChannel.class); Logger logger = Log.getLogger(HttpChannel.class);
try try
{ {
logger.info("EXPECTING: java.lang.IllegalStateException..."); logger.info("EXPECTING: java.lang.IllegalStateException...");
((StdErrLog)logger).setHideStacks(true); ((StdErrLog)logger).setHideStacks(true);
response=connector.getResponses(requests); response=connector.getResponses(requests);
offset = checkContains(response,offset,"HTTP/1.1 500"); offset = checkContains(response,offset,"HTTP/1.1 500");

View File

@ -379,6 +379,7 @@ public class RequestTest
} }
}; };
String content=""; String content="";
for (int l=0;l<1025;l++) for (int l=0;l<1025;l++)
@ -390,7 +391,9 @@ public class RequestTest
"Connection: close\r\n"+ "Connection: close\r\n"+
"\r\n"+ "\r\n"+
content; content;
Log.getRootLogger().debug("test l={}",l);
String response = _connector.getResponses(request); String response = _connector.getResponses(request);
Log.getRootLogger().debug(response);
assertEquals(l,length[0]); assertEquals(l,length[0]);
if (l>0) if (l>0)
assertEquals(l,_handler._content.length()); 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.HashSessionManager;
import org.eclipse.jetty.server.session.HashedSession; import org.eclipse.jetty.server.session.HashedSession;
import org.eclipse.jetty.util.FutureCallback; import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.IO;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.After; import org.junit.After;
import org.junit.Assert; 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 @Override
public ScheduledExecutorService getScheduler() public ScheduledExecutorService getScheduler()
{ {
return null; return null;
} }
@Override
public HttpConfiguration getHttpConfiguration()
{
return null;
}
@Override @Override
protected void execute(Runnable task) protected void execute(Runnable task)
{ {

View File

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