package com.intuit.tank.entity;
/*
* #%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.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import org.owasp.proxy.http.BufferedRequest;
import org.owasp.proxy.http.MessageFormatException;
import com.intuit.tank.conversation.Header;
import com.intuit.tank.conversation.Request;
import com.intuit.tank.conversation.Response;
import com.intuit.tank.conversation.Transaction;
import com.intuit.tank.proxy.config.ConfigInclusionExclusionRule;
import com.intuit.tank.proxy.config.ProxyConfiguration;
import com.intuit.tank.proxy.config.TransactionPart;
import com.intuit.tank.proxy.table.TransactionRecordedListener;
import com.intuit.tank.util.HeaderParser;
import com.intuit.tank.util.HeaderParser.HeaderType;
public final class Application {
// private Properties properties;
private JAXBContext context;
private Marshaller marshaller;
private OutputStreamWriter osw;
private boolean sessionStarted = false;
private TransactionRecordedListener listener;
private boolean paused;
private Map<String, Transaction> redirectMap;
public static final Header REDIRECT_MARKER = new Header("X-PROXY-APP", "redirectCollapse");
private ProxyConfiguration proxyConfiguration;
public Application(ProxyConfiguration proxyConfiguration) {
this.proxyConfiguration = proxyConfiguration;
this.redirectMap = new HashMap<String, Transaction>();
}
public void resumeSession() {
paused = false;
}
public void setConfig(ProxyConfiguration proxyConfiguration) {
this.proxyConfiguration = proxyConfiguration;
}
/**
* @return the paused
*/
public boolean isPaused() {
return paused;
}
public void startSession(TransactionRecordedListener l) {
this.listener = l;
try {
context = JAXBContext.newInstance(Request.class.getPackage().getName());
marshaller = context.createMarshaller();
marshaller.setProperty("jaxb.formatted.output", Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
osw = new OutputStreamWriter(new FileOutputStream(new File(proxyConfiguration.getOutputFile())));
osw.write("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>");
osw.write("<sns:session xmlns:sns=\"urn:proxy/conversation/v1\"" +
" followRedirects=\"" + proxyConfiguration.isFollowRedirects() + "\">\n");
sessionStarted = true;
paused = false;
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public synchronized Transaction setRequestForCurrentTransaction(Request request, BufferedRequest req) {
// check if url is in redirectMap
String path = null;
try {
path = getLocation(req);
} catch (MessageFormatException e) {
e.printStackTrace();
}
if (proxyConfiguration.isFollowRedirects() && path != null && redirectMap.containsKey(path)) {
System.out.println("Found redirection for locaiton " + path);
return redirectMap.get(path);
} else {
Transaction t = new Transaction();
t.setRequest(request);
return t;
}
}
/**
* Checks whether the content-type should be handled by the framework
*
* @param checkStatusCode
*
* @param extractContentType
* @return
* @throws IOException
* @throws FileNotFoundException
*/
public static boolean shouldInclude(Transaction t, Set<ConfigInclusionExclusionRule> inclusionRules,
Set<ConfigInclusionExclusionRule> exclusionRules, boolean checkStatusCode) {
boolean ret = true;
int statusCode = HeaderParser.extractStatusCode(t.getResponse().getFirstLine());
if (checkStatusCode && statusCode >= 300 && statusCode < 400) {
ret = false;
} else {
for (ConfigInclusionExclusionRule rule : inclusionRules) {
List<Header> headers = new ArrayList<Header>();
if (rule.getTransactionPart() == TransactionPart.request
|| rule.getTransactionPart() == TransactionPart.both) {
headers.addAll(t.getRequest().getHeaders());
}
if (rule.getTransactionPart() == TransactionPart.response
|| rule.getTransactionPart() == TransactionPart.both) {
headers.addAll(t.getResponse().getHeaders());
}
ret = evaluateRule(rule, headers, HeaderParser.extractPath(t.getRequest().getFirstLine()));
if (ret) {
break;
}
}
if (ret) {
for (ConfigInclusionExclusionRule rule : exclusionRules) {
List<Header> headers = new ArrayList<Header>();
if (rule.getTransactionPart() == TransactionPart.request
|| rule.getTransactionPart() == TransactionPart.both) {
headers.addAll(t.getRequest().getHeaders());
}
if (rule.getTransactionPart() == TransactionPart.response
|| rule.getTransactionPart() == TransactionPart.both) {
headers.addAll(t.getResponse().getHeaders());
}
ret = !evaluateRule(rule, headers, HeaderParser.extractPath(t.getRequest().getFirstLine()));
if (!ret) {
break;
}
}
}
}
return ret;
}
/**
* @param headers
* @param extractPath
* @return
*/
private static boolean evaluateRule(ConfigInclusionExclusionRule rule, List<Header> headers, String extractPath) {
boolean ret = false;
if ("path".equalsIgnoreCase(rule.getHeader())) {
ret = rule.matches(extractPath);
} else {
for (Header header : headers) {
if (rule.getHeader().equalsIgnoreCase("all") || header.getKey().equalsIgnoreCase(rule.getHeader())) {
ret = rule.matches(header.getValue());
if (ret) {
break;
}
}
}
}
return ret;
}
public synchronized void setResponseForCurrentTransaction(Transaction transaction, Response response,
BufferedRequest req)
throws JAXBException {
if (transaction != null && sessionStarted && !paused) {
int statusCode = HeaderParser.extractStatusCode(response.getFirstLine());
HeaderParser hp = new HeaderParser(response);
if (proxyConfiguration.isFollowRedirects() && statusCode == 302) { // redirect
String location = hp.getRedirectLocation();
try {
String oldLocation = getLocation(req);
Transaction remove = redirectMap.remove(oldLocation);
if (remove != null) {
System.out.println("removing location " + oldLocation + " got transaction "
+ remove.getRequest().getFirstLine());
} else {
System.out.println("could not remove location " + oldLocation);
}
} catch (MessageFormatException e) {
System.out.println("Error extracting path from first line: " + e);
}
System.out.println("Pushing redirect location " + location + " with transaction firstline "
+ transaction.getRequest().getFirstLine());
if (!transaction.getRequest().getHeaders().contains(REDIRECT_MARKER)) {
transaction.getRequest().addHeader(REDIRECT_MARKER);
}
transaction.getRequest().addHeader(new Header("X-Redirect-Location", location));
redirectMap.put(location, transaction);
} else {
transaction.setResponse(response);
boolean filtered = true;
if (!shouldInclude(transaction, proxyConfiguration.getBodyInclusions(),
proxyConfiguration.getBodyExclusions(), true)) {
response.setBody(new byte[0]);
}
if (shouldInclude(transaction, proxyConfiguration.getInclusions(),
proxyConfiguration.getExclusions(), false)) {
marshaller.marshal(transaction, osw);
filtered = false;
}
if (listener != null) {
listener.transactionProcessed(transaction, filtered);
}
}
}
}
/**
* @param req
* @throws MessageFormatException
*/
private String getLocation(BufferedRequest req) throws MessageFormatException {
StringBuilder sb = new StringBuilder().append(req.isSsl() ? "https://" : "http://");
sb.append(req.getHeader("Host"));
sb.append(HeaderParser.extractLocation(req.getStartLine()));
return sb.toString();
}
public void endSession() throws JAXBException, IOException {
if (sessionStarted) {
osw.write("\n");
osw.write("</sns:session>");
osw.flush();
osw.close();
}
sessionStarted = false;
System.out.println("Finishing up");
}
/**
*
*/
public void pauseSession() {
paused = true;
}
}