Update engine properly sets Content-Type to text/xml.

Since the payload sent to Omaha is XML encoded, update engine should
properly set the HTTP Content-Type header to text/xml, instead of using
the libcurl default (application/x-www-form-urlencoded).

BUG=chromium-os:7613
TEST=Tested against devserver, ensuring that Content-Type is set
correctly

Change-Id: I9766e8dd67ffd387634a0ab4ef83c2990b16b537
Reviewed-on: https://gerrit.chromium.org/gerrit/16051
Reviewed-by: Andrew de los Reyes <adlr@chromium.org>
Tested-by: Gilad Arnold <garnold@chromium.org>
Commit-Ready: Gilad Arnold <garnold@chromium.org>
diff --git a/http_common.cc b/http_common.cc
index 647faba..bf4e69d 100644
--- a/http_common.cc
+++ b/http_common.cc
@@ -51,3 +51,21 @@
 HttpResponseCode StringToHttpResponseCode(const char *s) {
   return static_cast<HttpResponseCode>(strtoul(s, NULL, 10));
 }
+
+
+const char *GetHttpContentTypeString(HttpContentType type) {
+  static const struct {
+    HttpContentType type;
+    const char* str;
+  } http_content_type_table[] = {
+    { kHttpContentTypeTextXml, "text/xml" },
+  };
+
+  bool is_found = false;
+  size_t i;
+  for (i = 0; i < ARRAYSIZE_UNSAFE(http_content_type_table); i++)
+    if ((is_found = (http_content_type_table[i].type == type)))
+      break;
+
+  return (is_found ? http_content_type_table[i].str : NULL);
+}
diff --git a/http_common.h b/http_common.h
index 807ee7d..b2ffbc7 100644
--- a/http_common.h
+++ b/http_common.h
@@ -45,4 +45,14 @@
 // Converts a string beginning with an HTTP error code into numerical value.
 HttpResponseCode StringToHttpResponseCode(const char *s);
 
+
+// Enumeration type for HTTP Content-Type.
+enum HttpContentType {
+  kHttpContentTypeUnspecified = 0,
+  kHttpContentTypeTextXml,
+};
+
+// Returns a standard HTTP Content-Type string.
+const char *GetHttpContentTypeString(HttpContentType type);
+
 #endif  // CHROMEOS_PLATFORM_UPDATE_ENGINE_HTTP_COMMON_H__
diff --git a/http_fetcher.cc b/http_fetcher.cc
index 9e1e0ac..b59f988 100644
--- a/http_fetcher.cc
+++ b/http_fetcher.cc
@@ -17,11 +17,17 @@
   }
 }
 
-void HttpFetcher::SetPostData(const void* data, size_t size) {
+void HttpFetcher::SetPostData(const void* data, size_t size,
+                              HttpContentType type) {
   post_data_set_ = true;
   post_data_.clear();
   const char *char_data = reinterpret_cast<const char*>(data);
   post_data_.insert(post_data_.end(), char_data, char_data + size);
+  post_content_type_ = type;
+}
+
+void HttpFetcher::SetPostData(const void* data, size_t size) {
+  SetPostData(data, size, kHttpContentTypeUnspecified);
 }
 
 // Proxy methods to set the proxies, then to pop them off.
diff --git a/http_fetcher.h b/http_fetcher.h
index 46f6903..2ca6fe2 100644
--- a/http_fetcher.h
+++ b/http_fetcher.h
@@ -14,6 +14,7 @@
 #include <glib.h>
 #include <google/protobuf/stubs/common.h>
 
+#include "http_common.h"
 #include "update_engine/proxy_resolver.h"
 
 // This class is a simple wrapper around an HTTP library (libcurl). We can
@@ -47,7 +48,11 @@
   int http_response_code() const { return http_response_code_; }
 
   // Optional: Post data to the server. The HttpFetcher should make a copy
-  // of this data and upload it via HTTP POST during the transfer.
+  // of this data and upload it via HTTP POST during the transfer. The type of
+  // the data is necessary for properly setting the Content-Type HTTP header.
+  void SetPostData(const void* data, size_t size, HttpContentType type);
+
+  // Same without a specified Content-Type.
   void SetPostData(const void* data, size_t size);
 
   // Proxy methods to set the proxies, then to pop them off.
@@ -111,6 +116,7 @@
   // POST data for the transfer, and whether or not it was ever set
   bool post_data_set_;
   std::vector<char> post_data_;
+  HttpContentType post_content_type_;
 
   // The server's HTTP response code from the last transfer. This
   // field should be set to 0 when a new transfer is initiated, and
diff --git a/libcurl_http_fetcher.cc b/libcurl_http_fetcher.cc
index 7af9364..b9da3a7 100644
--- a/libcurl_http_fetcher.cc
+++ b/libcurl_http_fetcher.cc
@@ -91,6 +91,22 @@
     CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE,
                               post_data_.size()),
              CURLE_OK);
+
+    // Set the Content-Type HTTP header, if one was specifically set.
+    CHECK(!curl_http_headers_);
+    if (post_content_type_ != kHttpContentTypeUnspecified) {
+      const string content_type_attr =
+        base::StringPrintf("Content-Type: %s",
+                           GetHttpContentTypeString(post_content_type_));
+      curl_http_headers_ = curl_slist_append(NULL, content_type_attr.c_str());
+      CHECK(curl_http_headers_);
+      CHECK_EQ(
+          curl_easy_setopt(curl_handle_, CURLOPT_HTTPHEADER,
+                           curl_http_headers_),
+          CURLE_OK);
+    } else {
+      LOG(WARNING) << "no content type set, using libcurl default";
+    }
   }
 
   if (bytes_downloaded_ > 0 || download_length_) {
@@ -480,6 +496,10 @@
     io_channels_[t].clear();
   }
 
+  if (curl_http_headers_) {
+    curl_slist_free_all(curl_http_headers_);
+    curl_http_headers_ = NULL;
+  }
   if (curl_handle_) {
     if (curl_multi_handle_) {
       CHECK_EQ(curl_multi_remove_handle(curl_multi_handle_, curl_handle_),
diff --git a/libcurl_http_fetcher.h b/libcurl_http_fetcher.h
index e87c678..4fc65b2 100644
--- a/libcurl_http_fetcher.h
+++ b/libcurl_http_fetcher.h
@@ -29,6 +29,7 @@
       : HttpFetcher(proxy_resolver),
         curl_multi_handle_(NULL),
         curl_handle_(NULL),
+        curl_http_headers_(NULL),
         timeout_source_(NULL),
         transfer_in_progress_(false),
         transfer_size_(0),
@@ -191,6 +192,7 @@
   // Handles for the libcurl library
   CURLM *curl_multi_handle_;
   CURL *curl_handle_;
+  struct curl_slist *curl_http_headers_;
 
   // Lists of all read(0)/write(1) file descriptors that we're waiting on from
   // the glib main loop. libcurl may open/close descriptors and switch their
diff --git a/omaha_request_action.cc b/omaha_request_action.cc
index 6311332..bbcc00b 100644
--- a/omaha_request_action.cc
+++ b/omaha_request_action.cc
@@ -233,7 +233,8 @@
                                     ping_active_days_,
                                     ping_roll_call_days_,
                                     prefs_));
-  http_fetcher_->SetPostData(request_post.data(), request_post.size());
+  http_fetcher_->SetPostData(request_post.data(), request_post.size(),
+                             kHttpContentTypeTextXml);
   LOG(INFO) << "Posting an Omaha request to " << params_.update_url;
   LOG(INFO) << "Request: " << request_post;
   http_fetcher_->BeginTransfer(params_.update_url);