#include "components/sync/engine/net/sync_server_connection_manager.h"
#include <stdint.h>
#include <utility>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#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"
namespace syncer {
namespace {
class Connection : public CancelationSignal::Observer {
public:
Connection(HttpPostProviderFactory* factory,
CancelationSignal* cancelation_signal);
Connection(const Connection&) = delete;
Connection& operator=(const Connection&) = delete;
~Connection() override;
HttpResponse Init(const GURL& connection_url,
const std::string& access_token,
const std::string& payload,
bool allow_batching);
bool ReadBufferResponse(std::string* buffer_out, HttpResponse* response);
void OnCancelationSignalReceived() override;
private:
const raw_ptr<HttpPostProviderFactory> factory_;
const raw_ptr<CancelationSignal> cancelation_signal_;
scoped_refptr<HttpPostProvider> const post_provider_;
std::string buffer_;
};
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::Init(const GURL& sync_request_url,
const std::string& access_token,
const std::string& payload,
bool allow_batching) {
post_provider_->SetURL(sync_request_url);
if (!access_token.empty()) {
std::string headers;
headers = "Authorization: Bearer " + access_token;
post_provider_->SetExtraRequestHeaders(headers.c_str());
}
post_provider_->SetPostPayload("application/octet-stream", payload.length(),
payload.data());
post_provider_->SetAllowBatching(allow_batching);
if (!cancelation_signal_->TryRegisterHandler(this)) {
return HttpResponse::ForUnspecifiedError();
}
base::ScopedClosureRunner auto_unregister(base::BindOnce(
&CancelationSignal::UnregisterHandler,
base::Unretained(cancelation_signal_), base::Unretained(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);
}
HttpResponse response = HttpResponse::ForHttpStatusCode(http_status_code);
response.content_length =
static_cast<int64_t>(post_provider_->GetResponseContentLength());
response.payload_length =
static_cast<int64_t>(post_provider_->GetResponseContentLength());
buffer_.assign(post_provider_->GetResponseContent(),
post_provider_->GetResponseContentLength());
return response;
}
bool Connection::ReadBufferResponse(std::string* buffer_out,
HttpResponse* response) {
DCHECK_EQ(response->server_status, HttpResponse::SERVER_CONNECTION_OK);
DCHECK_EQ(response->http_status_code, net::HTTP_OK);
if (response->content_length <= 0)
return false;
const int64_t bytes_read = buffer_.length();
CHECK_LE(response->content_length, bytes_read);
buffer_out->assign(buffer_);
if (bytes_read != response->content_length) {
response->server_status = HttpResponse::IO_ERROR;
return false;
}
return true;
}
void Connection::OnCancelationSignalReceived() {
DCHECK(post_provider_);
post_provider_->Abort();
}
}
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,
bool allow_batching,
std::string* buffer_out) {
if (access_token.empty()) {
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_);
HttpResponse http_response = connection->Init(sync_request_url_, access_token,
buffer_in, allow_batching);
if (http_response.server_status == HttpResponse::SYNC_AUTH_ERROR) {
ClearAccessToken();
} else if (http_response.server_status ==
HttpResponse::SERVER_CONNECTION_OK) {
connection->ReadBufferResponse(buffer_out, &http_response);
}
return http_response;
}
}