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/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