blob: 4b4fbaf42c03d79787b22ca37786881092ba643a [file] [log] [blame]
// Copyright (c) 2012 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.
#ifndef NET_TEST_SPAWNER_COMMUNICATOR_H_
#define NET_TEST_SPAWNER_COMMUNICATOR_H_
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "base/threading/thread.h"
#include "net/url_request/url_request.h"
namespace net {
class ScopedPortException;
// SpawnerCommunicator communicates with a spawner server that runs on a
// remote system.
//
// The test server used by unit tests is written in Python. However, Android
// does not support running Python code, so the test server cannot run on the
// same device running unit tests.
//
// The actual test server is executed on the host machine, while the unit tests
// themselves continue running on the device. To control the test server on the
// host machine, a second HTTP server is started, the spawner server, which
// controls the life cycle of remote test servers. Calls to start/kill the
// net::TestServer are then redirected to the spawner server via this spawner
// communicator.
//
// Currently only three commands are supported by spawner.
//
// (1) Start Python test server, format is:
// Path: "/start".
// Method: "POST".
// Data to server: all arguments needed to launch the Python test server, in
// JSON format.
// Data from server: a JSON dict includes the following two field if success,
// "port": the port the Python test server actually listen on that.
// "message": must be "started".
//
// (2) Kill Python test server, format is:
// Path: "/kill".
// Method: "GET".
// Data to server: None.
// Data from server: String "killed" returned if success.
//
// (3) Ping Python test server to see whether it is alive, format is:
// Path: "/ping".
// Method: "GET".
// Data to server: None.
// Data from server: String "ready" returned if success.
//
// The internal I/O thread is required by net stack to perform net I/O.
// The Start/StopServer methods block the caller thread until result is
// fetched from spawner server or timed-out.
class SpawnerCommunicator : public net::URLRequest::Delegate {
public:
explicit SpawnerCommunicator(uint16 port);
virtual ~SpawnerCommunicator();
// Starts an instance of the Python test server on the host/ machine.
// If successfully started, returns true, setting |*port| to the port
// on the local machine that can be used to communicate with the remote
// test server.
bool StartServer(const std::string& arguments,
uint16* port) WARN_UNUSED_RESULT;
bool StopServer() WARN_UNUSED_RESULT;
private:
// Starts the IO thread. Called on the user thread.
void StartIOThread();
// Shuts down the remote test server spawner. Called on the user thread.
void Shutdown();
// Waits for the server response on IO thread. Called on the user thread.
void WaitForResponse();
// Sends a command to the test server over HTTP, returning the result code
// |*result_code| and response data in |*data_received|, those two arguments
// must be not NULL, otherwise the method returns immediately without sending
// the |command|. If |post_data| is empty, HTTP GET will be used to send
// |command|. If |post_data| is non-empty, performs an HTTP POST.
// This method is called on the user thread.
void SendCommandAndWaitForResult(const std::string& command,
const std::string& post_data,
int* result_code,
std::string* data_received);
// Performs the command sending on the IO thread. Called on the IO thread.
void SendCommandAndWaitForResultOnIOThread(const std::string& command,
const std::string& post_data,
int* result_code,
std::string* data_received);
// URLRequest::Delegate methods. Called on the IO thread.
virtual void OnResponseStarted(URLRequest* request) OVERRIDE;
virtual void OnReadCompleted(URLRequest* request, int num_bytes) OVERRIDE;
// Reads Result from the response. Called on the IO thread.
void ReadResult(URLRequest* request);
// Called on the IO thread upon completion of the spawner command.
void OnSpawnerCommandCompleted(URLRequest* request);
// Callback on the IO thread for time-out task of request with id |id|.
void OnTimeout(int id);
// A thread to communicate with test_spawner server.
base::Thread io_thread_;
// WaitableEvent to notify whether the communication is done.
base::WaitableEvent event_;
// The local port used to communicate with the TestServer spawner. This is
// used to control the startup and shutdown of the Python TestServer running
// on the remote machine. On Android, this port will be redirected to the
// same port on the host machine.
const uint16 port_;
// Helper to add |port_| to the list of the globally explicitly allowed ports.
scoped_ptr<ScopedPortException> allowed_port_;
// The next ID to use for |cur_request_| (monotonically increasing).
int next_id_;
// Factory for creating the time-out task. This takes care of revoking
// outstanding tasks when |this| is deleted.
base::WeakPtrFactory<SpawnerCommunicator> weak_factory_;
// Request context used by |cur_request_|.
scoped_ptr<URLRequestContext> context_;
// The current (in progress) request, or NULL.
scoped_ptr<URLRequest> cur_request_;
// Only gets/sets |is_running_| on user's thread to avoid race-condition.
bool is_running_;
DISALLOW_COPY_AND_ASSIGN(SpawnerCommunicator);
};
} // namespace net
#endif // NET_TEST_SPAWNER_COMMUNICATOR_H_