1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/test/spawner_communicator.h"
7 #include "base/json/json_reader.h"
8 #include "base/logging.h"
9 #include "base/stringprintf.h"
10 #include "base/supports_user_data.h"
11 #include "base/test/test_timeouts.h"
12 #include "base/time.h"
13 #include "base/values.h"
14 #include "build/build_config.h"
15 #include "googleurl/src/gurl.h"
16 #include "net/base/net_util.h"
17 #include "net/base/upload_bytes_element_reader.h"
18 #include "net/base/upload_data_stream.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/url_request/url_request_test_util.h"
26 GURL
GenerateSpawnerCommandURL(const std::string
& command
, uint16 port
) {
27 // Always performs HTTP request for sending command to the spawner server.
28 return GURL(base::StringPrintf("%s:%u/%s", "http://127.0.0.1", port
,
32 int kBufferSize
= 2048;
34 // A class to hold all data needed to send a command to spawner server.
35 class SpawnerRequestData
: public base::SupportsUserData::Data
{
37 SpawnerRequestData(int id
, int* result_code
, std::string
* data_received
)
39 buf_(new IOBuffer(kBufferSize
)),
40 result_code_(result_code
),
41 data_received_(data_received
),
42 response_started_count_(0) {
45 DCHECK(data_received
);
46 data_received_
->clear();
49 virtual ~SpawnerRequestData() {}
51 bool DoesRequestIdMatch(int request_id
) const {
52 return request_id_
== request_id
;
55 IOBuffer
* buf() const { return buf_
.get(); }
57 bool IsResultOK() const { return *result_code_
== OK
; }
59 void ClearReceivedData() { data_received_
->clear(); }
61 void SetResultCode(int result_code
) { *result_code_
= result_code
; }
63 void IncreaseResponseStartedCount() { response_started_count_
++; }
65 int response_started_count() const { return response_started_count_
; }
67 // Write data read from URLRequest::Read() to |data_received_|. Returns true
68 // if |num_bytes| is great than 0. |num_bytes| is 0 for EOF, < 0 on errors.
69 bool ConsumeBytesRead(int num_bytes
) {
70 // Error while reading, or EOF.
74 data_received_
->append(buf_
->data(), num_bytes
);
79 // Unique ID for the current request.
82 // Buffer that URLRequest writes into.
83 scoped_refptr
<IOBuffer
> buf_
;
85 // Holds the error condition that was hit on the current request, or OK.
88 // Data received from server;
89 std::string
* data_received_
;
91 // Used to track how many times the OnResponseStarted get called after
92 // sending a command to spawner server.
93 int response_started_count_
;
95 DISALLOW_COPY_AND_ASSIGN(SpawnerRequestData
);
100 SpawnerCommunicator::SpawnerCommunicator(uint16 port
)
101 : io_thread_("spawner_communicator"),
102 event_(false, false),
105 ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
106 is_running_(false) {}
108 SpawnerCommunicator::~SpawnerCommunicator() {
109 DCHECK(!is_running_
);
112 void SpawnerCommunicator::WaitForResponse() {
113 DCHECK_NE(MessageLoop::current(), io_thread_
.message_loop());
118 void SpawnerCommunicator::StartIOThread() {
119 DCHECK_NE(MessageLoop::current(), io_thread_
.message_loop());
123 allowed_port_
.reset(new ScopedPortException(port_
));
124 base::Thread::Options options
;
125 options
.message_loop_type
= MessageLoop::TYPE_IO
;
126 is_running_
= io_thread_
.StartWithOptions(options
);
130 void SpawnerCommunicator::Shutdown() {
131 DCHECK_NE(MessageLoop::current(), io_thread_
.message_loop());
133 // The request and its context should be created and destroyed only on the
135 DCHECK(!cur_request_
.get());
136 DCHECK(!context_
.get());
139 allowed_port_
.reset();
142 void SpawnerCommunicator::SendCommandAndWaitForResult(
143 const std::string
& command
,
144 const std::string
& post_data
,
146 std::string
* data_received
) {
147 if (!result_code
|| !data_received
)
149 // Start the communicator thread to talk to test server spawner.
151 DCHECK(io_thread_
.message_loop());
153 // Since the method will be blocked until SpawnerCommunicator gets result
154 // from the spawner server or timed-out. It's safe to use base::Unretained
155 // when using base::Bind.
156 io_thread_
.message_loop()->PostTask(FROM_HERE
, base::Bind(
157 &SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread
,
158 base::Unretained(this), command
, post_data
, result_code
, data_received
));
162 void SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread(
163 const std::string
& command
,
164 const std::string
& post_data
,
166 std::string
* data_received
) {
167 MessageLoop
* loop
= io_thread_
.message_loop();
169 DCHECK_EQ(MessageLoop::current(), loop
);
171 // Prepare the URLRequest for sending the command.
172 DCHECK(!cur_request_
.get());
173 context_
.reset(new TestURLRequestContext
);
174 cur_request_
.reset(context_
->CreateRequest(
175 GenerateSpawnerCommandURL(command
, port_
), this));
176 DCHECK(cur_request_
.get());
177 int current_request_id
= ++next_id_
;
178 SpawnerRequestData
* data
= new SpawnerRequestData(current_request_id
,
182 cur_request_
->SetUserData(this, data
);
184 if (post_data
.empty()) {
185 cur_request_
->set_method("GET");
187 cur_request_
->set_method("POST");
188 scoped_ptr
<UploadElementReader
> reader(
189 UploadOwnedBytesElementReader::CreateWithString(post_data
));
190 cur_request_
->set_upload(make_scoped_ptr(
191 UploadDataStream::CreateWithReader(reader
.Pass(), 0)));
192 net::HttpRequestHeaders headers
;
193 headers
.SetHeader(net::HttpRequestHeaders::kContentType
,
195 cur_request_
->SetExtraRequestHeaders(headers
);
198 // Post a task to timeout this request if it takes too long.
199 MessageLoop::current()->PostDelayedTask(
201 base::Bind(&SpawnerCommunicator::OnTimeout
, weak_factory_
.GetWeakPtr(),
203 TestTimeouts::action_max_timeout());
205 // Start the request.
206 cur_request_
->Start();
209 void SpawnerCommunicator::OnTimeout(int id
) {
210 // Timeout tasks may outlive the URLRequest they reference. Make sure it
211 // is still applicable.
212 if (!cur_request_
.get())
214 SpawnerRequestData
* data
=
215 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
218 if (!data
->DoesRequestIdMatch(id
))
220 // Set the result code and cancel the timed-out task.
221 data
->SetResultCode(ERR_TIMED_OUT
);
222 cur_request_
->Cancel();
223 OnSpawnerCommandCompleted(cur_request_
.get());
226 void SpawnerCommunicator::OnSpawnerCommandCompleted(URLRequest
* request
) {
227 if (!cur_request_
.get())
229 DCHECK_EQ(request
, cur_request_
.get());
230 SpawnerRequestData
* data
=
231 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
234 // If request is faild,return the error code.
235 if (!cur_request_
->status().is_success())
236 data
->SetResultCode(cur_request_
->status().error());
238 if (!data
->IsResultOK()) {
239 LOG(ERROR
) << "request failed, status: "
240 << static_cast<int>(request
->status().status())
241 << ", error: " << request
->status().error();
242 // Clear the buffer of received data if any net error happened.
243 data
->ClearReceivedData();
245 DCHECK_EQ(1, data
->response_started_count());
248 // Clear current request to indicate the completion of sending a command
249 // to spawner server and getting the result.
250 cur_request_
.reset();
252 // Invalidate the weak pointers on the IO thread.
253 weak_factory_
.InvalidateWeakPtrs();
255 // Wakeup the caller in user thread.
259 void SpawnerCommunicator::ReadResult(URLRequest
* request
) {
260 DCHECK_EQ(request
, cur_request_
.get());
261 SpawnerRequestData
* data
=
262 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
265 IOBuffer
* buf
= data
->buf();
266 // Read as many bytes as are available synchronously.
269 if (!request
->Read(buf
, kBufferSize
, &num_bytes
)) {
270 // Check whether the read failed synchronously.
271 if (!request
->status().is_io_pending())
272 OnSpawnerCommandCompleted(request
);
275 if (!data
->ConsumeBytesRead(num_bytes
)) {
276 OnSpawnerCommandCompleted(request
);
282 void SpawnerCommunicator::OnResponseStarted(URLRequest
* request
) {
283 DCHECK_EQ(request
, cur_request_
.get());
284 SpawnerRequestData
* data
=
285 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
288 data
->IncreaseResponseStartedCount();
290 if (!request
->status().is_success()) {
291 OnSpawnerCommandCompleted(request
);
295 // Require HTTP responses to have a success status code.
296 if (request
->GetResponseCode() != 200) {
297 LOG(ERROR
) << "Spawner server returned bad status: "
298 << request
->response_headers()->GetStatusLine();
299 data
->SetResultCode(ERR_FAILED
);
301 OnSpawnerCommandCompleted(request
);
308 void SpawnerCommunicator::OnReadCompleted(URLRequest
* request
, int num_bytes
) {
309 if (!cur_request_
.get())
311 DCHECK_EQ(request
, cur_request_
.get());
312 SpawnerRequestData
* data
=
313 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
316 if (data
->ConsumeBytesRead(num_bytes
)) {
320 OnSpawnerCommandCompleted(request
);
324 bool SpawnerCommunicator::StartServer(const std::string
& arguments
,
327 // Send the start command to spawner server to start the Python test server
328 // on remote machine.
329 std::string server_return_data
;
331 SendCommandAndWaitForResult("start", arguments
, &result_code
,
332 &server_return_data
);
333 if (OK
!= result_code
|| server_return_data
.empty())
336 // Check whether the data returned from spawner server is JSON-formatted.
337 scoped_ptr
<base::Value
> value(base::JSONReader::Read(server_return_data
));
338 if (!value
.get() || !value
->IsType(base::Value::TYPE_DICTIONARY
)) {
339 LOG(ERROR
) << "Invalid server data: " << server_return_data
.c_str();
343 // Check whether spawner server returns valid data.
344 DictionaryValue
* server_data
= static_cast<DictionaryValue
*>(value
.get());
346 if (!server_data
->GetString("message", &message
) || message
!= "started") {
347 LOG(ERROR
) << "Invalid message in server data: ";
351 if (!server_data
->GetInteger("port", &int_port
) || int_port
<= 0 ||
352 int_port
> kuint16max
) {
353 LOG(ERROR
) << "Invalid port value: " << int_port
;
356 *port
= static_cast<uint16
>(int_port
);
360 bool SpawnerCommunicator::StopServer() {
361 // It's OK to stop the SpawnerCommunicator without starting it. Some tests
362 // have test server on their test fixture but do not actually use it.
366 // When the test is done, ask the test server spawner to kill the test server
367 // on the remote machine.
368 std::string server_return_data
;
370 SendCommandAndWaitForResult("kill", "", &result_code
, &server_return_data
);
372 if (OK
!= result_code
|| server_return_data
!= "killed")