/*
* This file is part of the OWASP Proxy, a free intercepting proxy library.
* Copyright (C) 2008-2010 Rogan Dawes <rogan@dawes.za.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to:
* The Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.owasp.proxy.daemon;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.owasp.proxy.http.BufferedRequest;
import org.owasp.proxy.http.MessageFormatException;
import org.owasp.proxy.http.MessageUtils;
import org.owasp.proxy.http.MutableBufferedMessage;
import org.owasp.proxy.http.MutableBufferedRequest;
import org.owasp.proxy.http.MutableBufferedResponse;
import org.owasp.proxy.http.MutableRequestHeader;
import org.owasp.proxy.http.MutableResponseHeader;
import org.owasp.proxy.http.NamedValue;
import org.owasp.proxy.http.RequestHeader;
import org.owasp.proxy.http.ResponseHeader;
import org.owasp.proxy.http.StreamingRequest;
import org.owasp.proxy.http.StreamingResponse;
import org.owasp.proxy.http.server.BufferedMessageInterceptor;
import org.owasp.proxy.http.server.BufferingHttpRequestHandler;
import org.owasp.proxy.http.server.DefaultHttpRequestHandler;
import org.owasp.proxy.http.server.HttpRequestHandler;
import org.owasp.proxy.http.server.LoggingHttpRequestHandler;
import org.owasp.proxy.test.TraceServer;
import org.owasp.proxy.util.AsciiString;
public class BufferingHttpRequestHandlerTest {
private static MockRequestHandler requestHandler = new MockRequestHandler();
private static TraceServer ts = null;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
ts = new TraceServer(9999);
ts.start();
}
@AfterClass
public static void tearDownAfterClass() throws Exception {
ts.stop();
Thread.sleep(1000);
assertTrue("TraceServer shutdown failed!", ts.isStopped());
}
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void testOnline() throws Exception {
StreamingRequest request = new StreamingRequest.Impl();
request.setTarget(new InetSocketAddress("ajax.googleapis.com", 443));
request.setSsl(true);
request
.setHeader(AsciiString
.getBytes("GET /ajax/libs/jquery/1.3.2/jquery.min.js HTTP/1.1\r\n"
+ "User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1\r\n"
+ "Cache-Control: max-age=0\r\n"
+ "Referer: http://freshmeat.net/\r\n"
+ "Accept: */*\r\n"
+ "Accept-Language: en-us\r\n"
+ "Accept-Encoding: gzip, deflate\r\n"
+ "Connection: keep-alive\r\n"
+ "Host: ajax.googleapis.com\r\n\r\n"));
HttpRequestHandler rh = new DefaultHttpRequestHandler();
StreamingResponse response = rh.handleRequest(null, request, false);
MutableBufferedResponse brs = new MutableBufferedResponse.Impl();
MessageUtils.buffer(response, brs, Integer.MAX_VALUE);
byte[] content = MessageUtils.decode(brs);
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(content);
byte[] d = digest.digest();
System.out.print("Digest: ");
for (int i = 0; i < d.length; i++) {
System.out.print(Integer.toHexString((d[i] & 0xFF)) + " ");
}
// System.out.write(content);
request
.setHeader(AsciiString
.getBytes("GET /favicon.ico HTTP/1.1\r\n"
+ "User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_6; en-us) AppleWebKit/525.27.1 (KHTML, like Gecko) Version/3.2.1 Safari/525.27.1\r\n"
+ "Cache-Control: max-age=0\r\n"
+ "Referer: http://freshmeat.net/\r\n"
+ "Accept: */*\r\n"
+ "Accept-Language: en-us\r\n"
+ "Accept-Encoding: gzip, deflate\r\n"
+ "Connection: keep-alive\r\n"
+ "Host: ajax.googleapis.com\r\n\r\n"));
response = rh.handleRequest(null, request, false);
brs = new MutableBufferedResponse.Impl();
MessageUtils.buffer(response, brs, Integer.MAX_VALUE);
content = MessageUtils.decode(brs);
System.out.write(content.length);
//
// byte[] buff = new byte[1024];
// int got;
//
// InputStream in;
// in = new FileInputStream("/Users/rogan/tmp/unchunked");
// in = new ChunkedInputStream(in);
// ByteArrayOutputStream chunked = new ByteArrayOutputStream();
// in = new CopyInputStream(in, chunked);
// // in = new FileInputStream("/Users/rogan/tmp/op");
// try {
// in = new GunzipInputStream(in);
// while ((got = in.read(buff)) > -1)
// System.out.println("Got " + got);
// } catch (IOException ioe) {
// ioe.printStackTrace();
// }
// in = new FileInputStream("/Users/rogan/tmp/op");
// ByteArrayOutputStream unchunked = new ByteArrayOutputStream();
// in = new CopyInputStream(in, unchunked);
// try {
// in = new GunzipInputStream(in);
// while ((got = in.read(buff)) > -1)
// System.out.println("Got " + got);
// } catch (IOException ioe) {
// ioe.printStackTrace();
// }
// byte[] chunkedBytes = chunked.toByteArray();
// byte[] unchunkedBytes = unchunked.toByteArray();
// System.out.println("Chunked = " + chunkedBytes.length +
// " unchunked = "
// + unchunkedBytes.length);
// for (int i = 0; i < Math
// .min(chunkedBytes.length, unchunkedBytes.length); i++) {
// if (chunkedBytes[i] != unchunkedBytes[i])
// System.out.println(i + ": unchunked: " + unchunkedBytes[i]
// + " chunked: " + chunkedBytes[i]);
// }
//
}
@Test
public void testContinue() throws Exception {
HttpRequestHandler rh = new DefaultHttpRequestHandler();
rh = new LoggingHttpRequestHandler(rh);
BufferedMessageInterceptor bmi = new MockBufferedInterceptor();
rh = new BufferingHttpRequestHandler(rh, bmi, 1024);
StreamingRequest req = new StreamingRequest.Impl();
req.setTarget(new InetSocketAddress("localhost", 9999));
req.setSsl(false);
req
.setHeader(AsciiString
.getBytes("POST /target HTTP/1.1\r\n"
+ "Host: localhost\r\nContent-Length: 20\r\nExpect: 100-continue\r\n\r\n"));
byte[] content = AsciiString.getBytes("01234567890123456789");
StreamingResponse resp = rh.handleRequest(req.getTarget().getAddress(),
req, false);
assertEquals("Expected continue", "100", resp.getStatus());
req = new StreamingRequest.Impl(req);
req.setContent(new ByteArrayInputStream(content));
resp = rh.handleRequest(req.getTarget().getAddress(), req, true);
assertEquals("Expected OK", "200", resp.getStatus());
InputStream rc = resp.getContent();
assertTrue("Content is encorrect!", Arrays.equals(content, toArray(rc)));
}
private byte[] toArray(InputStream in) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buff = new byte[1024];
int got;
while ((got = in.read(buff)) > -1)
baos.write(buff, 0, got);
in.close();
return baos.toByteArray();
}
@Test
@Ignore
public void testHandleRequest() throws Exception {
MockBufferedInterceptor bm = new MockBufferedInterceptor();
BufferingHttpRequestHandler brh = new BufferingHttpRequestHandler(
requestHandler, bm);
brh.setMaximumContentSize(65536);
test(brh, bm, false, false, 32768);
test(brh, bm, true, true, 32768);
}
private void test(BufferingHttpRequestHandler brh,
MockBufferedInterceptor bm, boolean chunked, boolean gzipped,
int size) throws Exception {
MutableBufferedRequest brq = createRequest("/?chunked=" + chunked
+ "&gzipped=" + gzipped + "&size=" + size, size);
StreamingRequest srq = new StreamingRequest.Impl();
MessageUtils.stream(brq, srq);
StreamingResponse srs = brh.handleRequest(null, srq, false);
MutableBufferedResponse brs = new MutableBufferedResponse.Impl();
MessageUtils.buffer(srs, brs, Integer.MAX_VALUE);
compare(brq, bm.result.request);
compare(brs, bm.result.response);
}
private void compare(MutableBufferedMessage a, MutableBufferedMessage b) {
assertTrue(Arrays.equals(a.getHeader(), b.getHeader()));
if (!(a.getContent() == b.getContent() && a.getContent() == null)) {
assertTrue(Arrays.equals(a.getContent(), b.getContent()));
}
}
private MutableBufferedRequest createRequest(String resource, int size)
throws MessageFormatException {
MutableBufferedRequest request = new MutableBufferedRequest.Impl();
request.setStartLine("POST " + resource + " HTTP/1.1");
request.setHeader("Content-Length", Integer.toString(size));
byte[] content = new byte[size];
fill(content);
request.setContent(content);
return request;
}
private static void fill(byte[] a) {
for (int i = 0; i < a.length; i++) {
a[i] = (byte) (i % 256);
}
}
private static class Result {
public MutableBufferedRequest request = null;
public MutableBufferedResponse response = null;
public boolean requestOverflow = false;
public boolean responseOverflow = false;
public void reset() {
request = null;
response = null;
requestOverflow = false;
responseOverflow = false;
}
}
private static class MockBufferedInterceptor extends
BufferedMessageInterceptor {
private Result result = new Result();
public Action directRequest(MutableRequestHeader request) {
return Action.BUFFER;
}
public Action directResponse(RequestHeader request,
MutableResponseHeader response) {
return Action.BUFFER;
}
public void processRequest(MutableBufferedRequest request) {
result.request = request;
}
public void processResponse(RequestHeader request,
MutableBufferedResponse response) {
result.response = response;
}
public void requestContentSizeExceeded(BufferedRequest request, int size) {
result.requestOverflow = true;
}
public void responseContentSizeExceeded(RequestHeader request,
ResponseHeader response, int size) {
result.responseOverflow = true;
}
}
private static class MockRequestHandler implements HttpRequestHandler {
private static Logger logger = Logger.getAnonymousLogger();
public void dispose() throws IOException {
logger.info("Dispose called");
}
/*
* (non-Javadoc)
*
* @see org.owasp.proxy.daemon.HttpRequestHandler#handleRequest(java.net. InetAddress,
* org.owasp.httpclient.StreamingRequest)
*/
public StreamingResponse handleRequest(InetAddress source,
StreamingRequest request, boolean isContinue)
throws IOException, MessageFormatException {
boolean chunked = false;
boolean gzipped = false;
int size = 16384;
try {
String resource = request.getResource();
int q = resource.indexOf('?');
String query = q > -1 ? resource.substring(q + 1) : null;
NamedValue[] nv = NamedValue.parse(query, "&", "=");
chunked = "true".equals(NamedValue.findValue(nv, "chunked"));
gzipped = "true".equals(NamedValue.findValue(nv, "chunked"));
String s = NamedValue.findValue(nv, "size");
if (s != null) {
try {
size = Integer.parseInt(s);
} catch (NumberFormatException nfe) {
logger.info("Invalid message size");
}
}
byte[] content = new byte[size];
fill(content);
StreamingResponse response = new StreamingResponse.Impl();
response.setStartLine("HTTP/1.0 200 Ok");
if (chunked)
response.setHeader("Transfer-Encoding", "chunked");
if (gzipped)
response.setHeader("Content-Encoding", "gzip");
response.setContent(new ByteArrayInputStream(content));
response.setContent(MessageUtils.encode(response));
logger.info("Response (" + (chunked ? "chunked" : "unchunked")
+ "," + (gzipped ? "gzipped" : "unzipped")
+ ") size = " + size);
return response;
} catch (MessageFormatException mfe) {
return null;
}
}
}
}