/*
* 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 com.intuit.tank.handler;
/*
* #%L
* proxy-extension
* %%
* Copyright (C) 2011 - 2015 Intuit Inc.
* %%
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
* #L%
*/
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.SequenceInputStream;
import java.net.InetAddress;
import javax.xml.bind.JAXBException;
import org.owasp.proxy.http.BufferedRequest;
import org.owasp.proxy.http.MessageFormatException;
import org.owasp.proxy.http.MessageUtils;
import org.owasp.proxy.http.MutableBufferedRequest;
import org.owasp.proxy.http.MutableBufferedResponse;
import org.owasp.proxy.http.NamedValue;
import org.owasp.proxy.http.RequestHeader;
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.BufferedMessageInterceptor.Action;
import org.owasp.proxy.http.server.HttpRequestHandler;
import org.owasp.proxy.io.CountingInputStream;
import org.owasp.proxy.io.SizeLimitExceededException;
import org.owasp.proxy.util.AsciiString;
import com.intuit.tank.conversation.Header;
import com.intuit.tank.conversation.Protocol;
import com.intuit.tank.conversation.Request;
import com.intuit.tank.conversation.Response;
import com.intuit.tank.conversation.Transaction;
import com.intuit.tank.entity.Application;
import com.intuit.tank.util.HeaderParser;
public class BufferingHttpRequestHandler implements HttpRequestHandler {
private static final byte[] CONTINUE = AsciiString
.getBytes("HTTP/1.1 100 Continue\r\n\r\n");
private final HttpRequestHandler next;
protected int max = 0;
private BufferedMessageInterceptor interceptor;
private Application application;
public BufferingHttpRequestHandler(final HttpRequestHandler next,
BufferedMessageInterceptor interceptor, Application application) {
this.next = next;
this.application = application;
this.interceptor = interceptor;
}
public BufferingHttpRequestHandler(final HttpRequestHandler next,
BufferedMessageInterceptor interceptor, final int max, Application application) {
this.next = next;
this.interceptor = interceptor;
this.max = max;
this.application = application;
}
public void setMaximumContentSize(int max) {
this.max = max;
}
/*
* (non-Javadoc)
*
* @see org.owasp.proxy.daemon.HttpRequestHandler#dispose()
*/
public final void dispose() throws IOException {
next.dispose();
}
private BufferedRequest handleRequest(StreamingRequest request)
throws IOException, MessageFormatException {
final Action action = interceptor.directRequest(request);
final MutableBufferedRequest brq;
if (Action.BUFFER.equals(action)) {
brq = new MutableBufferedRequest.Impl();
try {
MessageUtils.buffer(request, brq, max);
interceptor.processRequest(brq);
MessageUtils.stream(brq, request);
} catch (SizeLimitExceededException slee) {
final InputStream buffered = new ByteArrayInputStream(brq
.getContent());
InputStream content = request.getContent();
content = new SequenceInputStream(buffered, content);
content = new CountingInputStream(content) {
protected void eof() {
interceptor.requestContentSizeExceeded(brq, getCount());
}
};
request.setContent(content);
}
} else if (Action.STREAM.equals(action)) {
brq = new MutableBufferedRequest.Impl();
MessageUtils.delayedCopy(request, brq, max,
new MessageUtils.DelayedCopyObserver() {
@Override
public void copyCompleted(boolean overflow, int size) {
if (overflow) {
interceptor.requestContentSizeExceeded(brq,
size);
} else {
interceptor.requestStreamed(brq);
}
}
});
} else {
brq = null;
}
storeRequest(brq);
return brq;
}
private Transaction storeRequest(final BufferedRequest brq) throws MessageFormatException {
Request r = new Request();
r.setBody(brq.getDecodedContent());
r.setFirstLine(brq.getStartLine());
r.setProtocol(brq.isSsl() ? Protocol.https : Protocol.http);
NamedValue[] headers = brq.getHeaders();
for (NamedValue namedValue : headers) {
r.getHeaders().add(new Header(namedValue.getName(), namedValue.getValue()));
}
return application.setRequestForCurrentTransaction(r, brq);
}
private void handleResponse(final BufferedRequest request,
final StreamingResponse response) throws IOException,
MessageFormatException {
Action action = interceptor.directResponse(request, response);
final MutableBufferedResponse brs;
Transaction transaction = storeRequest(request);
if (Action.BUFFER.equals(action)) {
brs = new MutableBufferedResponse.Impl();
try {
if (request.getMethod().equalsIgnoreCase("head")) {
brs.setContent(null);
brs.setDecodedContent(null);
brs.setHeader(response.getHeader());
// brs.setStartLine(response.getStartLine());
} else {
MessageUtils.buffer(response, brs, max);
}
interceptor.processResponse(request, brs);
storeResponse(transaction, brs, request);
MessageUtils.stream(brs, response);
} catch (SizeLimitExceededException slee) {
InputStream buffered = new ByteArrayInputStream(brs
.getContent());
InputStream content = response.getContent();
content = new SequenceInputStream(buffered, content);
content = new CountingInputStream(content) {
protected void eof() {
interceptor.responseContentSizeExceeded(request, brs,
getCount());
}
};
response.setContent(content);
}
} else if (Action.STREAM.equals(action)) {
brs = new MutableBufferedResponse.Impl();
MessageUtils.delayedCopy(response, brs, max,
new MessageUtils.DelayedCopyObserver() {
@Override
public void copyCompleted(boolean overflow, int size) {
if (overflow) {
interceptor.responseContentSizeExceeded(
request, brs, size);
} else {
interceptor.responseStreamed(request, brs);
}
}
});
storeResponse(transaction, brs, request);
}
}
private void storeResponse(Transaction transaction, MutableBufferedResponse brs, BufferedRequest request)
throws MessageFormatException {
Response response = new Response();
response.setBody(brs.getDecodedContent());
response.setFirstLine(brs.getStartLine());
NamedValue[] headers = brs.getHeaders();
for (NamedValue namedValue : headers) {
response.getHeaders().add(new Header(namedValue.getName(), namedValue.getValue()));
}
try {
if (transaction != null) {
application.setResponseForCurrentTransaction(transaction, response, request);
}
} catch (JAXBException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/*
* (non-Javadoc)
*
* @see org.owasp.proxy.daemon.HttpRequestHandler#handleRequest(java.net.InetAddress ,
* org.owasp.httpclient.StreamingRequest)
*/
final public StreamingResponse handleRequest(final InetAddress source,
final StreamingRequest request, boolean isContinue)
throws IOException, MessageFormatException {
if (!isContinue && isExpectContinue(request)) {
return get100Continue();
}
BufferedRequest brq = handleRequest(request);
isContinue = false;
StreamingResponse response = null;
if (isExpectContinue(request)) {
StreamingRequest cont = new StreamingRequest.Impl(request);
response = next.handleRequest(source, cont, false);
isContinue = isContinue(response);
if (!isContinue) {
return response;
}
}
response = next.handleRequest(source, request, isContinue);
if (brq != null) {
handleResponse(brq, response);
}
return response;
}
private boolean isExpectContinue(final RequestHeader request)
throws MessageFormatException {
return "100-continue".equalsIgnoreCase(request.getHeader("Expect"));
}
private boolean isContinue(StreamingResponse response)
throws MessageFormatException {
return "100".equals(response.getStatus());
}
private StreamingResponse get100Continue() {
StreamingResponse response = new StreamingResponse.Impl();
response.setHeader(CONTINUE);
return response;
}
}