diff options
Diffstat (limited to 'chromium/components/copresence/rpc')
-rw-r--r-- | chromium/components/copresence/rpc/http_post.cc | 110 | ||||
-rw-r--r-- | chromium/components/copresence/rpc/http_post.h | 75 | ||||
-rw-r--r-- | chromium/components/copresence/rpc/http_post_unittest.cc | 143 | ||||
-rw-r--r-- | chromium/components/copresence/rpc/rpc_handler.cc | 620 | ||||
-rw-r--r-- | chromium/components/copresence/rpc/rpc_handler.h | 213 | ||||
-rw-r--r-- | chromium/components/copresence/rpc/rpc_handler_unittest.cc | 348 |
6 files changed, 0 insertions, 1509 deletions
diff --git a/chromium/components/copresence/rpc/http_post.cc b/chromium/components/copresence/rpc/http_post.cc deleted file mode 100644 index 63ea7e74d29..00000000000 --- a/chromium/components/copresence/rpc/http_post.cc +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2014 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 "components/copresence/rpc/http_post.h" - -// TODO(ckehoe): Support third-party protobufs too. -#include <google/protobuf/message_lite.h> - -#include "base/bind.h" -#include "google_apis/google_api_keys.h" -#include "net/base/load_flags.h" -#include "net/base/net_errors.h" -#include "net/base/url_util.h" -#include "net/http/http_status_code.h" -#include "net/url_request/url_fetcher.h" -#include "net/url_request/url_request_context_getter.h" -#include "url/gurl.h" - -namespace copresence { - -const char HttpPost::kApiKeyField[] = "key"; -const char HttpPost::kTracingField[] = "trace"; - -HttpPost::HttpPost(net::URLRequestContextGetter* url_context_getter, - const std::string& server_host, - const std::string& rpc_name, - std::string api_key, - const std::string& auth_token, - const std::string& tracing_token, - const google::protobuf::MessageLite& request_proto) { - // Create the base URL to call. - GURL url(server_host + "/" + rpc_name); - - // Add the tracing token, if specified. - if (!tracing_token.empty()) { - url = net::AppendQueryParameter( - url, kTracingField, "token:" + tracing_token); - } - - // If we have no auth token, authenticate using the API key. - // If no API key is specified, use the Chrome API key. - if (auth_token.empty()) { - if (api_key.empty()) { -#ifdef GOOGLE_CHROME_BUILD - DCHECK(google_apis::HasKeysConfigured()); - api_key = google_apis::GetAPIKey(); -#else - LOG(ERROR) << "No Copresence API key provided"; -#endif - } - url = net::AppendQueryParameter(url, kApiKeyField, api_key); - } - - // Serialize the proto for transmission. - std::string request_data; - bool serialize_success = request_proto.SerializeToString(&request_data); - DCHECK(serialize_success); - - // Configure and send the request. - url_fetcher_ = - net::URLFetcher::Create(kUrlFetcherId, url, net::URLFetcher::POST, this); - url_fetcher_->SetRequestContext(url_context_getter); - url_fetcher_->SetLoadFlags(net::LOAD_BYPASS_CACHE | - net::LOAD_DISABLE_CACHE | - net::LOAD_DO_NOT_SAVE_COOKIES | - net::LOAD_DO_NOT_SEND_COOKIES | - net::LOAD_DO_NOT_SEND_AUTH_DATA); - url_fetcher_->SetUploadData("application/x-protobuf", request_data); - if (!auth_token.empty()) { - url_fetcher_->AddExtraRequestHeader("Authorization: Bearer " + auth_token); - } -} - -HttpPost::~HttpPost() {} - -void HttpPost::Start(const ResponseCallback& response_callback) { - response_callback_ = response_callback; - DVLOG(3) << "Sending Copresence request to " - << url_fetcher_->GetOriginalURL().spec(); - url_fetcher_->Start(); -} - -void HttpPost::OnURLFetchComplete(const net::URLFetcher* source) { - DCHECK_EQ(url_fetcher_.get(), source); - - // Gather response info. - std::string response; - source->GetResponseAsString(&response); - int response_code = source->GetResponseCode(); - - // Log any errors. - if (response_code < 0) { - net::URLRequestStatus status = source->GetStatus(); - LOG(WARNING) << "Couldn't contact the Copresence server at " - << source->GetURL() << ". Status code " << status.status(); - LOG_IF(WARNING, status.error()) - << "Network error: " << net::ErrorToString(status.error()); - LOG_IF(WARNING, !response.empty()) << "HTTP response: " << response; - } else if (response_code != net::HTTP_OK) { - LOG(WARNING) << "Copresence request got HTTP response code " - << response_code << ". Response:\n" << response; - } - - // Return the response. - response_callback_.Run(response_code, response); - delete this; -} - -} // namespace copresence diff --git a/chromium/components/copresence/rpc/http_post.h b/chromium/components/copresence/rpc/http_post.h deleted file mode 100644 index 911bc5f03a3..00000000000 --- a/chromium/components/copresence/rpc/http_post.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2014 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 COMPONENTS_COPRESENCE_RPC_HTTP_POST_H_ -#define COMPONENTS_COPRESENCE_RPC_HTTP_POST_H_ - -#include <memory> -#include <string> - -#include "base/callback.h" -#include "base/macros.h" -#include "net/url_request/url_fetcher_delegate.h" - -namespace google { -namespace protobuf { -class MessageLite; -} -} - -namespace net { -class URLRequestContextGetter; -} - -namespace copresence { - -// This class handles all Apiary calls to the Copresence server. -// It configures the HTTP request appropriately and reports any errors. -// If deleted, the HTTP request is cancelled. -// -// TODO(ckehoe): Add retry logic. -class HttpPost : public net::URLFetcherDelegate { - public: - // Callback to receive the HTTP status code and body of the response - // (if any). A pointer to this HttpPost object is also passed along. - using ResponseCallback = base::Callback<void(int, const std::string&)>; - - // Create a request to the Copresence server. - // |url_context_getter| is owned by the caller, - // and the context it provides must be available until the request completes. - HttpPost(net::URLRequestContextGetter* url_context_getter, - const std::string& server_host, - // TODO(ckehoe): Condense some of these into a struct. - const std::string& rpc_name, - std::string api_key, // If blank, we overwrite with a default. - const std::string& auth_token, - const std::string& tracing_token, - const google::protobuf::MessageLite& request_proto); - - // HTTP requests are cancelled on delete. - ~HttpPost() override; - - // Send an HttpPost request. - void Start(const ResponseCallback& response_callback); - - private: - static const int kUrlFetcherId = 1; - static const char kApiKeyField[]; - static const char kTracingField[]; - - friend class HttpPostTest; - - // Overridden from net::URLFetcherDelegate. - void OnURLFetchComplete(const net::URLFetcher* source) override; - - ResponseCallback response_callback_; - - std::unique_ptr<net::URLFetcher> url_fetcher_; - - DISALLOW_COPY_AND_ASSIGN(HttpPost); -}; - -} // namespace copresence - -#endif // COMPONENTS_COPRESENCE_RPC_HTTP_POST_H_ diff --git a/chromium/components/copresence/rpc/http_post_unittest.cc b/chromium/components/copresence/rpc/http_post_unittest.cc deleted file mode 100644 index 92bc5808ffa..00000000000 --- a/chromium/components/copresence/rpc/http_post_unittest.cc +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2014 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 "components/copresence/rpc/http_post.h" - -#include "base/test/test_simple_task_runner.h" -#include "components/copresence/proto/data.pb.h" -#include "net/base/url_util.h" -#include "net/http/http_status_code.h" -#include "net/url_request/test_url_fetcher_factory.h" -#include "net/url_request/url_request_test_util.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "url/gurl.h" - -namespace { - -const char kFakeServerHost[] = "test.server.google.com"; -const char kRPCName[] = "testRpc"; -const char kTracingToken[] = "trace me!"; -const char kApiKey[] = "unlock ALL the APIz"; -const char kAuthToken[] = "oogabooga"; - -} // namespace - -using google::protobuf::MessageLite; - -namespace copresence { - -class HttpPostTest : public testing::Test { - public: - HttpPostTest() { - context_getter_ = new net::TestURLRequestContextGetter( - make_scoped_refptr(new base::TestSimpleTaskRunner)); - proto_.set_client("test_client"); - proto_.set_version_code(123); - } - ~HttpPostTest() override {} - - // Check that the correct response was sent. - void TestResponseCallback(int expected_response_code, - const std::string& expected_response, - int actual_response_code, - const std::string& actual_response) { - CHECK_EQ(expected_response_code, actual_response_code); - CHECK_EQ(expected_response, actual_response); - } - - protected: - void CheckPassthrough(int response_code, const std::string& response) { - HttpPost* post = new HttpPost( - context_getter_.get(), std::string("http://") + kFakeServerHost, - kRPCName, kApiKey, - "", // auth token - "", // tracing token - proto_); - post->Start(base::Bind(&HttpPostTest::TestResponseCallback, - base::Unretained(this), - response_code, - response)); - - net::TestURLFetcher* fetcher = fetcher_factory_.GetFetcherByID( - HttpPost::kUrlFetcherId); - fetcher->set_response_code(response_code); - fetcher->SetResponseString(response); - fetcher->delegate()->OnURLFetchComplete(fetcher); - } - - net::TestURLFetcher* GetFetcher() { - return fetcher_factory_.GetFetcherByID(HttpPost::kUrlFetcherId); - } - - const std::string GetApiKeySent() { - std::string api_key_sent; - net::GetValueForKeyInQuery(GetFetcher()->GetOriginalURL(), - HttpPost::kApiKeyField, - &api_key_sent); - return api_key_sent; - } - - const std::string GetAuthHeaderSent() { - net::HttpRequestHeaders headers; - std::string header; - GetFetcher()->GetExtraRequestHeaders(&headers); - return headers.GetHeader("Authorization", &header) ? header : ""; - } - - const std::string GetTracingTokenSent() { - std::string tracing_token_sent; - net::GetValueForKeyInQuery(GetFetcher()->GetOriginalURL(), - HttpPost::kTracingField, - &tracing_token_sent); - return tracing_token_sent; - } - - net::TestURLFetcherFactory fetcher_factory_; - scoped_refptr<net::TestURLRequestContextGetter> context_getter_; - - ClientVersion proto_; -}; - -TEST_F(HttpPostTest, OKResponse) { - // "Send" the proto to the "server". - HttpPost* post = new HttpPost(context_getter_.get(), - std::string("http://") + kFakeServerHost, - kRPCName, - kApiKey, - kAuthToken, - kTracingToken, - proto_); - post->Start(base::Bind(&HttpPostTest::TestResponseCallback, - base::Unretained(this), - net::HTTP_OK, - "Hello World!")); - - // Verify that the data was sent to the right place. - GURL requested_url = GetFetcher()->GetOriginalURL(); - EXPECT_EQ(kFakeServerHost, requested_url.host()); - EXPECT_EQ(std::string("/") + kRPCName, requested_url.path()); - - // Check parameters. - EXPECT_EQ("", GetApiKeySent()); // No API key when using an auth token. - EXPECT_EQ(std::string("Bearer ") + kAuthToken, GetAuthHeaderSent()); - EXPECT_EQ(std::string("token:") + kTracingToken, GetTracingTokenSent()); - - // Verify that the right data was sent. - std::string upload_data; - ASSERT_TRUE(proto_.SerializeToString(&upload_data)); - EXPECT_EQ(upload_data, GetFetcher()->upload_data()); - - // Send a response and check that it's passed along correctly. - GetFetcher()->set_response_code(net::HTTP_OK); - GetFetcher()->SetResponseString("Hello World!"); - GetFetcher()->delegate()->OnURLFetchComplete(GetFetcher()); -} - -TEST_F(HttpPostTest, ErrorResponse) { - CheckPassthrough(net::HTTP_BAD_REQUEST, "Bad client. Shame on you."); - CheckPassthrough(net::HTTP_INTERNAL_SERVER_ERROR, "I'm dying. Forgive me."); - CheckPassthrough(-1, ""); -} - -} // namespace copresence diff --git a/chromium/components/copresence/rpc/rpc_handler.cc b/chromium/components/copresence/rpc/rpc_handler.cc deleted file mode 100644 index 58eec73d316..00000000000 --- a/chromium/components/copresence/rpc/rpc_handler.cc +++ /dev/null @@ -1,620 +0,0 @@ -// Copyright 2015 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 "components/copresence/rpc/rpc_handler.h" - -#include <stddef.h> -#include <stdint.h> - -#include <utility> - -#include "base/bind.h" -#include "base/command_line.h" -#include "base/logging.h" -#include "base/memory/ptr_util.h" -#include "base/strings/string_util.h" -#include "base/strings/stringprintf.h" -// TODO(ckehoe): time.h includes windows.h, which #defines DeviceCapabilities -// to DeviceCapabilitiesW. This breaks the pb.h headers below. For now, -// we fix this with an #undef. -#include "base/time/time.h" -#include "build/build_config.h" -#if defined(OS_WIN) -#undef DeviceCapabilities -#endif - -#include "components/audio_modem/public/audio_modem_types.h" -#include "components/copresence/copresence_state_impl.h" -#include "components/copresence/copresence_switches.h" -#include "components/copresence/handlers/directive_handler.h" -#include "components/copresence/handlers/gcm_handler.h" -#include "components/copresence/proto/codes.pb.h" -#include "components/copresence/proto/data.pb.h" -#include "components/copresence/proto/rpcs.pb.h" -#include "components/copresence/public/copresence_constants.h" -#include "components/copresence/public/copresence_delegate.h" -#include "components/copresence/rpc/http_post.h" -#include "net/http/http_status_code.h" - -using google::protobuf::MessageLite; - -using audio_modem::AUDIBLE; -using audio_modem::AudioToken; -using audio_modem::INAUDIBLE; - -// TODO(ckehoe): Return error messages for bad requests. - -namespace copresence { - -const char RpcHandler::kReportRequestRpcName[] = "report"; - -namespace { - -const int kTokenLoggingSuffix = 5; -const int kInvalidTokenExpiryTimeMinutes = 10; -const int kMaxInvalidTokens = 10000; -const char kRegisterDeviceRpcName[] = "registerdevice"; -const char kDefaultCopresenceServer[] = - "https://www.googleapis.com/copresence/v2/copresence"; - -// UrlSafe is defined as: -// '/' represented by a '_' and '+' represented by a '-' -// TODO(rkc): Move this to the wrapper. -std::string ToUrlSafe(std::string token) { - base::ReplaceChars(token, "+", "-", &token); - base::ReplaceChars(token, "/", "_", &token); - return token; -} - -// Logging - -// Checks for a copresence error. If there is one, logs it and returns true. -bool IsErrorStatus(const Status& status) { - if (status.code() != OK) { - LOG(ERROR) << "Copresence error code " << status.code() - << (status.message().empty() ? "" : ": " + status.message()); - } - return status.code() != OK; -} - -void LogIfErrorStatus(const util::error::Code& code, - const std::string& context) { - LOG_IF(ERROR, code != util::error::OK) - << context << " error " << code << ". See " - << "cs/google3/util/task/codes.proto for more info."; -} - -// If any errors occurred, logs them and returns true. -bool ReportErrorLogged(const ReportResponse& response) { - bool result = IsErrorStatus(response.header().status()); - - // The Report fails or succeeds as a unit. If any responses had errors, - // the header will too. Thus we don't need to propagate individual errors. - if (response.has_update_signals_response()) - LogIfErrorStatus(response.update_signals_response().status(), "Update"); - if (response.has_manage_messages_response()) - LogIfErrorStatus(response.manage_messages_response().status(), "Publish"); - if (response.has_manage_subscriptions_response()) { - LogIfErrorStatus(response.manage_subscriptions_response().status(), - "Subscribe"); - } - - return result; -} - -const std::string LoggingStrForToken(const std::string& auth_token) { - if (auth_token.empty()) - return "anonymous"; - - std::string token_suffix = auth_token.substr( - auth_token.length() - kTokenLoggingSuffix, kTokenLoggingSuffix); - return "token ..." + token_suffix; -} - - -// Request construction - -template <typename T> -BroadcastScanConfiguration GetBroadcastScanConfig(const T& msg) { - if (msg.has_token_exchange_strategy() && - msg.token_exchange_strategy().has_broadcast_scan_configuration()) { - return msg.token_exchange_strategy().broadcast_scan_configuration(); - } - return BROADCAST_SCAN_CONFIGURATION_UNKNOWN; -} - -std::unique_ptr<DeviceState> GetDeviceCapabilities( - const ReportRequest& request) { - std::unique_ptr<DeviceState> state(new DeviceState); - - TokenTechnology* ultrasound = - state->mutable_capabilities()->add_token_technology(); - ultrasound->set_medium(AUDIO_ULTRASOUND_PASSBAND); - ultrasound->add_instruction_type(TRANSMIT); - ultrasound->add_instruction_type(RECEIVE); - - TokenTechnology* audible = - state->mutable_capabilities()->add_token_technology(); - audible->set_medium(AUDIO_AUDIBLE_DTMF); - audible->add_instruction_type(TRANSMIT); - audible->add_instruction_type(RECEIVE); - - return state; -} - -// TODO(ckehoe): We're keeping this code in a separate function for now -// because we get a version string from Chrome, but the proto expects -// an int64_t version. We should probably change the version proto -// to handle a more detailed version. -ClientVersion* CreateVersion(const std::string& client, - const std::string& version_name) { - ClientVersion* version = new ClientVersion; - version->set_client(client); - version->set_version_name(version_name); - return version; -} - -void AddTokenToRequest(const AudioToken& token, ReportRequest* request) { - TokenObservation* token_observation = - request->mutable_update_signals_request()->add_token_observation(); - token_observation->set_token_id(ToUrlSafe(token.token)); - - TokenSignals* signals = token_observation->add_signals(); - signals->set_medium(token.audible ? AUDIO_AUDIBLE_DTMF - : AUDIO_ULTRASOUND_PASSBAND); - signals->set_observed_time_millis(base::Time::Now().ToJsTime()); -} - -} // namespace - - -// Public functions. - -RpcHandler::RpcHandler(CopresenceDelegate* delegate, - DirectiveHandler* directive_handler, - CopresenceStateImpl* state, - GCMHandler* gcm_handler, - const MessagesCallback& new_messages_callback, - const PostCallback& server_post_callback) - : delegate_(delegate), - directive_handler_(directive_handler), - state_(state), - gcm_handler_(gcm_handler), - new_messages_callback_(new_messages_callback), - server_post_callback_(server_post_callback), - invalid_audio_token_cache_( - base::TimeDelta::FromMinutes(kInvalidTokenExpiryTimeMinutes), - kMaxInvalidTokens) { - DCHECK(delegate_); - DCHECK(directive_handler_); - // |gcm_handler_| is optional. - - if (server_post_callback_.is_null()) { - server_post_callback_ = - base::Bind(&RpcHandler::SendHttpPost, base::Unretained(this)); - } - - if (gcm_handler_) { - gcm_handler_->GetGcmId( - base::Bind(&RpcHandler::RegisterGcmId, base::Unretained(this))); - } - } - -RpcHandler::~RpcHandler() { - // TODO(ckehoe): Cancel the GCM callback? - for (HttpPost* post : pending_posts_) - delete post; -} - -void RpcHandler::SendReportRequest(std::unique_ptr<ReportRequest> request, - const std::string& app_id, - const std::string& auth_token, - const StatusCallback& status_callback) { - DCHECK(request.get()); - - // Check that the app, if any, has some kind of authentication token. - // Don't allow it to piggyback on Chrome's credentials. - if (!app_id.empty() && delegate_->GetAPIKey(app_id).empty() && - auth_token.empty()) { - LOG(ERROR) << "App " << app_id << " has no API key or auth token"; - status_callback.Run(FAIL); - return; - } - - // Store just one auth token since we should have only one account - // per instance of the copresence component. - // TODO(ckehoe): We may eventually need to support multiple auth tokens. - const bool authenticated = !auth_token.empty(); - if (authenticated && auth_token != auth_token_) { - LOG_IF(ERROR, !auth_token_.empty()) - << "Overwriting old auth token: " << LoggingStrForToken(auth_token); - auth_token_ = auth_token; - } - - // Check that we have a "device" registered for this authentication state. - bool queue_request; - const std::string device_id = delegate_->GetDeviceId(authenticated); - if (device_id.empty()) { - queue_request = true; - if (pending_registrations_.count(authenticated) == 0) - RegisterDevice(authenticated); - // else, registration is already in progress. - } else { - queue_request = false; - } - - // We're not registered, or registration is in progress. - if (queue_request) { - pending_requests_queue_.push_back(new PendingRequest( - std::move(request), app_id, authenticated, status_callback)); - return; - } - - DVLOG(3) << "Sending ReportRequest to server."; - - // If we are unpublishing or unsubscribing, we need to stop those publish or - // subscribes right away, we don't need to wait for the server to tell us. - ProcessRemovedOperations(*request); - - request->mutable_update_signals_request()->set_allocated_state( - GetDeviceCapabilities(*request).release()); - - AddPlayingTokens(request.get()); - - request->set_allocated_header(CreateRequestHeader(app_id, device_id)); - server_post_callback_.Run( - delegate_->GetRequestContext(), kReportRequestRpcName, - delegate_->GetAPIKey(app_id), auth_token, - base::WrapUnique<MessageLite>(request.release()), - // On destruction, this request will be cancelled. - base::Bind(&RpcHandler::ReportResponseHandler, base::Unretained(this), - status_callback)); -} - -void RpcHandler::ReportTokens(const std::vector<AudioToken>& tokens) { - DCHECK(!tokens.empty()); - - std::unique_ptr<ReportRequest> request(new ReportRequest); - for (const AudioToken& token : tokens) { - if (invalid_audio_token_cache_.HasKey(ToUrlSafe(token.token))) - continue; - DVLOG(3) << "Sending token " << token.token << " to server"; - AddTokenToRequest(token, request.get()); - } - - ReportOnAllDevices(std::move(request)); -} - - -// Private functions. - -RpcHandler::PendingRequest::PendingRequest( - std::unique_ptr<ReportRequest> report, - const std::string& app_id, - bool authenticated, - const StatusCallback& callback) - : report(std::move(report)), - app_id(app_id), - authenticated(authenticated), - callback(callback) {} - -RpcHandler::PendingRequest::~PendingRequest() {} - -void RpcHandler::RegisterDevice(const bool authenticated) { - DVLOG(2) << "Sending " << (authenticated ? "authenticated" : "anonymous") - << " registration to server."; - - std::unique_ptr<RegisterDeviceRequest> request(new RegisterDeviceRequest); - - // Add a GCM ID for authenticated registration, if we have one. - if (!authenticated || gcm_id_.empty()) { - request->mutable_push_service()->set_service(PUSH_SERVICE_NONE); - } else { - DVLOG(2) << "Registering GCM ID with " << LoggingStrForToken(auth_token_); - request->mutable_push_service()->set_service(GCM); - request->mutable_push_service()->mutable_gcm_registration() - ->set_device_token(gcm_id_); - } - - // Only identify as a Chrome device if we're in anonymous mode. - // Authenticated calls come from a "GAIA device". - if (!authenticated) { - // Make sure this isn't a duplicate anonymous registration. - // Duplicate authenticated registrations are allowed, to update the GCM ID. - DCHECK(delegate_->GetDeviceId(false).empty()) - << "Attempted anonymous re-registration"; - - Identity* identity = - request->mutable_device_identifiers()->mutable_registrant(); - identity->set_type(CHROME); - } - - bool gcm_pending = authenticated && gcm_handler_ && gcm_id_.empty(); - pending_registrations_.insert(authenticated); - request->set_allocated_header(CreateRequestHeader( - // The device is empty on first registration. - // When re-registering to pass on the GCM ID, it will be present. - std::string(), delegate_->GetDeviceId(authenticated))); - if (authenticated) - DCHECK(!auth_token_.empty()); - server_post_callback_.Run( - delegate_->GetRequestContext(), kRegisterDeviceRpcName, std::string(), - authenticated ? auth_token_ : std::string(), - base::WrapUnique<MessageLite>(request.release()), - // On destruction, this request will be cancelled. - base::Bind(&RpcHandler::RegisterResponseHandler, base::Unretained(this), - authenticated, gcm_pending)); -} - -void RpcHandler::ProcessQueuedRequests(const bool authenticated) { - // If there is no device ID for this auth state, registration failed. - bool registration_failed = delegate_->GetDeviceId(authenticated).empty(); - - // We momentarily take ownership of all the pointers in the queue. - // They are either deleted here or passed on to a new queue. - ScopedVector<PendingRequest> requests_being_processed; - std::swap(requests_being_processed, pending_requests_queue_); - for (PendingRequest* request : requests_being_processed) { - if (request->authenticated == authenticated) { - if (registration_failed) { - request->callback.Run(FAIL); - } else { - if (request->authenticated) - DCHECK(!auth_token_.empty()); - SendReportRequest(std::move(request->report), request->app_id, - request->authenticated ? auth_token_ : std::string(), - request->callback); - } - delete request; - } else { - // The request is in a different auth state. - pending_requests_queue_.push_back(request); - } - } - - // Only keep the requests that weren't processed. - // All the pointers in the queue are now spoken for. - requests_being_processed.weak_clear(); -} - -void RpcHandler::ReportOnAllDevices(std::unique_ptr<ReportRequest> request) { - std::vector<bool> auth_states; - if (!auth_token_.empty() && !delegate_->GetDeviceId(true).empty()) - auth_states.push_back(true); - if (!delegate_->GetDeviceId(false).empty()) - auth_states.push_back(false); - if (auth_states.empty()) { - VLOG(2) << "Skipping reporting because no device IDs are registered"; - return; - } - - for (bool authenticated : auth_states) { - SendReportRequest( - base::WrapUnique(new ReportRequest(*request)), std::string(), - authenticated ? auth_token_ : std::string(), StatusCallback()); - } -} - -// Store a GCM ID and send it to the server if needed. The constructor passes -// this callback to the GCMHandler to receive the ID whenever it's ready. -// It may be returned immediately, if the ID is cached, or require a server -// round-trip. This ID must then be passed along to the copresence server. -// There are a few ways this can happen: -// -// 1. The GCM ID is available when we first register, and is passed along -// with the RegisterDeviceRequest. -// -// 2. The GCM ID becomes available after the RegisterDeviceRequest has -// completed. Then this function will invoke RegisterDevice() -// again to pass on the ID. -// -// 3. The GCM ID becomes available after the RegisterDeviceRequest is sent, -// but before it completes. In this case, the gcm_pending flag is passed -// through to the RegisterResponseHandler, which invokes RegisterDevice() -// again to pass on the ID. This function must skip pending registrations, -// as the device ID will be empty. -// -// TODO(ckehoe): Add tests for these scenarios. -void RpcHandler::RegisterGcmId(const std::string& gcm_id) { - gcm_id_ = gcm_id; - if (!gcm_id.empty()) { - const std::string& device_id = delegate_->GetDeviceId(true); - if (!auth_token_.empty() && !device_id.empty()) - RegisterDevice(true); - } -} - -void RpcHandler::RegisterResponseHandler( - bool authenticated, - bool gcm_pending, - HttpPost* completed_post, - int http_status_code, - const std::string& response_data) { - if (completed_post) { - size_t elements_erased = pending_posts_.erase(completed_post); - DCHECK_GT(elements_erased, 0u); - } - - size_t registrations_completed = pending_registrations_.erase(authenticated); - DCHECK_GT(registrations_completed, 0u); - - RegisterDeviceResponse response; - const std::string token_str = - LoggingStrForToken(authenticated ? auth_token_ : std::string()); - if (http_status_code != net::HTTP_OK) { - // TODO(ckehoe): Retry registration if appropriate. - LOG(ERROR) << token_str << " device registration failed"; - } else if (!response.ParseFromString(response_data)) { - LOG(ERROR) << "Invalid RegisterDeviceResponse:\n" << response_data; - } else if (!IsErrorStatus(response.header().status())) { - const std::string& device_id = response.registered_device_id(); - DCHECK(!device_id.empty()); - delegate_->SaveDeviceId(authenticated, device_id); - DVLOG(2) << token_str << " device registration successful. Id: " - << device_id; - - // If we have a GCM ID now, and didn't before, pass it on to the server. - if (gcm_pending && !gcm_id_.empty()) - RegisterDevice(authenticated); - } - - // Send or fail requests on this auth token. - ProcessQueuedRequests(authenticated); -} - -void RpcHandler::ReportResponseHandler(const StatusCallback& status_callback, - HttpPost* completed_post, - int http_status_code, - const std::string& response_data) { - if (completed_post) { - size_t elements_erased = pending_posts_.erase(completed_post); - DCHECK_GT(elements_erased, 0u); - } - - if (http_status_code != net::HTTP_OK) { - if (!status_callback.is_null()) - status_callback.Run(FAIL); - return; - } - - DVLOG(3) << "Received ReportResponse."; - ReportResponse response; - if (!response.ParseFromString(response_data)) { - LOG(ERROR) << "Invalid ReportResponse"; - if (!status_callback.is_null()) - status_callback.Run(FAIL); - return; - } - - if (ReportErrorLogged(response)) { - if (!status_callback.is_null()) - status_callback.Run(FAIL); - return; - } - - for (const MessageResult& result : - response.manage_messages_response().published_message_result()) { - DVLOG(2) << "Published message with id " << result.published_message_id(); - } - - for (const SubscriptionResult& result : - response.manage_subscriptions_response().subscription_result()) { - DVLOG(2) << "Created subscription with id " << result.subscription_id(); - } - - if (response.has_update_signals_response()) { - const UpdateSignalsResponse& update_response = - response.update_signals_response(); - new_messages_callback_.Run(update_response.message()); - - for (const Directive& directive : update_response.directive()) - directive_handler_->AddDirective(directive); - - for (const Token& token : update_response.token()) { - if (state_) - state_->UpdateTokenStatus(token.id(), token.status()); - switch (token.status()) { - case VALID: - // TODO(rkc/ckehoe): Store the token in a |valid_token_cache_| with a - // short TTL (like 10s) and send it up with every report request. - // Then we'll still get messages while we're waiting to hear it again. - VLOG(1) << "Got valid token " << token.id(); - break; - case INVALID: - DVLOG(3) << "Discarding invalid token " << token.id(); - invalid_audio_token_cache_.Add(token.id(), true); - break; - default: - DVLOG(2) << "Token " << token.id() << " has status code " - << token.status(); - } - } - } - - // TODO(ckehoe): Return a more detailed status response. - if (!status_callback.is_null()) - status_callback.Run(SUCCESS); -} - -void RpcHandler::ProcessRemovedOperations(const ReportRequest& request) { - // Remove unpublishes. - if (request.has_manage_messages_request()) { - for (const std::string& unpublish : - request.manage_messages_request().id_to_unpublish()) { - directive_handler_->RemoveDirectives(unpublish); - } - } - - // Remove unsubscribes. - if (request.has_manage_subscriptions_request()) { - for (const std::string& unsubscribe : - request.manage_subscriptions_request().id_to_unsubscribe()) { - directive_handler_->RemoveDirectives(unsubscribe); - } - } -} - -void RpcHandler::AddPlayingTokens(ReportRequest* request) { - const std::string& audible_token = - directive_handler_->GetCurrentAudioToken(AUDIBLE); - const std::string& inaudible_token = - directive_handler_->GetCurrentAudioToken(INAUDIBLE); - - if (!audible_token.empty()) - AddTokenToRequest(AudioToken(audible_token, true), request); - if (!inaudible_token.empty()) - AddTokenToRequest(AudioToken(inaudible_token, false), request); -} - -// TODO(ckehoe): Pass in the version string and -// group this with the local functions up top. -RequestHeader* RpcHandler::CreateRequestHeader( - const std::string& app_id, - const std::string& device_id) const { - RequestHeader* header = new RequestHeader; - - header->set_allocated_framework_version(CreateVersion( - "Chrome", delegate_->GetPlatformVersionString())); - if (!app_id.empty()) - header->set_allocated_client_version(CreateVersion(app_id, std::string())); - header->set_current_time_millis(base::Time::Now().ToJsTime()); - if (!device_id.empty()) - header->set_registered_device_id(device_id); - - DeviceFingerprint* fingerprint = new DeviceFingerprint; - fingerprint->set_platform_version(delegate_->GetPlatformVersionString()); - fingerprint->set_type(CHROME_PLATFORM_TYPE); - header->set_allocated_device_fingerprint(fingerprint); - - return header; -} - -void RpcHandler::SendHttpPost(net::URLRequestContextGetter* url_context_getter, - const std::string& rpc_name, - const std::string& api_key, - const std::string& auth_token, - std::unique_ptr<MessageLite> request_proto, - const PostCleanupCallback& callback) { - // Create the base URL to call. - base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - const std::string copresence_server_host = - command_line->HasSwitch(switches::kCopresenceServer) ? - command_line->GetSwitchValueASCII(switches::kCopresenceServer) : - kDefaultCopresenceServer; - - // Create the request and keep a pointer until it completes. - HttpPost* http_post = new HttpPost( - url_context_getter, - copresence_server_host, - rpc_name, - api_key, - auth_token, - command_line->GetSwitchValueASCII(switches::kCopresenceTracingToken), - *request_proto); - - http_post->Start(base::Bind(callback, http_post)); - pending_posts_.insert(http_post); -} - -} // namespace copresence diff --git a/chromium/components/copresence/rpc/rpc_handler.h b/chromium/components/copresence/rpc/rpc_handler.h deleted file mode 100644 index 16fd1184051..00000000000 --- a/chromium/components/copresence/rpc/rpc_handler.h +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2015 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 COMPONENTS_COPRESENCE_RPC_RPC_HANDLER_H_ -#define COMPONENTS_COPRESENCE_RPC_RPC_HANDLER_H_ - -#include <map> -#include <memory> -#include <set> -#include <string> -#include <vector> - -#include "base/callback_forward.h" -#include "base/memory/scoped_vector.h" -#include "components/audio_modem/public/audio_modem_types.h" -#include "components/copresence/proto/enums.pb.h" -#include "components/copresence/public/copresence_constants.h" -#include "components/copresence/public/copresence_delegate.h" -#include "components/copresence/timed_map.h" - -namespace copresence { - -class CopresenceDelegate; -class CopresenceStateImpl; -class DirectiveHandler; -class GCMHandler; -class HttpPost; -class ReportRequest; -class RequestHeader; -class SubscribedMessage; - -// This class handles all communication with the copresence server. -// Clients provide a ReportRequest proto containing publishes, subscribes, -// and token observations they want to send to the server. The RpcHandler -// will fill in details like the RequestHeader and DeviceCapabilities, -// and dispatch the results of the server call to the appropriate parts -// of the system. -// -// To create an RpcHandler, clients will need to provide a few other classes -// that support its functionality. Notable among them is the CopresenceDelegate, -// an interface clients must implement to provide settings and functionality -// that may depend on the environment. See the definition in -// //components/copresence/public/copresence_delegate.h. -// -// Here is an example of creating and using an RpcHandler. -// The GCMHandler and CopresenceStateImpl are optional. -// -// MyDelegate delegate(...); -// copresence::DirectiveHandlerImpl directive_handler; -// -// RpcHandler handler(&delegate, -// &directive_handler, -// nullptr, -// nullptr, -// base::Bind(&HandleMessages)); -// -// std::unique_ptr<ReportRequest> request(new ReportRequest); -// (Fill in ReportRequest.) -// -// handler.SendReportRequest(std::move(request), -// "my_app_id", -// "", -// base::Bind(&HandleStatus)); -// -// The server will respond with directives, which get passed to the -// DirectiveHandlerImpl. -// -// Tokens from the audio modem should also be forwarded -// via ReportTokens() so that messages get delivered properly. -class RpcHandler final { - public: - // An HttpPost::ResponseCallback along with an HttpPost object to be deleted. - // Arguments: - // HttpPost*: The handler should take ownership of (i.e. delete) this object. - // int: The HTTP status code of the response. - // string: The contents of the response. - using PostCleanupCallback = base::Callback<void(HttpPost*, - int, - const std::string&)>; - - // Callback to allow tests to stub out HTTP POST behavior. - // Arguments: - // URLRequestContextGetter: Context for the HTTP POST request. - // string: Name of the rpc to invoke. URL format: server.google.com/rpc_name - // string: The API key to pass in the request. - // string: The auth token to pass with the request. - // MessageLite: Contents of POST request to be sent. This needs to be - // a (scoped) pointer to ease handling of the abstract MessageLite class. - // PostCleanupCallback: Receives the response to the request. - using PostCallback = - base::Callback<void(net::URLRequestContextGetter*, - const std::string&, - const std::string&, - const std::string&, - std::unique_ptr<google::protobuf::MessageLite>, - const PostCleanupCallback&)>; - - // Report rpc name to send to Apiary. - static const char kReportRequestRpcName[]; - - // Constructor. The CopresenceStateImpl and GCMHandler may be null. - // The first four parameters are owned by the caller and (if not null) - // must outlive the RpcHandler. - RpcHandler(CopresenceDelegate* delegate, - DirectiveHandler* directive_handler, - CopresenceStateImpl* state, - GCMHandler* gcm_handler, - const MessagesCallback& new_messages_callback, - const PostCallback& server_post_callback = PostCallback()); - - // Not copyable. - RpcHandler(const RpcHandler&) = delete; - void operator=(const RpcHandler&) = delete; - - ~RpcHandler(); - - // Sends a ReportRequest from a specific app, and get notified of completion. - void SendReportRequest(std::unique_ptr<ReportRequest> request, - const std::string& app_id, - const std::string& auth_token, - const StatusCallback& callback); - - // Reports a set of tokens to the server for a given medium. - // Uses all active auth tokens (if any). - void ReportTokens(const std::vector<audio_modem::AudioToken>& tokens); - - private: - // A queued ReportRequest along with its metadata. - struct PendingRequest { - PendingRequest(std::unique_ptr<ReportRequest> report, - const std::string& app_id, - bool authenticated, - const StatusCallback& callback); - ~PendingRequest(); - - std::unique_ptr<ReportRequest> report; - const std::string app_id; - const bool authenticated; - const StatusCallback callback; - }; - - friend class RpcHandlerTest; - - // Before accepting any other calls, the server requires registration, - // which is tied to the auth token (or lack thereof) used to call Report. - void RegisterDevice(bool authenticated); - - // Device registration has completed. Send the requests that it was blocking. - void ProcessQueuedRequests(bool authenticated); - - // Sends a ReportRequest from Chrome itself, i.e. no app id. - void ReportOnAllDevices(std::unique_ptr<ReportRequest> request); - - // Stores a GCM ID and send it to the server if needed. - void RegisterGcmId(const std::string& gcm_id); - - // Server call response handlers. - void RegisterResponseHandler(bool authenticated, - bool gcm_pending, - HttpPost* completed_post, - int http_status_code, - const std::string& response_data); - void ReportResponseHandler(const StatusCallback& status_callback, - HttpPost* completed_post, - int http_status_code, - const std::string& response_data); - - // Removes unpublished or unsubscribed operations from the directive handlers. - void ProcessRemovedOperations(const ReportRequest& request); - - // Adds all currently playing tokens to the update signals in this report - // request. This ensures that the server doesn't keep issueing new tokens to - // us when we're already playing valid tokens. - void AddPlayingTokens(ReportRequest* request); - - void DispatchMessages( - const google::protobuf::RepeatedPtrField<SubscribedMessage>& - subscribed_messages); - - RequestHeader* CreateRequestHeader(const std::string& app_id, - const std::string& device_id) const; - - // Wrapper for the http post constructor. This is the default way - // to contact the server, but it can be overridden for testing. - void SendHttpPost( - net::URLRequestContextGetter* url_context_getter, - const std::string& rpc_name, - const std::string& api_key, - const std::string& auth_token, - std::unique_ptr<google::protobuf::MessageLite> request_proto, - const PostCleanupCallback& callback); - - // These belong to the caller. - CopresenceDelegate* const delegate_; - DirectiveHandler* const directive_handler_; - CopresenceStateImpl* state_; - GCMHandler* const gcm_handler_; - - MessagesCallback new_messages_callback_; - PostCallback server_post_callback_; - - ScopedVector<PendingRequest> pending_requests_queue_; - TimedMap<std::string, bool> invalid_audio_token_cache_; - std::set<HttpPost*> pending_posts_; - std::set<bool> pending_registrations_; - std::string auth_token_; - std::string gcm_id_; -}; - -} // namespace copresence - -#endif // COMPONENTS_COPRESENCE_RPC_RPC_HANDLER_H_ diff --git a/chromium/components/copresence/rpc/rpc_handler_unittest.cc b/chromium/components/copresence/rpc/rpc_handler_unittest.cc deleted file mode 100644 index ea4baab8f6b..00000000000 --- a/chromium/components/copresence/rpc/rpc_handler_unittest.cc +++ /dev/null @@ -1,348 +0,0 @@ -// Copyright 2015 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 "components/copresence/rpc/rpc_handler.h" - -#include <map> -#include <string> -#include <utility> -#include <vector> - -#include "base/bind.h" -#include "base/bind_helpers.h" -#include "base/memory/ptr_util.h" -#include "base/memory/scoped_vector.h" -#include "base/message_loop/message_loop.h" -#include "components/audio_modem/public/modem.h" -#include "components/audio_modem/test/stub_whispernet_client.h" -#include "components/copresence/copresence_state_impl.h" -#include "components/copresence/handlers/directive_handler.h" -#include "components/copresence/proto/data.pb.h" -#include "components/copresence/proto/enums.pb.h" -#include "components/copresence/proto/rpcs.pb.h" -#include "components/copresence/test/fake_directive_handler.h" -#include "net/http/http_status_code.h" -#include "testing/gmock/include/gmock/gmock.h" - -using google::protobuf::MessageLite; -using google::protobuf::RepeatedPtrField; - -using testing::ElementsAre; -using testing::Property; -using testing::SizeIs; - -using audio_modem::AudioToken; -using audio_modem::WhispernetClient; - -namespace copresence { - -namespace { - -const char kChromeVersion[] = "Chrome Version String"; - -void IgnoreMessages( - const RepeatedPtrField<SubscribedMessage>& /* messages */) {} - -} // namespace - -class RpcHandlerTest : public testing::Test, public CopresenceDelegate { - public: - RpcHandlerTest() - : whispernet_client_(new audio_modem::StubWhispernetClient), - // TODO(ckehoe): Use a FakeCopresenceState here - // and test that it gets called correctly. - rpc_handler_(this, - &directive_handler_, - nullptr, - nullptr, - base::Bind(&IgnoreMessages), - base::Bind(&RpcHandlerTest::CaptureHttpPost, - base::Unretained(this))), - status_(SUCCESS) {} - - // CopresenceDelegate implementation - - void HandleMessages(const std::string& /* app_id */, - const std::string& subscription_id, - const std::vector<Message>& messages) override { - NOTREACHED(); - } - - void HandleStatusUpdate(CopresenceStatus /* status */) override { - NOTREACHED(); - } - - net::URLRequestContextGetter* GetRequestContext() const override { - return nullptr; - } - - std::string GetPlatformVersionString() const override { - return kChromeVersion; - } - - std::string GetAPIKey(const std::string& app_id) const override { - return app_id + " API Key"; - } - - WhispernetClient* GetWhispernetClient() override { - return whispernet_client_.get(); - } - - // TODO(ckehoe): Add GCM tests. - gcm::GCMDriver* GetGCMDriver() override { - return nullptr; - } - - std::string GetDeviceId(bool authenticated) override { - return device_id_by_auth_state_[authenticated]; - } - - void SaveDeviceId(bool authenticated, const std::string& device_id) override { - device_id_by_auth_state_[authenticated] = device_id; - } - - protected: - - // Send test input to RpcHandler - - void RegisterDevice(bool authenticated) { - rpc_handler_.RegisterDevice(authenticated); - } - - void SendRegisterResponse(bool authenticated, - const std::string& device_id) { - RegisterDeviceResponse response; - response.set_registered_device_id(device_id); - response.mutable_header()->mutable_status()->set_code(OK); - - std::string serialized_response; - response.SerializeToString(&serialized_response); - rpc_handler_.RegisterResponseHandler( - authenticated, false, nullptr, net::HTTP_OK, serialized_response); - } - - void SendReport(std::unique_ptr<ReportRequest> request, - const std::string& app_id, - const std::string& auth_token) { - rpc_handler_.SendReportRequest(std::move(request), app_id, auth_token, - StatusCallback()); - } - - void SendReportResponse(int status_code, - std::unique_ptr<ReportResponse> response) { - response->mutable_header()->mutable_status()->set_code(OK); - - std::string serialized_response; - response->SerializeToString(&serialized_response); - rpc_handler_.ReportResponseHandler( - base::Bind(&RpcHandlerTest::CaptureStatus, base::Unretained(this)), - nullptr, - status_code, - serialized_response); - } - - // Read and modify RpcHandler state - - void SetAuthToken(const std::string& auth_token) { - rpc_handler_.auth_token_ = auth_token; - } - - const ScopedVector<RpcHandler::PendingRequest>& request_queue() const { - return rpc_handler_.pending_requests_queue_; - } - - void AddInvalidToken(const std::string& token) { - rpc_handler_.invalid_audio_token_cache_.Add(token, true); - } - - bool TokenIsInvalid(const std::string& token) { - return rpc_handler_.invalid_audio_token_cache_.HasKey(token); - } - - // For rpc_handler_.invalid_audio_token_cache_ - base::MessageLoop message_loop_; - - std::unique_ptr<WhispernetClient> whispernet_client_; - FakeDirectiveHandler directive_handler_; - RpcHandler rpc_handler_; - - std::map<bool, std::string> device_id_by_auth_state_; - - CopresenceStatus status_; - std::string rpc_name_; - std::string api_key_; - std::string auth_token_; - ScopedVector<MessageLite> request_protos_; - - private: - void CaptureHttpPost( - net::URLRequestContextGetter* url_context_getter, - const std::string& rpc_name, - const std::string& api_key, - const std::string& auth_token, - std::unique_ptr<MessageLite> request_proto, - const RpcHandler::PostCleanupCallback& response_callback) { - rpc_name_ = rpc_name; - api_key_ = api_key; - auth_token_ = auth_token; - request_protos_.push_back(request_proto.release()); - } - - void CaptureStatus(CopresenceStatus status) { - status_ = status; - } -}; - -TEST_F(RpcHandlerTest, RegisterDevice) { - RegisterDevice(false); - EXPECT_THAT(request_protos_, SizeIs(1)); - const RegisterDeviceRequest* registration = - static_cast<RegisterDeviceRequest*>(request_protos_[0]); - EXPECT_EQ(CHROME, registration->device_identifiers().registrant().type()); - - SetAuthToken("Register auth"); - RegisterDevice(true); - EXPECT_THAT(request_protos_, SizeIs(2)); - registration = static_cast<RegisterDeviceRequest*>(request_protos_[1]); - EXPECT_FALSE(registration->has_device_identifiers()); -} - -TEST_F(RpcHandlerTest, RequestQueuing) { - // Send a report. - ReportRequest* report = new ReportRequest; - report->mutable_manage_messages_request()->add_id_to_unpublish("unpublish"); - SendReport(base::WrapUnique(report), "Q App ID", "Q Auth Token"); - EXPECT_THAT(request_queue(), SizeIs(1)); - EXPECT_TRUE(request_queue()[0]->authenticated); - - // Check for registration request. - EXPECT_THAT(request_protos_, SizeIs(1)); - const RegisterDeviceRequest* registration = - static_cast<RegisterDeviceRequest*>(request_protos_[0]); - EXPECT_FALSE(registration->device_identifiers().has_registrant()); - EXPECT_EQ("Q Auth Token", auth_token_); - - // Send a second report. - report = new ReportRequest; - report->mutable_manage_subscriptions_request()->add_id_to_unsubscribe( - "unsubscribe"); - SendReport(base::WrapUnique(report), "Q App ID", "Q Auth Token"); - EXPECT_THAT(request_protos_, SizeIs(1)); - EXPECT_THAT(request_queue(), SizeIs(2)); - EXPECT_TRUE(request_queue()[1]->authenticated); - - // Send an anonymous report. - report = new ReportRequest; - report->mutable_update_signals_request()->add_token_observation() - ->set_token_id("Q Audio Token"); - SendReport(base::WrapUnique(report), "Q App ID", ""); - EXPECT_THAT(request_queue(), SizeIs(3)); - EXPECT_FALSE(request_queue()[2]->authenticated); - - // Check for another registration request. - EXPECT_THAT(request_protos_, SizeIs(2)); - registration = static_cast<RegisterDeviceRequest*>(request_protos_[1]); - EXPECT_TRUE(registration->device_identifiers().has_registrant()); - EXPECT_EQ("", auth_token_); - - // Respond to the first registration. - SendRegisterResponse(true, "Q Auth Device ID"); - EXPECT_EQ("Q Auth Device ID", device_id_by_auth_state_[true]); - - // Check that queued reports are sent. - EXPECT_THAT(request_protos_, SizeIs(4)); - EXPECT_THAT(request_queue(), SizeIs(1)); - EXPECT_THAT(directive_handler_.removed_directives(), - ElementsAre("unpublish", "unsubscribe")); - report = static_cast<ReportRequest*>(request_protos_[2]); - EXPECT_EQ("unpublish", report->manage_messages_request().id_to_unpublish(0)); - report = static_cast<ReportRequest*>(request_protos_[3]); - EXPECT_EQ("unsubscribe", - report->manage_subscriptions_request().id_to_unsubscribe(0)); - - // Respond to the second registration. - SendRegisterResponse(false, "Q Anonymous Device ID"); - EXPECT_EQ("Q Anonymous Device ID", device_id_by_auth_state_[false]); - - // Check for last report. - EXPECT_THAT(request_protos_, SizeIs(5)); - EXPECT_TRUE(request_queue().empty()); - report = static_cast<ReportRequest*>(request_protos_[4]); - EXPECT_EQ("Q Audio Token", - report->update_signals_request().token_observation(0).token_id()); -} - -TEST_F(RpcHandlerTest, CreateRequestHeader) { - device_id_by_auth_state_[true] = "CreateRequestHeader Device ID"; - SendReport(base::WrapUnique(new ReportRequest), "CreateRequestHeader App", - "CreateRequestHeader Auth Token"); - - EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); - EXPECT_EQ("CreateRequestHeader App API Key", api_key_); - EXPECT_EQ("CreateRequestHeader Auth Token", auth_token_); - const ReportRequest* report = static_cast<ReportRequest*>(request_protos_[0]); - EXPECT_EQ(kChromeVersion, - report->header().framework_version().version_name()); - EXPECT_EQ("CreateRequestHeader App", - report->header().client_version().client()); - EXPECT_EQ("CreateRequestHeader Device ID", - report->header().registered_device_id()); - EXPECT_EQ(CHROME_PLATFORM_TYPE, - report->header().device_fingerprint().type()); -} - -TEST_F(RpcHandlerTest, ReportTokens) { - std::vector<AudioToken> test_tokens; - test_tokens.push_back(AudioToken("token 1", false)); - test_tokens.push_back(AudioToken("token 2", false)); - test_tokens.push_back(AudioToken("token 3", true)); - AddInvalidToken("token 2"); - - device_id_by_auth_state_[false] = "ReportTokens Anonymous Device"; - device_id_by_auth_state_[true] = "ReportTokens Auth Device"; - SetAuthToken("ReportTokens Auth"); - - rpc_handler_.ReportTokens(test_tokens); - EXPECT_EQ(RpcHandler::kReportRequestRpcName, rpc_name_); - EXPECT_EQ(" API Key", api_key_); - EXPECT_THAT(request_protos_, SizeIs(2)); - const ReportRequest* report = static_cast<ReportRequest*>(request_protos_[0]); - RepeatedPtrField<TokenObservation> tokens_sent = - report->update_signals_request().token_observation(); - EXPECT_THAT(tokens_sent, ElementsAre( - Property(&TokenObservation::token_id, "token 1"), - Property(&TokenObservation::token_id, "token 3"), - Property(&TokenObservation::token_id, "current audible"), - Property(&TokenObservation::token_id, "current inaudible"))); -} - -TEST_F(RpcHandlerTest, ReportResponseHandler) { - // Fail on HTTP status != 200. - std::unique_ptr<ReportResponse> response(new ReportResponse); - status_ = SUCCESS; - SendReportResponse(net::HTTP_BAD_REQUEST, std::move(response)); - EXPECT_EQ(FAIL, status_); - - // Construct a test ReportResponse. - response.reset(new ReportResponse); - response->mutable_header()->mutable_status()->set_code(OK); - UpdateSignalsResponse* update_response = - response->mutable_update_signals_response(); - update_response->set_status(util::error::OK); - Token* invalid_token = update_response->add_token(); - invalid_token->set_id("bad token"); - invalid_token->set_status(INVALID); - update_response->add_directive()->set_subscription_id("Subscription 1"); - update_response->add_directive()->set_subscription_id("Subscription 2"); - - // Check processing. - status_ = FAIL; - SendReportResponse(net::HTTP_OK, std::move(response)); - EXPECT_EQ(SUCCESS, status_); - EXPECT_TRUE(TokenIsInvalid("bad token")); - EXPECT_THAT(directive_handler_.added_directives(), - ElementsAre("Subscription 1", "Subscription 2")); -} - -} // namespace copresence |