• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors
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/spawned_test_server/local_test_server.h"
6 
7 #include "base/command_line.h"
8 #include "base/json/json_reader.h"
9 #include "base/logging.h"
10 #include "base/notreached.h"
11 #include "base/path_service.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/threading/thread_restrictions.h"
14 #include "base/values.h"
15 #include "net/base/host_port_pair.h"
16 #include "net/base/net_errors.h"
17 #include "net/test/python_utils.h"
18 #include "url/gurl.h"
19 
20 namespace net {
21 
22 namespace {
23 
AppendArgumentFromJSONValue(const std::string & key,const base::Value & value_node,base::CommandLine * command_line)24 bool AppendArgumentFromJSONValue(const std::string& key,
25                                  const base::Value& value_node,
26                                  base::CommandLine* command_line) {
27   std::string argument_name = "--" + key;
28   switch (value_node.type()) {
29     case base::Value::Type::NONE:
30       command_line->AppendArg(argument_name);
31       break;
32     case base::Value::Type::INTEGER: {
33       command_line->AppendArg(argument_name + "=" +
34                               base::NumberToString(value_node.GetInt()));
35       break;
36     }
37     case base::Value::Type::STRING: {
38       if (!value_node.is_string())
39         return false;
40       const std::string value = value_node.GetString();
41       if (value.empty())
42         return false;
43       command_line->AppendArg(argument_name + "=" + value);
44       break;
45     }
46     case base::Value::Type::BOOLEAN:
47     case base::Value::Type::DOUBLE:
48     case base::Value::Type::LIST:
49     case base::Value::Type::DICT:
50     case base::Value::Type::BINARY:
51     default:
52       NOTREACHED() << "improper json type";
53   }
54   return true;
55 }
56 
57 }  // namespace
58 
LocalTestServer(Type type,const base::FilePath & document_root)59 LocalTestServer::LocalTestServer(Type type, const base::FilePath& document_root)
60     : BaseTestServer(type) {
61   if (!Init(document_root))
62     NOTREACHED();
63 }
64 
LocalTestServer(Type type,const SSLOptions & ssl_options,const base::FilePath & document_root)65 LocalTestServer::LocalTestServer(Type type,
66                                  const SSLOptions& ssl_options,
67                                  const base::FilePath& document_root)
68     : BaseTestServer(type, ssl_options) {
69   if (!Init(document_root))
70     NOTREACHED();
71 }
72 
~LocalTestServer()73 LocalTestServer::~LocalTestServer() {
74   Stop();
75 }
76 
GetTestServerPath(base::FilePath * testserver_path) const77 bool LocalTestServer::GetTestServerPath(base::FilePath* testserver_path) const {
78   base::FilePath testserver_dir;
79   if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &testserver_dir)) {
80     LOG(ERROR) << "Failed to get DIR_SRC_TEST_DATA_ROOT";
81     return false;
82   }
83   testserver_dir = testserver_dir.Append(FILE_PATH_LITERAL("net"))
84                        .Append(FILE_PATH_LITERAL("tools"))
85                        .Append(FILE_PATH_LITERAL("testserver"));
86   *testserver_path = testserver_dir.Append(FILE_PATH_LITERAL("testserver.py"));
87   return true;
88 }
89 
StartInBackground()90 bool LocalTestServer::StartInBackground() {
91   DCHECK(!started());
92 
93   base::ScopedAllowBlockingForTesting allow_blocking;
94 
95   // Get path to Python server script.
96   base::FilePath testserver_path;
97   if (!GetTestServerPath(&testserver_path)) {
98     LOG(ERROR) << "Could not get test server path.";
99     return false;
100   }
101 
102   std::optional<std::vector<base::FilePath>> python_path = GetPythonPath();
103   if (!python_path) {
104     LOG(ERROR) << "Could not get Python path.";
105     return false;
106   }
107 
108   if (!LaunchPython(testserver_path, *python_path)) {
109     LOG(ERROR) << "Could not launch Python with path " << testserver_path;
110     return false;
111   }
112 
113   return true;
114 }
115 
BlockUntilStarted()116 bool LocalTestServer::BlockUntilStarted() {
117   if (!WaitToStart()) {
118     Stop();
119     return false;
120   }
121 
122   return SetupWhenServerStarted();
123 }
124 
Stop()125 bool LocalTestServer::Stop() {
126   CleanUpWhenStoppingServer();
127 
128   if (!process_.IsValid())
129     return true;
130 
131   // First check if the process has already terminated.
132   bool ret = process_.WaitForExitWithTimeout(base::TimeDelta(), nullptr);
133   if (!ret) {
134     base::ScopedAllowBaseSyncPrimitivesForTesting allow_wait_process;
135     ret = process_.Terminate(1, true);
136   }
137 
138   if (ret)
139     process_.Close();
140   else
141     VLOG(1) << "Kill failed?";
142 
143   return ret;
144 }
145 
Init(const base::FilePath & document_root)146 bool LocalTestServer::Init(const base::FilePath& document_root) {
147   if (document_root.IsAbsolute())
148     return false;
149 
150   // At this point, the port that the test server will listen on is unknown.
151   // The test server will listen on an ephemeral port, and write the port
152   // number out over a pipe that this TestServer object will read from. Once
153   // that is complete, the host port pair will contain the actual port.
154   DCHECK(!GetPort());
155 
156   base::FilePath src_dir;
157   if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &src_dir)) {
158     return false;
159   }
160   SetResourcePath(src_dir.Append(document_root),
161                   src_dir.AppendASCII("net")
162                       .AppendASCII("data")
163                       .AppendASCII("ssl")
164                       .AppendASCII("certificates"));
165   return true;
166 }
167 
GetPythonPath() const168 std::optional<std::vector<base::FilePath>> LocalTestServer::GetPythonPath()
169     const {
170   base::FilePath third_party_dir;
171   if (!base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &third_party_dir)) {
172     LOG(ERROR) << "Failed to get DIR_SRC_TEST_DATA_ROOT";
173     return std::nullopt;
174   }
175   third_party_dir = third_party_dir.AppendASCII("third_party");
176 
177   std::vector<base::FilePath> ret = {
178       third_party_dir.AppendASCII("pywebsocket3").AppendASCII("src"),
179   };
180 
181   return ret;
182 }
183 
AddCommandLineArguments(base::CommandLine * command_line) const184 bool LocalTestServer::AddCommandLineArguments(
185     base::CommandLine* command_line) const {
186   std::optional<base::Value::Dict> arguments_dict = GenerateArguments();
187   if (!arguments_dict)
188     return false;
189 
190   // Serialize the argument dictionary into CommandLine.
191   for (auto it = arguments_dict->begin(); it != arguments_dict->end(); ++it) {
192     const base::Value& value = it->second;
193     const std::string& key = it->first;
194 
195     // Add arguments from a list.
196     if (value.is_list()) {
197       if (value.GetList().empty())
198         return false;
199       for (const auto& entry : value.GetList()) {
200         if (!AppendArgumentFromJSONValue(key, entry, command_line))
201           return false;
202       }
203     } else if (!AppendArgumentFromJSONValue(key, value, command_line)) {
204       return false;
205     }
206   }
207 
208   // Append the appropriate server type argument.
209   switch (type()) {
210     case TYPE_WS:
211     case TYPE_WSS:
212       command_line->AppendArg("--websocket");
213       break;
214     case TYPE_BASIC_AUTH_PROXY:
215       command_line->AppendArg("--basic-auth-proxy");
216       break;
217     case TYPE_PROXY:
218       command_line->AppendArg("--proxy");
219       break;
220     default:
221       NOTREACHED();
222   }
223 
224   return true;
225 }
226 
227 }  // namespace net
228