426870 - HTTP 1.0 Request with Connection: keep-alive and response

content hangs.

Refactored tests.
This commit is contained in:
Simone Bordet 2014-01-29 18:26:32 +01:00
parent cbdfd87d78
commit 3486046f1e
2 changed files with 106 additions and 392 deletions

View File

@ -18,12 +18,10 @@
package org.eclipse.jetty.http; package org.eclipse.jetty.http;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.EnumSet;
import java.util.List; import java.util.List;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
@ -33,22 +31,77 @@ import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters; import org.junit.runners.Parameterized.Parameters;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class HttpGeneratorServerTRTest public class HttpGeneratorServerHTTPTest
{ {
private static class TR @Parameter(value = 0)
public Run run;
private String _content;
private String _reason;
@Test
public void testHTTP() throws Exception
{
Handler handler = new Handler();
HttpGenerator gen = new HttpGenerator();
String t = run.toString();
run.result.getHttpFields().clear();
String response = run.result.build(run.httpVersion, gen, "OK\r\nTest", run.connection.val, null, run.chunks);
if (run.httpVersion == 9)
{
assertFalse(t, gen.isPersistent());
if (run.result._body != null)
assertEquals(t, run.result._body, response);
return;
}
HttpParser parser = new HttpParser(handler);
parser.setHeadResponse(run.result._head);
parser.parseNext(BufferUtil.toBuffer(response));
if (run.result._body != null)
assertEquals(t, run.result._body, this._content);
if (run.httpVersion == 10)
assertTrue(t, gen.isPersistent() || run.result._contentLength >= 0 || EnumSet.of(ConnectionType.CLOSE, ConnectionType.KEEP_ALIVE, ConnectionType.NONE).contains(run.connection));
else
assertTrue(t, gen.isPersistent() || EnumSet.of(ConnectionType.CLOSE, ConnectionType.TE_CLOSE).contains(run.connection));
if (run.httpVersion > 9)
assertEquals("OK??Test", _reason);
if (_content == null)
assertTrue(t, run.result._body == null);
else
assertThat(t, run.result._contentLength, either(equalTo(_content.length())).or(equalTo(-1)));
}
private static class Result
{ {
private HttpFields _fields = new HttpFields(); private HttpFields _fields = new HttpFields();
private final String _body; private final String _body;
private final int _code; private final int _code;
String _connection; private String _connection;
int _contentLength; private int _contentLength;
String _contentType; private String _contentType;
private final boolean _head; private final boolean _head;
String _other; private String _other;
String _te; private String _te;
private TR(int code, String contentType, int contentLength, String content, boolean head) private Result(int code, String contentType, int contentLength, String content, boolean head)
{ {
_code = code; _code = code;
_contentType = contentType; _contentType = contentType;
@ -89,20 +142,17 @@ public class HttpGeneratorServerTRTest
chunks[i - 1].limit(chunks[i].position()); chunks[i - 1].limit(chunks[i].position());
} }
content = chunks[c++]; content = chunks[c++];
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
} }
ByteBuffer header = null; ByteBuffer header = null;
ByteBuffer chunk = null; ByteBuffer chunk = null;
HttpGenerator.ResponseInfo info = null; HttpGenerator.ResponseInfo info = null;
loop: while (true) loop:
while (true)
{ {
// if we have unwritten content // if we have unwritten content
if (source != null && content != null && content.remaining() == 0 && c < nchunks) if (source != null && content != null && content.remaining() == 0 && c < nchunks)
{
content = chunks[c++]; content = chunks[c++];
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
}
// Generate // Generate
boolean last = !BufferUtil.hasContent(content); boolean last = !BufferUtil.hasContent(content);
@ -168,11 +218,6 @@ public class HttpGeneratorServerTRTest
private class Handler implements HttpParser.ResponseHandler<ByteBuffer> private class Handler implements HttpParser.ResponseHandler<ByteBuffer>
{ {
private final List<String> _hdr = new ArrayList<>();
private final List<String> _val = new ArrayList<>();
private int _status;
private HttpVersion _version;
@Override @Override
public boolean content(ByteBuffer ref) public boolean content(ByteBuffer ref)
{ {
@ -204,16 +249,12 @@ public class HttpGeneratorServerTRTest
@Override @Override
public boolean parsedHeader(HttpField field) public boolean parsedHeader(HttpField field)
{ {
_hdr.add(field.getName());
_val.add(field.getValue());
return false; return false;
} }
@Override @Override
public boolean startResponse(HttpVersion version, int status, String reason) public boolean startResponse(HttpVersion version, int status, String reason)
{ {
_version = version;
_status = status;
_reason = reason; _reason = reason;
return false; return false;
} }
@ -235,25 +276,25 @@ public class HttpGeneratorServerTRTest
private static class Run private static class Run
{ {
public static Run[] as(TR tr, int ver, int chunks, ConnectionType connection) public static Run[] as(Result result, int ver, int chunks, ConnectionType connection)
{ {
Run run = new Run(); Run run = new Run();
run.tr = tr; run.result = result;
run.httpVersion = ver; run.httpVersion = ver;
run.chunks = chunks; run.chunks = chunks;
run.connection = connection; run.connection = connection;
return new Run[]{run}; return new Run[]{run};
} }
TR tr; private Result result;
ConnectionType connection; private ConnectionType connection;
int httpVersion; private int httpVersion;
int chunks; private int chunks;
@Override @Override
public String toString() public String toString()
{ {
return String.format("tr=%s,ver=%d,chunks=%d,connection=%s",tr,httpVersion,chunks,connection.name()); return String.format("result=%s,version=%d,chunks=%d,connection=%s", result, httpVersion, chunks, connection.name());
} }
} }
@ -289,20 +330,21 @@ public class HttpGeneratorServerTRTest
@Parameters(name = "{0}") @Parameters(name = "{0}")
public static Collection<Run[]> data() public static Collection<Run[]> data()
{ {
TR[] trs = { Result[] results = {
/* 0 */new TR(200,null,-1,null,false), new Result(200, null, -1, null, false),
/* 1 */new TR(200,null,-1,CONTENT,false), new Result(200, null, -1, CONTENT, false),
/* 2 */new TR(200,null,CONTENT.length(),null,true), new Result(200, null, CONTENT.length(), null, true),
/* 3 */new TR(200,null,CONTENT.length(),CONTENT,false), new Result(200, null, CONTENT.length(), CONTENT, false),
/* 4 */new TR(200,"text/html",-1,null,true), new Result(200, "text/html", -1, null, true),
/* 5 */new TR(200,"text/html",-1,CONTENT,false), new Result(200, "text/html", -1, CONTENT, false),
/* 6 */new TR(200,"text/html",CONTENT.length(),null,true), new Result(200, "text/html", CONTENT.length(), null, true),
/* 7 */new TR(200,"text/html",CONTENT.length(),CONTENT,false), }; new Result(200, "text/html", CONTENT.length(), CONTENT, false)
};
List<Run[]> data = new ArrayList<>(); List<Run[]> data = new ArrayList<>();
// For each test result // For each test result
for (TR tr : trs) for (Result result : results)
{ {
// Loop over HTTP versions // Loop over HTTP versions
for (int v = 9; v <= 11; v++) for (int v = 9; v <= 11; v++)
@ -315,62 +357,12 @@ public class HttpGeneratorServerTRTest
{ {
if (connection.isSupportedByHttp(v)) if (connection.isSupportedByHttp(v))
{ {
data.add(Run.as(tr,v,chunks,connection)); data.add(Run.as(result, v, chunks, connection));
} }
} }
} }
} }
} }
return data; return data;
} }
@Parameter(value = 0)
public Run run;
private String _content;
private String _reason;
@Test
public void testHTTP() throws Exception
{
Handler handler = new Handler();
HttpGenerator gen = new HttpGenerator();
String t = run.toString();
run.tr.getHttpFields().clear();
String response = run.tr.build(run.httpVersion,gen,"OK\r\nTest",run.connection.val,null,run.chunks);
if (run.httpVersion == 9)
{
assertFalse(t,gen.isPersistent());
if (run.tr._body != null)
assertEquals(t,run.tr._body,response);
return;
}
HttpParser parser = new HttpParser(handler);
parser.setHeadResponse(run.tr._head);
parser.parseNext(BufferUtil.toBuffer(response));
if (run.tr._body != null)
assertEquals(t,run.tr._body,this._content);
if (run.httpVersion == 10)
assertTrue(t,gen.isPersistent() || run.tr._contentLength >= 0 || run.connection == ConnectionType.CLOSE || run.connection == ConnectionType.NONE);
else
assertTrue(t,gen.isPersistent() || run.connection == ConnectionType.CLOSE || run.connection == ConnectionType.TE_CLOSE);
if (run.httpVersion > 9)
assertEquals("OK??Test",_reason);
if (_content == null)
assertTrue(t,run.tr._body == null);
else
assertThat(t,run.tr._contentLength,either(equalTo(_content.length())).or(equalTo(-1)));
}
} }

View File

@ -19,8 +19,6 @@
package org.eclipse.jetty.http; package org.eclipse.jetty.http;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jetty.http.HttpGenerator.ResponseInfo; import org.eclipse.jetty.http.HttpGenerator.ResponseInfo;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
@ -28,289 +26,13 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class HttpGeneratorServerTest public class HttpGeneratorServerTest
{ {
private class Handler implements HttpParser.ResponseHandler<ByteBuffer>
{
@Override
public boolean content(ByteBuffer ref)
{
if (_content == null)
_content = "";
_content += BufferUtil.toString(ref);
ref.position(ref.limit());
return false;
}
@Override
public void earlyEOF()
{
}
@Override
public boolean headerComplete()
{
_content = null;
return false;
}
@Override
public boolean messageComplete()
{
return true;
}
@Override
public boolean parsedHeader(HttpField field)
{
_hdr.add(field.getName());
_val.add(field.getValue());
return false;
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
{
_version = version;
_status = status;
_reason = reason;
return false;
}
@Override
public void badMessage(int status, String reason)
{
throw new IllegalStateException(reason);
}
@Override
public int getHeaderCacheSize()
{
return 256;
}
}
private static class TR
{
private HttpFields _fields = new HttpFields();
private final String _body;
private final int _code;
String _connection;
int _contentLength;
String _contentType;
private final boolean _head;
String _other;
String _te;
private TR(int code, String contentType, int contentLength, String content, boolean head)
{
_code = code;
_contentType = contentType;
_contentLength = contentLength;
_other = "value";
_body = content;
_head = head;
}
private String build(int version, HttpGenerator gen, String reason, String connection, String te, int nchunks) throws Exception
{
String response = "";
_connection = connection;
_te = te;
if (_contentType != null)
_fields.put("Content-Type", _contentType);
if (_contentLength >= 0)
_fields.put("Content-Length", "" + _contentLength);
if (_connection != null)
_fields.put("Connection", _connection);
if (_te != null)
_fields.put("Transfer-Encoding", _te);
if (_other != null)
_fields.put("Other", _other);
ByteBuffer source = _body == null ? null : BufferUtil.toBuffer(_body);
ByteBuffer[] chunks = new ByteBuffer[nchunks];
ByteBuffer content = null;
int c = 0;
if (source != null)
{
for (int i = 0; i < nchunks; i++)
{
chunks[i] = source.duplicate();
chunks[i].position(i * (source.capacity() / nchunks));
if (i > 0)
chunks[i - 1].limit(chunks[i].position());
}
content = chunks[c++];
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
}
ByteBuffer header = null;
ByteBuffer chunk = null;
HttpGenerator.ResponseInfo info = null;
loop:
while (true)
{
// if we have unwritten content
if (source != null && content != null && content.remaining() == 0 && c < nchunks)
{
content = chunks[c++];
// System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content));
}
// Generate
boolean last = !BufferUtil.hasContent(content);
HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last);
switch (result)
{
case NEED_INFO:
info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version), _fields, _contentLength, _code, reason, _head);
continue;
case NEED_HEADER:
header = BufferUtil.allocate(2048);
continue;
case NEED_CHUNK:
chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE);
continue;
case FLUSH:
if (BufferUtil.hasContent(header))
{
response += BufferUtil.toString(header);
header.position(header.limit());
}
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 CONTINUE:
continue;
case SHUTDOWN_OUT:
break;
case DONE:
break loop;
}
}
return response;
}
@Override
public String toString()
{
return "[" + _code + "," + _contentType + "," + _contentLength + "," + (_body == null ? "null" : "content") + "]";
}
public HttpFields getHttpFields()
{
return _fields;
}
}
public final static String[] connections = {null, "keep-alive", "close", "TE, close"};
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";
private final List<String> _hdr = new ArrayList<>();
private final List<String> _val = new ArrayList<>();
private String _content;
private String _reason;
private int _status;
private HttpVersion _version;
private final TR[] tr =
{
/* 0 */ new TR(200, null, -1, null, false),
/* 1 */ new TR(200, null, -1, CONTENT, false),
/* 2 */ new TR(200, null, CONTENT.length(), null, true),
/* 3 */ new TR(200, null, CONTENT.length(), CONTENT, false),
/* 4 */ new TR(200, "text/html", -1, null, true),
/* 5 */ new TR(200, "text/html", -1, CONTENT, false),
/* 6 */ new TR(200, "text/html", CONTENT.length(), null, true),
/* 7 */ new TR(200, "text/html", CONTENT.length(), CONTENT, false),
};
@Test
public void testHTTP() throws Exception
{
Handler handler = new Handler();
// Loop over HTTP versions
for (int v = 9; v <= 11; v++)
{
// For each test result
for (int r = 0; r < tr.length; r++)
{
HttpGenerator gen = new HttpGenerator();
// Loop over chunks
for (int chunks = 1; chunks <= 6; chunks++)
{
// Loop over Connection values
for (int c = 0; c < (v == 11 ? connections.length : (connections.length - 1)); c++)
{
String t = "v=" + v + ",chunks=" + chunks + ",connection=" + connections[c] + ",tr=" + r + "=" + tr[r];
gen.reset();
tr[r].getHttpFields().clear();
String response = tr[r].build(v, gen, "OK\r\nTest", connections[c], null, chunks);
if (v == 9)
{
assertFalse(t, gen.isPersistent());
if (tr[r]._body != null)
assertEquals(t, tr[r]._body, response);
continue;
}
HttpParser parser = new HttpParser(handler);
parser.setHeadResponse(tr[r]._head);
parser.parseNext(BufferUtil.toBuffer(response));
if (tr[r]._body != null)
assertEquals(t, tr[r]._body, this._content);
if (v == 10)
assertTrue(t, gen.isPersistent() || tr[r]._contentLength >= 0 || c == 2 || c == 1 || c == 0);
else
assertTrue(t, gen.isPersistent() || c == 2 || c == 3);
if (v > 9)
assertEquals("OK??Test", _reason);
if (_content == null)
assertTrue(t, tr[r]._body == null);
else
assertThat(t, tr[r]._contentLength, either(equalTo(_content.length())).or(equalTo(-1)));
}
}
}
}
}
@Test @Test
public void testSendServerXPoweredBy() throws Exception public void testSendServerXPoweredBy() throws Exception
{ {