Trailers for generated requests

This commit is contained in:
Greg Wilkins 2017-02-02 13:27:12 +11:00
parent 7923032582
commit 80386028c1
7 changed files with 384 additions and 169 deletions

View File

@ -24,6 +24,7 @@ import java.io.IOException;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.function.Supplier;
import org.eclipse.jetty.http.HttpTokens.EndOfContent;
import org.eclipse.jetty.util.ArrayTrie;
@ -55,7 +56,7 @@ public class HttpGenerator
// states
public enum State { START, 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_CHUNK_TRAILER, FLUSH,CONTINUE,SHUTDOWN_OUT,DONE}
// other statics
public static final int CHUNK_SIZE = 12;
@ -66,6 +67,7 @@ public class HttpGenerator
private long _contentPrepared = 0;
private boolean _noContentResponse = false;
private Boolean _persistent = null;
private Supplier<HttpFields> _trailers = null;
private final int _send;
private final static int SEND_SERVER = 0x01;
@ -111,6 +113,7 @@ public class HttpGenerator
_persistent = null;
_contentPrepared = 0;
_needCRLF = false;
_trailers = null;
}
/* ------------------------------------------------------------ */
@ -278,52 +281,12 @@ public class HttpGenerator
case COMMITTED:
{
int len = BufferUtil.length(content);
if (len>0)
{
// Do we need a chunk buffer?
if (isChunking())
{
// Do we need a chunk buffer?
if (chunk==null)
return Result.NEED_CHUNK;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,len);
BufferUtil.flipToFlush(chunk,0);
}
_contentPrepared+=len;
}
if (last)
_state=State.COMPLETING;
return len>0?Result.FLUSH:Result.CONTINUE;
return committed(chunk,content,last);
}
case COMPLETING:
{
if (BufferUtil.hasContent(content))
{
if (LOG.isDebugEnabled())
LOG.debug("discarding content in COMPLETING");
BufferUtil.clear(content);
}
if (isChunking())
{
// Do we need a chunk buffer?
if (chunk==null)
return Result.NEED_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;
return completing(chunk,content);
}
case END:
@ -340,7 +303,82 @@ public class HttpGenerator
}
}
private Result committed( ByteBuffer chunk, ByteBuffer content, boolean last)
{
int len = BufferUtil.length(content);
// handle the content.
if (len>0)
{
if (isChunking())
{
if (chunk==null)
return Result.NEED_CHUNK;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,len);
BufferUtil.flipToFlush(chunk,0);
}
_contentPrepared+=len;
}
if (last)
{
_state=State.COMPLETING;
return len>0?Result.FLUSH:Result.CONTINUE;
}
return len>0?Result.FLUSH:Result.DONE;
}
private Result completing( ByteBuffer chunk, ByteBuffer content)
{
if (BufferUtil.hasContent(content))
{
if (LOG.isDebugEnabled())
LOG.debug("discarding content in COMPLETING");
BufferUtil.clear(content);
}
if (isChunking())
{
if (_trailers!=null)
{
// Do we need a chunk buffer?
if (chunk==null || chunk.capacity()<=CHUNK_SIZE)
return Result.NEED_CHUNK_TRAILER;
HttpFields trailers = _trailers.get();
if (trailers!=null)
{
// Write the last chunk
BufferUtil.clearToFill(chunk);
generateTrailers(chunk,trailers);
BufferUtil.flipToFlush(chunk,0);
_endOfContent=EndOfContent.UNKNOWN_CONTENT;
return Result.FLUSH;
}
}
// Do we need a chunk buffer?
if (chunk==null)
return Result.NEED_CHUNK;
// 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;
}
/* ------------------------------------------------------------ */
@Deprecated
public Result generateResponse(MetaData.Response info, ByteBuffer header, ByteBuffer chunk, ByteBuffer content, boolean last) throws IOException
{
return generateResponse(info,false,header,chunk,content,last);
@ -386,7 +424,7 @@ public class HttpGenerator
// prepare the header
int pos=BufferUtil.flipToFill(header);
try
{
{
// generate ResponseLine
generateResponseLine(info,header);
@ -442,29 +480,7 @@ public class HttpGenerator
case COMMITTED:
{
int len = BufferUtil.length(content);
// handle the content.
if (len>0)
{
if (isChunking())
{
if (chunk==null)
return Result.NEED_CHUNK;
BufferUtil.clearToFill(chunk);
prepareChunk(chunk,len);
BufferUtil.flipToFlush(chunk,0);
}
_contentPrepared+=len;
}
if (last)
{
_state=State.COMPLETING;
return len>0?Result.FLUSH:Result.CONTINUE;
}
return len>0?Result.FLUSH:Result.DONE;
return committed(chunk,content,last);
}
case COMPLETING_1XX:
@ -475,30 +491,7 @@ public class HttpGenerator
case COMPLETING:
{
if (BufferUtil.hasContent(content))
{
if (LOG.isDebugEnabled())
LOG.debug("discarding content in COMPLETING");
BufferUtil.clear(content);
}
if (isChunking())
{
// Do we need a chunk buffer?
if (chunk==null)
return Result.NEED_CHUNK;
// 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;
return completing(chunk,content);
}
case END:
@ -535,6 +528,30 @@ public class HttpGenerator
_needCRLF=false;
}
}
/* ------------------------------------------------------------ */
private void generateTrailers(ByteBuffer buffer, HttpFields trailer)
{
// if we need CRLF add this to header
if (_needCRLF)
BufferUtil.putCRLF(buffer);
// Add the chunk size to the header
buffer.put(ZERO_CHUNK);
int n=trailer.size();
for (int f=0;f<n;f++)
{
HttpField field = trailer.getField(f);
String v = field.getValue();
if (v==null || v.length()==0)
continue; // rfc7230 does not allow no value
putTo(field,buffer);
}
BufferUtil.putCRLF(buffer);
}
/* ------------------------------------------------------------ */
private void generateRequestLine(MetaData.Request request,ByteBuffer header)
@ -614,7 +631,8 @@ public class HttpGenerator
HttpField transfer_encoding=null;
boolean http11 = info.getHttpVersion() == HttpVersion.HTTP_1_1;
boolean close = false;
boolean chunked_hint = false;
_trailers = http11?info.getTrailerSupplier():null;
boolean chunked_hint = _trailers!=null;
boolean content_type = false;
long content_length = info.getContentLength();
boolean content_length_field = false;
@ -697,7 +715,7 @@ public class HttpGenerator
}
// Can we work out the content length?
if (last && content_length<0)
if (last && content_length<0 && _trailers==null)
content_length = _contentPrepared+BufferUtil.length(content);
// Calculate how to end _content and connection, _content length and transfer encoding
@ -840,6 +858,7 @@ public class HttpGenerator
/* ------------------------------------------------------------------------------- */
/* ------------------------------------------------------------------------------- */
// common _content
private static final byte[] ZERO_CHUNK = { (byte) '0', (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_CLOSE = StringUtil.getBytes("Connection: close\015\012");

View File

@ -20,12 +20,14 @@ package org.eclipse.jetty.http;
import java.util.Collections;
import java.util.Iterator;
import java.util.function.Supplier;
public class MetaData implements Iterable<HttpField>
{
private HttpVersion _httpVersion;
private HttpFields _fields;
private final HttpFields _fields;
private long _contentLength;
private Supplier<HttpFields> _trailers;
public MetaData(HttpVersion version, HttpFields fields)
{
@ -90,6 +92,16 @@ public class MetaData implements Iterable<HttpField>
return _fields;
}
public Supplier<HttpFields> getTrailerSupplier()
{
return _trailers;
}
public void setTrailerSupplier(Supplier<HttpFields> trailers)
{
_trailers = trailers;
}
/**
* @return the content length if available, otherwise {@link Long#MIN_VALUE}
*/

View File

@ -69,7 +69,7 @@ public class HttpGeneratorClientTest
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
result=gen.generateResponse(null,null,null,null, false);
result=gen.generateResponse(null,false,null,null, null, false);
Assert.assertEquals(HttpGenerator.Result.DONE, result);
Assert.assertEquals(HttpGenerator.State.END, gen.getState());
Assert.assertTrue(!gen.isChunking());
@ -106,7 +106,7 @@ public class HttpGeneratorClientTest
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
result=gen.generateResponse(null,null,null,null, false);
result=gen.generateResponse(null,false,null,null, null, false);
Assert.assertEquals(HttpGenerator.Result.DONE, result);
Assert.assertEquals(HttpGenerator.State.END, gen.getState());
Assert.assertTrue(!gen.isChunking());
@ -146,7 +146,7 @@ public class HttpGeneratorClientTest
out+=BufferUtil.toString(content0);
BufferUtil.clear(content0);
result=gen.generateResponse(null,null,null,null, false);
result=gen.generateResponse(null,false,null,null, null, false);
Assert.assertEquals(HttpGenerator.Result.DONE, result);
Assert.assertEquals(HttpGenerator.State.END, gen.getState());
Assert.assertTrue(!gen.isChunking());
@ -205,19 +205,19 @@ public class HttpGeneratorClientTest
out+=BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generateResponse(null,null,chunk,null, true);
result=gen.generateResponse(null,false,null,chunk, null, true);
Assert.assertEquals(HttpGenerator.Result.CONTINUE, result);
Assert.assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
Assert.assertTrue(gen.isChunking());
result=gen.generateResponse(null,null,chunk,null, true);
result=gen.generateResponse(null,false,null,chunk, null, true);
Assert.assertEquals(HttpGenerator.Result.FLUSH, result);
Assert.assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
out+=BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
Assert.assertTrue(!gen.isChunking());
result=gen.generateResponse(null,null,chunk,null, true);
result=gen.generateResponse(null,false,null,chunk, null, true);
Assert.assertEquals(HttpGenerator.Result.DONE, result);
Assert.assertEquals(HttpGenerator.State.END, gen.getState());
@ -271,12 +271,12 @@ public class HttpGeneratorClientTest
out+=BufferUtil.toString(content1);
BufferUtil.clear(content1);
result=gen.generateResponse(null,null,null,null, true);
result=gen.generateResponse(null,false,null,null, null, true);
Assert.assertEquals(HttpGenerator.Result.CONTINUE, result);
Assert.assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
Assert.assertTrue(!gen.isChunking());
result=gen.generateResponse(null,null,null,null, true);
result=gen.generateResponse(null,false,null,null, null, true);
Assert.assertEquals(HttpGenerator.Result.DONE, result);
Assert.assertEquals(HttpGenerator.State.END, gen.getState());
out+=BufferUtil.toString(chunk);

View File

@ -164,6 +164,11 @@ public class HttpGeneratorServerHTTPTest
chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case NEED_CHUNK_TRAILER:
chunk = BufferUtil.allocate(2048);
continue;
case FLUSH:
if (BufferUtil.hasContent(header))
{

View File

@ -26,6 +26,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.nio.ByteBuffer;
import java.util.function.Supplier;
import org.eclipse.jetty.util.BufferUtil;
import org.hamcrest.Matchers;
@ -42,7 +43,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, content, true);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, content, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -50,7 +51,7 @@ public class HttpGeneratorServerTest
info.getFields().add("Content-Type", "test/data");
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content, true);
result = gen.generateResponse(info, false, null, null, content, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String response = BufferUtil.toString(header);
@ -58,7 +59,7 @@ public class HttpGeneratorServerTest
response += BufferUtil.toString(content);
BufferUtil.clear(content);
result = gen.generateResponse(null, null, null, content, false);
result = gen.generateResponse(null, false, null, null, content, false);
assertEquals(HttpGenerator.Result.SHUTDOWN_OUT, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -78,7 +79,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, content, true);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, content, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -86,10 +87,10 @@ public class HttpGeneratorServerTest
info.getFields().add("Content-Type", "test/data");
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content, true);
result = gen.generateResponse(info, false, null, null, content, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
result = gen.generateResponse(info, header, null, content, true);
result = gen.generateResponse(info, false, header, null, content, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String response = BufferUtil.toString(header);
@ -97,7 +98,7 @@ public class HttpGeneratorServerTest
response += BufferUtil.toString(content);
BufferUtil.clear(content);
result = gen.generateResponse(null, null, null, content, false);
result = gen.generateResponse(null, false, null, null, content, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -121,7 +122,7 @@ public class HttpGeneratorServerTest
info.getFields().add("Content-Type", "test/data");
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
HttpGenerator.Result result = gen.generateResponse(info, header, null, content, true);
HttpGenerator.Result result = gen.generateResponse(info, false, header, null, content, true);
assertEquals(gen.isNoContent(), true);
assertEquals(HttpGenerator.Result.FLUSH, result);
@ -129,7 +130,7 @@ public class HttpGeneratorServerTest
String responseheaders = BufferUtil.toString(header);
BufferUtil.clear(header);
result = gen.generateResponse(null, null, null, content, false);
result = gen.generateResponse(null, false, null, null, content, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -150,7 +151,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, content, true);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, content, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -158,10 +159,10 @@ public class HttpGeneratorServerTest
info.getFields().add("Content-Type", "test/data;\r\nextra=value");
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content, true);
result = gen.generateResponse(info, false, null, null, content, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
result = gen.generateResponse(info, header, null, content, true);
result = gen.generateResponse(info, false, header, null, content, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String response = BufferUtil.toString(header);
@ -169,7 +170,7 @@ public class HttpGeneratorServerTest
response += BufferUtil.toString(content);
BufferUtil.clear(content);
result = gen.generateResponse(null, null, null, content, false);
result = gen.generateResponse(null, false, null, null, content, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -194,14 +195,14 @@ public class HttpGeneratorServerTest
String head;
HttpGenerator gen = new HttpGenerator(true, true);
gen.generateResponse(info, header, null, null, true);
gen.generateResponse(info, false, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(head, containsString("HTTP/1.1 200 OK"));
assertThat(head, containsString("Server: Jetty(9.x.x)"));
assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)"));
gen.reset();
gen.generateResponse(infoF, header, null, null, true);
gen.generateResponse(infoF, false, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(head, containsString("HTTP/1.1 200 OK"));
@ -212,14 +213,14 @@ public class HttpGeneratorServerTest
gen.reset();
gen = new HttpGenerator(false, false);
gen.generateResponse(info, header, null, null, true);
gen.generateResponse(info, false, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(head, containsString("HTTP/1.1 200 OK"));
assertThat(head, not(containsString("Server: Jetty(9.x.x)")));
assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)")));
gen.reset();
gen.generateResponse(infoF, header, null, null, true);
gen.generateResponse(infoF, false, header, null, null, true);
head = BufferUtil.toString(header);
BufferUtil.clear(header);
assertThat(head, containsString("HTTP/1.1 200 OK"));
@ -237,7 +238,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -245,12 +246,12 @@ public class HttpGeneratorServerTest
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
info.getFields().add("Content-Length", "11");
result = gen.generateResponse(info, null, null, null, true);
result = gen.generateResponse(info, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
try
{
gen.generateResponse(info, header, null, null, true);
gen.generateResponse(info, false, header, null, null, true);
Assert.fail();
}
catch(BadMessageException e)
@ -266,23 +267,23 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 0);
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, null, true);
result = gen.generateResponse(info, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
result = gen.generateResponse(info, header, null, null, true);
result = gen.generateResponse(info, false, header, null, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
result = gen.generateResponse(null, null, null, null, false);
result = gen.generateResponse(null, false, null, null, null, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -299,7 +300,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -307,16 +308,16 @@ public class HttpGeneratorServerTest
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
info.getFields().add("Connection", "close");
result = gen.generateResponse(info, null, null, null, true);
result = gen.generateResponse(info, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
result = gen.generateResponse(info, header, null, null, true);
result = gen.generateResponse(info, false, header, null, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
result = gen.generateResponse(null, null, null, null, false);
result = gen.generateResponse(null, false, null, null, null, false);
assertEquals(HttpGenerator.Result.SHUTDOWN_OUT, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -333,7 +334,7 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
@ -342,13 +343,13 @@ public class HttpGeneratorServerTest
info.getFields().add("Connection", "Upgrade");
info.getFields().add("Sec-WebSocket-Accept", "123456789==");
result = gen.generateResponse(info, header, null, null, true);
result = gen.generateResponse(info, false, header, null, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String head = BufferUtil.toString(header);
BufferUtil.clear(header);
result = gen.generateResponse(info, null, null, null, false);
result = gen.generateResponse(info, false, null, null, null, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -368,17 +369,17 @@ public class HttpGeneratorServerTest
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
result = gen.generateResponse(info, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, header, null, content0, false);
result = gen.generateResponse(info, false, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -387,7 +388,7 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, null, chunk, content1, false);
result = gen.generateResponse(null, false, null, chunk, content1, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(chunk);
@ -395,17 +396,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result = gen.generateResponse(null, null, chunk, null, true);
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, null, chunk, null, true);
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
out += BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
result = gen.generateResponse(null, null, chunk, null, true);
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -433,18 +434,18 @@ public class HttpGeneratorServerTest
HttpGenerator gen = new HttpGenerator();
gen.setPersistent(false);
HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
info.getFields().add(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED);
result = gen.generateResponse(info, null, null, content0, false);
result = gen.generateResponse(info, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, header, null, content0, false);
result = gen.generateResponse(info, false, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -453,7 +454,10 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, null, chunk, content1, false);
result = gen.generateResponse(null, false, null, null, content1, false);
assertEquals(HttpGenerator.Result.NEED_CHUNK, result);
result = gen.generateResponse(null, false, null, chunk, content1, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(chunk);
@ -461,17 +465,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result = gen.generateResponse(null, null, chunk, null, true);
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, null, chunk, null, true);
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
out += BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
result = gen.generateResponse(null, null, chunk, null, true);
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.SHUTDOWN_OUT, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -489,6 +493,170 @@ public class HttpGeneratorServerTest
"\r\n"));
}
@Test
public void testResponseWithContentAndTrailer() throws Exception
{
ByteBuffer header = BufferUtil.allocate(4096);
ByteBuffer chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
ByteBuffer trailer = 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.setPersistent(false);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
info.getFields().add(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED);
info.setTrailerSupplier(new Supplier<HttpFields>()
{
@Override
public HttpFields get()
{
HttpFields trailer = new HttpFields();
trailer.add("T-Name0","T-ValueA");
trailer.add("T-Name0","T-ValueB");
trailer.add("T-Name1","T-ValueC");
return trailer;
}
});
result = gen.generateResponse(info, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, false, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, false, null, null, content1, false);
assertEquals(HttpGenerator.Result.NEED_CHUNK, result);
result = gen.generateResponse(null, false, null, chunk, content1, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
out += BufferUtil.toString(chunk);
BufferUtil.clear(chunk);
out += BufferUtil.toString(content1);
BufferUtil.clear(content1);
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.NEED_CHUNK_TRAILER, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, false, null, trailer, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
out += BufferUtil.toString(trailer);
BufferUtil.clear(trailer);
result = gen.generateResponse(null, false, null, trailer, null, true);
assertEquals(HttpGenerator.Result.SHUTDOWN_OUT, 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("Content-Length")));
assertThat(out, containsString("Transfer-Encoding: chunked"));
assertThat(out, endsWith(
"\r\n\r\nD\r\n"+
"Hello World! \r\n"+
"2E\r\n"+
"The quick brown fox jumped over the lazy dog. \r\n"+
"0\r\n"+
"T-Name0: T-ValueA\r\n"+
"T-Name0: T-ValueB\r\n"+
"T-Name1: T-ValueC\r\n"+
"\r\n"));
}
@Test
public void testResponseWithTrailer() throws Exception
{
ByteBuffer header = BufferUtil.allocate(4096);
ByteBuffer chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
ByteBuffer trailer = BufferUtil.allocate(4096);
HttpGenerator gen = new HttpGenerator();
gen.setPersistent(false);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
info.getFields().add(HttpHeader.TRANSFER_ENCODING, HttpHeaderValue.CHUNKED);
info.setTrailerSupplier(new Supplier<HttpFields>()
{
@Override
public HttpFields get()
{
HttpFields trailer = new HttpFields();
trailer.add("T-Name0","T-ValueA");
trailer.add("T-Name0","T-ValueB");
trailer.add("T-Name1","T-ValueC");
return trailer;
}
});
result = gen.generateResponse(info, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, false, header, null, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
String out = BufferUtil.toString(header);
BufferUtil.clear(header);
result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.NEED_CHUNK_TRAILER, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, false, null, chunk, null, true);
assertEquals(HttpGenerator.Result.NEED_CHUNK_TRAILER, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, false, null, trailer, null, true);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
out += BufferUtil.toString(trailer);
BufferUtil.clear(trailer);
result = gen.generateResponse(null, false, null, trailer, null, true);
assertEquals(HttpGenerator.Result.SHUTDOWN_OUT, 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("Content-Length")));
assertThat(out, containsString("Transfer-Encoding: chunked"));
assertThat(out, endsWith(
"\r\n\r\n"+
"0\r\n"+
"T-Name0: T-ValueA\r\n"+
"T-Name0: T-ValueB\r\n"+
"T-Name1: T-ValueC\r\n"+
"\r\n"));
}
@Test
public void testResponseWithKnownContentLengthFromMetaData() throws Exception
@ -498,17 +666,17 @@ public class HttpGeneratorServerTest
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), 59);
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
result = gen.generateResponse(info, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, header, null, content0, false);
result = gen.generateResponse(info, false, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -517,17 +685,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, null, null, content1, false);
result = gen.generateResponse(null, false, 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, null, true);
result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, null, null, null, true);
result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -546,18 +714,18 @@ public class HttpGeneratorServerTest
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false);
HttpGenerator.Result result = gen.generateResponse(null, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), -1);
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
info.getFields().add("Content-Length",""+(content0.remaining()+content1.remaining()));
result = gen.generateResponse(info, null, null, content0, false);
result = gen.generateResponse(info, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, header, null, content0, false);
result = gen.generateResponse(info, false, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -566,17 +734,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, null, null, content1, false);
result = gen.generateResponse(null, false, 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, null, true);
result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, null, null, null, true);
result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -598,32 +766,32 @@ public class HttpGeneratorServerTest
ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. ");
HttpGenerator gen = new HttpGenerator();
HttpGenerator.Result result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false);
HttpGenerator.Result result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, false, null, null, null, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, header, null, null, false);
result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, false, header, null, null, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMPLETING_1XX, gen.getState());
String out = BufferUtil.toString(header);
result = gen.generateResponse(null, null, null, null, false);
result = gen.generateResponse(null, false, null, null, null, false);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.START, gen.getState());
assertThat(out, containsString("HTTP/1.1 100 Continue"));
result = gen.generateResponse(null, null, null, content0, false);
result = gen.generateResponse(null, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_INFO, result);
assertEquals(HttpGenerator.State.START, gen.getState());
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_1, 200, null, new HttpFields(), BufferUtil.length(content0)+BufferUtil.length(content1));
info.getFields().add("Last-Modified", DateGenerator.__01Jan1970);
result = gen.generateResponse(info, null, null, content0, false);
result = gen.generateResponse(info, false, null, null, content0, false);
assertEquals(HttpGenerator.Result.NEED_HEADER, result);
assertEquals(HttpGenerator.State.START, gen.getState());
result = gen.generateResponse(info, header, null, content0, false);
result = gen.generateResponse(info, false, header, null, content0, false);
assertEquals(HttpGenerator.Result.FLUSH, result);
assertEquals(HttpGenerator.State.COMMITTED, gen.getState());
@ -632,17 +800,17 @@ public class HttpGeneratorServerTest
out += BufferUtil.toString(content0);
BufferUtil.clear(content0);
result = gen.generateResponse(null, null, null, content1, false);
result = gen.generateResponse(null, false, 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, null, true);
result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.CONTINUE, result);
assertEquals(HttpGenerator.State.COMPLETING, gen.getState());
result = gen.generateResponse(null, null, null, null, true);
result = gen.generateResponse(null, false, null, null, null, true);
assertEquals(HttpGenerator.Result.DONE, result);
assertEquals(HttpGenerator.State.END, gen.getState());
@ -664,7 +832,7 @@ public class HttpGeneratorServerTest
fields.add(HttpHeader.CONNECTION, customValue);
MetaData.Response info = new MetaData.Response(HttpVersion.HTTP_1_0, 200, "OK", fields, -1);
ByteBuffer header = BufferUtil.allocate(4096);
HttpGenerator.Result result = generator.generateResponse(info, header, null, null, true);
HttpGenerator.Result result = generator.generateResponse(info, false, header, null, null, true);
Assert.assertSame(HttpGenerator.Result.FLUSH, result);
String headers = BufferUtil.toString(header);
Assert.assertTrue(headers.contains(HttpHeaderValue.KEEP_ALIVE.asString()));

View File

@ -404,6 +404,10 @@ public class HttpTester
chunk=BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case NEED_CHUNK_TRAILER:
chunk=BufferUtil.allocate(8192);
continue;
case NEED_INFO:
throw new IllegalStateException();

View File

@ -716,6 +716,13 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
chunk = _chunk = _bufferPool.acquire(HttpGenerator.CHUNK_SIZE, CHUNK_BUFFER_DIRECT);
continue;
}
case NEED_CHUNK_TRAILER:
{
if (_chunk!=null)
_bufferPool.release(_chunk);
chunk = _chunk = _bufferPool.acquire(_config.getResponseHeaderSize(), CHUNK_BUFFER_DIRECT);
continue;
}
case FLUSH:
{
// Don't write the chunk or the content if this is a HEAD response, or any other type of response that should have no content