/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.wsdl.monitor.jettyproxy;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.impl.wsdl.WsdlProject;
import com.eviware.soapui.impl.wsdl.actions.monitor.SoapMonitorAction;
import com.eviware.soapui.impl.wsdl.actions.monitor.SoapMonitorAction.LaunchForm;
import com.eviware.soapui.impl.wsdl.monitor.ContentTypes;
import com.eviware.soapui.impl.wsdl.monitor.JProxyServletWsdlMonitorMessageExchange;
import com.eviware.soapui.impl.wsdl.monitor.SoapMonitorListenerCallBack;
import com.eviware.soapui.impl.wsdl.submit.transports.http.ExtendedHttpMethod;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedGenericMethod;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedGetMethod;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedHeadMethod;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedOptionsMethod;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedPatchMethod;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedPostMethod;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedPutMethod;
import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedTraceMethod;
import com.eviware.soapui.impl.wsdl.support.http.HttpClientSupport;
import com.eviware.soapui.impl.wsdl.support.http.ProxyUtils;
import com.eviware.soapui.model.settings.Settings;
import com.eviware.soapui.support.Tools;
import com.eviware.soapui.support.types.StringToStringsMap;
import org.apache.http.Header;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpVersion;
import org.apache.http.client.params.ClientPNames;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.params.CoreProtocolPNames;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.mortbay.util.IO;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
public class ProxyServlet implements Servlet {
protected ServletConfig config;
protected ServletContext context;
protected WsdlProject project;
protected HttpContext httpState = new BasicHttpContext();
protected Settings settings;
protected final SoapMonitorListenerCallBack listenerCallBack;
private ContentTypes includedContentTypes = SoapMonitorAction.defaultContentTypes();
static HashSet<String> dontProxyHeaders = new HashSet<String>();
static {
dontProxyHeaders.add("proxy-connection");
dontProxyHeaders.add("connection");
dontProxyHeaders.add("keep-alive");
dontProxyHeaders.add("transfer-encoding");
dontProxyHeaders.add("te");
dontProxyHeaders.add("trailer");
dontProxyHeaders.add("proxy-authorization");
dontProxyHeaders.add("proxy-authenticate");
dontProxyHeaders.add("upgrade");
dontProxyHeaders.add("content-length");
}
public ProxyServlet(final WsdlProject project, final SoapMonitorListenerCallBack listenerCallBack) {
this.listenerCallBack = listenerCallBack;
this.project = project;
settings = project.getSettings();
}
public void destroy() {
}
public ServletConfig getServletConfig() {
return config;
}
public String getServletInfo() {
return "SoapUI Monitor";
}
public void setIncludedContentTypes(ContentTypes includedContentTypes) {
this.includedContentTypes = includedContentTypes != null
? includedContentTypes
: SoapMonitorAction.defaultContentTypes();
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.context = config.getServletContext();
}
public void service(ServletRequest request, ServletResponse response) throws ServletException, IOException {
listenerCallBack.fireOnRequest(project, request, response);
if (response.isCommitted()) {
return;
}
ExtendedHttpMethod method;
HttpServletRequest httpRequest = (HttpServletRequest) request;
if (httpRequest.getMethod().equals("GET")) {
method = new ExtendedGetMethod();
} else if (httpRequest.getMethod().equals("POST")) {
method = new ExtendedPostMethod();
} else if (httpRequest.getMethod().equals("PUT")) {
method = new ExtendedPutMethod();
} else if (httpRequest.getMethod().equals("HEAD")) {
method = new ExtendedHeadMethod();
} else if (httpRequest.getMethod().equals("OPTIONS")) {
method = new ExtendedOptionsMethod();
} else if (httpRequest.getMethod().equals("TRACE")) {
method = new ExtendedTraceMethod();
} else if (httpRequest.getMethod().equals("PATCH")) {
method = new ExtendedPatchMethod();
} else {
method = new ExtendedGenericMethod(httpRequest.getMethod());
}
method.setDecompress(false);
ByteArrayOutputStream requestBody = null;
if (method instanceof HttpEntityEnclosingRequest) {
requestBody = Tools.readAll(request.getInputStream(), 0);
ByteArrayEntity entity = new ByteArrayEntity(requestBody.toByteArray());
entity.setContentType(request.getContentType());
((HttpEntityEnclosingRequest) method).setEntity(entity);
}
// for this create ui server and port, properties.
JProxyServletWsdlMonitorMessageExchange capturedData = new JProxyServletWsdlMonitorMessageExchange(project);
capturedData.setRequestHost(httpRequest.getServerName());
capturedData.setRequestMethod(httpRequest.getMethod());
capturedData.setRequestHeader(httpRequest);
capturedData.setHttpRequestParameters(httpRequest);
capturedData.setQueryParameters(httpRequest.getQueryString());
capturedData.setTargetURL(httpRequest.getRequestURL().toString());
// CaptureInputStream capture = new CaptureInputStream( httpRequest.getInputStream() );
// check connection header
String connectionHeader = httpRequest.getHeader("Connection");
if (connectionHeader != null) {
connectionHeader = connectionHeader.toLowerCase();
if (!connectionHeader.contains("keep-alive") && !connectionHeader.contains("close")) {
connectionHeader = null;
}
}
// copy headers
boolean xForwardedFor = false;
@SuppressWarnings("unused")
Enumeration<?> headerNames = httpRequest.getHeaderNames();
while (headerNames.hasMoreElements()) {
String hdr = (String) headerNames.nextElement();
String lhdr = hdr.toLowerCase();
if (dontProxyHeaders.contains(lhdr)) {
continue;
}
if (connectionHeader != null && connectionHeader.contains(lhdr)) {
continue;
}
Enumeration<?> vals = httpRequest.getHeaders(hdr);
while (vals.hasMoreElements()) {
String val = (String) vals.nextElement();
if (val != null) {
method.setHeader(lhdr, val);
xForwardedFor |= "X-Forwarded-For".equalsIgnoreCase(hdr);
}
}
}
// Proxy headers
method.setHeader("Via", "SoapUI Monitor");
if (!xForwardedFor) {
method.addHeader("X-Forwarded-For", request.getRemoteAddr());
}
StringBuffer url = new StringBuffer("http://");
url.append(httpRequest.getServerName());
if (httpRequest.getServerPort() != 80) {
url.append(":" + httpRequest.getServerPort());
}
if (httpRequest.getServletPath() != null) {
url.append(httpRequest.getServletPath());
try {
method.setURI(new java.net.URI(url.toString().replaceAll(" ", "%20")));
} catch (URISyntaxException e) {
SoapUI.logError(e);
}
if (httpRequest.getQueryString() != null) {
url.append("?" + httpRequest.getQueryString());
try {
method.setURI(new java.net.URI(url.toString()));
} catch (URISyntaxException e) {
SoapUI.logError(e);
}
}
}
method.getParams().setParameter(ClientPNames.HANDLE_REDIRECTS, false);
setProtocolversion(method, request.getProtocol());
ProxyUtils.setForceDirectConnection(method.getParams());
listenerCallBack.fireBeforeProxy(project, request, response, method);
if (settings.getBoolean(LaunchForm.SSLTUNNEL_REUSESTATE)) {
if (httpState == null) {
httpState = new BasicHttpContext();
}
HttpClientSupport.execute(method, httpState);
} else {
HttpClientSupport.execute(method);
}
// wait for transaction to end and store it.
capturedData.stopCapture();
capturedData.setRequest(requestBody == null ? null : requestBody.toByteArray());
capturedData.setRawResponseBody(method.getResponseBody());
capturedData.setResponseHeader(method.getHttpResponse());
capturedData.setRawRequestData(getRequestToBytes(request.toString(), requestBody));
capturedData.setRawResponseData(getResponseToBytes(method, capturedData.getRawResponseBody()));
byte[] decompressedResponseBody = method.getDecompressedResponseBody();
capturedData.setResponseContent(decompressedResponseBody != null ? new String(decompressedResponseBody) : "");
capturedData.setResponseStatusCode(method.hasHttpResponse() ? method.getHttpResponse().getStatusLine()
.getStatusCode() : null);
capturedData.setResponseStatusLine(method.hasHttpResponse() ? method.getHttpResponse().getStatusLine()
.toString() : null);
listenerCallBack.fireAfterProxy(project, request, response, method, capturedData);
((HttpServletResponse) response).setStatus(method.hasHttpResponse() ? method.getHttpResponse()
.getStatusLine().getStatusCode() : null);
if (!response.isCommitted()) {
StringToStringsMap responseHeaders = capturedData.getResponseHeaders();
// capturedData = null;
// copy headers to response
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
for (Map.Entry<String, List<String>> headerEntry : responseHeaders.entrySet()) {
for (String header : headerEntry.getValue()) {
httpServletResponse.addHeader(headerEntry.getKey(), header);
}
}
if (capturedData.getRawResponseBody() != null) {
IO.copy(new ByteArrayInputStream(capturedData.getRawResponseBody()), httpServletResponse.getOutputStream());
}
}
synchronized (this) {
if (contentTypeMatches(method)) {
listenerCallBack.fireAddMessageExchange(capturedData);
}
}
}
protected boolean contentTypeMatches(ExtendedHttpMethod method) {
if (method.hasHttpResponse()) {
Header[] headers = method.getHttpResponse().getHeaders("Content-Type");
if (headers.length == 0) {
return true;
}
for (Header header : headers) {
if (includedContentTypes.matches(header.getValue())) {
return true;
}
}
}
return false;
}
private byte[] getResponseToBytes(ExtendedHttpMethod method, byte[] res) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
StringBuilder response = new StringBuilder();
if (method.hasHttpResponse()) {
response.append(method.getHttpResponse().getStatusLine().toString());
response.append("\r\n");
Header[] headers = method.getHttpResponse().getAllHeaders();
for (Header header : headers) {
response.append(header.toString().trim()).append("\r\n");
}
response.append("\r\n");
try {
out.write(response.toString().getBytes());
if (res != null) {
out.write(res);
}
} catch (IOException e) {
e.printStackTrace();
}
}
return out.toByteArray();
}
private byte[] getRequestToBytes(String footer, ByteArrayOutputStream requestBody) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
out.write(footer.trim().getBytes());
out.write("\r\n\r\n".getBytes());
if (requestBody != null) {
out.write(requestBody.toByteArray());
}
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
protected void setProtocolversion(ExtendedHttpMethod postMethod, String protocolVersion) {
if (protocolVersion.equals(HttpVersion.HTTP_1_1.toString())) {
postMethod.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_1);
} else if (protocolVersion.equals(HttpVersion.HTTP_1_0.toString())) {
postMethod.getParams().setParameter(CoreProtocolPNames.PROTOCOL_VERSION, HttpVersion.HTTP_1_0);
}
}
}