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/spawned_test_server/spawner_communicator.h"
7 #include "base/json/json_reader.h"
8 #include "base/logging.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/supports_user_data.h"
11 #include "base/test/test_timeouts.h"
12 #include "base/time/time.h"
13 #include "base/values.h"
14 #include "build/build_config.h"
15 #include "net/base/elements_upload_data_stream.h"
16 #include "net/base/net_util.h"
17 #include "net/base/request_priority.h"
18 #include "net/base/upload_bytes_element_reader.h"
19 #include "net/http/http_response_headers.h"
20 #include "net/url_request/url_request_test_util.h"
27 GURL
GenerateSpawnerCommandURL(const std::string
& command
, uint16 port
) {
28 // Always performs HTTP request for sending command to the spawner server.
29 return GURL(base::StringPrintf("%s:%u/%s", "http://127.0.0.1", port
,
33 int kBufferSize
= 2048;
35 // A class to hold all data needed to send a command to spawner server.
36 class SpawnerRequestData
: public base::SupportsUserData::Data
{
38 SpawnerRequestData(int id
, int* result_code
, std::string
* data_received
)
40 buf_(new IOBuffer(kBufferSize
)),
41 result_code_(result_code
),
42 data_received_(data_received
),
43 response_started_count_(0) {
46 DCHECK(data_received
);
47 data_received_
->clear();
50 virtual ~SpawnerRequestData() {}
52 bool DoesRequestIdMatch(int request_id
) const {
53 return request_id_
== request_id
;
56 IOBuffer
* buf() const { return buf_
.get(); }
58 bool IsResultOK() const { return *result_code_
== OK
; }
60 void ClearReceivedData() { data_received_
->clear(); }
62 void SetResultCode(int result_code
) { *result_code_
= result_code
; }
64 void IncreaseResponseStartedCount() { response_started_count_
++; }
66 int response_started_count() const { return response_started_count_
; }
68 // Write data read from URLRequest::Read() to |data_received_|. Returns true
69 // if |num_bytes| is great than 0. |num_bytes| is 0 for EOF, < 0 on errors.
70 bool ConsumeBytesRead(int num_bytes
) {
71 // Error while reading, or EOF.
75 data_received_
->append(buf_
->data(), num_bytes
);
80 // Unique ID for the current request.
83 // Buffer that URLRequest writes into.
84 scoped_refptr
<IOBuffer
> buf_
;
86 // Holds the error condition that was hit on the current request, or OK.
89 // Data received from server;
90 std::string
* data_received_
;
92 // Used to track how many times the OnResponseStarted get called after
93 // sending a command to spawner server.
94 int response_started_count_
;
96 DISALLOW_COPY_AND_ASSIGN(SpawnerRequestData
);
101 SpawnerCommunicator::SpawnerCommunicator(uint16 port
)
102 : io_thread_("spawner_communicator"),
103 event_(false, false),
107 weak_factory_(this) {}
109 SpawnerCommunicator::~SpawnerCommunicator() {
110 DCHECK(!is_running_
);
113 void SpawnerCommunicator::WaitForResponse() {
114 DCHECK_NE(base::MessageLoop::current(), io_thread_
.message_loop());
119 void SpawnerCommunicator::StartIOThread() {
120 DCHECK_NE(base::MessageLoop::current(), io_thread_
.message_loop());
124 allowed_port_
.reset(new ScopedPortException(port_
));
125 base::Thread::Options options
;
126 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
127 is_running_
= io_thread_
.StartWithOptions(options
);
131 void SpawnerCommunicator::Shutdown() {
132 DCHECK_NE(base::MessageLoop::current(), io_thread_
.message_loop());
134 // The request and its context should be created and destroyed only on the
136 DCHECK(!cur_request_
.get());
137 DCHECK(!context_
.get());
140 allowed_port_
.reset();
143 void SpawnerCommunicator::SendCommandAndWaitForResult(
144 const std::string
& command
,
145 const std::string
& post_data
,
147 std::string
* data_received
) {
148 if (!result_code
|| !data_received
)
150 // Start the communicator thread to talk to test server spawner.
152 DCHECK(io_thread_
.message_loop());
154 // Since the method will be blocked until SpawnerCommunicator gets result
155 // from the spawner server or timed-out. It's safe to use base::Unretained
156 // when using base::Bind.
157 io_thread_
.message_loop()->PostTask(FROM_HERE
, base::Bind(
158 &SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread
,
159 base::Unretained(this), command
, post_data
, result_code
, data_received
));
163 void SpawnerCommunicator::SendCommandAndWaitForResultOnIOThread(
164 const std::string
& command
,
165 const std::string
& post_data
,
167 std::string
* data_received
) {
168 base::MessageLoop
* loop
= io_thread_
.message_loop();
170 DCHECK_EQ(base::MessageLoop::current(), loop
);
172 // Prepare the URLRequest for sending the command.
173 DCHECK(!cur_request_
.get());
174 context_
.reset(new TestURLRequestContext
);
175 cur_request_
= context_
->CreateRequest(
176 GenerateSpawnerCommandURL(command
, port_
), DEFAULT_PRIORITY
, this, NULL
);
177 DCHECK(cur_request_
);
178 int current_request_id
= ++next_id_
;
179 SpawnerRequestData
* data
= new SpawnerRequestData(current_request_id
,
183 cur_request_
->SetUserData(this, data
);
185 if (post_data
.empty()) {
186 cur_request_
->set_method("GET");
188 cur_request_
->set_method("POST");
189 scoped_ptr
<UploadElementReader
> reader(
190 UploadOwnedBytesElementReader::CreateWithString(post_data
));
191 cur_request_
->set_upload(
192 ElementsUploadDataStream::CreateWithReader(reader
.Pass(), 0));
193 HttpRequestHeaders headers
;
194 headers
.SetHeader(HttpRequestHeaders::kContentType
,
196 cur_request_
->SetExtraRequestHeaders(headers
);
199 // Post a task to timeout this request if it takes too long.
200 base::MessageLoop::current()->PostDelayedTask(
202 base::Bind(&SpawnerCommunicator::OnTimeout
,
203 weak_factory_
.GetWeakPtr(),
205 TestTimeouts::action_max_timeout());
207 // Start the request.
208 cur_request_
->Start();
211 void SpawnerCommunicator::OnTimeout(int id
) {
212 // Timeout tasks may outlive the URLRequest they reference. Make sure it
213 // is still applicable.
214 if (!cur_request_
.get())
216 SpawnerRequestData
* data
=
217 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
220 if (!data
->DoesRequestIdMatch(id
))
222 // Set the result code and cancel the timed-out task.
223 data
->SetResultCode(ERR_TIMED_OUT
);
224 cur_request_
->Cancel();
225 OnSpawnerCommandCompleted(cur_request_
.get());
228 void SpawnerCommunicator::OnSpawnerCommandCompleted(URLRequest
* request
) {
229 if (!cur_request_
.get())
231 DCHECK_EQ(request
, cur_request_
.get());
232 SpawnerRequestData
* data
=
233 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
236 // If request is faild,return the error code.
237 if (!cur_request_
->status().is_success())
238 data
->SetResultCode(cur_request_
->status().error());
240 if (!data
->IsResultOK()) {
241 LOG(ERROR
) << "request failed, status: "
242 << static_cast<int>(request
->status().status())
243 << ", error: " << request
->status().error();
244 // Clear the buffer of received data if any net error happened.
245 data
->ClearReceivedData();
247 DCHECK_EQ(1, data
->response_started_count());
250 // Clear current request to indicate the completion of sending a command
251 // to spawner server and getting the result.
252 cur_request_
.reset();
254 // Invalidate the weak pointers on the IO thread.
255 weak_factory_
.InvalidateWeakPtrs();
257 // Wakeup the caller in user thread.
261 void SpawnerCommunicator::ReadResult(URLRequest
* request
) {
262 DCHECK_EQ(request
, cur_request_
.get());
263 SpawnerRequestData
* data
=
264 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
267 IOBuffer
* buf
= data
->buf();
268 // Read as many bytes as are available synchronously.
271 if (!request
->Read(buf
, kBufferSize
, &num_bytes
)) {
272 // Check whether the read failed synchronously.
273 if (!request
->status().is_io_pending())
274 OnSpawnerCommandCompleted(request
);
277 if (!data
->ConsumeBytesRead(num_bytes
)) {
278 OnSpawnerCommandCompleted(request
);
284 void SpawnerCommunicator::OnResponseStarted(URLRequest
* request
) {
285 DCHECK_EQ(request
, cur_request_
.get());
286 SpawnerRequestData
* data
=
287 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
290 data
->IncreaseResponseStartedCount();
292 if (!request
->status().is_success()) {
293 OnSpawnerCommandCompleted(request
);
297 // Require HTTP responses to have a success status code.
298 if (request
->GetResponseCode() != 200) {
299 LOG(ERROR
) << "Spawner server returned bad status: "
300 << request
->response_headers()->GetStatusLine();
301 data
->SetResultCode(ERR_FAILED
);
303 OnSpawnerCommandCompleted(request
);
310 void SpawnerCommunicator::OnReadCompleted(URLRequest
* request
, int num_bytes
) {
311 if (!cur_request_
.get())
313 DCHECK_EQ(request
, cur_request_
.get());
314 SpawnerRequestData
* data
=
315 static_cast<SpawnerRequestData
*>(cur_request_
->GetUserData(this));
318 if (data
->ConsumeBytesRead(num_bytes
)) {
322 OnSpawnerCommandCompleted(request
);
326 bool SpawnerCommunicator::StartServer(const std::string
& arguments
,
329 // Send the start command to spawner server to start the Python test server
330 // on remote machine.
331 std::string server_return_data
;
333 SendCommandAndWaitForResult("start", arguments
, &result_code
,
334 &server_return_data
);
335 if (OK
!= result_code
|| server_return_data
.empty())
338 // Check whether the data returned from spawner server is JSON-formatted.
339 scoped_ptr
<base::Value
> value(base::JSONReader::Read(server_return_data
));
340 if (!value
.get() || !value
->IsType(base::Value::TYPE_DICTIONARY
)) {
341 LOG(ERROR
) << "Invalid server data: " << server_return_data
.c_str();
345 // Check whether spawner server returns valid data.
346 base::DictionaryValue
* server_data
=
347 static_cast<base::DictionaryValue
*>(value
.get());
349 if (!server_data
->GetString("message", &message
) || message
!= "started") {
350 LOG(ERROR
) << "Invalid message in server data: ";
354 if (!server_data
->GetInteger("port", &int_port
) || int_port
<= 0 ||
355 int_port
> kuint16max
) {
356 LOG(ERROR
) << "Invalid port value: " << int_port
;
359 *port
= static_cast<uint16
>(int_port
);
363 bool SpawnerCommunicator::StopServer() {
364 // It's OK to stop the SpawnerCommunicator without starting it. Some tests
365 // have test server on their test fixture but do not actually use it.
369 // When the test is done, ask the test server spawner to kill the test server
370 // on the remote machine.
371 std::string server_return_data
;
373 SendCommandAndWaitForResult("kill", "", &result_code
, &server_return_data
);
375 if (OK
!= result_code
|| server_return_data
!= "killed")