blob: 6156e20c78248948d774d881cbdd6173495b8205 [file] [log] [blame]
//
// ========================================================================
// Copyright (c) 1995-2017 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.http;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.List;
import org.eclipse.jetty.util.BufferUtil;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
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)
public class HttpGeneratorServerHTTPTest
{
@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 final String _body;
private final int _code;
private String _connection;
private int _contentLength;
private String _contentType;
private final boolean _head;
private String _other;
private String _te;
private Result(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++];
}
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++];
// 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;
}
}
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)
{
return false;
}
@Override
public boolean startResponse(HttpVersion version, int status, String reason)
{
_reason = reason;
return false;
}
@Override
public void badMessage(int status, String reason)
{
throw new IllegalStateException(reason);
}
@Override
public int getHeaderCacheSize()
{
return 256;
}
}
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 static class Run
{
public static Run[] as(Result result, int ver, int chunks, ConnectionType connection)
{
Run run = new Run();
run.result = result;
run.httpVersion = ver;
run.chunks = chunks;
run.connection = connection;
return new Run[]{run};
}
private Result result;
private ConnectionType connection;
private int httpVersion;
private int chunks;
@Override
public String toString()
{
return String.format("result=%s,version=%d,chunks=%d,connection=%s", result, httpVersion, chunks, connection.name());
}
}
private enum ConnectionType
{
NONE(null, 9, 10, 11),
KEEP_ALIVE("keep-alive", 9, 10, 11),
CLOSE("close", 9, 10, 11),
TE_CLOSE("TE, close", 11);
private String val;
private int[] supportedHttpVersions;
private ConnectionType(String val, int... supportedHttpVersions)
{
this.val = val;
this.supportedHttpVersions = supportedHttpVersions;
}
public boolean isSupportedByHttp(int version)
{
for (int supported : supportedHttpVersions)
{
if (supported == version)
{
return true;
}
}
return false;
}
}
@Parameters(name = "{0}")
public static Collection<Run[]> data()
{
Result[] results = {
new Result(200, null, -1, null, false),
new Result(200, null, -1, CONTENT, false),
new Result(200, null, CONTENT.length(), null, true),
new Result(200, null, CONTENT.length(), CONTENT, false),
new Result(200, "text/html", -1, null, true),
new Result(200, "text/html", -1, CONTENT, false),
new Result(200, "text/html", CONTENT.length(), null, true),
new Result(200, "text/html", CONTENT.length(), CONTENT, false)
};
List<Run[]> data = new ArrayList<>();
// For each test result
for (Result result : results)
{
// Loop over HTTP versions
for (int v = 9; v <= 11; v++)
{
// Loop over chunks
for (int chunks = 1; chunks <= 6; chunks++)
{
// Loop over Connection values
for (ConnectionType connection : ConnectionType.values())
{
if (connection.isSupportedByHttp(v))
{
data.add(Run.as(result, v, chunks, connection));
}
}
}
}
}
return data;
}
}