OLD | NEW |
| (Empty) |
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. | |
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/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" | |
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: | |
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 bool Includes(const base::ProcessEntry& entry) const override { | |
38 if (entry.parent_pid() != 1) | |
39 return false; | |
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(); | |
45 ++it) { | |
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; | |
50 } | |
51 return found_path_string && found_port_string; | |
52 } | |
53 | |
54 private: | |
55 std::string path_string_; | |
56 std::string port_string_; | |
57 DISALLOW_COPY_AND_ASSIGN(OrphanedTestServerFilter); | |
58 }; | |
59 | |
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]; | |
69 | |
70 poll_fds[0].fd = fd; | |
71 poll_fds[0].events = POLLIN | POLLPRI; | |
72 poll_fds[0].revents = 0; | |
73 | |
74 int rv = HANDLE_EINTR(poll(poll_fds, 1, | |
75 remaining_time->InMilliseconds())); | |
76 if (rv == 0) { | |
77 LOG(ERROR) << "poll() timed out; bytes_read=" << bytes_read; | |
78 return false; | |
79 } else if (rv < 0) { | |
80 PLOG(ERROR) << "poll() failed for child file descriptor; bytes_read=" | |
81 << bytes_read; | |
82 return false; | |
83 } | |
84 | |
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; | |
90 | |
91 ssize_t num_bytes = HANDLE_EINTR(read(fd, buffer + bytes_read, | |
92 bytes_max - bytes_read)); | |
93 if (num_bytes <= 0) | |
94 return false; | |
95 bytes_read += num_bytes; | |
96 } | |
97 return true; | |
98 } | |
99 | |
100 } // namespace | |
101 | |
102 namespace net { | |
103 | |
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); | |
108 | |
109 base::CommandLine python_command(base::CommandLine::NO_PROGRAM); | |
110 if (!GetPythonCommand(&python_command)) | |
111 return false; | |
112 | |
113 python_command.AppendArgPath(testserver_path); | |
114 if (!AddCommandLineArguments(&python_command)) | |
115 return false; | |
116 | |
117 int pipefd[2]; | |
118 if (pipe(pipefd) != 0) { | |
119 PLOG(ERROR) << "Could not create pipe."; | |
120 return false; | |
121 } | |
122 | |
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])); | |
128 | |
129 python_command.AppendArg("--startup-pipe=" + base::IntToString(pipefd[1])); | |
130 | |
131 // Try to kill any orphaned testserver processes that may be running. | |
132 OrphanedTestServerFilter filter(testserver_path.value(), | |
133 base::IntToString(GetPort())); | |
134 if (!base::KillProcesses("python", -1, &filter)) { | |
135 LOG(WARNING) << "Failed to clean up older orphaned testserver instances."; | |
136 } | |
137 | |
138 // Launch a new testserver process. | |
139 base::LaunchOptions options; | |
140 | |
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(); | |
145 return false; | |
146 } | |
147 | |
148 return true; | |
149 } | |
150 | |
151 bool LocalTestServer::WaitToStart() { | |
152 base::ScopedFD our_fd(child_fd_.release()); | |
153 | |
154 base::TimeDelta remaining_time = TestTimeouts::action_timeout(); | |
155 | |
156 uint32 server_data_len = 0; | |
157 if (!ReadData(our_fd.get(), sizeof(server_data_len), | |
158 reinterpret_cast<uint8*>(&server_data_len), | |
159 &remaining_time)) { | |
160 LOG(ERROR) << "Could not read server_data_len"; | |
161 return false; | |
162 } | |
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]), | |
166 &remaining_time)) { | |
167 LOG(ERROR) << "Could not read server_data (" << server_data_len | |
168 << " bytes)"; | |
169 return false; | |
170 } | |
171 | |
172 if (!ParseServerData(server_data)) { | |
173 LOG(ERROR) << "Could not parse server_data: " << server_data; | |
174 return false; | |
175 } | |
176 | |
177 return true; | |
178 } | |
179 | |
180 } // namespace net | |
OLD | NEW |