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