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/local_test_server.h"
11 #include "base/command_line.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_file.h"
14 #include "base/logging.h"
15 #include "base/process/kill.h"
16 #include "base/process/launch.h"
17 #include "base/process/process_iterator.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/test/test_timeouts.h"
21 #include "net/test/python_utils.h"
25 // Helper class used to detect and kill orphaned python test server processes.
26 // Checks if the command line of a process contains |path_string| (the path
27 // from which the test server was launched) and |port_string| (the port used by
28 // the test server), and if the parent pid of the process is 1 (indicating that
29 // it is an orphaned process).
30 class OrphanedTestServerFilter
: public base::ProcessFilter
{
32 OrphanedTestServerFilter(
33 const std::string
& path_string
, const std::string
& port_string
)
34 : path_string_(path_string
),
35 port_string_(port_string
) {}
37 bool Includes(const base::ProcessEntry
& entry
) const override
{
38 if (entry
.parent_pid() != 1)
40 bool found_path_string
= false;
41 bool found_port_string
= false;
42 for (std::vector
<std::string
>::const_iterator it
=
43 entry
.cmd_line_args().begin();
44 it
!= entry
.cmd_line_args().end();
46 if (it
->find(path_string_
) != std::string::npos
)
47 found_path_string
= true;
48 if (it
->find(port_string_
) != std::string::npos
)
49 found_port_string
= true;
51 return found_path_string
&& found_port_string
;
55 std::string path_string_
;
56 std::string port_string_
;
57 DISALLOW_COPY_AND_ASSIGN(OrphanedTestServerFilter
);
60 // Given a file descriptor, reads into |buffer| until |bytes_max|
61 // bytes has been read or an error has been encountered. Returns true
62 // if the read was successful. |remaining_time| is used as a timeout.
63 bool ReadData(int fd
, ssize_t bytes_max
, uint8
* buffer
,
64 base::TimeDelta
* remaining_time
) {
65 ssize_t bytes_read
= 0;
66 base::TimeTicks previous_time
= base::TimeTicks::Now();
67 while (bytes_read
< bytes_max
) {
68 struct pollfd poll_fds
[1];
71 poll_fds
[0].events
= POLLIN
| POLLPRI
;
72 poll_fds
[0].revents
= 0;
74 int rv
= HANDLE_EINTR(poll(poll_fds
, 1,
75 remaining_time
->InMilliseconds()));
77 LOG(ERROR
) << "poll() timed out; bytes_read=" << bytes_read
;
80 PLOG(ERROR
) << "poll() failed for child file descriptor; bytes_read="
85 base::TimeTicks current_time
= base::TimeTicks::Now();
86 base::TimeDelta elapsed_time_cycle
= current_time
- previous_time
;
87 DCHECK_GE(elapsed_time_cycle
.InMilliseconds(), 0);
88 *remaining_time
-= elapsed_time_cycle
;
89 previous_time
= current_time
;
91 ssize_t num_bytes
= HANDLE_EINTR(read(fd
, buffer
+ bytes_read
,
92 bytes_max
- bytes_read
));
95 bytes_read
+= num_bytes
;
104 bool LocalTestServer::LaunchPython(const base::FilePath
& testserver_path
) {
105 // Log is useful in the event you want to run a nearby script (e.g. a test) in
106 // the same environment as the TestServer.
107 VLOG(1) << "LaunchPython called with PYTHONPATH = " << getenv(kPythonPathEnv
);
109 base::CommandLine
python_command(base::CommandLine::NO_PROGRAM
);
110 if (!GetPythonCommand(&python_command
))
113 python_command
.AppendArgPath(testserver_path
);
114 if (!AddCommandLineArguments(&python_command
))
118 if (pipe(pipefd
) != 0) {
119 PLOG(ERROR
) << "Could not create pipe.";
123 // Save the read half. The write half is sent to the child.
124 child_fd_
.reset(pipefd
[0]);
125 base::ScopedFD
write_closer(pipefd
[1]);
126 base::FileHandleMappingVector map_write_fd
;
127 map_write_fd
.push_back(std::make_pair(pipefd
[1], pipefd
[1]));
129 python_command
.AppendArg("--startup-pipe=" + base::IntToString(pipefd
[1]));
131 // Try to kill any orphaned testserver processes that may be running.
132 OrphanedTestServerFilter
filter(testserver_path
.value(),
133 base::UintToString(GetPort()));
134 if (!base::KillProcesses("python", -1, &filter
)) {
135 LOG(WARNING
) << "Failed to clean up older orphaned testserver instances.";
138 // Launch a new testserver process.
139 base::LaunchOptions options
;
141 options
.fds_to_remap
= &map_write_fd
;
142 process_
= base::LaunchProcess(python_command
, options
);
143 if (!process_
.IsValid()) {
144 LOG(ERROR
) << "Failed to launch " << python_command
.GetCommandLineString();
151 bool LocalTestServer::WaitToStart() {
152 base::ScopedFD
our_fd(child_fd_
.release());
154 base::TimeDelta remaining_time
= TestTimeouts::action_timeout();
156 uint32 server_data_len
= 0;
157 if (!ReadData(our_fd
.get(), sizeof(server_data_len
),
158 reinterpret_cast<uint8
*>(&server_data_len
),
160 LOG(ERROR
) << "Could not read server_data_len";
163 std::string
server_data(server_data_len
, '\0');
164 if (!ReadData(our_fd
.get(), server_data_len
,
165 reinterpret_cast<uint8
*>(&server_data
[0]),
167 LOG(ERROR
) << "Could not read server_data (" << server_data_len
172 if (!ParseServerData(server_data
)) {
173 LOG(ERROR
) << "Could not parse server_data: " << server_data
;