blob: e4d4713b1803897ae2096bb0cb995b7a957f9e12 [file] [log] [blame]
// Copyright 2014 The Chromium OS 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 <chromeos/http/http_transport_curl.h>
#include <base/bind.h>
#include <base/logging.h>
#include <base/message_loop/message_loop_proxy.h>
#include <chromeos/http/http_connection_curl.h>
#include <chromeos/http/http_request.h>
#include <chromeos/strings/string_utils.h>
namespace chromeos {
namespace http {
namespace curl {
Transport::Transport(const std::shared_ptr<CurlInterface>& curl_interface)
: curl_interface_{curl_interface},
task_runner_{base::MessageLoopProxy::current()} {
VLOG(1) << "curl::Transport created";
}
Transport::Transport(const std::shared_ptr<CurlInterface>& curl_interface,
scoped_refptr<base::TaskRunner> task_runner)
: curl_interface_{curl_interface}, task_runner_{task_runner} {
VLOG(1) << "curl::Transport created";
}
Transport::Transport(const std::shared_ptr<CurlInterface>& curl_interface,
const std::string& proxy)
: curl_interface_{curl_interface},
proxy_{proxy},
task_runner_{base::MessageLoopProxy::current()} {
VLOG(1) << "curl::Transport created with proxy " << proxy;
}
Transport::~Transport() {
VLOG(1) << "curl::Transport destroyed";
}
std::shared_ptr<http::Connection> Transport::CreateConnection(
const std::string& url,
const std::string& method,
const HeaderList& headers,
const std::string& user_agent,
const std::string& referer,
chromeos::ErrorPtr* error) {
std::shared_ptr<http::Connection> connection;
CURL* curl_handle = curl_interface_->EasyInit();
if (!curl_handle) {
LOG(ERROR) << "Failed to initialize CURL";
chromeos::Error::AddTo(error,
FROM_HERE,
http::kErrorDomain,
"curl_init_failed",
"Failed to initialize CURL");
return connection;
}
LOG(INFO) << "Sending a " << method << " request to " << url;
CURLcode code = curl_interface_->EasySetOptStr(curl_handle, CURLOPT_URL, url);
if (code == CURLE_OK && !user_agent.empty()) {
code = curl_interface_->EasySetOptStr(
curl_handle, CURLOPT_USERAGENT, user_agent);
}
if (code == CURLE_OK && !referer.empty()) {
code =
curl_interface_->EasySetOptStr(curl_handle, CURLOPT_REFERER, referer);
}
if (code == CURLE_OK && !proxy_.empty()) {
code = curl_interface_->EasySetOptStr(curl_handle, CURLOPT_PROXY, proxy_);
}
// Setup HTTP request method and optional request body.
if (code == CURLE_OK) {
if (method == request_type::kGet) {
code = curl_interface_->EasySetOptInt(curl_handle, CURLOPT_HTTPGET, 1);
} else if (method == request_type::kHead) {
code = curl_interface_->EasySetOptInt(curl_handle, CURLOPT_NOBODY, 1);
} else if (method == request_type::kPut) {
code = curl_interface_->EasySetOptInt(curl_handle, CURLOPT_UPLOAD, 1);
} else {
// POST and custom request methods
code = curl_interface_->EasySetOptInt(curl_handle, CURLOPT_POST, 1);
if (code == CURLE_OK)
code = curl_interface_->EasySetOptPtr(
curl_handle, CURLOPT_POSTFIELDS, nullptr);
if (code == CURLE_OK && method != request_type::kPost)
code = curl_interface_->EasySetOptStr(
curl_handle, CURLOPT_CUSTOMREQUEST, method);
}
}
if (code != CURLE_OK) {
AddCurlError(error, FROM_HERE, code, curl_interface_.get());
curl_interface_->EasyCleanup(curl_handle);
return connection;
}
connection = std::make_shared<http::curl::Connection>(
curl_handle, method, curl_interface_, shared_from_this());
if (!connection->SendHeaders(headers, error)) {
connection.reset();
}
return connection;
}
void Transport::RunCallbackAsync(const tracked_objects::Location& from_here,
const base::Closure& callback) {
task_runner_->PostTask(from_here, callback);
}
void Transport::StartAsyncTransfer(http::Connection* connection,
const SuccessCallback& success_callback,
const ErrorCallback& error_callback) {
// TODO(avakulenko): For now using synchronous operation behind the scenes,
// but this is to change in the follow-up CLs.
http::curl::Connection* curl_connection =
static_cast<http::curl::Connection*>(connection);
CURLcode ret = curl_interface_->EasyPerform(curl_connection->curl_handle_);
if (ret != CURLE_OK) {
chromeos::ErrorPtr error;
AddCurlError(&error, FROM_HERE, ret, curl_interface_.get());
RunCallbackAsync(FROM_HERE,
base::Bind(error_callback, base::Owned(error.release())));
} else {
scoped_ptr<Response> response{new Response{connection->shared_from_this()}};
RunCallbackAsync(FROM_HERE,
base::Bind(success_callback, base::Passed(&response)));
}
}
void Transport::AddCurlError(chromeos::ErrorPtr* error,
const tracked_objects::Location& location,
CURLcode code,
CurlInterface* curl_interface) {
chromeos::Error::AddTo(error,
location,
"curl_error",
chromeos::string_utils::ToString(code),
curl_interface->EasyStrError(code));
}
} // namespace curl
} // namespace http
} // namespace chromeos