Improved the HttpTester and added a Test
This commit is contained in:
parent
3e809af7f6
commit
05c6a3664f
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue