jetty-9 work in progress on moving aggregation out of HttpChannel

This commit is contained in:
Greg Wilkins 2012-08-20 20:43:10 +10:00
parent 9080882900
commit 325bfc290d
20 changed files with 1382 additions and 1952 deletions

View File

@ -42,18 +42,15 @@ public class HttpGenerator
new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0,HttpStatus.INTERNAL_SERVER_ERROR_500,null,false);
// states
public enum Action { FLUSH, COMPLETE, PREPARE }
public enum State { START, COMMITTING, COMMITTING_COMPLETING, COMMITTED, COMPLETING, COMPLETING_1XX, END }
public enum Result { NEED_CHUNK,NEED_INFO,NEED_HEADER,NEED_BUFFER,FLUSH,FLUSH_CONTENT,OK,SHUTDOWN_OUT}
public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END }
public enum Result { NEED_CHUNK,NEED_INFO,NEED_HEADER,FLUSH,CONTINUE,SHUTDOWN_OUT,DONE}
// other statics
public static final int CHUNK_SIZE = 12;
private State _state = State.START;
private EndOfContent _content = EndOfContent.UNKNOWN_CONTENT;
private EndOfContent _endOfContent = EndOfContent.UNKNOWN_CONTENT;
private int _largeContent=4096;
private long _contentPrepared = 0;
private boolean _noContent = false;
private Boolean _persistent = null;
@ -79,7 +76,7 @@ public class HttpGenerator
public void reset()
{
_state = State.START;
_content = EndOfContent.UNKNOWN_CONTENT;
_endOfContent = EndOfContent.UNKNOWN_CONTENT;
_noContent=false;
_persistent = null;
_contentPrepared = 0;
@ -118,7 +115,7 @@ public class HttpGenerator
}
/* ------------------------------------------------------------ */
public boolean isComplete()
public boolean isEnd()
{
return _state == State.END;
}
@ -132,19 +129,7 @@ public class HttpGenerator
/* ------------------------------------------------------------ */
public boolean isChunking()
{
return _content==EndOfContent.CHUNKED_CONTENT;
}
/* ------------------------------------------------------------ */
public int getLargeContent()
{
return _largeContent;
}
/* ------------------------------------------------------------ */
public void setLargeContent(int largeContent)
{
_largeContent = largeContent;
return _endOfContent==EndOfContent.CHUNKED_CONTENT;
}
/* ------------------------------------------------------------ */
@ -179,298 +164,292 @@ public class HttpGenerator
{
_persistent=false;
_state=State.END;
_content=null;
_endOfContent=null;
}
/* ------------------------------------------------------------ */
public Result generate(Info info, ByteBuffer header, ByteBuffer chunk, ByteBuffer buffer, ByteBuffer content, Action action)
public Result generateRequest(RequestInfo info, ByteBuffer headerOrChunk, ByteBuffer content, boolean last)
{
Result result = Result.OK;
if (_state==State.END)
return result;
if (action==null)
action=Action.PREPARE;
// Do we have content to handle
if (BufferUtil.hasContent(content))
switch(_state)
{
// Do we have too much content?
if (_content==EndOfContent.CONTENT_LENGTH && info!=null && info.getContentLength()>=0 && content.remaining()>(info.getContentLength()-_contentPrepared))
case START:
{
LOG.warn("Content truncated. Info.getContentLength()=="+info.getContentLength()+" prepared="+_contentPrepared+" content="+content.remaining(),new Throwable());
content.limit(content.position()+(int)(info.getContentLength()-_contentPrepared));
}
if (info==null)
return Result.NEED_INFO;
// Can we do a direct flush
if (BufferUtil.isEmpty(buffer) && content.remaining()>_largeContent)
{
if (isCommitted())
// Do we need a request header
if (headerOrChunk==null || headerOrChunk.capacity()<=CHUNK_SIZE)
return Result.NEED_HEADER;
// If we have not been told our persistence, set the default
if (_persistent==null)
_persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
// prepare the header
ByteBuffer header = headerOrChunk;
int pos=BufferUtil.flipToFill(header);
try
{
if (isChunking())
{
if (chunk==null)
return Result.NEED_CHUNK;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,content.remaining());
BufferUtil.flipToFlush(chunk,0);
}
_contentPrepared+=content.remaining();
return Result.FLUSH_CONTENT;
}
// generate ResponseLine
generateRequestLine(info,header);
_state=action==Action.COMPLETE?State.COMMITTING_COMPLETING:State.COMMITTING;
result=Result.FLUSH_CONTENT;
}
else
{
// we copy content to buffer
// if we don't have one, we need one
if (buffer==null)
return Result.NEED_BUFFER;
// Copy the content
_contentPrepared+=BufferUtil.append(content,buffer);
// are we full?
if (BufferUtil.isFull(buffer))
{
if (isCommitted())
{
if (isChunking())
{
if (chunk==null)
return Result.NEED_CHUNK;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,buffer.remaining());
BufferUtil.flipToFlush(chunk,0);
}
return Result.FLUSH;
}
_state=action==Action.COMPLETE?State.COMMITTING_COMPLETING:State.COMMITTING;
result=Result.FLUSH;
}
}
}
// Handle the actions
if (result==Result.OK)
{
switch(action)
{
case COMPLETE:
if (!isCommitted())
_state=State.COMMITTING_COMPLETING;
else if (_state==State.COMMITTED)
_state=State.COMPLETING;
result=BufferUtil.hasContent(buffer)?Result.FLUSH:Result.OK;
break;
case FLUSH:
if (!isCommitted())
_state=State.COMMITTING;
result=BufferUtil.hasContent(buffer)?Result.FLUSH:Result.OK;
break;
}
}
// flip header if we have one
final int pos=header==null?-1:BufferUtil.flipToFill(header);
try
{
// handle by state
switch (_state)
{
case START:
return Result.OK;
case COMMITTING:
case COMMITTING_COMPLETING:
{
if (info==null)
return Result.NEED_INFO;
if (info instanceof RequestInfo)
{
if (header==null || header.capacity()<=CHUNK_SIZE)
return Result.NEED_HEADER;
if(info.getHttpVersion()==HttpVersion.HTTP_0_9)
{
_noContent=true;
generateRequestLine((RequestInfo)info,header);
_state = State.END;
return Result.OK;
}
_persistent=true;
generateRequestLine((RequestInfo)info,header);
}
if (info.getHttpVersion()==HttpVersion.HTTP_0_9)
_noContent=true;
else
generateHeaders(info,header,content,last);
// handle the content.
int len = BufferUtil.length(content);
if (len>0)
{
// Responses
// Do we need a response header?
if (info.getHttpVersion() == HttpVersion.HTTP_0_9)
{
_persistent = false;
_content=EndOfContent.EOF_CONTENT;
_state = State.COMMITTED;
if (result==Result.FLUSH_CONTENT)
_contentPrepared+=content.remaining();
return result;
}
// yes we need a response header
if (header==null || header.capacity()<=CHUNK_SIZE)
return Result.NEED_HEADER;
// Are we persistent by default?
if (_persistent==null)
_persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
generateResponseLine(((ResponseInfo)info),header);
// Handle 1xx
int status=((ResponseInfo)info).getStatus();
if (status>=100 && status<200 )
{
_noContent=true;
if (status!=HttpStatus.SWITCHING_PROTOCOLS_101 )
{
header.put(HttpTokens.CRLF);
_state=State.COMPLETING_1XX;
return Result.FLUSH;
}
}
else if (status==HttpStatus.NO_CONTENT_204 || status==HttpStatus.NOT_MODIFIED_304)
{
_noContent=true;
}
_contentPrepared+=len;
if (isChunking())
prepareChunk(header,len);
}
boolean completing=action==Action.COMPLETE||_state==State.COMMITTING_COMPLETING;
generateHeaders(info,header,content,completing);
_state = completing?State.COMPLETING:State.COMMITTED;
// handle result
switch(result)
{
case FLUSH:
if (isChunking())
prepareChunk(header,buffer.remaining());
break;
case FLUSH_CONTENT:
if (isChunking())
prepareChunk(header,content.remaining());
_contentPrepared+=content.remaining();
break;
case OK:
if (BufferUtil.hasContent(buffer))
{
if (isChunking())
prepareChunk(header,buffer.remaining());
}
result=Result.FLUSH;
}
return result;
_state = last?State.COMPLETING:State.COMMITTED;
}
catch(Exception e)
{
if (e instanceof BufferOverflowException)
LOG.warn("Response header too large");
throw e;
}
finally
{
BufferUtil.flipToFlush(header,pos);
}
case COMMITTED:
return Result.OK;
case COMPLETING:
// handle content with commit
return Result.FLUSH;
}
case COMMITTED:
{
int len = BufferUtil.length(content);
if (len>0)
{
// Do we need a chunk buffer?
if (isChunking())
{
if (chunk==null)
// Do we need a chunk buffer?
if (headerOrChunk==null || headerOrChunk.capacity()>CHUNK_SIZE)
return Result.NEED_CHUNK;
ByteBuffer chunk = headerOrChunk;
BufferUtil.clearToFill(chunk);
switch(result)
{
case FLUSH:
prepareChunk(chunk,buffer.remaining());
break;
case FLUSH_CONTENT:
prepareChunk(chunk,content.remaining());
break;
case OK:
if (BufferUtil.hasContent(buffer))
{
result=Result.FLUSH;
prepareChunk(chunk,buffer.remaining());
}
else
{
result=Result.FLUSH;
_state=State.END;
prepareChunk(chunk,0);
}
}
prepareChunk(chunk,len);
BufferUtil.flipToFlush(chunk,0);
}
else if (result==Result.OK)
{
if (BufferUtil.hasContent(buffer))
result=Result.FLUSH;
else
{
if (!Boolean.TRUE.equals(_persistent))
result=Result.SHUTDOWN_OUT;
_state=State.END;
}
}
return result;
case COMPLETING_1XX:
reset();
return Result.OK;
case END:
return Boolean.TRUE.equals(_persistent) ? Result.OK : Result.SHUTDOWN_OUT;
default:
throw new IllegalStateException();
}
}
catch(Exception e)
{
if (header!=null && info instanceof ResponseInfo)
{
if (e instanceof BufferOverflowException)
{
LOG.warn("Response header too large");
LOG.debug(e);
_contentPrepared+=len;
}
else
LOG.warn(e);
_state=State.COMPLETING;
// We were probably trying to generate a header, so let's make it a 500 instead
header.clear();
_persistent=false;
generateResponseLine(RESPONSE_500_INFO,header);
generateHeaders(RESPONSE_500_INFO,header,null,true);
if (buffer!=null)
BufferUtil.clear(buffer);
if (chunk!=null)
BufferUtil.clear(chunk);
if (content!=null)
BufferUtil.clear(content);
return Result.FLUSH;
if (last)
{
_state=State.COMPLETING;
return len>0?Result.FLUSH:Result.CONTINUE;
}
return len>0?Result.FLUSH:Result.DONE;
}
throw e;
}
finally
{
if (pos>=0)
BufferUtil.flipToFlush(header,pos);
case COMPLETING:
{
if (BufferUtil.hasContent(content))
throw new IllegalStateException(); // Can't pass new content in COMPLETING state
if (isChunking())
{
// Do we need a chunk buffer?
if (headerOrChunk==null || headerOrChunk.capacity()>CHUNK_SIZE)
return Result.NEED_CHUNK;
ByteBuffer chunk=headerOrChunk;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,0);
BufferUtil.flipToFlush(chunk,0);
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
return Result.FLUSH;
}
_state=State.END;
return Boolean.TRUE.equals(_persistent)?Result.DONE:Result.SHUTDOWN_OUT;
}
case END:
if (BufferUtil.hasContent(content))
throw new IllegalStateException(); // Can't pass new content in END state
return Result.DONE;
default:
throw new IllegalStateException();
}
}
/* ------------------------------------------------------------ */
public Result generateResponse(ResponseInfo info, ByteBuffer headerOrChunk, ByteBuffer content, boolean last)
{
switch(_state)
{
case START:
{
if (info==null)
return Result.NEED_INFO;
// Handle 0.9
if (info.getHttpVersion() == HttpVersion.HTTP_0_9)
{
_persistent = false;
_endOfContent=EndOfContent.EOF_CONTENT;
if (BufferUtil.hasContent(content))
_contentPrepared+=content.remaining();
_state = last?State.COMPLETING:State.COMMITTED;
return Result.FLUSH;
}
// Do we need a response header
if (headerOrChunk==null || headerOrChunk.capacity()<=CHUNK_SIZE)
return Result.NEED_HEADER;
// If we have not been told our persistence, set the default
if (_persistent==null)
_persistent=(info.getHttpVersion().ordinal() > HttpVersion.HTTP_1_0.ordinal());
// prepare the header
ByteBuffer header = headerOrChunk;
int pos=BufferUtil.flipToFill(header);
try
{
// generate ResponseLine
generateResponseLine(info,header);
// Handle 1xx and no content responses
int status=info.getStatus();
if (status>=100 && status<200 )
{
_noContent=true;
if (status!=HttpStatus.SWITCHING_PROTOCOLS_101 )
{
header.put(HttpTokens.CRLF);
_state=State.COMPLETING_1XX;
return Result.FLUSH;
}
}
else if (status==HttpStatus.NO_CONTENT_204 || status==HttpStatus.NOT_MODIFIED_304)
{
_noContent=true;
}
generateHeaders(info,header,content,last);
// handle the content.
int len = BufferUtil.length(content);
if (len>0)
{
_contentPrepared+=len;
if (isChunking() && !info.isHead())
prepareChunk(header,len);
}
_state = last?State.COMPLETING:State.COMMITTED;
}
catch(Exception e)
{
if (e instanceof BufferOverflowException)
{
LOG.warn("Response header too large");
LOG.debug(e);
}
else
LOG.warn(e);
// We were probably trying to generate a header, so let's make it a 500 instead
_persistent=false;
BufferUtil.clearToFill(header);
generateResponseLine(RESPONSE_500_INFO,header);
generateHeaders(RESPONSE_500_INFO,header,null,true);
_state=State.COMPLETING;
}
finally
{
BufferUtil.flipToFlush(header,pos);
}
return Result.FLUSH;
}
case COMMITTED:
{
int len = BufferUtil.length(content);
// handle the content.
if (len>0)
{
// Do we need a chunk buffer?
if (isChunking() && (headerOrChunk==null || headerOrChunk.capacity()>CHUNK_SIZE))
return Result.NEED_CHUNK;
ByteBuffer chunk = headerOrChunk;
_contentPrepared+=len;
if (isChunking())
{
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,len);
BufferUtil.flipToFlush(chunk,0);
}
}
if (last)
{
_state=State.COMPLETING;
return len>0?Result.FLUSH:Result.CONTINUE;
}
return len>0?Result.FLUSH:Result.DONE;
}
case COMPLETING_1XX:
{
reset();
return Result.DONE;
}
case COMPLETING:
{
if (BufferUtil.hasContent(content))
throw new IllegalStateException(); // Can't pass new content in COMPLETING state
if (isChunking())
{
// Do we need a chunk buffer?
if (headerOrChunk==null || headerOrChunk.capacity()>CHUNK_SIZE)
return Result.NEED_CHUNK;
ByteBuffer chunk=headerOrChunk;
// Write the last chunk
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,0);
BufferUtil.flipToFlush(chunk,0);
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
return Result.FLUSH;
}
_state=State.END;
return Boolean.TRUE.equals(_persistent)?Result.DONE:Result.SHUTDOWN_OUT;
}
case END:
if (BufferUtil.hasContent(content))
throw new IllegalStateException(); // Can't pass new content in END state
return Result.DONE;
default:
throw new IllegalStateException();
}
}
/* ------------------------------------------------------------ */
private void prepareChunk(ByteBuffer chunk, int remaining)
{
@ -585,13 +564,13 @@ public class HttpGenerator
case CONTENT_LENGTH:
// handle specially below
if (_info.getContentLength()>=0)
_content=EndOfContent.CONTENT_LENGTH;
_endOfContent=EndOfContent.CONTENT_LENGTH;
break;
case CONTENT_TYPE:
{
if (field.getValue().startsWith(MimeTypes.Type.MULTIPART_BYTERANGES.toString()))
_content=EndOfContent.SELF_DEFINING_CONTENT;
_endOfContent=EndOfContent.SELF_DEFINING_CONTENT;
// write the field to the header
content_type=true;
@ -647,8 +626,8 @@ public class HttpGenerator
if (_response!=null)
{
_persistent=false;
if (_content == EndOfContent.UNKNOWN_CONTENT)
_content=EndOfContent.EOF_CONTENT;
if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
_endOfContent=EndOfContent.EOF_CONTENT;
}
break;
}
@ -715,7 +694,7 @@ public class HttpGenerator
// 5. multipart/byteranges
// 6. close
int status=_response!=null?_response.getStatus():-1;
switch (_content)
switch (_endOfContent)
{
case UNKNOWN_CONTENT:
// It may be that we have no _content, or perhaps _content just has not been
@ -723,11 +702,11 @@ public class HttpGenerator
// Response known not to have a body
if (_contentPrepared == 0 && _response!=null && (status < 200 || status == 204 || status == 304))
_content=EndOfContent.NO_CONTENT;
_endOfContent=EndOfContent.NO_CONTENT;
else if (_info.getContentLength()>0)
{
// we have been given a content length
_content=EndOfContent.CONTENT_LENGTH;
_endOfContent=EndOfContent.CONTENT_LENGTH;
long content_length = _info.getContentLength();
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
{
@ -740,7 +719,7 @@ public class HttpGenerator
else if (last)
{
// we have seen all the _content there is, so we can be content-length limited.
_content=EndOfContent.CONTENT_LENGTH;
_endOfContent=EndOfContent.CONTENT_LENGTH;
long content_length = _contentPrepared+BufferUtil.length(content);
// Do we need to tell the headers about it
@ -754,10 +733,10 @@ public class HttpGenerator
else
{
// No idea, so we must assume that a body is coming
_content = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT;
if (_response!=null && _content==EndOfContent.EOF_CONTENT)
_endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT;
if (_response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
{
_content=EndOfContent.NO_CONTENT;
_endOfContent=EndOfContent.NO_CONTENT;
_noContent=true;
}
}
@ -808,7 +787,7 @@ public class HttpGenerator
}
// Handle connection if need be
if (_content==EndOfContent.EOF_CONTENT)
if (_endOfContent==EndOfContent.EOF_CONTENT)
{
keep_alive=false;
_persistent=false;

View File

@ -23,6 +23,8 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import org.eclipse.jetty.http.HttpGenerator.RequestInfo;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
@ -172,20 +174,25 @@ public class HttpTester
System.err.println(info);
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteBuffer header=BufferUtil.allocate(8192);
ByteBuffer buffer=BufferUtil.allocate(8192);
ByteBuffer chunk=BufferUtil.allocate(16);
ByteBuffer header=null;
ByteBuffer content=_content==null?null:ByteBuffer.wrap(_content.toByteArray());
loop: while(true)
loop: while(!generator.isEnd())
{
HttpGenerator.Result result = generator.generate(info,header,chunk,buffer,content,HttpGenerator.Action.COMPLETE);
HttpGenerator.Result result = info instanceof RequestInfo
?generator.generateRequest((RequestInfo)info,header,content,true)
:generator.generateResponse((ResponseInfo)info,header,content,true);
switch(result)
{
case NEED_BUFFER:
case NEED_HEADER:
header=BufferUtil.allocate(8192);
continue;
case NEED_CHUNK:
header=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case NEED_INFO:
throw new IllegalStateException();
@ -195,36 +202,13 @@ 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(buffer))
{
out.write(BufferUtil.toArray(buffer));
BufferUtil.clear(buffer);
}
break;
case FLUSH_CONTENT:
if (BufferUtil.hasContent(header))
{
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));
BufferUtil.clear(content);
}
break;
case OK:
case SHUTDOWN_OUT:
break loop;
}

View File

@ -26,7 +26,6 @@ import static org.junit.matchers.JUnitMatchers.containsString;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpGenerator.Action;
import org.eclipse.jetty.util.BufferUtil;
import org.junit.Test;
@ -55,86 +54,77 @@ public class HttpGeneratorClientTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,null,Action.COMPLETE);
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,gen.getState());
result=gen.generateRequest(null,null,null,true);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.START,gen.getState());
Info info = new Info("GET","/index.html");
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
assertTrue(!gen.isChunking());
result=gen.generate(info,header,null,null,null,null);
result=gen.generateRequest(info,null,null,true);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateRequest(info,header,null,true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertTrue(!gen.isChunking());
String head = BufferUtil.toString(header);
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
result=gen.generate(info,null,null,null,null,null);
assertEquals(HttpGenerator.Result.OK,result);
result=gen.generateResponse(null,null,null,false);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertTrue(!gen.isChunking());
assertEquals(0,gen.getContentPrepared());
assertThat(head,containsString("GET /index.html HTTP/1.1"));
assertThat(head,not(containsString("Content-Length")));
assertThat(out,containsString("GET /index.html HTTP/1.1"));
assertThat(out,not(containsString("Content-Length")));
}
@Test
public void testRequestWithSmallContent() throws Exception
public void testRequestWithContent() throws Exception
{
String body="";
String out;
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer buffer=BufferUtil.allocate(8096);
ByteBuffer content=BufferUtil.toBuffer("Hello World");
ByteBuffer content1=BufferUtil.toBuffer(". The quick brown fox jumped over the lazy dog.");
ByteBuffer content0=BufferUtil.toBuffer("Hello World. The quick brown fox jumped over the lazy dog.");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,content,null);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generate(null,null,null,buffer,content,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World",BufferUtil.toString(buffer));
assertTrue(BufferUtil.isEmpty(content));
result=gen.generate(null,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",BufferUtil.toString(buffer));
assertTrue(BufferUtil.isEmpty(content1));
result=gen.generate(null,null,null,buffer,null,Action.COMPLETE);
result=gen.generateRequest(null,null,content0,true);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,gen.getState());
assertEquals(HttpGenerator.State.START,gen.getState());
Info info = new Info("POST","/index.html");
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
result=gen.generate(info,header,null,buffer,null,null);
result=gen.generateRequest(info,null,content0,true);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateRequest(info,header,content0,true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
String head = BufferUtil.toString(header);
assertTrue(!gen.isChunking());
out = BufferUtil.toString(header);
BufferUtil.clear(header);
body += BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generate(info,null,null,buffer,null,null);
assertEquals(HttpGenerator.Result.OK,result);
result=gen.generateResponse(null,null,null,false);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertThat(head,containsString("POST /index.html HTTP/1.1"));
assertThat(head,containsString("Host: something"));
assertThat(head,containsString("Content-Length: 58"));
assertTrue(head.endsWith("\r\n\r\n"));
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",body);
assertTrue(!gen.isChunking());
assertThat(out,containsString("POST /index.html HTTP/1.1"));
assertThat(out,containsString("Host: something"));
assertThat(out,containsString("Content-Length: 58"));
assertThat(out,containsString("Hello World. The quick brown fox jumped over the lazy dog."));
assertEquals(58,gen.getContentPrepared());
}
@ -142,328 +132,132 @@ public class HttpGeneratorClientTest
@Test
public void testRequestWithChunkedContent() throws Exception
{
String body="";
String out;
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer buffer=BufferUtil.allocate(16);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
ByteBuffer content0=BufferUtil.toBuffer("Hello World. ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog.");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,content0,null);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generate(null,null,null,buffer,content0,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! ",BufferUtil.toString(buffer));
assertEquals(0,content0.remaining());
result=gen.generate(null,null,null,buffer,content1,null);
result=gen.generateRequest(null,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertEquals(HttpGenerator.State.START,gen.getState());
Info info = new Info("POST","/index.html");
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
result=gen.generate(info,header,null,buffer,content1,null);
result=gen.generateRequest(info,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateRequest(info,header,content0,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertTrue(gen.isChunking());
String head = BufferUtil.toString(header);
out = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generate(info,null,null,buffer,content1,null);
result=gen.generateRequest(null,header,content1,false);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
result=gen.generate(info,null,chunk,buffer,content1,null);
result=gen.generateRequest(null,chunk,content1,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
assertEquals(" quick brown fox",BufferUtil.toString(buffer));
assertEquals(27,content1.remaining());
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
assertTrue(gen.isChunking());
out+=BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.generate(info,null,chunk,buffer,content1,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
assertEquals(" jumped over the",BufferUtil.toString(buffer));
assertEquals(11,content1.remaining());
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.generate(info,null,chunk,buffer,content1,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("",BufferUtil.toString(chunk));
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
assertEquals(0,content1.remaining());
result=gen.generate(info,null,chunk,buffer,null,Action.COMPLETE);
out+=BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generateResponse(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);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertEquals("\r\nB\r\n",BufferUtil.toString(chunk));
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
out+=BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
assertTrue(!gen.isChunking());
result=gen.generate(info,null,chunk,buffer,null,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
result=gen.generateResponse(null,chunk,null,true);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals("\r\n0\r\n\r\n",BufferUtil.toString(chunk));
assertEquals(0,buffer.remaining());
BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
assertThat(out,containsString("POST /index.html HTTP/1.1"));
assertThat(out,containsString("Host: something"));
assertThat(out,containsString("Transfer-Encoding: chunked"));
assertThat(out,containsString("\r\nD\r\nHello World. \r\n"));
assertThat(out,containsString("\r\n2D\r\nThe quick brown fox jumped over the lazy dog.\r\n"));
assertThat(out,containsString("\r\n0\r\n\r\n"));
result=gen.generate(info,null,chunk,buffer,null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(59,gen.getContentPrepared());
// System.err.println(head+body);
assertThat(head,containsString("POST /index.html HTTP/1.1"));
assertThat(head,containsString("Host: something"));
assertThat(head,not(containsString("Content-Length")));
assertThat(head,containsString("Transfer-Encoding: chunked"));
assertTrue(head.endsWith("\r\n\r\n10\r\n"));
assertThat(body,containsString("dog"));
assertEquals(58,gen.getContentPrepared());
}
@Test
public void testRequestWithLargeChunkedContent() throws Exception
{
String body="";
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer content0=BufferUtil.toBuffer("Hello Cruel World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
gen.setLargeContent(8);
HttpGenerator.Result
result=gen.generate(null,null,null,null,content0,null);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
Info info = new Info("POST","/index.html");
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
result=gen.generate(info,header,null,null,content0,null);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generate(info,header,null,null,content0,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.generate(info,null,null,null,content1,null);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
result=gen.generate(info,null,chunk,null,content1,null);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n2E\r\n",BufferUtil.toString(chunk));
body+=BufferUtil.toString(chunk)+BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generate(info,null,chunk,null,null,Action.COMPLETE);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals("\r\n0\r\n\r\n",BufferUtil.toString(chunk));
BufferUtil.toString(chunk);
result=gen.generate(info,null,chunk,null,null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(65,gen.getContentPrepared());
// System.err.println(head+body);
assertThat(head,containsString("POST /index.html HTTP/1.1"));
assertThat(head,containsString("Host: something"));
assertThat(head,not(containsString("Content-Length")));
assertThat(head,containsString("Transfer-Encoding: chunked"));
assertTrue(head.endsWith("\r\n\r\n13\r\n"));
assertThat(body,containsString("dog"));
}
@Test
public void testRequestWithKnownContent() throws Exception
{
String body="";
String out;
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer buffer=BufferUtil.allocate(16);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
ByteBuffer content0=BufferUtil.toBuffer("Hello World. ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog.");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,content0,null);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
result=gen.generateRequest(null,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generate(null,null,null,buffer,content0,null);
assertEquals(HttpGenerator.Result.OK,result);
Info info = new Info("POST","/index.html",58);
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
result=gen.generateRequest(info,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! ",BufferUtil.toString(buffer));
assertEquals(0,content0.remaining());
result=gen.generate(null,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
Info info = new Info("POST","/index.html",59);
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
info.getHttpFields().add("Content-Length","59");
result=gen.generate(info,header,null,buffer,content1,null);
result=gen.generateRequest(info,header,content0,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertTrue(!gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals(" quick brown fox",BufferUtil.toString(buffer));
assertEquals(27,content1.remaining());
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals(" jumped over the",BufferUtil.toString(buffer));
assertEquals(11,content1.remaining());
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
assertEquals(0,content1.remaining());
result=gen.generate(info,null,null,buffer,null,Action.COMPLETE);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,buffer.remaining());
assertEquals(59,gen.getContentPrepared());
// System.err.println(head+body);
assertThat(head,containsString("POST /index.html HTTP/1.1"));
assertThat(head,containsString("Host: something"));
assertThat(head,containsString("Content-Length: 59"));
assertThat(head,not(containsString("chunked")));
assertTrue(head.endsWith("\r\n\r\n"));
assertThat(body,containsString("dog"));
}
@Test
public void testRequestWithKnownLargeContent() throws Exception
{
String body="";
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
gen.setLargeContent(8);
HttpGenerator.Result
result=gen.generate(null,null,null,null,content0,null);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
Info info = new Info("POST","/index.html",59);
info.getHttpFields().add("Host","something");
info.getHttpFields().add("User-Agent","test");
info.getHttpFields().add("Content-Length","59");
result=gen.generate(info,header,null,null,content0,null);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(!gen.isChunking());
String head = BufferUtil.toString(header);
out = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(content0);
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generate(info,header,null,null,null,null);
assertEquals(HttpGenerator.Result.OK,result);
result=gen.generateRequest(null,null,content1,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.generate(info,null,null,null,content1,null);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
body+=BufferUtil.toString(content1);
assertTrue(!gen.isChunking());
out+=BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generate(info,null,null,null,null,Action.COMPLETE);
assertEquals(HttpGenerator.Result.OK,result);
result=gen.generateResponse(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);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
out+=BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
assertThat(out,containsString("POST /index.html HTTP/1.1"));
assertThat(out,containsString("Host: something"));
assertThat(out,containsString("Content-Length: 58"));
assertThat(out,containsString("\r\n\r\nHello World. The quick brown fox jumped over the lazy dog."));
assertEquals(59,gen.getContentPrepared());
// System.err.println(head+body);
assertThat(head,containsString("POST /index.html HTTP/1.1"));
assertThat(head,containsString("Host: something"));
assertThat(head,containsString("Content-Length: 59"));
assertThat(head,not(containsString("chunked")));
assertTrue(head.endsWith("\r\n\r\n"));
assertThat(body,containsString("dog"));
assertEquals(58,gen.getContentPrepared());
}
}

View File

@ -32,7 +32,6 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.http.HttpGenerator.Action;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.Matchers;
@ -160,11 +159,10 @@ public class HttpGeneratorServerTest
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
}
ByteBuffer header=null;
ByteBuffer chunk=null;
ByteBuffer buffer=null;
HttpGenerator.Info info=null;
HttpGenerator.ResponseInfo info=null;
while(!gen.isComplete())
loop:
while(true)
{
// if we have unwritten content
if (source!=null && content!=null && content.remaining()==0 && c<nchunks)
@ -174,7 +172,7 @@ public class HttpGeneratorServerTest
}
// Generate
Action action=BufferUtil.hasContent(content)?null:Action.COMPLETE;
boolean last=!BufferUtil.hasContent(content);
/*
System.err.printf("generate(%s,%s,%s,%s,%s)@%s%n",
@ -184,7 +182,7 @@ public class HttpGeneratorServerTest
BufferUtil.toSummaryString(content),
action,gen.getState());
*/
HttpGenerator.Result result=gen.generate(info,header,chunk,buffer,content,action);
HttpGenerator.Result result=gen.generateResponse(info,header,content,last);
/*System.err.printf("%s (%s,%s,%s,%s,%s)@%s%n",
result,
BufferUtil.toSummaryString(header),
@ -197,20 +195,15 @@ public class HttpGeneratorServerTest
{
case NEED_INFO:
info=new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version),_fields,_contentLength,_code,reason,_head);
break;
continue;
case NEED_HEADER:
header=BufferUtil.allocate(2048);
break;
case NEED_BUFFER:
buffer=BufferUtil.allocate(8192);
break;
continue;
case NEED_CHUNK:
header=null;
chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
break;
header=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case FLUSH:
if (BufferUtil.hasContent(header))
@ -218,29 +211,6 @@ public class HttpGeneratorServerTest
response+=BufferUtil.toString(header);
header.position(header.limit());
}
else if (BufferUtil.hasContent(chunk))
{
response+=BufferUtil.toString(chunk);
chunk.position(chunk.limit());
}
if (BufferUtil.hasContent(buffer))
{
response+=BufferUtil.toString(buffer);
buffer.position(buffer.limit());
}
break;
case FLUSH_CONTENT:
if (BufferUtil.hasContent(header))
{
response+=BufferUtil.toString(header);
header.position(header.limit());
}
else if (BufferUtil.hasContent(chunk))
{
response+=BufferUtil.toString(chunk);
chunk.position(chunk.limit());
}
if (BufferUtil.hasContent(content))
{
response+=BufferUtil.toString(content);
@ -248,9 +218,14 @@ public class HttpGeneratorServerTest
}
break;
case OK:
case CONTINUE:
continue;
case SHUTDOWN_OUT:
// TODO
break;
case DONE:
break loop;
}
}
return response;
@ -376,20 +351,24 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,null,Action.COMPLETE);
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,gen.getState());
result=gen.generateResponse(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.generate(info,header,null,null,null,null);
result=gen.generateResponse(info,null,null,true);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
result=gen.generateResponse(info,header,null,true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
result=gen.generate(info,null,null,null,null,null);
assertEquals(HttpGenerator.Result.OK,result);
result=gen.generateResponse(null,null,null,false);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,gen.getContentPrepared());
@ -406,22 +385,23 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,null,Action.COMPLETE);
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,gen.getState());
result=gen.generateResponse(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,101,null,false);
info.getHttpFields().add("Upgrade","WebSocket");
info.getHttpFields().add("Connection","Upgrade");
info.getHttpFields().add("Sec-WebSocket-Accept","123456789==");
result=gen.generate(info,header,null,null,null,null);
result=gen.generateResponse(info,header,null,true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
result=gen.generate(info,null,null,null,null,null);
assertEquals(HttpGenerator.Result.OK,result);
result=gen.generateResponse(info,null,null,false);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,gen.getContentPrepared());
@ -434,441 +414,185 @@ public class HttpGeneratorServerTest
@Test
public void testResponseWithChunkedContent() throws Exception
{
String body="";
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer buffer=BufferUtil.allocate(16);
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,content0,null);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generate(null,null,null,buffer,content0,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! ",BufferUtil.toString(buffer));
assertEquals(0,content0.remaining());
result=gen.generate(null,null,null,buffer,content1,null);
result=gen.generateResponse(null,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
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.generate(info,header,null,buffer,content1,null);
result=gen.generateResponse(info,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateResponse(info,header,content0,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertTrue(gen.isChunking());
String head = BufferUtil.toString(header);
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,content1,null);
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generateResponse(null,header,content1,false);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
result=gen.generate(info,null,chunk,buffer,content1,null);
result=gen.generateResponse(null,chunk,content1,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
assertEquals(" quick brown fox",BufferUtil.toString(buffer));
assertEquals(27,content1.remaining());
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
out+=BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
out+=BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generate(info,null,chunk,buffer,content1,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
assertEquals(" jumped over the",BufferUtil.toString(buffer));
assertEquals(11,content1.remaining());
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.generateResponse(null,chunk,null,true);
assertEquals(HttpGenerator.Result.CONTINUE,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
result=gen.generate(info,null,chunk,buffer,content1,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("",BufferUtil.toString(chunk));
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
assertEquals(0,content1.remaining());
result=gen.generate(info,null,chunk,buffer,null,Action.COMPLETE);
result=gen.generateResponse(null,chunk,null,true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertEquals("\r\nB\r\n",BufferUtil.toString(chunk));
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.generate(info,null,chunk,buffer,null,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals("\r\n0\r\n\r\n",BufferUtil.toString(chunk));
assertEquals(0,buffer.remaining());
body+=BufferUtil.toString(chunk);
out+=BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
result=gen.generate(info,null,chunk,buffer,null,null);
assertEquals(HttpGenerator.Result.OK,result);
result=gen.generateResponse(null,chunk,null,true);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(59,gen.getContentPrepared());
// System.err.println(head+body);
assertThat(head,containsString("HTTP/1.1 200 OK"));
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(head,not(containsString("Content-Length")));
assertThat(head,containsString("Transfer-Encoding: chunked"));
assertTrue(head.endsWith("\r\n\r\n10\r\n"));
assertThat(out,containsString("HTTP/1.1 200 OK"));
assertThat(out,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(out,not(containsString("Content-Length")));
assertThat(out,containsString("Transfer-Encoding: chunked"));
assertThat(out,containsString("\r\n\r\nD\r\n"));
assertThat(out,containsString("\r\nHello World! \r\n"));
assertThat(out,containsString("\r\n2E\r\n"));
assertThat(out,containsString("\r\nThe quick brown fox jumped over the lazy dog. \r\n"));
assertThat(out,containsString("\r\n0\r\n"));
}
@Test
public void testResponseWithKnownContent() throws Exception
{
String body="";
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer buffer=BufferUtil.allocate(16);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,content0,null);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generate(null,null,null,buffer,content0,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! ",BufferUtil.toString(buffer));
assertEquals(0,content0.remaining());
result=gen.generate(null,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),59,200,null,false);
info.getHttpFields().add("Last-Modified",HttpFields.__01Jan1970);
info.getHttpFields().add("Content-Length","59");
result=gen.generate(info,header,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertTrue(!gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals(" quick brown fox",BufferUtil.toString(buffer));
assertEquals(27,content1.remaining());
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals(" jumped over the",BufferUtil.toString(buffer));
assertEquals(11,content1.remaining());
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
assertEquals(0,content1.remaining());
result=gen.generate(info,null,null,buffer,null,Action.COMPLETE);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,buffer.remaining());
assertEquals(59,gen.getContentPrepared());
// System.err.println(head+body);
assertThat(head,containsString("HTTP/1.1 200 OK"));
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(head,containsString("Content-Length: 59"));
assertThat(head,not(containsString("chunked")));
assertTrue(head.endsWith("\r\n\r\n"));
}
@Test
public void testResponseWithKnownLargeContent() throws Exception
{
String body="";
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
gen.setLargeContent(8);
HttpGenerator.Result
result=gen.generate(null,null,null,null,content0,null);
result=gen.generateResponse(null,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
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);
info.getHttpFields().add("Content-Length","59");
result=gen.generate(info,header,null,null,content0,null);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(!gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generate(info,header,null,null,null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.generate(info,null,null,null,content1,null);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
body+=BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generate(info,null,null,null,null,Action.COMPLETE);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(59,gen.getContentPrepared());
// System.err.println(head+body);
assertThat(head,containsString("HTTP/1.1 200 OK"));
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(head,containsString("Content-Length: 59"));
assertThat(head,not(containsString("chunked")));
assertTrue(head.endsWith("\r\n\r\n"));
}
@Test
public void testResponseWithLargeChunkedContent() throws Exception
{
String body="";
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer content0=BufferUtil.toBuffer("Hello Cruel World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
gen.setLargeContent(8);
HttpGenerator.Result
result=gen.generate(null,null,null,null,content0,null);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING,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.generate(info,header,null,null,content0,null);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generate(info,header,null,null,content0,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.generate(info,null,null,null,content1,null);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
result=gen.generate(info,null,chunk,null,content1,null);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n2E\r\n",BufferUtil.toString(chunk));
body+=BufferUtil.toString(chunk)+BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generate(info,null,chunk,null,null,Action.COMPLETE);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals("\r\n0\r\n\r\n",BufferUtil.toString(chunk));
body+=BufferUtil.toString(chunk);
result=gen.generate(info,null,chunk,null,null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(65,gen.getContentPrepared());
// System.err.println(head+body);
assertThat(head,containsString("HTTP/1.1 200 OK"));
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(head,not(containsString("Content-Length")));
assertThat(head,containsString("Transfer-Encoding: chunked"));
assertTrue(head.endsWith("\r\n\r\n13\r\n"));
}
@Test
public void testResponseWithSmallContent() throws Exception
{
String body="";
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer buffer=BufferUtil.allocate(8096);
ByteBuffer content=BufferUtil.toBuffer("Hello World");
ByteBuffer content1=BufferUtil.toBuffer(". The quick brown fox jumped over the lazy dog.");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(null,null,null,null,content,null);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generate(null,null,null,buffer,content,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World",BufferUtil.toString(buffer));
assertTrue(BufferUtil.isEmpty(content));
result=gen.generate(null,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",BufferUtil.toString(buffer));
assertTrue(BufferUtil.isEmpty(content1));
result=gen.generate(null,null,null,buffer,null,Action.COMPLETE);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,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.generate(info,header,null,buffer,null,null);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertThat(head,containsString("HTTP/1.1 200 OK"));
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(head,containsString("Content-Length: 58"));
assertTrue(head.endsWith("\r\n\r\n"));
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",body);
assertEquals(58,gen.getContentPrepared());
}
@Test
public void test100ThenResponseWithSmallContent() throws Exception
{
String body="";
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer buffer=BufferUtil.allocate(8096);
ByteBuffer content=BufferUtil.toBuffer("Hello World");
ByteBuffer content1=BufferUtil.toBuffer(". The quick brown fox jumped over the lazy dog.");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generate(HttpGenerator.CONTINUE_100_INFO,null,null,null,null,Action.COMPLETE);
result=gen.generateResponse(info,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,gen.getState());
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateResponse(info,header,content0,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.generate(HttpGenerator.CONTINUE_100_INFO,header,null,null,null,Action.COMPLETE);
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generateResponse(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);
assertEquals(HttpGenerator.Result.CONTINUE,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
result=gen.generateResponse(null,null,null,true);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertThat(out,containsString("HTTP/1.1 200 OK"));
assertThat(out,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(out,not(containsString("chunked")));
assertThat(out,containsString("Content-Length: 59"));
assertThat(out,containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
}
@Test
public void test100ThenResponseWithContent() throws Exception
{
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result
result=gen.generateResponse(HttpGenerator.CONTINUE_100_INFO,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);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING_1XX,gen.getState());
assertThat(BufferUtil.toString(header),Matchers.startsWith("HTTP/1.1 100 Continue"));
BufferUtil.clear(header);
result=gen.generate(null,null,null,null,null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
String out = BufferUtil.toString(header);
result=gen.generate(null,null,null,null,content,null);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
result=gen.generateResponse(null,null,null,false);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generate(null,null,null,buffer,content,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World",BufferUtil.toString(buffer));
assertTrue(BufferUtil.isEmpty(content));
assertThat(out,containsString("HTTP/1.1 100 Continue"));
result=gen.generate(null,null,null,buffer,content1,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",BufferUtil.toString(buffer));
assertTrue(BufferUtil.isEmpty(content1));
result=gen.generate(null,null,null,buffer,null,Action.COMPLETE);
result=gen.generateResponse(null,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_INFO,result);
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,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(),59,200,null,false);
info.getHttpFields().add("Last-Modified",HttpFields.__01Jan1970);
result=gen.generate(info,header,null,buffer,null,null);
result=gen.generateResponse(info,null,content0,false);
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.generateResponse(info,header,content0,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
out = BufferUtil.toString(header);
BufferUtil.clear(header);
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generateResponse(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);
assertEquals(HttpGenerator.Result.CONTINUE,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
body+=BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.generate(info,null,null,buffer,null,null);
assertEquals(HttpGenerator.Result.OK,result);
result=gen.generateResponse(null,null,null,true);
assertEquals(HttpGenerator.Result.DONE,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertThat(head,containsString("HTTP/1.1 200 OK"));
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(head,containsString("Content-Length: 58"));
assertTrue(head.endsWith("\r\n\r\n"));
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",body);
assertEquals(58,gen.getContentPrepared());
assertThat(out,containsString("HTTP/1.1 200 OK"));
assertThat(out,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
assertThat(out,not(containsString("chunked")));
assertThat(out,containsString("Content-Length: 59"));
assertThat(out,containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
}
}

View File

@ -301,7 +301,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint
shutdownInput();
if (_ishut)
return -1;
int filled=BufferUtil.append(_in,buffer);
int filled=BufferUtil.flipPutFlip(_in,buffer);
if (filled>0)
notIdle();
return filled;
@ -331,12 +331,12 @@ public class ByteArrayEndPoint extends AbstractEndPoint
if (b.remaining()>BufferUtil.space(_out))
{
ByteBuffer n = BufferUtil.allocate(_out.capacity()+b.remaining()*2);
BufferUtil.append(_out,n);
BufferUtil.flipPutFlip(_out,n);
_out=n;
}
}
flushed+=BufferUtil.append(b,_out);
flushed+=BufferUtil.flipPutFlip(b,_out);
if (BufferUtil.hasContent(b))
break;

View File

@ -407,7 +407,7 @@ public class SslConnection extends AbstractConnection
{
// Do we already have some decrypted data?
if (BufferUtil.hasContent(_decryptedInput))
return BufferUtil.append(_decryptedInput, buffer);
return BufferUtil.flipPutFlip(_decryptedInput, buffer);
// We will need a network buffer
if (_encryptedInput == null)
@ -502,7 +502,7 @@ public class SslConnection extends AbstractConnection
{
if (app_in == buffer)
return unwrapResult.bytesProduced();
return BufferUtil.append(_decryptedInput, buffer);
return BufferUtil.flipPutFlip(_decryptedInput, buffer);
}
// Dang! we have to care about the handshake state

View File

@ -163,7 +163,7 @@ public class SelectChannelEndPointTest
}
// Copy to the out buffer
if (BufferUtil.hasContent(_in) && BufferUtil.append(_in, _out) > 0)
if (BufferUtil.hasContent(_in) && BufferUtil.flipPutFlip(_in, _out) > 0)
progress = true;
// Blocking writes

View File

@ -20,9 +20,11 @@ package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import javax.servlet.DispatcherType;
import javax.servlet.ServletException;
@ -46,6 +48,7 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.io.UncheckedPrintWriter;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.log.Log;
@ -197,7 +200,18 @@ public abstract class HttpChannel
{
if (_response.isCommitted())
throw new IllegalStateException("Committed before 100 Continues");
commitResponse(HttpGenerator.CONTINUE_100_INFO,null);
try
{
write(HttpGenerator.CONTINUE_100_INFO,null).get();
}
catch (final InterruptedException e)
{
throw new InterruptedIOException(){{this.initCause(e);}};
}
catch (final ExecutionException e)
{
throw new IOException(){{this.initCause(e);}};
}
}
_expect100Continue=false;
}
@ -359,12 +373,24 @@ public abstract class HttpChannel
}
HttpGenerator.ResponseInfo info = _handler.commit();
commitResponse(info,buffer);
try
{
write(info,buffer).get();
}
catch (final InterruptedException e)
{
throw new InterruptedIOException(){{this.initCause(e);}};
}
catch (final ExecutionException e)
{
throw new IOException(){{this.initCause(e);}};
}
return true;
}
catch(Exception e)
{
e.printStackTrace();
LOG.debug("failed to sendError {} {}",status, reason, e);
}
finally
@ -697,7 +723,7 @@ public abstract class HttpChannel
// Process content.
if (content instanceof ByteBuffer)
{
commitResponse(_handler.commit(),(ByteBuffer)content);
HttpChannel.this.write(_handler.commit(),(ByteBuffer)content);
}
else if (content instanceof InputStream)
{
@ -712,20 +738,9 @@ public abstract class HttpChannel
public abstract HttpConfiguration getHttpConfiguration();
protected abstract int write(ByteBuffer content) throws IOException;
protected abstract void write(ByteBuffer content,boolean last) throws IOException;
/* Called by the channel or application to commit a specific response info */
protected abstract void commitResponse(ResponseInfo info, ByteBuffer content) throws IOException;
protected abstract int getContentBufferSize();
protected abstract void increaseContentBufferSize(int size);
protected abstract void resetBuffer();
protected abstract void flushResponse() throws IOException;
protected abstract void completeResponse() throws IOException;
protected abstract FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException;
protected abstract void completed();

View File

@ -25,7 +25,6 @@ import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpGenerator.Action;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.http.HttpParser;
import org.eclipse.jetty.http.HttpStatus;
@ -47,9 +46,15 @@ public class HttpConnection extends AbstractConnection
private static final Logger LOG = Log.getLogger(HttpConnection.class);
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
private static final FutureCallback<Void> __completed = new FutureCallback<>();
static
{
__completed.completed(null);
}
public static final String UPGRADE_CONNECTION_ATTR = "org.eclispe.jetty.server.HttpConnection.UPGRADE";
private final Server _server;
private final HttpConfiguration _httpConfig;
private final Connector _connector;
@ -59,11 +64,10 @@ public class HttpConnection extends AbstractConnection
private final ByteBufferPool _bufferPool;
private final HttpHttpInput _httpInput;
private ResponseInfo _info;
ByteBuffer _requestBuffer=null;
ByteBuffer _responseHeader=null;
ByteBuffer _chunk=null;
ByteBuffer _responseBuffer=null;
private int _headerBytes;
@ -146,16 +150,6 @@ public class HttpConnection extends AbstractConnection
_channel.reset();
_httpInput.recycle();
releaseRequestBuffer();
if (_responseHeader!=null && !_responseHeader.hasRemaining())
{
_bufferPool.release(_responseHeader);
_responseHeader=null;
}
if (_responseBuffer!=null && !_responseBuffer.hasRemaining())
{
_bufferPool.release(_responseBuffer);
_responseBuffer=null;
}
if (_chunk!=null)
_bufferPool.release(_chunk);
_chunk=null;
@ -339,46 +333,6 @@ public class HttpConnection extends AbstractConnection
super(server,HttpConnection.this,_httpInput);
}
@Override
protected int write(ByteBuffer content) throws IOException
{
return generate(content,Action.PREPARE);
}
@Override
protected void resetBuffer()
{
if (_responseBuffer!=null)
BufferUtil.clear(_responseBuffer);
}
@Override
protected void increaseContentBufferSize(int size)
{
if (_responseBuffer!=null && _responseBuffer.capacity()>=size)
return;
if (_responseBuffer==null && _httpConfig.getResponseBufferSize()>=size)
return;
ByteBuffer r=_bufferPool.acquire(size,false);
if (_responseBuffer!=null)
{
BufferUtil.append(_responseBuffer,r);
_bufferPool.release(_responseBuffer);
}
_responseBuffer=r;
}
@Override
protected int getContentBufferSize()
{
ByteBuffer buffer=_responseBuffer;
if (buffer!=null)
return buffer.capacity();
return _httpConfig.getResponseBufferSize();
}
public Connector getConnector()
{
return _connector;
@ -389,19 +343,6 @@ public class HttpConnection extends AbstractConnection
return _httpConfig;
}
@Override
protected void flushResponse() throws IOException
{
generate(null,Action.FLUSH);
}
@Override
protected void completeResponse() throws IOException
{
generate(null,Action.COMPLETE);
}
@Override
protected boolean commitError(int status, String reason, String content)
{
@ -411,9 +352,6 @@ public class HttpConnection extends AbstractConnection
// We could not send the error, so a shutdown of the connection will at least tell
// the client something is wrong
if (BufferUtil.hasContent(_responseBuffer))
BufferUtil.clear(_responseBuffer);
getEndPoint().shutdownOutput();
_generator.abort();
return false;
@ -488,8 +426,7 @@ public class HttpConnection extends AbstractConnection
getEndPoint().shutdownOutput();
}
}
// make sure that an oshut connection is driven towards close
// TODO this is a little ugly
if (getEndPoint().isOpen() && getEndPoint().isOutputShutdown())
@ -506,33 +443,33 @@ public class HttpConnection extends AbstractConnection
}
/* ------------------------------------------------------------ */
private int generate(ByteBuffer content, Action action) throws IOException
@Override
public void write(ByteBuffer content, boolean last) throws IOException
{
// Only one response writer at a time.
synchronized(this)
{
long prepared_before=0;
long prepared_after;
ByteBuffer header=null;
try
{
if (_generator.isComplete())
if (_generator.isEnd())
{
if (Action.COMPLETE==action)
return 0;
// TODO do we need this escape?
if (last && BufferUtil.isEmpty(content))
return;
throw new EofException();
}
prepared_before=_generator.getContentPrepared();
loop: while (true)
{
HttpGenerator.Result result=_generator.generate(_info,_responseHeader,_chunk,_responseBuffer,content,action);
HttpGenerator.Result result=_generator.generateResponse(_info,header,content,last);
if (LOG.isDebugEnabled())
LOG.debug("{} generate: {} ({},{},{})@{}",
this,
result,
BufferUtil.toSummaryString(_responseHeader),
BufferUtil.toSummaryString(_responseBuffer),
BufferUtil.toSummaryString(header),
BufferUtil.toSummaryString(content),
last,
_generator.getState());
switch(result)
@ -540,53 +477,37 @@ public class HttpConnection extends AbstractConnection
case NEED_INFO:
if (_info==null)
_info=_channel.getEventHandler().commit();
LOG.debug("{} Gcommit {}",this,_info);
if (_responseHeader==null)
_responseHeader=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
continue;
case NEED_HEADER:
_responseHeader=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
continue;
case NEED_BUFFER:
_responseBuffer=_bufferPool.acquire(_httpConfig.getResponseBufferSize(),false);
if (header!=null)
_bufferPool.release(header);
header=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
continue;
case NEED_CHUNK:
_responseHeader=null;
_chunk=_bufferPool.acquire(HttpGenerator.CHUNK_SIZE,false);
if (header!=null)
_bufferPool.release(header);
header=_bufferPool.acquire(HttpGenerator.CHUNK_SIZE,false);
continue;
case FLUSH:
if (_info.isHead())
{
if (_chunk!=null)
BufferUtil.clear(_chunk);
if (_responseBuffer!=null)
BufferUtil.clear(_responseBuffer);
write(header,null).get();
BufferUtil.clear(content);
}
write(_responseHeader,_chunk,_responseBuffer).get();
else
write(header,content).get();
continue;
case FLUSH_CONTENT:
if (_info.isHead())
{
if (_chunk!=null)
BufferUtil.clear(_chunk);
if (_responseBuffer!=null)
BufferUtil.clear(content);
}
write(_responseHeader,_chunk,content).get();
break;
case SHUTDOWN_OUT:
terminate();
getEndPoint().shutdownOutput();
continue;
case DONE:
break loop;
case OK:
if (!BufferUtil.hasContent(content))
break loop;
}
}
}
@ -603,124 +524,84 @@ public class HttpConnection extends AbstractConnection
}
finally
{
prepared_after=_generator.getContentPrepared();
if (header!=null)
_bufferPool.release(header);
}
return (int)(prepared_after-prepared_before);
}
}
@Override
protected void commitResponse(ResponseInfo info, ByteBuffer content) throws IOException
protected FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException
{
// Only one response writer at a time.
synchronized (this)
synchronized(this)
{
_info=info;
LOG.debug("{} commit {}",this,_info);
ByteBuffer header=null;
try
{
if (_generator.isCommitted())
throw new IllegalStateException("committed");
if (BufferUtil.hasContent(_responseBuffer))
{
if (LOG.isDebugEnabled())
LOG.debug("discarding uncommitted response {}",BufferUtil.toDetailString(_responseBuffer));
BufferUtil.clear(_responseBuffer);
}
if (_generator.isComplete())
if (_generator.isEnd())
throw new EofException();
FutureCallback<Void> fcb=null;
loop: while (true)
{
HttpGenerator.Result result=_generator.generate(_info,_responseHeader,null,_responseBuffer,content,Action.COMPLETE);
HttpGenerator.Result result=_generator.generateResponse(info,header,content,true);
if (LOG.isDebugEnabled())
LOG.debug("{} commit: {} ({},{},{})@{}",
LOG.debug("{} send: {} ({},{})@{}",
this,
result,
BufferUtil.toDetailString(_responseHeader),
BufferUtil.toSummaryString(_responseBuffer),
BufferUtil.toSummaryString(header),
BufferUtil.toSummaryString(content),
_generator.getState());
switch(result)
{
case NEED_INFO:
_info=_channel.getEventHandler().commit();
if (_responseHeader==null)
_responseHeader=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
break;
throw new IllegalStateException();
case NEED_HEADER:
_responseHeader=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
break;
case NEED_BUFFER:
_responseBuffer=_bufferPool.acquire(_httpConfig.getResponseBufferSize(),false);
break;
if (header!=null)
_bufferPool.release(header);
header=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
continue;
case NEED_CHUNK:
throw new IllegalStateException("!chunk when content length known");
if (header!=null)
_bufferPool.release(header);
header=_bufferPool.acquire(HttpGenerator.CHUNK_SIZE,false);
continue;
case FLUSH:
if (_info.isHead())
if(info.isHead())
{
if (_chunk!=null)
BufferUtil.clear(_chunk);
if (_responseBuffer!=null)
BufferUtil.clear(_responseBuffer);
BufferUtil.clear(content);
fcb=write(header,null);
}
write(_responseHeader,_chunk,_responseBuffer).get();
break;
case FLUSH_CONTENT:
if (_info.isHead())
{
if (_chunk!=null)
BufferUtil.clear(_chunk);
if (_responseBuffer!=null)
BufferUtil.clear(content);
}
// TODO need a proper call back to complete.
write(_responseHeader,_chunk,content);
break loop;
else
fcb=write(header,content);
continue;
case SHUTDOWN_OUT:
terminate();
break loop;
case OK:
if (_info!=null && _info.isInformational())
_info=null;
getEndPoint().shutdownOutput();
continue;
case DONE:
if (fcb==null)
fcb=__completed;
break loop;
}
}
return fcb;
}
catch(InterruptedException e)
finally
{
LOG.debug(e);
}
catch(ExecutionException e)
{
LOG.debug(e);
FutureCallback.rethrow(e);
if (header!=null)
_bufferPool.release(header);
}
}
}
private void terminate()
{
// This method is called when the generator determines that the connection should be closed
// either for HTTP/1.0 or because of Connection headers.
// We need to close gently, first shutting down the output, so that we can send the SSL close
// message, and then closing without waiting to read -1, since the semantic of this method
// is exactly that the connection should be closed: no further reads must be accepted.
EndPoint endPoint = getEndPoint();
endPoint.shutdownOutput();
endPoint.close();
}
@Override
public ScheduledExecutorService getScheduler()
{
@ -733,41 +614,29 @@ public class HttpConnection extends AbstractConnection
_connector.getExecutor().execute(task);
}
private FutureCallback<Void> write(ByteBuffer b0,ByteBuffer b1,ByteBuffer b2)
private FutureCallback<Void> write(ByteBuffer b0,ByteBuffer b1)
{
FutureCallback<Void> fcb=new FutureCallback<>();
if (BufferUtil.hasContent(b0))
{
if (BufferUtil.hasContent(b1))
{
if (BufferUtil.hasContent(b2))
getEndPoint().write(null,fcb,b0,b1,b2);
else
getEndPoint().write(null,fcb,b0,b1);
getEndPoint().write(null,fcb,b0,b1);
}
else
{
if (BufferUtil.hasContent(b2))
getEndPoint().write(null,fcb,b0,b2);
else
getEndPoint().write(null,fcb,b0);
getEndPoint().write(null,fcb,b0);
}
}
else
{
if (BufferUtil.hasContent(b1))
{
if (BufferUtil.hasContent(b2))
getEndPoint().write(null,fcb,b1,b2);
else
getEndPoint().write(null,fcb,b1);
getEndPoint().write(null,fcb,b1);
}
else
{
if (BufferUtil.hasContent(b2))
getEndPoint().write(null,fcb,b2);
else
fcb.completed(null);
fcb.completed(null);
}
}
return fcb;

View File

@ -25,6 +25,7 @@ import java.nio.ByteBuffer;
import javax.servlet.ServletOutputStream;
import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.ByteArrayOutputStream2;
/** Output.
@ -41,6 +42,7 @@ public class HttpOutput extends ServletOutputStream
private final HttpChannel _channel;
private boolean _closed;
private long _written;
private ByteBuffer _aggregate;
/* ------------------------------------------------------------ */
public HttpOutput(HttpChannel channel)
@ -81,8 +83,18 @@ public class HttpOutput extends ServletOutputStream
public void close() throws IOException
{
if (!_closed)
_channel.completeResponse();
{
if (BufferUtil.hasContent(_aggregate))
_channel.write(_aggregate,!_channel.getResponse().isIncluding());
else
_channel.write(BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding());
}
_closed=true;
if (_aggregate!=null)
{
_channel.getConnector().getByteBufferPool().release(_aggregate);
_aggregate=null;
}
}
/* ------------------------------------------------------------ */
@ -95,13 +107,19 @@ public class HttpOutput extends ServletOutputStream
@Override
public void flush() throws IOException
{
_channel.flushResponse();
if (_closed)
throw new EofException();
if (BufferUtil.hasContent(_aggregate))
_channel.write(_aggregate,false);
else
_channel.write(BufferUtil.EMPTY_BUFFER,false);
}
/* ------------------------------------------------------------ */
public void checkAllWritten()
public boolean checkAllWritten()
{
_channel.getResponse().checkAllContentWritten(_written);
return _channel.getResponse().checkAllContentWritten(_written);
}
/* ------------------------------------------------------------ */
@ -110,23 +128,52 @@ public class HttpOutput extends ServletOutputStream
{
if (_closed)
throw new EofException();
// Do we have an aggregate buffer already
if (_aggregate==null)
{
// what size should the aggregate be?
int size=_channel.getHttpConfiguration().getResponseBufferSize();
// if this write would fill more than half the aggregate, just write it directory
if (len>size/2)
{
_channel.write(ByteBuffer.wrap(b,off,len),false);
_written+=len;
return;
}
// allocate an aggregate buffer
_aggregate=_channel.getConnector().getByteBufferPool().acquire(size,false);
}
// Do we have space to aggregate?
int space = BufferUtil.space(_aggregate);
if (len>space)
{
// No space so write the aggregate out if it is not empty
if (BufferUtil.hasContent(_aggregate))
{
_channel.write(_aggregate,false);
space=BufferUtil.space(_aggregate);
}
}
_written+=_channel.write(ByteBuffer.wrap(b,off,len));
checkAllWritten();
}
// Do we now have space to aggregate?
if (len>space)
{
// No space so write the content directly
_channel.write(ByteBuffer.wrap(b,off,len),false);
_written+=len;
return;
}
// aggregate the content
BufferUtil.append(_aggregate,b,off,len);
/* ------------------------------------------------------------ */
/*
* @see java.io.OutputStream#write(byte[])
*/
@Override
public void write(byte[] b) throws IOException
{
if (_closed)
throw new IOException("Closed");
_written+=_channel.write(ByteBuffer.wrap(b));
checkAllWritten();
// Check if all written or full
if (!checkAllWritten() && BufferUtil.isFull(_aggregate))
_channel.write(_aggregate,false);
}
/* ------------------------------------------------------------ */
@ -139,8 +186,15 @@ public class HttpOutput extends ServletOutputStream
if (_closed)
throw new IOException("Closed");
_written+=_channel.write(ByteBuffer.wrap(new byte[]{(byte)b}));
checkAllWritten();
if (_aggregate==null)
_aggregate=_channel.getConnector().getByteBufferPool().acquire(_channel.getHttpConfiguration().getResponseBufferSize(),false);
BufferUtil.append(_aggregate,(byte)b);
_written++;
// Check if all written or full
if (!checkAllWritten() && BufferUtil.isFull(_aggregate))
_channel.write(_aggregate,false);
}
/* ------------------------------------------------------------ */
@ -158,5 +212,35 @@ public class HttpOutput extends ServletOutputStream
{
throw new IllegalStateException("Not implemented");
}
/* ------------------------------------------------------------ */
public int getContentBufferSize()
{
if (_aggregate!=null)
return _aggregate.capacity();
return _channel.getHttpConfiguration().getResponseBufferSize();
}
/* ------------------------------------------------------------ */
public void increaseContentBufferSize(int size)
{
if (_aggregate==null || size<=getContentBufferSize())
return;
ByteBuffer r=_channel.getConnector().getByteBufferPool().acquire(size,false);
if (BufferUtil.hasContent(_aggregate))
BufferUtil.flipPutFlip(_aggregate,r);
if (_aggregate!=null)
_channel.getConnector().getByteBufferPool().release(_aggregate);
_aggregate=r;
}
/* ------------------------------------------------------------ */
public void resetBuffer()
{
if (BufferUtil.hasContent(_aggregate))
BufferUtil.clear(_aggregate);
}
}

View File

@ -19,11 +19,13 @@
package org.eclipse.jetty.server;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.PrintWriter;
import java.nio.channels.IllegalSelectorException;
import java.util.Collection;
import java.util.Collections;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
@ -454,7 +456,20 @@ public class Response implements HttpServletResponse
public void sendProcessing() throws IOException
{
if (_channel.isExpecting102Processing() && !isCommitted())
_channel.commitResponse(HttpGenerator.PROGRESS_102_INFO,null);
{
try
{
_channel.write(HttpGenerator.PROGRESS_102_INFO,null).get();
}
catch (final InterruptedException e)
{
throw new InterruptedIOException(){{this.initCause(e);}};
}
catch (final ExecutionException e)
{
throw new IOException(){{this.initCause(e);}};
}
}
}
/* ------------------------------------------------------------ */
@ -847,7 +862,7 @@ public class Response implements HttpServletResponse
}
/* ------------------------------------------------------------ */
public void checkAllContentWritten(long written)
public boolean checkAllContentWritten(long written)
{
if (_contentLength>=0 && written>=_contentLength)
{
@ -867,7 +882,9 @@ public class Response implements HttpServletResponse
{
throw new RuntimeException(e);
}
return true;
}
return false;
}
/* ------------------------------------------------------------ */
@ -1007,7 +1024,7 @@ public class Response implements HttpServletResponse
{
if (isCommitted() || getContentCount()>0 )
throw new IllegalStateException("Committed or content written");
_channel.increaseContentBufferSize(size);
_out.increaseContentBufferSize(size);
}
/* ------------------------------------------------------------ */
@ -1017,7 +1034,7 @@ public class Response implements HttpServletResponse
@Override
public int getBufferSize()
{
return _channel.getContentBufferSize();
return _out.getContentBufferSize();
}
/* ------------------------------------------------------------ */
@ -1027,7 +1044,7 @@ public class Response implements HttpServletResponse
@Override
public void flushBuffer() throws IOException
{
_channel.flushResponse();
_out.flush();
}
/* ------------------------------------------------------------ */
@ -1104,7 +1121,7 @@ public class Response implements HttpServletResponse
_out.reset();
}
_channel.resetBuffer();
_out.resetBuffer();
}
/* ------------------------------------------------------------ */
@ -1193,7 +1210,7 @@ public class Response implements HttpServletResponse
public void complete()
throws IOException
{
_channel.completeResponse();
_out.close();
}
/* ------------------------------------------------------------ */

View File

@ -18,14 +18,10 @@
package org.eclipse.jetty.server;
import org.eclipse.jetty.util.log.Log;
public class ChannelHttpServer
{
public static void main(String[] s) throws Exception
{
System.setProperty("org.eclipse.jetty.LEVEL","DEBUG");
Log.getRootLogger().setDebugEnabled(true);
Server server = new Server();
SelectChannelConnector connector = new SelectChannelConnector(server);
connector.setPort(8080);

View File

@ -26,6 +26,7 @@ import java.util.concurrent.ScheduledExecutorService;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.FutureCallback;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.Utf8StringBuilder;
@ -50,44 +51,6 @@ public class HttpWriterTest
return null;
}
@Override
protected int write(ByteBuffer content) throws IOException
{
return BufferUtil.append(content,_bytes);
}
@Override
protected void commitResponse(ResponseInfo info, ByteBuffer content) throws IOException
{
}
@Override
protected int getContentBufferSize()
{
return 0;
}
@Override
protected void increaseContentBufferSize(int size)
{
}
@Override
protected void resetBuffer()
{
BufferUtil.clear(_bytes);
}
@Override
protected void flushResponse() throws IOException
{
}
@Override
protected void completeResponse() throws IOException
{
}
@Override
protected void completed()
{
@ -111,6 +74,21 @@ public class HttpWriterTest
return null;
}
@Override
protected void write(ByteBuffer content, boolean last) throws IOException
{
BufferUtil.flipPutFlip(content,_bytes);
}
@Override
protected FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException
{
BufferUtil.flipPutFlip(content,_bytes);
FutureCallback<Void> fcb = new FutureCallback<>();
fcb.completed(null);
return fcb;
}
};
_httpOut = new HttpOutput(channel);

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.server;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertThat;
import org.eclipse.jetty.util.log.Log;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -35,6 +36,7 @@ public class LocalConnectorTest
{
_server = new Server();
_connector = new LocalConnector(_server);
_connector.setIdleTimeout(60000);
_server.addConnector(_connector);
_server.setHandler(new DumpHandler());
_server.start();
@ -51,9 +53,8 @@ public class LocalConnectorTest
@Test
public void testOneGET() throws Exception
{
{
String response=_connector.getResponses("GET /R1 HTTP/1.0\r\n\r\n");
assertThat(response,containsString("HTTP/1.1 200 OK"));
assertThat(response,containsString("pathInfo=/R1"));
}

View File

@ -217,18 +217,16 @@ public class RequestTest
_server.start();
// Request with illegal Host header
String request="GET / HTTP/1.1\r\n"+
"Host: whatever.com:\r\n"+
String request="GET / HTTP/1.1\n"+
"Host: whatever.com:\n"+
"Content-Type: text/html;charset=utf8\n"+
"Connection: close\n"+
"\n";
String responses=_connector.getResponses(request);
assertTrue("400 Bad Request response expected",responses.startsWith("HTTP/1.1 400"));
assertThat(responses,Matchers.startsWith("HTTP/1.1 400"));
}
@Test
public void testContentTypeEncoding() throws Exception
{

View File

@ -52,6 +52,7 @@ import org.eclipse.jetty.server.handler.ContextHandler;
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.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
@ -106,21 +107,18 @@ public class ResponseTest
_channel = new HttpChannel(_server,connection,input)
{
@Override
protected int write(ByteBuffer content) throws IOException
{
int length=content.remaining();
protected void write(ByteBuffer content, boolean last) throws IOException
{
content.clear();
return length;
}
@Override
protected void resetBuffer()
{
}
@Override
protected void increaseContentBufferSize(int size)
protected FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException
{
content.clear();
FutureCallback<Void> fcb = new FutureCallback<>();
fcb.completed(null);
return fcb;
}
@Override
@ -135,19 +133,6 @@ public class ResponseTest
return null;
}
@Override
protected int getContentBufferSize()
{
return 0;
}
@Override
protected void flushResponse() throws IOException
{
if (!_channel.getResponse().isCommitted())
_channel.getResponse().commit();
}
@Override
protected void execute(Runnable task)
{
@ -158,16 +143,6 @@ public class ResponseTest
{
}
@Override
protected void completeResponse() throws IOException
{
}
@Override
protected void commitResponse(ResponseInfo info, ByteBuffer content) throws IOException
{
}
@Override
public Connector getConnector()
{

View File

@ -306,7 +306,7 @@ public class BufferUtil
* @param to Buffer to put bytes to in flush mode. The buffer is flipToFill before the put and flipToFlush after.
* @return number of bytes moved
*/
public static int append(ByteBuffer from, ByteBuffer to)
public static int flipPutFlip(ByteBuffer from, ByteBuffer to)
{
int pos= flipToFill(to);
try
@ -318,6 +318,32 @@ public class BufferUtil
flipToFlush(to,pos);
}
}
/* ------------------------------------------------------------ */
/**
*/
public static void append(ByteBuffer to, byte[] b,int off,int len)
{
int pos= flipToFill(to);
try
{
to.put(b,off,len);
}
finally
{
flipToFlush(to,pos);
}
}
/* ------------------------------------------------------------ */
/**
*/
public static void append(ByteBuffer to, byte b)
{
int limit=to.limit();
to.put(limit,b);
to.limit(limit+1);
}
/* ------------------------------------------------------------ */
public static void readFrom(File file, ByteBuffer buffer) throws IOException

View File

@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicReference;
//TODO: Simplify, get rid of DOING. Probably replace states with AtomicBoolean
public class FutureCallback<C> implements Future<C>,Callback<C>
{
// TODO investigate use of a phasor
private enum State {NOT_DONE,DOING,DONE};
private final AtomicReference<State> _state=new AtomicReference<>(State.NOT_DONE);
private CountDownLatch _done= new CountDownLatch(1);
@ -39,17 +38,6 @@ public class FutureCallback<C> implements Future<C>,Callback<C>
private C _context;
private boolean _completed;
private void recycle()
{
// TODO make this public?
if (!isDone())
throw new IllegalStateException();
_cause=null;
_context=null;
_completed=false;
_done=new CountDownLatch(1);
}
@Override
public void completed(C context)
{

View File

@ -132,14 +132,14 @@ public class BufferUtilTest
ByteBuffer from=BufferUtil.toBuffer("12345");
BufferUtil.clear(to);
assertEquals(5,BufferUtil.append(from,to));
assertEquals(5,BufferUtil.flipPutFlip(from,to));
assertTrue(BufferUtil.isEmpty(from));
assertEquals("12345",BufferUtil.toString(to));
from=BufferUtil.toBuffer("XX67890ZZ");
from.position(2);
assertEquals(5,BufferUtil.append(from,to));
assertEquals(5,BufferUtil.flipPutFlip(from,to));
assertEquals(2,from.remaining());
assertEquals("1234567890",BufferUtil.toString(to));
}
@ -151,14 +151,14 @@ public class BufferUtilTest
ByteBuffer from=BufferUtil.toBuffer("12345");
BufferUtil.clear(to);
assertEquals(5,BufferUtil.append(from,to));
assertEquals(5,BufferUtil.flipPutFlip(from,to));
assertTrue(BufferUtil.isEmpty(from));
assertEquals("12345",BufferUtil.toString(to));
from=BufferUtil.toBuffer("XX67890ZZ");
from.position(2);
assertEquals(5,BufferUtil.append(from,to));
assertEquals(5,BufferUtil.flipPutFlip(from,to));
assertEquals(2,from.remaining());
assertEquals("1234567890",BufferUtil.toString(to));
}