#include "net/test/spawned_test_server/remote_test_server.h"
#include <stdint.h>
#include <limits>
#include <vector>
#include "base/base_paths.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/message_loop/message_pump_type.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "build/build_config.h"
#include "net/base/host_port_pair.h"
#include "net/base/ip_endpoint.h"
#include "net/base/net_errors.h"
#include "net/test/spawned_test_server/remote_test_server_spawner_request.h"
#include "url/gurl.h"
namespace net {
namespace {
std::string GetServerTypeString(BaseTestServer::Type type) {
switch (type) {
case BaseTestServer::TYPE_WS:
case BaseTestServer::TYPE_WSS:
return "ws";
default:
NOTREACHED();
}
return std::string();
}
#if !BUILDFLAG(IS_FUCHSIA)
base::FilePath GetTestServerConfigFilePath() {
base::FilePath dir;
#if BUILDFLAG(IS_ANDROID)
base::PathService::Get(base::DIR_ANDROID_EXTERNAL_STORAGE, &dir);
#else
base::PathService::Get(base::DIR_TEMP, &dir);
#endif
return dir.AppendASCII("net-test-server-config");
}
#endif
std::string GetSpawnerUrlBase() {
#if BUILDFLAG(IS_FUCHSIA)
std::string spawner_url_base(
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
"remote-test-server-spawner-url-base"));
LOG_IF(FATAL, spawner_url_base.empty())
<< "--remote-test-server-spawner-url-base missing from command line";
return spawner_url_base;
#else
base::ScopedAllowBlockingForTesting allow_blocking;
base::FilePath config_path = GetTestServerConfigFilePath();
if (!base::PathExists(config_path))
return "";
std::string config_json;
if (!ReadFileToString(config_path, &config_json))
LOG(FATAL) << "Failed to read " << config_path.value();
absl::optional<base::Value> config = base::JSONReader::Read(config_json);
if (!config)
LOG(FATAL) << "Failed to parse " << config_path.value();
std::string* result = config->FindStringKey("spawner_url_base");
if (!result)
LOG(FATAL) << "spawner_url_base is not specified in the config";
return *result;
#endif
}
}
RemoteTestServer::RemoteTestServer(Type type,
const base::FilePath& document_root)
: BaseTestServer(type), io_thread_("RemoteTestServer IO Thread") {
if (!Init(document_root))
NOTREACHED();
}
RemoteTestServer::RemoteTestServer(Type type,
const SSLOptions& ssl_options,
const base::FilePath& document_root)
: BaseTestServer(type, ssl_options),
io_thread_("RemoteTestServer IO Thread") {
if (!Init(document_root))
NOTREACHED();
}
RemoteTestServer::~RemoteTestServer() {
Stop();
}
bool RemoteTestServer::StartInBackground() {
DCHECK(!started());
DCHECK(!start_request_);
absl::optional<base::Value::Dict> arguments_dict = GenerateArguments();
if (!arguments_dict)
return false;
arguments_dict->Set("on-remote-server", base::Value());
arguments_dict->Set("server-type", GetServerTypeString(type()));
std::string arguments_string;
base::JSONWriter::Write(*arguments_dict, &arguments_string);
if (arguments_string.empty())
return false;
start_request_ = std::make_unique<RemoteTestServerSpawnerRequest>(
io_thread_.task_runner(), GetSpawnerUrl("start"), arguments_string);
return true;
}
bool RemoteTestServer::BlockUntilStarted() {
DCHECK(start_request_);
std::string server_data_json;
bool request_result = start_request_->WaitForCompletion(&server_data_json);
start_request_.reset();
if (!request_result)
return false;
if (server_data_json.empty() ||
!SetAndParseServerData(server_data_json, &remote_port_)) {
LOG(ERROR) << "Could not parse server_data: " << server_data_json;
return false;
}
SetPort(remote_port_);
return SetupWhenServerStarted();
}
bool RemoteTestServer::Stop() {
DCHECK(!start_request_);
if (remote_port_) {
std::unique_ptr<RemoteTestServerSpawnerRequest> kill_request =
std::make_unique<RemoteTestServerSpawnerRequest>(
io_thread_.task_runner(),
GetSpawnerUrl(base::StringPrintf("kill?port=%d", remote_port_)),
std::string());
if (!kill_request->WaitForCompletion(nullptr))
LOG(FATAL) << "Failed stopping RemoteTestServer";
remote_port_ = 0;
}
CleanUpWhenStoppingServer();
return true;
}
base::FilePath RemoteTestServer::GetDocumentRoot() const {
base::FilePath src_dir;
base::PathService::Get(base::DIR_SOURCE_ROOT, &src_dir);
return src_dir.Append(document_root());
}
bool RemoteTestServer::Init(const base::FilePath& document_root) {
if (document_root.IsAbsolute())
return false;
spawner_url_base_ = GetSpawnerUrlBase();
bool thread_started = io_thread_.StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0));
CHECK(thread_started);
base::FilePath fixed_root = document_root;
if (fixed_root.empty())
fixed_root = base::FilePath(base::FilePath::kCurrentDirectory);
SetResourcePath(fixed_root, base::FilePath()
.AppendASCII("net")
.AppendASCII("data")
.AppendASCII("ssl")
.AppendASCII("certificates"));
return true;
}
GURL RemoteTestServer::GetSpawnerUrl(const std::string& command) const {
CHECK(!spawner_url_base_.empty());
std::string url = spawner_url_base_ + "/" + command;
GURL result = GURL(url);
CHECK(result.is_valid()) << url;
return result;
}
}