Commit b23ba4d3 authored by Himanshu Jaju's avatar Himanshu Jaju Committed by Commit Bot

Ice Server API integration

Integrates network traversal API to fetch stun and turn servers for
webrtc p2p connection. Handles API failure by returning a list of
public Google stun servers as a fallback.

Bug: 1031156
Change-Id: I60b5d6ccdb749ce9375bcb3333dc2cbdab62aa18
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1994295
Commit-Queue: Himanshu Jaju <himanshujaju@chromium.org>
Reviewed-by: default avatarMartin Šrámek <msramek@chromium.org>
Reviewed-by: default avatarDavid Roger <droger@chromium.org>
Reviewed-by: default avatarRichard Knoll <knollr@chromium.org>
Reviewed-by: default avatarAlex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#733941}
parent 0b2e941e
...@@ -3559,6 +3559,8 @@ jumbo_static_library("browser") { ...@@ -3559,6 +3559,8 @@ jumbo_static_library("browser") {
"sharing/sharing_notification_handler.h", "sharing/sharing_notification_handler.h",
"sharing/sharing_ui_controller.cc", "sharing/sharing_ui_controller.cc",
"sharing/sharing_ui_controller.h", "sharing/sharing_ui_controller.h",
"sharing/webrtc/ice_config_fetcher.cc",
"sharing/webrtc/ice_config_fetcher.h",
"signin/signin_promo.cc", "signin/signin_promo.cc",
"signin/signin_promo.h", "signin/signin_promo.h",
"signin/signin_ui_util.cc", "signin/signin_ui_util.cc",
...@@ -3713,6 +3715,7 @@ jumbo_static_library("browser") { ...@@ -3713,6 +3715,7 @@ jumbo_static_library("browser") {
"//chrome/services/app_service/public/cpp:icon_loader", "//chrome/services/app_service/public/cpp:icon_loader",
"//chrome/services/app_service/public/cpp:intents", "//chrome/services/app_service/public/cpp:intents",
"//chrome/services/app_service/public/cpp:preferred_apps", "//chrome/services/app_service/public/cpp:preferred_apps",
"//chrome/services/sharing/public/mojom",
"//components/feedback", "//components/feedback",
"//components/image_fetcher/core", "//components/image_fetcher/core",
"//components/keep_alive_registry", "//components/keep_alive_registry",
......
...@@ -21,6 +21,7 @@ include_rules = [ ...@@ -21,6 +21,7 @@ include_rules = [
"+chrome/services/file_util/public", "+chrome/services/file_util/public",
"+chrome/services/media_gallery_util/public", "+chrome/services/media_gallery_util/public",
"+chrome/services/printing/public", "+chrome/services/printing/public",
"+chrome/services/sharing/public",
"+chrome/services/removable_storage_writer/public", "+chrome/services/removable_storage_writer/public",
"+chrome/services/util_win/public", "+chrome/services/util_win/public",
"+chromeos", "+chromeos",
......
// 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/sharing/webrtc/ice_config_fetcher.h"
#include "base/bind.h"
#include "base/json/json_reader.h"
#include "base/optional.h"
#include "base/strings/strcat.h"
#include "google_apis/google_api_keys.h"
#include "net/base/load_flags.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
namespace {
const char kIceConfigApiUrl[] =
"https://networktraversal.googleapis.com/v1alpha/iceconfig?key=";
// Response with 2 ice server configs takes ~1KB. A loose upper bound of 16KB is
// chosen to avoid breaking the flow in case the response has longer URLs in ice
// configs.
constexpr int kMaxBodySize = 16 * 1024;
const net::NetworkTrafficAnnotationTag kTrafficAnnotation =
net::DefineNetworkTrafficAnnotation("ice_config_fetcher", R"(
semantics {
sender: "IceConfigFetcher"
description:
"Fetches ice server configurations for p2p webrtc connection as "
"described in "
"https://www.w3.org/TR/webrtc/#rtciceserver-dictionary."
trigger:
"User uses any Chrome cross-device sharing feature and selects one"
" of their devices to send the data to."
data: "No data is sent in the request."
destination: GOOGLE_OWNED_SERVICE
}
policy {
cookies_allowed: NO
setting:
"Users can disable this behavior by signing out of Chrome."
chrome_policy {
BrowserSignin {
policy_options {mode: MANDATORY}
BrowserSignin: 0
}
}
})");
bool IsLoaderSuccessful(const network::SimpleURLLoader* loader) {
if (!loader || loader->NetError() != net::OK)
return false;
if (!loader->ResponseInfo() || !loader->ResponseInfo()->headers)
return false;
// Success response codes are 2xx.
return (loader->ResponseInfo()->headers->response_code() / 100) == 2;
}
} // namespace
IceConfigFetcher::IceConfigFetcher(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: url_loader_factory_(std::move(url_loader_factory)) {}
IceConfigFetcher::~IceConfigFetcher() = default;
void IceConfigFetcher::GetIceServers(IceServerCallback callback) {
url_loader_.reset();
auto resource_request = std::make_unique<network::ResourceRequest>();
resource_request->url =
GURL(base::StrCat({kIceConfigApiUrl, google_apis::GetSharingAPIKey()}));
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.SetHeader(net::HttpRequestHeaders::kContentType,
"application/json");
url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
kTrafficAnnotation);
url_loader_->DownloadToString(
url_loader_factory_.get(),
base::BindOnce(&IceConfigFetcher::OnIceServersResponse,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
kMaxBodySize);
}
void IceConfigFetcher::OnIceServersResponse(
IceServerCallback callback,
std::unique_ptr<std::string> response_body) {
std::vector<sharing::mojom::IceServerPtr> ice_servers;
if (IsLoaderSuccessful(url_loader_.get()) && response_body)
ice_servers = ParseIceConfigJson(*response_body);
if (ice_servers.empty())
ice_servers = GetDefaultIceServers();
std::move(callback).Run(std::move(ice_servers));
}
std::vector<sharing::mojom::IceServerPtr> IceConfigFetcher::ParseIceConfigJson(
std::string json) {
std::vector<sharing::mojom::IceServerPtr> ice_servers;
base::Optional<base::Value> response = base::JSONReader::Read(json);
if (!response)
return ice_servers;
base::Value* ice_servers_json = response->FindListKey("iceServers");
if (!ice_servers_json)
return ice_servers;
for (base::Value& server : ice_servers_json->GetList()) {
const base::Value* urls_json = server.FindListKey("urls");
if (!urls_json)
continue;
std::vector<GURL> urls;
for (const base::Value& url_json : urls_json->GetList()) {
std::string url;
if (!url_json.GetAsString(&url))
continue;
urls.emplace_back(url);
}
if (urls.empty())
continue;
sharing::mojom::IceServerPtr ice_server(sharing::mojom::IceServer::New());
ice_server->urls = std::move(urls);
std::string* retrieved_username = server.FindStringKey("username");
if (retrieved_username)
ice_server->username.emplace(std::move(*retrieved_username));
std::string* retrieved_credential = server.FindStringKey("credential");
if (retrieved_credential)
ice_server->credential.emplace(std::move(*retrieved_credential));
ice_servers.push_back(std::move(ice_server));
}
return ice_servers;
}
// static
std::vector<sharing::mojom::IceServerPtr>
IceConfigFetcher::GetDefaultIceServers() {
sharing::mojom::IceServerPtr ice_server(sharing::mojom::IceServer::New());
ice_server->urls.emplace_back("stun:stun.l.google.com:19302");
ice_server->urls.emplace_back("stun:stun1.l.google.com:19302");
ice_server->urls.emplace_back("stun:stun2.l.google.com:19302");
ice_server->urls.emplace_back("stun:stun3.l.google.com:19302");
ice_server->urls.emplace_back("stun:stun4.l.google.com:19302");
std::vector<sharing::mojom::IceServerPtr> default_servers;
default_servers.push_back(std::move(ice_server));
return default_servers;
}
// 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.
#ifndef CHROME_BROWSER_SHARING_WEBRTC_ICE_CONFIG_FETCHER_H_
#define CHROME_BROWSER_SHARING_WEBRTC_ICE_CONFIG_FETCHER_H_
#include <memory>
#include <string>
#include <vector>
#include "base/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/optional.h"
#include "chrome/services/sharing/public/mojom/webrtc.mojom.h"
#include "url/gurl.h"
namespace network {
class SharedURLLoaderFactory;
class SimpleURLLoader;
} // namespace network
class IceConfigFetcher {
public:
using IceServerCallback =
base::OnceCallback<void(std::vector<sharing::mojom::IceServerPtr>)>;
explicit IceConfigFetcher(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~IceConfigFetcher();
IceConfigFetcher(const IceConfigFetcher& other) = delete;
IceConfigFetcher& operator=(const IceConfigFetcher& other) = delete;
// TODO(himanshujaju) - Cache configs fetched from server.
void GetIceServers(IceServerCallback callback);
private:
void OnIceServersResponse(IceServerCallback callback,
std::unique_ptr<std::string> response_body);
std::vector<sharing::mojom::IceServerPtr> ParseIceConfigJson(
std::string json);
// Returns public ice servers if API fails to respond.
static std::vector<sharing::mojom::IceServerPtr> GetDefaultIceServers();
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
std::unique_ptr<network::SimpleURLLoader> url_loader_;
base::WeakPtrFactory<IceConfigFetcher> weak_ptr_factory_{this};
};
#endif // CHROME_BROWSER_SHARING_WEBRTC_ICE_CONFIG_FETCHER_H_
// 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/sharing/webrtc/ice_config_fetcher.h"
#include "base/files/file_util.h"
#include "base/path_service.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/test/bind_test_util.h"
#include "base/test/task_environment.h"
#include "chrome/common/chrome_paths.h"
#include "google_apis/google_api_keys.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
class IceConfigFetcherTest : public testing::Test {
public:
IceConfigFetcherTest()
: test_shared_loader_factory_(
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_)),
ice_config_fetcher_(test_shared_loader_factory_) {}
~IceConfigFetcherTest() override = default;
std::string GetApiUrl() const {
return base::StrCat(
{"https://networktraversal.googleapis.com/v1alpha/iceconfig?key=",
google_apis::GetSharingAPIKey()});
}
void CheckSuccessResponse(
const std::vector<sharing::mojom::IceServerPtr>& ice_servers) {
ASSERT_EQ(2u, ice_servers.size());
// First response doesnt have credentials.
ASSERT_EQ(1u, ice_servers[0]->urls.size());
ASSERT_FALSE(ice_servers[0]->username);
ASSERT_FALSE(ice_servers[0]->credential);
// Second response has credentials.
ASSERT_EQ(2u, ice_servers[1]->urls.size());
ASSERT_EQ("username", ice_servers[1]->username);
ASSERT_EQ("credential", ice_servers[1]->credential);
}
void GetSuccessResponse(std::string* response) const {
base::FilePath path;
ASSERT_TRUE(base::PathService::Get(chrome::DIR_TEST_DATA, &path));
path = path.AppendASCII("sharing");
ASSERT_TRUE(base::PathExists(path));
ASSERT_TRUE(base::ReadFileToString(
path.AppendASCII("network_traversal_response.json"), response));
}
protected:
base::test::SingleThreadTaskEnvironment task_environment_;
network::TestURLLoaderFactory test_url_loader_factory_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
IceConfigFetcher ice_config_fetcher_;
};
TEST_F(IceConfigFetcherTest, ResponseSuccessful) {
base::RunLoop run_loop;
ice_config_fetcher_.GetIceServers(base::BindLambdaForTesting(
[&](std::vector<sharing::mojom::IceServerPtr> ice_servers) {
CheckSuccessResponse(ice_servers);
run_loop.Quit();
}));
const std::string expected_api_url = GetApiUrl();
std::string response;
GetSuccessResponse(&response);
ASSERT_TRUE(test_url_loader_factory_.IsPending(expected_api_url, nullptr));
test_url_loader_factory_.AddResponse(expected_api_url, response,
net::HTTP_OK);
run_loop.Run();
}
TEST_F(IceConfigFetcherTest, ResponseError) {
base::RunLoop run_loop;
ice_config_fetcher_.GetIceServers(base::BindLambdaForTesting(
[&](std::vector<sharing::mojom::IceServerPtr> ice_servers) {
// Makes sure that we at least return default servers in case of an
// error.
EXPECT_FALSE(ice_servers.empty());
run_loop.Quit();
}));
const std::string expected_api_url = GetApiUrl();
ASSERT_TRUE(test_url_loader_factory_.IsPending(expected_api_url, nullptr));
test_url_loader_factory_.AddResponse(expected_api_url, "",
net::HTTP_INTERNAL_SERVER_ERROR);
run_loop.Run();
}
...@@ -4193,6 +4193,7 @@ test("unit_tests") { ...@@ -4193,6 +4193,7 @@ test("unit_tests") {
"../browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc", "../browser/sharing/shared_clipboard/shared_clipboard_utils_unittest.cc",
"../browser/sharing/sms/sms_fetch_request_handler_unittest.cc", "../browser/sharing/sms/sms_fetch_request_handler_unittest.cc",
"../browser/sharing/sms/sms_remote_fetcher_unittest.cc", "../browser/sharing/sms/sms_remote_fetcher_unittest.cc",
"../browser/sharing/webrtc/ice_config_fetcher_unittest.cc",
"../browser/ui/autofill/payments/local_card_migration_bubble_controller_impl_unittest.cc", "../browser/ui/autofill/payments/local_card_migration_bubble_controller_impl_unittest.cc",
"../browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc", "../browser/ui/autofill/payments/save_card_bubble_controller_impl_unittest.cc",
"../browser/ui/bluetooth/bluetooth_chooser_controller_unittest.cc", "../browser/ui/bluetooth/bluetooth_chooser_controller_unittest.cc",
......
{
"lifetimeDuration": "400s",
"iceServers": [
{
"urls": [
"stun:url1"
]
},
{
"urls": [
"turn:url2?transport=udp",
"turn:url3?transport=tcp"
],
"username": "username",
"credential": "credential",
"maxRateKbps": "1"
}
],
"blockStatus": "NOT_BLOCKED",
"iceTransportPolicy": "all"
}
\ No newline at end of file
...@@ -83,6 +83,11 @@ ...@@ -83,6 +83,11 @@
#define GOOGLE_API_KEY_REMOTING DUMMY_API_TOKEN #define GOOGLE_API_KEY_REMOTING DUMMY_API_TOKEN
#endif #endif
// API key for SharingService.
#if !defined(GOOGLE_API_KEY_SHARING)
#define GOOGLE_API_KEY_SHARING DUMMY_API_TOKEN
#endif
// These are used as shortcuts for developers and users providing // These are used as shortcuts for developers and users providing
// OAuth credentials via preprocessor defines or environment // OAuth credentials via preprocessor defines or environment
// variables. If set, they will be used to replace any of the client // variables. If set, they will be used to replace any of the client
...@@ -126,6 +131,10 @@ class APIKeyCache { ...@@ -126,6 +131,10 @@ class APIKeyCache {
STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_REMOTING), nullptr, std::string(), STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_REMOTING), nullptr, std::string(),
environment.get(), command_line); environment.get(), command_line);
api_key_sharing_ = CalculateKeyValue(
GOOGLE_API_KEY_SHARING, STRINGIZE_NO_EXPANSION(GOOGLE_API_KEY_SHARING),
nullptr, std::string(), environment.get(), command_line);
metrics_key_ = CalculateKeyValue( metrics_key_ = CalculateKeyValue(
GOOGLE_METRICS_SIGNING_KEY, GOOGLE_METRICS_SIGNING_KEY,
STRINGIZE_NO_EXPANSION(GOOGLE_METRICS_SIGNING_KEY), nullptr, STRINGIZE_NO_EXPANSION(GOOGLE_METRICS_SIGNING_KEY), nullptr,
...@@ -195,6 +204,7 @@ class APIKeyCache { ...@@ -195,6 +204,7 @@ class APIKeyCache {
#endif #endif
std::string api_key_non_stable() const { return api_key_non_stable_; } std::string api_key_non_stable() const { return api_key_non_stable_; }
std::string api_key_remoting() const { return api_key_remoting_; } std::string api_key_remoting() const { return api_key_remoting_; }
std::string api_key_sharing() const { return api_key_sharing_; }
std::string metrics_key() const { return metrics_key_; } std::string metrics_key() const { return metrics_key_; }
...@@ -293,6 +303,7 @@ class APIKeyCache { ...@@ -293,6 +303,7 @@ class APIKeyCache {
std::string api_key_; std::string api_key_;
std::string api_key_non_stable_; std::string api_key_non_stable_;
std::string api_key_remoting_; std::string api_key_remoting_;
std::string api_key_sharing_;
std::string metrics_key_; std::string metrics_key_;
std::string client_ids_[CLIENT_NUM_ITEMS]; std::string client_ids_[CLIENT_NUM_ITEMS];
std::string client_secrets_[CLIENT_NUM_ITEMS]; std::string client_secrets_[CLIENT_NUM_ITEMS];
...@@ -317,6 +328,10 @@ std::string GetRemotingAPIKey() { ...@@ -317,6 +328,10 @@ std::string GetRemotingAPIKey() {
return g_api_key_cache.Get().api_key_remoting(); return g_api_key_cache.Get().api_key_remoting();
} }
std::string GetSharingAPIKey() {
return g_api_key_cache.Get().api_key_sharing();
}
#if defined(OS_IOS) #if defined(OS_IOS)
void SetAPIKey(const std::string& api_key) { void SetAPIKey(const std::string& api_key) {
g_api_key_cache.Get().set_api_key(api_key); g_api_key_cache.Get().set_api_key(api_key);
......
...@@ -76,6 +76,9 @@ std::string GetNonStableAPIKey(); ...@@ -76,6 +76,9 @@ std::string GetNonStableAPIKey();
// Retrieves the Chrome Remote Desktop API key. // Retrieves the Chrome Remote Desktop API key.
std::string GetRemotingAPIKey(); std::string GetRemotingAPIKey();
// Retrieves the Sharing API Key.
std::string GetSharingAPIKey();
#if defined(OS_IOS) #if defined(OS_IOS)
// Sets the API key. This should be called as early as possible before this // Sets the API key. This should be called as early as possible before this
// API key is even accessed. // API key is even accessed.
......
...@@ -136,6 +136,7 @@ Refer to README.md for content description and update process. ...@@ -136,6 +136,7 @@ Refer to README.md for content description and update process.
<item id="history_ui_favicon_request_handler_get_favicon" hash_code="17562717" type="0" content_hash_code="64054629" os_list="linux,windows" file_path="components/favicon/core/history_ui_favicon_request_handler_impl.cc"/> <item id="history_ui_favicon_request_handler_get_favicon" hash_code="17562717" type="0" content_hash_code="64054629" os_list="linux,windows" file_path="components/favicon/core/history_ui_favicon_request_handler_impl.cc"/>
<item id="http_server_error_response" hash_code="32197336" type="0" content_hash_code="61082230" os_list="linux,windows" file_path="net/server/http_server.cc"/> <item id="http_server_error_response" hash_code="32197336" type="0" content_hash_code="61082230" os_list="linux,windows" file_path="net/server/http_server.cc"/>
<item id="https_server_previews_navigation" hash_code="35725390" type="0" content_hash_code="84423109" os_list="linux,windows" file_path="chrome/browser/previews/previews_lite_page_redirect_serving_url_loader.cc"/> <item id="https_server_previews_navigation" hash_code="35725390" type="0" content_hash_code="84423109" os_list="linux,windows" file_path="chrome/browser/previews/previews_lite_page_redirect_serving_url_loader.cc"/>
<item id="ice_config_fetcher" hash_code="137093034" type="0" content_hash_code="60051202" os_list="linux,windows" file_path="chrome/browser/sharing/webrtc/ice_config_fetcher.cc"/>
<item id="icon_cacher" hash_code="103133150" type="0" content_hash_code="116368348" os_list="linux,windows" file_path="components/ntp_tiles/icon_cacher_impl.cc"/> <item id="icon_cacher" hash_code="103133150" type="0" content_hash_code="116368348" os_list="linux,windows" file_path="components/ntp_tiles/icon_cacher_impl.cc"/>
<item id="icon_catcher_get_large_icon" hash_code="44494884" type="0" content_hash_code="98262037" os_list="linux,windows" file_path="components/ntp_tiles/icon_cacher_impl.cc"/> <item id="icon_catcher_get_large_icon" hash_code="44494884" type="0" content_hash_code="98262037" os_list="linux,windows" file_path="components/ntp_tiles/icon_cacher_impl.cc"/>
<item id="image_annotation" hash_code="107881858" type="0" content_hash_code="96203979" os_list="linux,windows" file_path="services/image_annotation/annotator.cc"/> <item id="image_annotation" hash_code="107881858" type="0" content_hash_code="96203979" os_list="linux,windows" file_path="services/image_annotation/annotator.cc"/>
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment