buffet: reworked http transport to prepare for unit testing

Changed the way HTTP transport classes are implemented. Now
the Transport class is a very simple factory class that just
creates an appropriate instance of http::Connection object.
http::Connection is a thin layer wrapper around underlying
transport library, such as libcurl.

Also, the Transport class is now stateless and can be used
to initiate multiple HTTP connections.

Majority of HTTP processing is done in http::Request and
http::Response classes which are not dependent on the underlying
transport.

The HTTP utility functions now take the Transport class as
a parameter to facilitate unit tesing.

Also added a stub http_utils_unittest.cc to be populated
with actual tests when the fake HTTP transport is implemented.

BUG=chromium:364733
TEST=Unit tests pass.

Change-Id: If506854d274f725bbc2d6f765f19344d8697a239
Reviewed-on: https://chromium-review.googlesource.com/196153
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/HACKING b/buffet/HACKING
index d657397..5bab79e 100644
--- a/buffet/HACKING
+++ b/buffet/HACKING
@@ -14,3 +14,11 @@
 
 # Deploy the most recently built version of buffet to a DUT:
 cros deploy --board=${BOARD} <remote host> platform2
+
+#To enable additional debug logging in buffet daemon, run it as:
+# buffet --v=<level>, where <level> is verbosity level of debug info:
+#  1 - enable additional tracing of internal object construction and destruction
+#  2 - add tracing of request and response data sent over HTTP (beware of
+#      privacy concerns).
+#  3 - enable low-level CURL tracing for HTTP communication.
+buffet --v=2
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index afdabb6..06e87f5 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -34,6 +34,7 @@
         'device_registration_info.cc',
         'exported_property_set.cc',
         'http_request.cc',
+        'http_connection_curl.cc',
         'http_transport_curl.cc',
         'http_utils.cc',
         'async_event_sequencer.cc',
@@ -76,6 +77,7 @@
         'data_encoding_unittest.cc',
         'exported_property_set_unittest.cc',
         'async_event_sequencer_unittest.cc',
+        'http_utils_unittest.cc',
         'mime_utils_unittest.cc',
         'string_utils_unittest.cc',
         'url_utils_unittest.cc'
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index 20fbdf2..de89aff 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -10,13 +10,14 @@
 #include <base/file_util.h>
 #include <memory>
 
+#include "buffet/data_encoding.h"
+#include "buffet/http_transport_curl.h"
 #include "buffet/http_utils.h"
 #include "buffet/mime_utils.h"
 #include "buffet/string_utils.h"
-#include "buffet/data_encoding.h"
 #include "buffet/url_utils.h"
 
-using namespace chromeos::http;
+using namespace chromeos;
 using namespace chromeos::data_encoding;
 
 namespace {
@@ -38,9 +39,9 @@
     FILE_PATH_LITERAL("/var/lib/buffet/device_reg_info");
 
 bool GetParamValue(
-  const std::map<std::string, std::shared_ptr<base::Value>>& params,
-  const std::string& param_name,
-  std::string* param_value) {
+    const std::map<std::string, std::shared_ptr<base::Value>>& params,
+    const std::string& param_name,
+    std::string* param_value) {
   auto p = params.find(param_name);
   if (p == params.end())
     return false;
@@ -51,17 +52,17 @@
 std::pair<std::string, std::string> BuildAuthHeader(
     const std::string& access_token_type,
     const std::string& access_token) {
-  std::string authorization = chromeos::string_utils::Join(' ',
-                                                           access_token_type,
-                                                           access_token);
-  return {request_header::kAuthorization, authorization};
+  std::string authorization = string_utils::Join(' ',
+                                                 access_token_type,
+                                                 access_token);
+  return {http::request_header::kAuthorization, authorization};
 }
 
 std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
-  const Response* response, std::string* error_message) {
+    const http::Response* response, std::string* error_message) {
   int code = 0;
-  auto resp = ParseJsonResponse(response, &code, error_message);
-  if (resp && code >= status_code::BadRequest) {
+  auto resp = http::ParseJsonResponse(response, &code, error_message);
+  if (resp && code >= http::status_code::BadRequest) {
     if (error_message) {
       error_message->clear();
       std::string error_code, error;
@@ -80,14 +81,21 @@
 std::string BuildURL(const std::string& url,
                      const std::vector<std::string>& subpaths,
                      const WebParamList& params) {
-  std::string result = chromeos::url::CombineMultiple(url, subpaths);
-  return chromeos::url::AppendQueryParams(result, params);
+  std::string result = url::CombineMultiple(url, subpaths);
+  return url::AppendQueryParams(result, params);
 }
 
 
 } // anonymous namespace
 
 namespace buffet {
+DeviceRegistrationInfo::DeviceRegistrationInfo()
+    : transport_(new http::curl::Transport()){
+}
+
+DeviceRegistrationInfo::DeviceRegistrationInfo(
+    std::shared_ptr<http::Transport> transport) : transport_(transport) {
+}
 
 std::pair<std::string, std::string>
     DeviceRegistrationInfo::GetAuthorizationHeader() const {
@@ -209,12 +217,12 @@
     return true;
   }
 
-  auto response = PostFormData(GetOAuthURL("token"), {
+  auto response = http::PostFormData(GetOAuthURL("token"), {
     {"refresh_token", refresh_token_},
     {"client_id", client_id_},
     {"client_secret", client_secret_},
     {"grant_type", "refresh_token"},
-  });
+  }, transport_);
   if (!response)
     return false;
 
@@ -246,13 +254,14 @@
   if (!CheckRegistration())
     return std::unique_ptr<base::Value>();
 
-  auto response = Get(GetDeviceURL(), {GetAuthorizationHeader()});
+  auto response = http::Get(GetDeviceURL(),
+                            {GetAuthorizationHeader()}, transport_);
   int status_code = 0;
   std::unique_ptr<base::Value> device_info =
-      ParseJsonResponse(response.get(), &status_code, nullptr);
+      http::ParseJsonResponse(response.get(), &status_code, nullptr);
 
   if (device_info) {
-    if (status_code >= status_code::BadRequest) {
+    if (status_code >= http::status_code::BadRequest) {
       LOG(WARNING) << "Failed to retrieve the device info. Response code = "
                    << status_code;
       return std::unique_ptr<base::Value>();
@@ -332,8 +341,8 @@
   req_json.Set("deviceDraft.commands.base.vendorCommands", vendor_commands);
 
   std::string url = GetServiceURL("registrationTickets", {{"key", api_key_}});
-  auto resp_json = ParseJsonResponse(PostJson(url, &req_json).get(),
-                                     nullptr, error_msg);
+  auto resp_json = http::ParseJsonResponse(
+      http::PostJson(url, &req_json, transport_).get(), nullptr, error_msg);
   if (!resp_json)
     return std::string();
 
@@ -371,32 +380,34 @@
   }
 
   std::string url = GetServiceURL("registrationTickets/" + ticket_id_);
-  std::unique_ptr<Response> response;
+  std::unique_ptr<http::Response> response;
   if (!user_auth_code.empty()) {
     std::string user_access_token;
-    response = PostFormData(GetOAuthURL("token"), {
+    response = http::PostFormData(GetOAuthURL("token"), {
       {"code", user_auth_code},
       {"client_id", client_id_},
       {"client_secret", client_secret_},
       {"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
       {"grant_type", "authorization_code"}
-    });
+    }, transport_);
     if (!response)
       return false;
 
-    auto json_resp = ParseOAuthResponse(response.get(), nullptr);
+    std::string error;
+    auto json_resp = ParseOAuthResponse(response.get(), &error);
     if (!json_resp ||
         !json_resp->GetString("access_token", &user_access_token)) {
+      LOG(ERROR) << "Error parsing OAuth response: " << error;
       return false;
     }
 
     base::DictionaryValue user_info;
     user_info.SetString("userEmail", "me");
-    response = PatchJson(url, &user_info,
-                         {BuildAuthHeader("Bearer", user_access_token)});
+    response = http::PatchJson(
+        url, &user_info, {BuildAuthHeader("Bearer", user_access_token)},
+        transport_);
 
-    std::string error;
-    auto json = ParseJsonResponse(response.get(), nullptr, &error);
+    auto json = http::ParseJsonResponse(response.get(), nullptr, &error);
     if (!json) {
       LOG(ERROR) << "Error populating user info: " << error;
       return false;
@@ -407,30 +418,30 @@
   url += "/finalize?key=" + api_key_;
   do {
     LOG(INFO) << "Sending request to: " << url;
-    response = PostBinary(url, nullptr, 0);
+    response = http::PostBinary(url, nullptr, 0, transport_);
     if (response) {
-      if (response->GetStatusCode() == status_code::BadRequest)
+      if (response->GetStatusCode() == http::status_code::BadRequest)
         sleep(1);
     }
   }
   while (response &&
-         response->GetStatusCode() == status_code::BadRequest);
+         response->GetStatusCode() == http::status_code::BadRequest);
   if (response &&
-      response->GetStatusCode() == status_code::Ok) {
-    auto json_resp = ParseJsonResponse(response.get(), nullptr, nullptr);
+      response->GetStatusCode() == http::status_code::Ok) {
+    auto json_resp = http::ParseJsonResponse(response.get(), nullptr, nullptr);
     if (json_resp &&
         json_resp->GetString("robotAccountEmail", &device_robot_account_) &&
         json_resp->GetString("robotAccountAuthorizationCode", &auth_code) &&
         json_resp->GetString("deviceDraft.id", &device_id_)) {
       // Now get access_token and refresh_token
-      response = PostFormData(GetOAuthURL("token"), {
+      response = http::PostFormData(GetOAuthURL("token"), {
         {"code", auth_code},
         {"client_id", client_id_},
         {"client_secret", client_secret_},
         {"redirect_uri", "oob"},
         {"scope", "https://www.googleapis.com/auth/clouddevices"},
         {"grant_type", "authorization_code"}
-      });
+      }, transport_);
       if (!response)
         return false;
 
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h
index 88a655b..6eea8eb 100644
--- a/buffet/device_registration_info.h
+++ b/buffet/device_registration_info.h
@@ -5,13 +5,15 @@
 #ifndef BUFFET_DEVICE_INFO_H_
 #define BUFFET_DEVICE_INFO_H_
 
-#include <base/basictypes.h>
-#include <base/time/time.h>
 #include <string>
 #include <map>
 #include <memory>
 
+#include <base/basictypes.h>
+#include <base/time/time.h>
+
 #include "buffet/data_encoding.h"
+#include "buffet/http_transport.h"
 
 namespace base {
   class Value;
@@ -22,7 +24,11 @@
 // The DeviceRegistrationInfo class represents device registration information.
   class DeviceRegistrationInfo {
  public:
-   DeviceRegistrationInfo() = default;
+   // Default-constructed uses CURL HTTP transport.
+   DeviceRegistrationInfo();
+   // This constructor allows to pass in a custom HTTP transport
+   // (mainly for testing).
+   DeviceRegistrationInfo(std::shared_ptr<chromeos::http::Transport> transport);
 
   // Returns the authorization HTTP header that can be used to talk
   // to GCD server for authenticated device communication.
@@ -114,6 +120,9 @@
   std::string system_name_ = "coffee_pot";
   std::string display_name_ = "Coffee Pot";
 
+  // HTTP transport used for communications.
+  std::shared_ptr<chromeos::http::Transport> transport_;
+
   DISALLOW_COPY_AND_ASSIGN(DeviceRegistrationInfo);
 };
 
diff --git a/buffet/http_connection.h b/buffet/http_connection.h
new file mode 100644
index 0000000..318afdc
--- /dev/null
+++ b/buffet/http_connection.h
@@ -0,0 +1,83 @@
+// 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.
+
+#ifndef BUFFET_HTTP_CONNECTION_H_
+#define BUFFET_HTTP_CONNECTION_H_
+
+#include <string>
+#include <vector>
+
+#include <base/basictypes.h>
+
+#include "buffet/http_transport.h"
+
+namespace chromeos {
+namespace http {
+
+///////////////////////////////////////////////////////////////////////////////
+// Connection class is the base class for HTTP comminication session.
+// It abstracts the implementation of underlying transport library (ex libcurl).
+// When the Connection-derived class is constructed, it is pre-set up with
+// basic initialization information necessary to initiate the server request
+// connection (such as the URL, request method, etc - see
+// Transport::CreateConnection() for more details). But most implementations
+// would not probably initiate the physical connection until SendHeaders
+// is called.
+// You normally shouldn't worry about using this class directly.
+// http::Request and http::Response classes use it for communictaion.
+///////////////////////////////////////////////////////////////////////////////
+class Connection {
+ public:
+  Connection(std::shared_ptr<Transport> transport) : transport_(transport) {}
+  virtual ~Connection() = default;
+
+  // Called by http::Request to initiate the connection with the server.
+  // This normally opens the socket and sends the request headers.
+  virtual bool SendHeaders(const HeaderList& headers) = 0;
+  // If needed, this function can be called to send the request body data.
+  // This function can be called repeatedly until all data is sent.
+  virtual bool WriteRequestData(const void* data, size_t size) = 0;
+  // This function is called when all the data is sent off and it's time
+  // to receive the response data.
+  virtual bool FinishRequest() = 0;
+
+  // Returns the HTTP status code (e.g. 200 for success).
+  virtual int GetResponseStatusCode() const = 0;
+  // Returns the status text (e.g. for error 403 it could be "NOT AUTHORIZED").
+  virtual std::string GetResponseStatusText() const = 0;
+  // Returns the HTTP protocol version (e.g. "HTTP/1.1").
+  virtual std::string GetProtocolVersion() const = 0;
+  // Returns the value of particular response header, or empty string if the
+  // headers wasn't received.
+  virtual std::string GetResponseHeader(
+      const std::string& header_name) const = 0;
+  // Returns the response data size, if known. For chunked (streaming)
+  // transmission this might not be known until all the data is sent.
+  // In this case GetResponseDataSize() will return 0.
+  virtual uint64_t GetResponseDataSize() const = 0;
+  // This function is called to read a block of response data.
+  // It needs to be called repeatedly until it returns false or |size_read| is
+  // set to 0. |data| is the destination buffer to read the data into.
+  // |buffer_size| is the size of the buffer (amount of data to read).
+  // |read_size| is the amount of data actually read, which could be less than
+  // the size requested or 0 if there is no more data available.
+  virtual bool ReadResponseData(void* data, size_t buffer_size,
+                                size_t* size_read) = 0;
+  // Returns additional error information if any of the above functions fail.
+  virtual std::string GetErrorMessage() const = 0;
+
+ protected:
+  // |transport_| is mainly used to keep the object alive as long as the
+  // connection exists. But some implementations of Connection could use
+  // the Transport-derived class for their own needs as well.
+  std::shared_ptr<Transport> transport_;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Connection);
+};
+
+} // namespace http
+} // namespace chromeos
+
+#endif // BUFFET_HTTP_CONNECTION_H_
diff --git a/buffet/http_connection_curl.cc b/buffet/http_connection_curl.cc
new file mode 100644
index 0000000..59edc15
--- /dev/null
+++ b/buffet/http_connection_curl.cc
@@ -0,0 +1,223 @@
+// 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 "buffet/http_connection_curl.h"
+
+#include <base/logging.h>
+
+#include "buffet/http_request.h"
+#include "buffet/string_utils.h"
+
+using namespace chromeos;
+using namespace chromeos::http::curl;
+
+static int curl_trace(CURL *handle, curl_infotype type,
+                      char *data, size_t size, void *userp) {
+  std::string msg(data, size);
+
+  switch (type) {
+  case CURLINFO_TEXT:
+    VLOG(3) << "== Info: " << msg;
+    break;
+  case CURLINFO_HEADER_OUT:
+    VLOG(3) << "=> Send headers:\n" << msg;
+    break;
+  case CURLINFO_DATA_OUT:
+    VLOG(3) << "=> Send data:\n" << msg;
+    break;
+  case CURLINFO_SSL_DATA_OUT:
+    VLOG(3) << "=> Send SSL data" << msg;
+    break;
+  case CURLINFO_HEADER_IN:
+    VLOG(3) << "<= Recv header: " << msg;
+    break;
+  case CURLINFO_DATA_IN:
+    VLOG(3) << "<= Recv data:\n" << msg;
+    break;
+  case CURLINFO_SSL_DATA_IN:
+    VLOG(3) << "<= Recv SSL data" << msg;
+    break;
+  default:
+    break;
+  }
+  return 0;
+}
+
+Connection::Connection(CURL* curl_handle, const std::string& method,
+                       std::shared_ptr<http::Transport> transport) :
+    http::Connection(transport), method_(method), curl_handle_(curl_handle) {
+  VLOG(1) << "curl::Connection created: " << method_;
+}
+
+Connection::~Connection() {
+  VLOG(1) << "curl::Connection destroyed";
+}
+
+bool Connection::SendHeaders(const HeaderList& headers) {
+  headers_.insert(headers.begin(), headers.end());
+  return true;
+}
+
+bool Connection::WriteRequestData(const void* data, size_t size) {
+  if (size > 0) {
+    auto data_ptr = reinterpret_cast<const unsigned char*>(data);
+    request_data_.insert(request_data_.end(), data_ptr, data_ptr + size);
+  }
+  return true;
+}
+
+bool Connection::FinishRequest() {
+  if (VLOG_IS_ON(3)) {
+    curl_easy_setopt(curl_handle_, CURLOPT_DEBUGFUNCTION, curl_trace);
+    curl_easy_setopt(curl_handle_, CURLOPT_VERBOSE, 1L);
+  }
+
+  // Set up HTTP request data.
+  if (method_ == request_type::kPut) {
+    curl_easy_setopt(curl_handle_, CURLOPT_INFILESIZE_LARGE,
+                      curl_off_t(request_data_.size()));
+  } else {
+    curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE_LARGE,
+                      curl_off_t(request_data_.size()));
+  }
+  if (!request_data_.empty()) {
+    curl_easy_setopt(curl_handle_,
+                     CURLOPT_READFUNCTION, &Connection::read_callback);
+    curl_easy_setopt(curl_handle_, CURLOPT_READDATA, this);
+    VLOG(2) << "Raw request data: "
+        << std::string(reinterpret_cast<const char*>(request_data_.data()),
+                       request_data_.size());
+  }
+
+  curl_slist* header_list = nullptr;
+  if (!headers_.empty()) {
+    for (auto pair : headers_) {
+      std::string header = string_utils::Join(": ", pair.first, pair.second);
+      VLOG(2) << "Request header: " << header;
+      header_list = curl_slist_append(header_list, header.c_str());
+    }
+    curl_easy_setopt(curl_handle_, CURLOPT_HTTPHEADER, header_list);
+  }
+
+  headers_.clear();
+
+  // Set up HTTP response data.
+  if (method_ != request_type::kHead) {
+    curl_easy_setopt(curl_handle_,
+                     CURLOPT_WRITEFUNCTION, &Connection::write_callback);
+    curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this);
+  }
+
+  // HTTP response headers
+  curl_easy_setopt(curl_handle_,
+                   CURLOPT_HEADERFUNCTION, &Connection::header_callback);
+  curl_easy_setopt(curl_handle_, CURLOPT_HEADERDATA, this);
+
+  CURLcode ret = curl_easy_perform(curl_handle_);
+  if (header_list)
+    curl_slist_free_all(header_list);
+  if (ret != CURLE_OK) {
+    error_ = curl_easy_strerror(ret);
+    LOG(ERROR) << "CURL request failed: " << error_;
+  } else {
+    LOG(INFO) << "Response: " << GetResponseStatusCode() << " ("
+      << GetResponseStatusText() << ")";
+    VLOG(2) << "Response data (" << response_data_.size() << "): "
+        << std::string(reinterpret_cast<const char*>(response_data_.data()),
+                       response_data_.size());
+  }
+  return (ret == CURLE_OK);
+}
+
+int Connection::GetResponseStatusCode() const {
+  long status_code = 0;
+  curl_easy_getinfo(curl_handle_, CURLINFO_RESPONSE_CODE, &status_code);
+  return status_code;
+}
+
+std::string Connection::GetResponseStatusText() const {
+  return status_text_;
+}
+
+std::string Connection::GetProtocolVersion() const {
+  return protocol_version_;
+}
+
+std::string Connection::GetResponseHeader(
+    const std::string& header_name) const {
+  auto p = headers_.find(header_name);
+  return p != headers_.end() ? p->second : std::string();
+}
+
+uint64_t Connection::GetResponseDataSize() const {
+  return response_data_.size();
+}
+
+bool Connection::ReadResponseData(void* data, size_t buffer_size,
+                                  size_t* size_read) {
+  size_t size_to_read = response_data_.size() - response_data_ptr_;
+  if (size_to_read > buffer_size)
+    size_to_read = buffer_size;
+  memcpy(data, response_data_.data() + response_data_ptr_, size_to_read);
+  if (size_read)
+    *size_read = size_to_read;
+  response_data_ptr_ += size_to_read;
+  return true;
+}
+
+std::string Connection::GetErrorMessage() const {
+  return error_;
+}
+
+size_t Connection::write_callback(char* ptr, size_t size,
+                                  size_t num, void* data) {
+  Connection* me = reinterpret_cast<Connection*>(data);
+  size_t data_len = size * num;
+  me->response_data_.insert(me->response_data_.end(), ptr, ptr + data_len);
+  return data_len;
+}
+
+size_t Connection::read_callback(char* ptr, size_t size,
+                                 size_t num, void* data) {
+  Connection* me = reinterpret_cast<Connection*>(data);
+  size_t data_len = size * num;
+
+  if (me->request_data_ptr_ >= me->request_data_.size())
+    return 0;
+
+  if (me->request_data_ptr_ + data_len > me->request_data_.size())
+    data_len = me->request_data_.size() - me->request_data_ptr_;
+
+  memcpy(ptr, me->request_data_.data() + me->request_data_ptr_, data_len);
+  me->request_data_ptr_ += data_len;
+
+  return data_len;
+}
+
+size_t Connection::header_callback(char* ptr, size_t size,
+                                   size_t num, void* data) {
+  Connection* me = reinterpret_cast<Connection*>(data);
+  size_t hdr_len = size * num;
+  std::string header(ptr, int(hdr_len));
+  // Remove newlines at the end of header line.
+  while (!header.empty() && (header.back() == '\r' || header.back() == '\n')) {
+    header.pop_back();
+  }
+
+  VLOG(2) << "Response header: " << header;
+
+  if (!me->status_text_set_) {
+    // First header - response code as "HTTP/1.1 200 OK".
+    // Need to extract the OK part
+    auto pair = string_utils::SplitAtFirst(header, ' ');
+    me->protocol_version_ = pair.first;
+    me->status_text_ = string_utils::SplitAtFirst(pair.second, ' ').second;
+    me->status_text_set_ = true;
+  } else {
+    auto pair = string_utils::SplitAtFirst(header, ':');
+    if (!pair.second.empty())
+      me->headers_.insert(pair);
+  }
+  return hdr_len;
+}
diff --git a/buffet/http_connection_curl.h b/buffet/http_connection_curl.h
new file mode 100644
index 0000000..0cf34e8
--- /dev/null
+++ b/buffet/http_connection_curl.h
@@ -0,0 +1,88 @@
+// 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.
+
+#ifndef BUFFET_HTTP_CONNECTION_CURL_H_
+#define BUFFET_HTTP_CONNECTION_CURL_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/basictypes.h>
+#include <curl/curl.h>
+
+#include "buffet/http_connection.h"
+
+namespace chromeos {
+namespace http {
+namespace curl {
+
+// This is a libcurl-based implementation of http::Connection.
+class Connection : public chromeos::http::Connection {
+ public:
+  Connection(CURL* curl_handle, const std::string& method,
+             std::shared_ptr<http::Transport> transport);
+  virtual ~Connection();
+
+  // Overrides from http::Connection.
+  // See http_connection.h for description of these methods.
+  virtual bool SendHeaders(const HeaderList& headers) override;
+  virtual bool WriteRequestData(const void* data, size_t size) override;
+  virtual bool FinishRequest() override;
+
+  virtual int GetResponseStatusCode() const override;
+  virtual std::string GetResponseStatusText() const override;
+  virtual std::string GetProtocolVersion() const override;
+  virtual std::string GetResponseHeader(
+     const std::string& header_name) const override;
+  virtual uint64_t GetResponseDataSize() const override;
+  virtual bool ReadResponseData(void* data, size_t buffer_size,
+                                size_t* size_read) override;
+  virtual std::string GetErrorMessage() const override;
+
+ protected:
+  // Write data callback. Used by CURL when receiving response data.
+  static size_t write_callback(char* ptr, size_t size, size_t num, void* data);
+  // Read data callback. Used by CURL when sending request body data.
+  static size_t read_callback(char* ptr, size_t size, size_t num, void* data);
+  // Write header data callback. Used by CURL when receiving response headers.
+  static size_t header_callback(char* ptr, size_t size, size_t num, void* data);
+
+  // HTTP request verb, such as "GET", "POST", "PUT", ...
+  std::string method_;
+
+  // Binary data for request body.
+  std::vector<unsigned char> request_data_;
+  // Read pointer for request data. Used when streaming data to the server.
+  size_t request_data_ptr_ = 0;
+
+  // Received response data.
+  std::vector<unsigned char> response_data_;
+  size_t response_data_ptr_ = 0;
+
+  // List of optional request headers provided by the caller.
+  // After request has been sent, contains the received response headers.
+  std::map<std::string, std::string> headers_;
+
+  // CURL error message in case request fails completely.
+  std::string error_;
+  // HTTP protocol version, such as HTTP/1.1
+  std::string protocol_version_;
+  // Reponse status text, such as "OK" for 200, or "Forbidden" for 403
+  std::string status_text_;
+  // Flag used when parsing response headers to separate the response status
+  // from the rest of response headers.
+  bool status_text_set_ = false;
+
+  CURL* curl_handle_ = nullptr;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Connection);
+};
+
+}  // namespace curl
+}  // namespace http
+}  // namespace chromeos
+
+#endif // BUFFET_HTTP_CONNECTION_CURL_H_
diff --git a/buffet/http_request.cc b/buffet/http_request.cc
index db1ecb9..11dafff 100644
--- a/buffet/http_request.cc
+++ b/buffet/http_request.cc
@@ -4,8 +4,13 @@
 
 #include "buffet/http_request.h"
 
+#include <base/logging.h>
+
+#include "buffet/http_connection_curl.h"
 #include "buffet/http_transport_curl.h"
+#include "buffet/map_utils.h"
 #include "buffet/mime_utils.h"
+#include "buffet/string_utils.h"
 
 using namespace chromeos;
 using namespace chromeos::http;
@@ -98,167 +103,200 @@
 //**************************************************************************
 //********************** Request Class **********************
 //**************************************************************************
-Request::Request(const std::string& url, const char* method) :
-  transport_(new curl::Transport(url, method)) {
+Request::Request(const std::string& url, const char* method,
+                 std::shared_ptr<Transport> transport) :
+    transport_(transport), request_url_(url), method_(method) {
+  VLOG(1) << "http::Request created";
+  if (!transport_)
+    transport_.reset(new http::curl::Transport());
 }
 
-Request::Request(const std::string& url) :
-  transport_(new curl::Transport(url, nullptr)) {
-}
-
-Request::Request(std::shared_ptr<TransportInterface> transport) :
-  transport_(transport) {
+Request::~Request() {
+  VLOG(1) << "http::Request destroyed";
 }
 
 void Request::AddRange(int64_t bytes) {
-  if (transport_)
-    transport_->AddRange(bytes);
+  if (bytes < 0) {
+    ranges_.emplace_back(Request::range_value_omitted, -bytes);
+  } else {
+    ranges_.emplace_back(bytes, Request::range_value_omitted);
+  }
 }
 
 void Request::AddRange(uint64_t from_byte, uint64_t to_byte) {
-  if (transport_)
-    transport_->AddRange(from_byte, to_byte);
+  ranges_.emplace_back(from_byte, to_byte);
 }
 
 std::unique_ptr<Response> Request::GetResponse() {
-  if (transport_) {
-    if (transport_->GetStage() == TransportInterface::Stage::initialized) {
-      if(transport_->Perform())
-        return std::unique_ptr<Response>(new Response(transport_));
-    } else if (transport_->GetStage() ==
-               TransportInterface::Stage::response_received) {
-      return std::unique_ptr<Response>(new Response(transport_));
-    }
-  }
-  return std::unique_ptr<Response>();
+  if (!SendRequestIfNeeded() || !connection_->FinishRequest())
+    return std::unique_ptr<Response>();
+  std::unique_ptr<Response> response(new Response(std::move(connection_)));
+  transport_.reset(); // Indicate that the response has been received
+  return response;
 }
 
 void Request::SetAccept(const char* accept_mime_types) {
-  if (transport_)
-    transport_->SetAccept(accept_mime_types);
+  accept_ = accept_mime_types;
 }
 
 std::string Request::GetAccept() const {
-  return transport_ ? transport_->GetAccept() : std::string();
-}
-
-std::string Request::GetRequestURL() const {
-  return transport_ ? transport_->GetRequestURL() : std::string();
+  return accept_;
 }
 
 void Request::SetContentType(const char* contentType) {
-  if (transport_)
-    transport_->SetContentType(contentType);
+  content_type_ = contentType;
 }
 
 std::string Request::GetContentType() const {
-  return transport_ ? transport_->GetContentType() : std::string();
+  return content_type_;
 }
 
 void Request::AddHeader(const char* header, const char* value) {
-  if (transport_)
-    transport_->AddHeader(header, value);
+  headers_[header] = value;
 }
 
 void Request::AddHeaders(const HeaderList& headers) {
-  for (auto&& pair : headers)
-    AddHeader(pair.first.c_str(), pair.second.c_str());
+  headers_.insert(headers.begin(), headers.end());
 }
 
 bool Request::AddRequestBody(const void* data, size_t size) {
-  return transport_ && transport_->AddRequestBody(data, size);
-}
-
-void Request::SetMethod(const char* method) {
-  if (transport_)
-    transport_->SetMethod(method);
-}
-
-std::string Request::GetMethod() const {
-  return transport_ ? transport_->GetMethod() : std::string();
+  if (!SendRequestIfNeeded())
+    return false;
+  bool ret = connection_->WriteRequestData(data, size);
+  if (!ret)
+    error_ = "Failed to send request data";
+  return ret;
 }
 
 void Request::SetReferer(const char* referer) {
-  if (transport_)
-    transport_->SetReferer(referer);
+  referer_ = referer;
 }
 
 std::string Request::GetReferer() const {
-  return transport_ ? transport_->GetReferer() : std::string();
+  return referer_;
 }
 
 void Request::SetUserAgent(const char* user_agent) {
-  if (transport_)
-    transport_->SetUserAgent(user_agent);
+  user_agent_ = user_agent;
 }
 
 std::string Request::GetUserAgent() const {
-  return transport_ ? transport_->GetUserAgent() : std::string();
+  return user_agent_;
 }
 
 std::string Request::GetErrorMessage() const {
-  if (transport_ &&
-      transport_->GetStage() == TransportInterface::Stage::failed) {
-    return transport_->GetErrorMessage();
-  }
-
-  return std::string();
+  return error_;
 }
 
-//**************************************************************************
-//********************** Response Class **********************
-//**************************************************************************
-Response::Response(std::shared_ptr<TransportInterface> transport) :
-    transport_(transport) {
-}
+bool Request::SendRequestIfNeeded() {
+  if (transport_) {
+    if (!connection_) {
+      chromeos::http::HeaderList headers = MapToVector(headers_);
+      std::vector<std::string> ranges;
+      if (method_ != request_type::kHead) {
+        ranges.reserve(ranges_.size());
+        for (auto p : ranges_) {
+          if (p.first != range_value_omitted ||
+              p.second != range_value_omitted) {
+            std::string range;
+            if (p.first != range_value_omitted) {
+              range = std::to_string(p.first);
+            }
+            range += '-';
+            if (p.second != range_value_omitted) {
+              range += std::to_string(p.second);
+            }
+            ranges.push_back(range);
+          }
+        }
+      }
+      if (!ranges.empty())
+        headers.emplace_back(request_header::kRange,
+                             "bytes=" + string_utils::Join(',', ranges));
 
-bool Response::IsSuccessful() const {
-  if (transport_ &&
-      transport_->GetStage() == TransportInterface::Stage::response_received) {
-    int code = GetStatusCode();
-    return code >= status_code::Continue && code < status_code::BadRequest;
+      headers.emplace_back(request_header::kAccept, GetAccept());
+      if (method_ != request_type::kGet && method_ != request_type::kHead) {
+        if (!content_type_.empty())
+          headers.emplace_back(request_header::kContentType, content_type_);
+      }
+      connection_ = transport_->CreateConnection(transport_, request_url_,
+                                                 method_, headers,
+                                                 user_agent_, referer_,
+                                                 &error_);
+    }
+
+    if (connection_)
+      return true;
+  } else {
+    error_ = "HTTP response already received";
+    LOG(ERROR) << error_;
   }
   return false;
 }
 
+
+//**************************************************************************
+//********************** Response Class **********************
+//**************************************************************************
+Response::Response(std::unique_ptr<Connection> connection) :
+    connection_(std::move(connection)) {
+  VLOG(1) << "http::Response created";
+  // Response object doesn't have streaming interface for response data (yet),
+  // so read the data into a buffer and cache it.
+  if (connection_) {
+    size_t size = static_cast<size_t>(connection_->GetResponseDataSize());
+    response_data_.reserve(size);
+    unsigned char buffer[1024];
+    size_t read = 0;
+    while (connection_->ReadResponseData(buffer, sizeof(buffer), &read) &&
+           read > 0) {
+      response_data_.insert(response_data_.end(), buffer, buffer + read);
+    }
+  }
+}
+
+Response::~Response() {
+  VLOG(1) << "http::Response destroyed";
+}
+
+bool Response::IsSuccessful() const {
+  int code = GetStatusCode();
+  return code >= status_code::Continue && code < status_code::BadRequest;
+}
+
 int Response::GetStatusCode() const {
-  if (!transport_)
+  if (!connection_)
     return -1;
 
-  return transport_->GetResponseStatusCode();
+  return connection_->GetResponseStatusCode();
 }
 
 std::string Response::GetStatusText() const {
-  if (!transport_)
+  if (!connection_)
     return std::string();
 
-  return transport_->GetResponseStatusText();
+  return connection_->GetResponseStatusText();
 }
 
 std::string Response::GetContentType() const {
   return GetHeader(response_header::kContentType);
 }
 
-std::vector<unsigned char> Response::GetData() const {
-  if (transport_)
-    return transport_->GetResponseData();
-
-  return std::vector<unsigned char>();
+const std::vector<unsigned char>& Response::GetData() const {
+  return response_data_;
 }
 
 std::string Response::GetDataAsString() const {
-  if (transport_) {
-    auto data = transport_->GetResponseData();
-    const char* data_buf = reinterpret_cast<const char*>(data.data());
-    return std::string(data_buf, data_buf + data.size());
-  }
+  if (response_data_.empty())
+    return std::string();
 
-  return std::string();
+  const char* data_buf = reinterpret_cast<const char*>(response_data_.data());
+  return std::string(data_buf, data_buf + response_data_.size());
 }
 
 std::string Response::GetHeader(const char* header_name) const {
-  if (transport_)
-    return transport_->GetResponseHeader(header_name);
+  if (connection_)
+    return connection_->GetResponseHeader(header_name);
 
   return std::string();
 }
diff --git a/buffet/http_request.h b/buffet/http_request.h
index 185c84e..a93af94 100644
--- a/buffet/http_request.h
+++ b/buffet/http_request.h
@@ -5,12 +5,15 @@
 #ifndef BUFFET_HTTP_REQUEST_H_
 #define BUFFET_HTTP_REQUEST_H_
 
-#include <vector>
+#include <map>
 #include <memory>
 #include <string>
+#include <vector>
+
 #include <base/basictypes.h>
 
-#include "buffet/transport_interface.h"
+#include "buffet/http_connection.h"
+#include "buffet/http_transport.h"
 
 namespace chromeos {
 namespace http {
@@ -192,7 +195,7 @@
   static const int VersionNotSupported = 505;
 } // namespace status_code
 
-class Response; // Just a forward-declarartion
+class Response; // Just a forward declarartion.
 
 ///////////////////////////////////////////////////////////////////////////////
 // Request class is the main object used to set up and initiate an HTTP
@@ -207,15 +210,11 @@
 class Request {
  public:
   // The main constructor. |url| specifies the remote host address/path
-  // to send the request to. Optional |method| is the HTTP request verb. If
-  // omitted, "GET" is used.
-  // Uses the default libcurl-based implementation of TransportInterface
-  Request(const std::string& url, const char* method);
-  Request(const std::string& url);
-
-  // Custom constructor that allows non-default implementations
-  // of TransportInterface to be used.
-  Request(std::shared_ptr<TransportInterface> transport);
+  // to send the request to. |method| is the HTTP request verb and
+  // |transport| is the HTTP transport implementation for server communications.
+  Request(const std::string& url, const char* method,
+          std::shared_ptr<Transport> transport);
+  ~Request();
 
   // Gets/Sets "Accept:" header value. The default value is "*/*" if not set.
   void SetAccept(const char* accept_mime_types);
@@ -247,10 +246,6 @@
   // All individual ranges will be sent as part of "Range:" HTTP request header.
   void AddRange(uint64_t from_byte, uint64_t to_byte);
 
-  // Gets/Sets an HTTP request verb to be used with request
-  void SetMethod(const char* method);
-  std::string GetMethod() const;
-
   // Returns the request URL
   std::string GetRequestURL() const;
 
@@ -273,7 +268,48 @@
   std::string GetErrorMessage() const;
 
  private:
-  std::shared_ptr<TransportInterface> transport_;
+  // Helper function to create an http::Connection and send off request headers.
+  bool SendRequestIfNeeded();
+
+  // Implementation that provides particular HTTP transport.
+  std::shared_ptr<Transport> transport_;
+
+  // An established connection for adding request body. This connection
+  // is maintained by the request object after the headers have been
+  // sent and before the response is requested.
+  std::unique_ptr<Connection> connection_;
+
+  // Full request URL, such as "http://www.host.com/path/to/object"
+  std::string request_url_;
+  // HTTP request verb, such as "GET", "POST", "PUT", ...
+  std::string method_;
+
+  // Referrer URL, if any. Sent to the server via "Referer: " header.
+  std::string referer_;
+  // User agent string, if any. Sent to the server via "User-Agent: " header.
+  std::string user_agent_;
+  // Content type of the request body data.
+  // Sent to the server via "Content-Type: " header.
+  std::string content_type_;
+  // List of acceptable response data types.
+  // Sent to the server via "Accept: " header.
+  std::string accept_ = "*/*";
+
+  // List of optional request headers provided by the caller.
+  // After request has been sent, contains the received response headers.
+  std::map<std::string, std::string> headers_;
+  // List of optional data ranges to request partial content from the server.
+  // Sent to thr server as "Range: " header.
+  std::vector<std::pair<uint64_t, uint64_t>> ranges_;
+
+  // range_value_omitted is used in |ranges_| list to indicate omitted value.
+  // E.g. range (10,range_value_omitted) represents bytes from 10 to the end
+  // of the data stream.
+  static const uint64_t range_value_omitted = (uint64_t)-1;
+
+  // Error message in case request fails completely.
+  std::string error_;
+
   DISALLOW_COPY_AND_ASSIGN(Request);
 };
 
@@ -284,7 +320,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 class Response {
  public:
-  Response(std::shared_ptr<TransportInterface> transport);
+  Response(std::unique_ptr<Connection> connection);
+  ~Response();
 
   // Returns true if server returned a success code (status code below 400).
   bool IsSuccessful() const;
@@ -299,7 +336,7 @@
   std::string GetContentType() const;
 
   // Returns response data as a byte array
-  std::vector<unsigned char> GetData() const;
+  const std::vector<unsigned char>& GetData() const;
 
   // Returns response data as a string
   std::string GetDataAsString() const;
@@ -308,7 +345,8 @@
   std::string GetHeader(const char* header_name) const;
 
  private:
-  std::shared_ptr<TransportInterface> transport_;
+  std::unique_ptr<Connection> connection_;
+  std::vector<unsigned char> response_data_;
   DISALLOW_COPY_AND_ASSIGN(Response);
 };
 
diff --git a/buffet/http_transport.h b/buffet/http_transport.h
new file mode 100644
index 0000000..752dcbe
--- /dev/null
+++ b/buffet/http_transport.h
@@ -0,0 +1,51 @@
+// 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.
+
+#ifndef BUFFET_HTTP_TRANSPORT_H_
+#define BUFFET_HTTP_TRANSPORT_H_
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <base/basictypes.h>
+
+namespace chromeos {
+namespace http {
+
+typedef std::vector<std::pair<std::string, std::string>> HeaderList;
+
+class Request;
+class Connection;
+
+///////////////////////////////////////////////////////////////////////////////
+// Transport is a base class for specific implementation of HTTP communication.
+// This class (and its underlying implementation) is used by http::Request and
+// http::Response classes to provide HTTP functionality to the clients.
+///////////////////////////////////////////////////////////////////////////////
+class Transport {
+ public:
+  Transport() = default;
+  virtual ~Transport() = default;
+
+  // Creates a connection object and initializes it with the specified data.
+  // |transport| is a shared pointer to this transport object instance,
+  // used to maintain the object alive as long as the connection exists.
+  virtual std::unique_ptr<Connection> CreateConnection(
+      std::shared_ptr<Transport> transport,
+      const std::string& url,
+      const std::string& method,
+      const HeaderList& headers,
+      const std::string& user_agent,
+      const std::string& referer,
+      std::string* error_msg) = 0;
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(Transport);
+};
+
+} // namespace http
+} // namespace chromeos
+
+#endif // BUFFET_HTTP_TRANSPORT_H_
diff --git a/buffet/http_transport_curl.cc b/buffet/http_transport_curl.cc
index be204d9..8c4b946 100644
--- a/buffet/http_transport_curl.cc
+++ b/buffet/http_transport_curl.cc
@@ -4,317 +4,73 @@
 
 #include "buffet/http_transport_curl.h"
 
-#define __STDC_FORMAT_MACROS
-#include <inttypes.h>
-#include <string.h>
 #include <base/logging.h>
 
-#include "buffet/mime_utils.h"
-#include "buffet/string_utils.h"
-#include "buffet/map_utils.h"
+#include "buffet/http_connection_curl.h"
+#include "buffet/http_request.h"
 
 using namespace chromeos;
 using namespace chromeos::http::curl;
 
-#define VERBOSE_CURL 0  // Set to 1 to log advanced debugging info for CURL
-
-#if VERBOSE_CURL
-static int curl_trace(CURL *handle, curl_infotype type,
-                      char *data, size_t size, void *userp) {
-  std::string msg(data, size);
-
-  switch (type) {
-  case CURLINFO_TEXT:
-    LOG(INFO) << "== Info: " << msg;
-    break;
-  case CURLINFO_HEADER_OUT:
-    LOG(INFO) << "=> Send headers:\n" << msg;
-    break;
-  case CURLINFO_DATA_OUT:
-    LOG(INFO) << "=> Send data:\n" << msg;
-    break;
-  case CURLINFO_SSL_DATA_OUT:
-    LOG(INFO) << "=> Send SSL data" << msg;
-    break;
-  case CURLINFO_HEADER_IN:
-    LOG(INFO) << "<= Recv header: " << msg;
-    break;
-  case CURLINFO_DATA_IN:
-    LOG(INFO) << "<= Recv data:\n" << msg;
-    break;
-  case CURLINFO_SSL_DATA_IN:
-    LOG(INFO) << "<= Recv SSL data" << msg;
-    break;
-  default:
-    break;
-  }
-  return 0;
-}
-#endif
-
-Transport::Transport(const std::string& url, const char* method) :
-    request_url_(url),
-    method_(method ? method : request_type::kGet) {
-  stage_ = Stage::initialized;
+Transport::Transport() {
+  VLOG(1) << "curl::Transport created";
 }
 
 Transport::~Transport() {
-  Close();
+  VLOG(1) << "curl::Transport destroyed";
 }
 
-void Transport::AddRange(int64_t bytes) {
-  if (bytes < 0) {
-    ranges_.emplace_back(Transport::range_value_omitted, -bytes);
-  } else {
-    ranges_.emplace_back(bytes, Transport::range_value_omitted);
-  }
-}
-
-void Transport::AddRange(uint64_t fromByte, uint64_t toByte) {
-  ranges_.emplace_back(fromByte, toByte);
-}
-
-std::string Transport::GetAccept() const {
-  return accept_;
-}
-
-chromeos::http::HeaderList Transport::GetHeaders() const {
-  chromeos::http::HeaderList headers = MapToVector(headers_);
-  std::vector<std::string> ranges;
-  if (method_ != request_type::kHead) {
-    ranges.reserve(ranges_.size());
-    for (auto p : ranges_) {
-      if (p.first != range_value_omitted || p.second != range_value_omitted) {
-        std::string range;
-        if (p.first != range_value_omitted) {
-          range = std::to_string(p.first);
-        }
-        range += '-';
-        if (p.second != range_value_omitted) {
-          range += std::to_string(p.second);
-        }
-        ranges.push_back(range);
-      }
-    }
-  }
-  if (!ranges.empty())
-    headers.emplace_back(request_header::kRange,
-                         "bytes=" + string_utils::Join(',', ranges));
-
-  headers.emplace_back(request_header::kAccept, GetAccept());
-
-  return headers;
-}
-
-void Transport::AddHeader(const char* header, const char* value) {
-  headers_[header] = value;
-}
-
-void Transport::RemoveHeader(const char* header) {
-  AddHeader(header, "");
-}
-
-bool Transport::AddRequestBody(const void* data, size_t size) {
-  if (size == 0)
-    return true;
-
-  if (data == nullptr) {
-    LOG(ERROR) << "Invalid request body data pointer";
-    return false;
-  }
-
-  const unsigned char* data_ptr = reinterpret_cast<const unsigned char*>(data);
-  request_data_.insert(request_data_.end(), data_ptr, data_ptr + size);
-  return true;
-}
-
-bool Transport::Perform() {
-  if (stage_ != Stage::initialized) {
-    LOG(ERROR) << "Cannot call Perform() on unintialized transport object";
-    return false;
-  }
-
-  curl_handle_ = curl_easy_init();
-  if (!curl_handle_) {
+std::unique_ptr<http::Connection> Transport::CreateConnection(
+    std::shared_ptr<http::Transport> transport,
+    const std::string& url,
+    const std::string& method,
+    const HeaderList& headers,
+    const std::string& user_agent,
+    const std::string& referer,
+    std::string* error_msg) {
+  CURL* curl_handle = curl_easy_init();
+  if (!curl_handle) {
     LOG(ERROR) << "Failed to initialize CURL";
-    return false;
+    if (error_msg)
+      *error_msg = "Failed to initialize CURL";
+    return std::unique_ptr<http::Connection>();
   }
 
-  LOG(INFO) << "Sending a " << method_ << " request to " << request_url_;
-  curl_easy_setopt(curl_handle_, CURLOPT_URL, request_url_.c_str());
+  LOG(INFO) << "Sending a " << method << " request to " << url;
+  curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str());
 
-#if VERBOSE_CURL
-  curl_easy_setopt(curl_handle_, CURLOPT_DEBUGFUNCTION, curl_trace);
-  curl_easy_setopt(curl_handle_, CURLOPT_VERBOSE, 1L);
-#endif
-
-  if (!user_agent_.empty()) {
-    curl_easy_setopt(curl_handle_,
-                     CURLOPT_USERAGENT, user_agent_.c_str());
+  if (!user_agent.empty()) {
+    curl_easy_setopt(curl_handle,
+                     CURLOPT_USERAGENT, user_agent.c_str());
   }
 
-  if (!referer_.empty()) {
-    curl_easy_setopt(curl_handle_,
-                     CURLOPT_REFERER, referer_.c_str());
+  if (!referer.empty()) {
+    curl_easy_setopt(curl_handle,
+                     CURLOPT_REFERER, referer.c_str());
   }
 
   // Setup HTTP request method and optional request body.
-  if (method_ == request_type::kGet) {
-    curl_easy_setopt(curl_handle_, CURLOPT_HTTPGET, 1L);
-  } else if (method_ == request_type::kHead) {
-    curl_easy_setopt(curl_handle_, CURLOPT_NOBODY, 1L);
-  } else if (method_ == request_type::kPut) {
-    curl_easy_setopt(curl_handle_, CURLOPT_UPLOAD, 1L);
-    curl_easy_setopt(curl_handle_, CURLOPT_INFILESIZE_LARGE,
-                     curl_off_t(request_data_.size()));
-    curl_easy_setopt(curl_handle_,
-                     CURLOPT_READFUNCTION, &Transport::read_callback);
-    curl_easy_setopt(curl_handle_, CURLOPT_READDATA, this);
+  if (method ==  request_type::kGet) {
+    curl_easy_setopt(curl_handle, CURLOPT_HTTPGET, 1L);
+  } else if (method == request_type::kHead) {
+    curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1L);
+  } else if (method == request_type::kPut) {
+    curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L);
   } else {
     // POST and custom request methods
-    curl_easy_setopt(curl_handle_, CURLOPT_POST, 1L);
-    curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS, nullptr);
-    if (!request_data_.empty()) {
-      curl_easy_setopt(curl_handle_,
-                       CURLOPT_READFUNCTION, &Transport::read_callback);
-      curl_easy_setopt(curl_handle_, CURLOPT_READDATA, this);
-    }
-    curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE_LARGE,
-                     curl_off_t(request_data_.size()));
-    if (method_ != request_type::kPost)
-      curl_easy_setopt(curl_handle_, CURLOPT_CUSTOMREQUEST, method_.c_str());
+    curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
+    curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, nullptr);
+    if (method != request_type::kPost)
+      curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, method.c_str());
   }
 
-  LOG(INFO) << "Request data (" << request_data_.size() << ")";
-  VLOG_IF(2, !request_data_.empty()) << "Raw request data: "
-      << std::string(reinterpret_cast<const char*>(request_data_.data()),
-                     request_data_.size());
-
-  // Setup HTTP response data.
-  if (method_ != request_type::kHead) {
-    curl_easy_setopt(curl_handle_,
-                     CURLOPT_WRITEFUNCTION, &Transport::write_callback);
-    curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this);
+  std::unique_ptr<http::Connection> connection(
+      new http::curl::Connection(curl_handle, method, transport));
+  CHECK(connection) << "Unable to create Connection object";
+  if (!connection->SendHeaders(headers)) {
+    connection.reset();
+    if (error_msg)
+      *error_msg = "Failed to send request headers";
   }
-
-  // HTTP request headers
-  auto headers = GetHeaders();
-  if (method_ != request_type::kGet && method_ != request_type::kHead) {
-    if (!content_type_.empty())
-      headers.emplace_back(request_header::kContentType, content_type_);
-  }
-
-  curl_slist* header_list = nullptr;
-  if (!headers.empty()) {
-    for (auto pair : headers) {
-      std::string header = string_utils::Join(": ", pair.first, pair.second);
-      VLOG(2) << "Request header: " << header;
-      header_list = curl_slist_append(header_list, header.c_str());
-    }
-    curl_easy_setopt(curl_handle_, CURLOPT_HTTPHEADER, header_list);
-  }
-
-  headers.clear();
-
-  // HTTP response headers
-  curl_easy_setopt(curl_handle_,
-                   CURLOPT_HEADERFUNCTION, &Transport::header_callback);
-  curl_easy_setopt(curl_handle_, CURLOPT_HEADERDATA, this);
-
-  CURLcode ret = curl_easy_perform(curl_handle_);
-  if (ret != CURLE_OK) {
-    error_ = curl_easy_strerror(ret);
-    stage_ = Stage::failed;
-    LOG(ERROR) << "CURL request failed: " << error_;
-  } else {
-    stage_ = Stage::response_received;
-  }
-  curl_slist_free_all(header_list);
-  if (stage_ == Stage::response_received) {
-    LOG(INFO) << "Response: " << GetResponseStatusCode() << " ("
-              << GetResponseStatusText() << ")";
-    VLOG(2) << "Response data (" << response_data_.size() << "): "
-        << std::string(reinterpret_cast<const char*>(response_data_.data()),
-                       response_data_.size());
-  }
-  return (ret == CURLE_OK);
-}
-
-int Transport::GetResponseStatusCode() const {
-  if (stage_ != Stage::response_received)
-    return 0;
-  long status_code = 0;
-  curl_easy_getinfo(curl_handle_, CURLINFO_RESPONSE_CODE, &status_code);
-  return status_code;
-}
-
-std::string Transport::GetResponseHeader(const char* headerName) const {
-  auto p = headers_.find(headerName);
-  return p != headers_.end() ? p->second : std::string();
-}
-
-const std::vector<unsigned char>& Transport::GetResponseData() const {
-  return response_data_;
-}
-
-void Transport::Close() {
-  if (curl_handle_) {
-    curl_easy_cleanup(curl_handle_);
-    curl_handle_ = nullptr;
-  }
-  stage_ = Stage::closed;
-}
-
-size_t Transport::write_callback(char* ptr, size_t size,
-                                 size_t num, void* data) {
-  Transport* me = reinterpret_cast<Transport*>(data);
-  size_t data_len = size * num;
-  me->response_data_.insert(me->response_data_.end(), ptr, ptr + data_len);
-  return data_len;
-}
-
-size_t Transport::read_callback(char* ptr, size_t size,
-                                size_t num, void* data) {
-  Transport* me = reinterpret_cast<Transport*>(data);
-  size_t data_len = size * num;
-
-  if (me->request_data_ptr_ >= me->request_data_.size())
-    return 0;
-
-  if (me->request_data_ptr_ + data_len > me->request_data_.size())
-    data_len = me->request_data_.size() - me->request_data_ptr_;
-
-  memcpy(ptr, me->request_data_.data() + me->request_data_ptr_, data_len);
-  me->request_data_ptr_ += data_len;
-
-  return data_len;
-}
-
-size_t Transport::header_callback(char* ptr, size_t size,
-                                  size_t num, void* data) {
-  Transport* me = reinterpret_cast<Transport*>(data);
-  size_t hdr_len = size * num;
-  std::string header(ptr, int(hdr_len));
-  // Remove newlines at the end of header line.
-  while (!header.empty() && (header.back() == '\r' || header.back() == '\n')) {
-    header.pop_back();
-  }
-
-  VLOG(2) << "Response header: " << header;
-
-  if (!me->status_text_set_) {
-    // First header - response code as "HTTP/1.1 200 OK".
-    // Need to extract the OK part
-    size_t pos = header.find(' ');
-    if(pos != std::string::npos)
-      pos = header.find(' ', pos + 1);
-    if (pos != std::string::npos)
-      me->status_text_ = header.substr(pos + 1);
-    me->status_text_set_ = true;
-  } else {
-    auto pair = string_utils::SplitAtFirst(header, ':');
-    if (!pair.second.empty())
-      me->headers_.insert(pair);
-  }
-  return hdr_len;
+  return connection;
 }
diff --git a/buffet/http_transport_curl.h b/buffet/http_transport_curl.h
index 9778b94..f63f815 100644
--- a/buffet/http_transport_curl.h
+++ b/buffet/http_transport_curl.h
@@ -5,155 +5,34 @@
 #ifndef BUFFET_HTTP_TRANSPORT_CURL_H_
 #define BUFFET_HTTP_TRANSPORT_CURL_H_
 
-#include <map>
-#include <curl/curl.h>
-
-#include "buffet/http_request.h"
+#include "buffet/http_transport.h"
 
 namespace chromeos {
 namespace http {
 namespace curl {
 
 ///////////////////////////////////////////////////////////////////////////////
-// A particular implementation of TransportInterface that uses libcurl for
-// HTTP communications. This class (as TransportInterface interface)
+// An implementation of http::Transport that uses libcurl for
+// HTTP communications. This class (as http::Transport base)
 // is used by http::Request and http::Response classes to provide HTTP
 // functionality to the clients.
+// See http_transport.h for more details.
 ///////////////////////////////////////////////////////////////////////////////
-class Transport : public TransportInterface {
+class Transport : public http::Transport {
  public:
-  // Standard constructor. |url| is the full request URL with protocol
-  // schema, host address, resource path as well as optional query parameters
-  // and/or user name/password. |method| is one of HTTP request verbs such as
-  // "GET", "POST", etc. If nullptr is specified, "GET" is assumed.
-  Transport(const std::string& url, const char* method);
-  ~Transport();
+  Transport();
+  virtual ~Transport();
 
-  // Returns the current request/response stage.
-  virtual Stage GetStage() const override { return stage_; }
-
-  // Implementation of Request::AddRange.
-  virtual void AddRange(int64_t bytes) override;
-  virtual void AddRange(uint64_t from_byte, uint64_t to_byte) override;
-
-  // Implementation of Request::SetAccept/Request::GetAccept.
-  virtual void SetAccept(const char* acceptMimeTypes) override {
-    accept_ = acceptMimeTypes;
-  }
-  virtual std::string GetAccept() const override;
-
-  // Implementation of Request::GetRequestURL.
-  virtual std::string GetRequestURL() const override { return request_url_; }
-
-  // Implementation of Request::SetContentType/Request::GetContentType.
-  virtual void SetContentType(const char* content_type) override {
-    content_type_ = content_type;
-  }
-  virtual std::string GetContentType() const override { return content_type_; }
-
-  // Implementation of Request::AddHeader.
-  virtual void AddHeader(const char* header, const char* value) override;
-
-  // Implementation of Request::RemoveHeader.
-  virtual void RemoveHeader(const char* header) override;
-
-  // Implementation of Request::AddRequestBody.
-  virtual bool AddRequestBody(const void* data, size_t size) override;
-
-  // Implementation of Request::SetMethod/Request::GetMethod.
-  virtual void SetMethod(const char* method) override { method_ = method; }
-  virtual std::string GetMethod() const override { return method_; }
-
-  // Implementation of Request::SetReferer/Request::GetReferer.
-  virtual void SetReferer(const char* referer) override { referer_ = referer; }
-  virtual std::string GetReferer() const override { return referer_; }
-
-  // Implementation of Request::SetUserAgent/Request::GetUserAgent.
-  virtual void SetUserAgent(const char* user_agent) override {
-    user_agent_ = user_agent;
-  }
-  virtual std::string GetUserAgent() const override { return user_agent_; }
-
-  // Sends the HTTP request to the server. Used by Request::GetResponse().
-  virtual bool Perform() override;
-
-  // Implementation of Response::GetStatusCode.
-  virtual int GetResponseStatusCode() const override;
-
-  // Implementation of Response::GetStatusText.
-  virtual std::string GetResponseStatusText() const override {
-    return status_text_;
-  }
-
-  // Implementation of Response::GetHeader.
-  virtual std::string GetResponseHeader(const char* header_name) const override;
-
-  // Implementation of Response::GetData.
-  virtual const std::vector<unsigned char>& GetResponseData() const override;
-
-  // Implementation of Response::GetErrorMessage.
-  virtual std::string GetErrorMessage() const override { return error_; }
-
-  // Closes the connection and frees up internal data
-  virtual void Close() override;
+  virtual std::unique_ptr<http::Connection> CreateConnection(
+      std::shared_ptr<http::Transport> transport,
+      const std::string& url,
+      const std::string& method,
+      const HeaderList& headers,
+      const std::string& user_agent,
+      const std::string& referer,
+      std::string* error_msg) override;
 
  private:
-   HeaderList GetHeaders() const;
-
-  // Write data callback. Used by CURL when receiving response data.
-  static size_t write_callback(char* ptr, size_t size, size_t num, void* data);
-  // Read data callback. Used by CURL when sending request body data.
-  static size_t read_callback(char* ptr, size_t size, size_t num, void* data);
-  // Write header data callback. Used by CURL when receiving response headers.
-  static size_t header_callback(char* ptr, size_t size, size_t num, void* data);
-
-  // Full request URL, such as "http://www.host.com/path/to/object"
-  std::string request_url_;
-  // HTTP request verb, such as "GET", "POST", "PUT", ...
-  std::string method_;
-
-  // Referrer URL, if any. Sent to the server via "Referer: " header.
-  std::string referer_;
-  // User agent string, if any. Sent to the server via "User-Agent: " header.
-  std::string user_agent_;
-  // Content type of the request body data.
-  // Sent to the server via "Content-Type: " header.
-  std::string content_type_;
-  // List of acceptable response data types.
-  // Sent to the server via "Accept: " header.
-  std::string accept_ = "*/*";
-
-  // List of optional request headers provided by the caller.
-  // After request has been sent, contains the received response headers.
-  std::map<std::string, std::string> headers_;
-  // List of optional data ranges to request partial content from the server.
-  // Sent to thr server as "Range: " header.
-  std::vector<std::pair<uint64_t, uint64_t>> ranges_;
-  // Binary data for request body.
-  std::vector<unsigned char> request_data_;
-  // Read pointer for request data. Used when streaming data to the server.
-  size_t request_data_ptr_ = 0;
-
-  // Received response data.
-  std::vector<unsigned char> response_data_;
-
-  // Current progress stage.
-  Stage stage_ = Stage::failed;
-  // CURL error message in case request fails completely.
-  std::string error_;
-  // Reponse status text, such as "OK" for 200, or "Forbidden" for 403
-  std::string status_text_;
-  // Flag used when parsing response headers to separate the response status
-  // from the rest of response headers.
-  bool status_text_set_ = false;
-
-  // range_value_omitted is used in |ranges_| list to indicate omitted value.
-  // E.g. range (10,range_value_omitted) represents bytes from 10 to the end
-  // of the data stream.
-  static const uint64_t range_value_omitted = (uint64_t)-1;
-
-  CURL* curl_handle_ = nullptr;
-
   DISALLOW_COPY_AND_ASSIGN(Transport);
 };
 
diff --git a/buffet/http_utils.cc b/buffet/http_utils.cc
index 8210221..2cd8b80 100644
--- a/buffet/http_utils.cc
+++ b/buffet/http_utils.cc
@@ -17,30 +17,35 @@
 namespace http {
 
 std::unique_ptr<Response> Get(const std::string& url,
-                              const HeaderList& headers) {
-  return SendRequest(request_type::kGet, url, nullptr, 0, nullptr, headers);
+                              const HeaderList& headers,
+                              std::shared_ptr<Transport> transport) {
+  return SendRequest(request_type::kGet, url, nullptr, 0, nullptr,
+                     headers, transport);
 }
 
 std::string GetAsString(const std::string& url,
-                        const HeaderList& headers) {
-  auto resp = Get(url, headers);
+                        const HeaderList& headers,
+                        std::shared_ptr<Transport> transport) {
+  auto resp = Get(url, headers, transport);
   return resp ? resp->GetDataAsString() : std::string();
 }
 
-std::unique_ptr<Response> Head(const std::string& url) {
-  Request request(url, request_type::kHead);
+std::unique_ptr<Response> Head(const std::string& url,
+                               std::shared_ptr<Transport> transport) {
+  Request request(url, request_type::kHead, transport);
   return request.GetResponse();
 }
 
 std::unique_ptr<Response> PostText(const std::string& url,
                                    const char* data,
                                    const char* mime_type,
-                                   const HeaderList& headers) {
+                                   const HeaderList& headers,
+                                   std::shared_ptr<Transport> transport) {
   if (mime_type == nullptr) {
     mime_type = chromeos::mime::application::kWwwFormUrlEncoded;
   }
 
-  return PostBinary(url, data, strlen(data), mime_type, headers);
+  return PostBinary(url, data, strlen(data), mime_type, headers, transport);
 }
 
 std::unique_ptr<Response> SendRequest(const char * method,
@@ -48,8 +53,9 @@
                                       const void* data,
                                       size_t data_size,
                                       const char* mime_type,
-                                      const HeaderList& headers) {
-  Request request(url, method);
+                                      const HeaderList& headers,
+                                      std::shared_ptr<Transport> transport) {
+  Request request(url, method, transport);
   request.AddHeaders(headers);
   if (data_size > 0) {
     if (mime_type == nullptr) {
@@ -63,38 +69,43 @@
 
 std::unique_ptr<Response> PostBinary(const std::string & url, const void* data,
                                      size_t data_size, const char* mime_type,
-                                     const HeaderList& headers) {
+                                     const HeaderList& headers,
+                                     std::shared_ptr<Transport> transport) {
   return SendRequest(request_type::kPost, url,
-                     data, data_size, mime_type, headers);
+                     data, data_size, mime_type, headers, transport);
 }
 
 std::unique_ptr<Response> PostFormData(const std::string& url,
                                        const FormFieldList& data,
-                                       const HeaderList& headers) {
+                                       const HeaderList& headers,
+                                       std::shared_ptr<Transport> transport) {
   std::string encoded_data = chromeos::data_encoding::WebParamsEncode(data);
   return PostBinary(url, encoded_data.c_str(), encoded_data.size(),
-                    chromeos::mime::application::kWwwFormUrlEncoded, headers);
+                    chromeos::mime::application::kWwwFormUrlEncoded,
+                    headers, transport);
 }
 
 
 std::unique_ptr<Response> PostJson(const std::string& url,
                                    const base::Value* json,
-                                   const HeaderList& headers) {
+                                   const HeaderList& headers,
+                                   std::shared_ptr<Transport> transport) {
   std::string data;
   if (json)
     base::JSONWriter::Write(json, &data);
   return PostBinary(url, data.c_str(), data.size(),
-                    mime::application::kJson, headers);
+                    mime::application::kJson, headers, transport);
 }
 
 std::unique_ptr<Response> PatchJson(const std::string& url,
                                     const base::Value* json,
-                                    const HeaderList& headers) {
+                                    const HeaderList& headers,
+                                    std::shared_ptr<Transport> transport) {
   std::string data;
   if (json)
     base::JSONWriter::Write(json, &data);
   return SendRequest(request_type::kPatch, url, data.c_str(), data.size(),
-                     mime::application::kJson, headers);
+                     mime::application::kJson, headers, transport);
 }
 
 std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
diff --git a/buffet/http_utils.h b/buffet/http_utils.h
index 2979ed3..c10805a 100644
--- a/buffet/http_utils.h
+++ b/buffet/http_utils.h
@@ -29,32 +29,35 @@
 // returned data and additional information (such as returned HTTP headers)
 // can be obtained from the returned Response object.
 // If data MIME type is not specified, "application/octet-stream" is assumed.
-std::unique_ptr<Response> SendRequest(const char* method,
-                                      const std::string& url,
-                                      const void* data,
-                                      size_t data_size,
-                                      const char* mime_type,
-                                      const HeaderList& headers);
+std::unique_ptr<Response> SendRequest(
+    const char* method, const std::string& url,
+    const void* data, size_t data_size, const char* mime_type,
+    const HeaderList& headers, std::shared_ptr<Transport> transport);
 
 // Performs a simple GET request and returns the data as a string.
-std::string GetAsString(const std::string& url, const HeaderList& headers);
-inline std::string GetAsString(const std::string& url) {
-  return GetAsString(url, HeaderList());
+std::string GetAsString(const std::string& url, const HeaderList& headers,
+                        std::shared_ptr<Transport> transport);
+inline std::string GetAsString(const std::string& url,
+                               std::shared_ptr<Transport> transport) {
+  return GetAsString(url, HeaderList(), transport);
 }
 
 // Performs a GET request. Success status, returned data and additional
 // information (such as returned HTTP headers) can be obtained from
 // the returned Response object.
 std::unique_ptr<Response> Get(const std::string& url,
-                              const HeaderList& headers);
-inline std::unique_ptr<Response> Get(const std::string& url) {
-  return Get(url, HeaderList());
+                              const HeaderList& headers,
+                              std::shared_ptr<Transport> transport);
+inline std::unique_ptr<Response> Get(
+    const std::string& url, std::shared_ptr<Transport> transport) {
+  return Get(url, HeaderList(), transport);
 }
 
 // Performs a HEAD request. Success status and additional
 // information (such as returned HTTP headers) can be obtained from
 // the returned Response object.
-std::unique_ptr<Response> Head(const std::string& url);
+std::unique_ptr<Response> Head(const std::string& url,
+                               std::shared_ptr<Transport> transport);
 
 // Performs a POST request with binary data. Success status, returned data
 // and additional information (such as returned HTTP headers) can be obtained
@@ -64,19 +67,19 @@
                                      const void* data,
                                      size_t data_size,
                                      const char* mime_type,
-                                     const HeaderList& headers);
+                                     const HeaderList& headers,
+                                     std::shared_ptr<Transport> transport);
 
-inline std::unique_ptr<Response> PostBinary(const std::string& url,
-                                            const void* data,
-                                            size_t data_size,
-                                            const char* mime_type) {
-  return PostBinary(url, data, data_size, mime_type, HeaderList());
+inline std::unique_ptr<Response> PostBinary(
+    const std::string& url, const void* data, size_t data_size,
+    const char* mime_type, std::shared_ptr<Transport> transport) {
+  return PostBinary(url, data, data_size, mime_type, HeaderList(), transport);
 }
 
-inline std::unique_ptr<Response> PostBinary(const std::string& url,
-                                            const void* data,
-                                            size_t data_size) {
-  return PostBinary(url, data, data_size, nullptr);
+inline std::unique_ptr<Response> PostBinary(
+    const std::string& url, const void* data, size_t data_size,
+    std::shared_ptr<Transport> transport) {
+  return PostBinary(url, data, data_size, nullptr, transport);
 }
 
 // Performs a POST request with text data. Success status, returned data
@@ -87,30 +90,33 @@
 std::unique_ptr<Response> PostText(const std::string& url,
                                    const char* data,
                                    const char* mime_type,
-                                   const HeaderList& headers);
+                                   const HeaderList& headers,
+                                   std::shared_ptr<Transport> transport);
 
-inline std::unique_ptr<Response> PostText(const std::string& url,
-                                          const char* data,
-                                          const char* mime_type) {
-  return PostText(url, data, mime_type, HeaderList());
+inline std::unique_ptr<Response> PostText(
+    const std::string& url, const char* data, const char* mime_type,
+    std::shared_ptr<Transport> transport) {
+  return PostText(url, data, mime_type, HeaderList(), transport);
 }
 
-inline std::unique_ptr<Response> PostText(const std::string& url,
-                                          const char* data) {
-  return PostText(url, data, nullptr);
+inline std::unique_ptr<Response> PostText(
+    const std::string& url, const char* data,
+    std::shared_ptr<Transport> transport) {
+  return PostText(url, data, nullptr, transport);
 }
 
 // Performs a POST request with form data. Success status, returned data
 // and additional information (such as returned HTTP headers) can be obtained
 // from the returned Response object. The form data is a list of key/value
 // pairs. The data is posed as "application/x-www-form-urlencoded".
-std::unique_ptr<Response> PostFormData(const std::string& url,
-                                       const FormFieldList& data,
-                                       const HeaderList& headers);
+std::unique_ptr<Response> PostFormData(
+    const std::string& url, const FormFieldList& data,
+    const HeaderList& headers, std::shared_ptr<Transport> transport);
 
-inline std::unique_ptr<Response> PostFormData(const std::string& url,
-                                              const FormFieldList& data) {
-  return PostFormData(url, data, HeaderList());
+inline std::unique_ptr<Response> PostFormData(
+    const std::string& url, const FormFieldList& data,
+    std::shared_ptr<Transport> transport) {
+  return PostFormData(url, data, HeaderList(), transport);
 }
 
 // Performs a POST request with JSON data. Success status, returned data
@@ -119,11 +125,13 @@
 // use ParseJsonResponse() method on the returned Response object.
 std::unique_ptr<Response> PostJson(const std::string& url,
                                    const base::Value* json,
-                                   const HeaderList& headers);
+                                   const HeaderList& headers,
+                                   std::shared_ptr<Transport> transport);
 
-inline std::unique_ptr<Response> PostJson(const std::string& url,
-                                          const base::Value* json) {
-  return PostJson(url, json, HeaderList());
+inline std::unique_ptr<Response> PostJson(
+    const std::string& url, const base::Value* json,
+    std::shared_ptr<Transport> transport) {
+  return PostJson(url, json, HeaderList(), transport);
 }
 
 // Performs a PATCH request with JSON data. Success status, returned data
@@ -132,11 +140,13 @@
 // use ParseJsonResponse() method on the returned Response object.
 std::unique_ptr<Response> PatchJson(const std::string& url,
                                     const base::Value* json,
-                                    const HeaderList& headers);
+                                    const HeaderList& headers,
+                                    std::shared_ptr<Transport> transport);
 
-inline std::unique_ptr<Response> PatchJson(const std::string& url,
-                                           const base::Value* json) {
-  return PatchJson(url, json, HeaderList());
+inline std::unique_ptr<Response> PatchJson(
+    const std::string& url, const base::Value* json,
+    std::shared_ptr<Transport> transport) {
+  return PatchJson(url, json, HeaderList(), transport);
 }
 
 // Given an http::Response object, parse the body data into Json object.
diff --git a/buffet/http_utils_unittest.cc b/buffet/http_utils_unittest.cc
new file mode 100644
index 0000000..ab137a7
--- /dev/null
+++ b/buffet/http_utils_unittest.cc
@@ -0,0 +1,13 @@
+// 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 "buffet/http_utils.h"
+
+#include <gtest/gtest.h>
+
+using namespace chromeos::http;
+
+TEST(HttpUtils, SendRequest) {
+  // TODO(avakulenko)
+}
diff --git a/buffet/transport_interface.h b/buffet/transport_interface.h
deleted file mode 100644
index e833239..0000000
--- a/buffet/transport_interface.h
+++ /dev/null
@@ -1,81 +0,0 @@
-// 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.
-
-#ifndef BUFFET_TRANSPORT_INTERFACE_H_
-#define BUFFET_TRANSPORT_INTERFACE_H_
-
-#include <vector>
-#include <string>
-#include <base/basictypes.h>
-
-namespace chromeos {
-namespace http {
-
-typedef std::vector<std::pair<std::string, std::string>> HeaderList;
-
-///////////////////////////////////////////////////////////////////////////////
-// TransportInterface is an interface to abstract specific implementation
-// of HTTP communication. This interface (and its underlying implementation)
-// is used by http::Request and http::Response classes to provide HTTP
-// functionality to the clients. This interface should be of no interest
-// to the clients unless they want to implement/use their own network library.
-///////////////////////////////////////////////////////////////////////////////
-class TransportInterface {
- public:
-  enum class Stage {
-    initialized,
-    response_received,
-    failed,
-    closed
-  };
-
-  virtual ~TransportInterface() {}
-
-  virtual Stage GetStage() const = 0;
-
-  virtual void AddRange(int64_t bytes) = 0;
-  virtual void AddRange(uint64_t from_byte, uint64_t to_byte) = 0;
-
-  virtual void SetAccept(const char* accept_mime_types) = 0;
-  virtual std::string GetAccept() const = 0;
-
-  virtual std::string GetRequestURL() const = 0;
-
-  virtual void SetContentType(const char* content_type) = 0;
-  virtual std::string GetContentType() const = 0;
-
-  virtual void AddHeader(const char* header, const char* value) = 0;
-  virtual void RemoveHeader(const char* header) = 0;
-
-  virtual bool AddRequestBody(const void* data, size_t size) = 0;
-
-  virtual void SetMethod(const char* method) = 0;
-  virtual std::string GetMethod() const = 0;
-
-  virtual void SetReferer(const char* referer) = 0;
-  virtual std::string GetReferer() const = 0;
-
-  virtual void SetUserAgent(const char* user_agent) = 0;
-  virtual std::string GetUserAgent() const = 0;
-
-  virtual bool Perform() = 0;
-
-  virtual int GetResponseStatusCode() const = 0;
-  virtual std::string GetResponseStatusText() const = 0;
-
-  virtual std::string GetResponseHeader(const char* header_name) const = 0;
-  virtual const std::vector<unsigned char>& GetResponseData() const = 0;
-  virtual std::string GetErrorMessage() const = 0;
-
-  virtual void Close() = 0;
-
- protected:
-  TransportInterface() {}
-  DISALLOW_COPY_AND_ASSIGN(TransportInterface);
-};
-
-} // namespace http
-} // namespace chromeos
-
-#endif // BUFFET_TRANSPORT_INTERFACE_H_