1 // Copyright 2015 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 package org.chromium.net.test; 6 7 import android.content.Context; 8 import android.os.Build; 9 import android.os.Handler; 10 import android.os.HandlerThread; 11 import android.os.RemoteException; 12 13 import org.jni_zero.CalledByNative; 14 import org.jni_zero.JNINamespace; 15 import org.jni_zero.NativeMethods; 16 17 import org.chromium.base.ContextUtils; 18 import org.chromium.base.Log; 19 import org.chromium.base.library_loader.LibraryLoader; 20 import org.chromium.base.library_loader.LibraryProcessType; 21 import org.chromium.base.test.util.UrlUtils; 22 23 import java.util.concurrent.Callable; 24 import java.util.concurrent.ExecutionException; 25 import java.util.concurrent.FutureTask; 26 import java.util.concurrent.atomic.AtomicInteger; 27 28 /** 29 * Java bindings for running a net::test_server::EmbeddedTestServer. 30 * 31 * This should not be used directly. Use {@link EmbeddedTestServer} instead. 32 */ 33 @JNINamespace("net::test_server") 34 public class EmbeddedTestServerImpl extends IEmbeddedTestServerImpl.Stub { 35 private static final String TAG = "TestServer"; 36 37 private static AtomicInteger sCount = new AtomicInteger(); 38 39 private final Context mContext; 40 private Handler mHandler; 41 private HandlerThread mHandlerThread; 42 private long mNativeEmbeddedTestServer; 43 private IConnectionListener mConnectionListener; 44 45 /** Create an uninitialized EmbeddedTestServer. */ EmbeddedTestServerImpl(Context context)46 public EmbeddedTestServerImpl(Context context) { 47 mContext = context; 48 } 49 runOnHandlerThread(Callable<V> c)50 private <V> V runOnHandlerThread(Callable<V> c) { 51 FutureTask<V> t = new FutureTask<>(c); 52 mHandler.post(t); 53 try { 54 return t.get(); 55 } catch (ExecutionException e) { 56 Log.e(TAG, "Exception raised from native EmbeddedTestServer", e); 57 } catch (InterruptedException e) { 58 Log.e(TAG, "Interrupted while waiting for native EmbeddedTestServer", e); 59 } 60 return null; 61 } 62 63 /** Initialize the native EmbeddedTestServer object. 64 * 65 * @param https True if the server should use HTTPS, and false otherwise. 66 * @return Whether the native object was successfully initialized. 67 */ 68 @Override initializeNative(final boolean https)69 public boolean initializeNative(final boolean https) { 70 // This is necessary as EmbeddedTestServerImpl is in a different process than the tests 71 // using it, so it needs to initialize its own application context. 72 ContextUtils.initApplicationContext(mContext.getApplicationContext()); 73 LibraryLoader.getInstance().setLibraryProcessType(LibraryProcessType.PROCESS_BROWSER); 74 LibraryLoader.getInstance().ensureInitialized(); 75 76 mHandlerThread = new HandlerThread("EmbeddedTestServer" + sCount.getAndIncrement()); 77 mHandlerThread.start(); 78 mHandler = new Handler(mHandlerThread.getLooper()); 79 80 runOnHandlerThread( 81 new Callable<Void>() { 82 @Override 83 public Void call() { 84 if (mNativeEmbeddedTestServer == 0) { 85 EmbeddedTestServerImplJni.get() 86 .init( 87 EmbeddedTestServerImpl.this, 88 UrlUtils.getIsolatedTestRoot(), 89 https); 90 } 91 assert mNativeEmbeddedTestServer != 0; 92 return null; 93 } 94 }); 95 return true; 96 } 97 98 /** Starts the server. 99 * 100 * Note that this should be called after handlers are set up, including any relevant calls 101 * serveFilesFromDirectory. 102 * 103 * @param port The port to use for the server, 0 to auto-select an unused port. 104 * 105 * @return Whether the server was successfully started. 106 */ 107 @Override start(int port)108 public boolean start(int port) { 109 return runOnHandlerThread( 110 new Callable<Boolean>() { 111 @Override 112 public Boolean call() { 113 return EmbeddedTestServerImplJni.get() 114 .start(mNativeEmbeddedTestServer, port); 115 } 116 }); 117 } 118 119 /** Returns the path to a PEM file containing the server's root certificate. 120 * 121 * @return The path to a PEM file containing the server's root certificate. 122 */ 123 @Override 124 public String getRootCertPemPath() { 125 return runOnHandlerThread( 126 new Callable<String>() { 127 @Override 128 public String call() { 129 return EmbeddedTestServerImplJni.get() 130 .getRootCertPemPath(mNativeEmbeddedTestServer); 131 } 132 }); 133 } 134 135 /** Add the default handlers and serve files from the provided directory relative to the 136 * external storage directory. 137 * 138 * @param directoryPath The path of the directory from which files should be served, relative 139 * to the external storage directory. 140 */ 141 @Override 142 public void addDefaultHandlers(final String directoryPath) { 143 runOnHandlerThread( 144 new Callable<Void>() { 145 @Override 146 public Void call() { 147 EmbeddedTestServerImplJni.get() 148 .addDefaultHandlers(mNativeEmbeddedTestServer, directoryPath); 149 return null; 150 } 151 }); 152 } 153 154 /** Configure the server to use a particular type of SSL certificate. 155 * 156 * @param serverCertificate The type of certificate the server should use. 157 */ 158 @Override 159 public void setSSLConfig(final int serverCertificate) { 160 runOnHandlerThread( 161 new Callable<Void>() { 162 @Override 163 public Void call() { 164 EmbeddedTestServerImplJni.get() 165 .setSSLConfig(mNativeEmbeddedTestServer, serverCertificate); 166 return null; 167 } 168 }); 169 } 170 171 /** Register multiple request handlers. 172 * Handlers must be registered before starting the server. 173 * 174 * @param handler The pointer of handler to be registered. 175 */ 176 public void registerRequestHandler(final long handler) { 177 runOnHandlerThread( 178 new Callable<Void>() { 179 @Override 180 public Void call() { 181 EmbeddedTestServerImplJni.get() 182 .registerRequestHandler(mNativeEmbeddedTestServer, handler); 183 return null; 184 } 185 }); 186 } 187 188 /** Serve files from the provided directory. 189 * 190 * @param directoryPath The path of the directory from which files should be served. 191 */ 192 @Override 193 public void serveFilesFromDirectory(final String directoryPath) { 194 runOnHandlerThread( 195 new Callable<Void>() { 196 @Override 197 public Void call() { 198 EmbeddedTestServerImplJni.get() 199 .serveFilesFromDirectory(mNativeEmbeddedTestServer, directoryPath); 200 return null; 201 } 202 }); 203 } 204 205 /** Sets a connection listener to be notified of new connections and socket reads. 206 * 207 * Must be done before starting the server. Setting a new one erases the previous one. 208 * 209 * @param listener Listener to notify. 210 */ 211 @Override 212 public void setConnectionListener(final IConnectionListener listener) { 213 runOnHandlerThread( 214 new Callable<Void>() { 215 @Override 216 public Void call() { 217 mConnectionListener = listener; 218 return null; 219 } 220 }); 221 } 222 223 /** Get the full URL for the given relative URL. 224 * 225 * @param relativeUrl The relative URL for which a full URL should be returned. 226 * @return The URL as a String. 227 */ 228 @Override 229 public String getURL(final String relativeUrl) { 230 return runOnHandlerThread( 231 new Callable<String>() { 232 @Override 233 public String call() { 234 return EmbeddedTestServerImplJni.get() 235 .getURL(mNativeEmbeddedTestServer, relativeUrl); 236 } 237 }); 238 } 239 240 /** Get the full URL for the given relative URL. Similar to the above method but uses the given 241 * hostname instead of 127.0.0.1. The hostname should be resolved to 127.0.0.1. 242 * 243 * @param hostName The host name which should be used. 244 * @param relativeUrl The relative URL for which a full URL should be returned. 245 * @return The URL as a String. 246 */ 247 @Override 248 public String getURLWithHostName(final String hostName, final String relativeUrl) { 249 return runOnHandlerThread( 250 new Callable<String>() { 251 @Override 252 public String call() { 253 return EmbeddedTestServerImplJni.get() 254 .getURLWithHostName( 255 mNativeEmbeddedTestServer, hostName, relativeUrl); 256 } 257 }); 258 } 259 260 /** Shut down the server. 261 * 262 * @return Whether the server was successfully shut down. 263 */ 264 @Override 265 public boolean shutdownAndWaitUntilComplete() { 266 return runOnHandlerThread( 267 new Callable<Boolean>() { 268 @Override 269 public Boolean call() { 270 return EmbeddedTestServerImplJni.get() 271 .shutdownAndWaitUntilComplete(mNativeEmbeddedTestServer); 272 } 273 }); 274 } 275 276 /** Destroy the native EmbeddedTestServer object. */ 277 @Override 278 public void destroy() { 279 runOnHandlerThread( 280 new Callable<Void>() { 281 @Override 282 public Void call() { 283 assert mNativeEmbeddedTestServer != 0; 284 EmbeddedTestServerImplJni.get().destroy(mNativeEmbeddedTestServer); 285 assert mNativeEmbeddedTestServer == 0; 286 return null; 287 } 288 }); 289 290 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { 291 mHandlerThread.quitSafely(); 292 } else { 293 runOnHandlerThread( 294 new Callable<Void>() { 295 @Override 296 public Void call() { 297 mHandlerThread.quit(); 298 return null; 299 } 300 }); 301 } 302 303 try { 304 mHandlerThread.join(); 305 } catch (InterruptedException e) { 306 } 307 } 308 309 @CalledByNative 310 private void acceptedSocket(long socketId) { 311 if (mConnectionListener == null) return; 312 try { 313 mConnectionListener.acceptedSocket(socketId); 314 } catch (RemoteException e) { 315 // Callback, ignore exception. 316 } 317 } 318 319 @CalledByNative 320 private void readFromSocket(long socketId) { 321 if (mConnectionListener == null) return; 322 try { 323 mConnectionListener.readFromSocket(socketId); 324 } catch (RemoteException e) { 325 // Callback, ignore exception. 326 } 327 } 328 329 @CalledByNative 330 private void setNativePtr(long nativePtr) { 331 assert mNativeEmbeddedTestServer == 0; 332 mNativeEmbeddedTestServer = nativePtr; 333 } 334 335 @CalledByNative 336 private void clearNativePtr() { 337 assert mNativeEmbeddedTestServer != 0; 338 mNativeEmbeddedTestServer = 0; 339 } 340 341 @NativeMethods 342 interface Natives { 343 void init(EmbeddedTestServerImpl obj, String testDataDir, boolean https); 344 345 void destroy(long nativeEmbeddedTestServerAndroid); 346 347 boolean start(long nativeEmbeddedTestServerAndroid, int port); 348 349 String getRootCertPemPath(long nativeEmbeddedTestServerAndroid); 350 351 boolean shutdownAndWaitUntilComplete(long nativeEmbeddedTestServerAndroid); 352 353 void addDefaultHandlers(long nativeEmbeddedTestServerAndroid, String directoryPath); 354 355 void setSSLConfig(long nativeEmbeddedTestServerAndroid, int serverCertificate); 356 357 void registerRequestHandler(long nativeEmbeddedTestServerAndroid, long handler); 358 359 String getURL(long nativeEmbeddedTestServerAndroid, String relativeUrl); 360 361 String getURLWithHostName( 362 long nativeEmbeddedTestServerAndroid, String hostName, String relativeUrl); 363 364 void serveFilesFromDirectory(long nativeEmbeddedTestServerAndroid, String directoryPath); 365 } 366 } 367