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  
13  package com.eviware.soapui.impl.wsdl.submit.filters;
14  
15  import java.io.File;
16  import java.io.UnsupportedEncodingException;
17  import java.net.URLEncoder;
18  import java.util.ArrayList;
19  import java.util.List;
20  
21  import javax.activation.DataHandler;
22  import javax.activation.FileDataSource;
23  import javax.mail.MessagingException;
24  import javax.mail.internet.MimeBodyPart;
25  import javax.mail.internet.MimeMessage;
26  import javax.mail.internet.MimeMultipart;
27  import javax.mail.internet.PreencodedMimeBodyPart;
28  
29  import org.apache.commons.httpclient.HttpMethod;
30  import org.apache.commons.httpclient.URI;
31  import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
32  import org.apache.commons.httpclient.methods.EntityEnclosingMethod;
33  import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
34  import org.apache.commons.httpclient.methods.StringRequestEntity;
35  import org.apache.xmlbeans.XmlBoolean;
36  
37  import com.eviware.soapui.SoapUI;
38  import com.eviware.soapui.impl.rest.RestRequest;
39  import com.eviware.soapui.impl.rest.support.RestParamProperty;
40  import com.eviware.soapui.impl.rest.support.RestParamsPropertyHolder;
41  import com.eviware.soapui.impl.rest.support.RestUtils;
42  import com.eviware.soapui.impl.rest.support.RestParamsPropertyHolder.ParameterStyle;
43  import com.eviware.soapui.impl.support.http.HttpRequestInterface;
44  import com.eviware.soapui.impl.wsdl.submit.transports.http.BaseHttpRequestTransport;
45  import com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments.AttachmentDataSource;
46  import com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments.AttachmentUtils;
47  import com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments.RestRequestDataSource;
48  import com.eviware.soapui.impl.wsdl.submit.transports.http.support.attachments.RestRequestMimeMessageRequestEntity;
49  import com.eviware.soapui.impl.wsdl.support.FileAttachment;
50  import com.eviware.soapui.impl.wsdl.support.PathUtils;
51  import com.eviware.soapui.model.iface.Attachment;
52  import com.eviware.soapui.model.iface.SubmitContext;
53  import com.eviware.soapui.model.propertyexpansion.PropertyExpander;
54  import com.eviware.soapui.support.StringUtils;
55  import com.eviware.soapui.support.editor.inspectors.attachments.ContentTypeHandler;
56  import com.eviware.soapui.support.types.StringToStringMap;
57  
58  /***
59   * RequestFilter that adds SOAP specific headers
60   * 
61   * @author Ole.Matzura
62   */
63  
64  public class HttpRequestFilter extends AbstractRequestFilter
65  {
66  	@SuppressWarnings( "deprecation" )
67  	@Override
68  	public void filterHttpRequest( SubmitContext context, HttpRequestInterface<?> request )
69  	{
70  		HttpMethod httpMethod = ( HttpMethod )context.getProperty( BaseHttpRequestTransport.HTTP_METHOD );
71  
72  		String path = PropertyExpander.expandProperties( context, request.getPath() );
73  		StringBuffer query = new StringBuffer();
74  
75  		StringToStringMap responseProperties = ( StringToStringMap )context
76  				.getProperty( BaseHttpRequestTransport.RESPONSE_PROPERTIES );
77  
78  		MimeMultipart formMp = "multipart/form-data".equals( request.getMediaType() )
79  				&& httpMethod instanceof EntityEnclosingMethod ? new MimeMultipart() : null;
80  
81  		RestParamsPropertyHolder params = request.getParams();
82  
83  		for( int c = 0; c < params.getPropertyCount(); c++ )
84  		{
85  			RestParamProperty param = params.getPropertyAt( c );
86  
87  			String value = PropertyExpander.expandProperties( context, param.getValue() );
88  			responseProperties.put( param.getName(), value );
89  
90  			List<String> valueParts = RestUtils.splitMultipleParameters( value );
91  
92  			if( value != null && param.getStyle() != ParameterStyle.HEADER && !param.isDisableUrlEncoding() )
93  			{
94  				try
95  				{
96  					String encoding = System.getProperty( "soapui.request.encoding", request.getEncoding() );
97  
98  					if( StringUtils.hasContent( encoding ) )
99  					{
100 						value = URLEncoder.encode( value, encoding );
101 						for( int i = 0; i < valueParts.size(); i++ )
102 							valueParts.set( i, URLEncoder.encode( valueParts.get( i ), encoding ) );
103 					}
104 					else
105 					{
106 						value = URLEncoder.encode( value );
107 						for( int i = 0; i < valueParts.size(); i++ )
108 							valueParts.set( i, URLEncoder.encode( valueParts.get( i ) ) );
109 					}
110 				}
111 				catch( UnsupportedEncodingException e1 )
112 				{
113 					SoapUI.logError( e1 );
114 					value = URLEncoder.encode( value );
115 					for( int i = 0; i < valueParts.size(); i++ )
116 						valueParts.set( i, URLEncoder.encode( valueParts.get( i ) ) );
117 				}
118 				// URLEncoder replaces space with "+", but we want "%20".
119 				value = value.replaceAll( "//+", "%20" );
120 				for( int i = 0; i < valueParts.size(); i++ )
121 					valueParts.set( i, valueParts.get( i ).replaceAll( "//+", "%20" ) );
122 			}
123 
124 			if( !StringUtils.hasContent( value ) && !param.getRequired() )
125 				continue;
126 
127 			switch( param.getStyle() )
128 			{
129 			case HEADER :
130 				for( String valuePart : valueParts )
131 					httpMethod.addRequestHeader( param.getName(), valuePart );
132 				break;
133 			case QUERY :
134 				if( formMp == null || !request.isPostQueryString() )
135 				{
136 					for( String valuePart : valueParts )
137 					{
138 						if( query.length() > 0 )
139 							query.append( '&' );
140 
141 						query.append( URLEncoder.encode( param.getName() ) );
142 						query.append( '=' );
143 						if( StringUtils.hasContent( valuePart ) )
144 							query.append( valuePart );
145 					}
146 				}
147 				else
148 				{
149 					try
150 					{
151 						addFormMultipart( request, formMp, param.getName(), responseProperties.get( param.getName() ) );
152 					}
153 					catch( MessagingException e )
154 					{
155 						e.printStackTrace();
156 					}
157 				}
158 
159 				break;
160 			case TEMPLATE :
161 				path = path.replaceAll( "//{" + param.getName() + "//}", value == null ? "" : value );
162 				break;
163 			case MATRIX :
164 				if( param.getType().equals( XmlBoolean.type.getName() ) )
165 				{
166 					if( value.toUpperCase().equals( "TRUE" ) || value.equals( "1" ) )
167 					{
168 						path += ";" + param.getName();
169 					}
170 				}
171 				else
172 				{
173 					path += ";" + param.getName();
174 					if( StringUtils.hasContent( value ) )
175 					{
176 						path += "=" + value;
177 					}
178 				}
179 			case PLAIN :
180 				break;
181 			}
182 		}
183 
184 		path = PathUtils.fixForwardSlashesInPath( path );
185 
186 		if( PathUtils.isHttpPath( path ) )
187 		{
188 			try
189 			{
190 				// URI(String) automatically URLencodes the input, so we need to
191 				// decode it first...
192 				URI uri = new URI( path, false );
193 				context.setProperty( BaseHttpRequestTransport.REQUEST_URI, uri );
194 				httpMethod.setURI( uri );
195 			}
196 			catch( Exception e )
197 			{
198 				e.printStackTrace();
199 			}
200 		}
201 		else if( StringUtils.hasContent( path ) )
202 		{
203 			httpMethod.setPath( path );
204 		}
205 
206 		if( query.length() > 0 && !request.isPostQueryString() )
207 		{
208 			httpMethod.setQueryString( query.toString() );
209 		}
210 
211 		if( request instanceof RestRequest )
212 		{
213 			String acceptEncoding = ( ( RestRequest )request ).getAccept();
214 			if( StringUtils.hasContent( acceptEncoding ) )
215 			{
216 				httpMethod.setRequestHeader( "Accept", acceptEncoding );
217 			}
218 		}
219 
220 		String encoding = System.getProperty( "soapui.request.encoding", StringUtils.unquote( request.getEncoding() ) );
221 
222 		if( formMp != null )
223 		{
224 			// create request message
225 			try
226 			{
227 				if( request.hasRequestBody() && httpMethod instanceof EntityEnclosingMethod )
228 				{
229 					String requestContent = PropertyExpander.expandProperties( context, request.getRequestContent(), request
230 							.isEntitizeProperties() );
231 					if( StringUtils.hasContent( requestContent ) )
232 					{
233 						initRootPart( request, requestContent, formMp );
234 					}
235 				}
236 
237 				for( Attachment attachment : request.getAttachments() )
238 				{
239 					MimeBodyPart part = new PreencodedMimeBodyPart( "binary" );
240 
241 					if( attachment instanceof FileAttachment<?> )
242 					{
243 						String name = attachment.getName();
244 						if( StringUtils.hasContent( attachment.getContentID() ) && !name.equals( attachment.getContentID() ) )
245 							name = attachment.getContentID();
246 
247 						part.setDisposition( "form-data; name=\"" + name + "\"; filename=\"" + attachment.getName() + "\"" );
248 					}
249 					else
250 						part.setDisposition( "form-data; name=\"" + attachment.getName() + "\"" );
251 
252 					part.setDataHandler( new DataHandler( new AttachmentDataSource( attachment ) ) );
253 
254 					formMp.addBodyPart( part );
255 				}
256 
257 				MimeMessage message = new MimeMessage( AttachmentUtils.JAVAMAIL_SESSION );
258 				message.setContent( formMp );
259 				message.saveChanges();
260 				RestRequestMimeMessageRequestEntity mimeMessageRequestEntity = new RestRequestMimeMessageRequestEntity(
261 						message, request );
262 				( ( EntityEnclosingMethod )httpMethod ).setRequestEntity( mimeMessageRequestEntity );
263 				httpMethod.setRequestHeader( "Content-Type", mimeMessageRequestEntity.getContentType() );
264 				httpMethod.setRequestHeader( "MIME-Version", "1.0" );
265 			}
266 			catch( Throwable e )
267 			{
268 				SoapUI.logError( e );
269 			}
270 		}
271 		else if( request.hasRequestBody() && httpMethod instanceof EntityEnclosingMethod )
272 		{
273 			httpMethod.setRequestHeader( "Content-Type", getContentTypeHeader( request.getMediaType(), encoding ) );
274 
275 			if( request.isPostQueryString() )
276 			{
277 				( ( EntityEnclosingMethod )httpMethod ).setRequestEntity( new StringRequestEntity( query.toString() ) );
278 			}
279 			else
280 			{
281 				String requestContent = PropertyExpander.expandProperties( context, request.getRequestContent(), request
282 						.isEntitizeProperties() );
283 				List<Attachment> attachments = new ArrayList<Attachment>();
284 
285 				for( Attachment attachment : request.getAttachments() )
286 				{
287 					if( attachment.getContentType().equals( request.getMediaType() ) )
288 					{
289 						attachments.add( attachment );
290 					}
291 				}
292 
293 				if( StringUtils.hasContent( requestContent ) && attachments.isEmpty() )
294 				{
295 					try
296 					{
297 						byte[] content = encoding == null ? requestContent.getBytes() : requestContent.getBytes( encoding );
298 						( ( EntityEnclosingMethod )httpMethod ).setRequestEntity( new ByteArrayRequestEntity( content ) );
299 					}
300 					catch( UnsupportedEncodingException e )
301 					{
302 						( ( EntityEnclosingMethod )httpMethod ).setRequestEntity( new ByteArrayRequestEntity( requestContent
303 								.getBytes() ) );
304 					}
305 				}
306 				else if( attachments.size() > 0 )
307 				{
308 					try
309 					{
310 						MimeMultipart mp = null;
311 
312 						if( StringUtils.hasContent( requestContent ) )
313 						{
314 							mp = new MimeMultipart();
315 							initRootPart( request, requestContent, mp );
316 						}
317 						else if( attachments.size() == 1 )
318 						{
319 							( ( EntityEnclosingMethod )httpMethod ).setRequestEntity( new InputStreamRequestEntity(
320 									attachments.get( 0 ).getInputStream() ) );
321 
322 							httpMethod.setRequestHeader( "Content-Type", getContentTypeHeader( request.getMediaType(),
323 									encoding ) );
324 						}
325 
326 						if( ( ( EntityEnclosingMethod )httpMethod ).getRequestEntity() == null )
327 						{
328 							if( mp == null )
329 								mp = new MimeMultipart();
330 
331 							// init mimeparts
332 							AttachmentUtils.addMimeParts( request, attachments, mp, new StringToStringMap() );
333 
334 							// create request message
335 							MimeMessage message = new MimeMessage( AttachmentUtils.JAVAMAIL_SESSION );
336 							message.setContent( mp );
337 							message.saveChanges();
338 							RestRequestMimeMessageRequestEntity mimeMessageRequestEntity = new RestRequestMimeMessageRequestEntity(
339 									message, request );
340 							( ( EntityEnclosingMethod )httpMethod ).setRequestEntity( mimeMessageRequestEntity );
341 							httpMethod.setRequestHeader( "Content-Type", getContentTypeHeader( mimeMessageRequestEntity
342 									.getContentType(), encoding ) );
343 							httpMethod.setRequestHeader( "MIME-Version", "1.0" );
344 						}
345 					}
346 					catch( Exception e )
347 					{
348 						e.printStackTrace();
349 					}
350 				}
351 			}
352 		}
353 	}
354 
355 	private String getContentTypeHeader( String contentType, String encoding )
356 	{
357 		return ( encoding == null || encoding.trim().length() == 0 ) ? contentType : contentType + ";charset=" + encoding;
358 	}
359 
360 	private void addFormMultipart( HttpRequestInterface<?> request, MimeMultipart formMp, String name, String value )
361 			throws MessagingException
362 	{
363 		MimeBodyPart part = new MimeBodyPart();
364 
365 		if( value.startsWith( "file:" ) )
366 		{
367 			String fileName = value.substring( 5 );
368 			File file = new File( fileName );
369 			part.setDisposition( "form-data; name=\"" + name + "\"; filename=\"" + file.getName() + "\"" );
370 			if( file.exists() )
371 			{
372 				part.setDataHandler( new DataHandler( new FileDataSource( file ) ) );
373 			}
374 			else
375 			{
376 				for( Attachment attachment : request.getAttachments() )
377 				{
378 					if( attachment.getName().equals( fileName ) )
379 					{
380 						part.setDataHandler( new DataHandler( new AttachmentDataSource( attachment ) ) );
381 						break;
382 					}
383 				}
384 			}
385 
386 			part.setHeader( "Content-Type", ContentTypeHandler.getContentTypeFromFilename( file.getName() ) );
387 			part.setHeader( "Content-Transfer-Encoding", "binary" );
388 		}
389 		else
390 		{
391 			part.setDisposition( "form-data; name=\"" + name + "\"" );
392 			part.setText( value, System.getProperty( "soapui.request.encoding", request.getEncoding() ) );
393 		}
394 
395 		if( part != null )
396 		{
397 			formMp.addBodyPart( part );
398 		}
399 	}
400 
401 	protected void initRootPart( HttpRequestInterface<?> wsdlRequest, String requestContent, MimeMultipart mp )
402 			throws MessagingException
403 	{
404 		MimeBodyPart rootPart = new PreencodedMimeBodyPart( "8bit" );
405 		// rootPart.setContentID( AttachmentUtils.ROOTPART_SOAPUI_ORG );
406 		mp.addBodyPart( rootPart, 0 );
407 
408 		DataHandler dataHandler = new DataHandler( new RestRequestDataSource( wsdlRequest, requestContent ) );
409 		rootPart.setDataHandler( dataHandler );
410 	}
411 }