| // Copyright 2012 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "components/sync/engine/net/server_connection_manager.h" | |
| #include <errno.h> | |
| #include <ostream> | |
| #include "base/check_is_test.h" | |
| #include "base/feature_list.h" | |
| #include "base/metrics/histogram.h" | |
| #include "base/observer_list.h" | |
| #include "build/build_config.h" | |
| #include "components/sync/base/features.h" | |
| #include "components/sync/engine/cancelation_signal.h" | |
| #include "components/sync/engine/net/url_translator.h" | |
| #include "components/sync/engine/syncer.h" | |
| #include "net/http/http_status_code.h" | |
| #include "url/gurl.h" | |
| namespace syncer { | |
| namespace { | |
| #define ENUM_CASE(x) \ | |
| case HttpResponse::x: \ | |
| return #x; \ | |
| break | |
| const char* GetServerConnectionCodeString( | |
| HttpResponse::ServerConnectionCode code) { | |
| switch (code) { | |
| ENUM_CASE(NONE); | |
| ENUM_CASE(CONNECTION_UNAVAILABLE); | |
| ENUM_CASE(SYNC_SERVER_ERROR); | |
| ENUM_CASE(SYNC_AUTH_ERROR); | |
| ENUM_CASE(SERVER_CONNECTION_OK); | |
| } | |
| NOTREACHED(); | |
| } | |
| #undef ENUM_CASE | |
| } // namespace | |
| HttpResponse::HttpResponse() | |
| : server_status(NONE), | |
| net_error_code(-1), | |
| http_status_code(-1), | |
| content_length(-1) {} | |
| // static | |
| HttpResponse HttpResponse::Uninitialized() { | |
| return HttpResponse(); | |
| } | |
| // static | |
| HttpResponse HttpResponse::ForNetError(int net_error_code) { | |
| HttpResponse response; | |
| response.server_status = CONNECTION_UNAVAILABLE; | |
| response.net_error_code = net_error_code; | |
| return response; | |
| } | |
| // static | |
| HttpResponse HttpResponse::ForUnspecifiedError() { | |
| HttpResponse response; | |
| response.server_status = CONNECTION_UNAVAILABLE; | |
| return response; | |
| } | |
| // static | |
| HttpResponse HttpResponse::ForHttpStatusCode(int http_status_code) { | |
| HttpResponse response; | |
| if (http_status_code == net::HTTP_OK) { | |
| response.server_status = SERVER_CONNECTION_OK; | |
| } else if (http_status_code == net::HTTP_UNAUTHORIZED) { | |
| response.server_status = SYNC_AUTH_ERROR; | |
| } else { | |
| response.server_status = SYNC_SERVER_ERROR; | |
| } | |
| response.http_status_code = http_status_code; | |
| return response; | |
| } | |
| // static | |
| HttpResponse HttpResponse::ForSuccessForTest() { | |
| CHECK_IS_TEST(); | |
| HttpResponse response; | |
| response.server_status = SERVER_CONNECTION_OK; | |
| response.http_status_code = net::HTTP_OK; | |
| return response; | |
| } | |
| ServerConnectionManager::ServerConnectionManager() | |
| : server_response_(HttpResponse::Uninitialized()) {} | |
| ServerConnectionManager::~ServerConnectionManager() = default; | |
| bool ServerConnectionManager::SetAccessTokenInfo( | |
| const signin::AccessTokenInfo& access_token_info) { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| CHECK(!base::FeatureList::IsEnabled(kSyncUsePropagatedAccessToken)); | |
| cached_access_token_info_ = access_token_info; | |
| if (IsAccessTokenInfoValid(cached_access_token_info_)) { | |
| return true; | |
| } | |
| ClearCachedAccessToken(); | |
| // The token was probably expired. Notify sync frontend again to request new | |
| // token, otherwise backend will stay in SYNC_AUTH_ERROR state while frontend | |
| // thinks everything is fine and takes no actions. | |
| SetServerResponse(HttpResponse::ForHttpStatusCode(net::HTTP_UNAUTHORIZED)); | |
| return false; | |
| } | |
| bool ServerConnectionManager::HasCachedAccessToken() const { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| CHECK(!base::FeatureList::IsEnabled(kSyncUsePropagatedAccessToken)); | |
| return !cached_access_token_info_.token.empty(); | |
| } | |
| void ServerConnectionManager::ClearCachedAccessToken() { | |
| cached_access_token_info_ = signin::AccessTokenInfo(); | |
| } | |
| // static | |
| bool ServerConnectionManager::IsAccessTokenInfoValid( | |
| const signin::AccessTokenInfo& access_token_info) { | |
| if (access_token_info.token.empty()) { | |
| return false; | |
| } | |
| // Assume the token is valid if the expiration time is not set or the | |
| // validation feature is disabled. | |
| if (access_token_info.expiration_time.is_null() || | |
| !base::FeatureList::IsEnabled(kSyncValidateAccessToken)) { | |
| return true; | |
| } | |
| return access_token_info.expiration_time > base::Time::Now(); | |
| } | |
| void ServerConnectionManager::SetServerResponse( | |
| const HttpResponse& server_response) { | |
| // Notify only if the server status changed, except for SYNC_AUTH_ERROR: In | |
| // that case, always notify in order to poke observers to do something about | |
| // it. | |
| bool notify = | |
| (server_response.server_status == HttpResponse::SYNC_AUTH_ERROR || | |
| server_response_.server_status != server_response.server_status); | |
| server_response_ = server_response; | |
| if (notify) { | |
| NotifyStatusChanged(); | |
| } | |
| } | |
| void ServerConnectionManager::NotifyStatusChanged() { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| for (ServerConnectionEventListener& observer : listeners_) { | |
| observer.OnServerConnectionEvent( | |
| ServerConnectionEvent(server_response_.server_status)); | |
| } | |
| } | |
| HttpResponse ServerConnectionManager::PostBufferWithAccessToken( | |
| const std::string& buffer_in, | |
| std::string* buffer_out, | |
| const signin::AccessTokenInfo& access_token_info) { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| HttpResponse http_response = | |
| PostBuffer(buffer_in, buffer_out, access_token_info); | |
| SetServerResponse(http_response); | |
| return server_response_; | |
| } | |
| HttpResponse ServerConnectionManager::PostBufferWithCachedAuth( | |
| const std::string& buffer_in, | |
| std::string* buffer_out) { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| CHECK(!base::FeatureList::IsEnabled(kSyncUsePropagatedAccessToken)); | |
| return PostBufferWithAccessToken(buffer_in, buffer_out, | |
| cached_access_token_info_); | |
| } | |
| void ServerConnectionManager::AddListener( | |
| ServerConnectionEventListener* listener) { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| listeners_.AddObserver(listener); | |
| } | |
| void ServerConnectionManager::RemoveListener( | |
| ServerConnectionEventListener* listener) { | |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); | |
| listeners_.RemoveObserver(listener); | |
| } | |
| std::ostream& operator<<(std::ostream& s, const struct HttpResponse& hr) { | |
| s << " Response Code (bogus on error): " << hr.http_status_code; | |
| s << " Content-Length (bogus on error): " << hr.content_length; | |
| s << " Server Status: " << GetServerConnectionCodeString(hr.server_status); | |
| return s; | |
| } | |
| } // namespace syncer |