| 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/local_test_server.h" | |
| 6 | |
| 7 #include <poll.h> | |
| 8 | |
| 9 #include <vector> | |
| 10 | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/file_util.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/process_util.h" | |
| 15 #include "base/string_number_conversions.h" | |
| 16 #include "base/string_util.h" | |
| 17 #include "base/test/test_timeouts.h" | |
| 18 #include "net/test/python_utils.h" | |
| 19 | |
| 20 namespace { | |
| 21 | |
| 22 // Helper class used to detect and kill orphaned python test server processes. | |
| 23 // Checks if the command line of a process contains |path_string| (the path | |
| 24 // from which the test server was launched) and |port_string| (the port used by | |
| 25 // the test server), and if the parent pid of the process is 1 (indicating that | |
| 26 // it is an orphaned process). | |
| 27 class OrphanedTestServerFilter : public base::ProcessFilter { | |
| 28 public: | |
| 29 OrphanedTestServerFilter( | |
| 30 const std::string& path_string, const std::string& port_string) | |
| 31 : path_string_(path_string), | |
| 32 port_string_(port_string) {} | |
| 33 | |
| 34 virtual bool Includes(const base::ProcessEntry& entry) const OVERRIDE { | |
| 35 if (entry.parent_pid() != 1) | |
| 36 return false; | |
| 37 bool found_path_string = false; | |
| 38 bool found_port_string = false; | |
| 39 for (std::vector<std::string>::const_iterator it = | |
| 40 entry.cmd_line_args().begin(); | |
| 41 it != entry.cmd_line_args().end(); | |
| 42 ++it) { | |
| 43 if (it->find(path_string_) != std::string::npos) | |
| 44 found_path_string = true; | |
| 45 if (it->find(port_string_) != std::string::npos) | |
| 46 found_port_string = true; | |
| 47 } | |
| 48 return found_path_string && found_port_string; | |
| 49 } | |
| 50 | |
| 51 private: | |
| 52 std::string path_string_; | |
| 53 std::string port_string_; | |
| 54 DISALLOW_COPY_AND_ASSIGN(OrphanedTestServerFilter); | |
| 55 }; | |
| 56 | |
| 57 // Given a file descriptor, reads into |buffer| until |bytes_max| | |
| 58 // bytes has been read or an error has been encountered. Returns true | |
| 59 // if the read was successful. |remaining_time| is used as a timeout. | |
| 60 bool ReadData(int fd, ssize_t bytes_max, uint8* buffer, | |
| 61 base::TimeDelta* remaining_time) { | |
| 62 ssize_t bytes_read = 0; | |
| 63 base::TimeTicks previous_time = base::TimeTicks::Now(); | |
| 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 int rv = HANDLE_EINTR(poll(poll_fds, 1, | |
| 72 remaining_time->InMilliseconds())); | |
| 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 base::TimeTicks current_time = base::TimeTicks::Now(); | |
| 83 base::TimeDelta elapsed_time_cycle = current_time - previous_time; | |
| 84 DCHECK_GE(elapsed_time_cycle.InMilliseconds(), 0); | |
| 85 *remaining_time -= elapsed_time_cycle; | |
| 86 previous_time = current_time; | |
| 87 | |
| 88 ssize_t num_bytes = HANDLE_EINTR(read(fd, buffer + bytes_read, | |
| 89 bytes_max - bytes_read)); | |
| 90 if (num_bytes <= 0) | |
| 91 return false; | |
| 92 bytes_read += num_bytes; | |
| 93 } | |
| 94 return true; | |
| 95 } | |
| 96 | |
| 97 } // namespace | |
| 98 | |
| 99 namespace net { | |
| 100 | |
| 101 bool LocalTestServer::LaunchPython(const base::FilePath& testserver_path) { | |
| 102 // Log is useful in the event you want to run a nearby script (e.g. a test) in | |
| 103 // the same environment as the TestServer. | |
| 104 VLOG(1) << "LaunchPython called with PYTHONPATH = " << getenv(kPythonPathEnv); | |
| 105 | |
| 106 CommandLine python_command(CommandLine::NO_PROGRAM); | |
| 107 if (!GetPythonCommand(&python_command)) | |
| 108 return false; | |
| 109 | |
| 110 python_command.AppendArgPath(testserver_path); | |
| 111 if (!AddCommandLineArguments(&python_command)) | |
| 112 return false; | |
| 113 | |
| 114 int pipefd[2]; | |
| 115 if (pipe(pipefd) != 0) { | |
| 116 PLOG(ERROR) << "Could not create pipe."; | |
| 117 return false; | |
| 118 } | |
| 119 | |
| 120 // Save the read half. The write half is sent to the child. | |
| 121 child_fd_ = pipefd[0]; | |
| 122 child_fd_closer_.reset(&child_fd_); | |
| 123 file_util::ScopedFD write_closer(&pipefd[1]); | |
| 124 base::FileHandleMappingVector map_write_fd; | |
| 125 map_write_fd.push_back(std::make_pair(pipefd[1], pipefd[1])); | |
| 126 | |
| 127 python_command.AppendArg("--startup-pipe=" + base::IntToString(pipefd[1])); | |
| 128 | |
| 129 // Try to kill any orphaned testserver processes that may be running. | |
| 130 OrphanedTestServerFilter filter(testserver_path.value(), | |
| 131 base::IntToString(GetPort())); | |
| 132 if (!base::KillProcesses("python", -1, &filter)) { | |
| 133 LOG(WARNING) << "Failed to clean up older orphaned testserver instances."; | |
| 134 } | |
| 135 | |
| 136 // Launch a new testserver process. | |
| 137 base::LaunchOptions options; | |
| 138 | |
| 139 // TODO(phajdan.jr): Remove after fixing http://crbug.com/96594 . | |
| 140 options.debug = true; | |
| 141 | |
| 142 options.fds_to_remap = &map_write_fd; | |
| 143 if (!base::LaunchProcess(python_command, options, &process_handle_)) { | |
| 144 LOG(ERROR) << "Failed to launch " << python_command.GetCommandLineString(); | |
| 145 return false; | |
| 146 } | |
| 147 | |
| 148 return true; | |
| 149 } | |
| 150 | |
| 151 bool LocalTestServer::WaitToStart() { | |
| 152 file_util::ScopedFD child_fd_closer(child_fd_closer_.release()); | |
| 153 | |
| 154 base::TimeDelta remaining_time = TestTimeouts::action_timeout(); | |
| 155 | |
| 156 uint32 server_data_len = 0; | |
| 157 if (!ReadData(child_fd_, 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(child_fd_, 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 |