blob: e0e76abd659b5bae0645c1d694c45993a9283364 [file] [log] [blame]
// Copyright 2019 The Chromium 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 "remoting/signaling/remoting_log_to_server.h"
#include <sstream>
#include "base/bind.h"
#include "remoting/base/grpc_support/grpc_async_unary_request.h"
#include "remoting/base/grpc_support/grpc_authenticated_executor.h"
#include "remoting/base/grpc_support/grpc_channel.h"
#include "remoting/base/service_urls.h"
#include "remoting/proto/remoting/v1/telemetry_service.grpc.pb.h"
#include "third_party/grpc/src/include/grpcpp/support/status.h"
namespace remoting {
namespace {
const net::BackoffEntry::Policy kBackoffPolicy = {
// Number of initial errors (in sequence) to ignore before applying
// exponential back-off rules.
0,
// Initial delay for exponential back-off in ms.
1000,
// Factor by which the waiting time will be multiplied.
2,
// Fuzzing percentage. ex: 10% will spread requests randomly
// between 90%-100% of the calculated time.
0.5,
// Maximum amount of time we are willing to delay our request in ms.
60000,
// Time to keep an entry from being discarded even when it
// has no significant state, -1 to never discard.
-1,
// Starts with initial delay.
false,
};
using CreateLogEntryResponseCallback =
base::OnceCallback<void(const grpc::Status&,
const apis::v1::CreateLogEntryResponse&)>;
class TelemetryClient {
public:
explicit TelemetryClient(OAuthTokenGetter* token_getter);
~TelemetryClient();
void CreateLogEntry(const apis::v1::CreateLogEntryRequest& request,
CreateLogEntryResponseCallback callback);
private:
using TelemetryService = apis::v1::RemotingTelemetryService;
GrpcAuthenticatedExecutor executor_;
std::unique_ptr<TelemetryService::Stub> stub_;
DISALLOW_COPY_AND_ASSIGN(TelemetryClient);
};
TelemetryClient::TelemetryClient(OAuthTokenGetter* token_getter)
: executor_(token_getter) {
stub_ = TelemetryService::NewStub(CreateSslChannelForEndpoint(
ServiceUrls::GetInstance()->remoting_server_endpoint()));
}
TelemetryClient::~TelemetryClient() = default;
void TelemetryClient::CreateLogEntry(
const apis::v1::CreateLogEntryRequest& request,
CreateLogEntryResponseCallback callback) {
executor_.ExecuteRpc(CreateGrpcAsyncUnaryRequest(
base::BindOnce(&TelemetryService::Stub::AsyncCreateLogEntry,
base::Unretained(stub_.get())),
request, std::move(callback)));
}
} // namespace
RemotingLogToServer::RemotingLogToServer(
ServerLogEntry::Mode mode,
std::unique_ptr<OAuthTokenGetter> token_getter)
: mode_(mode),
token_getter_(std::move(token_getter)),
backoff_(&kBackoffPolicy),
create_log_entry_(base::BindRepeating(
&TelemetryClient::CreateLogEntry,
std::make_unique<TelemetryClient>(token_getter_.get()))) {
DETACH_FROM_SEQUENCE(sequence_checker_);
}
RemotingLogToServer::~RemotingLogToServer() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
}
void RemotingLogToServer::Log(const ServerLogEntry& entry) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
apis::v1::CreateLogEntryRequest request;
*request.mutable_payload()->mutable_entry() = entry.ToGenericLogEntry();
SendLogRequestWithBackoff(request, kMaxSendLogAttempts);
}
void RemotingLogToServer::SendLogRequest(
const apis::v1::CreateLogEntryRequest& request,
int attempts_left) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (VLOG_IS_ON(1)) {
std::ostringstream log_stream;
log_stream << "Sending log entry with " << attempts_left
<< " attempts left: \n";
for (const auto& field : request.payload().entry().field()) {
log_stream << field.key() << ": " << field.value() << "\n";
}
log_stream << "=========================================================";
VLOG(1) << log_stream.str();
}
create_log_entry_.Run(
request,
base::BindOnce(&RemotingLogToServer::OnSendLogRequestResult,
base::Unretained(this), request, attempts_left - 1));
}
void RemotingLogToServer::SendLogRequestWithBackoff(
const apis::v1::CreateLogEntryRequest& request,
int attempts_left) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
auto time_until_release = backoff_.GetTimeUntilRelease();
VLOG(1) << "Scheduling request in " << time_until_release
<< ", attempts left: " << attempts_left;
backoff_timer_.Start(
FROM_HERE, time_until_release,
base::BindOnce(&RemotingLogToServer::SendLogRequest,
base::Unretained(this), request, attempts_left));
}
void RemotingLogToServer::OnSendLogRequestResult(
const apis::v1::CreateLogEntryRequest& request,
int attempts_left,
const grpc::Status& status,
const apis::v1::CreateLogEntryResponse& response) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (status.ok()) {
VLOG(1) << "One log has been successfully sent.";
backoff_.InformOfRequest(true);
return;
}
LOG(WARNING) << "Failed to send one log."
<< " Error: " << status.error_code()
<< " Message: " << status.error_message();
backoff_.InformOfRequest(false);
if (attempts_left <= 0) {
LOG(WARNING) << "Exceeded maximum retry attempts. Dropping it...";
return;
}
SendLogRequestWithBackoff(request, attempts_left);
}
ServerLogEntry::Mode RemotingLogToServer::mode() const {
return mode_;
}
} // namespace remoting