| // Copyright 2020 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 "chrome/browser/nearby_sharing/instantmessaging/send_message_express.h" |
| |
| #include <sstream> |
| |
| #include "base/metrics/histogram_functions.h" |
| #include "base/optional.h" |
| #include "base/strings/stringprintf.h" |
| #include "chrome/browser/nearby_sharing/common/nearby_share_http_result.h" |
| #include "chrome/browser/nearby_sharing/instantmessaging/constants.h" |
| #include "chrome/browser/nearby_sharing/instantmessaging/proto/instantmessaging.pb.h" |
| #include "chrome/browser/nearby_sharing/instantmessaging/token_fetcher.h" |
| #include "chrome/browser/nearby_sharing/logging/logging.h" |
| #include "net/base/load_flags.h" |
| #include "services/network/public/cpp/resource_request.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "services/network/public/cpp/simple_url_loader.h" |
| #include "url/gurl.h" |
| |
| namespace { |
| |
| // 256 KB as max response size. |
| constexpr int kMaxSendResponseSize = 256; |
| |
| // Timeout for network calls to instantmessaging servers. |
| const base::TimeDelta kNetworkTimeout = base::TimeDelta::FromMilliseconds(2500); |
| |
| // TODO(crbug.com/1123164) - Add nearby sharing policy when available. |
| const net::NetworkTrafficAnnotationTag kTrafficAnnotation = |
| net::DefineNetworkTrafficAnnotation("send_message_express", R"( |
| semantics { |
| sender: "SendMessageExpress" |
| description: |
| "Sends a message to another device via a Gaia authenticated Google" |
| " messaging backend." |
| trigger: |
| "User uses any Chrome cross-device sharing feature and selects a" |
| " peer device to send the data to." |
| data: "WebRTC session description protocol messages are exchanged " |
| "between devices to set up a peer to peer connection as documented " |
| "in https://tools.ietf.org/html/rfc4566 and " |
| "https://www.w3.org/TR/webrtc/#session-description-model. No user " |
| "data is sent in the request." |
| destination: GOOGLE_OWNED_SERVICE |
| } |
| policy { |
| cookies_allowed: NO |
| setting: |
| "This feature is only enabled for signed-in users who enable " |
| "Nearby sharing" |
| chrome_policy { |
| BrowserSignin { |
| policy_options {mode: MANDATORY} |
| BrowserSignin: 0 |
| } |
| } |
| })"); |
| |
| void LogSendResult(bool success, |
| const NearbyShareHttpStatus& http_status, |
| const std::string& request_id) { |
| std::stringstream ss; |
| ss << "Instant messaging send express " << (success ? "succeeded" : "failed") |
| << " for request " << request_id << ". HTTP status: " << http_status; |
| if (success) { |
| NS_LOG(VERBOSE) << ss.str(); |
| } else { |
| NS_LOG(ERROR) << ss.str(); |
| } |
| base::UmaHistogramBoolean( |
| "Nearby.Connections.InstantMessaging.SendExpress.Result", success); |
| if (!success) { |
| base::UmaHistogramSparse( |
| "Nearby.Connections.InstantMessaging.SendExpress.Result.FailureReason", |
| http_status.GetResultCodeForMetrics()); |
| } |
| } |
| |
| } // namespace |
| |
| SendMessageExpress::SendMessageExpress( |
| signin::IdentityManager* identity_manager, |
| scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) |
| : token_fetcher_(identity_manager), |
| url_loader_factory_(std::move(url_loader_factory)) {} |
| |
| SendMessageExpress::~SendMessageExpress() = default; |
| |
| void SendMessageExpress::SendMessage( |
| const chrome_browser_nearby_sharing_instantmessaging:: |
| SendMessageExpressRequest& request, |
| SuccessCallback callback) { |
| token_fetcher_.GetAccessToken(base::BindOnce( |
| &SendMessageExpress::DoSendMessage, weak_ptr_factory_.GetWeakPtr(), |
| request, std::move(callback))); |
| } |
| |
| void SendMessageExpress::DoSendMessage( |
| const chrome_browser_nearby_sharing_instantmessaging:: |
| SendMessageExpressRequest& request, |
| SuccessCallback callback, |
| const std::string& oauth_token) { |
| base::UmaHistogramBoolean( |
| "Nearby.Connections.InstantMessaging.SendExpress.OAuthTokenFetchResult", |
| !oauth_token.empty()); |
| if (oauth_token.empty()) { |
| NS_LOG(ERROR) << __func__ << ": Failed to fetch OAuth token."; |
| std::move(callback).Run(false); |
| // NOTE: |this| might be destroyed here after running the callback |
| return; |
| } |
| |
| std::string request_id = request.header().request_id(); |
| |
| auto resource_request = std::make_unique<network::ResourceRequest>(); |
| resource_request->url = GURL(kInstantMessagingSendMessageAPI); |
| resource_request->load_flags = |
| net::LOAD_BYPASS_CACHE | net::LOAD_DISABLE_CACHE; |
| resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit; |
| resource_request->method = net::HttpRequestHeaders::kPostMethod; |
| resource_request->headers.AddHeaderFromString( |
| base::StringPrintf(kAuthorizationHeaderFormat, oauth_token.c_str())); |
| |
| std::unique_ptr<network::SimpleURLLoader> send_url_loader = |
| network::SimpleURLLoader::Create(std::move(resource_request), |
| kTrafficAnnotation); |
| auto* const send_url_loader_ptr = send_url_loader.get(); |
| send_url_loader->SetTimeoutDuration(kNetworkTimeout); |
| send_url_loader->AttachStringForUpload(request.SerializeAsString(), |
| "application/x-protobuf"); |
| send_url_loader_ptr->DownloadToString( |
| url_loader_factory_.get(), |
| base::BindOnce(&SendMessageExpress::OnSendMessageResponse, |
| weak_ptr_factory_.GetWeakPtr(), request_id, |
| std::move(send_url_loader), std::move(callback)), |
| kMaxSendResponseSize); |
| } |
| |
| void SendMessageExpress::OnSendMessageResponse( |
| const std::string& request_id, |
| std::unique_ptr<network::SimpleURLLoader> url_loader, |
| SuccessCallback callback, |
| std::unique_ptr<std::string> response_body) { |
| NearbyShareHttpStatus http_status(url_loader->NetError(), |
| url_loader->ResponseInfo()); |
| bool success = |
| http_status.IsSuccess() && response_body && !response_body->empty(); |
| LogSendResult(success, http_status, request_id); |
| std::move(callback).Run(success); |
| // NOTE: |this| might be destroyed here after running the callback |
| } |