jetty-9 work in progress

This commit is contained in:
Greg Wilkins 2012-03-07 17:26:33 +11:00
parent a4bee5b12d
commit 3d12ef7075
6 changed files with 1518 additions and 879 deletions

View File

@ -1073,13 +1073,12 @@ public class HttpFields implements Iterable<HttpFields.Field>
}
/* ------------------------------------------------------------ */
public void putTo(ByteBuffer buffer) throws IOException
public void putTo(ByteBuffer buffer)
{
HttpHeader header = HttpHeader.CACHE.get(_name);
if (header!=null)
{
buffer.put(header.toBuffer());
buffer.put(__colon_space);
buffer.put(header.toBytesColonSpace());
if (HttpHeaderValue.hasKnownValues(header))
{
@ -1089,6 +1088,8 @@ public class HttpFields implements Iterable<HttpFields.Field>
else
buffer.put(toSanitisedBytes(_value));
}
else
buffer.put(toSanitisedBytes(_value));
}
else
{
@ -1101,7 +1102,7 @@ public class HttpFields implements Iterable<HttpFields.Field>
}
/* ------------------------------------------------------------ */
public void putValueTo(ByteBuffer buffer) throws IOException
public void putValueTo(ByteBuffer buffer)
{
buffer.put(toSanitisedBytes(_value));
}

View File

@ -82,7 +82,7 @@ public class HttpGenerator
// states
enum State { START, CHUNKING, STREAMING, COMPLETING, END };
enum State { START, COMPLETING_UNCOMMITTED, COMMITTING, COMMITTING_COMPLETING, COMMITTED, COMPLETING, END };
enum Result { NEED_COMMIT,NEED_CHUNK,NEED_BUFFER,FLUSH,FLUSH_CONTENT,NEED_COMPLETE,OK,SHUTDOWN_OUT};
public static final byte[] NO_BYTES = {};
@ -112,8 +112,7 @@ public class HttpGenerator
// common _content
private static final byte[] LAST_CHUNK =
{ (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
private static final byte[] LAST_CHUNK = { (byte) '0', (byte) '\015', (byte) '\012', (byte) '\015', (byte) '\012'};
private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012");
private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012");
private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012");
@ -124,7 +123,7 @@ public class HttpGenerator
private static byte[] SERVER = StringUtil.getBytes("Server: Jetty(7.0.x)\015\012");
// other statics
private static final int CHUNK_SPACE = 12;
public static final int CHUNK_SIZE = 12;
public static void setServerVersion(String version)
{
@ -133,9 +132,6 @@ public class HttpGenerator
// data
private boolean _needCRLF = false;
private boolean _sentEOC = false;
/* ------------------------------------------------------------------------------- */
public void reset()
@ -154,7 +150,6 @@ public class HttpGenerator
_method=null;
_needCRLF = false;
_sentEOC = false;
_uri=null;
_noContent=false;
}
@ -201,6 +196,12 @@ public class HttpGenerator
return _state != State.START;
}
/* ------------------------------------------------------------ */
public boolean isChunking()
{
return _contentLength==HttpTokens.CHUNKED_CONTENT;
}
/* ------------------------------------------------------------ */
public int getLargeContent()
{
@ -366,58 +367,34 @@ public class HttpGenerator
return _method==null;
}
/* ------------------------------------------------------------ */
public boolean needsHeader()
public Result commit(HttpFields fields,ByteBuffer header,ByteBuffer buffer, ByteBuffer content, boolean last) throws IOException
{
return _state==State.START || _contentLength==HttpTokens.CHUNKED_CONTENT;
}
/* ------------------------------------------------------------ */
public Result commit(HttpFields fields,ByteBuffer header,ByteBuffer buffer, ByteBuffer content, boolean last)
throws IOException
{
if (header==null)
throw new IllegalArgumentException("!header");
if (isResponse() && _status==0)
throw new EofException(); // TODO ???
if (_state == State.START)
{
if (isRequest() && _version==HttpVersion.HTTP_0_9)
_noContent=true;
boolean has_server = false;
int pos=header.position();
try
{
BufferUtil.flipToFill(header);
switch(_state)
{
case START:
case COMPLETING_UNCOMMITTED:
if (isRequest())
{
_persistent=true;
if(_version==HttpVersion.HTTP_0_9)
{
_contentLength = HttpTokens.NO_CONTENT;
header.put(_method);
header.put((byte)' ');
header.put(_uri);
header.put(HttpTokens.CRLF);
_state = State.END;
_noContent=true;
return Result.FLUSH;
generateRequestLine(header);
_state = State.END;
return Result.OK;
}
header.put(_method);
header.put((byte)' ');
header.put(_uri);
header.put((byte)' ');
header.put((_version==HttpVersion.HTTP_1_0?HttpVersion.HTTP_1_0:HttpVersion.HTTP_1_1).toBytes());
header.put(HttpTokens.CRLF);
_persistent=true;
generateRequestLine(header);
}
else
{
@ -426,7 +403,7 @@ public class HttpGenerator
{
_persistent = false;
_contentLength = HttpTokens.EOF_CONTENT;
_state = State.STREAMING;
_state = State.COMMITTED;
return prepareContent(null,buffer,content);
}
@ -434,6 +411,107 @@ public class HttpGenerator
if (_persistent==null)
_persistent=(_version.ordinal() > HttpVersion.HTTP_1_0.ordinal());
generateResponseLine(header);
// Handle 1xx
if (_status>=100 && _status<200 )
{
_noContent=true;
if (_status!=101 )
{
header.put(HttpTokens.CRLF);
_state = State.START;
return Result.OK;
}
}
else if (_status==204 || _status==304)
{
_noContent=true;
}
}
generateHeaders(fields,header,content,last);
_state = _state==State.COMPLETING_UNCOMMITTED?State.COMMITTING_COMPLETING:State.COMMITTING;
// fall through to COMMITTING states
case COMMITTING:
case COMMITTING_COMPLETING:
// Handle any content
if (BufferUtil.hasContent(content))
{
// Do we have too much content?
if (_contentLength>0 && content.remaining()>(_contentLength-_contentPrepared))
{
LOG.warn("Content truncated at {}",new Throwable());
content.limit(content.position()+(int)(_contentLength-_contentPrepared));
}
// Can we do a direct flush
if (BufferUtil.isEmpty(buffer) && content.remaining()>_largeContent)
{
_contentPrepared+=content.remaining();
if (isChunking())
prepareChunk(header,content.remaining());
return Result.FLUSH_CONTENT;
}
// we copy content to buffer
// if we don't have one, we need one
if (buffer==null)
return Result.NEED_BUFFER;
_contentPrepared+=BufferUtil.put(content,buffer);
if (isChunking())
prepareChunk(header,buffer.remaining());
return Result.FLUSH;
}
_state = _state==State.COMMITTING?State.COMMITTED:State.COMPLETING;
break;
default:
throw new IllegalStateException(this.toString());
}
}
catch(BufferOverflowException e)
{
throw new RuntimeException("Header>"+header.capacity(),e);
}
finally
{
BufferUtil.flipToFlush(header,pos);
}
return _state==State.COMPLETING?Result.NEED_COMPLETE:Result.OK;
}
/* ------------------------------------------------------------ */
private void generateRequestLine(ByteBuffer header)
{
header.put(_method);
header.put((byte)' ');
header.put(_uri);
switch(_version)
{
case HTTP_1_0:
case HTTP_1_1:
header.put((byte)' ');
header.put(_version.toBytes());
}
header.put(HttpTokens.CRLF);
}
/* ------------------------------------------------------------ */
private void generateResponseLine(ByteBuffer header)
{
// Look for prepared response line
Status status = _status<__status.length?__status[_status]:null;
if (status!=null)
@ -464,24 +542,11 @@ public class HttpGenerator
header.put(_reason);
header.put(HttpTokens.CRLF);
}
}
// Handle 1xx
if (_status>=100 && _status<200 )
/* ------------------------------------------------------------ */
private void generateHeaders(HttpFields fields,ByteBuffer header,ByteBuffer content,boolean last)
{
_noContent=true;
if (_status!=101 )
{
header.put(HttpTokens.CRLF);
_state = State.START;
return Result.FLUSH;
}
}
else if (_status==204 || _status==304)
{
_noContent=true;
}
}
// Add Date header
if (_status>=200 && _date!=null)
@ -492,6 +557,7 @@ public class HttpGenerator
}
// default field values
boolean has_server = false;
HttpFields.Field transfer_encoding=null;
boolean keep_alive=false;
boolean close=false;
@ -518,7 +584,9 @@ public class HttpGenerator
else
{
// write the field to the header
field.putTo(header);
header.put(HttpHeader.CONTENT_LENGTH.toBytesColonSpace());
BufferUtil.putDecLong(header,length);
BufferUtil.putCRLF(header);
_contentLength=length;
content_length=true;
}
@ -695,7 +763,7 @@ public class HttpGenerator
}
// Add transfer_encoding if needed
if (_contentLength == HttpTokens.CHUNKED_CONTENT)
if (isChunking())
{
// try to use user supplied encoding as it may have other values.
if (transfer_encoding != null && !HttpHeaderValue.CHUNKED.toString().equalsIgnoreCase(transfer_encoding.getValue()))
@ -717,6 +785,7 @@ public class HttpGenerator
_persistent=false;
}
// If this is a response, work out persistence
if (isResponse())
{
if (!_persistent && (close || _version.ordinal() > HttpVersion.HTTP_1_0.ordinal()))
@ -756,55 +825,19 @@ public class HttpGenerator
// end the header.
header.put(HttpTokens.CRLF);
_state = _contentLength==HttpTokens.CHUNKED_CONTENT?State.CHUNKING:State.STREAMING;
}
catch(BufferOverflowException e)
{
throw new RuntimeException("Header>"+header.capacity(),e);
}
finally
{
BufferUtil.flipToFlush(header,pos);
}
}
// Handle the content
if (BufferUtil.hasContent(content))
{
// Can we do a direct flush
if (BufferUtil.isEmpty(buffer) && content.remaining()>_largeContent)
{
_contentPrepared+=content.remaining();
if (_state==State.CHUNKING)
prepareChunk(header,content.remaining());
return Result.FLUSH_CONTENT;
}
// we copy content to buffer
// if we don't have one, we need one
if (buffer==null)
return Result.NEED_BUFFER;
_contentPrepared+=BufferUtil.put(content,buffer);
if (_state==State.CHUNKING)
prepareChunk(header,buffer.remaining());
}
if (BufferUtil.hasContent(buffer))
{
if (last && BufferUtil.isEmpty(content) || _contentLength>0&&_contentLength==_contentPrepared)
return Result.NEED_COMPLETE;
return Result.FLUSH;
}
return Result.OK;
}
/* ------------------------------------------------------------ */
public Result prepareContent(ByteBuffer chunk, ByteBuffer buffer, ByteBuffer content)
{
// Do we have too much content?
if (_contentLength>0 && content.remaining()>(_contentLength-_contentPrepared))
{
LOG.warn("Content truncated at {}",new Throwable());
content.limit(content.position()+(int)(_contentLength-_contentPrepared));
}
switch (_state)
{
case START:
@ -817,14 +850,7 @@ public class HttpGenerator
if (buffer==null)
return Result.NEED_BUFFER;
// are we limited by content length?
if (_contentLength>0)
{
_contentPrepared+=BufferUtil.put(content,buffer,_contentLength-_contentPrepared);
if (_contentPrepared==_contentLength)
return Result.NEED_COMMIT;
}
else
// copy content to buffer
_contentPrepared+=BufferUtil.put(content,buffer);
// are we full?
@ -833,74 +859,49 @@ public class HttpGenerator
return Result.OK;
case STREAMING:
case COMPLETING:
return Result.NEED_COMPLETE;
case COMMITTED:
// Can we do a direct flush
if (BufferUtil.isEmpty(buffer) && content.remaining()>_largeContent)
{
if (_contentLength>0)
{
long total=_contentPrepared+content.remaining();
if (total>_contentLength)
throw new IllegalStateException();
if (total==_contentLength)
return Result.NEED_COMPLETE;
}
_contentPrepared+=content.remaining();
return Result.FLUSH_CONTENT;
}
// we copy content to buffer
// if we don't have one, we need one
if (buffer==null)
return Result.NEED_BUFFER;
// are we limited by content length?
if (_contentLength>0)
{
_contentPrepared+=BufferUtil.put(content,buffer,_contentLength-_contentPrepared);
if (_contentPrepared==_contentLength)
return Result.NEED_COMPLETE;
}
else
_contentPrepared+=BufferUtil.put(content,buffer);
// are we full?
if (BufferUtil.isAtCapacity(buffer))
return Result.FLUSH;
return Result.OK;
case CHUNKING:
if (isChunking())
{
if (chunk==null)
return Result.NEED_CHUNK;
// Can we do a direct flush
if (BufferUtil.isEmpty(buffer) && content.remaining()>_largeContent)
{
_contentPrepared+=content.remaining();
BufferUtil.clear(chunk);
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,content.remaining());
BufferUtil.flipToFlush(chunk,0);
}
_contentPrepared+=content.remaining();
return Result.FLUSH_CONTENT;
}
// 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.put(content,buffer);
// are we full?
if (BufferUtil.isAtCapacity(buffer))
{
BufferUtil.clear(chunk);
if (isChunking())
{
if (chunk==null)
return Result.NEED_CHUNK;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,buffer.remaining());
BufferUtil.flipToFlush(chunk,0);
}
return Result.FLUSH;
}
return Result.OK;
}
default:
throw new IllegalStateException();
}
@ -914,17 +915,21 @@ public class HttpGenerator
BufferUtil.putCRLF(chunk);
// Add the chunk size to the header
if (remaining>0)
{
BufferUtil.putHexInt(chunk, remaining);
BufferUtil.putCRLF(chunk);
// Need a CRLF after the content
_needCRLF=remaining>0;
_needCRLF=true;
}
else
{
chunk.put(LAST_CHUNK);
_needCRLF=false;
}
}
/* ------------------------------------------------------------ */
/**
* Complete the message.
*
* @throws IOException
*/
public Result flush(ByteBuffer chunk, ByteBuffer buffer) throws IOException
@ -933,14 +938,18 @@ public class HttpGenerator
{
case START:
return Result.NEED_COMMIT;
case CHUNKING:
case COMMITTED:
if (isChunking())
{
if (chunk==null)
return Result.NEED_CHUNK;
if (BufferUtil.hasContent(buffer))
{
BufferUtil.clear(chunk);
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,buffer.remaining());
BufferUtil.flipToFlush(chunk,0);
}
}
}
return Result.FLUSH;
@ -960,40 +969,37 @@ public class HttpGenerator
switch(_state)
{
case START:
case COMPLETING_UNCOMMITTED:
_state=State.COMPLETING_UNCOMMITTED;
return Result.NEED_COMMIT;
case CHUNKING:
case COMPLETING:
case COMMITTED:
_state=State.COMPLETING;
if (isChunking())
{
if (BufferUtil.hasContent(buffer))
{
if (chunk==null)
return Result.NEED_CHUNK;
if (BufferUtil.hasContent(buffer))
{
_state=State.COMPLETING;
BufferUtil.clear(chunk);
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,buffer.remaining());
BufferUtil.flipToFlush(chunk,0);
return Result.FLUSH;
}
if (!_sentEOC)
{
_state=State.END;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,0);
_sentEOC=true;
BufferUtil.flipToFlush(chunk,0);
return Result.FLUSH;
}
return Result.OK;
case STREAMING:
if (BufferUtil.hasContent(buffer))
{
_state=State.COMPLETING;
else if (BufferUtil.hasContent(buffer))
return Result.FLUSH;
}
_state=State.END;
return Result.OK;
}
return Result.OK;
}
/* ------------------------------------------------------------------------------- */
public static byte[] getReasonBuffer(int code)
@ -1004,11 +1010,11 @@ public class HttpGenerator
return null;
}
/* ------------------------------------------------------------------------------- */
@Override
public String toString()
{
return String.format("%s{s=%d}",
return String.format("%s{s=%s}",
getClass().getSimpleName(),
_state);
}

View File

@ -72,4 +72,16 @@ public enum HttpVersion
{
return _string;
}
/* ------------------------------------------------------------ */
public static HttpVersion fromVersion(int version)
{
switch(version)
{
case 9: return HttpVersion.HTTP_0_9;
case 10: return HttpVersion.HTTP_1_0;
case 11: return HttpVersion.HTTP_1_1;
default: throw new IllegalArgumentException();
}
}
}

View File

@ -13,8 +13,11 @@
package org.eclipse.jetty.http;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.matchers.JUnitMatchers.containsString;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -23,332 +26,444 @@ import javax.swing.text.View;
import org.eclipse.jetty.io.ByteArrayEndPoint;
import org.eclipse.jetty.io.SimpleBuffers;
import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Test;
import org.junit.matchers.JUnitMatchers;
public class HttpGeneratorClientTest
{
public final static String CONTENT="The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n";
public final static String[] connect={null,"keep-alive","close"};
@Test
public void testContentLength() throws Exception
public void testRequestNoContent() throws Exception
{
ByteBuffer bb=new ByteArrayBuffer(8096);
ByteBuffer sb=new ByteArrayBuffer(1500);
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096);
HttpGenerator generator = new HttpGenerator(new SimpleBuffers(sb,bb),endp);
generator.setRequest("GET","/usr");
ByteBuffer header=BufferUtil.allocate(8096);
HttpFields fields = new HttpFields();
fields.add("Header","Value");
fields.add("Content-Type","text/plain");
HttpGenerator gen = new HttpGenerator();
String content = "The quick brown fox jumped over the lazy dog";
fields.addLongField("Content-Length",content.length());
fields.add("Host","something");
fields.add("User-Agent","test");
generator.completeHeader(fields,false);
gen.setRequest(HttpMethod.GET,"/index.html",HttpVersion.HTTP_1_1);
generator.addContent(new ByteArrayBuffer(content),true);
generator.flushBuffer();
generator.complete();
generator.flushBuffer();
HttpGenerator.Result
result=gen.complete(null,null);
assertEquals(HttpGenerator.State.COMPLETING_UNCOMMITTED,gen.getState());
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
String result=endp.getOut().toString().replace("\r\n","|").replace('\r','|').replace('\n','|');
assertEquals("GET /usr HTTP/1.1|Header: Value|Content-Type: text/plain|Content-Length: 44||"+content,result);
result=gen.commit(fields,header,null,null,true);
assertEquals(HttpGenerator.Result.NEED_COMPLETE,result);
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(out,containsString("GET /index.html HTTP/1.1"));
assertThat(out,not(containsString("Content-Length")));
result=gen.complete(null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,gen.getContentWritten()); }
@Test
public void testRequestWithSmallContent() throws Exception
{
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.");
HttpFields fields = new HttpFields();
HttpGenerator gen = new HttpGenerator();
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setRequest("POST","/index.html");
fields.add("Host","something");
fields.add("User-Agent","test");
HttpGenerator.Result
result=gen.prepareContent(null,null,content);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.prepareContent(null,buffer,content);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World",BufferUtil.toString(buffer));
assertTrue(BufferUtil.isEmpty(content));
result=gen.prepareContent(null,buffer,content1);
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(content));
result=gen.complete(null,buffer);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.COMPLETING_UNCOMMITTED,gen.getState());
result=gen.commit(fields,header,buffer,content,true);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.complete(null,buffer);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertThat(head,containsString("POST /index.html HTTP/1.1"));
assertThat(head,containsString("Host: something"));
assertThat(head,containsString("Content-Length: 58"));
assertTrue(head.endsWith("\r\n\r\n"));
assertEquals("Hello World. The quick brown fox jumped over the lazy dog.",body);
assertEquals(58,gen.getContentWritten());
}
@Test
public void testAutoContentLength() throws Exception
public void testRequestWithChunkedContent() throws Exception
{
ByteBuffer bb=new ByteArrayBuffer(8096);
ByteBuffer sb=new ByteArrayBuffer(1500);
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096);
HttpGenerator generator = new HttpGenerator(new SimpleBuffers(sb,bb),endp);
generator.setRequest("GET","/usr");
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. ");
HttpFields fields = new HttpFields();
fields.add("Header","Value");
fields.add("Content-Type","text/plain");
HttpGenerator gen = new HttpGenerator();
String content = "The quick brown fox jumped over the lazy dog";
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setRequest("POST","/index.html");
fields.add("Host","something");
fields.add("User-Agent","test");
generator.addContent(new ByteArrayBuffer(content),true);
generator.completeHeader(fields,true);
HttpGenerator.Result
generator.flushBuffer();
generator.complete();
generator.flushBuffer();
result=gen.prepareContent(null,null,content0);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
String result=endp.getOut().toString().replace("\r\n","|").replace('\r','|').replace('\n','|');
assertEquals("GET /usr HTTP/1.1|Header: Value|Content-Type: text/plain|Content-Length: 44||"+content,result);
result=gen.prepareContent(null,buffer,content0);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! ",BufferUtil.toString(buffer));
assertEquals(0,content0.remaining());
result=gen.prepareContent(null,buffer,content1);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
result=gen.commit(fields,header,buffer,content1,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTING,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertTrue(gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.commit(fields,header,buffer,content1,false);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.prepareContent(null,buffer,content1);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
result=gen.prepareContent(chunk,buffer,content1);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
assertEquals(" quick brown fox",BufferUtil.toString(buffer));
assertEquals(27,content1.remaining());
body += BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.prepareContent(chunk,buffer,content1);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
assertEquals(" jumped over the",BufferUtil.toString(buffer));
assertEquals(11,content1.remaining());
body += BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.prepareContent(chunk,buffer,content1);
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.complete(chunk,buffer);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertEquals("\r\nB\r\n",BufferUtil.toString(chunk));
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
body += BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.complete(chunk,buffer);
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(buffer);
result=gen.complete(chunk,buffer);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(59,gen.getContentWritten());
// 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"));
}
@Test
public void testChunked() throws Exception
public void testRequestWithLargeChunkedContent() throws Exception
{
ByteBuffer bb=new ByteArrayBuffer(8096);
ByteBuffer sb=new ByteArrayBuffer(1500);
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096);
HttpGenerator generator = new HttpGenerator(new SimpleBuffers(sb,bb),endp);
generator.setRequest("GET","/usr");
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. ");
HttpFields fields = new HttpFields();
fields.add("Header","Value");
fields.add("Content-Type","text/plain");
HttpGenerator gen = new HttpGenerator();
gen.setLargeContent(8);
String content = "The quick brown fox jumped over the lazy dog";
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setRequest("POST","/index.html");
fields.add("Host","something");
fields.add("User-Agent","test");
generator.completeHeader(fields,false);
HttpGenerator.Result
generator.addContent(new ByteArrayBuffer(content),false);
generator.flushBuffer();
generator.complete();
generator.flushBuffer();
result=gen.prepareContent(null,null,content0);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.START,gen.getState());
String result=endp.getOut().toString().replace("\r\n","|").replace('\r','|').replace('\n','|');
assertEquals("GET /usr HTTP/1.1|Header: Value|Content-Type: text/plain|Transfer-Encoding: chunked||2C|"+content+"|0||",result);
result=gen.commit(fields,header,null,content0,false);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.commit(fields,header,null,content0,false);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.prepareContent(null,null,content1);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
result=gen.prepareContent(chunk,null,content1);
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.complete(chunk,null);
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.complete(chunk,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(65,gen.getContentWritten());
// 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"));
}
@Test
public void testRequestWithKnownContent() throws Exception
{
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. ");
HttpFields fields = new HttpFields();
HttpGenerator gen = new HttpGenerator();
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setRequest("POST","/index.html");
fields.add("Host","something");
fields.add("User-Agent","test");
fields.add("Content-Length","59");
gen.setContentLength(59);
HttpGenerator.Result
result=gen.prepareContent(null,null,content0);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.prepareContent(null,buffer,content0);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! ",BufferUtil.toString(buffer));
assertEquals(0,content0.remaining());
result=gen.prepareContent(null,buffer,content1);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
result=gen.commit(fields,header,buffer,content1,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertTrue(!gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.prepareContent(null,buffer,content1);
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.prepareContent(null,buffer,content1);
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.prepareContent(null,buffer,content1);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
assertEquals(0,content1.remaining());
result=gen.complete(null,buffer);
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.complete(null,buffer);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,buffer.remaining());
assertEquals(59,gen.getContentWritten());
// 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"));
}
@Test
public void testHTTP() throws Exception
public void testRequestWithKnownLargeContent() throws Exception
{
ByteBuffer bb=new ByteArrayBuffer(8096);
ByteBuffer sb=new ByteArrayBuffer(1500);
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpFields fields = new HttpFields();
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096);
HttpGenerator hb = new HttpGenerator(new SimpleBuffers(sb,bb),endp);
Handler handler = new Handler();
HttpParser parser=null;
HttpGenerator gen = new HttpGenerator();
gen.setLargeContent(8);
// For HTTP version
for (int v=9;v<=11;v++)
{
// For each test result
for (int r=0;r<tr.length;r++)
{
// chunks = 1 to 3
for (int chunks=1;chunks<=6;chunks++)
{
// For none, keep-alive, close
for (int c=0;c<connect.length;c++)
{
String t="v="+v+",r="+r+",chunks="+chunks+",c="+c+",tr="+tr[r];
// System.err.println(t);
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setRequest("POST","/index.html");
fields.add("Host","something");
fields.add("User-Agent","test");
fields.add("Content-Length","59");
gen.setContentLength(59);
hb.reset();
endp.reset();
fields.clear();
HttpGenerator.Result
// System.out.println("TEST: "+t);
result=gen.prepareContent(null,null,content0);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.START,gen.getState());
try
{
tr[r].build(v,hb,connect[c],null,chunks, fields);
}
catch(IllegalStateException e)
{
if (v<10 || v==10 && chunks>2)
continue;
System.err.println(t);
throw e;
}
String request=endp.getOut().toString();
// System.out.println(request+(hb.isPersistent()?"...\n":"---\n"));
result=gen.commit(fields,header,null,content0,false);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(!gen.isChunking());
assertTrue(t,hb.isPersistent());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(content0);
BufferUtil.clear(content0);
if (v==9)
{
assertEquals(t,"GET /context/path/info\r\n", request);
continue;
result=gen.commit(fields,header,null,null,false);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.prepareContent(null,null,content1);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
body += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.complete(null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
result=gen.complete(null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(59,gen.getContentWritten());
// 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"));
}
parser=new HttpParser(new ByteArrayBuffer(request.getBytes()), handler);
try
{
parser.parse();
}
catch(IOException e)
{
if (tr[r].body!=null)
throw e;
continue;
}
if (tr[r].body!=null)
assertEquals(t,tr[r].body, this.content);
if (v==10)
assertTrue(t,hb.isPersistent() || tr[r].values[1]==null || c==2 || c==0);
else
assertTrue(t,hb.isPersistent() || c==2);
assertTrue(t,tr[r].values[1]==null || content.length()==Integer.parseInt(tr[r].values[1]));
}
}
}
}
}
private static final String[] headers= { "Content-Type","Content-Length","Connection","Transfer-Encoding","Other"};
private static class TR
{
private String[] values=new String[headers.length];
private String body;
private TR(String ct, String cl ,String content)
{
values[0]=ct;
values[1]=cl;
values[4]="value";
this.body=content;
}
private void build(int version,HttpGenerator hb, String connection, String te, int chunks, HttpFields fields)
throws Exception
{
values[2]=connection;
values[3]=te;
hb.setRequest(HttpMethod.GET,"/context/path/info");
hb.setVersion(version);
for (int i=0;i<headers.length;i++)
{
if (values[i]==null)
continue;
fields.put(new ByteArrayBuffer(headers[i]),new ByteArrayBuffer(values[i]));
}
if (body!=null)
{
int inc=1+body.length()/chunks;
ByteBuffer buf=new ByteArrayBuffer(body);
View view = new View(buf);
for (int i=1;i<chunks;i++)
{
view.setPutIndex(i*inc);
view.setGetIndex((i-1)*inc);
hb.addContent(view,Generator.MORE);
if (hb.isBufferFull() && hb.isState(AbstractGenerator.STATE_HEADER))
hb.completeHeader(fields, Generator.MORE);
if (i%2==0)
{
if (hb.isState(AbstractGenerator.STATE_HEADER))
{
if (version<11)
fields.addLongField("Content-Length",body.length());
hb.completeHeader(fields, Generator.MORE);
}
hb.flushBuffer();
}
}
view.setPutIndex(buf.putIndex());
view.setGetIndex((chunks-1)*inc);
hb.addContent(view,Generator.LAST);
if(hb.isState(AbstractGenerator.STATE_HEADER))
hb.completeHeader(fields, Generator.LAST);
}
else
{
hb.completeHeader(fields, Generator.LAST);
}
hb.complete();
while(!hb.isComplete())
hb.flushBuffer();
}
@Override
public String toString()
{
return "["+values[0]+","+values[1]+","+(body==null?"none":"_content")+"]";
}
}
private final TR[] tr =
{
/* 0 */ new TR(null,null,null),
/* 1 */ new TR(null,null,CONTENT),
/* 3 */ new TR(null,""+CONTENT.length(),CONTENT),
/* 4 */ new TR("text/html",null,null),
/* 5 */ new TR("text/html",null,CONTENT),
/* 7 */ new TR("text/html",""+CONTENT.length(),CONTENT),
};
private String content;
private String f0;
private String f1;
private String f2;
private String[] hdr;
private String[] val;
private int h;
private class Handler extends HttpParser.EventHandler
{
private int index=0;
@Override
public void content(ByteBuffer ref)
{
if (index == 0)
content= "";
content= content.substring(0, index) + ref;
index+=ref.length();
}
@Override
public void startRequest(ByteBuffer tok0, ByteBuffer tok1, ByteBuffer tok2)
{
h= -1;
hdr= new String[9];
val= new String[9];
f0= tok0.toString();
f1= tok1.toString();
if (tok2!=null)
f2= tok2.toString();
else
f2=null;
index=0;
// System.out.println(f0+" "+f1+" "+f2);
}
/* (non-Javadoc)
* @see org.eclipse.jetty.EventHandler#startResponse(org.eclipse.io.Buffer, int, org.eclipse.io.Buffer)
*/
@Override
public void startResponse(ByteBuffer version, int status, ByteBuffer reason)
{
h= -1;
hdr= new String[9];
val= new String[9];
f0= version.toString();
f1= ""+status;
if (reason!=null)
f2= reason.toString();
else
f2=null;
index=0;
}
@Override
public void parsedHeader(ByteBuffer name,ByteBuffer value)
{
hdr[++h]= name.toString();
val[h]= value.toString();
}
@Override
public void headerComplete()
{
content= null;
}
@Override
public void messageComplete(long contentLength)
{
}
}
}

View File

@ -13,9 +13,12 @@
package org.eclipse.jetty.http;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.matchers.JUnitMatchers.containsString;
import java.io.IOException;
import java.nio.ByteBuffer;
@ -38,8 +41,10 @@ public class HttpGeneratorTest
public final static String CONTENT="The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n";
public final static String[] connect={null,"keep-alive","close","TE, close"};
@Test
public void testRequest() throws Exception
public void testResponseNoContent() throws Exception
{
ByteBuffer header=BufferUtil.allocate(8096);
HttpFields fields = new HttpFields();
@ -52,68 +57,411 @@ public class HttpGeneratorTest
HttpGenerator.Result
result=gen.complete(null,null);
assertEquals(HttpGenerator.State.COMPLETING_UNCOMMITTED,gen.getState());
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
result=gen.commit(fields,header,null,null,true);
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
assertEquals(HttpGenerator.Result.NEED_COMPLETE,result);
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
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")));
result=gen.complete(null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertTrue(out.indexOf("GET /index.html HTTP/1.1")==0);
assertTrue(out.indexOf("Content-Length")==-1);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,gen.getContentWritten()); }
assertEquals(0,gen.getContentWritten());
}
@Test
public void testRequestWithSmallContent() throws Exception
public void testResponseWithSmallContent() throws Exception
{
ByteBuffer header=BufferUtil.allocate(8096);
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.");
HttpFields fields = new HttpFields();
HttpGenerator gen = new HttpGenerator();
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setRequest("POST","/index.html");
fields.add("Host","something");
fields.add("User-Agent","test");
gen.setResponse(200,null);
fields.add("Last-Modified",HttpFields.__01Jan1970);
HttpGenerator.Result
result=gen.prepareContent(null,null,content);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.prepareContent(null,buffer,content);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World",BufferUtil.toString(buffer));
assertTrue(BufferUtil.isEmpty(content));
result=gen.prepareContent(null,buffer,content1);
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(content));
result=gen.complete(null,buffer);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.COMPLETING_UNCOMMITTED,gen.getState());
result=gen.commit(fields,header,buffer,content,true);
assertEquals(HttpGenerator.Result.FLUSH,result);
String out = BufferUtil.toString(header);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.complete(null,buffer);
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.getContentWritten());
}
@Test
public void testResponseWithChunkedContent() throws Exception
{
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. ");
HttpFields fields = new HttpFields();
HttpGenerator gen = new HttpGenerator();
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setResponse(200,null);
fields.add("Last-Modified",HttpFields.__01Jan1970);
HttpGenerator.Result
result=gen.prepareContent(null,null,content0);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.prepareContent(null,buffer,content0);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! ",BufferUtil.toString(buffer));
assertEquals(0,content0.remaining());
result=gen.prepareContent(null,buffer,content1);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
result=gen.commit(fields,header,buffer,content1,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertTrue(gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.prepareContent(null,buffer,content1);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
result=gen.prepareContent(chunk,buffer,content1);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
assertEquals(" quick brown fox",BufferUtil.toString(buffer));
assertEquals(27,content1.remaining());
body += BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.prepareContent(chunk,buffer,content1);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("\r\n10\r\n",BufferUtil.toString(chunk));
assertEquals(" jumped over the",BufferUtil.toString(buffer));
assertEquals(11,content1.remaining());
body += BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.prepareContent(chunk,buffer,content1);
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.complete(chunk,buffer);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMPLETING,gen.getState());
assertEquals("\r\nB\r\n",BufferUtil.toString(chunk));
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
body += BufferUtil.toString(chunk)+BufferUtil.toString(buffer);
BufferUtil.clear(chunk);
BufferUtil.clear(buffer);
result=gen.complete(chunk,buffer);
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(buffer);
result=gen.complete(chunk,buffer);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(59,gen.getContentWritten());
// System.err.println(head+body);
assertThat(head,containsString("HTTP/1.1 200 OK"));
assertThat(head,containsString("Last-Modified: Thu, 01 Jan 1970 00?00?00 GMT"));
assertThat(head,not(containsString("Content-Length")));
assertThat(head,containsString("Transfer-Encoding: chunked"));
assertTrue(head.endsWith("\r\n\r\n10\r\n"));
}
@Test
public void testResponseWithLargeChunkedContent() throws Exception
{
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. ");
HttpFields fields = new HttpFields();
HttpGenerator gen = new HttpGenerator();
gen.setLargeContent(8);
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setResponse(200,null);
fields.add("Last-Modified",HttpFields.__01Jan1970);
HttpGenerator.Result
result=gen.prepareContent(null,null,content0);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.commit(fields,header,null,content0,false);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.commit(fields,header,null,content0,false);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.prepareContent(null,null,content1);
assertEquals(HttpGenerator.Result.NEED_CHUNK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
ByteBuffer chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
result=gen.prepareContent(chunk,null,content1);
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.complete(chunk,null);
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.complete(chunk,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(65,gen.getContentWritten());
// 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"));
}
result=gen.commit(fields,header,null,null,true);
assertEquals(HttpGenerator.Result.NEED_COMPLETE,result);
@Test
public void testResponseWithKnownContent() throws Exception
{
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. ");
HttpFields fields = new HttpFields();
HttpGenerator gen = new HttpGenerator();
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setResponse(200,null);
fields.add("Last-Modified",HttpFields.__01Jan1970);
fields.add("Content-Length","59");
gen.setContentLength(59);
HttpGenerator.Result
result=gen.prepareContent(null,null,content0);
assertEquals(HttpGenerator.Result.NEED_BUFFER,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.prepareContent(null,buffer,content0);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! ",BufferUtil.toString(buffer));
assertEquals(0,content0.remaining());
result=gen.prepareContent(null,buffer,content1);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.START,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
result=gen.commit(fields,header,buffer,content1,false);
assertEquals(HttpGenerator.Result.FLUSH,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals("Hello World! The",BufferUtil.toString(buffer));
assertEquals(43,content1.remaining());
assertTrue(!gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(buffer);
BufferUtil.clear(buffer);
result=gen.prepareContent(null,buffer,content1);
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.prepareContent(null,buffer,content1);
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.prepareContent(null,buffer,content1);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertEquals(" lazy dog. ",BufferUtil.toString(buffer));
assertEquals(0,content1.remaining());
result=gen.complete(null,buffer);
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.complete(null,buffer);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,buffer.remaining());
assertEquals(59,gen.getContentWritten());
// 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
{
ByteBuffer header=BufferUtil.allocate(4096);
ByteBuffer content0=BufferUtil.toBuffer("Hello World! ");
ByteBuffer content1=BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpFields fields = new HttpFields();
HttpGenerator gen = new HttpGenerator();
gen.setLargeContent(8);
gen.setVersion(HttpVersion.HTTP_1_1);
gen.setResponse(200,null);
fields.add("Last-Modified",HttpFields.__01Jan1970);
fields.add("Content-Length","59");
gen.setContentLength(59);
HttpGenerator.Result
result=gen.prepareContent(null,null,content0);
assertEquals(HttpGenerator.Result.NEED_COMMIT,result);
assertEquals(HttpGenerator.State.START,gen.getState());
result=gen.commit(fields,header,null,content0,false);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
assertTrue(!gen.isChunking());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
String body = BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.commit(fields,header,null,null,false);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
result=gen.prepareContent(null,null,content1);
assertEquals(HttpGenerator.Result.FLUSH_CONTENT,result);
assertEquals(HttpGenerator.State.COMMITTED,gen.getState());
body += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.complete(null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertTrue(out.indexOf("GET /index.html HTTP/1.1")==0);
assertTrue(out.indexOf("Content-Length")==-1);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(0,gen.getContentWritten());
result=gen.complete(null,null);
assertEquals(HttpGenerator.Result.OK,result);
assertEquals(HttpGenerator.State.END,gen.getState());
assertEquals(59,gen.getContentWritten());
// 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"));
}
@ -122,14 +470,10 @@ public class HttpGeneratorTest
@Test
public void testHTTP() throws Exception
{
ByteBuffer bb=new ByteArrayBuffer(8096);
ByteBuffer sb=new ByteArrayBuffer(1500);
HttpFields fields = new HttpFields();
ByteArrayEndPoint endp = new ByteArrayEndPoint(new byte[0],4096);
HttpGenerator hb = new HttpGenerator(new SimpleBuffers(sb,bb),endp);
Handler handler = new Handler();
HttpParser parser=null;
HttpGenerator gen = new HttpGenerator();
// Handler handler = new Handler();
// HttpParser parser=null;
// For HTTP version
@ -147,22 +491,22 @@ public class HttpGeneratorTest
String t="v="+v+",r="+r+",chunks="+chunks+",connect="+c+",tr="+tr[r];
// System.err.println(t);
hb.reset();
endp.reset();
gen.reset();
fields.clear();
tr[r].build(v,hb,"OK\r\nTest",connect[c],null,chunks, fields);
String response=endp.getOut().toString();
//System.out.println("RESPONSE: "+t+"\n"+response+(hb.isPersistent()?"...\n":"---\n"));
String response=tr[r].build(v,gen,"OK\r\nTest",connect[c],null,chunks, fields);
System.out.println("RESPONSE: "+t+"\n"+response+(gen.isPersistent()?"...\n":"---\n"));
if (v==9)
{
assertFalse(t,hb.isPersistent());
assertFalse(t,gen.isPersistent());
if (tr[r]._body!=null)
assertEquals(t,tr[r]._body, response);
continue;
}
/*
parser=new HttpParser(new ByteArrayBuffer(response.getBytes()), handler);
parser.setHeadResponse(tr[r]._head);
@ -192,6 +536,7 @@ public class HttpGeneratorTest
assertTrue(t,tr[r]._body==null);
else
assertTrue(t,tr[r]._contentLength==null || content.length()==Integer.parseInt(tr[r]._contentLength));
*/
}
}
}
@ -220,58 +565,210 @@ public class HttpGeneratorTest
_head=head;
}
private void build(int version,HttpGenerator hb,String reason, String connection, String te, int chunks, HttpFields fields) throws Exception
private String build(int version,HttpGenerator gen,String reason, String connection, String te, int chunks, HttpFields fields) throws Exception
{
String response="";
_connection=connection;
_te=te;
hb.setVersion(version);
hb.setResponse(_code,reason);
hb.setHead(_head);
gen.setVersion(HttpVersion.fromVersion(version));
gen.setResponse(_code,reason);
gen.setHead(_head);
if (_contentType!=null)
fields.put(new ByteArrayBuffer("Content-Type"),new ByteArrayBuffer(_contentType));
fields.put("Content-Type",_contentType);
if (_contentLength!=null)
fields.put(new ByteArrayBuffer("Content-Length"),new ByteArrayBuffer(_contentLength));
{
fields.put("Content-Length",_contentLength);
gen.setContentLength(Long.parseLong(_contentLength));
}
if (_connection!=null)
fields.put(new ByteArrayBuffer("Connection"),new ByteArrayBuffer(_connection));
fields.put("Connection",_connection);
if (_te!=null)
fields.put(new ByteArrayBuffer("Transfer-Encoding"),new ByteArrayBuffer(_te));
fields.put("Transfer-Encoding",_te);
if (_other!=null)
fields.put(new ByteArrayBuffer("Other"),new ByteArrayBuffer(_other));
fields.put("Other",_other);
if (_body!=null)
ByteBuffer content=_body==null?null:BufferUtil.toBuffer(_body);
if (content!=null)
content.limit(0);
ByteBuffer chunk=null;
ByteBuffer buffer=null;
mainLoop: while(true)
{
int inc=1+_body.length()/chunks;
ByteBuffer buf=new ByteArrayBuffer(_body);
View view = new View(buf);
for (int i=1;i<chunks;i++)
// if we have unwritten content
if (content!=null && content.position()<content.capacity())
{
view.setPutIndex(i*inc);
view.setGetIndex((i-1)*inc);
hb.addContent(view,Generator.MORE);
if (hb.isBufferFull() && hb.isState(AbstractGenerator.STATE_HEADER))
hb.completeHeader(fields, Generator.MORE);
if (i%2==0)
// if we need a new chunk
if (content.remaining()==0)
{
if (hb.isState(AbstractGenerator.STATE_HEADER))
hb.completeHeader(fields, Generator.MORE);
hb.flushBuffer();
}
}
view.setPutIndex(buf.putIndex());
view.setGetIndex((chunks-1)*inc);
hb.addContent(view,Generator.LAST);
if(hb.isState(AbstractGenerator.STATE_HEADER))
hb.completeHeader(fields, Generator.LAST);
}
if (chunks-->1)
content.limit(content.position()+content.remaining()/2);
else
{
hb.completeHeader(fields, Generator.LAST);
content.limit(content.capacity());
}
hb.complete();
while(!hb.isComplete())
hb.flushBuffer();
switch(gen.getState())
{
case START:
case COMPLETING_UNCOMMITTED:
case COMMITTED:
case COMPLETING:
case END:
}
}
switch(gen.prepareContent(chunk,buffer,content))
{
case FLUSH:
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(chunk))
{
response+=BufferUtil.toString(chunk);
chunk.position(chunk.limit());
}
if (BufferUtil.hasContent(content))
{
response+=BufferUtil.toString(content);
content.position(content.limit());
}
break;
case NEED_BUFFER:
buffer=BufferUtil.allocate(8192);
break;
case NEED_CHUNK:
chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
break;
case NEED_COMMIT:
{
commitLoop: while (true)
{
ByteBuffer header=BufferUtil.allocate(4096);
switch(gen.commit(fields,header,buffer,content,chunks==0))
{
case FLUSH:
if (BufferUtil.hasContent(header))
{
response+=BufferUtil.toString(header);
header.position(header.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());
}
if (BufferUtil.hasContent(content))
{
response+=BufferUtil.toString(content);
content.position(content.limit());
}
break;
case NEED_BUFFER:
buffer=BufferUtil.allocate(8192);
break;
case OK:
break commitLoop;
default:
throw new IllegalStateException(gen.toString());
}
}
}
break;
case NEED_COMPLETE:
{
completeLoop: while (true)
{
ByteBuffer header=BufferUtil.allocate(4096);
switch(gen.complete(chunk,buffer))
{
case FLUSH:
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 OK:
break completeLoop;
default:
throw new IllegalStateException(gen.toString());
}
}
}
break;
}
continue;
}
while (true)
{
switch(gen.complete(chunk,buffer))
{
case FLUSH:
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 OK:
break mainLoop;
default:
throw new IllegalStateException(gen.toString());
}
}
}
return response;
}
@Override

View File

@ -34,13 +34,6 @@ public class BufferUtil
(byte)'E', (byte)'F' };
/* ------------------------------------------------------------ */
public static void clear(ByteBuffer buffer)
{
buffer.position(0);
buffer.limit(0);
}
/* ------------------------------------------------------------ */
/** Allocate ByteBuffer in output mode.
* The position and limit will both be zero, indicating that the buffer is
@ -70,10 +63,25 @@ public class BufferUtil
}
/* ------------------------------------------------------------ */
public static void clear(ByteBuffer buffer)
{
buffer.position(0);
buffer.limit(0);
}
/* ------------------------------------------------------------ */
public static void clearToFill(ByteBuffer buffer)
{
buffer.position(0);
buffer.limit(buffer.capacity());
}
/* ------------------------------------------------------------ */
public static void flipToFill(ByteBuffer buffer)
{
buffer.position(buffer.limit());
buffer.position(buffer.hasRemaining()?buffer.limit():0);
buffer.limit(buffer.capacity());
}
@ -128,7 +136,7 @@ public class BufferUtil
/**
* Put data from one buffer into another, avoiding over/under flows
* @param from Buffer to take bytes from
* @param to Buffer to put bytes to
* @param to Buffer to put bytes to. The buffer is flipped before and after the put.
* @return number of bytes moved
*/
public static int put(ByteBuffer from, ByteBuffer to, long maxBytes)
@ -141,7 +149,7 @@ public class BufferUtil
/**
* Put data from one buffer into another, avoiding over/under flows
* @param from Buffer to take bytes from
* @param to Buffer to put bytes to
* @param to Buffer to put bytes to. The buffer is flipped before and after the put.
* @return number of bytes moved
*/
public static int put(ByteBuffer from, ByteBuffer to, int maxBytes)
@ -191,7 +199,7 @@ public class BufferUtil
/**
* Put data from one buffer into another, avoiding over/under flows
* @param from Buffer to take bytes from
* @param to Buffer to put bytes to
* @param to Buffer to put bytes to. The buffer is flipped before and after the put.
* @return number of bytes moved
*/
public static int put(ByteBuffer from, ByteBuffer to)