123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556 |
- // 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/ftl_signal_strategy.h"
- #include <utility>
- #include "base/bind.h"
- #include "base/logging.h"
- #include "base/memory/ptr_util.h"
- #include "base/memory/weak_ptr.h"
- #include "base/observer_list.h"
- #include "base/rand_util.h"
- #include "base/sequence_checker.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/threading/sequenced_task_runner_handle.h"
- #include "remoting/base/logging.h"
- #include "remoting/base/oauth_token_getter.h"
- #include "remoting/base/protobuf_http_status.h"
- #include "remoting/signaling/ftl_device_id_provider.h"
- #include "remoting/signaling/ftl_messaging_client.h"
- #include "remoting/signaling/ftl_registration_manager.h"
- #include "remoting/signaling/signaling_address.h"
- #include "remoting/signaling/xmpp_constants.h"
- #include "services/network/public/cpp/shared_url_loader_factory.h"
- #include "third_party/libjingle_xmpp/xmllite/xmlelement.h"
- namespace remoting {
- class FtlSignalStrategy::Core {
- public:
- Core(std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
- std::unique_ptr<RegistrationManager> registration_manager,
- std::unique_ptr<MessagingClient> messaging_client);
- Core(const Core&) = delete;
- Core& operator=(const Core&) = delete;
- ~Core();
- void Connect();
- void Disconnect();
- State GetState() const;
- Error GetError() const;
- const SignalingAddress& GetLocalAddress() const;
- void AddListener(Listener* listener);
- void RemoveListener(Listener* listener);
- bool SendStanza(std::unique_ptr<jingle_xmpp::XmlElement> stanza);
- bool SendMessage(const SignalingAddress& destination_address,
- const ftl::ChromotingMessage& message);
- bool IsSignInError() const;
- private:
- // Methods are called in the order below when Connect() is called.
- void OnGetOAuthTokenResponse(OAuthTokenGetter::Status status,
- const std::string& user_email,
- const std::string& access_token);
- void OnSignInGaiaResponse(const ProtobufHttpStatus& status);
- void StartReceivingMessages();
- void OnReceiveMessagesStreamStarted();
- void OnReceiveMessagesStreamClosed(const ProtobufHttpStatus& status);
- void OnMessageReceived(const ftl::Id& sender_id,
- const std::string& sender_registration_id,
- const ftl::ChromotingMessage& message);
- void SendMessageImpl(const SignalingAddress& receiver,
- const ftl::ChromotingMessage& message,
- MessagingClient::DoneCallback callback);
- void OnSendMessageResponse(const SignalingAddress& receiver,
- const std::string& stanza_id,
- const ProtobufHttpStatus& status);
- // Returns true if the status is handled.
- void HandleProtobufHttpStatusError(const base::Location& location,
- const ProtobufHttpStatus& status);
- void OnStanza(const SignalingAddress& sender_address,
- std::unique_ptr<jingle_xmpp::XmlElement> stanza);
- std::unique_ptr<OAuthTokenGetter> oauth_token_getter_;
- std::unique_ptr<RegistrationManager> registration_manager_;
- std::unique_ptr<MessagingClient> messaging_client_;
- std::string user_email_;
- SignalingAddress local_address_;
- base::CallbackListSubscription receive_message_subscription_;
- Error error_ = OK;
- bool is_sign_in_error_ = false;
- base::ObserverList<Listener, true> listeners_;
- SEQUENCE_CHECKER(sequence_checker_);
- base::WeakPtrFactory<Core> weak_factory_{this};
- };
- FtlSignalStrategy::Core::Core(
- std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
- std::unique_ptr<RegistrationManager> registration_manager,
- std::unique_ptr<MessagingClient> messaging_client) {
- DCHECK(oauth_token_getter);
- DCHECK(registration_manager);
- DCHECK(messaging_client);
- oauth_token_getter_ = std::move(oauth_token_getter);
- registration_manager_ = std::move(registration_manager);
- messaging_client_ = std::move(messaging_client);
- }
- FtlSignalStrategy::Core::~Core() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- Disconnect();
- }
- void FtlSignalStrategy::Core::Connect() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (GetState() != DISCONNECTED) {
- LOG(WARNING) << "Signaling is not disconnected. State: " << GetState();
- return;
- }
- error_ = OK;
- is_sign_in_error_ = false;
- receive_message_subscription_ =
- messaging_client_->RegisterMessageCallback(base::BindRepeating(
- &Core::OnMessageReceived, weak_factory_.GetWeakPtr()));
- for (auto& observer : listeners_)
- observer.OnSignalStrategyStateChange(CONNECTING);
- StartReceivingMessages();
- }
- void FtlSignalStrategy::Core::Disconnect() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (registration_manager_->IsSignedIn()) {
- registration_manager_->SignOut();
- }
- if (receive_message_subscription_) {
- local_address_ = SignalingAddress();
- receive_message_subscription_ = {};
- messaging_client_->StopReceivingMessages();
- for (auto& observer : listeners_)
- observer.OnSignalStrategyStateChange(DISCONNECTED);
- }
- }
- SignalStrategy::State FtlSignalStrategy::Core::GetState() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!local_address_.empty()) {
- DCHECK(receive_message_subscription_);
- return CONNECTED;
- } else if (receive_message_subscription_) {
- return CONNECTING;
- } else {
- return DISCONNECTED;
- }
- }
- SignalStrategy::Error FtlSignalStrategy::Core::GetError() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return error_;
- }
- const SignalingAddress& FtlSignalStrategy::Core::GetLocalAddress() const {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- return local_address_;
- }
- void FtlSignalStrategy::Core::AddListener(Listener* listener) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- listeners_.AddObserver(listener);
- }
- void FtlSignalStrategy::Core::RemoveListener(Listener* listener) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- listeners_.RemoveObserver(listener);
- }
- bool FtlSignalStrategy::Core::SendStanza(
- std::unique_ptr<jingle_xmpp::XmlElement> stanza) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (GetState() != CONNECTED) {
- HOST_LOG << "Dropping signaling message because FTL is not connected.";
- return false;
- }
- std::string to_error;
- SignalingAddress to =
- SignalingAddress::Parse(stanza.get(), SignalingAddress::TO, &to_error);
- DCHECK(to_error.empty());
- // Synthesizing the from attribute in the message.
- stanza->SetAttr(kQNameFrom, local_address_.id());
- std::string stanza_id = stanza->Attr(kQNameId);
- ftl::ChromotingMessage crd_message;
- crd_message.mutable_xmpp()->set_stanza(stanza->Str());
- SendMessageImpl(to, crd_message,
- base::BindOnce(&Core::OnSendMessageResponse,
- weak_factory_.GetWeakPtr(), to, stanza_id));
- // Return false if the SendMessageImpl() call above resulted in the
- // SignalStrategy being disconnected.
- return GetState() == CONNECTED;
- }
- bool FtlSignalStrategy::Core::SendMessage(
- const SignalingAddress& destination_address,
- const ftl::ChromotingMessage& message) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (GetState() != CONNECTED) {
- HOST_LOG << "Dropping message because FTL is not connected.";
- return false;
- }
- SendMessageImpl(
- destination_address, message,
- base::BindOnce(&Core::OnSendMessageResponse, weak_factory_.GetWeakPtr(),
- destination_address, std::string()));
- return true;
- }
- bool FtlSignalStrategy::Core::IsSignInError() const {
- return is_sign_in_error_;
- }
- void FtlSignalStrategy::Core::OnGetOAuthTokenResponse(
- OAuthTokenGetter::Status status,
- const std::string& user_email,
- const std::string& access_token) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (status != OAuthTokenGetter::Status::SUCCESS) {
- switch (status) {
- case OAuthTokenGetter::Status::NETWORK_ERROR:
- error_ = SignalStrategy::Error::NETWORK_ERROR;
- break;
- case OAuthTokenGetter::Status::AUTH_ERROR:
- error_ = SignalStrategy::Error::AUTHENTICATION_FAILED;
- break;
- default:
- NOTREACHED();
- break;
- }
- is_sign_in_error_ = true;
- Disconnect();
- return;
- }
- user_email_ = user_email;
- StartReceivingMessages();
- }
- void FtlSignalStrategy::Core::OnSignInGaiaResponse(
- const ProtobufHttpStatus& status) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (!status.ok()) {
- is_sign_in_error_ = true;
- HandleProtobufHttpStatusError(FROM_HERE, status);
- return;
- }
- StartReceivingMessages();
- }
- void FtlSignalStrategy::Core::StartReceivingMessages() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK_EQ(CONNECTING, GetState());
- DCHECK(!messaging_client_->IsReceivingMessages());
- if (user_email_.empty()) {
- oauth_token_getter_->CallWithToken(base::BindOnce(
- &Core::OnGetOAuthTokenResponse, weak_factory_.GetWeakPtr()));
- return;
- }
- if (!registration_manager_->IsSignedIn()) {
- registration_manager_->SignInGaia(base::BindOnce(
- &Core::OnSignInGaiaResponse, weak_factory_.GetWeakPtr()));
- return;
- }
- messaging_client_->StartReceivingMessages(
- base::BindOnce(&Core::OnReceiveMessagesStreamStarted,
- weak_factory_.GetWeakPtr()),
- base::BindOnce(&Core::OnReceiveMessagesStreamClosed,
- weak_factory_.GetWeakPtr()));
- }
- void FtlSignalStrategy::Core::OnReceiveMessagesStreamStarted() {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- local_address_ = SignalingAddress::CreateFtlSignalingAddress(
- user_email_, registration_manager_->GetRegistrationId());
- for (auto& observer : listeners_)
- observer.OnSignalStrategyStateChange(CONNECTED);
- }
- void FtlSignalStrategy::Core::OnReceiveMessagesStreamClosed(
- const ProtobufHttpStatus& status) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (status.error_code() == ProtobufHttpStatus::Code::CANCELLED) {
- LOG(WARNING) << "ReceiveMessages stream closed with CANCELLED code.";
- }
- DCHECK(!status.ok());
- HandleProtobufHttpStatusError(FROM_HERE, status);
- }
- void FtlSignalStrategy::Core::OnMessageReceived(
- const ftl::Id& sender_id,
- const std::string& sender_registration_id,
- const ftl::ChromotingMessage& message) {
- for (auto& listener : listeners_) {
- if (listener.OnSignalStrategyIncomingMessage(
- sender_id, sender_registration_id, message)) {
- return;
- }
- }
- if (!message.has_xmpp()) {
- LOG(WARNING) << "Ignoring message that doesn't have XMPP field.";
- return;
- }
- auto sender_address = SignalingAddress::CreateFtlSignalingAddress(
- sender_id.id(), sender_registration_id);
- DCHECK(message.xmpp().has_stanza());
- auto stanza = base::WrapUnique<jingle_xmpp::XmlElement>(
- jingle_xmpp::XmlElement::ForStr(message.xmpp().stanza()));
- if (!stanza) {
- LOG(WARNING) << "Failed to parse XMPP: " << message.xmpp().stanza();
- return;
- }
- OnStanza(sender_address, std::move(stanza));
- }
- void FtlSignalStrategy::Core::SendMessageImpl(
- const SignalingAddress& receiver,
- const ftl::ChromotingMessage& message,
- MessagingClient::DoneCallback callback) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- std::string receiver_username;
- std::string receiver_registration_id;
- bool get_info_result =
- receiver.GetFtlInfo(&receiver_username, &receiver_registration_id);
- if (!get_info_result) {
- LOG(DFATAL) << "Receiver is not in FTL address: " << receiver.id();
- return;
- }
- std::string message_payload;
- if (message.has_xmpp()) {
- message_payload = message.xmpp().stanza();
- } else if (message.has_echo()) {
- message_payload = message.echo().message();
- } else {
- message_payload = "Error displaying message due to unknown format.";
- }
- HOST_LOG << "Sending outgoing message:\n"
- << "Receiver: " << receiver_username << "\n"
- << "Receiver registration ID: " << receiver_registration_id << "\n"
- << message_payload
- << "\n=========================================================";
- messaging_client_->SendMessage(receiver_username, receiver_registration_id,
- message, std::move(callback));
- }
- void FtlSignalStrategy::Core::OnSendMessageResponse(
- const SignalingAddress& receiver,
- const std::string& stanza_id,
- const ProtobufHttpStatus& status) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- if (status.ok()) {
- return;
- }
- if (status.error_code() == ProtobufHttpStatus::Code::UNAUTHENTICATED) {
- HandleProtobufHttpStatusError(FROM_HERE, status);
- return;
- }
- LOG(ERROR) << "Failed to send message to peer. Error code: "
- << static_cast<int>(status.error_code())
- << ", message: " << status.error_message();
- if (stanza_id.empty()) {
- // If the message sent was not related to signaling, then exit early.
- return;
- }
- // Fake an error message so JingleSession will take it as PEER_IS_OFFLINE.
- auto error_iq = std::make_unique<jingle_xmpp::XmlElement>(kQNameIq);
- error_iq->SetAttr(kQNameType, kIqTypeError);
- error_iq->SetAttr(kQNameId, stanza_id);
- error_iq->SetAttr(kQNameFrom, receiver.id());
- error_iq->SetAttr(kQNameTo, local_address_.id());
- OnStanza(receiver, std::move(error_iq));
- }
- void FtlSignalStrategy::Core::HandleProtobufHttpStatusError(
- const base::Location& location,
- const ProtobufHttpStatus& status) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- DCHECK(!status.ok());
- // We don't map HTTP_UNAUTHORIZED to AUTHENTICATION_FAILED here, as it will
- // permanently terminate the host, which is not desirable since it might
- // happen when the FTL registration becomes invalid while the robot account
- // itself is still intact.
- // AUTHENTICATION_FAILED is only reported if the OAuthTokenGetter fails to
- // fetch the token.
- error_ = Error::NETWORK_ERROR;
- LOG(ERROR) << "Received server error. Error code: "
- << static_cast<int>(status.error_code())
- << ", message: " << status.error_message()
- << ", location: " << location.ToString();
- if (status.error_code() == ProtobufHttpStatus::Code::UNAUTHENTICATED ||
- status.error_code() == ProtobufHttpStatus::Code::PERMISSION_DENIED) {
- oauth_token_getter_->InvalidateCache();
- }
- Disconnect();
- }
- void FtlSignalStrategy::Core::OnStanza(
- const SignalingAddress& sender_address,
- std::unique_ptr<jingle_xmpp::XmlElement> stanza) {
- DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
- // Validate the schema and FTL IDs.
- if (stanza->Name() != kQNameIq) {
- LOG(DFATAL) << "Received unexpected non-IQ packet " << stanza->Str();
- return;
- }
- if (SignalingAddress(stanza->Attr(kQNameFrom)) != sender_address) {
- LOG(DFATAL) << "Expected sender: " << sender_address.id()
- << ", but received: " << stanza->Attr(kQNameFrom);
- return;
- }
- if (SignalingAddress(stanza->Attr(kQNameTo)) != local_address_) {
- LOG(DFATAL) << "Expected receiver: " << local_address_.id()
- << ", but received: " << stanza->Attr(kQNameTo);
- return;
- }
- HOST_LOG << "Received incoming stanza:\n"
- << stanza->Str()
- << "\n=========================================================";
- for (auto& listener : listeners_) {
- if (listener.OnSignalStrategyIncomingStanza(stanza.get()))
- return;
- }
- }
- FtlSignalStrategy::FtlSignalStrategy(
- std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
- scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
- std::unique_ptr<FtlDeviceIdProvider> device_id_provider,
- SignalingTracker* signaling_tracker) {
- // TODO(yuweih): Just make FtlMessagingClient own FtlRegistrationManager and
- // call SignInGaia() transparently.
- auto registration_manager = std::make_unique<FtlRegistrationManager>(
- oauth_token_getter.get(), url_loader_factory,
- std::move(device_id_provider));
- auto messaging_client = std::make_unique<FtlMessagingClient>(
- oauth_token_getter.get(), url_loader_factory, registration_manager.get(),
- signaling_tracker);
- CreateCore(std::move(oauth_token_getter), std::move(registration_manager),
- std::move(messaging_client));
- }
- FtlSignalStrategy::FtlSignalStrategy(
- std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
- std::unique_ptr<RegistrationManager> registration_manager,
- std::unique_ptr<MessagingClient> messaging_client) {
- CreateCore(std::move(oauth_token_getter), std::move(registration_manager),
- std::move(messaging_client));
- }
- FtlSignalStrategy::~FtlSignalStrategy() {
- // All listeners should be removed at this point, so it's safe to detach
- // |core_|.
- base::SequencedTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE,
- core_.release());
- }
- void FtlSignalStrategy::Connect() {
- core_->Connect();
- }
- void FtlSignalStrategy::Disconnect() {
- core_->Disconnect();
- }
- SignalStrategy::State FtlSignalStrategy::GetState() const {
- return core_->GetState();
- }
- SignalStrategy::Error FtlSignalStrategy::GetError() const {
- return core_->GetError();
- }
- const SignalingAddress& FtlSignalStrategy::GetLocalAddress() const {
- return core_->GetLocalAddress();
- }
- void FtlSignalStrategy::AddListener(Listener* listener) {
- core_->AddListener(listener);
- }
- void FtlSignalStrategy::RemoveListener(Listener* listener) {
- core_->RemoveListener(listener);
- }
- bool FtlSignalStrategy::SendStanza(
- std::unique_ptr<jingle_xmpp::XmlElement> stanza) {
- return core_->SendStanza(std::move(stanza));
- }
- bool FtlSignalStrategy::SendMessage(const SignalingAddress& destination_address,
- const ftl::ChromotingMessage& message) {
- return core_->SendMessage(destination_address, message);
- }
- std::string FtlSignalStrategy::GetNextId() {
- return base::NumberToString(base::RandUint64());
- }
- bool FtlSignalStrategy::IsSignInError() const {
- return core_->IsSignInError();
- }
- void FtlSignalStrategy::CreateCore(
- std::unique_ptr<OAuthTokenGetter> oauth_token_getter,
- std::unique_ptr<RegistrationManager> registration_manager,
- std::unique_ptr<MessagingClient> messaging_client) {
- core_ = std::make_unique<Core>(std::move(oauth_token_getter),
- std::move(registration_manager),
- std::move(messaging_client));
- }
- } // namespace remoting
|