xref: /aosp_15_r20/external/cronet/net/test/spawned_test_server/remote_test_server.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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/remote_test_server.h"
6 
7 #include <stdint.h>
8 
9 #include <limits>
10 #include <vector>
11 
12 #include "base/base_paths.h"
13 #include "base/command_line.h"
14 #include "base/files/file_path.h"
15 #include "base/files/file_util.h"
16 #include "base/json/json_reader.h"
17 #include "base/json/json_writer.h"
18 #include "base/logging.h"
19 #include "base/message_loop/message_pump_type.h"
20 #include "base/path_service.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/threading/thread_restrictions.h"
25 #include "base/values.h"
26 #include "build/build_config.h"
27 #include "net/base/host_port_pair.h"
28 #include "net/base/ip_endpoint.h"
29 #include "net/base/net_errors.h"
30 #include "net/test/spawned_test_server/remote_test_server_spawner_request.h"
31 #include "url/gurl.h"
32 
33 namespace net {
34 
35 namespace {
36 
37 // Please keep in sync with dictionary SERVER_TYPES in testserver.py
GetServerTypeString(BaseTestServer::Type type)38 std::string GetServerTypeString(BaseTestServer::Type type) {
39   switch (type) {
40     case BaseTestServer::TYPE_WS:
41     case BaseTestServer::TYPE_WSS:
42       return "ws";
43     default:
44       NOTREACHED();
45   }
46   return std::string();
47 }
48 
49 #if !BUILDFLAG(IS_FUCHSIA)
50 // Returns platform-specific path to the config file for the test server.
GetTestServerConfigFilePath()51 base::FilePath GetTestServerConfigFilePath() {
52   base::FilePath dir;
53 #if BUILDFLAG(IS_ANDROID)
54   base::PathService::Get(base::DIR_ANDROID_EXTERNAL_STORAGE, &dir);
55 #else
56   base::PathService::Get(base::DIR_TEMP, &dir);
57 #endif
58   return dir.AppendASCII("net-test-server-config");
59 }
60 #endif  // !BUILDFLAG(IS_FUCHSIA)
61 
62 // Reads base URL for the test server spawner. That URL is used to control the
63 // test server.
GetSpawnerUrlBase()64 std::string GetSpawnerUrlBase() {
65 #if BUILDFLAG(IS_FUCHSIA)
66   std::string spawner_url_base(
67       base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
68           "remote-test-server-spawner-url-base"));
69   LOG_IF(FATAL, spawner_url_base.empty())
70       << "--remote-test-server-spawner-url-base missing from command line";
71   return spawner_url_base;
72 #else   // BUILDFLAG(IS_FUCHSIA)
73   base::ScopedAllowBlockingForTesting allow_blocking;
74 
75   base::FilePath config_path = GetTestServerConfigFilePath();
76 
77   if (!base::PathExists(config_path))
78     return "";
79 
80   std::string config_json;
81   if (!ReadFileToString(config_path, &config_json))
82     LOG(FATAL) << "Failed to read " << config_path.value();
83 
84   std::optional<base::Value> config = base::JSONReader::Read(config_json);
85   if (!config)
86     LOG(FATAL) << "Failed to parse " << config_path.value();
87 
88   std::string* result = config->GetDict().FindString("spawner_url_base");
89   if (!result)
90     LOG(FATAL) << "spawner_url_base is not specified in the config";
91 
92   return *result;
93 #endif  // BUILDFLAG(IS_FUCHSIA)
94 }
95 
96 }  // namespace
97 
RemoteTestServer(Type type,const base::FilePath & document_root)98 RemoteTestServer::RemoteTestServer(Type type,
99                                    const base::FilePath& document_root)
100     : BaseTestServer(type), io_thread_("RemoteTestServer IO Thread") {
101   if (!Init(document_root))
102     NOTREACHED();
103 }
104 
RemoteTestServer(Type type,const SSLOptions & ssl_options,const base::FilePath & document_root)105 RemoteTestServer::RemoteTestServer(Type type,
106                                    const SSLOptions& ssl_options,
107                                    const base::FilePath& document_root)
108     : BaseTestServer(type, ssl_options),
109       io_thread_("RemoteTestServer IO Thread") {
110   if (!Init(document_root))
111     NOTREACHED();
112 }
113 
~RemoteTestServer()114 RemoteTestServer::~RemoteTestServer() {
115   Stop();
116 }
117 
StartInBackground()118 bool RemoteTestServer::StartInBackground() {
119   DCHECK(!started());
120   DCHECK(!start_request_);
121 
122   std::optional<base::Value::Dict> arguments_dict = GenerateArguments();
123   if (!arguments_dict)
124     return false;
125 
126   arguments_dict->Set("on-remote-server", base::Value());
127 
128   // Append the 'server-type' argument which is used by spawner server to
129   // pass right server type to Python test server.
130   arguments_dict->Set("server-type", GetServerTypeString(type()));
131 
132   // Generate JSON-formatted argument string.
133   std::string arguments_string;
134   base::JSONWriter::Write(*arguments_dict, &arguments_string);
135   if (arguments_string.empty())
136     return false;
137 
138   start_request_ = std::make_unique<RemoteTestServerSpawnerRequest>(
139       io_thread_.task_runner(), GetSpawnerUrl("start"), arguments_string);
140 
141   return true;
142 }
143 
BlockUntilStarted()144 bool RemoteTestServer::BlockUntilStarted() {
145   DCHECK(start_request_);
146 
147   std::string server_data_json;
148   bool request_result = start_request_->WaitForCompletion(&server_data_json);
149   start_request_.reset();
150   if (!request_result)
151     return false;
152 
153   // Parse server_data_json.
154   if (server_data_json.empty() ||
155       !SetAndParseServerData(server_data_json, &remote_port_)) {
156     LOG(ERROR) << "Could not parse server_data: " << server_data_json;
157     return false;
158   }
159 
160   SetPort(remote_port_);
161 
162   return SetupWhenServerStarted();
163 }
164 
Stop()165 bool RemoteTestServer::Stop() {
166   DCHECK(!start_request_);
167 
168   if (remote_port_) {
169     std::unique_ptr<RemoteTestServerSpawnerRequest> kill_request =
170         std::make_unique<RemoteTestServerSpawnerRequest>(
171             io_thread_.task_runner(),
172             GetSpawnerUrl(base::StringPrintf("kill?port=%d", remote_port_)),
173             std::string());
174 
175     if (!kill_request->WaitForCompletion(nullptr))
176       LOG(FATAL) << "Failed stopping RemoteTestServer";
177 
178     remote_port_ = 0;
179   }
180 
181   CleanUpWhenStoppingServer();
182 
183   return true;
184 }
185 
186 // On Android, the document root in the device is not the same as the document
187 // root in the host machine where the test server is launched. So prepend
188 // DIR_SRC_TEST_DATA_ROOT here to get the actual path of document root on the
189 // Android device.
GetDocumentRoot() const190 base::FilePath RemoteTestServer::GetDocumentRoot() const {
191   base::FilePath src_dir;
192   base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &src_dir);
193   return src_dir.Append(document_root());
194 }
195 
Init(const base::FilePath & document_root)196 bool RemoteTestServer::Init(const base::FilePath& document_root) {
197   if (document_root.IsAbsolute())
198     return false;
199 
200   spawner_url_base_ = GetSpawnerUrlBase();
201 
202   bool thread_started = io_thread_.StartWithOptions(
203       base::Thread::Options(base::MessagePumpType::IO, 0));
204   CHECK(thread_started);
205 
206   // Unlike LocalTestServer, RemoteTestServer passes relative paths to the test
207   // server. The test server fails on empty strings in some configurations.
208   base::FilePath fixed_root = document_root;
209   if (fixed_root.empty())
210     fixed_root = base::FilePath(base::FilePath::kCurrentDirectory);
211   SetResourcePath(fixed_root, base::FilePath()
212                                   .AppendASCII("net")
213                                   .AppendASCII("data")
214                                   .AppendASCII("ssl")
215                                   .AppendASCII("certificates"));
216   return true;
217 }
218 
GetSpawnerUrl(const std::string & command) const219 GURL RemoteTestServer::GetSpawnerUrl(const std::string& command) const {
220   CHECK(!spawner_url_base_.empty());
221   std::string url = spawner_url_base_ + "/" + command;
222   GURL result = GURL(url);
223   CHECK(result.is_valid()) << url;
224   return result;
225 }
226 
227 }  // namespace net
228