| // 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/sync_server_connection_manager.h" | |
| #include <stdint.h> | |
| #include <utility> | |
| #include "base/feature_list.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/metrics/histogram_functions.h" | |
| #include "components/sync/base/features.h" | |
| #include "components/sync/engine/cancelation_signal.h" | |
| #include "components/sync/engine/net/http_post_provider.h" | |
| #include "components/sync/engine/net/http_post_provider_factory.h" | |
| #include "net/base/net_errors.h" | |
| #include "net/http/http_status_code.h" | |
| #include "third_party/abseil-cpp/absl/cleanup/cleanup.h" | |
| namespace syncer { | |
| namespace { | |
| // This provides HTTP Post functionality through the interface provided | |
| // by the application hosting the syncer backend. | |
| class Connection : public CancelationSignal::Observer { | |
| public: | |
| // All pointers must not be null and must outlive this object. | |
| Connection(HttpPostProviderFactory* factory, | |
| CancelationSignal* cancelation_signal); | |
| Connection(const Connection&) = delete; | |
| Connection& operator=(const Connection&) = delete; | |
| ~Connection() override; | |
| HttpResponse PostRequestAndDownloadResponse(const GURL& connection_url, | |
| const std::string& access_token, | |
| const std::string& payload, | |
| std::string* buffer_out); | |
| // CancelationSignal::Observer overrides. | |
| void OnCancelationSignalReceived() override; | |
| private: | |
| // Pointer to the factory we use for creating HttpPostProviders. We do not | |
| // own `factory_`. | |
| const raw_ptr<HttpPostProviderFactory> factory_; | |
| // Cancelation signal is signalled when engine shuts down. Current blocking | |
| // operation should be aborted. | |
| const raw_ptr<CancelationSignal> cancelation_signal_; | |
| scoped_refptr<HttpPostProvider> const post_provider_; | |
| }; | |
| Connection::Connection(HttpPostProviderFactory* factory, | |
| CancelationSignal* cancelation_signal) | |
| : factory_(factory), | |
| cancelation_signal_(cancelation_signal), | |
| post_provider_(factory_->Create()) { | |
| DCHECK(factory); | |
| DCHECK(cancelation_signal); | |
| DCHECK(post_provider_); | |
| } | |
| Connection::~Connection() = default; | |
| HttpResponse Connection::PostRequestAndDownloadResponse( | |
| const GURL& sync_request_url, | |
| const std::string& access_token, | |
| const std::string& payload, | |
| std::string* buffer_out) { | |
| post_provider_->SetURL(sync_request_url); | |
| if (!access_token.empty()) { | |
| net::HttpRequestHeaders headers; | |
| headers.SetHeader("Authorization", "Bearer " + access_token); | |
| post_provider_->SetExtraRequestHeaders(headers); | |
| } | |
| // Must be octet-stream, or the payload may be parsed for a cookie. | |
| post_provider_->SetPostPayload("application/octet-stream", payload.length(), | |
| payload.data()); | |
| // Issue the POST, blocking until it finishes. | |
| if (!cancelation_signal_->TryRegisterHandler(this)) { | |
| // Return early because cancelation signal was signaled. | |
| return HttpResponse::ForUnspecifiedError(); | |
| } | |
| absl::Cleanup auto_unregister = [this] { | |
| cancelation_signal_->UnregisterHandler(this); | |
| }; | |
| int net_error_code = 0; | |
| int http_status_code = 0; | |
| if (!post_provider_->MakeSynchronousPost(&net_error_code, | |
| &http_status_code)) { | |
| DCHECK_NE(net_error_code, net::OK); | |
| DVLOG(1) << "Http POST failed, error returns: " << net_error_code; | |
| return HttpResponse::ForNetError(net_error_code); | |
| } | |
| // We got a server response, copy over response codes and content. | |
| HttpResponse response = HttpResponse::ForHttpStatusCode(http_status_code); | |
| response.content_length = post_provider_->GetResponseContentLength(); | |
| // Write the content into the buffer. | |
| buffer_out->assign(post_provider_->GetResponseContent(), | |
| post_provider_->GetResponseContentLength()); | |
| return response; | |
| } | |
| void Connection::OnCancelationSignalReceived() { | |
| DCHECK(post_provider_); | |
| post_provider_->Abort(); | |
| } | |
| } // namespace | |
| SyncServerConnectionManager::SyncServerConnectionManager( | |
| const GURL& sync_request_url, | |
| std::unique_ptr<HttpPostProviderFactory> factory, | |
| CancelationSignal* cancelation_signal) | |
| : sync_request_url_(sync_request_url), | |
| post_provider_factory_(std::move(factory)), | |
| cancelation_signal_(cancelation_signal) { | |
| DCHECK(post_provider_factory_); | |
| DCHECK(cancelation_signal_); | |
| } | |
| SyncServerConnectionManager::~SyncServerConnectionManager() = default; | |
| HttpResponse SyncServerConnectionManager::PostBuffer( | |
| const std::string& buffer_in, | |
| std::string* buffer_out, | |
| const signin::AccessTokenInfo& access_token_info) { | |
| const bool is_access_token_valid = IsAccessTokenInfoValid(access_token_info); | |
| base::UmaHistogramBoolean("Sync.URLFetchAccessToken", is_access_token_valid); | |
| if (!is_access_token_valid) { | |
| ClearCachedAccessToken(); | |
| return HttpResponse::ForHttpStatusCode(net::HTTP_UNAUTHORIZED); | |
| } | |
| if (cancelation_signal_->IsSignalled()) { | |
| return HttpResponse::ForUnspecifiedError(); | |
| } | |
| auto connection = std::make_unique<Connection>(post_provider_factory_.get(), | |
| cancelation_signal_); | |
| // Note that the post may be aborted by now, which will just cause Init to | |
| // fail with CONNECTION_UNAVAILABLE. | |
| HttpResponse http_response = connection->PostRequestAndDownloadResponse( | |
| sync_request_url_, access_token_info.token, buffer_in, buffer_out); | |
| if (http_response.server_status == HttpResponse::SYNC_AUTH_ERROR) { | |
| ClearCachedAccessToken(); | |
| } | |
| return http_response; | |
| } | |
| } // namespace syncer |