blob: c6a6e44fb068d0457e1407dc8da092452ed400f9 [file] [log] [blame]
// 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/memory/raw_ptr.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 =
static_cast<int64_t>(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,
const std::string& access_token,
std::string* buffer_out) {
if (access_token.empty()) {
// Print a log to distinguish this "known failure" from others.
DVLOG(1) << "ServerConnectionManager forcing SYNC_AUTH_ERROR due to missing"
" access token";
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, buffer_in, buffer_out);
if (http_response.server_status == HttpResponse::SYNC_AUTH_ERROR) {
ClearAccessToken();
}
return http_response;
}
} // namespace syncer