jetty-9 work in progress on moving aggregation out of HttpChannel
This commit is contained in:
parent
9080882900
commit
325bfc290d
|
@ -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);
|
new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(){{put(HttpHeader.CONNECTION,HttpHeaderValue.CLOSE);}},0,HttpStatus.INTERNAL_SERVER_ERROR_500,null,false);
|
||||||
|
|
||||||
// states
|
// states
|
||||||
public enum Action { FLUSH, COMPLETE, PREPARE }
|
public enum State { START, COMMITTED, COMPLETING, COMPLETING_1XX, END }
|
||||||
public enum State { START, COMMITTING, COMMITTING_COMPLETING, COMMITTED, COMPLETING, COMPLETING_1XX, END }
|
public enum Result { NEED_CHUNK,NEED_INFO,NEED_HEADER,FLUSH,CONTINUE,SHUTDOWN_OUT,DONE}
|
||||||
public enum Result { NEED_CHUNK,NEED_INFO,NEED_HEADER,NEED_BUFFER,FLUSH,FLUSH_CONTENT,OK,SHUTDOWN_OUT}
|
|
||||||
|
|
||||||
// other statics
|
// other statics
|
||||||
public static final int CHUNK_SIZE = 12;
|
public static final int CHUNK_SIZE = 12;
|
||||||
|
|
||||||
|
|
||||||
private State _state = State.START;
|
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 long _contentPrepared = 0;
|
||||||
private boolean _noContent = false;
|
private boolean _noContent = false;
|
||||||
private Boolean _persistent = null;
|
private Boolean _persistent = null;
|
||||||
|
@ -79,7 +76,7 @@ public class HttpGenerator
|
||||||
public void reset()
|
public void reset()
|
||||||
{
|
{
|
||||||
_state = State.START;
|
_state = State.START;
|
||||||
_content = EndOfContent.UNKNOWN_CONTENT;
|
_endOfContent = EndOfContent.UNKNOWN_CONTENT;
|
||||||
_noContent=false;
|
_noContent=false;
|
||||||
_persistent = null;
|
_persistent = null;
|
||||||
_contentPrepared = 0;
|
_contentPrepared = 0;
|
||||||
|
@ -118,7 +115,7 @@ public class HttpGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public boolean isComplete()
|
public boolean isEnd()
|
||||||
{
|
{
|
||||||
return _state == State.END;
|
return _state == State.END;
|
||||||
}
|
}
|
||||||
|
@ -132,19 +129,7 @@ public class HttpGenerator
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public boolean isChunking()
|
public boolean isChunking()
|
||||||
{
|
{
|
||||||
return _content==EndOfContent.CHUNKED_CONTENT;
|
return _endOfContent==EndOfContent.CHUNKED_CONTENT;
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public int getLargeContent()
|
|
||||||
{
|
|
||||||
return _largeContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
|
||||||
public void setLargeContent(int largeContent)
|
|
||||||
{
|
|
||||||
_largeContent = largeContent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -179,298 +164,292 @@ public class HttpGenerator
|
||||||
{
|
{
|
||||||
_persistent=false;
|
_persistent=false;
|
||||||
_state=State.END;
|
_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;
|
switch(_state)
|
||||||
if (_state==State.END)
|
|
||||||
return result;
|
|
||||||
if (action==null)
|
|
||||||
action=Action.PREPARE;
|
|
||||||
|
|
||||||
// Do we have content to handle
|
|
||||||
if (BufferUtil.hasContent(content))
|
|
||||||
{
|
{
|
||||||
// Do we have too much content?
|
case START:
|
||||||
if (_content==EndOfContent.CONTENT_LENGTH && info!=null && info.getContentLength()>=0 && content.remaining()>(info.getContentLength()-_contentPrepared))
|
|
||||||
{
|
{
|
||||||
LOG.warn("Content truncated. Info.getContentLength()=="+info.getContentLength()+" prepared="+_contentPrepared+" content="+content.remaining(),new Throwable());
|
if (info==null)
|
||||||
content.limit(content.position()+(int)(info.getContentLength()-_contentPrepared));
|
return Result.NEED_INFO;
|
||||||
}
|
|
||||||
|
|
||||||
// Can we do a direct flush
|
// Do we need a request header
|
||||||
if (BufferUtil.isEmpty(buffer) && content.remaining()>_largeContent)
|
if (headerOrChunk==null || headerOrChunk.capacity()<=CHUNK_SIZE)
|
||||||
{
|
return Result.NEED_HEADER;
|
||||||
if (isCommitted())
|
|
||||||
|
// 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())
|
// generate ResponseLine
|
||||||
{
|
generateRequestLine(info,header);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
_state=action==Action.COMPLETE?State.COMMITTING_COMPLETING:State.COMMITTING;
|
if (info.getHttpVersion()==HttpVersion.HTTP_0_9)
|
||||||
result=Result.FLUSH_CONTENT;
|
_noContent=true;
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
|
generateHeaders(info,header,content,last);
|
||||||
|
|
||||||
|
// handle the content.
|
||||||
|
int len = BufferUtil.length(content);
|
||||||
|
if (len>0)
|
||||||
{
|
{
|
||||||
// Responses
|
_contentPrepared+=len;
|
||||||
|
if (isChunking())
|
||||||
// Do we need a response header?
|
prepareChunk(header,len);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
_state = last?State.COMPLETING:State.COMMITTED;
|
||||||
boolean completing=action==Action.COMPLETE||_state==State.COMMITTING_COMPLETING;
|
}
|
||||||
generateHeaders(info,header,content,completing);
|
catch(Exception e)
|
||||||
_state = completing?State.COMPLETING:State.COMMITTED;
|
{
|
||||||
|
if (e instanceof BufferOverflowException)
|
||||||
// handle result
|
LOG.warn("Response header too large");
|
||||||
switch(result)
|
throw e;
|
||||||
{
|
}
|
||||||
case FLUSH:
|
finally
|
||||||
if (isChunking())
|
{
|
||||||
prepareChunk(header,buffer.remaining());
|
BufferUtil.flipToFlush(header,pos);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Result.FLUSH;
|
||||||
case COMMITTED:
|
}
|
||||||
return Result.OK;
|
|
||||||
|
case COMMITTED:
|
||||||
|
{
|
||||||
case COMPLETING:
|
int len = BufferUtil.length(content);
|
||||||
// handle content with commit
|
|
||||||
|
if (len>0)
|
||||||
|
{
|
||||||
|
// Do we need a chunk buffer?
|
||||||
if (isChunking())
|
if (isChunking())
|
||||||
{
|
{
|
||||||
if (chunk==null)
|
// Do we need a chunk buffer?
|
||||||
|
if (headerOrChunk==null || headerOrChunk.capacity()>CHUNK_SIZE)
|
||||||
return Result.NEED_CHUNK;
|
return Result.NEED_CHUNK;
|
||||||
|
ByteBuffer chunk = headerOrChunk;
|
||||||
BufferUtil.clearToFill(chunk);
|
BufferUtil.clearToFill(chunk);
|
||||||
|
prepareChunk(chunk,len);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BufferUtil.flipToFlush(chunk,0);
|
BufferUtil.flipToFlush(chunk,0);
|
||||||
}
|
}
|
||||||
else if (result==Result.OK)
|
_contentPrepared+=len;
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
LOG.warn(e);
|
if (last)
|
||||||
_state=State.COMPLETING;
|
{
|
||||||
// We were probably trying to generate a header, so let's make it a 500 instead
|
_state=State.COMPLETING;
|
||||||
header.clear();
|
return len>0?Result.FLUSH:Result.CONTINUE;
|
||||||
_persistent=false;
|
}
|
||||||
generateResponseLine(RESPONSE_500_INFO,header);
|
|
||||||
generateHeaders(RESPONSE_500_INFO,header,null,true);
|
return len>0?Result.FLUSH:Result.DONE;
|
||||||
if (buffer!=null)
|
|
||||||
BufferUtil.clear(buffer);
|
|
||||||
if (chunk!=null)
|
|
||||||
BufferUtil.clear(chunk);
|
|
||||||
if (content!=null)
|
|
||||||
BufferUtil.clear(content);
|
|
||||||
return Result.FLUSH;
|
|
||||||
}
|
}
|
||||||
throw e;
|
|
||||||
}
|
case COMPLETING:
|
||||||
finally
|
{
|
||||||
{
|
if (BufferUtil.hasContent(content))
|
||||||
if (pos>=0)
|
throw new IllegalStateException(); // Can't pass new content in COMPLETING state
|
||||||
BufferUtil.flipToFlush(header,pos);
|
|
||||||
|
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)
|
private void prepareChunk(ByteBuffer chunk, int remaining)
|
||||||
{
|
{
|
||||||
|
@ -585,13 +564,13 @@ public class HttpGenerator
|
||||||
case CONTENT_LENGTH:
|
case CONTENT_LENGTH:
|
||||||
// handle specially below
|
// handle specially below
|
||||||
if (_info.getContentLength()>=0)
|
if (_info.getContentLength()>=0)
|
||||||
_content=EndOfContent.CONTENT_LENGTH;
|
_endOfContent=EndOfContent.CONTENT_LENGTH;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CONTENT_TYPE:
|
case CONTENT_TYPE:
|
||||||
{
|
{
|
||||||
if (field.getValue().startsWith(MimeTypes.Type.MULTIPART_BYTERANGES.toString()))
|
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
|
// write the field to the header
|
||||||
content_type=true;
|
content_type=true;
|
||||||
|
@ -647,8 +626,8 @@ public class HttpGenerator
|
||||||
if (_response!=null)
|
if (_response!=null)
|
||||||
{
|
{
|
||||||
_persistent=false;
|
_persistent=false;
|
||||||
if (_content == EndOfContent.UNKNOWN_CONTENT)
|
if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
|
||||||
_content=EndOfContent.EOF_CONTENT;
|
_endOfContent=EndOfContent.EOF_CONTENT;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -715,7 +694,7 @@ public class HttpGenerator
|
||||||
// 5. multipart/byteranges
|
// 5. multipart/byteranges
|
||||||
// 6. close
|
// 6. close
|
||||||
int status=_response!=null?_response.getStatus():-1;
|
int status=_response!=null?_response.getStatus():-1;
|
||||||
switch (_content)
|
switch (_endOfContent)
|
||||||
{
|
{
|
||||||
case UNKNOWN_CONTENT:
|
case UNKNOWN_CONTENT:
|
||||||
// It may be that we have no _content, or perhaps _content just has not been
|
// 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
|
// Response known not to have a body
|
||||||
if (_contentPrepared == 0 && _response!=null && (status < 200 || status == 204 || status == 304))
|
if (_contentPrepared == 0 && _response!=null && (status < 200 || status == 204 || status == 304))
|
||||||
_content=EndOfContent.NO_CONTENT;
|
_endOfContent=EndOfContent.NO_CONTENT;
|
||||||
else if (_info.getContentLength()>0)
|
else if (_info.getContentLength()>0)
|
||||||
{
|
{
|
||||||
// we have been given a content length
|
// we have been given a content length
|
||||||
_content=EndOfContent.CONTENT_LENGTH;
|
_endOfContent=EndOfContent.CONTENT_LENGTH;
|
||||||
long content_length = _info.getContentLength();
|
long content_length = _info.getContentLength();
|
||||||
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
|
if ((_response!=null || content_length>0 || content_type ) && !_noContent)
|
||||||
{
|
{
|
||||||
|
@ -740,7 +719,7 @@ public class HttpGenerator
|
||||||
else if (last)
|
else if (last)
|
||||||
{
|
{
|
||||||
// we have seen all the _content there is, so we can be content-length limited.
|
// 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);
|
long content_length = _contentPrepared+BufferUtil.length(content);
|
||||||
|
|
||||||
// Do we need to tell the headers about it
|
// Do we need to tell the headers about it
|
||||||
|
@ -754,10 +733,10 @@ public class HttpGenerator
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// No idea, so we must assume that a body is coming
|
// 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;
|
_endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT;
|
||||||
if (_response!=null && _content==EndOfContent.EOF_CONTENT)
|
if (_response!=null && _endOfContent==EndOfContent.EOF_CONTENT)
|
||||||
{
|
{
|
||||||
_content=EndOfContent.NO_CONTENT;
|
_endOfContent=EndOfContent.NO_CONTENT;
|
||||||
_noContent=true;
|
_noContent=true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -808,7 +787,7 @@ public class HttpGenerator
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle connection if need be
|
// Handle connection if need be
|
||||||
if (_content==EndOfContent.EOF_CONTENT)
|
if (_endOfContent==EndOfContent.EOF_CONTENT)
|
||||||
{
|
{
|
||||||
keep_alive=false;
|
keep_alive=false;
|
||||||
_persistent=false;
|
_persistent=false;
|
||||||
|
|
|
@ -23,6 +23,8 @@ import java.io.IOException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.Charset;
|
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.io.RuntimeIOException;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
@ -172,20 +174,25 @@ public class HttpTester
|
||||||
System.err.println(info);
|
System.err.println(info);
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
ByteBuffer header=BufferUtil.allocate(8192);
|
ByteBuffer header=null;
|
||||||
ByteBuffer buffer=BufferUtil.allocate(8192);
|
|
||||||
ByteBuffer chunk=BufferUtil.allocate(16);
|
|
||||||
ByteBuffer content=_content==null?null:ByteBuffer.wrap(_content.toByteArray());
|
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)
|
switch(result)
|
||||||
{
|
{
|
||||||
case NEED_BUFFER:
|
|
||||||
case NEED_HEADER:
|
case NEED_HEADER:
|
||||||
|
header=BufferUtil.allocate(8192);
|
||||||
|
continue;
|
||||||
|
|
||||||
case NEED_CHUNK:
|
case NEED_CHUNK:
|
||||||
|
header=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
||||||
|
continue;
|
||||||
|
|
||||||
case NEED_INFO:
|
case NEED_INFO:
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
|
|
||||||
|
@ -195,36 +202,13 @@ public class HttpTester
|
||||||
out.write(BufferUtil.toArray(header));
|
out.write(BufferUtil.toArray(header));
|
||||||
BufferUtil.clear(header);
|
BufferUtil.clear(header);
|
||||||
}
|
}
|
||||||
if (BufferUtil.hasContent(chunk))
|
|
||||||
{
|
|
||||||
out.write(BufferUtil.toArray(chunk));
|
|
||||||
BufferUtil.clear(chunk);
|
|
||||||
}
|
|
||||||
if (BufferUtil.hasContent(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))
|
if (BufferUtil.hasContent(content))
|
||||||
{
|
{
|
||||||
out.write(BufferUtil.toArray(content));
|
out.write(BufferUtil.toArray(content));
|
||||||
BufferUtil.clear(content);
|
BufferUtil.clear(content);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case OK:
|
|
||||||
case SHUTDOWN_OUT:
|
case SHUTDOWN_OUT:
|
||||||
break loop;
|
break loop;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ import static org.junit.matchers.JUnitMatchers.containsString;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpGenerator.Action;
|
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -55,86 +54,77 @@ public class HttpGeneratorClientTest
|
||||||
HttpGenerator gen = new HttpGenerator();
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
HttpGenerator.Result
|
HttpGenerator.Result
|
||||||
result=gen.generate(null,null,null,null,null,Action.COMPLETE);
|
result=gen.generateRequest(null,null,null,true);
|
||||||
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,gen.getState());
|
|
||||||
assertEquals(HttpGenerator.Result.NEED_INFO,result);
|
assertEquals(HttpGenerator.Result.NEED_INFO,result);
|
||||||
|
assertEquals(HttpGenerator.State.START,gen.getState());
|
||||||
|
|
||||||
Info info = new Info("GET","/index.html");
|
Info info = new Info("GET","/index.html");
|
||||||
info.getHttpFields().add("Host","something");
|
info.getHttpFields().add("Host","something");
|
||||||
info.getHttpFields().add("User-Agent","test");
|
info.getHttpFields().add("User-Agent","test");
|
||||||
assertTrue(!gen.isChunking());
|
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.Result.FLUSH,result);
|
||||||
|
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
||||||
assertTrue(!gen.isChunking());
|
assertTrue(!gen.isChunking());
|
||||||
String head = BufferUtil.toString(header);
|
String out = BufferUtil.toString(header);
|
||||||
BufferUtil.clear(header);
|
BufferUtil.clear(header);
|
||||||
|
|
||||||
result=gen.generate(info,null,null,null,null,null);
|
result=gen.generateResponse(null,null,null,false);
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
assertEquals(HttpGenerator.Result.DONE,result);
|
||||||
assertEquals(HttpGenerator.State.END,gen.getState());
|
assertEquals(HttpGenerator.State.END,gen.getState());
|
||||||
assertTrue(!gen.isChunking());
|
assertTrue(!gen.isChunking());
|
||||||
|
|
||||||
assertEquals(0,gen.getContentPrepared());
|
assertEquals(0,gen.getContentPrepared());
|
||||||
assertThat(head,containsString("GET /index.html HTTP/1.1"));
|
assertThat(out,containsString("GET /index.html HTTP/1.1"));
|
||||||
assertThat(head,not(containsString("Content-Length")));
|
assertThat(out,not(containsString("Content-Length")));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRequestWithSmallContent() throws Exception
|
public void testRequestWithContent() throws Exception
|
||||||
{
|
{
|
||||||
String body="";
|
String out;
|
||||||
ByteBuffer header=BufferUtil.allocate(4096);
|
ByteBuffer header=BufferUtil.allocate(4096);
|
||||||
ByteBuffer buffer=BufferUtil.allocate(8096);
|
ByteBuffer content0=BufferUtil.toBuffer("Hello World. The quick brown fox jumped over the lazy dog.");
|
||||||
ByteBuffer content=BufferUtil.toBuffer("Hello World");
|
|
||||||
ByteBuffer content1=BufferUtil.toBuffer(". The quick brown fox jumped over the lazy dog.");
|
|
||||||
HttpGenerator gen = new HttpGenerator();
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
|
|
||||||
HttpGenerator.Result
|
HttpGenerator.Result
|
||||||
|
result=gen.generateRequest(null,null,content0,true);
|
||||||
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.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 info = new Info("POST","/index.html");
|
||||||
info.getHttpFields().add("Host","something");
|
info.getHttpFields().add("Host","something");
|
||||||
info.getHttpFields().add("User-Agent","test");
|
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.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
||||||
|
assertTrue(!gen.isChunking());
|
||||||
String head = BufferUtil.toString(header);
|
out = BufferUtil.toString(header);
|
||||||
BufferUtil.clear(header);
|
BufferUtil.clear(header);
|
||||||
body += BufferUtil.toString(buffer);
|
out+=BufferUtil.toString(content0);
|
||||||
BufferUtil.clear(buffer);
|
BufferUtil.clear(content0);
|
||||||
|
|
||||||
result=gen.generate(info,null,null,buffer,null,null);
|
result=gen.generateResponse(null,null,null,false);
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
assertEquals(HttpGenerator.Result.DONE,result);
|
||||||
assertEquals(HttpGenerator.State.END,gen.getState());
|
assertEquals(HttpGenerator.State.END,gen.getState());
|
||||||
|
assertTrue(!gen.isChunking());
|
||||||
assertThat(head,containsString("POST /index.html HTTP/1.1"));
|
|
||||||
assertThat(head,containsString("Host: something"));
|
|
||||||
assertThat(head,containsString("Content-Length: 58"));
|
assertThat(out,containsString("POST /index.html HTTP/1.1"));
|
||||||
assertTrue(head.endsWith("\r\n\r\n"));
|
assertThat(out,containsString("Host: something"));
|
||||||
|
assertThat(out,containsString("Content-Length: 58"));
|
||||||
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",body);
|
assertThat(out,containsString("Hello World. The quick brown fox jumped over the lazy dog."));
|
||||||
|
|
||||||
assertEquals(58,gen.getContentPrepared());
|
assertEquals(58,gen.getContentPrepared());
|
||||||
}
|
}
|
||||||
|
@ -142,328 +132,132 @@ public class HttpGeneratorClientTest
|
||||||
@Test
|
@Test
|
||||||
public void testRequestWithChunkedContent() throws Exception
|
public void testRequestWithChunkedContent() throws Exception
|
||||||
{
|
{
|
||||||
String body="";
|
String out;
|
||||||
ByteBuffer header=BufferUtil.allocate(4096);
|
ByteBuffer header=BufferUtil.allocate(4096);
|
||||||
ByteBuffer buffer=BufferUtil.allocate(16);
|
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
||||||
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
|
ByteBuffer content0=BufferUtil.toBuffer("Hello World. ");
|
||||||
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog.");
|
||||||
HttpGenerator gen = new HttpGenerator();
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
|
|
||||||
HttpGenerator.Result
|
HttpGenerator.Result
|
||||||
|
result=gen.generateRequest(null,null,content0,false);
|
||||||
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.Result.NEED_INFO,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
|
assertEquals(HttpGenerator.State.START,gen.getState());
|
||||||
assertEquals("Hello World! The",BufferUtil.toString(buffer));
|
|
||||||
assertEquals(43,content1.remaining());
|
|
||||||
|
|
||||||
Info info = new Info("POST","/index.html");
|
Info info = new Info("POST","/index.html");
|
||||||
info.getHttpFields().add("Host","something");
|
info.getHttpFields().add("Host","something");
|
||||||
info.getHttpFields().add("User-Agent","test");
|
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.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
||||||
assertEquals("Hello World! The",BufferUtil.toString(buffer));
|
|
||||||
assertEquals(43,content1.remaining());
|
|
||||||
assertTrue(gen.isChunking());
|
assertTrue(gen.isChunking());
|
||||||
|
out = BufferUtil.toString(header);
|
||||||
String head = BufferUtil.toString(header);
|
|
||||||
BufferUtil.clear(header);
|
BufferUtil.clear(header);
|
||||||
body+=BufferUtil.toString(buffer);
|
out+=BufferUtil.toString(content0);
|
||||||
BufferUtil.clear(buffer);
|
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.Result.NEED_CHUNK,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
||||||
|
|
||||||
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
result=gen.generateRequest(null,chunk,content1,false);
|
||||||
result=gen.generate(info,null,chunk,buffer,content1,null);
|
|
||||||
assertEquals(HttpGenerator.Result.FLUSH,result);
|
assertEquals(HttpGenerator.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
||||||
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
|
assertTrue(gen.isChunking());
|
||||||
assertEquals(" quick brown fox",BufferUtil.toString(buffer));
|
out+=BufferUtil.toString(chunk);
|
||||||
assertEquals(27,content1.remaining());
|
|
||||||
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
|
|
||||||
BufferUtil.clear(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);
|
result=gen.generateResponse(null,chunk,null,true);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.Result.CONTINUE,result);
|
||||||
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
|
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
||||||
assertEquals(" jumped over the",BufferUtil.toString(buffer));
|
assertTrue(gen.isChunking());
|
||||||
assertEquals(11,content1.remaining());
|
|
||||||
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
|
result=gen.generateResponse(null,chunk,null,true);
|
||||||
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);
|
|
||||||
assertEquals(HttpGenerator.Result.FLUSH,result);
|
assertEquals(HttpGenerator.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
||||||
assertEquals("\r\nB\r\n",BufferUtil.toString(chunk));
|
out+=BufferUtil.toString(chunk);
|
||||||
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
|
|
||||||
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
|
|
||||||
BufferUtil.clear(chunk);
|
BufferUtil.clear(chunk);
|
||||||
BufferUtil.clear(buffer);
|
assertTrue(!gen.isChunking());
|
||||||
|
|
||||||
result=gen.generate(info,null,chunk,buffer,null,null);
|
result=gen.generateResponse(null,chunk,null,true);
|
||||||
assertEquals(HttpGenerator.Result.FLUSH,result);
|
assertEquals(HttpGenerator.Result.DONE,result);
|
||||||
assertEquals(HttpGenerator.State.END,gen.getState());
|
assertEquals(HttpGenerator.State.END,gen.getState());
|
||||||
assertEquals("\r\n0\r\n\r\n",BufferUtil.toString(chunk));
|
|
||||||
assertEquals(0,buffer.remaining());
|
assertThat(out,containsString("POST /index.html HTTP/1.1"));
|
||||||
BufferUtil.toString(chunk);
|
assertThat(out,containsString("Host: something"));
|
||||||
BufferUtil.clear(chunk);
|
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(58,gen.getContentPrepared());
|
||||||
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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@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
|
@Test
|
||||||
public void testRequestWithKnownContent() throws Exception
|
public void testRequestWithKnownContent() throws Exception
|
||||||
{
|
{
|
||||||
String body="";
|
String out;
|
||||||
ByteBuffer header=BufferUtil.allocate(4096);
|
ByteBuffer header=BufferUtil.allocate(4096);
|
||||||
ByteBuffer buffer=BufferUtil.allocate(16);
|
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
||||||
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
|
ByteBuffer content0=BufferUtil.toBuffer("Hello World. ");
|
||||||
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog.");
|
||||||
HttpGenerator gen = new HttpGenerator();
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
HttpGenerator.Result
|
HttpGenerator.Result
|
||||||
|
result=gen.generateRequest(null,null,content0,false);
|
||||||
result=gen.generate(null,null,null,null,content0,null);
|
assertEquals(HttpGenerator.Result.NEED_INFO,result);
|
||||||
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
|
|
||||||
assertEquals(HttpGenerator.State.START,gen.getState());
|
assertEquals(HttpGenerator.State.START,gen.getState());
|
||||||
|
|
||||||
result=gen.generate(null,null,null,buffer,content0,null);
|
Info info = new Info("POST","/index.html",58);
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
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(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(info,header,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());
|
|
||||||
|
|
||||||
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);
|
|
||||||
assertEquals(HttpGenerator.Result.FLUSH,result);
|
assertEquals(HttpGenerator.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
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());
|
assertTrue(!gen.isChunking());
|
||||||
|
out = BufferUtil.toString(header);
|
||||||
String head = BufferUtil.toString(header);
|
|
||||||
BufferUtil.clear(header);
|
BufferUtil.clear(header);
|
||||||
body+=BufferUtil.toString(content0);
|
out+=BufferUtil.toString(content0);
|
||||||
BufferUtil.clear(content0);
|
BufferUtil.clear(content0);
|
||||||
|
|
||||||
result=gen.generate(info,header,null,null,null,null);
|
result=gen.generateRequest(null,null,content1,false);
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
assertEquals(HttpGenerator.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
||||||
|
assertTrue(!gen.isChunking());
|
||||||
result=gen.generate(info,null,null,null,content1,null);
|
out+=BufferUtil.toString(content1);
|
||||||
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
|
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
|
||||||
body+=BufferUtil.toString(content1);
|
|
||||||
BufferUtil.clear(content1);
|
BufferUtil.clear(content1);
|
||||||
|
|
||||||
result=gen.generate(info,null,null,null,null,Action.COMPLETE);
|
result=gen.generateResponse(null,null,null,true);
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
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());
|
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());
|
assertEquals(58,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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,7 +32,6 @@ import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpGenerator.Action;
|
|
||||||
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
@ -160,11 +159,10 @@ public class HttpGeneratorServerTest
|
||||||
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
|
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
|
||||||
}
|
}
|
||||||
ByteBuffer header=null;
|
ByteBuffer header=null;
|
||||||
ByteBuffer chunk=null;
|
HttpGenerator.ResponseInfo info=null;
|
||||||
ByteBuffer buffer=null;
|
|
||||||
HttpGenerator.Info info=null;
|
|
||||||
|
|
||||||
while(!gen.isComplete())
|
loop:
|
||||||
|
while(true)
|
||||||
{
|
{
|
||||||
// if we have unwritten content
|
// if we have unwritten content
|
||||||
if (source!=null && content!=null && content.remaining()==0 && c<nchunks)
|
if (source!=null && content!=null && content.remaining()==0 && c<nchunks)
|
||||||
|
@ -174,7 +172,7 @@ public class HttpGeneratorServerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate
|
// 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",
|
System.err.printf("generate(%s,%s,%s,%s,%s)@%s%n",
|
||||||
|
@ -184,7 +182,7 @@ public class HttpGeneratorServerTest
|
||||||
BufferUtil.toSummaryString(content),
|
BufferUtil.toSummaryString(content),
|
||||||
action,gen.getState());
|
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",
|
/*System.err.printf("%s (%s,%s,%s,%s,%s)@%s%n",
|
||||||
result,
|
result,
|
||||||
BufferUtil.toSummaryString(header),
|
BufferUtil.toSummaryString(header),
|
||||||
|
@ -197,20 +195,15 @@ public class HttpGeneratorServerTest
|
||||||
{
|
{
|
||||||
case NEED_INFO:
|
case NEED_INFO:
|
||||||
info=new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version),_fields,_contentLength,_code,reason,_head);
|
info=new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version),_fields,_contentLength,_code,reason,_head);
|
||||||
break;
|
continue;
|
||||||
|
|
||||||
case NEED_HEADER:
|
case NEED_HEADER:
|
||||||
header=BufferUtil.allocate(2048);
|
header=BufferUtil.allocate(2048);
|
||||||
break;
|
continue;
|
||||||
|
|
||||||
case NEED_BUFFER:
|
|
||||||
buffer=BufferUtil.allocate(8192);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NEED_CHUNK:
|
case NEED_CHUNK:
|
||||||
header=null;
|
header=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
||||||
chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
continue;
|
||||||
break;
|
|
||||||
|
|
||||||
case FLUSH:
|
case FLUSH:
|
||||||
if (BufferUtil.hasContent(header))
|
if (BufferUtil.hasContent(header))
|
||||||
|
@ -218,29 +211,6 @@ public class HttpGeneratorServerTest
|
||||||
response+=BufferUtil.toString(header);
|
response+=BufferUtil.toString(header);
|
||||||
header.position(header.limit());
|
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))
|
if (BufferUtil.hasContent(content))
|
||||||
{
|
{
|
||||||
response+=BufferUtil.toString(content);
|
response+=BufferUtil.toString(content);
|
||||||
|
@ -248,9 +218,14 @@ public class HttpGeneratorServerTest
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OK:
|
case CONTINUE:
|
||||||
|
continue;
|
||||||
|
|
||||||
case SHUTDOWN_OUT:
|
case SHUTDOWN_OUT:
|
||||||
// TODO
|
break;
|
||||||
|
|
||||||
|
case DONE:
|
||||||
|
break loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return response;
|
return response;
|
||||||
|
@ -376,20 +351,24 @@ public class HttpGeneratorServerTest
|
||||||
HttpGenerator gen = new HttpGenerator();
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
HttpGenerator.Result
|
HttpGenerator.Result
|
||||||
result=gen.generate(null,null,null,null,null,Action.COMPLETE);
|
result=gen.generateResponse(null,null,null,true);
|
||||||
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,gen.getState());
|
|
||||||
assertEquals(HttpGenerator.Result.NEED_INFO,result);
|
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);
|
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),-1,200,null,false);
|
||||||
info.getHttpFields().add("Last-Modified",HttpFields.__01Jan1970);
|
info.getHttpFields().add("Last-Modified",HttpFields.__01Jan1970);
|
||||||
|
|
||||||
result=gen.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.Result.FLUSH,result);
|
||||||
|
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
||||||
String head = BufferUtil.toString(header);
|
String head = BufferUtil.toString(header);
|
||||||
BufferUtil.clear(header);
|
BufferUtil.clear(header);
|
||||||
|
|
||||||
result=gen.generate(info,null,null,null,null,null);
|
result=gen.generateResponse(null,null,null,false);
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
assertEquals(HttpGenerator.Result.DONE,result);
|
||||||
assertEquals(HttpGenerator.State.END,gen.getState());
|
assertEquals(HttpGenerator.State.END,gen.getState());
|
||||||
|
|
||||||
assertEquals(0,gen.getContentPrepared());
|
assertEquals(0,gen.getContentPrepared());
|
||||||
|
@ -406,22 +385,23 @@ public class HttpGeneratorServerTest
|
||||||
HttpGenerator gen = new HttpGenerator();
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
HttpGenerator.Result
|
HttpGenerator.Result
|
||||||
result=gen.generate(null,null,null,null,null,Action.COMPLETE);
|
result=gen.generateResponse(null,null,null,true);
|
||||||
assertEquals(HttpGenerator.State.COMMITTING_COMPLETING,gen.getState());
|
|
||||||
assertEquals(HttpGenerator.Result.NEED_INFO,result);
|
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);
|
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),-1,101,null,false);
|
||||||
info.getHttpFields().add("Upgrade","WebSocket");
|
info.getHttpFields().add("Upgrade","WebSocket");
|
||||||
info.getHttpFields().add("Connection","Upgrade");
|
info.getHttpFields().add("Connection","Upgrade");
|
||||||
info.getHttpFields().add("Sec-WebSocket-Accept","123456789==");
|
info.getHttpFields().add("Sec-WebSocket-Accept","123456789==");
|
||||||
|
|
||||||
result=gen.generate(info,header,null,null,null,null);
|
result=gen.generateResponse(info,header,null,true);
|
||||||
assertEquals(HttpGenerator.Result.FLUSH,result);
|
assertEquals(HttpGenerator.Result.FLUSH,result);
|
||||||
|
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
||||||
String head = BufferUtil.toString(header);
|
String head = BufferUtil.toString(header);
|
||||||
BufferUtil.clear(header);
|
BufferUtil.clear(header);
|
||||||
|
|
||||||
result=gen.generate(info,null,null,null,null,null);
|
result=gen.generateResponse(info,null,null,false);
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
assertEquals(HttpGenerator.Result.DONE,result);
|
||||||
assertEquals(HttpGenerator.State.END,gen.getState());
|
assertEquals(HttpGenerator.State.END,gen.getState());
|
||||||
|
|
||||||
assertEquals(0,gen.getContentPrepared());
|
assertEquals(0,gen.getContentPrepared());
|
||||||
|
@ -434,441 +414,185 @@ public class HttpGeneratorServerTest
|
||||||
@Test
|
@Test
|
||||||
public void testResponseWithChunkedContent() throws Exception
|
public void testResponseWithChunkedContent() throws Exception
|
||||||
{
|
{
|
||||||
String body="";
|
|
||||||
ByteBuffer header=BufferUtil.allocate(4096);
|
ByteBuffer header=BufferUtil.allocate(4096);
|
||||||
ByteBuffer buffer=BufferUtil.allocate(16);
|
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
||||||
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
|
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
|
||||||
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
||||||
HttpGenerator gen = new HttpGenerator();
|
HttpGenerator gen = new HttpGenerator();
|
||||||
|
|
||||||
HttpGenerator.Result
|
HttpGenerator.Result
|
||||||
|
|
||||||
result=gen.generate(null,null,null,null,content0,null);
|
result=gen.generateResponse(null,null,content0,false);
|
||||||
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.Result.NEED_INFO,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
|
assertEquals(HttpGenerator.State.START,gen.getState());
|
||||||
assertEquals("Hello World! The",BufferUtil.toString(buffer));
|
|
||||||
assertEquals(43,content1.remaining());
|
|
||||||
|
|
||||||
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),-1,200,null,false);
|
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),-1,200,null,false);
|
||||||
info.getHttpFields().add("Last-Modified",HttpFields.__01Jan1970);
|
info.getHttpFields().add("Last-Modified",HttpFields.__01Jan1970);
|
||||||
result=gen.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.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
||||||
assertEquals("Hello World! The",BufferUtil.toString(buffer));
|
|
||||||
assertEquals(43,content1.remaining());
|
String out = BufferUtil.toString(header);
|
||||||
assertTrue(gen.isChunking());
|
|
||||||
|
|
||||||
String head = BufferUtil.toString(header);
|
|
||||||
BufferUtil.clear(header);
|
BufferUtil.clear(header);
|
||||||
body+=BufferUtil.toString(buffer);
|
out+=BufferUtil.toString(content0);
|
||||||
BufferUtil.clear(buffer);
|
BufferUtil.clear(content0);
|
||||||
|
|
||||||
result=gen.generate(info,null,null,buffer,content1,null);
|
result=gen.generateResponse(null,header,content1,false);
|
||||||
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
|
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
||||||
|
|
||||||
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
|
result=gen.generateResponse(null,chunk,content1,false);
|
||||||
result=gen.generate(info,null,chunk,buffer,content1,null);
|
|
||||||
assertEquals(HttpGenerator.Result.FLUSH,result);
|
assertEquals(HttpGenerator.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
||||||
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
|
out+=BufferUtil.toString(chunk);
|
||||||
assertEquals(" quick brown fox",BufferUtil.toString(buffer));
|
|
||||||
assertEquals(27,content1.remaining());
|
|
||||||
body+=BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
|
|
||||||
BufferUtil.clear(chunk);
|
BufferUtil.clear(chunk);
|
||||||
BufferUtil.clear(buffer);
|
out+=BufferUtil.toString(content1);
|
||||||
|
BufferUtil.clear(content1);
|
||||||
|
|
||||||
result=gen.generate(info,null,chunk,buffer,content1,null);
|
result=gen.generateResponse(null,chunk,null,true);
|
||||||
assertEquals(HttpGenerator.Result.FLUSH,result);
|
assertEquals(HttpGenerator.Result.CONTINUE,result);
|
||||||
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
|
assertEquals(HttpGenerator.State.COMPLETING,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);
|
result=gen.generateResponse(null,chunk,null,true);
|
||||||
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);
|
|
||||||
assertEquals(HttpGenerator.Result.FLUSH,result);
|
assertEquals(HttpGenerator.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
||||||
assertEquals("\r\nB\r\n",BufferUtil.toString(chunk));
|
out+=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);
|
|
||||||
BufferUtil.clear(chunk);
|
BufferUtil.clear(chunk);
|
||||||
|
|
||||||
result=gen.generate(info,null,chunk,buffer,null,null);
|
result=gen.generateResponse(null,chunk,null,true);
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
assertEquals(HttpGenerator.Result.DONE,result);
|
||||||
assertEquals(HttpGenerator.State.END,gen.getState());
|
assertEquals(HttpGenerator.State.END,gen.getState());
|
||||||
|
|
||||||
assertEquals(59,gen.getContentPrepared());
|
assertThat(out,containsString("HTTP/1.1 200 OK"));
|
||||||
|
assertThat(out,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
||||||
// System.err.println(head+body);
|
assertThat(out,not(containsString("Content-Length")));
|
||||||
|
assertThat(out,containsString("Transfer-Encoding: chunked"));
|
||||||
assertThat(head,containsString("HTTP/1.1 200 OK"));
|
assertThat(out,containsString("\r\n\r\nD\r\n"));
|
||||||
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
assertThat(out,containsString("\r\nHello World! \r\n"));
|
||||||
assertThat(head,not(containsString("Content-Length")));
|
assertThat(out,containsString("\r\n2E\r\n"));
|
||||||
assertThat(head,containsString("Transfer-Encoding: chunked"));
|
assertThat(out,containsString("\r\nThe quick brown fox jumped over the lazy dog. \r\n"));
|
||||||
assertTrue(head.endsWith("\r\n\r\n10\r\n"));
|
assertThat(out,containsString("\r\n0\r\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testResponseWithKnownContent() throws Exception
|
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 header=BufferUtil.allocate(4096);
|
||||||
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
|
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
|
||||||
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
|
||||||
HttpGenerator gen = new HttpGenerator();
|
HttpGenerator gen = new HttpGenerator();
|
||||||
gen.setLargeContent(8);
|
|
||||||
|
|
||||||
HttpGenerator.Result
|
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.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);
|
ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1,new HttpFields(),59,200,null,false);
|
||||||
info.getHttpFields().add("Last-Modified",HttpFields.__01Jan1970);
|
info.getHttpFields().add("Last-Modified",HttpFields.__01Jan1970);
|
||||||
info.getHttpFields().add("Content-Length","59");
|
result=gen.generateResponse(info,null,content0,false);
|
||||||
|
|
||||||
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);
|
|
||||||
assertEquals(HttpGenerator.Result.NEED_HEADER,result);
|
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.Result.FLUSH,result);
|
||||||
assertEquals(HttpGenerator.State.COMPLETING_1XX,gen.getState());
|
assertEquals(HttpGenerator.State.COMPLETING_1XX,gen.getState());
|
||||||
assertThat(BufferUtil.toString(header),Matchers.startsWith("HTTP/1.1 100 Continue"));
|
String out = BufferUtil.toString(header);
|
||||||
BufferUtil.clear(header);
|
|
||||||
|
|
||||||
result=gen.generate(null,null,null,null,null,null);
|
|
||||||
assertEquals(HttpGenerator.Result.OK,result);
|
|
||||||
assertEquals(HttpGenerator.State.START,gen.getState());
|
|
||||||
|
|
||||||
result=gen.generate(null,null,null,null,content,null);
|
result=gen.generateResponse(null,null,null,false);
|
||||||
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
|
assertEquals(HttpGenerator.Result.DONE,result);
|
||||||
assertEquals(HttpGenerator.State.START,gen.getState());
|
assertEquals(HttpGenerator.State.START,gen.getState());
|
||||||
|
|
||||||
result=gen.generate(null,null,null,buffer,content,null);
|
assertThat(out,containsString("HTTP/1.1 100 Continue"));
|
||||||
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);
|
result=gen.generateResponse(null,null,content0,false);
|
||||||
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.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);
|
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.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());
|
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
|
||||||
|
|
||||||
String head = BufferUtil.toString(header);
|
result=gen.generateResponse(null,null,null,true);
|
||||||
BufferUtil.clear(header);
|
assertEquals(HttpGenerator.Result.DONE,result);
|
||||||
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(HttpGenerator.State.END,gen.getState());
|
||||||
|
|
||||||
assertThat(head,containsString("HTTP/1.1 200 OK"));
|
assertThat(out,containsString("HTTP/1.1 200 OK"));
|
||||||
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
assertThat(out,containsString("Last-Modified: Thu, 01 Jan 1970 00:00:00 GMT"));
|
||||||
assertThat(head,containsString("Content-Length: 58"));
|
assertThat(out,not(containsString("chunked")));
|
||||||
assertTrue(head.endsWith("\r\n\r\n"));
|
assertThat(out,containsString("Content-Length: 59"));
|
||||||
|
assertThat(out,containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. "));
|
||||||
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",body);
|
|
||||||
|
|
||||||
assertEquals(58,gen.getContentPrepared());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,7 +301,7 @@ public class ByteArrayEndPoint extends AbstractEndPoint
|
||||||
shutdownInput();
|
shutdownInput();
|
||||||
if (_ishut)
|
if (_ishut)
|
||||||
return -1;
|
return -1;
|
||||||
int filled=BufferUtil.append(_in,buffer);
|
int filled=BufferUtil.flipPutFlip(_in,buffer);
|
||||||
if (filled>0)
|
if (filled>0)
|
||||||
notIdle();
|
notIdle();
|
||||||
return filled;
|
return filled;
|
||||||
|
@ -331,12 +331,12 @@ public class ByteArrayEndPoint extends AbstractEndPoint
|
||||||
if (b.remaining()>BufferUtil.space(_out))
|
if (b.remaining()>BufferUtil.space(_out))
|
||||||
{
|
{
|
||||||
ByteBuffer n = BufferUtil.allocate(_out.capacity()+b.remaining()*2);
|
ByteBuffer n = BufferUtil.allocate(_out.capacity()+b.remaining()*2);
|
||||||
BufferUtil.append(_out,n);
|
BufferUtil.flipPutFlip(_out,n);
|
||||||
_out=n;
|
_out=n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
flushed+=BufferUtil.append(b,_out);
|
flushed+=BufferUtil.flipPutFlip(b,_out);
|
||||||
|
|
||||||
if (BufferUtil.hasContent(b))
|
if (BufferUtil.hasContent(b))
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -407,7 +407,7 @@ public class SslConnection extends AbstractConnection
|
||||||
{
|
{
|
||||||
// Do we already have some decrypted data?
|
// Do we already have some decrypted data?
|
||||||
if (BufferUtil.hasContent(_decryptedInput))
|
if (BufferUtil.hasContent(_decryptedInput))
|
||||||
return BufferUtil.append(_decryptedInput, buffer);
|
return BufferUtil.flipPutFlip(_decryptedInput, buffer);
|
||||||
|
|
||||||
// We will need a network buffer
|
// We will need a network buffer
|
||||||
if (_encryptedInput == null)
|
if (_encryptedInput == null)
|
||||||
|
@ -502,7 +502,7 @@ public class SslConnection extends AbstractConnection
|
||||||
{
|
{
|
||||||
if (app_in == buffer)
|
if (app_in == buffer)
|
||||||
return unwrapResult.bytesProduced();
|
return unwrapResult.bytesProduced();
|
||||||
return BufferUtil.append(_decryptedInput, buffer);
|
return BufferUtil.flipPutFlip(_decryptedInput, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dang! we have to care about the handshake state
|
// Dang! we have to care about the handshake state
|
||||||
|
|
|
@ -163,7 +163,7 @@ public class SelectChannelEndPointTest
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy to the out buffer
|
// 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;
|
progress = true;
|
||||||
|
|
||||||
// Blocking writes
|
// Blocking writes
|
||||||
|
|
|
@ -20,9 +20,11 @@ package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import javax.servlet.DispatcherType;
|
import javax.servlet.DispatcherType;
|
||||||
import javax.servlet.ServletException;
|
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.EofException;
|
||||||
import org.eclipse.jetty.io.UncheckedPrintWriter;
|
import org.eclipse.jetty.io.UncheckedPrintWriter;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.FutureCallback;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
import org.eclipse.jetty.util.URIUtil;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
@ -197,7 +200,18 @@ public abstract class HttpChannel
|
||||||
{
|
{
|
||||||
if (_response.isCommitted())
|
if (_response.isCommitted())
|
||||||
throw new IllegalStateException("Committed before 100 Continues");
|
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;
|
_expect100Continue=false;
|
||||||
}
|
}
|
||||||
|
@ -359,12 +373,24 @@ public abstract class HttpChannel
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpGenerator.ResponseInfo info = _handler.commit();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
|
e.printStackTrace();
|
||||||
LOG.debug("failed to sendError {} {}",status, reason, e);
|
LOG.debug("failed to sendError {} {}",status, reason, e);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
@ -697,7 +723,7 @@ public abstract class HttpChannel
|
||||||
// Process content.
|
// Process content.
|
||||||
if (content instanceof ByteBuffer)
|
if (content instanceof ByteBuffer)
|
||||||
{
|
{
|
||||||
commitResponse(_handler.commit(),(ByteBuffer)content);
|
HttpChannel.this.write(_handler.commit(),(ByteBuffer)content);
|
||||||
}
|
}
|
||||||
else if (content instanceof InputStream)
|
else if (content instanceof InputStream)
|
||||||
{
|
{
|
||||||
|
@ -712,20 +738,9 @@ public abstract class HttpChannel
|
||||||
|
|
||||||
public abstract HttpConfiguration getHttpConfiguration();
|
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 FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException;
|
||||||
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 void completed();
|
protected abstract void completed();
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,6 @@ import java.util.concurrent.RejectedExecutionException;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpGenerator;
|
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.HttpGenerator.ResponseInfo;
|
||||||
import org.eclipse.jetty.http.HttpParser;
|
import org.eclipse.jetty.http.HttpParser;
|
||||||
import org.eclipse.jetty.http.HttpStatus;
|
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 Logger LOG = Log.getLogger(HttpConnection.class);
|
||||||
|
|
||||||
private static final ThreadLocal<HttpConnection> __currentConnection = new ThreadLocal<>();
|
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";
|
public static final String UPGRADE_CONNECTION_ATTR = "org.eclispe.jetty.server.HttpConnection.UPGRADE";
|
||||||
|
|
||||||
|
|
||||||
private final Server _server;
|
private final Server _server;
|
||||||
private final HttpConfiguration _httpConfig;
|
private final HttpConfiguration _httpConfig;
|
||||||
private final Connector _connector;
|
private final Connector _connector;
|
||||||
|
@ -59,11 +64,10 @@ public class HttpConnection extends AbstractConnection
|
||||||
private final ByteBufferPool _bufferPool;
|
private final ByteBufferPool _bufferPool;
|
||||||
private final HttpHttpInput _httpInput;
|
private final HttpHttpInput _httpInput;
|
||||||
|
|
||||||
|
|
||||||
private ResponseInfo _info;
|
private ResponseInfo _info;
|
||||||
ByteBuffer _requestBuffer=null;
|
ByteBuffer _requestBuffer=null;
|
||||||
ByteBuffer _responseHeader=null;
|
|
||||||
ByteBuffer _chunk=null;
|
ByteBuffer _chunk=null;
|
||||||
ByteBuffer _responseBuffer=null;
|
|
||||||
private int _headerBytes;
|
private int _headerBytes;
|
||||||
|
|
||||||
|
|
||||||
|
@ -146,16 +150,6 @@ public class HttpConnection extends AbstractConnection
|
||||||
_channel.reset();
|
_channel.reset();
|
||||||
_httpInput.recycle();
|
_httpInput.recycle();
|
||||||
releaseRequestBuffer();
|
releaseRequestBuffer();
|
||||||
if (_responseHeader!=null && !_responseHeader.hasRemaining())
|
|
||||||
{
|
|
||||||
_bufferPool.release(_responseHeader);
|
|
||||||
_responseHeader=null;
|
|
||||||
}
|
|
||||||
if (_responseBuffer!=null && !_responseBuffer.hasRemaining())
|
|
||||||
{
|
|
||||||
_bufferPool.release(_responseBuffer);
|
|
||||||
_responseBuffer=null;
|
|
||||||
}
|
|
||||||
if (_chunk!=null)
|
if (_chunk!=null)
|
||||||
_bufferPool.release(_chunk);
|
_bufferPool.release(_chunk);
|
||||||
_chunk=null;
|
_chunk=null;
|
||||||
|
@ -339,46 +333,6 @@ public class HttpConnection extends AbstractConnection
|
||||||
super(server,HttpConnection.this,_httpInput);
|
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()
|
public Connector getConnector()
|
||||||
{
|
{
|
||||||
return _connector;
|
return _connector;
|
||||||
|
@ -389,19 +343,6 @@ public class HttpConnection extends AbstractConnection
|
||||||
return _httpConfig;
|
return _httpConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void flushResponse() throws IOException
|
|
||||||
{
|
|
||||||
generate(null,Action.FLUSH);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void completeResponse() throws IOException
|
|
||||||
{
|
|
||||||
generate(null,Action.COMPLETE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean commitError(int status, String reason, String content)
|
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
|
// We could not send the error, so a shutdown of the connection will at least tell
|
||||||
// the client something is wrong
|
// the client something is wrong
|
||||||
|
|
||||||
if (BufferUtil.hasContent(_responseBuffer))
|
|
||||||
BufferUtil.clear(_responseBuffer);
|
|
||||||
getEndPoint().shutdownOutput();
|
getEndPoint().shutdownOutput();
|
||||||
_generator.abort();
|
_generator.abort();
|
||||||
return false;
|
return false;
|
||||||
|
@ -488,8 +426,7 @@ public class HttpConnection extends AbstractConnection
|
||||||
getEndPoint().shutdownOutput();
|
getEndPoint().shutdownOutput();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// make sure that an oshut connection is driven towards close
|
// make sure that an oshut connection is driven towards close
|
||||||
// TODO this is a little ugly
|
// TODO this is a little ugly
|
||||||
if (getEndPoint().isOpen() && getEndPoint().isOutputShutdown())
|
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.
|
// Only one response writer at a time.
|
||||||
synchronized(this)
|
synchronized(this)
|
||||||
{
|
{
|
||||||
long prepared_before=0;
|
ByteBuffer header=null;
|
||||||
long prepared_after;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_generator.isComplete())
|
if (_generator.isEnd())
|
||||||
{
|
{
|
||||||
if (Action.COMPLETE==action)
|
// TODO do we need this escape?
|
||||||
return 0;
|
if (last && BufferUtil.isEmpty(content))
|
||||||
|
return;
|
||||||
throw new EofException();
|
throw new EofException();
|
||||||
}
|
}
|
||||||
|
|
||||||
prepared_before=_generator.getContentPrepared();
|
|
||||||
loop: while (true)
|
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())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} generate: {} ({},{},{})@{}",
|
LOG.debug("{} generate: {} ({},{},{})@{}",
|
||||||
this,
|
this,
|
||||||
result,
|
result,
|
||||||
BufferUtil.toSummaryString(_responseHeader),
|
BufferUtil.toSummaryString(header),
|
||||||
BufferUtil.toSummaryString(_responseBuffer),
|
|
||||||
BufferUtil.toSummaryString(content),
|
BufferUtil.toSummaryString(content),
|
||||||
|
last,
|
||||||
_generator.getState());
|
_generator.getState());
|
||||||
|
|
||||||
switch(result)
|
switch(result)
|
||||||
|
@ -540,53 +477,37 @@ public class HttpConnection extends AbstractConnection
|
||||||
case NEED_INFO:
|
case NEED_INFO:
|
||||||
if (_info==null)
|
if (_info==null)
|
||||||
_info=_channel.getEventHandler().commit();
|
_info=_channel.getEventHandler().commit();
|
||||||
LOG.debug("{} Gcommit {}",this,_info);
|
|
||||||
if (_responseHeader==null)
|
|
||||||
_responseHeader=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case NEED_HEADER:
|
case NEED_HEADER:
|
||||||
_responseHeader=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
|
if (header!=null)
|
||||||
continue;
|
_bufferPool.release(header);
|
||||||
|
header=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
|
||||||
case NEED_BUFFER:
|
|
||||||
_responseBuffer=_bufferPool.acquire(_httpConfig.getResponseBufferSize(),false);
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case NEED_CHUNK:
|
case NEED_CHUNK:
|
||||||
_responseHeader=null;
|
if (header!=null)
|
||||||
_chunk=_bufferPool.acquire(HttpGenerator.CHUNK_SIZE,false);
|
_bufferPool.release(header);
|
||||||
|
header=_bufferPool.acquire(HttpGenerator.CHUNK_SIZE,false);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case FLUSH:
|
case FLUSH:
|
||||||
if (_info.isHead())
|
if (_info.isHead())
|
||||||
{
|
{
|
||||||
if (_chunk!=null)
|
write(header,null).get();
|
||||||
BufferUtil.clear(_chunk);
|
BufferUtil.clear(content);
|
||||||
if (_responseBuffer!=null)
|
|
||||||
BufferUtil.clear(_responseBuffer);
|
|
||||||
}
|
}
|
||||||
write(_responseHeader,_chunk,_responseBuffer).get();
|
else
|
||||||
|
write(header,content).get();
|
||||||
|
|
||||||
continue;
|
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:
|
case SHUTDOWN_OUT:
|
||||||
terminate();
|
getEndPoint().shutdownOutput();
|
||||||
|
continue;
|
||||||
|
|
||||||
|
case DONE:
|
||||||
break loop;
|
break loop;
|
||||||
|
|
||||||
case OK:
|
|
||||||
if (!BufferUtil.hasContent(content))
|
|
||||||
break loop;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -603,124 +524,84 @@ public class HttpConnection extends AbstractConnection
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
prepared_after=_generator.getContentPrepared();
|
if (header!=null)
|
||||||
|
_bufferPool.release(header);
|
||||||
}
|
}
|
||||||
return (int)(prepared_after-prepared_before);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.
|
// Only one response writer at a time.
|
||||||
synchronized (this)
|
synchronized(this)
|
||||||
{
|
{
|
||||||
_info=info;
|
ByteBuffer header=null;
|
||||||
|
|
||||||
LOG.debug("{} commit {}",this,_info);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (_generator.isCommitted())
|
if (_generator.isEnd())
|
||||||
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())
|
|
||||||
throw new EofException();
|
throw new EofException();
|
||||||
|
|
||||||
|
FutureCallback<Void> fcb=null;
|
||||||
|
|
||||||
loop: while (true)
|
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())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("{} commit: {} ({},{},{})@{}",
|
LOG.debug("{} send: {} ({},{})@{}",
|
||||||
this,
|
this,
|
||||||
result,
|
result,
|
||||||
BufferUtil.toDetailString(_responseHeader),
|
BufferUtil.toSummaryString(header),
|
||||||
BufferUtil.toSummaryString(_responseBuffer),
|
|
||||||
BufferUtil.toSummaryString(content),
|
BufferUtil.toSummaryString(content),
|
||||||
_generator.getState());
|
_generator.getState());
|
||||||
|
|
||||||
switch(result)
|
switch(result)
|
||||||
{
|
{
|
||||||
case NEED_INFO:
|
case NEED_INFO:
|
||||||
_info=_channel.getEventHandler().commit();
|
throw new IllegalStateException();
|
||||||
if (_responseHeader==null)
|
|
||||||
_responseHeader=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NEED_HEADER:
|
case NEED_HEADER:
|
||||||
_responseHeader=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
|
if (header!=null)
|
||||||
break;
|
_bufferPool.release(header);
|
||||||
|
header=_bufferPool.acquire(_httpConfig.getResponseHeaderSize(),false);
|
||||||
case NEED_BUFFER:
|
continue;
|
||||||
_responseBuffer=_bufferPool.acquire(_httpConfig.getResponseBufferSize(),false);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case NEED_CHUNK:
|
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:
|
case FLUSH:
|
||||||
if (_info.isHead())
|
if(info.isHead())
|
||||||
{
|
{
|
||||||
if (_chunk!=null)
|
BufferUtil.clear(content);
|
||||||
BufferUtil.clear(_chunk);
|
fcb=write(header,null);
|
||||||
if (_responseBuffer!=null)
|
|
||||||
BufferUtil.clear(_responseBuffer);
|
|
||||||
}
|
}
|
||||||
write(_responseHeader,_chunk,_responseBuffer).get();
|
else
|
||||||
break;
|
fcb=write(header,content);
|
||||||
|
continue;
|
||||||
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;
|
|
||||||
|
|
||||||
case SHUTDOWN_OUT:
|
case SHUTDOWN_OUT:
|
||||||
terminate();
|
getEndPoint().shutdownOutput();
|
||||||
break loop;
|
continue;
|
||||||
|
|
||||||
case OK:
|
case DONE:
|
||||||
if (_info!=null && _info.isInformational())
|
if (fcb==null)
|
||||||
_info=null;
|
fcb=__completed;
|
||||||
break loop;
|
break loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return fcb;
|
||||||
}
|
}
|
||||||
catch(InterruptedException e)
|
finally
|
||||||
{
|
{
|
||||||
LOG.debug(e);
|
if (header!=null)
|
||||||
}
|
_bufferPool.release(header);
|
||||||
catch(ExecutionException e)
|
|
||||||
{
|
|
||||||
LOG.debug(e);
|
|
||||||
FutureCallback.rethrow(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
@Override
|
||||||
public ScheduledExecutorService getScheduler()
|
public ScheduledExecutorService getScheduler()
|
||||||
{
|
{
|
||||||
|
@ -733,41 +614,29 @@ public class HttpConnection extends AbstractConnection
|
||||||
_connector.getExecutor().execute(task);
|
_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<>();
|
FutureCallback<Void> fcb=new FutureCallback<>();
|
||||||
if (BufferUtil.hasContent(b0))
|
if (BufferUtil.hasContent(b0))
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(b1))
|
if (BufferUtil.hasContent(b1))
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(b2))
|
getEndPoint().write(null,fcb,b0,b1);
|
||||||
getEndPoint().write(null,fcb,b0,b1,b2);
|
|
||||||
else
|
|
||||||
getEndPoint().write(null,fcb,b0,b1);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(b2))
|
getEndPoint().write(null,fcb,b0);
|
||||||
getEndPoint().write(null,fcb,b0,b2);
|
|
||||||
else
|
|
||||||
getEndPoint().write(null,fcb,b0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(b1))
|
if (BufferUtil.hasContent(b1))
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(b2))
|
getEndPoint().write(null,fcb,b1);
|
||||||
getEndPoint().write(null,fcb,b1,b2);
|
|
||||||
else
|
|
||||||
getEndPoint().write(null,fcb,b1);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (BufferUtil.hasContent(b2))
|
fcb.completed(null);
|
||||||
getEndPoint().write(null,fcb,b2);
|
|
||||||
else
|
|
||||||
fcb.completed(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return fcb;
|
return fcb;
|
||||||
|
|
|
@ -25,6 +25,7 @@ import java.nio.ByteBuffer;
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.EofException;
|
import org.eclipse.jetty.io.EofException;
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
import org.eclipse.jetty.util.ByteArrayOutputStream2;
|
import org.eclipse.jetty.util.ByteArrayOutputStream2;
|
||||||
|
|
||||||
/** Output.
|
/** Output.
|
||||||
|
@ -41,6 +42,7 @@ public class HttpOutput extends ServletOutputStream
|
||||||
private final HttpChannel _channel;
|
private final HttpChannel _channel;
|
||||||
private boolean _closed;
|
private boolean _closed;
|
||||||
private long _written;
|
private long _written;
|
||||||
|
private ByteBuffer _aggregate;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public HttpOutput(HttpChannel channel)
|
public HttpOutput(HttpChannel channel)
|
||||||
|
@ -81,8 +83,18 @@ public class HttpOutput extends ServletOutputStream
|
||||||
public void close() throws IOException
|
public void close() throws IOException
|
||||||
{
|
{
|
||||||
if (!_closed)
|
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;
|
_closed=true;
|
||||||
|
if (_aggregate!=null)
|
||||||
|
{
|
||||||
|
_channel.getConnector().getByteBufferPool().release(_aggregate);
|
||||||
|
_aggregate=null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -95,13 +107,19 @@ public class HttpOutput extends ServletOutputStream
|
||||||
@Override
|
@Override
|
||||||
public void flush() throws IOException
|
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)
|
if (_closed)
|
||||||
throw new EofException();
|
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));
|
// Do we now have space to aggregate?
|
||||||
checkAllWritten();
|
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);
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
// Check if all written or full
|
||||||
/*
|
if (!checkAllWritten() && BufferUtil.isFull(_aggregate))
|
||||||
* @see java.io.OutputStream#write(byte[])
|
_channel.write(_aggregate,false);
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void write(byte[] b) throws IOException
|
|
||||||
{
|
|
||||||
if (_closed)
|
|
||||||
throw new IOException("Closed");
|
|
||||||
|
|
||||||
_written+=_channel.write(ByteBuffer.wrap(b));
|
|
||||||
checkAllWritten();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -139,8 +186,15 @@ public class HttpOutput extends ServletOutputStream
|
||||||
if (_closed)
|
if (_closed)
|
||||||
throw new IOException("Closed");
|
throw new IOException("Closed");
|
||||||
|
|
||||||
_written+=_channel.write(ByteBuffer.wrap(new byte[]{(byte)b}));
|
if (_aggregate==null)
|
||||||
checkAllWritten();
|
_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");
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,11 +19,13 @@
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.io.InterruptedIOException;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.channels.IllegalSelectorException;
|
import java.nio.channels.IllegalSelectorException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
@ -454,7 +456,20 @@ public class Response implements HttpServletResponse
|
||||||
public void sendProcessing() throws IOException
|
public void sendProcessing() throws IOException
|
||||||
{
|
{
|
||||||
if (_channel.isExpecting102Processing() && !isCommitted())
|
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)
|
if (_contentLength>=0 && written>=_contentLength)
|
||||||
{
|
{
|
||||||
|
@ -867,7 +882,9 @@ public class Response implements HttpServletResponse
|
||||||
{
|
{
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -1007,7 +1024,7 @@ public class Response implements HttpServletResponse
|
||||||
{
|
{
|
||||||
if (isCommitted() || getContentCount()>0 )
|
if (isCommitted() || getContentCount()>0 )
|
||||||
throw new IllegalStateException("Committed or content written");
|
throw new IllegalStateException("Committed or content written");
|
||||||
_channel.increaseContentBufferSize(size);
|
_out.increaseContentBufferSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -1017,7 +1034,7 @@ public class Response implements HttpServletResponse
|
||||||
@Override
|
@Override
|
||||||
public int getBufferSize()
|
public int getBufferSize()
|
||||||
{
|
{
|
||||||
return _channel.getContentBufferSize();
|
return _out.getContentBufferSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -1027,7 +1044,7 @@ public class Response implements HttpServletResponse
|
||||||
@Override
|
@Override
|
||||||
public void flushBuffer() throws IOException
|
public void flushBuffer() throws IOException
|
||||||
{
|
{
|
||||||
_channel.flushResponse();
|
_out.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -1104,7 +1121,7 @@ public class Response implements HttpServletResponse
|
||||||
_out.reset();
|
_out.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
_channel.resetBuffer();
|
_out.resetBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -1193,7 +1210,7 @@ public class Response implements HttpServletResponse
|
||||||
public void complete()
|
public void complete()
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
_channel.completeResponse();
|
_out.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
|
|
@ -18,14 +18,10 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
package org.eclipse.jetty.server;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
|
||||||
|
|
||||||
public class ChannelHttpServer
|
public class ChannelHttpServer
|
||||||
{
|
{
|
||||||
public static void main(String[] s) throws Exception
|
public static void main(String[] s) throws Exception
|
||||||
{
|
{
|
||||||
System.setProperty("org.eclipse.jetty.LEVEL","DEBUG");
|
|
||||||
Log.getRootLogger().setDebugEnabled(true);
|
|
||||||
Server server = new Server();
|
Server server = new Server();
|
||||||
SelectChannelConnector connector = new SelectChannelConnector(server);
|
SelectChannelConnector connector = new SelectChannelConnector(server);
|
||||||
connector.setPort(8080);
|
connector.setPort(8080);
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,6 +26,7 @@ import java.util.concurrent.ScheduledExecutorService;
|
||||||
|
|
||||||
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
|
||||||
import org.eclipse.jetty.util.BufferUtil;
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.FutureCallback;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.TypeUtil;
|
import org.eclipse.jetty.util.TypeUtil;
|
||||||
import org.eclipse.jetty.util.Utf8StringBuilder;
|
import org.eclipse.jetty.util.Utf8StringBuilder;
|
||||||
|
@ -50,44 +51,6 @@ public class HttpWriterTest
|
||||||
return null;
|
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
|
@Override
|
||||||
protected void completed()
|
protected void completed()
|
||||||
{
|
{
|
||||||
|
@ -111,6 +74,21 @@ public class HttpWriterTest
|
||||||
return null;
|
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);
|
_httpOut = new HttpOutput(channel);
|
||||||
|
|
|
@ -21,6 +21,7 @@ package org.eclipse.jetty.server;
|
||||||
import static org.hamcrest.Matchers.containsString;
|
import static org.hamcrest.Matchers.containsString;
|
||||||
import static org.junit.Assert.assertThat;
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -35,6 +36,7 @@ public class LocalConnectorTest
|
||||||
{
|
{
|
||||||
_server = new Server();
|
_server = new Server();
|
||||||
_connector = new LocalConnector(_server);
|
_connector = new LocalConnector(_server);
|
||||||
|
_connector.setIdleTimeout(60000);
|
||||||
_server.addConnector(_connector);
|
_server.addConnector(_connector);
|
||||||
_server.setHandler(new DumpHandler());
|
_server.setHandler(new DumpHandler());
|
||||||
_server.start();
|
_server.start();
|
||||||
|
@ -51,9 +53,8 @@ public class LocalConnectorTest
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOneGET() throws Exception
|
public void testOneGET() throws Exception
|
||||||
{
|
{
|
||||||
String response=_connector.getResponses("GET /R1 HTTP/1.0\r\n\r\n");
|
String response=_connector.getResponses("GET /R1 HTTP/1.0\r\n\r\n");
|
||||||
|
|
||||||
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
assertThat(response,containsString("HTTP/1.1 200 OK"));
|
||||||
assertThat(response,containsString("pathInfo=/R1"));
|
assertThat(response,containsString("pathInfo=/R1"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -217,18 +217,16 @@ public class RequestTest
|
||||||
_server.start();
|
_server.start();
|
||||||
|
|
||||||
// Request with illegal Host header
|
// Request with illegal Host header
|
||||||
String request="GET / HTTP/1.1\r\n"+
|
String request="GET / HTTP/1.1\n"+
|
||||||
"Host: whatever.com:\r\n"+
|
"Host: whatever.com:\n"+
|
||||||
"Content-Type: text/html;charset=utf8\n"+
|
"Content-Type: text/html;charset=utf8\n"+
|
||||||
"Connection: close\n"+
|
"Connection: close\n"+
|
||||||
"\n";
|
"\n";
|
||||||
|
|
||||||
String responses=_connector.getResponses(request);
|
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
|
@Test
|
||||||
public void testContentTypeEncoding() throws Exception
|
public void testContentTypeEncoding() throws Exception
|
||||||
{
|
{
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.eclipse.jetty.server.handler.ContextHandler;
|
||||||
import org.eclipse.jetty.server.session.HashSessionIdManager;
|
import org.eclipse.jetty.server.session.HashSessionIdManager;
|
||||||
import org.eclipse.jetty.server.session.HashSessionManager;
|
import org.eclipse.jetty.server.session.HashSessionManager;
|
||||||
import org.eclipse.jetty.server.session.HashedSession;
|
import org.eclipse.jetty.server.session.HashedSession;
|
||||||
|
import org.eclipse.jetty.util.FutureCallback;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
@ -106,21 +107,18 @@ public class ResponseTest
|
||||||
_channel = new HttpChannel(_server,connection,input)
|
_channel = new HttpChannel(_server,connection,input)
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected int write(ByteBuffer content) throws IOException
|
protected void write(ByteBuffer content, boolean last) throws IOException
|
||||||
{
|
{
|
||||||
int length=content.remaining();
|
|
||||||
content.clear();
|
content.clear();
|
||||||
return length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void resetBuffer()
|
protected FutureCallback<Void> write(ResponseInfo info, ByteBuffer content) throws IOException
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void increaseContentBufferSize(int size)
|
|
||||||
{
|
{
|
||||||
|
content.clear();
|
||||||
|
FutureCallback<Void> fcb = new FutureCallback<>();
|
||||||
|
fcb.completed(null);
|
||||||
|
return fcb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -135,19 +133,6 @@ public class ResponseTest
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getContentBufferSize()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void flushResponse() throws IOException
|
|
||||||
{
|
|
||||||
if (!_channel.getResponse().isCommitted())
|
|
||||||
_channel.getResponse().commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void execute(Runnable task)
|
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
|
@Override
|
||||||
public Connector getConnector()
|
public Connector getConnector()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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.
|
* @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
|
* @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);
|
int pos= flipToFill(to);
|
||||||
try
|
try
|
||||||
|
@ -318,6 +318,32 @@ public class BufferUtil
|
||||||
flipToFlush(to,pos);
|
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
|
public static void readFrom(File file, ByteBuffer buffer) throws IOException
|
||||||
|
|
|
@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||||
//TODO: Simplify, get rid of DOING. Probably replace states with AtomicBoolean
|
//TODO: Simplify, get rid of DOING. Probably replace states with AtomicBoolean
|
||||||
public class FutureCallback<C> implements Future<C>,Callback<C>
|
public class FutureCallback<C> implements Future<C>,Callback<C>
|
||||||
{
|
{
|
||||||
// TODO investigate use of a phasor
|
|
||||||
private enum State {NOT_DONE,DOING,DONE};
|
private enum State {NOT_DONE,DOING,DONE};
|
||||||
private final AtomicReference<State> _state=new AtomicReference<>(State.NOT_DONE);
|
private final AtomicReference<State> _state=new AtomicReference<>(State.NOT_DONE);
|
||||||
private CountDownLatch _done= new CountDownLatch(1);
|
private CountDownLatch _done= new CountDownLatch(1);
|
||||||
|
@ -39,17 +38,6 @@ public class FutureCallback<C> implements Future<C>,Callback<C>
|
||||||
private C _context;
|
private C _context;
|
||||||
private boolean _completed;
|
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
|
@Override
|
||||||
public void completed(C context)
|
public void completed(C context)
|
||||||
{
|
{
|
||||||
|
|
|
@ -132,14 +132,14 @@ public class BufferUtilTest
|
||||||
ByteBuffer from=BufferUtil.toBuffer("12345");
|
ByteBuffer from=BufferUtil.toBuffer("12345");
|
||||||
|
|
||||||
BufferUtil.clear(to);
|
BufferUtil.clear(to);
|
||||||
assertEquals(5,BufferUtil.append(from,to));
|
assertEquals(5,BufferUtil.flipPutFlip(from,to));
|
||||||
assertTrue(BufferUtil.isEmpty(from));
|
assertTrue(BufferUtil.isEmpty(from));
|
||||||
assertEquals("12345",BufferUtil.toString(to));
|
assertEquals("12345",BufferUtil.toString(to));
|
||||||
|
|
||||||
from=BufferUtil.toBuffer("XX67890ZZ");
|
from=BufferUtil.toBuffer("XX67890ZZ");
|
||||||
from.position(2);
|
from.position(2);
|
||||||
|
|
||||||
assertEquals(5,BufferUtil.append(from,to));
|
assertEquals(5,BufferUtil.flipPutFlip(from,to));
|
||||||
assertEquals(2,from.remaining());
|
assertEquals(2,from.remaining());
|
||||||
assertEquals("1234567890",BufferUtil.toString(to));
|
assertEquals("1234567890",BufferUtil.toString(to));
|
||||||
}
|
}
|
||||||
|
@ -151,14 +151,14 @@ public class BufferUtilTest
|
||||||
ByteBuffer from=BufferUtil.toBuffer("12345");
|
ByteBuffer from=BufferUtil.toBuffer("12345");
|
||||||
|
|
||||||
BufferUtil.clear(to);
|
BufferUtil.clear(to);
|
||||||
assertEquals(5,BufferUtil.append(from,to));
|
assertEquals(5,BufferUtil.flipPutFlip(from,to));
|
||||||
assertTrue(BufferUtil.isEmpty(from));
|
assertTrue(BufferUtil.isEmpty(from));
|
||||||
assertEquals("12345",BufferUtil.toString(to));
|
assertEquals("12345",BufferUtil.toString(to));
|
||||||
|
|
||||||
from=BufferUtil.toBuffer("XX67890ZZ");
|
from=BufferUtil.toBuffer("XX67890ZZ");
|
||||||
from.position(2);
|
from.position(2);
|
||||||
|
|
||||||
assertEquals(5,BufferUtil.append(from,to));
|
assertEquals(5,BufferUtil.flipPutFlip(from,to));
|
||||||
assertEquals(2,from.remaining());
|
assertEquals(2,from.remaining());
|
||||||
assertEquals("1234567890",BufferUtil.toString(to));
|
assertEquals("1234567890",BufferUtil.toString(to));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue