View Javadoc

1   /*
2    *  soapUI, copyright (C) 2004-2010 eviware.com 
3    *
4    *  soapUI is free software; you can redistribute it and/or modify it under the 
5    *  terms of version 2.1 of the GNU Lesser General Public License as published by 
6    *  the Free Software Foundation.
7    *
8    *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
9    *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
10   *  See the GNU Lesser General Public License for more details at gnu.org.
11   */
12  package com.eviware.soapui.impl.wsdl.monitor.jettyproxy;
13  
14  import java.io.ByteArrayInputStream;
15  import java.io.ByteArrayOutputStream;
16  import java.io.IOException;
17  import java.util.ArrayList;
18  import java.util.Enumeration;
19  import java.util.HashSet;
20  import java.util.List;
21  
22  import javax.servlet.Servlet;
23  import javax.servlet.ServletConfig;
24  import javax.servlet.ServletContext;
25  import javax.servlet.ServletException;
26  import javax.servlet.ServletRequest;
27  import javax.servlet.ServletResponse;
28  import javax.servlet.http.HttpServletRequest;
29  import javax.servlet.http.HttpServletResponse;
30  
31  import org.apache.commons.httpclient.Header;
32  import org.apache.commons.httpclient.HostConfiguration;
33  import org.apache.commons.httpclient.HttpState;
34  import org.apache.commons.httpclient.URI;
35  import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
36  import org.mortbay.util.IO;
37  
38  import com.eviware.soapui.impl.wsdl.WsdlProject;
39  import com.eviware.soapui.impl.wsdl.actions.monitor.SoapMonitorAction;
40  import com.eviware.soapui.impl.wsdl.actions.monitor.SoapMonitorAction.LaunchForm;
41  import com.eviware.soapui.impl.wsdl.monitor.CaptureInputStream;
42  import com.eviware.soapui.impl.wsdl.monitor.JProxyServletWsdlMonitorMessageExchange;
43  import com.eviware.soapui.impl.wsdl.monitor.SoapMonitor;
44  import com.eviware.soapui.impl.wsdl.submit.transports.http.ExtendedHttpMethod;
45  import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedGetMethod;
46  import com.eviware.soapui.impl.wsdl.submit.transports.http.support.methods.ExtendedPostMethod;
47  import com.eviware.soapui.impl.wsdl.support.http.HttpClientSupport;
48  import com.eviware.soapui.model.settings.Settings;
49  import com.eviware.soapui.support.types.StringToStringsMap;
50  
51  public class ProxyServlet implements Servlet
52  {
53  	protected ServletConfig config;
54  	protected ServletContext context;
55  	protected SoapMonitor monitor;
56  	protected WsdlProject project;
57  	protected HttpState httpState = new HttpState();
58  	protected Settings settings;
59  
60  	static HashSet<String> dontProxyHeaders = new HashSet<String>();
61  	{
62  		dontProxyHeaders.add( "proxy-connection" );
63  		dontProxyHeaders.add( "connection" );
64  		dontProxyHeaders.add( "keep-alive" );
65  		dontProxyHeaders.add( "transfer-encoding" );
66  		dontProxyHeaders.add( "te" );
67  		dontProxyHeaders.add( "trailer" );
68  		dontProxyHeaders.add( "proxy-authorization" );
69  		dontProxyHeaders.add( "proxy-authenticate" );
70  		dontProxyHeaders.add( "upgrade" );
71  	}
72  
73  	public ProxyServlet( SoapMonitor soapMonitor )
74  	{
75  		this.monitor = soapMonitor;
76  		this.project = monitor.getProject();
77  		settings = project.getSettings();
78  	}
79  
80  	public void destroy()
81  	{
82  	}
83  
84  	public ServletConfig getServletConfig()
85  	{
86  		return config;
87  	}
88  
89  	public String getServletInfo()
90  	{
91  		return "SoapUI Monitor";
92  	}
93  
94  	public void init( ServletConfig config ) throws ServletException
95  	{
96  		this.config = config;
97  		this.context = config.getServletContext();
98  	}
99  
100 	public void service( ServletRequest request, ServletResponse response ) throws ServletException, IOException
101 	{
102 		monitor.fireOnRequest( request, response );
103 		if( response.isCommitted() )
104 			return;
105 
106 		ExtendedHttpMethod method;
107 		HttpServletRequest httpRequest = ( HttpServletRequest )request;
108 		if( httpRequest.getMethod().equals( "GET" ) )
109 			method = new ExtendedGetMethod();
110 		else
111 			method = new ExtendedPostMethod();
112 
113 		method.setDecompress( false );
114 
115 		// for this create ui server and port, properties.
116 		JProxyServletWsdlMonitorMessageExchange capturedData = new JProxyServletWsdlMonitorMessageExchange( project );
117 		capturedData.setRequestHost( httpRequest.getServerName() );
118 		capturedData.setRequestMethod( httpRequest.getMethod() );
119 		capturedData.setRequestHeader( httpRequest );
120 		capturedData.setHttpRequestParameters( httpRequest );
121 		capturedData.setTargetURL( httpRequest.getRequestURL().toString() );
122 
123 		CaptureInputStream capture = new CaptureInputStream( httpRequest.getInputStream() );
124 
125 		// check connection header
126 		String connectionHeader = httpRequest.getHeader( "Connection" );
127 		if( connectionHeader != null )
128 		{
129 			connectionHeader = connectionHeader.toLowerCase();
130 			if( connectionHeader.indexOf( "keep-alive" ) < 0 && connectionHeader.indexOf( "close" ) < 0 )
131 				connectionHeader = null;
132 		}
133 
134 		// copy headers
135 		boolean xForwardedFor = false;
136 		@SuppressWarnings( "unused" )
137 		long contentLength = -1;
138 		Enumeration<?> headerNames = httpRequest.getHeaderNames();
139 		while( headerNames.hasMoreElements() )
140 		{
141 			String hdr = ( String )headerNames.nextElement();
142 			String lhdr = hdr.toLowerCase();
143 
144 			if( dontProxyHeaders.contains( lhdr ) )
145 				continue;
146 			if( connectionHeader != null && connectionHeader.indexOf( lhdr ) >= 0 )
147 				continue;
148 
149 			if( "content-length".equals( lhdr ) )
150 				contentLength = request.getContentLength();
151 
152 			Enumeration<?> vals = httpRequest.getHeaders( hdr );
153 			while( vals.hasMoreElements() )
154 			{
155 				String val = ( String )vals.nextElement();
156 				if( val != null )
157 				{
158 					method.setRequestHeader( lhdr, val );
159 					xForwardedFor |= "X-Forwarded-For".equalsIgnoreCase( hdr );
160 				}
161 			}
162 		}
163 
164 		// Proxy headers
165 		method.setRequestHeader( "Via", "SoapUI Monitor" );
166 		if( !xForwardedFor )
167 			method.addRequestHeader( "X-Forwarded-For", request.getRemoteAddr() );
168 
169 		if( method instanceof ExtendedPostMethod )
170 			( ( ExtendedPostMethod )method ).setRequestEntity( new InputStreamRequestEntity( capture, request
171 					.getContentType() ) );
172 
173 		HostConfiguration hostConfiguration = new HostConfiguration();
174 
175 		StringBuffer url = new StringBuffer( "http://" );
176 		url.append( httpRequest.getServerName() );
177 		if( httpRequest.getServerPort() != 80 )
178 			url.append( ":" + httpRequest.getServerPort() );
179 		if( httpRequest.getServletPath() != null )
180 		{
181 			url.append( httpRequest.getServletPath() );
182 			method.setPath( httpRequest.getServletPath() );
183 			if( httpRequest.getQueryString() != null )
184 			{
185 				url.append( "?" + httpRequest.getQueryString() );
186 				method.setPath( httpRequest.getServletPath() + "?" + httpRequest.getQueryString() );
187 			}
188 		}
189 		hostConfiguration.setHost( new URI( url.toString(), true ) );
190 
191 		// SoapUI.log("PROXY to:" + url);
192 
193 		monitor.fireBeforeProxy( request, response, method, hostConfiguration );
194 
195 		if( settings.getBoolean( LaunchForm.SSLTUNNEL_REUSESTATE ) )
196 		{
197 			if( httpState == null )
198 				httpState = new HttpState();
199 			HttpClientSupport.getHttpClient().executeMethod( hostConfiguration, method, httpState );
200 		}
201 		else
202 		{
203 			HttpClientSupport.getHttpClient().executeMethod( hostConfiguration, method );
204 		}
205 
206 		// wait for transaction to end and store it.
207 		capturedData.stopCapture();
208 
209 		capturedData.setRequest( capture.getCapturedData() );
210 		capturedData.setRawResponseBody( method.getResponseBody() );
211 		capturedData.setResponseHeader( method );
212 		capturedData.setRawRequestData( getRequestToBytes( request.toString(), method, capture ) );
213 		capturedData.setRawResponseData( getResponseToBytes( response.toString(), method, capturedData
214 				.getRawResponseBody() ) );
215 		capturedData.setResponseContent( new String( method.getDecompressedResponseBody() ) );
216 
217 		monitor.fireAfterProxy( request, response, method, capturedData );
218 
219 		if( !response.isCommitted() )
220 		{
221 			StringToStringsMap responseHeaders = capturedData.getResponseHeaders();
222 			// capturedData = null;
223 
224 			// copy headers to response
225 			HttpServletResponse httpResponse = ( HttpServletResponse )response;
226 			for( String name : responseHeaders.keySet() )
227 			{
228 				for( String header : responseHeaders.get( name ) )
229 					httpResponse.addHeader( name, header );
230 			}
231 
232 			IO.copy( new ByteArrayInputStream( capturedData.getRawResponseBody() ), httpResponse.getOutputStream() );
233 		}
234 
235 		synchronized( this )
236 		{
237 			if( checkContentType( method ) )
238 			{
239 				monitor.addMessageExchange( capturedData );
240 			}
241 		}
242 	}
243 
244 	private boolean checkContentType( ExtendedHttpMethod method )
245 	{
246 		String[] contentTypes = settings
247 				.getString( LaunchForm.SET_CONTENT_TYPES, SoapMonitorAction.defaultContentTypes() ).split( "," );
248 		List<String> contentTypelist = new ArrayList<String>();
249 		for( String ct : contentTypes )
250 		{
251 			contentTypelist.add( ct.trim().replace( "*", "" ) );
252 		}
253 
254 		Header[] headers = method.getResponseHeaders( "Content-Type" );
255 		for( Header header : headers )
256 		{
257 			for( String contentType : contentTypelist )
258 			{
259 				if( header.getValue().indexOf( contentType ) > 0 )
260 				{
261 					return true;
262 				}
263 			}
264 		}
265 		return false;
266 	}
267 
268 	private byte[] getResponseToBytes( String status, ExtendedHttpMethod postMethod, byte[] res )
269 	{
270 		String response = status.trim() + "\r\n";
271 
272 		Header[] headers = postMethod.getResponseHeaders();
273 		for( Header header : headers )
274 		{
275 			response += header.toString().trim() + "\r\n";
276 		}
277 		response += "\r\n";
278 
279 		ByteArrayOutputStream out = new ByteArrayOutputStream();
280 		try
281 		{
282 			out.write( response.getBytes() );
283 			out.write( res );
284 		}
285 		catch( IOException e )
286 		{
287 			e.printStackTrace();
288 		}
289 		return out.toByteArray();
290 	}
291 
292 	private byte[] getRequestToBytes( String footer, ExtendedHttpMethod postMethod, CaptureInputStream capture )
293 	{
294 		ByteArrayOutputStream out = new ByteArrayOutputStream();
295 
296 		try
297 		{
298 			out.write( footer.trim().getBytes() );
299 			out.write( "\r\n\r\n".getBytes() );
300 			out.write( capture.getCapturedData() );
301 		}
302 		catch( IOException e )
303 		{
304 			e.printStackTrace();
305 		}
306 
307 		return out.toByteArray();
308 	}
309 
310 }