Improved the HttpTester and added a Test

This commit is contained in:
Greg Wilkins 2016-05-30 21:08:37 +10:00
parent 3e809af7f6
commit 05c6a3664f
3 changed files with 501 additions and 21 deletions

View File

@ -298,6 +298,12 @@ public class HttpParser
_complianceHandler=(ComplianceHandler)(handler instanceof ComplianceHandler?handler:null);
}
/* ------------------------------------------------------------------------------- */
public HttpHandler getHandler()
{
return _handler;
}
/* ------------------------------------------------------------------------------- */
/** Check RFC compliance violation
* @param compliance The compliance level violated

View File

@ -20,13 +20,44 @@ package org.eclipse.jetty.http;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.ByteChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.StringUtil;
/**
* A HTTP Testing helper class.
*
* Example usage:
* <pre>
* try(Socket socket = new Socket("www.google.com",80))
* {
* HttpTester.Request request = HttpTester.newRequest();
* request.setMethod("POST");
* request.setURI("/search");
* request.setVersion(HttpVersion.HTTP_1_0);
* request.put(HttpHeader.HOST,"www.google.com");
* request.put("Content-Type","application/x-www-form-urlencoded");
* request.setContent("q=jetty%20server");
* ByteBuffer output = request.generate();
*
* socket.getOutputStream().write(output.array(),output.arrayOffset()+output.position(),output.remaining());
* HttpTester.Input input = HttpTester.from(socket.getInputStream());
* HttpTester.Response response = HttpTester.parseResponse(input);
* System.err.printf("%s %s %s%n",response.getVersion(),response.getStatus(),response.getReason());
* for (HttpField field:response)
* System.err.printf("%s: %s%n",field.getName(),field.getValue());
* System.err.printf("%n%s%n",response.getContent());
* }
* </pre>
*/
public class HttpTester
{
private HttpTester()
@ -35,7 +66,11 @@ public class HttpTester
public static Request newRequest()
{
return new Request();
Request r=new Request();
r.setMethod(HttpMethod.GET.asString());
r.setURI("/");
r.setVersion(HttpVersion.HTTP_1_1);
return r;
}
public static Request parseRequest(String request)
@ -70,12 +105,121 @@ public class HttpTester
return r;
}
public abstract static class Input
{
boolean _eof=false;
HttpParser _parser;
ByteBuffer _buffer = BufferUtil.allocate(8192);
public ByteBuffer getBuffer()
{
return _buffer;
}
public void setHttpParser(HttpParser parser)
{
_parser=parser;
}
public HttpParser getHttpParser()
{
return _parser;
}
public HttpParser takeHttpParser()
{
HttpParser p=_parser;
_parser=null;
return p;
}
public boolean isEOF()
{
return BufferUtil.isEmpty(_buffer) && _eof;
}
public abstract int fillBuffer() throws IOException;
}
public static Input from(final InputStream in)
{
return new Input()
{
@Override
public int fillBuffer() throws IOException
{
BufferUtil.compact(_buffer);
int len=in.read(_buffer.array(),_buffer.arrayOffset()+_buffer.limit(),BufferUtil.space(_buffer));
if (len<0)
_eof=true;
else
_buffer.limit(_buffer.limit()+len);
return len;
}
};
}
public static Input from(final ReadableByteChannel in)
{
return new Input()
{
@Override
public int fillBuffer() throws IOException
{
BufferUtil.compact(_buffer);
int pos=BufferUtil.flipToFill(_buffer);
int len=in.read(_buffer);
if (len<0)
_eof=true;
BufferUtil.flipToFlush(_buffer,pos);
return len;
}
};
}
public static Response parseResponse(Input in) throws IOException
{
Response r;
HttpParser parser=in.takeHttpParser();
if (parser==null)
{
r=new Response();
parser =new HttpParser(r);
}
else
r=(Response)parser.getHandler();
ByteBuffer buffer = in.getBuffer();
int len=0;
while(len>=0)
{
if (BufferUtil.hasContent(buffer))
if (parser.parseNext(buffer))
break;
if (in.fillBuffer()<=0)
break;
}
if (r.isComplete())
return r;
in.setHttpParser(parser);
return null;
}
public abstract static class Message extends HttpFields implements HttpParser.HttpHandler
{
boolean _complete=false;
ByteArrayOutputStream _content;
HttpVersion _version=HttpVersion.HTTP_1_0;
public boolean isComplete()
{
return _complete;
}
public HttpVersion getVersion()
{
return _version;
@ -129,6 +273,27 @@ public class HttpTester
throw new RuntimeException(e);
}
}
public byte[] getContentBytes()
{
if (_content==null)
return null;
return _content.toByteArray();
}
public String getContent()
{
if (_content==null)
return null;
byte[] bytes=_content.toByteArray();
String content_type=get(HttpHeader.CONTENT_TYPE);
String encoding=MimeTypes.getCharsetFromContentType(content_type);
Charset charset=encoding==null?StandardCharsets.UTF_8:Charset.forName(encoding);
return new String(bytes,charset);
}
@Override
public void parsedHeader(HttpField field)
{
@ -138,6 +303,7 @@ public class HttpTester
@Override
public boolean messageComplete()
{
_complete=true;
return true;
}
@ -323,26 +489,6 @@ public class HttpTester
return _reason;
}
public byte[] getContentBytes()
{
if (_content==null)
return null;
return _content.toByteArray();
}
public String getContent()
{
if (_content==null)
return null;
byte[] bytes=_content.toByteArray();
String content_type=get(HttpHeader.CONTENT_TYPE);
String encoding=MimeTypes.getCharsetFromContentType(content_type);
Charset charset=encoding==null?StandardCharsets.UTF_8:Charset.forName(encoding);
return new String(bytes,charset);
}
@Override
public MetaData.Response getInfo()
{

View File

@ -0,0 +1,328 @@
//
// ========================================================================
// Copyright (c) 1995-2016 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 static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.*;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.junit.Ignore;
import org.junit.Test;
public class HttpTesterTest
{
@Test
@Ignore
public void testExampleUsage() throws Exception
{
try(Socket socket = new Socket("www.google.com",80))
{
HttpTester.Request request = HttpTester.newRequest();
request.setMethod("POST");
request.setURI("/search");
request.setVersion(HttpVersion.HTTP_1_0);
request.put(HttpHeader.HOST,"www.google.com");
request.put("Content-Type","application/x-www-form-urlencoded");
request.setContent("q=jetty%20server");
ByteBuffer output = request.generate();
socket.getOutputStream().write(output.array(),output.arrayOffset()+output.position(),output.remaining());
HttpTester.Input input = HttpTester.from(socket.getInputStream());
HttpTester.Response response = HttpTester.parseResponse(input);
System.err.printf("%s %s %s%n",response.getVersion(),response.getStatus(),response.getReason());
for (HttpField field:response)
System.err.printf("%s: %s%n",field.getName(),field.getValue());
System.err.printf("%n%s%n",response.getContent());
}
}
@Test
public void testGetRequestBuffer10()
{
HttpTester.Request request =HttpTester.parseRequest(
"GET /uri HTTP/1.0\r\n"+
"Host: localhost\r\n"+
"Connection: keep-alive\r\n"+
"\r\n"+
"GET /some/other/request /HTTP/1.0\r\n"+
"Host: localhost\r\n"+
"\r\n"
);
assertThat(request.getMethod(),is("GET"));
assertThat(request.getUri(),is("/uri"));
assertThat(request.getVersion(),is(HttpVersion.HTTP_1_0));
assertThat(request.get(HttpHeader.HOST),is("localhost"));
assertThat(request.getContent(),is(""));
}
@Test
public void testGetRequestBuffer11()
{
HttpTester.Request request =HttpTester.parseRequest(
"GET /uri HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"\r\n"+
"GET /some/other/request /HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"\r\n"
);
assertThat(request.getMethod(),is("GET"));
assertThat(request.getUri(),is("/uri"));
assertThat(request.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(request.get(HttpHeader.HOST),is("localhost"));
assertThat(request.getContent(),is(""));
}
@Test
public void testPostRequestBuffer10()
{
HttpTester.Request request =HttpTester.parseRequest(
"POST /uri HTTP/1.0\r\n"+
"Host: localhost\r\n"+
"Connection: keep-alive\r\n"+
"Content-Length: 16\r\n"+
"\r\n"+
"0123456789ABCDEF"+
"\r\n"+
"GET /some/other/request /HTTP/1.0\r\n"+
"Host: localhost\r\n"+
"\r\n"
);
assertThat(request.getMethod(),is("POST"));
assertThat(request.getUri(),is("/uri"));
assertThat(request.getVersion(),is(HttpVersion.HTTP_1_0));
assertThat(request.get(HttpHeader.HOST),is("localhost"));
assertThat(request.getContent(),is("0123456789ABCDEF"));
}
@Test
public void testPostRequestBuffer11()
{
HttpTester.Request request =HttpTester.parseRequest(
"POST /uri HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"Transfer-Encoding: chunked\r\n"+
"\r\n"+
"A\r\n"+
"0123456789\r\n"+
"6\r\n"+
"ABCDEF\r\n"+
"0\r\n"+
"\r\n"+
"GET /some/other/request /HTTP/1.1\r\n"+
"Host: localhost\r\n"+
"\r\n"
);
assertThat(request.getMethod(),is("POST"));
assertThat(request.getUri(),is("/uri"));
assertThat(request.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(request.get(HttpHeader.HOST),is("localhost"));
assertThat(request.getContent(),is("0123456789ABCDEF"));
}
@Test
public void testResponseEOFBuffer()
{
HttpTester.Response response =HttpTester.parseResponse(
"HTTP/1.1 200 OK\r\n"+
"Header: value\r\n"+
"Connection: close\r\n"+
"\r\n"+
"0123456789ABCDEF"
);
assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(response.getStatus(),is(200));
assertThat(response.getReason(),is("OK"));
assertThat(response.get("Header"),is("value"));
assertThat(response.getContent(),is("0123456789ABCDEF"));
}
@Test
public void testResponseLengthBuffer()
{
HttpTester.Response response =HttpTester.parseResponse(
"HTTP/1.1 200 OK\r\n"+
"Header: value\r\n"+
"Content-Length: 16\r\n"+
"\r\n"+
"0123456789ABCDEF"+
"HTTP/1.1 200 OK\r\n"+
"\r\n"
);
assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(response.getStatus(),is(200));
assertThat(response.getReason(),is("OK"));
assertThat(response.get("Header"),is("value"));
assertThat(response.getContent(),is("0123456789ABCDEF"));
}
@Test
public void testResponseChunkedBuffer()
{
HttpTester.Response response =HttpTester.parseResponse(
"HTTP/1.1 200 OK\r\n"+
"Header: value\r\n"+
"Transfer-Encoding: chunked\r\n"+
"\r\n"+
"A\r\n"+
"0123456789\r\n"+
"6\r\n"+
"ABCDEF\r\n"+
"0\r\n"+
"\r\n"+
"HTTP/1.1 200 OK\r\n"+
"\r\n"
);
assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(response.getStatus(),is(200));
assertThat(response.getReason(),is("OK"));
assertThat(response.get("Header"),is("value"));
assertThat(response.getContent(),is("0123456789ABCDEF"));
}
@Test
public void testResponsesInput() throws Exception
{
ByteArrayInputStream stream = new ByteArrayInputStream((
"HTTP/1.1 200 OK\r\n"+
"Header: value\r\n"+
"Transfer-Encoding: chunked\r\n"+
"\r\n"+
"A\r\n"+
"0123456789\r\n"+
"6\r\n"+
"ABCDEF\r\n"+
"0\r\n"+
"\r\n"+
"HTTP/1.1 400 OK\r\n"+
"Next: response\r\n"+
"Content-Length: 16\r\n"+
"\r\n"+
"0123456789ABCDEF").getBytes(StandardCharsets.ISO_8859_1)
);
HttpTester.Input in = HttpTester.from(stream);
HttpTester.Response response = HttpTester.parseResponse(in);
assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(response.getStatus(),is(200));
assertThat(response.getReason(),is("OK"));
assertThat(response.get("Header"),is("value"));
assertThat(response.getContent(),is("0123456789ABCDEF"));
response = HttpTester.parseResponse(in);
assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(response.getStatus(),is(400));
assertThat(response.getReason(),is("OK"));
assertThat(response.get("Next"),is("response"));
assertThat(response.getContent(),is("0123456789ABCDEF"));
}
@Test
public void testResponsesSplitInput() throws Exception
{
PipedOutputStream src = new PipedOutputStream();
PipedInputStream stream = new PipedInputStream(src)
{
@Override
public synchronized int read(byte[] b, int off, int len) throws IOException
{
if (available()==0)
return 0;
return super.read(b,off,len);
}
};
HttpTester.Input in = HttpTester.from(stream);
src.write((
"HTTP/1.1 200 OK\r\n"+
"Header: value\r\n"+
"Transfer-Encoding: chunked\r\n"+
"\r\n"+
"A\r\n"+
"0123456789\r\n"+
"6\r\n"+
"ABC"
).getBytes(StandardCharsets.ISO_8859_1)
);
HttpTester.Response response = HttpTester.parseResponse(in);
assertThat(response,nullValue());
src.write((
"DEF\r\n"+
"0\r\n"+
"\r\n"+
"HTTP/1.1 400 OK\r\n"+
"Next: response\r\n"+
"Content-Length: 16\r\n"+
"\r\n"+
"0123456789"
).getBytes(StandardCharsets.ISO_8859_1)
);
response = HttpTester.parseResponse(in);
assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(response.getStatus(),is(200));
assertThat(response.getReason(),is("OK"));
assertThat(response.get("Header"),is("value"));
assertThat(response.getContent(),is("0123456789ABCDEF"));
response = HttpTester.parseResponse(in);
assertThat(response,nullValue());
src.write((
"ABCDEF"
).getBytes(StandardCharsets.ISO_8859_1)
);
response = HttpTester.parseResponse(in);
assertThat(response.getVersion(),is(HttpVersion.HTTP_1_1));
assertThat(response.getStatus(),is(400));
assertThat(response.getReason(),is("OK"));
assertThat(response.get("Next"),is("response"));
assertThat(response.getContent(),is("0123456789ABCDEF"));
}
}