xref: /aosp_15_r20/external/cronet/net/test/spawned_test_server/local_test_server_posix.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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