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/local_test_server.h"
11 #include "base/command_line.h"
12 #include "base/file_util.h"
13 #include "base/logging.h"
14 #include "base/process_util.h"
15 #include "base/string_number_conversions.h"
16 #include "base/string_util.h"
17 #include "base/test/test_timeouts.h"
18 #include "net/test/python_utils.h"
22 // Helper class used to detect and kill orphaned python test server processes.
23 // Checks if the command line of a process contains |path_string| (the path
24 // from which the test server was launched) and |port_string| (the port used by
25 // the test server), and if the parent pid of the process is 1 (indicating that
26 // it is an orphaned process).
27 class OrphanedTestServerFilter
: public base::ProcessFilter
{
29 OrphanedTestServerFilter(
30 const std::string
& path_string
, const std::string
& port_string
)
31 : path_string_(path_string
),
32 port_string_(port_string
) {}
34 virtual bool Includes(const base::ProcessEntry
& entry
) const OVERRIDE
{
35 if (entry
.parent_pid() != 1)
37 bool found_path_string
= false;
38 bool found_port_string
= false;
39 for (std::vector
<std::string
>::const_iterator it
=
40 entry
.cmd_line_args().begin();
41 it
!= entry
.cmd_line_args().end();
43 if (it
->find(path_string_
) != std::string::npos
)
44 found_path_string
= true;
45 if (it
->find(port_string_
) != std::string::npos
)
46 found_port_string
= true;
48 return found_path_string
&& found_port_string
;
52 std::string path_string_
;
53 std::string port_string_
;
54 DISALLOW_COPY_AND_ASSIGN(OrphanedTestServerFilter
);
57 // Given a file descriptor, reads into |buffer| until |bytes_max|
58 // bytes has been read or an error has been encountered. Returns true
59 // if the read was successful. |remaining_time| is used as a timeout.
60 bool ReadData(int fd
, ssize_t bytes_max
, uint8
* buffer
,
61 base::TimeDelta
* remaining_time
) {
62 ssize_t bytes_read
= 0;
63 base::Time previous_time
= base::Time::Now();
64 while (bytes_read
< bytes_max
) {
65 struct pollfd poll_fds
[1];
68 poll_fds
[0].events
= POLLIN
| POLLPRI
;
69 poll_fds
[0].revents
= 0;
71 int rv
= HANDLE_EINTR(poll(poll_fds
, 1,
72 remaining_time
->InMilliseconds()));
74 LOG(ERROR
) << "poll() timed out; bytes_read=" << bytes_read
;
77 PLOG(ERROR
) << "poll() failed for child file descriptor; bytes_read="
82 base::Time current_time
= base::Time::Now();
83 base::TimeDelta elapsed_time_cycle
= current_time
- previous_time
;
84 DCHECK_GE(elapsed_time_cycle
.InMilliseconds(), 0);
85 *remaining_time
-= elapsed_time_cycle
;
86 previous_time
= current_time
;
88 ssize_t num_bytes
= HANDLE_EINTR(read(fd
, buffer
+ bytes_read
,
89 bytes_max
- bytes_read
));
92 bytes_read
+= num_bytes
;
101 bool LocalTestServer::LaunchPython(const FilePath
& testserver_path
) {
102 // Log is useful in the event you want to run a nearby script (e.g. a test) in
103 // the same environment as the TestServer.
104 VLOG(1) << "LaunchPython called with PYTHONPATH = " << getenv(kPythonPathEnv
);
106 CommandLine
python_command(CommandLine::NO_PROGRAM
);
107 if (!GetPythonCommand(&python_command
))
110 python_command
.AppendArgPath(testserver_path
);
111 if (!AddCommandLineArguments(&python_command
))
115 if (pipe(pipefd
) != 0) {
116 PLOG(ERROR
) << "Could not create pipe.";
120 // Save the read half. The write half is sent to the child.
121 child_fd_
= pipefd
[0];
122 child_fd_closer_
.reset(&child_fd_
);
123 file_util::ScopedFD
write_closer(&pipefd
[1]);
124 base::FileHandleMappingVector map_write_fd
;
125 map_write_fd
.push_back(std::make_pair(pipefd
[1], pipefd
[1]));
127 python_command
.AppendArg("--startup-pipe=" + base::IntToString(pipefd
[1]));
129 // Try to kill any orphaned testserver processes that may be running.
130 OrphanedTestServerFilter
filter(testserver_path
.value(),
131 base::IntToString(GetPort()));
132 if (!base::KillProcesses("python", -1, &filter
)) {
133 LOG(WARNING
) << "Failed to clean up older orphaned testserver instances.";
136 // Launch a new testserver process.
137 base::LaunchOptions options
;
138 options
.fds_to_remap
= &map_write_fd
;
139 if (!base::LaunchProcess(python_command
, options
, &process_handle_
)) {
140 LOG(ERROR
) << "Failed to launch " << python_command
.GetCommandLineString();
147 bool LocalTestServer::WaitToStart() {
148 file_util::ScopedFD
child_fd_closer(child_fd_closer_
.release());
150 base::TimeDelta remaining_time
= TestTimeouts::action_timeout();
152 uint32 server_data_len
= 0;
153 if (!ReadData(child_fd_
, sizeof(server_data_len
),
154 reinterpret_cast<uint8
*>(&server_data_len
),
156 LOG(ERROR
) << "Could not read server_data_len";
159 std::string
server_data(server_data_len
, '\0');
160 if (!ReadData(child_fd_
, server_data_len
,
161 reinterpret_cast<uint8
*>(&server_data
[0]),
163 LOG(ERROR
) << "Could not read server_data (" << server_data_len
168 if (!ParseServerData(server_data
)) {
169 LOG(ERROR
) << "Could not parse server_data: " << server_data
;