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.support.http;
14  
15  import java.io.File;
16  import java.io.IOException;
17  import java.security.GeneralSecurityException;
18  import java.security.KeyManagementException;
19  import java.security.KeyStoreException;
20  import java.security.NoSuchAlgorithmException;
21  import java.security.cert.CertificateException;
22  
23  import org.apache.commons.httpclient.Header;
24  import org.apache.commons.httpclient.HttpClient;
25  import org.apache.commons.httpclient.HttpMethod;
26  import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
27  import org.apache.commons.httpclient.protocol.Protocol;
28  import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
29  import org.apache.commons.ssl.KeyMaterial;
30  import org.apache.log4j.Logger;
31  
32  import com.eviware.soapui.SoapUI;
33  import com.eviware.soapui.impl.wsdl.support.CompressionSupport;
34  import com.eviware.soapui.model.settings.Settings;
35  import com.eviware.soapui.model.settings.SettingsListener;
36  import com.eviware.soapui.settings.HttpSettings;
37  import com.eviware.soapui.settings.SSLSettings;
38  import com.eviware.soapui.support.StringUtils;
39  
40  /***
41   * HttpClient related tools
42   * 
43   * @author Ole.Matzura
44   */
45  
46  public class HttpClientSupport
47  {
48  	private final static Helper helper = new Helper();
49  
50  	/***
51  	 * Internal helper to ensure synchronized access..
52  	 */
53  
54  	private static class Helper
55  	{
56  		private HttpClient httpClient;
57  		private final static Logger log = Logger.getLogger( HttpClientSupport.Helper.class );
58  		private SoapUIEasySSLProtocolSocketFactory easySSL;
59  		private SoapUIMultiThreadedHttpConnectionManager connectionManager;
60  
61  		public Helper()
62  		{
63  			try
64  			{
65  				easySSL = new SoapUIEasySSLProtocolSocketFactory();
66  				initSSL( easySSL );
67  
68  				Protocol easyhttps = new Protocol( "https", ( ProtocolSocketFactory )easySSL, 443 );
69  				Protocol.registerProtocol( "https", easyhttps );
70  			}
71  			catch( Throwable e )
72  			{
73  				SoapUI.log( e );
74  			}
75  
76  			Settings settings = SoapUI.getSettings();
77  
78  			connectionManager = new SoapUIMultiThreadedHttpConnectionManager();
79  			connectionManager.getParams().setDefaultMaxConnectionsPerHost(
80  					( int )settings.getLong( HttpSettings.MAX_CONNECTIONS_PER_HOST, 500 ) );
81  			connectionManager.getParams().setMaxTotalConnections(
82  					( int )settings.getLong( HttpSettings.MAX_TOTAL_CONNECTIONS, 2000 ) );
83  			httpClient = new HttpClient( connectionManager );
84  
85  			settings.addSettingsListener( new SSLSettingsListener() );
86  		}
87  
88  		private void initSSL( EasySSLProtocolSocketFactory easySSL ) throws IOException, GeneralSecurityException
89  		{
90  			initKeyMaterial( easySSL );
91  
92  			/*
93  			 * Commented out for now - EasySSLProtocolSocketFactory already trusts
94  			 * everything! Below is some code that might work for when SoapUI moves
95  			 * away from "EasySSLProtocolSocketFactory".
96  			 * 
97  			 * String trustStore = settings.getString( SSLSettings.TRUSTSTORE, null
98  			 * ); trustStore = trustStore != null ? trustStore.trim() : ""; pass =
99  			 * settings.getString( SSLSettings.TRUSTSTORE_PASSWORD, "" ); pwd =
100 			 * pass.toCharArray(); if ( !"".equals( trustStore ) ) { File f = new
101 			 * File( trustStore ); if ( f.exists() ) { TrustMaterial tm = null; try
102 			 * { tm = new TrustMaterial( trustStore, pwd ); } catch (
103 			 * GeneralSecurityException gse ) { String trimmedPass = pass.trim();
104 			 * if ( "".equals( trimmedPass ) ) { // If the password is all spaces,
105 			 * then we'll allow // loading of the TrustMaterial without a password.
106 			 * tm = new TrustMaterial( trustStore ); } else { log.error(
107 			 * "Failed to load TrustMaterial: " + gse ); } } if ( tm != null ) {
108 			 * easySSL.setTrustMaterial( tm ); log.info(
109 			 * "Added TrustStore from file [" + trustStore + "]" ); } } else {
110 			 * log.error( "Missing trustStore [" + trustStore + "]" ); } }
111 			 */
112 		}
113 
114 		private void initKeyMaterial( EasySSLProtocolSocketFactory easySSL ) throws IOException,
115 				NoSuchAlgorithmException, KeyStoreException, KeyManagementException, CertificateException
116 		{
117 			Settings settings = SoapUI.getSettings();
118 
119 			String keyStore = settings.getString( SSLSettings.KEYSTORE, null );
120 			keyStore = keyStore != null ? keyStore.trim() : "";
121 			String pass = settings.getString( SSLSettings.KEYSTORE_PASSWORD, "" );
122 			char[] pwd = pass.toCharArray();
123 			if( !"".equals( keyStore ) )
124 			{
125 				log.info( "Initializing KeyStore" );
126 
127 				File f = new File( keyStore );
128 				if( f.exists() )
129 				{
130 					KeyMaterial km = null;
131 					try
132 					{
133 						km = new KeyMaterial( keyStore, pwd );
134 						log.info( "Set KeyMaterial from file [" + keyStore + "]" );
135 					}
136 					catch( GeneralSecurityException gse )
137 					{
138 						SoapUI.logError( gse );
139 					}
140 					if( km != null )
141 					{
142 						easySSL.setKeyMaterial( km );
143 					}
144 				}
145 			}
146 			else
147 			{
148 				easySSL.setKeyMaterial( null );
149 			}
150 		}
151 
152 		public HttpClient getHttpClient()
153 		{
154 			return httpClient;
155 		}
156 
157 		public final class SSLSettingsListener implements SettingsListener
158 		{
159 			public void settingChanged( String name, String newValue, String oldValue )
160 			{
161 				if( !StringUtils.hasContent( newValue ) )
162 					return;
163 
164 				if( name.equals( SSLSettings.KEYSTORE ) || name.equals( SSLSettings.KEYSTORE_PASSWORD ) )
165 				{
166 					try
167 					{
168 						log.info( "Updating keyStore.." );
169 						initKeyMaterial( easySSL );
170 					}
171 					catch( Throwable e )
172 					{
173 						SoapUI.logError( e );
174 					}
175 				}
176 				else if( name.equals( HttpSettings.MAX_CONNECTIONS_PER_HOST ) )
177 				{
178 					log.info( "Updating max connections per host to " + newValue );
179 					connectionManager.getParams().setDefaultMaxConnectionsPerHost( Integer.parseInt( newValue ) );
180 				}
181 				else if( name.equals( HttpSettings.MAX_TOTAL_CONNECTIONS ) )
182 				{
183 					log.info( "Updating max total connections host to " + newValue );
184 					connectionManager.getParams().setMaxTotalConnections( Integer.parseInt( newValue ) );
185 				}
186 			}
187 
188 			@Override
189 			public void settingsReloaded()
190 			{
191 				try
192 				{
193 					log.info( "Updating keyStore.." );
194 					initKeyMaterial( easySSL );
195 				}
196 				catch( Throwable e )
197 				{
198 					SoapUI.logError( e );
199 				}
200 			}
201 		}
202 
203 	}
204 
205 	public static HttpClient getHttpClient()
206 	{
207 		return helper.getHttpClient();
208 	}
209 
210 	public static void applyHttpSettings( HttpMethod httpMethod, Settings settings )
211 	{
212 		// user agent?
213 		String userAgent = settings.getString( HttpSettings.USER_AGENT, null );
214 		if( userAgent != null && userAgent.length() > 0 )
215 			httpMethod.setRequestHeader( "User-Agent", userAgent );
216 
217 		// timeout?
218 		long timeout = settings.getLong( HttpSettings.SOCKET_TIMEOUT, HttpSettings.DEFAULT_SOCKET_TIMEOUT );
219 		httpMethod.getParams().setSoTimeout( ( int )timeout );
220 	}
221 
222 	public static String getResponseCompressionType( HttpMethod method )
223 	{
224 		Header contentType = method.getResponseHeader( "Content-Type" );
225 		Header contentEncoding = method.getResponseHeader( "Content-Encoding" );
226 
227 		return getCompressionType( contentType == null ? null : contentType.getValue(), contentEncoding == null ? null
228 				: contentEncoding.getValue() );
229 	}
230 
231 	public static String getCompressionType( String contentType, String contentEncoding )
232 	{
233 		String compressionAlg = contentType == null ? null : CompressionSupport.getAvailableAlgorithm( contentType );
234 		if( compressionAlg != null )
235 			return compressionAlg;
236 
237 		if( contentEncoding == null )
238 			return null;
239 		else
240 			return CompressionSupport.getAvailableAlgorithm( contentEncoding );
241 	}
242 
243 	public static void addSSLListener( Settings settings )
244 	{
245 		settings.addSettingsListener( helper.new SSLSettingsListener() );
246 	}
247 }