blob: 07932f93e8827c584aebc8bd19de1893e1a4273a [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.net.test;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.RemoteException;
import org.chromium.base.ContextUtils;
import org.chromium.base.Log;
import org.chromium.base.annotations.CalledByNative;
import org.chromium.base.annotations.JNINamespace;
import org.chromium.base.library_loader.LibraryLoader;
import org.chromium.base.library_loader.LibraryProcessType;
import org.chromium.base.library_loader.ProcessInitException;
import org.chromium.base.test.util.UrlUtils;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Java bindings for running a net::test_server::EmbeddedTestServer.
*
* This should not be used directly. Use {@link EmbeddedTestServer} instead.
*/
@JNINamespace("net::test_server")
public class EmbeddedTestServerImpl extends IEmbeddedTestServerImpl.Stub {
private static final String TAG = "cr_TestServer";
private static AtomicInteger sCount = new AtomicInteger();
private final Context mContext;
private Handler mHandler;
private HandlerThread mHandlerThread;
private long mNativeEmbeddedTestServer;
private IConnectionListener mConnectionListener;
/** Create an uninitialized EmbeddedTestServer. */
public EmbeddedTestServerImpl(Context context) {
mContext = context;
}
private <V> V runOnHandlerThread(Callable<V> c) {
FutureTask<V> t = new FutureTask<>(c);
mHandler.post(t);
try {
return t.get();
} catch (ExecutionException e) {
Log.e(TAG, "Exception raised from native EmbeddedTestServer", e);
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted while waiting for native EmbeddedTestServer", e);
}
return null;
}
/** Initialize the native EmbeddedTestServer object.
*
* @param https True if the server should use HTTPS, and false otherwise.
* @return Whether the native object was successfully initialized.
*/
@Override
public boolean initializeNative(final boolean https) {
// This is necessary as EmbeddedTestServerImpl is in a different process than the tests
// using it, so it needs to initialize its own application context.
ContextUtils.initApplicationContext(mContext.getApplicationContext());
try {
LibraryLoader.getInstance().ensureInitialized(LibraryProcessType.PROCESS_BROWSER);
} catch (ProcessInitException e) {
Log.e(TAG, "Failed to load native libraries.", e);
return false;
}
mHandlerThread = new HandlerThread("EmbeddedTestServer" + sCount.getAndIncrement());
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
runOnHandlerThread(new Callable<Void>() {
@Override
public Void call() {
if (mNativeEmbeddedTestServer == 0) {
nativeInit(UrlUtils.getIsolatedTestRoot(), https);
}
assert mNativeEmbeddedTestServer != 0;
return null;
}
});
return true;
}
/** Starts the server.
*
* Note that this should be called after handlers are set up, including any relevant calls
* serveFilesFromDirectory.
*
* @param port The port to use for the server, 0 to auto-select an unused port.
*
* @return Whether the server was successfully started.
*/
@Override
public boolean start(int port) {
return runOnHandlerThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return nativeStart(mNativeEmbeddedTestServer, port);
}
});
}
/** Returns the path to a PEM file containing the server's root certificate.
*
* @return The path to a PEM file containing the server's root certificate.
*/
@Override
public String getRootCertPemPath() {
return runOnHandlerThread(new Callable<String>() {
@Override
public String call() {
return nativeGetRootCertPemPath(mNativeEmbeddedTestServer);
}
});
}
/** Add the default handlers and serve files from the provided directory relative to the
* external storage directory.
*
* @param directoryPath The path of the directory from which files should be served, relative
* to the external storage directory.
*/
@Override
public void addDefaultHandlers(final String directoryPath) {
runOnHandlerThread(new Callable<Void>() {
@Override
public Void call() {
nativeAddDefaultHandlers(mNativeEmbeddedTestServer, directoryPath);
return null;
}
});
}
/** Configure the server to use a particular type of SSL certificate.
*
* @param serverCertificate The type of certificate the server should use.
*/
@Override
public void setSSLConfig(final int serverCertificate) {
runOnHandlerThread(new Callable<Void>() {
@Override
public Void call() {
nativeSetSSLConfig(mNativeEmbeddedTestServer, serverCertificate);
return null;
}
});
}
/** Register multiple request handlers.
* Handlers must be registered before starting the server.
*
* @param handler The pointer of handler to be registered.
*/
public void registerRequestHandler(final long handler) {
runOnHandlerThread(new Callable<Void>() {
@Override
public Void call() {
nativeRegisterRequestHandler(mNativeEmbeddedTestServer, handler);
return null;
}
});
}
/** Serve files from the provided directory.
*
* @param directoryPath The path of the directory from which files should be served.
*/
@Override
public void serveFilesFromDirectory(final String directoryPath) {
runOnHandlerThread(new Callable<Void>() {
@Override
public Void call() {
nativeServeFilesFromDirectory(mNativeEmbeddedTestServer, directoryPath);
return null;
}
});
}
/** Sets a connection listener to be notified of new connections and socket reads.
*
* Must be done before starting the server. Setting a new one erases the previous one.
*
* @param listener Listener to notify.
*/
@Override
public void setConnectionListener(final IConnectionListener listener) {
runOnHandlerThread(new Callable<Void>() {
@Override
public Void call() {
mConnectionListener = listener;
return null;
}
});
}
/** Get the full URL for the given relative URL.
*
* @param relativeUrl The relative URL for which a full URL should be returned.
* @return The URL as a String.
*/
@Override
public String getURL(final String relativeUrl) {
return runOnHandlerThread(new Callable<String>() {
@Override
public String call() {
return nativeGetURL(mNativeEmbeddedTestServer, relativeUrl);
}
});
}
/** Get the full URL for the given relative URL. Similar to the above method but uses the given
* hostname instead of 127.0.0.1. The hostname should be resolved to 127.0.0.1.
*
* @param hostName The host name which should be used.
* @param relativeUrl The relative URL for which a full URL should be returned.
* @return The URL as a String.
*/
@Override
public String getURLWithHostName(final String hostName, final String relativeUrl) {
return runOnHandlerThread(new Callable<String>() {
@Override
public String call() {
return nativeGetURLWithHostName(mNativeEmbeddedTestServer, hostName, relativeUrl);
}
});
}
/** Shut down the server.
*
* @return Whether the server was successfully shut down.
*/
@Override
public boolean shutdownAndWaitUntilComplete() {
return runOnHandlerThread(new Callable<Boolean>() {
@Override
public Boolean call() {
return nativeShutdownAndWaitUntilComplete(mNativeEmbeddedTestServer);
}
});
}
/** Destroy the native EmbeddedTestServer object. */
@Override
public void destroy() {
runOnHandlerThread(new Callable<Void>() {
@Override
public Void call() {
assert mNativeEmbeddedTestServer != 0;
nativeDestroy(mNativeEmbeddedTestServer);
assert mNativeEmbeddedTestServer == 0;
return null;
}
});
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
mHandlerThread.quitSafely();
} else {
runOnHandlerThread(new Callable<Void>() {
@Override
public Void call() {
mHandlerThread.quit();
return null;
}
});
}
try {
mHandlerThread.join();
} catch (InterruptedException e) {
}
}
@CalledByNative
private void acceptedSocket(long socketId) {
if (mConnectionListener == null) return;
try {
mConnectionListener.acceptedSocket(socketId);
} catch (RemoteException e) {
// Callback, ignore exception.
}
}
@CalledByNative
private void readFromSocket(long socketId) {
if (mConnectionListener == null) return;
try {
mConnectionListener.readFromSocket(socketId);
} catch (RemoteException e) {
// Callback, ignore exception.
}
}
@CalledByNative
private void setNativePtr(long nativePtr) {
assert mNativeEmbeddedTestServer == 0;
mNativeEmbeddedTestServer = nativePtr;
}
@CalledByNative
private void clearNativePtr() {
assert mNativeEmbeddedTestServer != 0;
mNativeEmbeddedTestServer = 0;
}
private native void nativeInit(String testDataDir, boolean https);
private native void nativeDestroy(long nativeEmbeddedTestServerAndroid);
private native boolean nativeStart(long nativeEmbeddedTestServerAndroid, int port);
private native String nativeGetRootCertPemPath(long nativeEmbeddedTestServerAndroid);
private native boolean nativeShutdownAndWaitUntilComplete(long nativeEmbeddedTestServerAndroid);
private native void nativeAddDefaultHandlers(
long nativeEmbeddedTestServerAndroid, String directoryPath);
private native void nativeSetSSLConfig(
long nativeEmbeddedTestServerAndroid, int serverCertificate);
private native void nativeRegisterRequestHandler(
long nativeEmbeddedTestServerAndroid, long handler);
private native String nativeGetURL(long nativeEmbeddedTestServerAndroid, String relativeUrl);
private native String nativeGetURLWithHostName(
long nativeEmbeddedTestServerAndroid, String hostName, String relativeUrl);
private native void nativeServeFilesFromDirectory(
long nativeEmbeddedTestServerAndroid, String directoryPath);
}