1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/test/spawned_test_server/local_test_server.h"
6
7 #include <poll.h>
8
9 #include <vector>
10
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/path_service.h"
16 #include "base/process/kill.h"
17 #include "base/process/launch.h"
18 #include "base/process/process_iterator.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "net/test/python_utils.h"
22
23 namespace {
24
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 {
31 public:
OrphanedTestServerFilter(const std::string & path_string,const std::string & port_string)32 OrphanedTestServerFilter(
33 const std::string& path_string, const std::string& port_string)
34 : path_string_(path_string),
35 port_string_(port_string) {}
36
37 OrphanedTestServerFilter(const OrphanedTestServerFilter&) = delete;
38 OrphanedTestServerFilter& operator=(const OrphanedTestServerFilter&) = delete;
39
Includes(const base::ProcessEntry & entry) const40 bool Includes(const base::ProcessEntry& entry) const override {
41 if (entry.parent_pid() != 1)
42 return false;
43 bool found_path_string = false;
44 bool found_port_string = false;
45 for (const auto& cmd_line_arg : entry.cmd_line_args()) {
46 if (cmd_line_arg.find(path_string_) != std::string::npos)
47 found_path_string = true;
48 if (cmd_line_arg.find(port_string_) != std::string::npos)
49 found_port_string = true;
50 }
51 return found_path_string && found_port_string;
52 }
53
54 private:
55 std::string path_string_;
56 std::string port_string_;
57 };
58
59 // Given a file descriptor, reads into |buffer| until |bytes_max|
60 // bytes has been read or an error has been encountered. Returns true
61 // if the read was successful.
ReadData(int fd,ssize_t bytes_max,uint8_t * buffer)62 bool ReadData(int fd, ssize_t bytes_max, uint8_t* buffer) {
63 ssize_t bytes_read = 0;
64 while (bytes_read < bytes_max) {
65 struct pollfd poll_fds[1];
66
67 poll_fds[0].fd = fd;
68 poll_fds[0].events = POLLIN | POLLPRI;
69 poll_fds[0].revents = 0;
70
71 // Each test itself has its own timeout, so no need to use one here.
72 int rv = HANDLE_EINTR(poll(poll_fds, 1, -1));
73 if (rv == 0) {
74 LOG(ERROR) << "poll() timed out; bytes_read=" << bytes_read;
75 return false;
76 } else if (rv < 0) {
77 PLOG(ERROR) << "poll() failed for child file descriptor; bytes_read="
78 << bytes_read;
79 return false;
80 }
81
82 ssize_t num_bytes = HANDLE_EINTR(read(fd, buffer + bytes_read,
83 bytes_max - bytes_read));
84 if (num_bytes <= 0)
85 return false;
86 bytes_read += num_bytes;
87 }
88 return true;
89 }
90
91 } // namespace
92
93 namespace net {
94
LaunchPython(const base::FilePath & testserver_path,const std::vector<base::FilePath> & python_path)95 bool LocalTestServer::LaunchPython(
96 const base::FilePath& testserver_path,
97 const std::vector<base::FilePath>& python_path) {
98 base::CommandLine python_command(base::CommandLine::NO_PROGRAM);
99 if (!GetPython3Command(&python_command))
100 return false;
101
102 python_command.AppendArgPath(testserver_path);
103 if (!AddCommandLineArguments(&python_command))
104 return false;
105
106 int pipefd[2];
107 if (pipe(pipefd) != 0) {
108 PLOG(ERROR) << "Could not create pipe.";
109 return false;
110 }
111
112 // Save the read half. The write half is sent to the child.
113 child_fd_.reset(pipefd[0]);
114 base::ScopedFD write_closer(pipefd[1]);
115
116 python_command.AppendArg("--startup-pipe=" + base::NumberToString(pipefd[1]));
117
118 // Try to kill any orphaned testserver processes that may be running.
119 OrphanedTestServerFilter filter(testserver_path.value(),
120 base::NumberToString(GetPort()));
121 if (!base::KillProcesses("python", -1, &filter)) {
122 LOG(WARNING) << "Failed to clean up older orphaned testserver instances.";
123 }
124
125 // Launch a new testserver process.
126 base::LaunchOptions options;
127 SetPythonPathInEnvironment(python_path, &options.environment);
128
129 // Log is useful in the event you want to run a nearby script (e.g. a test) in
130 // the same environment as the TestServer.
131 LOG(ERROR) << "LaunchPython called with PYTHONPATH = "
132 << options.environment["PYTHONPATH"];
133
134 // Set CWD to source root.
135 if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT,
136 &options.current_directory)) {
137 LOG(ERROR) << "Failed to get DIR_SRC_TEST_DATA_ROOT";
138 return false;
139 }
140
141 options.fds_to_remap.emplace_back(pipefd[1], pipefd[1]);
142 LOG(ERROR) << "Running: " << python_command.GetCommandLineString();
143 process_ = base::LaunchProcess(python_command, options);
144 if (!process_.IsValid()) {
145 LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString();
146 return false;
147 }
148
149 return true;
150 }
151
WaitToStart()152 bool LocalTestServer::WaitToStart() {
153 base::ScopedFD our_fd(child_fd_.release());
154
155 uint32_t server_data_len = 0;
156 if (!ReadData(our_fd.get(), sizeof(server_data_len),
157 reinterpret_cast<uint8_t*>(&server_data_len))) {
158 LOG(ERROR) << "Could not read server_data_len";
159 return false;
160 }
161 std::string server_data(server_data_len, '\0');
162 if (!ReadData(our_fd.get(), server_data_len,
163 reinterpret_cast<uint8_t*>(&server_data[0]))) {
164 LOG(ERROR) << "Could not read server_data (" << server_data_len
165 << " bytes)";
166 return false;
167 }
168
169 int port;
170 if (!SetAndParseServerData(server_data, &port)) {
171 LOG(ERROR) << "Could not parse server_data: " << server_data;
172 return false;
173 }
174 SetPort(port);
175
176 return true;
177 }
178
179 } // namespace net
180