1// Copyright (c) 2010 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/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 19namespace { 20 21// Helper class used to detect and kill orphaned python test server processes. 22// Checks if the command line of a process contains |path_string| (the path 23// from which the test server was launched) and |port_string| (the port used by 24// the test server), and if the parent pid of the process is 1 (indicating that 25// it is an orphaned process). 26class OrphanedTestServerFilter : public base::ProcessFilter { 27 public: 28 OrphanedTestServerFilter( 29 const std::string& path_string, const std::string& port_string) 30 : path_string_(path_string), 31 port_string_(port_string) {} 32 33 virtual bool Includes(const base::ProcessEntry& entry) const { 34 if (entry.parent_pid() != 1) 35 return false; 36 bool found_path_string = false; 37 bool found_port_string = false; 38 for (std::vector<std::string>::const_iterator it = 39 entry.cmd_line_args().begin(); 40 it != entry.cmd_line_args().end(); 41 ++it) { 42 if (it->find(path_string_) != std::string::npos) 43 found_path_string = true; 44 if (it->find(port_string_) != std::string::npos) 45 found_port_string = true; 46 } 47 return found_path_string && found_port_string; 48 } 49 50 private: 51 std::string path_string_; 52 std::string port_string_; 53 DISALLOW_COPY_AND_ASSIGN(OrphanedTestServerFilter); 54}; 55 56// Given a file descriptor, reads into |buffer| until |bytes_max| 57// bytes has been read or an error has been encountered. Returns true 58// if the read was successful. |remaining_time| is used as a timeout. 59bool ReadData(int fd, ssize_t bytes_max, uint8* buffer, 60 base::TimeDelta* remaining_time) { 61 ssize_t bytes_read = 0; 62 base::Time previous_time = base::Time::Now(); 63 while (bytes_read < bytes_max) { 64 struct pollfd poll_fds[1]; 65 66 poll_fds[0].fd = fd; 67 poll_fds[0].events = POLLIN | POLLPRI; 68 poll_fds[0].revents = 0; 69 70 int rv = HANDLE_EINTR(poll(poll_fds, 1, 71 remaining_time->InMilliseconds())); 72 if (rv != 1) { 73 LOG(ERROR) << "Failed to poll for the child file descriptor."; 74 return false; 75 } 76 77 base::Time current_time = base::Time::Now(); 78 base::TimeDelta elapsed_time_cycle = current_time - previous_time; 79 DCHECK(elapsed_time_cycle.InMilliseconds() >= 0); 80 *remaining_time -= elapsed_time_cycle; 81 previous_time = current_time; 82 83 ssize_t num_bytes = HANDLE_EINTR(read(fd, buffer + bytes_read, 84 bytes_max - bytes_read)); 85 if (num_bytes <= 0) 86 return false; 87 bytes_read += num_bytes; 88 } 89 return true; 90} 91 92} // namespace 93 94namespace net { 95 96bool TestServer::LaunchPython(const FilePath& testserver_path) { 97 CommandLine python_command(FilePath(FILE_PATH_LITERAL("python"))); 98 python_command.AppendArgPath(testserver_path); 99 if (!AddCommandLineArguments(&python_command)) 100 return false; 101 102 int pipefd[2]; 103 if (pipe(pipefd) != 0) { 104 PLOG(ERROR) << "Could not create pipe."; 105 return false; 106 } 107 108 // Save the read half. The write half is sent to the child. 109 child_fd_ = pipefd[0]; 110 child_fd_closer_.reset(&child_fd_); 111 file_util::ScopedFD write_closer(&pipefd[1]); 112 base::file_handle_mapping_vector map_write_fd; 113 map_write_fd.push_back(std::make_pair(pipefd[1], pipefd[1])); 114 115 python_command.AppendSwitchASCII("startup-pipe", 116 base::IntToString(pipefd[1])); 117 118 // Try to kill any orphaned testserver processes that may be running. 119 OrphanedTestServerFilter filter(testserver_path.value(), 120 base::IntToString(host_port_pair_.port())); 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 if (!base::LaunchApp(python_command.argv(), map_write_fd, false, 127 &process_handle_)) { 128 LOG(ERROR) << "Failed to launch " << python_command.command_line_string() 129 << " ..."; 130 return false; 131 } 132 133 return true; 134} 135 136bool TestServer::WaitToStart() { 137 file_util::ScopedFD child_fd_closer(child_fd_closer_.release()); 138 139 base::TimeDelta remaining_time = base::TimeDelta::FromMilliseconds( 140 TestTimeouts::action_max_timeout_ms()); 141 142 uint32 server_data_len = 0; 143 if (!ReadData(child_fd_, sizeof(server_data_len), 144 reinterpret_cast<uint8*>(&server_data_len), 145 &remaining_time)) { 146 LOG(ERROR) << "Could not read server_data_len"; 147 return false; 148 } 149 std::string server_data(server_data_len, '\0'); 150 if (!ReadData(child_fd_, server_data_len, 151 reinterpret_cast<uint8*>(&server_data[0]), 152 &remaining_time)) { 153 LOG(ERROR) << "Could not read server_data (" << server_data_len 154 << " bytes)"; 155 return false; 156 } 157 158 if (!ParseServerData(server_data)) { 159 LOG(ERROR) << "Could not parse server_data: " << server_data; 160 return false; 161 } 162 163 return true; 164} 165 166} // namespace net 167