Update to newer network service implementation and mojoms from monet

This updates to using the network service implementation and mojoms from
https://github.com/domokit/monet/commit/050294719f6890ceea4b27540d66295fe6e710a3

R=blundell@chromium.org, qsr@chromium.org, viettrungluu@chromium.org

Review URL: https://codereview.chromium.org/1157783002
diff --git a/PRESUBMIT.py b/PRESUBMIT.py
index 10fbc60..7b3b26c 100644
--- a/PRESUBMIT.py
+++ b/PRESUBMIT.py
@@ -640,6 +640,7 @@
                 input_api.DEFAULT_BLACK_LIST +
                 (r"^base/logging\.h$",
                  r"^base/logging\.cc$",
+                 r"^examples/wget/wget\.cc$",
                  r"^shell/application_manager/network_fetcher\.cc$",
                  r"^shell/tracer\.cc$",
                  r"^sandbox/linux/.*",
diff --git a/examples/pdf_viewer/BUILD.gn b/examples/pdf_viewer/BUILD.gn
index a3a4ff6..d21cc1d 100644
--- a/examples/pdf_viewer/BUILD.gn
+++ b/examples/pdf_viewer/BUILD.gn
@@ -11,6 +11,7 @@
 
   deps = [
     "//examples/bitmap_uploader",
+    "//mojo/common",
     "//mojo/application",
     "//mojo/application:content_handler",
     "//mojo/public/cpp/bindings",
diff --git a/examples/pdf_viewer/pdf_viewer.cc b/examples/pdf_viewer/pdf_viewer.cc
index 0e6a72c..21e38d7 100644
--- a/examples/pdf_viewer/pdf_viewer.cc
+++ b/examples/pdf_viewer/pdf_viewer.cc
@@ -2,10 +2,12 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "base/strings/string_tokenizer.h"
+#include <string>
+
 #include "examples/bitmap_uploader/bitmap_uploader.h"
 #include "mojo/application/application_runner_chromium.h"
 #include "mojo/application/content_handler_factory.h"
+#include "mojo/common/data_pipe_utils.h"
 #include "mojo/public/c/system/main.h"
 #include "mojo/public/cpp/application/application_connection.h"
 #include "mojo/public/cpp/application/application_delegate.h"
@@ -160,46 +162,16 @@
   }
 
   void FetchPDF(URLResponsePtr response) {
-    int content_length = GetContentLength(response->headers);
-    scoped_ptr<unsigned char[]> data;
-    data_.reset(new unsigned char[content_length]);
-    unsigned char* buf = data_.get();
-    uint32_t bytes_remaining = content_length;
-    uint32_t num_bytes = bytes_remaining;
-    while (bytes_remaining > 0) {
-      MojoResult result = ReadDataRaw(response->body.get(), buf, &num_bytes,
-                                      MOJO_READ_DATA_FLAG_NONE);
-      if (result == MOJO_RESULT_SHOULD_WAIT) {
-        Wait(response->body.get(), MOJO_HANDLE_SIGNAL_READABLE,
-             MOJO_DEADLINE_INDEFINITE, nullptr);
-      } else if (result == MOJO_RESULT_OK) {
-        buf += num_bytes;
-        num_bytes = bytes_remaining -= num_bytes;
-      } else {
-        break;
-      }
-    }
-
-    doc_ = FPDF_LoadMemDocument(data_.get(), content_length, NULL);
+    data_.clear();
+    mojo::common::BlockingCopyToString(response->body.Pass(), &data_);
+    if (data_.length() >= static_cast<size_t>(std::numeric_limits<int>::max()))
+      return;
+    doc_ = FPDF_LoadMemDocument(data_.data(), static_cast<int>(data_.length()),
+                                nullptr);
     page_count_ = FPDF_GetPageCount(doc_);
   }
 
-  int GetContentLength(const Array<String>& headers) {
-    for (size_t i = 0; i < headers.size(); ++i) {
-      base::StringTokenizer t(headers[i], ": ;=");
-      while (t.GetNext()) {
-        if (!t.token_is_delim() && t.token() == "Content-Length") {
-          while (t.GetNext()) {
-            if (!t.token_is_delim())
-              return atoi(t.token().c_str());
-          }
-        }
-      }
-    }
-    return 0;
-  }
-
-  scoped_ptr<unsigned char[]> data_;
+  std::string data_;
   int current_page_;
   int page_count_;
   FPDF_DOCUMENT doc_;
diff --git a/examples/png_viewer/BUILD.gn b/examples/png_viewer/BUILD.gn
index 364150a..2204976 100644
--- a/examples/png_viewer/BUILD.gn
+++ b/examples/png_viewer/BUILD.gn
@@ -11,6 +11,7 @@
 
   deps = [
     "//examples/bitmap_uploader",
+    "//mojo/common",
     "//mojo/application",
     "//mojo/application:content_handler",
     "//mojo/public/cpp/bindings",
diff --git a/examples/png_viewer/png_viewer.cc b/examples/png_viewer/png_viewer.cc
index d99dfc3..8cd42f1 100644
--- a/examples/png_viewer/png_viewer.cc
+++ b/examples/png_viewer/png_viewer.cc
@@ -3,14 +3,15 @@
 // found in the LICENSE file.
 
 #include <algorithm>
+#include <string>
 
 #include "base/macros.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/message_loop/message_loop.h"
-#include "base/strings/string_tokenizer.h"
 #include "examples/bitmap_uploader/bitmap_uploader.h"
 #include "mojo/application/application_runner_chromium.h"
 #include "mojo/application/content_handler_factory.h"
+#include "mojo/common/data_pipe_utils.h"
 #include "mojo/public/c/system/main.h"
 #include "mojo/public/cpp/application/application_connection.h"
 #include "mojo/public/cpp/application/application_delegate.h"
@@ -144,46 +145,14 @@
   }
 
   void DecodePNG(URLResponsePtr response) {
-    int content_length = GetContentLength(response->headers);
-    scoped_ptr<unsigned char[]> data(new unsigned char[content_length]);
-    unsigned char* buf = data.get();
-    uint32_t bytes_remaining = content_length;
-    uint32_t num_bytes = bytes_remaining;
-    while (bytes_remaining > 0) {
-      MojoResult result = ReadDataRaw(response->body.get(), buf, &num_bytes,
-                                      MOJO_READ_DATA_FLAG_NONE);
-      if (result == MOJO_RESULT_SHOULD_WAIT) {
-        Wait(response->body.get(), MOJO_HANDLE_SIGNAL_READABLE,
-             MOJO_DEADLINE_INDEFINITE, nullptr);
-      } else if (result == MOJO_RESULT_OK) {
-        buf += num_bytes;
-        num_bytes = bytes_remaining -= num_bytes;
-      } else {
-        break;
-      }
-    }
-
+    std::string data;
+    mojo::common::BlockingCopyToString(response->body.Pass(), &data);
     bitmap_.reset(new std::vector<unsigned char>);
-    gfx::PNGCodec::Decode(static_cast<const unsigned char*>(data.get()),
-                          content_length, gfx::PNGCodec::FORMAT_BGRA,
+    gfx::PNGCodec::Decode(reinterpret_cast<const unsigned char*>(data.data()),
+                          data.length(), gfx::PNGCodec::FORMAT_BGRA,
                           bitmap_.get(), &width_, &height_);
   }
 
-  int GetContentLength(const Array<String>& headers) {
-    for (size_t i = 0; i < headers.size(); ++i) {
-      base::StringTokenizer t(headers[i], ": ;=");
-      while (t.GetNext()) {
-        if (!t.token_is_delim() && t.token() == "Content-Length") {
-          while (t.GetNext()) {
-            if (!t.token_is_delim())
-              return atoi(t.token().c_str());
-          }
-        }
-      }
-    }
-    return 0;
-  }
-
   int width_;
   int height_;
   scoped_ptr<std::vector<unsigned char>> bitmap_;
diff --git a/examples/wget/wget.cc b/examples/wget/wget.cc
index 0815522..23be460 100644
--- a/examples/wget/wget.cc
+++ b/examples/wget/wget.cc
@@ -35,7 +35,9 @@
     printf("  %s\n", response->status_line.get().c_str());
     if (response->headers) {
       for (size_t i = 0; i < response->headers.size(); ++i)
-        printf("  %s\n", response->headers[i].get().c_str());
+        printf("  %s=%s\n",
+               response->headers[i]->name.To<std::string>().c_str(),
+               response->headers[i]->value.To<std::string>().c_str());
     }
   }
 
diff --git a/mojo/public/tools/NETWORK_SERVICE_VERSION b/mojo/public/tools/NETWORK_SERVICE_VERSION
index f66a51a..79a193f 100644
--- a/mojo/public/tools/NETWORK_SERVICE_VERSION
+++ b/mojo/public/tools/NETWORK_SERVICE_VERSION
@@ -1 +1 @@
-custom_build_base_0f9ad3a20275b77d228f67c2edd89dbea0e3e9ea_issue_1113493008_patchset_20001
\ No newline at end of file
+050294719f6890ceea4b27540d66295fe6e710a3
\ No newline at end of file
diff --git a/mojo/services/network/public/interfaces/BUILD.gn b/mojo/services/network/public/interfaces/BUILD.gn
index f20b731..eda4dd7 100644
--- a/mojo/services/network/public/interfaces/BUILD.gn
+++ b/mojo/services/network/public/interfaces/BUILD.gn
@@ -8,6 +8,9 @@
 mojom("interfaces") {
   sources = [
     "cookie_store.mojom",
+    "http_connection.mojom",
+    "http_message.mojom",
+    "http_server.mojom",
     "net_address.mojom",
     "network_error.mojom",
     "network_service.mojom",
diff --git a/mojo/services/network/public/interfaces/http_connection.mojom b/mojo/services/network/public/interfaces/http_connection.mojom
new file mode 100644
index 0000000..2cb79bd
--- /dev/null
+++ b/mojo/services/network/public/interfaces/http_connection.mojom
@@ -0,0 +1,42 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo;
+
+import "network/public/interfaces/http_message.mojom";
+import "network/public/interfaces/network_error.mojom";
+import "network/public/interfaces/web_socket.mojom";
+
+interface HttpConnection {
+  // Sets the OS send buffer size (in bytes) for the underlying socket.
+  SetSendBufferSize(uint32 size) => (NetworkError result);
+
+  // Sets the OS receive buffer size (in bytes) for the underlying socket.
+  SetReceiveBufferSize(uint32 size) => (NetworkError result);
+};
+
+interface HttpConnectionDelegate {
+  // Called when an HTTP request is received.
+  OnReceivedRequest(HttpRequest request) => (HttpResponse response);
+
+  // Called when an WebSocket request is received.
+  //
+  // If the delegate decides to accept the request, it should respond with
+  // non-null arguments in the callback. |send_stream| is a data pipe which
+  // should remain open for the lifetime of the WebSocket. Data to send over the
+  // WebSocket should be written to the producer end of the |send_stream|.
+  // |web_socket| will be already connected. There is no need to call Connect()
+  // on it. But |client| will still receive a DidConnect() notification.
+  //
+  // NOTE: WebSocket server support is not fully implemented. For now the
+  // following are not supported:
+  // - negotiating subprotocol or extension;
+  // - fragmented or non-text messages;
+  // - failure or close notification;
+  // - flow control.
+  OnReceivedWebSocketRequest(HttpRequest request)
+      => (WebSocket&? web_socket,
+          handle<data_pipe_consumer>? send_stream,
+          WebSocketClient? client);
+};
diff --git a/mojo/services/network/public/interfaces/http_message.mojom b/mojo/services/network/public/interfaces/http_message.mojom
new file mode 100644
index 0000000..b89ba84
--- /dev/null
+++ b/mojo/services/network/public/interfaces/http_message.mojom
@@ -0,0 +1,23 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo;
+
+struct HttpHeader {
+  string name;
+  string value;
+};
+
+struct HttpRequest {
+  string method = "GET";
+  string url;
+  array<HttpHeader>? headers;
+  handle<data_pipe_consumer>? body;
+};
+
+struct HttpResponse {
+  uint32 status_code = 200;
+  array<HttpHeader>? headers;
+  handle<data_pipe_consumer>? body;
+};
diff --git a/mojo/services/network/public/interfaces/http_server.mojom b/mojo/services/network/public/interfaces/http_server.mojom
new file mode 100644
index 0000000..dde9c45
--- /dev/null
+++ b/mojo/services/network/public/interfaces/http_server.mojom
@@ -0,0 +1,12 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+module mojo;
+
+import "network/public/interfaces/http_connection.mojom";
+
+interface HttpServerDelegate {
+  // Called when a connection is established.
+  OnConnected(HttpConnection connection, HttpConnectionDelegate& delegate);
+};
diff --git a/mojo/services/network/public/interfaces/network_service.mojom b/mojo/services/network/public/interfaces/network_service.mojom
index 23bdebc..fc0e6b3 100644
--- a/mojo/services/network/public/interfaces/network_service.mojom
+++ b/mojo/services/network/public/interfaces/network_service.mojom
@@ -5,6 +5,7 @@
 module mojo;
 
 import "network/public/interfaces/cookie_store.mojom";
+import "network/public/interfaces/http_server.mojom";
 import "network/public/interfaces/net_address.mojom";
 import "network/public/interfaces/network_error.mojom";
 import "network/public/interfaces/tcp_bound_socket.mojom";
@@ -58,4 +59,17 @@
           NetAddress? local_address);
 
   CreateUDPSocket(UDPSocket& socket);
+
+  // Starts an HTTP server running on the given local address. The delegate will
+  // be notified with incoming connections.
+  //
+  // The local address can specify 0 for the port to specify that the OS should
+  // pick an available port for the given address, or it can pass 0 for the
+  // address and port for the OS to pick both the local address and port. In
+  // all success cases, the resulting local address will be passed to the
+  // callback as bound_to.
+  CreateHttpServer(NetAddress local_address,
+                   HttpServerDelegate delegate)
+      => (NetworkError result,
+          NetAddress? bound_to);
 };
diff --git a/mojo/services/network/public/interfaces/url_loader.mojom b/mojo/services/network/public/interfaces/url_loader.mojom
index 77db3ad..d0a7164 100644
--- a/mojo/services/network/public/interfaces/url_loader.mojom
+++ b/mojo/services/network/public/interfaces/url_loader.mojom
@@ -4,6 +4,7 @@
 
 module mojo;
 
+import "network/public/interfaces/http_message.mojom";
 import "network/public/interfaces/network_error.mojom";
 
 struct URLRequest {
@@ -14,7 +15,7 @@
   string method = "GET";
 
   // Additional HTTP request headers.
-  array<string>? headers;
+  array<HttpHeader>? headers;
 
   // The payload for the request body, represented as a concatenation of data
   // streams. For HTTP requests, the method must be set to "POST" or "PUT".
@@ -34,9 +35,6 @@
   // servers to also not satisfy the request from their cache.  This has the
   // effect of forcing a full end-to-end fetch.
   bool bypass_cache = false;
-
-  // The referrer request header.
-  string? referrer;
 };
 
 struct URLResponse {
@@ -57,7 +55,7 @@
   string? status_line;
 
   // The HTTP response headers.
-  array<string>? headers;
+  array<HttpHeader>? headers;
 
   // The MIME type of the response body.
   string? mime_type;
diff --git a/services/authenticating_url_loader/authenticating_url_loader_impl.cc b/services/authenticating_url_loader/authenticating_url_loader_impl.cc
index df1919a..c1fa21b 100644
--- a/services/authenticating_url_loader/authenticating_url_loader_impl.cc
+++ b/services/authenticating_url_loader/authenticating_url_loader_impl.cc
@@ -46,8 +46,10 @@
   headers_ = request->headers.Clone();
   pending_request_callback_ = callback;
   if (cached_tokens_->find(url_.GetOrigin()) != cached_tokens_->end()) {
-    request->headers.push_back("Authorization: Bearer " +
-                               (*cached_tokens_)[url_.GetOrigin()]);
+    auto auth_header = HttpHeader::New();
+    auth_header->name = "Authorization";
+    auth_header->value = "Bearer " + (*cached_tokens_)[url_.GetOrigin()];
+    request->headers.push_back(auth_header.Pass());
   }
   StartNetworkRequest(request.Pass());
 }
@@ -180,10 +182,13 @@
   DCHECK(token);
   (*cached_tokens_)[url_.GetOrigin()] = token;
   token_ = token;
-  mojo::Array<mojo::String> headers(0);
+  auto auth_header = HttpHeader::New();
+  auth_header->name = "Authorization";
+  auth_header->value = "Bearer " + token.get();
+  Array<HttpHeaderPtr> headers;
   if (headers_)
     headers = headers_.Clone();
-  headers.push_back("Authorization: Bearer " + token.get());
+  headers.push_back(auth_header.Pass());
 
   URLRequestPtr request(mojo::URLRequest::New());
   request->url = url_.spec();
diff --git a/services/authenticating_url_loader/authenticating_url_loader_impl.h b/services/authenticating_url_loader/authenticating_url_loader_impl.h
index f2ee787..a80df2c 100644
--- a/services/authenticating_url_loader/authenticating_url_loader_impl.h
+++ b/services/authenticating_url_loader/authenticating_url_loader_impl.h
@@ -66,7 +66,7 @@
   GURL url_;
   bool auto_follow_redirects_;
   bool bypass_cache_;
-  Array<String> headers_;
+  Array<HttpHeaderPtr> headers_;
   String username_;
   String token_;
   Callback<void(URLResponsePtr)> pending_request_callback_;
diff --git a/services/python/mojo_url_redirector/mojo_url_redirector_apptest.cc b/services/python/mojo_url_redirector/mojo_url_redirector_apptest.cc
index f7ae0bf..be6b58f 100644
--- a/services/python/mojo_url_redirector/mojo_url_redirector_apptest.cc
+++ b/services/python/mojo_url_redirector/mojo_url_redirector_apptest.cc
@@ -49,12 +49,11 @@
 
   // Check that the response contains a header redirecting to the expected
   // location.
-  std::string expected_redirect_header = "location: " + expected_redirect;
   bool found_redirect_header = false;
   EXPECT_FALSE(response->headers.is_null());
   for (size_t i = 0; i < response->headers.size(); i++) {
-    mojo::String header = response->headers[i];
-    if (header == expected_redirect_header) {
+    const auto& header = response->headers[i];
+    if (header->name == "location" && header->value == expected_redirect) {
       found_redirect_header = true;
       break;
     }
diff --git a/services/url_response_disk_cache/url_response_disk_cache_apptest.cc b/services/url_response_disk_cache/url_response_disk_cache_apptest.cc
index c2788c2..61bf5a2 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_apptest.cc
+++ b/services/url_response_disk_cache/url_response_disk_cache_apptest.cc
@@ -42,13 +42,19 @@
       std::string(reinterpret_cast<char*>(&path.front()), path.size()));
 }
 
+HttpHeaderPtr RandomEtagHeader() {
+  auto etag_header = HttpHeader::New();
+  etag_header->name = "ETag";
+  etag_header->value = base::StringPrintf("%f", base::RandDouble());
+  return etag_header;
+}
+
 }  // namespace
 
 TEST_F(URLResponseDiskCacheAppTest, GetFile) {
   URLResponsePtr url_response = mojo::URLResponse::New();
   url_response->url = "http://www.example.com/1";
-  url_response->headers = Array<String>(1);
-  url_response->headers[0] = base::StringPrintf("ETag: %f", base::RandDouble());
+  url_response->headers.push_back(RandomEtagHeader());
   DataPipe pipe;
   std::string content = base::RandBytesAsString(32);
   uint32_t num_bytes = content.size();
@@ -77,8 +83,7 @@
 TEST_F(URLResponseDiskCacheAppTest, GetExtractedContent) {
   URLResponsePtr url_response = mojo::URLResponse::New();
   url_response->url = "http://www.example.com/2";
-  url_response->headers = Array<String>(1);
-  url_response->headers[0] = base::StringPrintf("ETag: %f", base::RandDouble());
+  url_response->headers.push_back(RandomEtagHeader());
   DataPipe pipe;
   std::string content = base::RandBytesAsString(32);
   uint32_t num_bytes = kTestData.size;
@@ -111,9 +116,13 @@
 TEST_F(URLResponseDiskCacheAppTest, CacheTest) {
   URLResponsePtr url_response = mojo::URLResponse::New();
   url_response->url = "http://www.example.com/3";
-  url_response->headers = Array<String>(1);
-  std::string etag = base::StringPrintf("ETag: %f", base::RandDouble());
-  url_response->headers[0] = etag;
+  std::string etag_value = base::StringPrintf("%f", base::RandDouble());
+  {
+    auto etag_header = HttpHeader::New();
+    etag_header->name = "ETag";
+    etag_header->value = etag_value;
+    url_response->headers.push_back(etag_header.Pass());
+  }
   DataPipe pipe1;
   std::string content = base::RandBytesAsString(32);
   uint32_t num_bytes = content.size();
@@ -147,8 +156,12 @@
   // different content. The cached value should be returned.
   url_response = mojo::URLResponse::New();
   url_response->url = "http://www.example.com/3";
-  url_response->headers = Array<String>(1);
-  url_response->headers[0] = etag;
+  {
+    auto etag_header = HttpHeader::New();
+    etag_header->name = "ETag";
+    etag_header->value = etag_value;
+    url_response->headers.push_back(etag_header.Pass());
+  }
   DataPipe pipe2;
   std::string new_content = base::RandBytesAsString(32);
   num_bytes = new_content.size();
@@ -179,8 +192,7 @@
   // that the new content is returned, and the cached files is deleted.
   url_response = mojo::URLResponse::New();
   url_response->url = "http://www.example.com/3";
-  url_response->headers = Array<String>(1);
-  url_response->headers[0] = base::StringPrintf("ETag: %f", base::RandDouble());
+  url_response->headers.push_back(RandomEtagHeader());
   DataPipe pipe3;
   new_content = base::RandBytesAsString(32);
   num_bytes = new_content.size();
diff --git a/services/url_response_disk_cache/url_response_disk_cache_entry.mojom b/services/url_response_disk_cache/url_response_disk_cache_entry.mojom
index 379dce0..2d41947 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_entry.mojom
+++ b/services/url_response_disk_cache/url_response_disk_cache_entry.mojom
@@ -4,10 +4,15 @@
 
 module mojo;
 
+struct CacheHeaders {
+  string name;
+  string value;
+};
+
 // One entry in the service cache.
 struct CacheEntry {
   uint32 version = 0;
   string url;
   string content_path;
-  array<string>? headers;
+  array<CacheHeaders>? headers;
 };
diff --git a/services/url_response_disk_cache/url_response_disk_cache_impl.cc b/services/url_response_disk_cache/url_response_disk_cache_impl.cc
index f510ed8..52161ab 100644
--- a/services/url_response_disk_cache/url_response_disk_cache_impl.cc
+++ b/services/url_response_disk_cache/url_response_disk_cache_impl.cc
@@ -26,7 +26,7 @@
 
 // The current version of the cache. This should only be incremented. When this
 // is incremented, all current cache entries will be invalidated.
-const uint32_t kCurrentVersion = 1;
+const uint32_t kCurrentVersion = 2;
 
 const char kEtagHeader[] = "etag";
 
@@ -140,29 +140,14 @@
 
 // Returns the list of values for the given |header_name| in the given list of
 // headers.
+template <typename HeaderType>
 std::vector<std::string> GetHeaderValues(const std::string& header_name,
-                                         const Array<String>& headers) {
+                                         const Array<HeaderType>& headers) {
   std::vector<std::string> result;
-  for (std::string header : headers.storage()) {
-    if (StartsWithASCII(header, header_name, false)) {
-      auto begin = header.begin();
-      auto end = header.end();
-      begin += header_name.size();
-      // Extract the content of the header by finding the remaining string after
-      // the ':' and stripping all spaces.
-      while (begin < end && *begin != ':')
-        begin++;
-      if (begin < end) {
-        begin++;
-        while (begin < end && *begin == ' ')
-          begin++;
-        while (end > begin && *(end - 1) == ' ')
-          end--;
-        if (begin < end) {
-          result.push_back(std::string(begin, end));
-        }
-      }
-    }
+  for (size_t i = 0u; i < headers.size(); ++i) {
+    std::string name = headers[i]->name;
+    if (LowerCaseEqualsASCII(name, header_name.c_str()))
+      result.push_back(headers[i]->value);
   }
   return result;
 }
@@ -301,7 +286,12 @@
   entry->version = kCurrentVersion;
   entry->url = response->url;
   entry->content_path = content.value();
-  entry->headers = response->headers.Pass();
+  for (size_t i = 0u; i < response->headers.size(); ++i) {
+    auto cache_header = CacheHeaders::New();
+    cache_header->name = response->headers[i]->name;
+    cache_header->value = response->headers[i]->value;
+    entry->headers.push_back(cache_header.Pass());
+  }
   // Asynchronously copy the response body to the cached file. The entry is send
   // to the callback so that it is saved on disk only if the copy of the body
   // succeded.
diff --git a/shell/application_manager/local_fetcher.cc b/shell/application_manager/local_fetcher.cc
index 58d2a2d..a975b0f 100644
--- a/shell/application_manager/local_fetcher.cc
+++ b/shell/application_manager/local_fetcher.cc
@@ -60,12 +60,17 @@
   response->body = data_pipe.consumer_handle.Pass();
   base::stat_wrapper_t stat_result;
   if (stat64(path_.value().c_str(), &stat_result) == 0) {
-    response->headers = mojo::Array<mojo::String>(2);
-    response->headers[0] =
-        base::StringPrintf("Content-Length: %" PRId64, stat_result.st_size);
-    response->headers[1] = base::StringPrintf(
-        "ETag: \"%" PRId64 "-%" PRId64 "-%" PRId64 "\"", stat_result.st_dev,
+    auto content_length_header = mojo::HttpHeader::New();
+    content_length_header->name = "Content-Length";
+    content_length_header->value =
+        base::StringPrintf("%" PRId64, stat_result.st_size);
+    response->headers.push_back(content_length_header.Pass());
+    auto etag_header = mojo::HttpHeader::New();
+    etag_header->name = "ETag";
+    etag_header->value = base::StringPrintf(
+        "\"%" PRId64 "-%" PRId64 "-%" PRId64 "\"", stat_result.st_dev,
         stat_result.st_ino, static_cast<uint64_t>(stat_result.st_mtime));
+    response->headers.push_back(etag_header.Pass());
   }
   mojo::common::CopyFromFile(path_, data_pipe.producer_handle.Pass(), skip,
                              task_runner, base::Bind(&IgnoreResult));
diff --git a/shell/application_manager/network_fetcher.cc b/shell/application_manager/network_fetcher.cc
index cf726c6..acfce9e 100644
--- a/shell/application_manager/network_fetcher.cc
+++ b/shell/application_manager/network_fetcher.cc
@@ -224,8 +224,11 @@
   request->url = mojo::String::From(url);
   request->auto_follow_redirects = false;
   request->bypass_cache = disable_cache_;
-  mojo::Array<mojo::String> headers(1);
-  headers[0] = base::StringPrintf("X-Architecture: %s", kArchitecture);
+  auto header = mojo::HttpHeader::New();
+  header->name = "X-Architecture";
+  header->value = kArchitecture;
+  mojo::Array<mojo::HttpHeaderPtr> headers;
+  headers.push_back(header.Pass());
   request->headers = headers.Pass();
 
   url_loader_factory->CreateAuthenticatingURLLoader(GetProxy(&url_loader_));
diff --git a/sky/services/oknet/src/org/domokit/oknet/NetworkServiceImpl.java b/sky/services/oknet/src/org/domokit/oknet/NetworkServiceImpl.java
index f8b5b73..8919c55 100644
--- a/sky/services/oknet/src/org/domokit/oknet/NetworkServiceImpl.java
+++ b/sky/services/oknet/src/org/domokit/oknet/NetworkServiceImpl.java
@@ -16,6 +16,7 @@
 import org.chromium.mojo.system.MessagePipeHandle;
 import org.chromium.mojo.system.MojoException;
 import org.chromium.mojom.mojo.CookieStore;
+import org.chromium.mojom.mojo.HttpServerDelegate;
 import org.chromium.mojom.mojo.NetAddress;
 import org.chromium.mojom.mojo.NetworkService;
 import org.chromium.mojom.mojo.TcpBoundSocket;
@@ -42,8 +43,9 @@
         assert core != null;
         mCore = core;
 
-        if (sThreadPool == null)
+        if (sThreadPool == null) {
             sThreadPool = Executors.newCachedThreadPool();
+        }
 
         if (sClient == null) {
             sClient = new OkHttpClient();
@@ -102,4 +104,10 @@
     public void createUdpSocket(InterfaceRequest<UdpSocket> socket) {
         socket.close();
     }
+
+    @Override
+    public void createHttpServer(NetAddress localAddress, HttpServerDelegate delegate,
+            CreateHttpServerResponse callback) {
+        delegate.close();
+    }
 }
diff --git a/sky/services/oknet/src/org/domokit/oknet/UrlLoaderImpl.java b/sky/services/oknet/src/org/domokit/oknet/UrlLoaderImpl.java
index 48d7915..058023d 100644
--- a/sky/services/oknet/src/org/domokit/oknet/UrlLoaderImpl.java
+++ b/sky/services/oknet/src/org/domokit/oknet/UrlLoaderImpl.java
@@ -21,6 +21,7 @@
 import org.chromium.mojo.system.MojoException;
 import org.chromium.mojo.system.MojoResult;
 import org.chromium.mojo.system.Pair;
+import org.chromium.mojom.mojo.HttpHeader;
 import org.chromium.mojom.mojo.NetworkError;
 import org.chromium.mojom.mojo.UrlLoader;
 import org.chromium.mojom.mojo.UrlLoaderStatus;
@@ -120,11 +121,8 @@
                 new Request.Builder().url(request.url).method(request.method, null);
 
         if (request.headers != null) {
-            for (String header : request.headers) {
-                String[] parts = header.split(":");
-                String name = parts[0].trim();
-                String value = parts.length > 1 ? parts[1].trim() : "";
-                builder.addHeader(name, value);
+            for (HttpHeader header : request.headers) {
+                builder.addHeader(header.name, header.value);
             }
         }
 
@@ -160,11 +158,12 @@
                 }
 
                 Headers headers = response.headers();
-                urlResponse.headers = new String[headers.size()];
+                urlResponse.headers = new HttpHeader[headers.size()];
                 for (int i = 0; i < headers.size(); ++i) {
-                    String name = headers.name(i);
-                    String value = headers.value(i);
-                    urlResponse.headers[i] = name + ": " + value;
+                    HttpHeader header = new HttpHeader();
+                    header.name = headers.name(i);
+                    header.value = headers.value(i);
+                    urlResponse.headers[i] = header;
                 }
 
                 ResponseBody body = response.body();
@@ -172,8 +171,9 @@
                 if (mediaType != null) {
                     urlResponse.mimeType = mediaType.type() + "/" + mediaType.subtype();
                     Charset charset = mediaType.charset();
-                    if (charset != null)
+                    if (charset != null) {
                         urlResponse.charset = charset.displayName();
+                    }
                 }
 
                 Pair<DataPipe.ProducerHandle, DataPipe.ConsumerHandle> handles =
diff --git a/sky/services/platform/url_request_types.cc b/sky/services/platform/url_request_types.cc
index 18e4a96..367577b 100644
--- a/sky/services/platform/url_request_types.cc
+++ b/sky/services/platform/url_request_types.cc
@@ -31,21 +31,27 @@
     if (LowerCaseEqualsASCII(name_latin1, "accept"))
       has_accept_header_ = true;
 
-    buffer_.push_back(name_latin1 + ": " + value_latin1);
+    auto header = HttpHeader::New();
+    header->name = name_latin1;
+    header->value = value_latin1;
+    buffer_.push_back(header.Pass());
   }
 
-  Array<String> GetBuffer() {
+  Array<HttpHeaderPtr> GetBuffer() {
     // In some cases, WebKit doesn't add an Accept header, but not having the
     // header confuses some web servers.  See bug 808613.
     if (!has_accept_header_) {
-      buffer_.push_back("Accept: */*");
+      auto accept_header = HttpHeader::New();
+      accept_header->name = "Accept";
+      accept_header->value = "*/*";
+      buffer_.push_back(accept_header.Pass());
       has_accept_header_ = true;
     }
     return buffer_.Pass();
   }
 
  private:
-  Array<String> buffer_;
+  Array<HttpHeaderPtr> buffer_;
   bool has_accept_header_;
 };
 
diff --git a/sky/services/platform/weburlloader_impl.cc b/sky/services/platform/weburlloader_impl.cc
index 596bc59..16f39d6 100644
--- a/sky/services/platform/weburlloader_impl.cc
+++ b/sky/services/platform/weburlloader_impl.cc
@@ -49,19 +49,9 @@
   result.setLoadTiming(timing);
 
   for (size_t i = 0; i < url_response->headers.size(); ++i) {
-    const std::string& header_line = url_response->headers[i];
-    size_t first_colon = header_line.find(":");
-
-    if (first_colon == std::string::npos || first_colon == 0)
-      continue;
-
-    std::string value;
-    TrimWhitespaceASCII(header_line.substr(first_colon + 1),
-                        base::TRIM_LEADING,
-                        &value);
-    result.setHTTPHeaderField(
-        blink::WebString::fromUTF8(header_line.substr(0, first_colon)),
-        blink::WebString::fromUTF8(value));
+    const auto& header = url_response->headers[i];
+    result.setHTTPHeaderField(blink::WebString::fromUTF8(header->name),
+                              blink::WebString::fromUTF8(header->value));
   }
 
   return result;