| Index: net/test/android/javatests/src/org/chromium/net/test/TestServerSpawner.java
|
| diff --git a/net/test/android/javatests/src/org/chromium/net/test/TestServerSpawner.java b/net/test/android/javatests/src/org/chromium/net/test/TestServerSpawner.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..67fa5629076248ee17843b0412a873e1f0a18c47
|
| --- /dev/null
|
| +++ b/net/test/android/javatests/src/org/chromium/net/test/TestServerSpawner.java
|
| @@ -0,0 +1,195 @@
|
| +// 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.util.Log;
|
| +
|
| +import org.apache.http.HttpEntity;
|
| +import org.apache.http.HttpEntityEnclosingRequest;
|
| +import org.apache.http.HttpException;
|
| +import org.apache.http.HttpRequest;
|
| +import org.apache.http.HttpResponse;
|
| +import org.apache.http.HttpStatus;
|
| +import org.apache.http.HttpVersion;
|
| +import org.apache.http.RequestLine;
|
| +import org.apache.http.entity.StringEntity;
|
| +import org.apache.http.message.BasicHttpResponse;
|
| +
|
| +import org.json.JSONException;
|
| +import org.json.JSONObject;
|
| +
|
| +import java.io.BufferedInputStream;
|
| +import java.io.BufferedReader;
|
| +import java.io.IOException;
|
| +import java.io.InputStreamReader;
|
| +import java.io.UnsupportedEncodingException;
|
| +import java.net.Socket;
|
| +
|
| +/**
|
| + * A server that spawns test servers based on request JSONs.
|
| + *
|
| + * This communicates with net::SpawnerCommunicator according to the protocol specified in
|
| + * //net/test/spawned_test_server/spawner_communicator.h. It spawns test servers on the device.
|
| + */
|
| +public class TestServerSpawner extends BaseHttpTestServer {
|
| + private static final String TAG = "TestServerSpawner";
|
| +
|
| + private static final int ARBITRARY_MAX_JSON_SIZE = 65536;
|
| + private static final String COMMAND_KILL = "/kill";
|
| + private static final String COMMAND_PING = "/ping";
|
| + private static final String COMMAND_START = "/start";
|
| +
|
| + private BaseTestServer mTestServer;
|
| + private Thread mTestServerThread;
|
| +
|
| + /**
|
| + * Creates a test server spawner on the given port.
|
| + *
|
| + * @param serverPort The port to listen on for incoming connections.
|
| + * @param acceptTimeoutMs The timeout for calls to ServerSocket.accept(), in milliseconds.
|
| + * @throws IOException If the server port can't be bound.
|
| + */
|
| + public TestServerSpawner(int serverPort, int acceptTimeoutMs) throws IOException {
|
| + super(serverPort, acceptTimeoutMs);
|
| + mTestServer = null;
|
| + mTestServerThread = null;
|
| + }
|
| +
|
| + /**
|
| + * Returns true if the socket is coming from a local address.
|
| + *
|
| + * @param sock The socket to validate.
|
| + * @return True if the remote endpoint is local, false otherwise.
|
| + */
|
| + @Override
|
| + protected boolean validateSocket(Socket sock) {
|
| + return sock.getInetAddress().isLoopbackAddress();
|
| + }
|
| +
|
| + /**
|
| + * Handles a GET request.
|
| + *
|
| + * This handles the /kill and /ping commands. It returns 403s for anything else.
|
| + *
|
| + * @param request The GET request to handle.
|
| + * @return The response to |request|.
|
| + */
|
| + @Override
|
| + protected HttpResponse handleGet(HttpRequest request) {
|
| + RequestLine requestLine = request.getRequestLine();
|
| + String uri = requestLine.getUri();
|
| +
|
| + int status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
| + String reason = "";
|
| + HttpEntity entity = null;
|
| +
|
| + try {
|
| + // TODO(jbudorick): Refactor how this communicates with RemoteTestServer and
|
| + // SpawnerCommunicator (or maybe how we spawn and manage test servers on android
|
| + // entirely) once tests are no longer using chrome_test_server_spawner.py.
|
| + // The server startup and shutdown processes should be done in code (e.g. with events)
|
| + // rather than over HTTP. crbug/452596
|
| + if (COMMAND_KILL.equals(uri)) {
|
| + Log.i(TAG, "Received GET /kill request.");
|
| +
|
| + if (mTestServer != null) {
|
| + mTestServer.stop();
|
| + mTestServerThread.join();
|
| + status = HttpStatus.SC_OK;
|
| + entity = new StringEntity("killed");
|
| + mTestServer = null;
|
| + } else {
|
| + status = HttpStatus.SC_BAD_REQUEST;
|
| + reason = "Test server does not exist.";
|
| + }
|
| + } else if (COMMAND_PING.equals(uri)) {
|
| + Log.i(TAG, "Received GET /ping request.");
|
| + status = HttpStatus.SC_OK;
|
| + entity = new StringEntity("ready");
|
| + } else {
|
| + status = HttpStatus.SC_FORBIDDEN;
|
| + }
|
| + } catch (InterruptedException e) {
|
| + Log.e(TAG, "Interrupted while joining test server thread: " + e.toString());
|
| + } catch (UnsupportedEncodingException e) {
|
| + Log.e(TAG, "Unsupported encoding while writing response entity: " + e.toString());
|
| + entity = null;
|
| + }
|
| +
|
| + BasicHttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status, reason);
|
| + response.setEntity(entity);
|
| + return response;
|
| + }
|
| +
|
| + /**
|
| + * Handles a POST request.
|
| + *
|
| + * This handles the /start command. It returns 403s for anything else.
|
| + *
|
| + * @param request The POST request to handle.
|
| + * @return The response to |request|.
|
| + */
|
| + @Override
|
| + protected HttpResponse handlePost(HttpEntityEnclosingRequest request) throws HttpException {
|
| + RequestLine requestLine = request.getRequestLine();
|
| + String uri = requestLine.getUri();
|
| +
|
| + int status = HttpStatus.SC_INTERNAL_SERVER_ERROR;
|
| + String reason = "";
|
| + HttpEntity responseEntity = null;
|
| +
|
| + if (COMMAND_START.equals(uri)) {
|
| + Log.i(TAG, "Received POST /start request.");
|
| + BufferedReader entityReader = null;
|
| + try {
|
| + HttpEntity requestEntity = request.getEntity();
|
| + if (requestEntity.getContentLength() > ARBITRARY_MAX_JSON_SIZE) {
|
| + throw new HttpException("Request JSON too long ("
|
| + + Long.toString(requestEntity.getContentLength()) + " bytes)");
|
| + }
|
| +
|
| + entityReader = new BufferedReader(
|
| + new InputStreamReader(new BufferedInputStream(requestEntity.getContent())));
|
| + StringBuilder rawJson = new StringBuilder();
|
| + for (String line = entityReader.readLine(); line != null;
|
| + line = entityReader.readLine()) {
|
| + rawJson.append(line);
|
| + }
|
| +
|
| + mTestServer = new TestServerBuilder(new JSONObject(rawJson.toString())).build();
|
| + mTestServerThread = new Thread(mTestServer);
|
| + mTestServerThread.start();
|
| +
|
| + status = HttpStatus.SC_OK;
|
| + JSONObject entityJson = new JSONObject();
|
| + entityJson.put("port", mTestServer.getServerPort());
|
| + entityJson.put("message", "started");
|
| + responseEntity = new StringEntity(entityJson.toString());
|
| + } catch (UnsupportedOperationException e) {
|
| + // TODO(jbudorick): Remove this catch block once TestServerFactory.createTestServer
|
| + // is fully implemented.
|
| + throw new HttpException("Error creating test server", e);
|
| + } catch (JSONException e) {
|
| + throw new HttpException("Error handling JSON", e);
|
| + } catch (UnsupportedEncodingException e) {
|
| + throw new HttpException("Error generating response", e);
|
| + } catch (IOException e) {
|
| + throw new HttpException("Error while reading HTTP entity", e);
|
| + } finally {
|
| + try {
|
| + if (entityReader != null) entityReader.close();
|
| + } catch (IOException e) {
|
| + Log.e(TAG, "Unable to close entity input stream", e);
|
| + }
|
| + }
|
| + } else {
|
| + status = HttpStatus.SC_FORBIDDEN;
|
| + }
|
| +
|
| + BasicHttpResponse response = new BasicHttpResponse(HttpVersion.HTTP_1_0, status, reason);
|
| + response.setEntity(responseEntity);
|
| + return response;
|
| + }
|
| +}
|
|
|