• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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