• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 namespace {
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).
26 class OrphanedTestServerFilter : public base::ProcessFilter {
27  public:
OrphanedTestServerFilter(const std::string & path_string,const std::string & port_string)28   OrphanedTestServerFilter(
29       const std::string& path_string, const std::string& port_string)
30       : path_string_(path_string),
31         port_string_(port_string) {}
32 
Includes(const base::ProcessEntry & entry) const33   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.
ReadData(int fd,ssize_t bytes_max,uint8 * buffer,base::TimeDelta * remaining_time)59 bool 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 
94 namespace net {
95 
LaunchPython(const FilePath & testserver_path)96 bool 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 
WaitToStart()136 bool 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