Fix crash after Launcher drag/drop (take 3 - no unit test)
[chromium-blink-merge.git] / net / test / spawned_test_server / local_test_server_posix.cc
blobc770152ea6bc44c407c444cd5eb9709d68763fa3
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"
7 #include <poll.h>
9 #include <vector>
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"
23 namespace {
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) {}
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;
51 return found_path_string && found_port_string;
54 private:
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];
70 poll_fds[0].fd = fd;
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()));
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;
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));
93 if (num_bytes <= 0)
94 return false;
95 bytes_read += num_bytes;
97 return true;
100 } // namespace
102 namespace net {
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))
111 return false;
113 python_command.AppendArgPath(testserver_path);
114 if (!AddCommandLineArguments(&python_command))
115 return false;
117 int pipefd[2];
118 if (pipe(pipefd) != 0) {
119 PLOG(ERROR) << "Could not create pipe.";
120 return false;
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::IntToString(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();
145 return false;
148 return true;
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),
159 &remaining_time)) {
160 LOG(ERROR) << "Could not read server_data_len";
161 return false;
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;
172 if (!ParseServerData(server_data)) {
173 LOG(ERROR) << "Could not parse server_data: " << server_data;
174 return false;
177 return true;
180 } // namespace net