1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package android.core; 18 19import android.test.AndroidTestCase; 20import android.os.Debug; 21import org.apache.harmony.xnet.provider.jsse.FileClientSessionCache; 22import org.apache.harmony.xnet.provider.jsse.OpenSSLContextImpl; 23import org.apache.harmony.xnet.provider.jsse.SSLClientSessionCache; 24import org.apache.http.conn.scheme.SchemeRegistry; 25import org.apache.http.conn.scheme.Scheme; 26import org.apache.http.conn.ClientConnectionManager; 27import org.apache.http.conn.ssl.SSLSocketFactory; 28import org.apache.http.impl.conn.SingleClientConnManager; 29import org.apache.http.impl.client.DefaultHttpClient; 30import org.apache.http.client.methods.HttpGet; 31import org.apache.http.client.ResponseHandler; 32import org.apache.http.client.ClientProtocolException; 33import org.apache.http.HttpResponse; 34 35import javax.net.ssl.SSLSession; 36import javax.net.ssl.SSLSessionContext; 37import java.io.File; 38import java.io.IOException; 39import java.lang.reflect.Method; 40import java.lang.reflect.InvocationTargetException; 41import java.security.cert.Certificate; 42import java.security.Principal; 43import java.security.KeyManagementException; 44import java.util.Arrays; 45 46public class SSLPerformanceTest extends AndroidTestCase { 47 48 static final byte[] SESSION_DATA = new byte[6000]; 49 static { 50 for (int i = 0; i < SESSION_DATA.length; i++) { 51 SESSION_DATA[i] = (byte) i; 52 } 53 } 54 55 static final File dataDir = new File("/data/data/android.core/"); 56 static final File filesDir = new File(dataDir, "files"); 57 static final File dbDir = new File(dataDir, "databases"); 58 59 static final String CACHE_DIR 60 = SSLPerformanceTest.class.getName() + "/cache"; 61 62 static final int ITERATIONS = 10; 63 64 public void testCreateNewEmptyDatabase() { 65 deleteDatabase(); 66 67 Stopwatch stopwatch = new Stopwatch(); 68 69 DatabaseSessionCache cache = new DatabaseSessionCache(getContext()); 70 cache.getSessionData("crazybob.org", 443); 71 72 stopwatch.stop(); 73 } 74 75 public void testCreateNewEmptyDirectory() throws IOException { 76 deleteDirectory(); 77 78 Stopwatch stopwatch = new Stopwatch(); 79 80 SSLClientSessionCache cache = FileClientSessionCache.usingDirectory( 81 getCacheDirectory()); 82 cache.getSessionData("crazybob.org", 443); 83 84 stopwatch.stop(); 85 } 86 87 public void testOpenDatabaseWith10Sessions() { 88 deleteDatabase(); 89 90 DatabaseSessionCache cache = new DatabaseSessionCache(getContext()); 91 putSessionsIn(cache); 92 closeDatabase(); 93 94 System.err.println("Size of ssl_sessions.db w/ 10 sessions: " 95 + new File(dbDir, "ssl_sessions.db").length()); 96 97 Stopwatch stopwatch = new Stopwatch(); 98 99 cache = new DatabaseSessionCache(getContext()); 100 cache.getSessionData("crazybob.org", 443); 101 102 stopwatch.stop(); 103 } 104 105 public void testOpenDirectoryWith10Sessions() throws IOException { 106 deleteDirectory(); 107 108 SSLClientSessionCache cache = FileClientSessionCache.usingDirectory( 109 getCacheDirectory()); 110 putSessionsIn(cache); 111 closeDirectoryCache(); 112 113 Stopwatch stopwatch = new Stopwatch(); 114 115 cache = FileClientSessionCache.usingDirectory( 116 getCacheDirectory()); 117 cache.getSessionData("crazybob.org", 443); 118 119 stopwatch.stop(); 120 } 121 122 public void testGetSessionFromDatabase() { 123 deleteDatabase(); 124 125 DatabaseSessionCache cache = new DatabaseSessionCache(getContext()); 126 cache.putSessionData(new FakeSession("foo"), SESSION_DATA); 127 closeDatabase(); 128 129 cache = new DatabaseSessionCache(getContext()); 130 cache.getSessionData("crazybob.org", 443); 131 132 Stopwatch stopwatch = new Stopwatch(); 133 134 byte[] sessionData = cache.getSessionData("foo", 443); 135 136 stopwatch.stop(); 137 138 assertTrue(Arrays.equals(SESSION_DATA, sessionData)); 139 } 140 141 public void testGetSessionFromDirectory() throws IOException { 142 deleteDirectory(); 143 144 SSLClientSessionCache cache = FileClientSessionCache.usingDirectory( 145 getCacheDirectory()); 146 cache.putSessionData(new FakeSession("foo"), SESSION_DATA); 147 closeDirectoryCache(); 148 149 cache = FileClientSessionCache.usingDirectory( 150 getCacheDirectory()); 151 cache.getSessionData("crazybob.org", 443); 152 153 Stopwatch stopwatch = new Stopwatch(); 154 155 byte[] sessionData = cache.getSessionData("foo", 443); 156 157 stopwatch.stop(); 158 159 assertTrue(Arrays.equals(SESSION_DATA, sessionData)); 160 } 161 162 public void testPutSessionIntoDatabase() { 163 deleteDatabase(); 164 165 DatabaseSessionCache cache = new DatabaseSessionCache(getContext()); 166 cache.getSessionData("crazybob.org", 443); 167 168 Stopwatch stopwatch = new Stopwatch(); 169 170 cache.putSessionData(new FakeSession("foo"), SESSION_DATA); 171 172 stopwatch.stop(); 173 } 174 175 public void testPutSessionIntoDirectory() throws IOException { 176 deleteDirectory(); 177 178 SSLClientSessionCache cache = FileClientSessionCache.usingDirectory( 179 getCacheDirectory()); 180 cache.getSessionData("crazybob.org", 443); 181 182 Stopwatch stopwatch = new Stopwatch(); 183 184 cache.putSessionData(new FakeSession("foo"), SESSION_DATA); 185 186 stopwatch.stop(); 187 } 188 189 public void testEngineInit() throws IOException, KeyManagementException { 190 Stopwatch stopwatch = new Stopwatch(); 191 192 new OpenSSLContextImpl().engineInit(null, null, null); 193 194 stopwatch.stop(); 195 } 196 197 public void testWebRequestWithoutCache() throws IOException, 198 KeyManagementException { 199 OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); 200 sslContext.engineInit(null, null, null); 201 202 Stopwatch stopwatch = new Stopwatch(); 203 204 getVerisignDotCom(sslContext); 205 206 stopwatch.stop(); 207 } 208 209 public void testWebRequestWithFileCache() throws IOException, 210 KeyManagementException { 211 deleteDirectory(); 212 213 OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); 214 sslContext.engineInit(null, null, null); 215 sslContext.engineGetClientSessionContext().setPersistentCache( 216 FileClientSessionCache.usingDirectory(getCacheDirectory())); 217 218 // Make sure www.google.com is in the cache. 219 getVerisignDotCom(sslContext); 220 221 // Re-initialize so we hit the file cache. 222 sslContext.engineInit(null, null, null); 223 sslContext.engineGetClientSessionContext().setPersistentCache( 224 FileClientSessionCache.usingDirectory(getCacheDirectory())); 225 226 Stopwatch stopwatch = new Stopwatch(); 227 228 getVerisignDotCom(sslContext); 229 230 stopwatch.stop(); 231 } 232 233 public void testWebRequestWithInMemoryCache() throws IOException, 234 KeyManagementException { 235 deleteDirectory(); 236 237 OpenSSLContextImpl sslContext = new OpenSSLContextImpl(); 238 sslContext.engineInit(null, null, null); 239 240 // Make sure www.google.com is in the cache. 241 getVerisignDotCom(sslContext); 242 243 Stopwatch stopwatch = new Stopwatch(); 244 245 getVerisignDotCom(sslContext); 246 247 stopwatch.stop(); 248 } 249 250 private void getVerisignDotCom(OpenSSLContextImpl sslContext) 251 throws IOException { 252 SchemeRegistry schemeRegistry = new SchemeRegistry(); 253 schemeRegistry.register(new Scheme("https", 254 new SSLSocketFactory(sslContext.engineGetSocketFactory()), 255 443)); 256 257 ClientConnectionManager manager = 258 new SingleClientConnManager(null, schemeRegistry); 259 260 new DefaultHttpClient(manager, null).execute( 261 new HttpGet("https://www.verisign.com"), 262 new ResponseHandler<Object>() { 263 public Object handleResponse(HttpResponse response) 264 throws ClientProtocolException, IOException { 265 return null; 266 } 267 }); 268 } 269 270 private void putSessionsIn(SSLClientSessionCache cache) { 271 for (int i = 0; i < 10; i++) { 272 cache.putSessionData(new FakeSession("host" + i), SESSION_DATA); 273 } 274 } 275 276 private void deleteDatabase() { 277 closeDatabase(); 278 if (!new File(dbDir, "ssl_sessions.db").delete()) { 279 System.err.println("Failed to delete database."); 280 } 281 } 282 283 private void closeDatabase() { 284 if (DatabaseSessionCache.sDefaultDatabaseHelper != null) { 285 DatabaseSessionCache.sDefaultDatabaseHelper.close(); 286 } 287 DatabaseSessionCache.sDefaultDatabaseHelper = null; 288 DatabaseSessionCache.sHookInitializationDone = false; 289 DatabaseSessionCache.mNeedsCacheLoad = true; 290 } 291 292 private void deleteDirectory() { 293 closeDirectoryCache(); 294 295 File dir = getCacheDirectory(); 296 if (!dir.exists()) { 297 return; 298 } 299 for (File file : dir.listFiles()) { 300 file.delete(); 301 } 302 if (!dir.delete()) { 303 System.err.println("Failed to delete directory."); 304 } 305 } 306 307 private void closeDirectoryCache() { 308 try { 309 Method reset = FileClientSessionCache.class 310 .getDeclaredMethod("reset"); 311 reset.setAccessible(true); 312 reset.invoke(null); 313 } catch (NoSuchMethodException e) { 314 throw new RuntimeException(e); 315 } catch (IllegalAccessException e) { 316 throw new RuntimeException(e); 317 } catch (InvocationTargetException e) { 318 throw new RuntimeException(e); 319 } 320 } 321 322 private File getCacheDirectory() { 323 return new File(getContext().getFilesDir(), CACHE_DIR); 324 } 325 326 class Stopwatch { 327 { 328 Debug.startAllocCounting(); 329 } 330 long start = System.nanoTime(); 331 332 void stop() { 333 long elapsed = (System.nanoTime() - start) / 1000; 334 Debug.stopAllocCounting(); 335 System.err.println(getName() + ": " + elapsed + "us, " 336 + Debug.getThreadAllocCount() + " allocations, " 337 + Debug.getThreadAllocSize() + " bytes"); 338 } 339 } 340} 341 342class FakeSession implements SSLSession { 343 final String host; 344 345 FakeSession(String host) { 346 this.host = host; 347 } 348 349 public int getApplicationBufferSize() { 350 throw new UnsupportedOperationException(); 351 } 352 353 public String getCipherSuite() { 354 throw new UnsupportedOperationException(); 355 } 356 357 public long getCreationTime() { 358 throw new UnsupportedOperationException(); 359 } 360 361 public byte[] getId() { 362 return host.getBytes(); 363 } 364 365 public long getLastAccessedTime() { 366 throw new UnsupportedOperationException(); 367 } 368 369 public Certificate[] getLocalCertificates() { 370 throw new UnsupportedOperationException(); 371 } 372 373 public Principal getLocalPrincipal() { 374 throw new UnsupportedOperationException(); 375 } 376 377 public int getPacketBufferSize() { 378 throw new UnsupportedOperationException(); 379 } 380 381 public javax.security.cert.X509Certificate[] getPeerCertificateChain() { 382 throw new UnsupportedOperationException(); 383 } 384 385 public Certificate[] getPeerCertificates() { 386 throw new UnsupportedOperationException(); 387 } 388 389 public String getPeerHost() { 390 return host; 391 } 392 393 public int getPeerPort() { 394 return 443; 395 } 396 397 public Principal getPeerPrincipal() { 398 throw new UnsupportedOperationException(); 399 } 400 401 public String getProtocol() { 402 throw new UnsupportedOperationException(); 403 } 404 405 public SSLSessionContext getSessionContext() { 406 throw new UnsupportedOperationException(); 407 } 408 409 public Object getValue(String name) { 410 throw new UnsupportedOperationException(); 411 } 412 413 public String[] getValueNames() { 414 throw new UnsupportedOperationException(); 415 } 416 417 public void invalidate() { 418 throw new UnsupportedOperationException(); 419 } 420 421 public boolean isValid() { 422 throw new UnsupportedOperationException(); 423 } 424 425 public void putValue(String name, Object value) { 426 throw new UnsupportedOperationException(); 427 } 428 429 public void removeValue(String name) { 430 throw new UnsupportedOperationException(); 431 } 432} 433