text: // 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_
  #pragma once
  
  #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.
    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_;
  
    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_
  