summaryrefslogtreecommitdiffstats
path: root/net/test/spawned_test_server/spawner_communicator.h
blob: ee1f0577dd11b87cb9ea8e11cdcbeeb76feba191 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 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_SPAWNED_TEST_SERVER_SPAWNER_COMMUNICATOR_H_
#define NET_TEST_SPAWNED_TEST_SERVER_SPAWNER_COMMUNICATOR_H_

#include <stdint.h>

#include <string>

#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
// SpawnedTestServer 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 URLRequest::Delegate {
 public:
  explicit SpawnerCommunicator(uint16_t port);
  ~SpawnerCommunicator() override;

  // 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_t* 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.
  void OnResponseStarted(URLRequest* request) override;
  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_t 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_;

  // 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_;

  // Factory for creating the time-out task. This takes care of revoking
  // outstanding tasks when |this| is deleted.
  base::WeakPtrFactory<SpawnerCommunicator> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(SpawnerCommunicator);
};

}  // namespace net

#endif  // NET_TEST_SPAWNED_TEST_SERVER_SPAWNER_COMMUNICATOR_H_