blob: debb3ea2dac4de15082f69fbb845433cc9af226c [file] [log] [blame]
// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "p2p/server/http_server.h"
#include "p2p/server/http_server_external_process.h"
#include <glib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <vector>
#include <base/files/file_path.h>
#include <base/logging.h>
#include "p2p/common/constants.h"
#include "p2p/common/server_message.h"
#include "p2p/common/struct_serializer.h"
using std::string;
using std::vector;
using base::FilePath;
using p2p::util::P2PServerMessage;
using p2p::util::P2PServerMessageType;
using p2p::util::StructSerializerWatcher;
namespace p2p {
namespace server {
HttpServerExternalProcess::HttpServerExternalProcess(
MetricsLibraryInterface* metrics_lib,
const FilePath& root_dir,
const FilePath& bin_dir,
uint16_t port)
: metrics_lib_(metrics_lib),
root_dir_(root_dir),
http_binary_path_(bin_dir.Append(p2p::constants::kHttpServerBinaryName)),
requested_port_(port),
port_(0),
num_connections_(0),
pid_(0),
child_stdout_fd_(-1) {}
HttpServerExternalProcess::~HttpServerExternalProcess() {
child_watch_.reset(nullptr);
if (child_stdout_fd_ != -1) {
close(child_stdout_fd_);
}
if (pid_ != 0) {
kill(pid_, SIGTERM);
}
}
bool HttpServerExternalProcess::Start() {
string command_line;
if (pid_ != 0) {
LOG(ERROR) << "Server is already running with pid " << pid_;
return false;
}
CHECK_EQ(child_stdout_fd_, -1);
vector<const char*> args;
string dir_arg = string("--directory=") + root_dir_.value();
string requested_port_arg =
string("--port=") + std::to_string(requested_port_);
args.push_back(http_binary_path_.value().c_str());
args.push_back(dir_arg.c_str());
args.push_back(requested_port_arg.c_str());
args.push_back(NULL);
GError* error = NULL;
if (!g_spawn_async_with_pipes(NULL, // working_dir
const_cast<gchar**>(args.data()),
NULL, // envp
(GSpawnFlags)0, NULL,
NULL, // child_setup, user_data
&pid_,
NULL, // standard_input
&child_stdout_fd_,
NULL, // standard_error
&error)) {
LOG(ERROR) << "Error launching process: " << error->message;
g_error_free(error);
return false;
}
// Setup the watch class for child messages.
child_watch_.reset(new StructSerializerWatcher<P2PServerMessage>(
child_stdout_fd_, OnMessageReceived, reinterpret_cast<void*>(this)));
return true;
}
void HttpServerExternalProcess::OnMessageReceived(const P2PServerMessage& msg,
void* user_data) {
HttpServerExternalProcess* server =
reinterpret_cast<HttpServerExternalProcess*>(user_data);
P2PServerMessageType message_type;
string metric;
if (!p2p::util::ValidP2PServerMessageMagic(msg) ||
!p2p::util::ParseP2PServerMessageType(msg.message_type, &message_type)) {
LOG(ERROR) << "Received invalid message: " << p2p::util::ToString(msg);
LOG(ERROR) << "Attempting to restarting P2P service.";
// Stop the child and abort ourselves.
server->Stop();
exit(1);
}
switch (message_type) {
case p2p::util::kP2PServerNumConnections:
if (msg.value >= 0)
server->UpdateNumConnections(msg.value);
break;
case p2p::util::kP2PServerRequestResult:
metric = "P2P.Server.RequestResult";
p2p::util::P2PServerRequestResult req_res;
if (p2p::util::ParseP2PServerRequestResult(msg.value, &req_res)) {
LOG(INFO) << "Uploading " << ToString(req_res) << " for metric "
<< metric;
server->metrics_lib_->SendEnumToUMA(
metric, req_res, p2p::util::kNumP2PServerRequestResults);
} else {
LOG(ERROR) << "Received invalid message: " << p2p::util::ToString(msg);
}
break;
case p2p::util::kP2PServerServedSuccessfullyMB:
metric = "P2P.Server.ContentServedSuccessfullyMB";
LOG(INFO) << "Uploading " << msg.value << " (count) for metric "
<< metric;
server->metrics_lib_->SendToUMA(metric, msg.value, 0 /* min */,
1000 /* max */, 50);
break;
case p2p::util::kP2PServerServedInterruptedMB:
metric = "P2P.Server.ContentServedInterruptedMB";
LOG(INFO) << "Uploading " << msg.value << " (count) for metric "
<< metric;
server->metrics_lib_->SendToUMA(metric, msg.value, 0 /* min */,
1000 /* max */, 50);
break;
case p2p::util::kP2PServerRangeBeginPercentage:
metric = "P2P.Server.RangeBeginPercentage";
LOG(INFO) << "Uploading " << msg.value << " (count) for metric "
<< metric;
server->metrics_lib_->SendToUMA(metric, msg.value, 0 /* min */,
100 /* max */, 100);
break;
case p2p::util::kP2PServerDownloadSpeedKBps:
metric = "P2P.Server.DownloadSpeedKBps";
LOG(INFO) << "Uploading " << msg.value << " (count) for metric "
<< metric;
server->metrics_lib_->SendToUMA(metric, msg.value, 0 /* min */,
10000 /* max */, 100);
break;
case p2p::util::kP2PServerPeakDownloadSpeedKBps:
metric = "P2P.Server.PeakDownloadSpeedKBps";
LOG(INFO) << "Uploading " << msg.value << " (count) for metric "
<< metric;
server->metrics_lib_->SendToUMA(metric, msg.value, 0 /* min */,
10000 /* max */, 100);
break;
case p2p::util::kP2PServerClientCount:
metric = "P2P.Server.ClientCount";
LOG(INFO) << "Uploading " << msg.value << " (count) for metric "
<< metric;
server->metrics_lib_->SendToUMA(metric, msg.value, 0 /* min */,
50 /* max */, 50);
break;
case p2p::util::kP2PServerPortNumber:
server->port_ = msg.value;
break;
// ParseP2PServerMessageType ensures this case is not reached.
case p2p::util::kNumP2PServerMessageTypes:
NOTREACHED();
}
}
void HttpServerExternalProcess::UpdateNumConnections(int num_connections) {
if (num_connections_ == num_connections)
return;
num_connections_ = num_connections;
if (!num_connections_callback_.is_null())
num_connections_callback_.Run(num_connections);
}
bool HttpServerExternalProcess::Stop() {
if (pid_ == 0) {
LOG(ERROR) << "Server is not running";
return false;
}
child_watch_.reset(nullptr);
if (child_stdout_fd_ != -1) {
close(child_stdout_fd_);
}
child_stdout_fd_ = -1;
if (pid_ != 0) {
kill(pid_, SIGTERM);
}
pid_ = 0;
port_ = 0;
return true;
}
bool HttpServerExternalProcess::IsRunning() {
return pid_ != 0;
}
uint16_t HttpServerExternalProcess::Port() {
return port_;
}
void HttpServerExternalProcess::SetNumConnectionsCallback(
NumConnectionsCallback callback) {
num_connections_callback_ = callback;
}
// ----------------------------------------------------------------------------
HttpServer* HttpServer::Construct(MetricsLibraryInterface* metrics_lib,
const FilePath& root_dir,
const FilePath& bin_dir,
uint16_t port) {
return new HttpServerExternalProcess(metrics_lib, root_dir, bin_dir, port);
}
} // namespace server
} // namespace p2p