Buffet utilities updated for GCD device registration

Updated some utility functions to provid the functionality needed
for implementing GCD device registration services.

DataEncoding - typedefed key-value pairs as WebParamList to simplify
the usage of WebParamsEncode/WebParamsDecode functions

http::Request - Added a helper function to add a list of HTTP headers
to the request at once.

http::curl::Transport - added a lot of debug logging information to
help debugging HTTP communications between Buffet and external
servers. Also fixed sending data with custom request such as PATCH.
Finally, response headers used to include trailing new line chars.

http::TransportInterface - provided a typedef for the list of
HTTP request headers

http_utils - Switched from using char const* to std::string const&
in URLs for ease of use.
Added generic SendRequest().
Added the ability to add custom request headers when using PostXXX()
Added helper for sending PATCH request with JSON objects.
Changed ParseJsonResponse() to expect JSON object (dictionary)
instead of generic value as the server response. This simplifies the
common usage. Also enabled this function to parse error responses
as well.

BUG=chromium:363348
TEST=unit tests passed.

Change-Id: Ieb407731d6664feb0370bbaeeda16df8f6b7c5d1
Reviewed-on: https://chromium-review.googlesource.com/194856
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/data_encoding.cc b/buffet/data_encoding.cc
index 0e040ab..1c0013a 100644
--- a/buffet/data_encoding.cc
+++ b/buffet/data_encoding.cc
@@ -72,9 +72,8 @@
   return result;
 }
 
-std::string WebParamsEncode(
-    std::vector<std::pair<std::string, std::string>> const& params,
-    bool encodeSpaceAsPlus) {
+std::string WebParamsEncode(WebParamList const& params,
+                            bool encodeSpaceAsPlus) {
   std::vector<std::string> pairs;
   pairs.reserve(params.size());
   for (auto const& p : params) {
@@ -86,9 +85,8 @@
   return string_utils::Join('&', pairs);
 }
 
-std::vector<std::pair<std::string, std::string>> WebParamsDecode(
-    std::string const& data) {
-  std::vector<std::pair<std::string, std::string>> result;
+WebParamList WebParamsDecode(std::string const& data) {
+  WebParamList result;
   std::vector<std::string> params = string_utils::Split(data, '&');
   for (auto p : params) {
     auto pair = string_utils::SplitAtFirst(p, '=');
diff --git a/buffet/data_encoding.h b/buffet/data_encoding.h
index 6c46e45..7a4eda0 100644
--- a/buffet/data_encoding.h
+++ b/buffet/data_encoding.h
@@ -11,6 +11,8 @@
 namespace chromeos {
 namespace data_encoding {
 
+typedef std::vector<std::pair<std::string, std::string>> WebParamList;
+
 // Encode/escape string to be used in the query portion of a URL.
 // If |encodeSpaceAsPlus| is set to true, spaces are encoded as '+' instead
 // of "%20"
@@ -26,20 +28,16 @@
 
 // Converts a list of key-value pairs into a string compatible with
 // 'application/x-www-form-urlencoded' content encoding.
-std::string WebParamsEncode(
-    std::vector<std::pair<std::string, std::string>> const& params,
-    bool encodeSpaceAsPlus);
+std::string WebParamsEncode(WebParamList const& params, bool encodeSpaceAsPlus);
 
-inline std::string WebParamsEncode(
-    std::vector<std::pair<std::string, std::string>> const& params) {
+inline std::string WebParamsEncode(WebParamList const& params) {
   return WebParamsEncode(params, true);
 }
 
 // Parses a string of '&'-delimited key-value pairs (separated by '=') and
 // encoded in a way compatible with 'application/x-www-form-urlencoded'
 // content encoding.
-std::vector<std::pair<std::string, std::string>> WebParamsDecode(
-    std::string const& data);
+WebParamList WebParamsDecode(std::string const& data);
 
 } // namespace data_encoding
 } // namespace chromeos
diff --git a/buffet/http_request.cc b/buffet/http_request.cc
index 5d82473..3e9ec71 100644
--- a/buffet/http_request.cc
+++ b/buffet/http_request.cc
@@ -160,6 +160,11 @@
     transport_->AddHeader(header, value);
 }
 
+void Request::AddHeaders(HeaderList const& headers) {
+  for (auto&& pair : headers)
+    AddHeader(pair.first.c_str(), pair.second.c_str());
+}
+
 bool Request::AddRequestBody(void const* data, size_t size) {
   return transport_ && transport_->AddRequestBody(data, size);
 }
diff --git a/buffet/http_request.h b/buffet/http_request.h
index acdab89..fcdc9bd 100644
--- a/buffet/http_request.h
+++ b/buffet/http_request.h
@@ -227,6 +227,7 @@
 
   // Adds additional HTTP request header
   void AddHeader(char const* header, char const* value);
+  void AddHeaders(HeaderList const& headers);
 
   // Removes HTTP request header
   void RemoveHeader(char const* header);
diff --git a/buffet/http_transport_curl.cc b/buffet/http_transport_curl.cc
index 47090e9..6cb9c16 100644
--- a/buffet/http_transport_curl.cc
+++ b/buffet/http_transport_curl.cc
@@ -16,6 +16,42 @@
 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(std::string const& url, char const* method) :
     request_url_(url),
     method_(method ? method : request_type::kGet) {
@@ -42,8 +78,8 @@
   return accept_;
 }
 
-std::vector<std::pair<std::string, std::string>> Transport::GetHeaders() const {
-  auto headers = MapToVector(headers_);
+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());
@@ -104,8 +140,14 @@
     return false;
   }
 
+  LOG(INFO) << "Sending a " << method_ << " request to " << request_url_;
   curl_easy_setopt(curl_handle_, CURLOPT_URL, request_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());
@@ -121,7 +163,15 @@
     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::kPost) {
+  } 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);
+  } 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()) {
@@ -131,22 +181,15 @@
     }
     curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE_LARGE,
                      curl_off_t(request_data_.size()));
-  } 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);
-  } else {
-    curl_easy_setopt(curl_handle_, CURLOPT_CUSTOMREQUEST, method_.c_str());
-    if (!request_data_.empty()) {
-      curl_easy_setopt(curl_handle_,
-                       CURLOPT_READFUNCTION, &Transport::read_callback);
-      curl_easy_setopt(curl_handle_, CURLOPT_READDATA, this);
-    }
+    if (method_ != request_type::kPost)
+      curl_easy_setopt(curl_handle_, CURLOPT_CUSTOMREQUEST, method_.c_str());
   }
 
+  VLOG_IF(2, !request_data_.empty()) << "Request data ("
+      << request_data_.size() << "): "
+      << std::string(reinterpret_cast<char const*>(request_data_.data()),
+      request_data_.size());
+
   // Setup HTTP response data.
   if (method_ != request_type::kHead) {
     curl_easy_setopt(curl_handle_,
@@ -165,6 +208,7 @@
   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);
@@ -186,6 +230,13 @@
     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<char const*>(response_data_.data()),
+                       response_data_.size());
+  }
   return (ret == CURLE_OK);
 }
 
@@ -202,6 +253,9 @@
   return p != headers_.end() ? p->second : std::string();
 }
 
+std::vector<unsigned char> const& Transport::GetResponseData() const {
+  return response_data_;
+}
 
 void Transport::Close() {
   if (curl_handle_) {
@@ -241,6 +295,13 @@
   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
diff --git a/buffet/http_transport_curl.h b/buffet/http_transport_curl.h
index ab2ea2b..5ef533e 100644
--- a/buffet/http_transport_curl.h
+++ b/buffet/http_transport_curl.h
@@ -89,9 +89,7 @@
   virtual std::string GetResponseHeader(char const* header_name) const override;
 
   // Implementation of Response::GetData.
-  virtual std::vector<unsigned char> const& GetResponseData() const override {
-    return response_data_;
-  }
+  virtual std::vector<unsigned char> const& GetResponseData() const override;
 
   // Implementation of Response::GetErrorMessage.
   virtual std::string GetErrorMessage() const override { return error_; }
@@ -100,7 +98,7 @@
   virtual void Close() override;
 
  private:
-  std::vector<std::pair<std::string, std::string>> GetHeaders() const;
+   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);
diff --git a/buffet/http_utils.cc b/buffet/http_utils.cc
index a921386..9db6b1c 100644
--- a/buffet/http_utils.cc
+++ b/buffet/http_utils.cc
@@ -10,78 +10,128 @@
 #include <base/json/json_reader.h>
 #include <base/json/json_writer.h>
 
-#include "mime_utils.h"
+#include "buffet/mime_utils.h"
+#include "buffet/data_encoding.h"
 
 namespace chromeos {
 namespace http {
 
-std::unique_ptr<Response> Get(char const* url) {
+std::unique_ptr<Response> Get(std::string const& url) {
   Request request(url);
   return request.GetResponse();
 }
 
-std::string GetAsString(char const* url) {
+std::string GetAsString(std::string const& url) {
   auto resp = Get(url);
   return resp ? resp->GetDataAsString() : std::string();
 }
 
-std::unique_ptr<Response> Head(char const* url) {
+std::unique_ptr<Response> Head(std::string const& url) {
   Request request(url, request_type::kHead);
   return request.GetResponse();
 }
 
-std::unique_ptr<Response> PostText(char const* url,
+std::unique_ptr<Response> PostText(std::string const& url,
                                    char const* data,
-                                   char const* mime_type) {
+                                   char const* mime_type,
+                                   HeaderList const& headers) {
   if (mime_type == nullptr) {
     mime_type = chromeos::mime::application::kWwwFormUrlEncoded;
   }
 
-  return PostBinary(url, data, strlen(data), mime_type);
+  return PostBinary(url, data, strlen(data), mime_type, headers);
 }
 
-std::unique_ptr<Response> PostBinary(char const* url, void const* data,
-                                     size_t data_size, char const* mime_type) {
-  if (mime_type == nullptr) {
-    mime_type = chromeos::mime::application::kOctet_stream;
+std::unique_ptr<Response> SendRequest(char const* method,
+                                      std::string const& url,
+                                      void const* data,
+                                      size_t data_size,
+                                      char const* mime_type,
+                                      HeaderList const& headers) {
+  Request request(url, method);
+  request.AddHeaders(headers);
+  if (data_size > 0) {
+    if (mime_type == nullptr) {
+      mime_type = chromeos::mime::application::kOctet_stream;
+    }
+    request.SetContentType(mime_type);
+    request.AddRequestBody(data, data_size);
   }
-
-  Request request(url, request_type::kPost);
-  request.SetContentType(mime_type);
-  request.AddRequestBody(data, data_size);
   return request.GetResponse();
 }
 
-std::unique_ptr<Response> PostJson(char const* url, base::Value const* json) {
-  std::string data;
-  base::JSONWriter::Write(json, &data);
-  return PostBinary(url, data.c_str(), data.size(), mime::application::kJson);
+std::unique_ptr<Response> PostBinary(std::string const& url, void const* data,
+                                     size_t data_size, char const* mime_type,
+                                     HeaderList const& headers) {
+  return SendRequest(request_type::kPost, url,
+                     data, data_size, mime_type, headers);
 }
 
-std::unique_ptr<base::Value> ParseJsonResponse(Response const* response,
-                                               std::string* error_message) {
-  std::unique_ptr<base::Value> value;
+std::unique_ptr<Response> PostFormData(std::string const& url,
+                                       FormFieldList const& data,
+                                       HeaderList const& headers) {
+  std::string encoded_data = chromeos::data_encoding::WebParamsEncode(data);
+  return PostBinary(url, encoded_data.c_str(), encoded_data.size(),
+                    chromeos::mime::application::kWwwFormUrlEncoded, headers);
+}
+
+
+std::unique_ptr<Response> PostJson(std::string const& url,
+                                   base::Value const* json,
+                                   HeaderList const& headers) {
+  std::string data;
+  if (json)
+    base::JSONWriter::Write(json, &data);
+  return PostBinary(url, data.c_str(), data.size(),
+                    mime::application::kJson, headers);
+}
+
+std::unique_ptr<Response> PatchJson(std::string const& url,
+                                    base::Value const* json,
+                                    HeaderList const& headers) {
+  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);
+}
+
+std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
+    Response const* response, int* status_code, std::string* error_message) {
+  std::unique_ptr<base::DictionaryValue> dict;
   if (response) {
-    if (response->IsSuccessful()) {
-      // Make sure we have a correct content type. Do not try to parse
-      // binary files, or HTML output. Limit to application/json and text/plain.
-      auto content_type = mime::RemoveParameters(response->GetContentType());
-      if (content_type == mime::application::kJson ||
-          content_type == mime::text::kPlain) {
-        std::string json = response->GetDataAsString();
-        value.reset(base::JSONReader::ReadAndReturnError(json,
-                                                         base::JSON_PARSE_RFC,
-                                                         nullptr,
-                                                         error_message));
+    if (status_code)
+      *status_code = response->GetStatusCode();
+
+    // Make sure we have a correct content type. Do not try to parse
+    // binary files, or HTML output. Limit to application/json and text/plain.
+    auto content_type = mime::RemoveParameters(response->GetContentType());
+    if (content_type == mime::application::kJson ||
+        content_type == mime::text::kPlain) {
+      std::string json = response->GetDataAsString();
+      auto value = base::JSONReader::ReadAndReturnError(json,
+                                                        base::JSON_PARSE_RFC,
+                                                        nullptr,
+                                                        error_message);
+      if (value) {
+        base::DictionaryValue* dict_value = nullptr;
+        if (value->GetAsDictionary(&dict_value)) {
+          dict.reset(dict_value);
+        } else {
+          delete value;
+          if (error_message) {
+            *error_message = "Reponse is not a valid JSON object";
+          }
+        }
       }
-      else if (error_message) {
-        *error_message = "Unexpected response content type: " + content_type;
-      }
+    }
+    else if (error_message) {
+      *error_message = "Unexpected response content type: " + content_type;
     }
   } else if (error_message) {
     *error_message = "NULL response.";
   }
-  return value;
+  return dict;
 }
 
 } // namespace http
diff --git a/buffet/http_utils.h b/buffet/http_utils.h
index 0fff847..724f118 100644
--- a/buffet/http_utils.h
+++ b/buffet/http_utils.h
@@ -7,11 +7,16 @@
 
 #include "buffet/http_request.h"
 
-namespace base { class Value; }
+namespace base {
+  class Value;
+  class DictionaryValue;
+}  // namespace base
 
 namespace chromeos {
 namespace http {
 
+typedef std::vector<std::pair<std::string, std::string>> FormFieldList;
+
 ////////////////////////////////////////////////////////////////////////////////
 // The following are simple utility helper functions for common HTTP operations
 // that use http::Request object behind the scenes and set it up accordingly.
@@ -20,29 +25,48 @@
 // directly.
 ////////////////////////////////////////////////////////////////////////////////
 
+// Performs a generic HTTP request with binary data. Success status,
+// 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(char const* method,
+                                      std::string const& url,
+                                      void const* data,
+                                      size_t data_size,
+                                      char const* mime_type,
+                                      HeaderList const& headers);
+
 // Performs a simple GET request and returns the data as a string.
-std::string GetAsString(char const* url);
+std::string GetAsString(std::string const& url);
 
 // 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(char const* url);
+std::unique_ptr<Response> Get(std::string const& url);
 
 // 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(char const* url);
+std::unique_ptr<Response> Head(std::string const& url);
 
 // Performs a POST request with binary data. Success status, 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> PostBinary(char const* url,
+std::unique_ptr<Response> PostBinary(std::string const& url,
                                      void const* data,
                                      size_t data_size,
-                                     char const* mime_type);
+                                     char const* mime_type,
+                                     HeaderList const& headers);
 
-inline std::unique_ptr<Response> PostBinary(char const* url,
+inline std::unique_ptr<Response> PostBinary(std::string const& url,
+                                            void const* data,
+                                            size_t data_size,
+                                            char const* mime_type) {
+  return PostBinary(url, data, data_size, mime_type, HeaderList());
+}
+
+inline std::unique_ptr<Response> PostBinary(std::string const& url,
                                             void const* data,
                                             size_t data_size) {
   return PostBinary(url, data, data_size, nullptr);
@@ -52,26 +76,67 @@
 // and additional information (such as returned HTTP headers) can be obtained
 // from the returned Response object.
 // If data MIME type is not specified, "application/x-www-form-urlencoded"
-// is assumed
-std::unique_ptr<Response> PostText(char const* url,
+// is assumed.
+std::unique_ptr<Response> PostText(std::string const& url,
                                    char const* data,
-                                   char const* mime_type);
+                                   char const* mime_type,
+                                   HeaderList const& headers);
 
-inline std::unique_ptr<Response> PostText(char const* url, char const* data) {
+inline std::unique_ptr<Response> PostText(std::string const& url,
+                                          char const* data,
+                                          char const* mime_type) {
+  return PostText(url, data, mime_type, HeaderList());
+}
+
+inline std::unique_ptr<Response> PostText(std::string const& url,
+                                          char const* data) {
   return PostText(url, data, nullptr);
 }
 
+// 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(std::string const& url,
+                                       FormFieldList const& data,
+                                       HeaderList const& headers);
+
+inline std::unique_ptr<Response> PostFormData(std::string const& url,
+                                              FormFieldList const& data) {
+  return PostFormData(url, data, HeaderList());
+}
+
 // Performs a POST request with JSON data. Success status, returned data
 // and additional information (such as returned HTTP headers) can be obtained
 // from the returned Response object. If a JSON response is expected,
 // use ParseJsonResponse() method on the returned Response object.
-std::unique_ptr<Response> PostJson(char const* url, base::Value const* json);
+std::unique_ptr<Response> PostJson(std::string const& url,
+                                   base::Value const* json,
+                                   HeaderList const& headers);
+
+inline std::unique_ptr<Response> PostJson(std::string const& url,
+                                          base::Value const* json) {
+  return PostJson(url, json, HeaderList());
+}
+
+// Performs a PATCH request with JSON data. Success status, returned data
+// and additional information (such as returned HTTP headers) can be obtained
+// from the returned Response object. If a JSON response is expected,
+// use ParseJsonResponse() method on the returned Response object.
+std::unique_ptr<Response> PatchJson(std::string const& url,
+                                    base::Value const* json,
+                                    HeaderList const& headers);
+
+inline std::unique_ptr<Response> PatchJson(std::string const& url,
+                                           base::Value const* json) {
+  return PatchJson(url, json, HeaderList());
+}
 
 // Given an http::Response object, parse the body data into Json object.
 // Returns null if failed. Optional |error_message| can be passed in to
 // get the extended error information as to why the parse failed.
-std::unique_ptr<base::Value> ParseJsonResponse(Response const* response,
-                                               std::string* error_message);
+std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
+    Response const* response, int* status_code, std::string* error_message);
 
 } // namespace http
 } // namespace chromeos
diff --git a/buffet/transport_interface.h b/buffet/transport_interface.h
index fa858c6..0f9363b 100644
--- a/buffet/transport_interface.h
+++ b/buffet/transport_interface.h
@@ -12,6 +12,8 @@
 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)