Merge remote-tracking branch 'foo/master'

Conflicts:
	LICENSE

Change-Id: I7069ba9c21bc6bc8fd11e1b84c6b1a82701ee407
diff --git a/buffet/HACKING b/buffet/HACKING
new file mode 100644
index 0000000..d657397
--- /dev/null
+++ b/buffet/HACKING
@@ -0,0 +1,16 @@
+Some common workflows for developing with buffet:
+
+# Tell portage that you'd like to make local changes to Buffet:
+cros_workon start --board=${BOARD} platform2
+
+# Edit files in platform2/buffet/
+vim ...
+
+# Compile and install those changes into the chroot:
+USE=buffet emerge-<board> platform2
+
+# Compile and run buffet unittests
+USE=buffet P2_TEST_FILTER="buffet::*" FEATURES=test emerge-<board> platform2
+
+# Deploy the most recently built version of buffet to a DUT:
+cros deploy --board=${BOARD} <remote host> platform2
diff --git a/buffet/README b/buffet/README
new file mode 100644
index 0000000..9dc1e87
--- /dev/null
+++ b/buffet/README
@@ -0,0 +1,6 @@
+// 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.
+
+This directory contains the a Brillo service for registering a device and
+sending/receiving remote commands.
diff --git a/buffet/buffet.conf b/buffet/buffet.conf
new file mode 100644
index 0000000..7c3ee8c
--- /dev/null
+++ b/buffet/buffet.conf
@@ -0,0 +1,12 @@
+# 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.
+
+description     "Brillo Buffet Service"
+author          "chromium-os-dev@chromium.org"
+
+start on starting system-services
+stop on stopping system-services
+respawn
+
+exec buffet
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
new file mode 100644
index 0000000..f44d8dc
--- /dev/null
+++ b/buffet/buffet.gyp
@@ -0,0 +1,83 @@
+{
+  'variables': {
+    'libbase_ver': 242728,
+  },
+  'target_defaults': {
+    'dependencies': [
+      '../../metrics/libmetrics-<(libbase_ver).gyp:libmetrics-<(libbase_ver)',
+    ],
+    'variables': {
+      'deps': [
+        'dbus-1',
+        'libchrome-<(libbase_ver)',
+        'libcurl',
+      ],
+    },
+    # TODO(sosa): Remove gflags: crbug.com/356745.
+    'link_settings': {
+      'libraries': [
+        '-lgflags',
+      ],
+    },
+    'include_dirs': [
+      '..'  # To access all src/platform2 directories
+    ],
+    # TODO(sosa): Remove no-strict-aliasing: crbug.com/356745.
+    'cflags_cc': [
+      '-std=gnu++11',
+      '-fno-strict-aliasing',
+    ],
+  },
+  'targets': [
+    {
+      'target_name': 'buffet_common',
+      'type': 'static_library',
+      'sources': [
+        'data_encoding.cc',
+        'dbus_constants.cc',
+        'dbus_manager.cc',
+        'dbus_utils.cc',
+        'exported_property_set.cc',
+        'http_request.cc',
+        'http_transport_curl.cc',
+        'http_utils.cc',
+        'manager.cc',
+        'mime_utils.cc',
+        'string_utils.cc',
+      ],
+    },
+    {
+      'target_name': 'buffet',
+      'type': 'executable',
+      'sources': [
+        'main.cc',
+      ],
+      'dependencies': [
+        'buffet_common',
+      ],
+    },
+    {
+      'target_name': 'buffet_client',
+      'type': 'executable',
+      'sources': [
+        'buffet_client.cc',
+        'dbus_constants.cc',
+      ],
+    },
+    {
+      'target_name': 'buffet_testrunner',
+      'type': 'executable',
+      'dependencies': [
+        'buffet_common',
+      ],
+      'includes': ['../../common-mk/common_test.gypi'],
+      'sources': [
+        'buffet_testrunner.cc',
+        'data_encoding_unittest.cc',
+        'exported_property_set_unittest.cc',
+        'mime_utils_unittest.cc',
+        'string_utils_unittest.cc',
+      ],
+    },
+  ],
+}
diff --git a/buffet/buffet_client.cc b/buffet/buffet_client.cc
new file mode 100755
index 0000000..9568834
--- /dev/null
+++ b/buffet/buffet_client.cc
@@ -0,0 +1,149 @@
+// 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 <iostream>
+#include <string>
+
+#include <base/logging.h>
+#include <base/memory/scoped_ptr.h>
+#include <dbus/bus.h>
+#include <dbus/object_proxy.h>
+#include <dbus/message.h>
+
+#include "buffet/dbus_constants.h"
+#include "buffet/dbus_manager.h"
+
+using namespace buffet::dbus_constants;
+
+namespace {
+
+dbus::ObjectProxy* GetBuffetDBusProxy(dbus::Bus *bus,
+                                      const std::string& object_path) {
+  return bus->GetObjectProxy(
+      buffet::dbus_constants::kServiceName,
+      dbus::ObjectPath(object_path));
+}
+
+bool CallTestMethod(dbus::ObjectProxy* proxy) {
+  int timeout_ms = 1000;
+  dbus::MethodCall method_call(buffet::dbus_constants::kRootInterface,
+                               buffet::dbus_constants::kRootTestMethod);
+  scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call,
+                                                                timeout_ms));
+  if (!response) {
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
+  }
+  std::cout << "Received a response." << std::endl;
+  return true;
+}
+
+bool CallManagerRegisterDevice(dbus::ObjectProxy* proxy,
+                               const std::string& client_id,
+                               const std::string& client_secret,
+                               const std::string& api_key) {
+  dbus::MethodCall method_call(
+      buffet::dbus_constants::kManagerInterface,
+      buffet::dbus_constants::kManagerRegisterDeviceMethod);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(client_id);
+  writer.AppendString(client_secret);
+  writer.AppendString(api_key);
+  int timeout_ms = 1000;
+  scoped_ptr<dbus::Response> response(
+      proxy->CallMethodAndBlock(&method_call, timeout_ms));
+  if (!response) {
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
+  }
+
+  dbus::MessageReader reader(response.get());
+  std::string registration_id;
+  if (!reader.PopString(&registration_id)) {
+    std::cout << "No registration id in response." << std::endl;
+    return false;
+  }
+
+  std::cout << "Registration ID is " << registration_id << std::endl;
+  return true;
+}
+
+bool CallManagerUpdateState(dbus::ObjectProxy* proxy,
+                            const std::string& json_blob) {
+  dbus::MethodCall method_call(
+      buffet::dbus_constants::kManagerInterface,
+      buffet::dbus_constants::kManagerUpdateStateMethod);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(json_blob);
+  int timeout_ms = 1000;
+  scoped_ptr<dbus::Response> response(
+      proxy->CallMethodAndBlock(&method_call, timeout_ms));
+  if (!response) {
+    std::cout << "Failed to receive a response." << std::endl;
+    return false;
+  }
+  return true;
+}
+
+void usage() {
+  std::cerr << "Possible commands:" << std::endl;
+  std::cerr << "  " << kRootTestMethod << std::endl;
+  std::cerr << "  " << kManagerRegisterDeviceMethod
+            << "  " << " <client id> <client secret> <api key>" << std::endl;
+  std::cerr << "  " << kManagerUpdateStateMethod << std::endl;
+}
+
+} // namespace
+
+int main(int argc, char** argv) {
+  dbus::Bus::Options options;
+  options.bus_type = dbus::Bus::SYSTEM;
+  scoped_refptr<dbus::Bus> bus(new dbus::Bus(options));
+
+  if (argc < 2) {
+    usage();
+    return -1;
+  }
+
+  char* command = argv[1];
+  bool success = false;
+  if (strcmp(command, kRootTestMethod) == 0) {
+    auto proxy = GetBuffetDBusProxy(
+        bus, buffet::dbus_constants::kRootServicePath);
+    success = CallTestMethod(proxy);
+  } else if (strcmp(command, kManagerRegisterDeviceMethod) == 0) {
+    if (argc != 5) {
+      std::cerr << "Invalid number of arguments for "
+                << "Manager.RegisterDevice" << std::endl;
+      usage();
+      return -1;
+    }
+    auto proxy = GetBuffetDBusProxy(
+        bus, buffet::dbus_constants::kManagerServicePath);
+    success = CallManagerRegisterDevice(proxy, argv[2], argv[3], argv[4]);
+  } else if (strcmp(command, kManagerUpdateStateMethod) == 0) {
+    if (argc != 3) {
+      std::cerr << "Invalid number of arguments for "
+                << "Manager.UpdateState" << std::endl;
+      usage();
+      return -1;
+    }
+    auto proxy = GetBuffetDBusProxy(
+        bus, buffet::dbus_constants::kManagerServicePath);
+    success = CallManagerUpdateState(proxy, argv[2]);
+  } else {
+    std::cerr << "Unkown command: " << command << std::endl;
+    usage();
+    return -1;
+  }
+
+  if (success) {
+    std::cout << "Done." << std::endl;
+    return 0;
+  }
+
+  std::cout << "Done, with errors." << std::endl;
+  return -1;
+}
+
diff --git a/buffet/buffet_testrunner.cc b/buffet/buffet_testrunner.cc
new file mode 100644
index 0000000..575f952
--- /dev/null
+++ b/buffet/buffet_testrunner.cc
@@ -0,0 +1,12 @@
+// Copyright (c) 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 <base/at_exit.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+  base::AtExitManager exit_manager;
+  ::testing::InitGoogleTest(&argc, argv);
+  return RUN_ALL_TESTS();
+}
diff --git a/buffet/data_encoding.cc b/buffet/data_encoding.cc
new file mode 100644
index 0000000..0e040ab
--- /dev/null
+++ b/buffet/data_encoding.cc
@@ -0,0 +1,102 @@
+// 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/data_encoding.h"
+
+#include <base/strings/stringprintf.h>
+#include <string.h>
+
+#include "buffet/string_utils.h"
+
+namespace {
+
+inline int HexToDec(int hex) {
+  int dec = -1;
+  if (hex >= '0' && hex <= '9') {
+    dec = hex - '0';
+  } else if (hex >= 'A' && hex <= 'F') {
+    dec = hex - 'A' + 10;
+  } else if (hex >= 'a' && hex <= 'f') {
+    dec = hex - 'a' + 10;
+  }
+  return dec;
+}
+
+} // namespace
+
+/////////////////////////////////////////////////////////////////////////
+namespace chromeos {
+namespace data_encoding {
+
+std::string UrlEncode(char const* data, bool encodeSpaceAsPlus) {
+  std::string result;
+
+  while(*data) {
+    char c = *data++;
+    // According to RFC3986 (http://www.faqs.org/rfcs/rfc3986.html),
+    // section 2.3. - Unreserved Characters
+    if ((c >= '0' && c <= '9') ||
+        (c >= 'A' && c <= 'Z') ||
+        (c >= 'a' && c <= 'z') ||
+        c == '-' || c == '.' || c == '_' || c == '~') {
+      result += c;
+    } else if (c == ' ' && encodeSpaceAsPlus) {
+      // For historical reasons, some URLs have spaces encoded as '+',
+      // this also applies to form data encoded as
+      // 'application/x-www-form-urlencoded'
+      result += '+';
+    } else {
+      base::StringAppendF(&result, "%%%02X", (unsigned char)c); // Encode as %NN
+    }
+  }
+  return result;
+}
+
+std::string UrlDecode(char const* data) {
+  std::string result;
+  while (*data) {
+    char c = *data++;
+    int part1 = 0, part2 = 0;
+    // HexToDec would return -1 even for character 0 (end of string),
+    // so it is safe to access data[0] and data[1] without overrunning the buf.
+    if (c == '%' &&
+        (part1 = HexToDec(data[0])) >= 0 && (part2 = HexToDec(data[1])) >= 0) {
+      c = char((part1 << 4) | part2);
+      data += 2;
+    } else if (c == '+') {
+      c = ' ';
+    }
+    result += c;
+  }
+  return result;
+}
+
+std::string WebParamsEncode(
+    std::vector<std::pair<std::string, std::string>> const& params,
+    bool encodeSpaceAsPlus) {
+  std::vector<std::string> pairs;
+  pairs.reserve(params.size());
+  for (auto const& p : params) {
+    std::string key = UrlEncode(p.first.c_str(), encodeSpaceAsPlus);
+    std::string value = UrlEncode(p.second.c_str(), encodeSpaceAsPlus);
+    pairs.push_back(string_utils::Join('=', key, value));
+  }
+
+  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;
+  std::vector<std::string> params = string_utils::Split(data, '&');
+  for (auto p : params) {
+    auto pair = string_utils::SplitAtFirst(p, '=');
+    result.emplace_back(UrlDecode(pair.first.c_str()),
+                        UrlDecode(pair.second.c_str()));
+  }
+  return result;
+}
+
+} // namespace data_encoding
+} // namespace chromeos
diff --git a/buffet/data_encoding.h b/buffet/data_encoding.h
new file mode 100644
index 0000000..6c46e45
--- /dev/null
+++ b/buffet/data_encoding.h
@@ -0,0 +1,47 @@
+// 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_DATA_ENCODING__H_
+#define BUFFET_DATA_ENCODING__H_
+
+#include <vector>
+#include <string>
+
+namespace chromeos {
+namespace data_encoding {
+
+// 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"
+std::string UrlEncode(char const* data, bool encodeSpaceAsPlus);
+
+inline std::string UrlEncode(char const* data) {
+  return UrlEncode(data, true);
+}
+
+// Decodes/unescapes a URL. Replaces all %XX sequences with actual characters.
+// Also replaces '+' with spaces.
+std::string UrlDecode(char const* data);
+
+// 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);
+
+inline std::string WebParamsEncode(
+    std::vector<std::pair<std::string, std::string>> 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);
+
+} // namespace data_encoding
+} // namespace chromeos
+
+#endif // BUFFET_DATA_ENCODING__H_
diff --git a/buffet/data_encoding_unittest.cc b/buffet/data_encoding_unittest.cc
new file mode 100644
index 0000000..ae4894d
--- /dev/null
+++ b/buffet/data_encoding_unittest.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 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/data_encoding.h"
+
+#include <gtest/gtest.h>
+
+using namespace chromeos::data_encoding;
+
+TEST(data_encoding, UrlEncoding) {
+  std::string test = "\"http://sample/path/0014.html \"";
+  std::string encoded = UrlEncode(test.c_str());
+  EXPECT_EQ("%22http%3A%2F%2Fsample%2Fpath%2F0014.html+%22",
+            encoded);
+  EXPECT_EQ(test, UrlDecode(encoded.c_str()));
+
+  test = "\"http://sample/path/0014.html \"";
+  encoded = UrlEncode(test.c_str(), false);
+  EXPECT_EQ("%22http%3A%2F%2Fsample%2Fpath%2F0014.html%20%22",
+            encoded);
+  EXPECT_EQ(test, UrlDecode(encoded.c_str()));
+
+}
+
+TEST(data_encoding, WebParamsEncoding) {
+  std::string encoded = WebParamsEncode({{"q", "test"},
+                                         {"path", "/usr/bin"},
+                                         {"#", "%"}});
+  EXPECT_EQ("q=test&path=%2Fusr%2Fbin&%23=%25", encoded);
+
+  auto params = WebParamsDecode(encoded);
+  EXPECT_EQ(3, params.size());
+  EXPECT_EQ("q", params[0].first);
+  EXPECT_EQ("test", params[0].second);
+  EXPECT_EQ("path", params[1].first);
+  EXPECT_EQ("/usr/bin", params[1].second);
+  EXPECT_EQ("#", params[2].first);
+  EXPECT_EQ("%", params[2].second);
+}
diff --git a/buffet/dbus/org.chromium.Buffet.conf b/buffet/dbus/org.chromium.Buffet.conf
new file mode 100644
index 0000000..0f6ea4d
--- /dev/null
+++ b/buffet/dbus/org.chromium.Buffet.conf
@@ -0,0 +1,13 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+  <policy user="root">
+    <allow own="org.chromium.Buffet" />
+    <allow send_destination="org.chromium.Buffet" />
+  </policy>
+
+  <policy context="default">
+    <allow send_destination="org.chromium.Buffet" />
+  </policy>
+</busconfig>
\ No newline at end of file
diff --git a/buffet/dbus_constants.cc b/buffet/dbus_constants.cc
new file mode 100644
index 0000000..746058e
--- /dev/null
+++ b/buffet/dbus_constants.cc
@@ -0,0 +1,26 @@
+// 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/dbus_constants.h"
+
+namespace buffet {
+
+namespace dbus_constants {
+
+const char kServiceName[] = "org.chromium.Buffet";
+
+const char kRootInterface[] = "org.chromium.Buffet";
+const char kRootServicePath[] = "/org/chromium/Buffet";
+
+const char kRootTestMethod[] = "TestMethod";
+
+const char kManagerInterface[] = "org.chromium.Buffet.Manager";
+const char kManagerServicePath[] = "/org/chromium/Buffet/Manager";
+
+const char kManagerUpdateStateMethod[] = "UpdateState";
+const char kManagerRegisterDeviceMethod[] = "RegisterDevice";
+
+}  // namespace dbus_constants
+
+}  // namespace buffet
diff --git a/buffet/dbus_constants.h b/buffet/dbus_constants.h
new file mode 100644
index 0000000..e30fdde
--- /dev/null
+++ b/buffet/dbus_constants.h
@@ -0,0 +1,34 @@
+// 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_DBUS_CONSTANTS_H_
+#define BUFFET_DBUS_CONSTANTS_H_
+
+namespace buffet {
+
+namespace dbus_constants {
+
+// The service name claimed by the Buffet daemon.
+extern const char kServiceName[];
+
+// Interface implemented by the object at kRootServicePath.
+extern const char kRootInterface[];
+extern const char kRootServicePath[];
+
+// Methods exposed as part of kRootInterface.
+extern const char kRootTestMethod[];
+
+// Interface implemented by the object at kManagerServicePath.
+extern const char kManagerInterface[];
+extern const char kManagerServicePath[];
+
+// Methods exposed as part of kManagerInterface.
+extern const char kManagerUpdateStateMethod[];
+extern const char kManagerRegisterDeviceMethod[];
+
+}  // namespace dbus_constants
+
+}  // namespace buffet
+
+#endif  // BUFFET_DBUS_CONSTANTS_H_
diff --git a/buffet/dbus_manager.cc b/buffet/dbus_manager.cc
new file mode 100644
index 0000000..ceddee1
--- /dev/null
+++ b/buffet/dbus_manager.cc
@@ -0,0 +1,93 @@
+// 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/dbus_manager.h"
+
+#include <string>
+
+#include <base/bind.h>
+
+#include "buffet/dbus_constants.h"
+
+using ::std::string;
+
+namespace buffet {
+
+namespace {
+
+// Passes |method_call| to |handler| and passes the response to
+// |response_sender|. If |handler| returns NULL, an empty response is created
+// and sent.
+void HandleSynchronousDBusMethodCall(
+    base::Callback<scoped_ptr<dbus::Response>(dbus::MethodCall*)> handler,
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  auto response = handler.Run(method_call);
+  if (!response)
+    response = dbus::Response::FromMethodCall(method_call);
+
+  response_sender.Run(response.Pass());
+}
+
+}  // namespace
+
+DBusManager::DBusManager()
+    : bus_(nullptr) {}
+
+DBusManager::~DBusManager() {}
+
+void DBusManager::Init() {
+  InitDBus();
+}
+
+void DBusManager::Finalize() {
+  ShutDownDBus();
+}
+
+void DBusManager::InitDBus() {
+  dbus::Bus::Options options;
+  // TODO(sosa): Should this be on the system bus?
+  options.bus_type = dbus::Bus::SYSTEM;
+  bus_ = new dbus::Bus(options);
+  CHECK(bus_->Connect());
+
+  // buffet_dbus_object is owned by the Bus.
+  auto buffet_dbus_object = GetExportedObject(dbus_constants::kRootServicePath);
+  ExportDBusMethod(
+      buffet_dbus_object,
+      dbus_constants::kRootInterface, dbus_constants::kRootTestMethod,
+      base::Bind(&DBusManager::HandleTestMethod, base::Unretained(this)));
+
+  CHECK(bus_->RequestOwnershipAndBlock(dbus_constants::kServiceName,
+                                       dbus::Bus::REQUIRE_PRIMARY))
+      << "Unable to take ownership of " << dbus_constants::kServiceName;
+}
+
+void DBusManager::ShutDownDBus() {
+  bus_->ShutdownAndBlock();
+}
+
+dbus::ExportedObject* DBusManager::GetExportedObject(
+    const string& object_path) {
+  return bus_->GetExportedObject(dbus::ObjectPath(object_path));
+}
+
+void DBusManager::ExportDBusMethod(
+    dbus::ExportedObject* exported_object,
+    const string& interface_name,
+    const string& method_name,
+    base::Callback<scoped_ptr<dbus::Response>(dbus::MethodCall*)> handler) {
+  DCHECK(exported_object);
+  CHECK(exported_object->ExportMethodAndBlock(
+      interface_name, method_name,
+      base::Bind(&HandleSynchronousDBusMethodCall, handler)));
+}
+
+scoped_ptr<dbus::Response> DBusManager::HandleTestMethod(
+    dbus::MethodCall* method_call) {
+  LOG(INFO) << "Received call to test method.";
+  return scoped_ptr<dbus::Response>();
+}
+
+}  // namespace buffet
diff --git a/buffet/dbus_manager.h b/buffet/dbus_manager.h
new file mode 100644
index 0000000..a990ead
--- /dev/null
+++ b/buffet/dbus_manager.h
@@ -0,0 +1,56 @@
+// 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_DBUS_MANAGER_H_
+#define BUFFET_DBUS_MANAGER_H_
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+#include <dbus/bus.h>
+#include <dbus/exported_object.h>
+#include <dbus/message.h>
+
+namespace buffet {
+
+// Class that manages dbus interactions in buffet.
+class DBusManager {
+ public:
+  DBusManager();
+  virtual ~DBusManager();
+
+  void Init();
+  void Finalize();
+
+  // Get an object owned by the ::dbus::Bus object.  This object
+  // has methods to export DBus facing methods.
+  ::dbus::ExportedObject* GetExportedObject(
+      const std::string& object_path);
+
+  // Exports |method_name| on |exported_object| and uses |member|
+  // to handle calls.
+  void ExportDBusMethod(
+      ::dbus::ExportedObject* exported_object,
+      const std::string& interface_name,
+      const std::string& method_name,
+      base::Callback<scoped_ptr<::dbus::Response>(
+          ::dbus::MethodCall*)> handler);
+
+ private:
+  // Connects to the D-Bus system bus and exports methods.
+  void InitDBus();
+  void ShutDownDBus();
+
+  // Callbacks for handling D-Bus signals and method calls.
+  scoped_ptr<::dbus::Response> HandleTestMethod(
+      ::dbus::MethodCall* method_call);
+
+  scoped_refptr<::dbus::Bus> bus_;
+
+  DISALLOW_COPY_AND_ASSIGN(DBusManager);
+};
+
+}  // namespace buffet
+
+#endif  // BUFFET_DBUS_MANAGER_H_
diff --git a/buffet/dbus_utils.cc b/buffet/dbus_utils.cc
new file mode 100644
index 0000000..fcda628
--- /dev/null
+++ b/buffet/dbus_utils.cc
@@ -0,0 +1,23 @@
+// 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 <base/logging.h>
+
+#include "buffet/dbus_utils.h"
+
+namespace buffet {
+
+namespace dbus_utils {
+
+scoped_ptr<dbus::Response> GetBadArgsError(dbus::MethodCall* method_call,
+                                           const std::string& message) {
+  LOG(ERROR) << "Error while handling DBus call: " << message;
+  scoped_ptr<dbus::ErrorResponse> resp(dbus::ErrorResponse::FromMethodCall(
+      method_call, "org.freedesktop.DBus.Error.InvalidArgs", message));
+  return scoped_ptr<dbus::Response>(resp.release());
+}
+
+}  // namespace dbus_utils
+
+}  // namespace buffet
diff --git a/buffet/dbus_utils.h b/buffet/dbus_utils.h
new file mode 100644
index 0000000..dea6d02
--- /dev/null
+++ b/buffet/dbus_utils.h
@@ -0,0 +1,25 @@
+// 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_DBUS_UTILS_H_
+#define BUFFET_DBUS_UTILS_H_
+
+#include <string>
+
+#include <base/memory/scoped_ptr.h>
+#include <dbus/message.h>
+
+namespace buffet {
+
+namespace dbus_utils {
+
+scoped_ptr<dbus::Response> GetBadArgsError(dbus::MethodCall* method_call,
+                                           const std::string& message);
+
+}  // namespace dbus_utils
+
+}  // namespace buffet
+
+#endif  // BUFFET_DBUS_UTILS_H_
+
diff --git a/buffet/exported_property_set.cc b/buffet/exported_property_set.cc
new file mode 100644
index 0000000..fe1ef65
--- /dev/null
+++ b/buffet/exported_property_set.cc
@@ -0,0 +1,283 @@
+// 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/exported_property_set.h"
+
+#include <base/bind.h>
+#include <dbus/property.h>  // For kPropertyInterface
+
+#include "buffet/dbus_utils.h"
+
+namespace buffet {
+
+namespace dbus_utils {
+
+ExportedPropertySet::ExportedPropertySet(dbus::ExportedObject* exported_object)
+    : exported_object_(exported_object), weak_ptr_factory_(this) { }
+
+void ExportedPropertySet::ClaimPropertiesInterface() {
+  exported_object_->ExportMethodAndBlock(
+      dbus::kPropertiesInterface, dbus::kPropertiesGetAll,
+      base::Bind(&ExportedPropertySet::HandleGetAll,
+                 weak_ptr_factory_.GetWeakPtr()));
+  exported_object_->ExportMethodAndBlock(
+      dbus::kPropertiesInterface, dbus::kPropertiesGet,
+      base::Bind(&ExportedPropertySet::HandleGet,
+                 weak_ptr_factory_.GetWeakPtr()));
+  exported_object_->ExportMethodAndBlock(
+      dbus::kPropertiesInterface, dbus::kPropertiesSet,
+      base::Bind(&ExportedPropertySet::HandleSet,
+                 weak_ptr_factory_.GetWeakPtr()));
+}
+
+ExportedPropertySet::~ExportedPropertySet() { }
+
+void ExportedPropertySet::RegisterProperty(
+    const std::string& interface_name,
+    const std::string& property_name,
+    ExportedPropertyBase* exported_property) {
+  properties_[interface_name][property_name] = exported_property;
+  // Technically, the property set exists longer than the properties themselves,
+  // so we could use Unretained here rather than a weak pointer.
+  ExportedPropertyBase::OnUpdateCallback cb = base::Bind(
+      &ExportedPropertySet::HandlePropertyUpdated,
+      weak_ptr_factory_.GetWeakPtr(),
+      interface_name, property_name);
+  exported_property->SetUpdateCallback(cb);
+}
+
+void ExportedPropertySet::HandleGetAll(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+  std::string interface_name;
+  if (!reader.PopString(&interface_name)) {
+    response_sender.Run(
+        GetBadArgsError(method_call, "No interface name specified."));
+    return;
+  }
+  if (reader.HasMoreData()) {
+    response_sender.Run(
+        GetBadArgsError(method_call, "Too many arguments to GetAll."));
+    return;
+  }
+  auto property_map_itr = properties_.find(interface_name);
+  if (property_map_itr == properties_.end()) {
+    response_sender.Run(
+        GetBadArgsError(method_call, "No such interface on object."));
+    return;
+  }
+  scoped_ptr<dbus::Response> response(
+      dbus::Response::FromMethodCall(method_call));
+  dbus::MessageWriter resp_writer(response.get());
+  dbus::MessageWriter dict_writer(nullptr);
+  resp_writer.OpenArray("{sv}", &dict_writer);
+  for (const auto& kv : property_map_itr->second) {
+    dbus::MessageWriter entry_writer(nullptr);
+    dict_writer.OpenDictEntry(&entry_writer);
+    entry_writer.AppendString(kv.first);
+    kv.second->AppendValueToWriter(&entry_writer);
+    dict_writer.CloseContainer(&entry_writer);
+  }
+  resp_writer.CloseContainer(&dict_writer);
+  response_sender.Run(response.Pass());
+}
+
+void ExportedPropertySet::HandleGet(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  dbus::MessageReader reader(method_call);
+  std::string interface_name, property_name;
+  if (!reader.PopString(&interface_name)) {
+    response_sender.Run(
+        GetBadArgsError(method_call, "No interface name specified."));
+    return;
+  }
+  if (!reader.PopString(&property_name)) {
+    response_sender.Run(
+        GetBadArgsError(method_call, "No property name specified."));
+    return;
+  }
+  if (reader.HasMoreData()) {
+    response_sender.Run(
+        GetBadArgsError(method_call, "Too many arguments to Get."));
+    return;
+  }
+  auto property_map_itr = properties_.find(interface_name);
+  if (property_map_itr == properties_.end()) {
+    response_sender.Run(
+        GetBadArgsError(method_call, "No such interface on object."));
+    return;
+  }
+  LOG(ERROR) << "Looking for " << property_name << " on " << interface_name;
+  auto property_itr = property_map_itr->second.find(property_name);
+  if (property_itr == property_map_itr->second.end()) {
+    response_sender.Run(
+        GetBadArgsError(method_call, "No such property on interface."));
+    return;
+  }
+  scoped_ptr<dbus::Response> response(
+      dbus::Response::FromMethodCall(method_call));
+  dbus::MessageWriter resp_writer(response.get());
+  property_itr->second->AppendValueToWriter(&resp_writer);
+  response_sender.Run(response.Pass());
+}
+
+void ExportedPropertySet::HandleSet(
+    dbus::MethodCall* method_call,
+    dbus::ExportedObject::ResponseSender response_sender) {
+  scoped_ptr<dbus::ErrorResponse> error_resp(
+      dbus::ErrorResponse::FromMethodCall(
+          method_call, "org.freedesktop.DBus.Error.NotSupported", ""));
+  scoped_ptr<dbus::Response> response(error_resp.release());
+  response_sender.Run(response.Pass());
+}
+
+void ExportedPropertySet::HandlePropertyUpdated(
+    const std::string& interface,
+    const std::string& name,
+    const ExportedPropertyBase* property) {
+  // TODO(wiley): Send a signal from the exported object here.
+}
+
+template <typename T>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const T& value);
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const bool& value) {
+  writer->AppendVariantOfBool(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint8& value) {
+  writer->AppendVariantOfByte(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const int16& value) {
+  writer->AppendVariantOfInt16(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint16& value) {
+  writer->AppendVariantOfUint16(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const int32& value) {
+  writer->AppendVariantOfInt32(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint32& value) {
+  writer->AppendVariantOfUint32(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const int64& value) {
+  writer->AppendVariantOfInt64(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const uint64& value) {
+  writer->AppendVariantOfUint64(value);
+}
+
+template <>
+void AppendPropertyToWriter(dbus::MessageWriter* writer, const double& value) {
+  writer->AppendVariantOfDouble(value);
+}
+
+template <>
+void AppendPropertyToWriter(
+    dbus::MessageWriter* writer, const std::string& value) {
+  writer->AppendVariantOfString(value);
+}
+
+template <>
+void AppendPropertyToWriter(
+    dbus::MessageWriter* writer, const dbus::ObjectPath& value) {
+  writer->AppendVariantOfObjectPath(value);
+}
+
+template <>
+void AppendPropertyToWriter(
+    dbus::MessageWriter* writer, const std::vector<std::string>& value) {
+  dbus::MessageWriter variant_writer(nullptr);
+  writer->OpenVariant("as", &variant_writer);
+  variant_writer.AppendArrayOfStrings(value);
+  writer->CloseContainer(&variant_writer);
+}
+
+template <>
+void AppendPropertyToWriter(
+    dbus::MessageWriter* writer, const std::vector<dbus::ObjectPath>& value) {
+  dbus::MessageWriter variant_writer(nullptr);
+  writer->OpenVariant("ao", &variant_writer);
+  variant_writer.AppendArrayOfObjectPaths(value);
+  writer->CloseContainer(&variant_writer);
+}
+
+template <>
+void AppendPropertyToWriter(
+    dbus::MessageWriter* writer, const std::vector<uint8>& value) {
+  dbus::MessageWriter variant_writer(nullptr);
+  writer->OpenVariant("ay", &variant_writer);
+  variant_writer.AppendArrayOfBytes(value.data(), value.size());
+  writer->CloseContainer(&variant_writer);
+}
+
+template <typename T>
+ExportedProperty<T>::ExportedProperty() {}
+
+template <typename T>
+ExportedProperty<T>::~ExportedProperty() {}
+
+template <typename T>
+const T& ExportedProperty<T>::value() const { return value_; }
+
+template <typename T>
+void ExportedProperty<T>::SetValue(const T& new_value) {
+  if (value_ == new_value) {
+    return;
+  }
+  value_ = new_value;
+  // These is a brief period after the construction of an ExportedProperty
+  // when this callback is not initialized because the property has not
+  // been registered with the parent ExportedPropertySet.  During this period
+  // users should be initializing values via SetValue, and no notifications
+  // should be triggered by the ExportedPropertySet.
+  if (!on_update_.is_null()) {
+    on_update_.Run(this);
+  }
+}
+
+template <typename T>
+void ExportedProperty<T>::SetUpdateCallback(const OnUpdateCallback& cb) {
+  on_update_ = cb;
+}
+
+template <typename T>
+void ExportedProperty<T>::AppendValueToWriter(dbus::MessageWriter* writer) {
+  AppendPropertyToWriter(writer, value_);
+}
+
+template class ExportedProperty<bool>;
+template class ExportedProperty<uint8>;
+template class ExportedProperty<int16>;
+template class ExportedProperty<uint16>;
+template class ExportedProperty<int32>;
+template class ExportedProperty<uint32>;
+template class ExportedProperty<int64>;
+template class ExportedProperty<uint64>;
+template class ExportedProperty<double>;
+template class ExportedProperty<std::string>;
+template class ExportedProperty<dbus::ObjectPath>;
+template class ExportedProperty<std::vector<std::string>>;
+template class ExportedProperty<std::vector<dbus::ObjectPath>>;
+template class ExportedProperty<std::vector<uint8>>;
+
+}  // namespace dbus_utils
+
+}  // namespace buffet
diff --git a/buffet/exported_property_set.h b/buffet/exported_property_set.h
new file mode 100644
index 0000000..7ed9d9b
--- /dev/null
+++ b/buffet/exported_property_set.h
@@ -0,0 +1,185 @@
+// 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_EXPORTED_PROPERTY_SET_H_
+#define BUFFET_EXPORTED_PROPERTY_SET_H_
+
+#include <map>
+#include <string>
+
+#include <base/memory/weak_ptr.h>
+#include <dbus/exported_object.h>
+#include <dbus/message.h>
+#include <gtest/gtest_prod.h>
+
+namespace buffet {
+
+namespace dbus_utils {
+
+// This class may be used to implement the org.freedesktop.DBus.Properties
+// interface.  It sends the update signal on property updates:
+//
+//   org.freedesktop.DBus.Properties.PropertiesChanged (
+//       STRING interface_name,
+//       DICT<STRING,VARIANT> changed_properties,
+//       ARRAY<STRING> invalidated_properties);
+//
+//
+// and implements the required methods of the interface:
+//
+//   org.freedesktop.DBus.Properties.Get(in STRING interface_name,
+//                                       in STRING property_name,
+//                                       out VARIANT value);
+//   org.freedesktop.DBus.Properties.Set(in STRING interface_name,
+//                                       in STRING property_name,
+//                                       in VARIANT value);
+//   org.freedesktop.DBus.Properties.GetAll(in STRING interface_name,
+//                                          out DICT<STRING,VARIANT> props);
+//
+//  This class is very similar to the PropertySet class in Chrome, except that
+//  it allows objects to expose properties rather than to consume them.
+//
+//  Example usage:
+//
+//   class ExampleObjectExportingProperties {
+//    public:
+//     ExampleObjectExportingProperties(ExportedObject* exported_object)
+//         : p_(exported_object) {
+//       // Initialize properties appropriately.  Do this before
+//       // claiming the Properties interface so that daemons watching
+//       // this object don't see partial or inaccurate state.
+//       p_.ClaimPropertiesInterface();
+//     }
+//
+//    private:
+//     struct Properties : public buffet::dbus::ExportedPropertySet {
+//      public:
+//       buffet::dbus::ExportedProperty<std::string> name_;
+//       buffet::dbus::ExportedProperty<uint16> version_;
+//       buffet::dbus::ExportedProperty<dbus::ObjectPath> parent_;
+//       buffet::dbus::ExportedProperty<std::vector<std::string>> children_;
+//
+//       Properties(dbus::ExportedObject* exported_object)
+//           : buffet::dbus::ExportedPropertySet(exported_object) {
+//         RegisterProperty(kExampleInterfaceName, "Name", &name_);
+//         RegisterProperty(kExampleInterfaceName, "Version", &version_);
+//         RegisterProperty(kExampleInterfaceName, "Parent", &parent_);
+//         RegisterProperty(kExampleInterfaceName, "Children", &children_);
+//       }
+//       virtual ~Properties() {}
+//     };
+//
+//     Properties p_;
+//   };
+
+class ExportedPropertyBase {
+ public:
+  ExportedPropertyBase() {}
+  virtual ~ExportedPropertyBase() {}
+
+  typedef base::Callback<void(const ExportedPropertyBase*)> OnUpdateCallback;
+
+  // Called by ExportedPropertySet to register a callback.  This callback
+  // triggers ExportedPropertySet to send a signal from the properties
+  // interface of the exported object.
+  virtual void SetUpdateCallback(const OnUpdateCallback& cb) = 0;
+
+  // Appends a variant of the contained value to the writer.  This is
+  // needed to write out properties to Get and GetAll methods implemented
+  // by the ExportedPropertySet since it doesn't actually know the type
+  // of each property.
+  virtual void AppendValueToWriter(dbus::MessageWriter* writer) = 0;
+};
+
+class ExportedPropertySet {
+ public:
+  ExportedPropertySet(dbus::ExportedObject* exported_object);
+  ~ExportedPropertySet();
+
+  // Claims the org.freedesktop.DBus.Properties interface.  This
+  // needs to be done after all properties are initialized to
+  // appropriate values.
+  void ClaimPropertiesInterface();
+
+ protected:
+  void RegisterProperty(const std::string& interface_name,
+                        const std::string& property_name,
+                        ExportedPropertyBase* exported_property);
+
+ private:
+  void HandleGetAll(dbus::MethodCall* method_call,
+                    dbus::ExportedObject::ResponseSender response_sender);
+  void HandleGet(dbus::MethodCall* method_call,
+                 dbus::ExportedObject::ResponseSender response_sender);
+  // While Properties.Set has a handler to complete the interface,  we don't
+  // support writable properties.  This is almost a feature, since bindings for
+  // many languages don't support errors coming back from invalid writes.
+  // Instead, use setters in exposed interfaces.
+  void HandleSet(dbus::MethodCall* method_call,
+                 dbus::ExportedObject::ResponseSender response_sender);
+
+  virtual void HandlePropertyUpdated(const std::string& interface,
+                                     const std::string& name,
+                                     const ExportedPropertyBase* property);
+
+  dbus::ExportedObject* exported_object_;  // weak; owned by the Bus object.
+  // This is a map from interface name -> property name -> pointer to property.
+  std::map<std::string,
+           std::map<std::string, ExportedPropertyBase*>> properties_;
+
+  // D-Bus callbacks may last longer the property set exporting those methods.
+  base::WeakPtrFactory<ExportedPropertySet> weak_ptr_factory_;
+
+  friend class ExportedPropertySetTest;
+  DISALLOW_COPY_AND_ASSIGN(ExportedPropertySet);
+};
+
+template <typename T>
+class ExportedProperty : public ExportedPropertyBase {
+ public:
+  ExportedProperty();
+  virtual ~ExportedProperty() override;
+
+  // Retrieves the current value.
+  const T& value() const;
+
+  // Set the value exposed to remote applications.  This triggers notifications
+  // of changes over the Properties interface.
+  void SetValue(const T& new_value);
+
+  // Called by ExportedPropertySet.  This update callback triggers
+  // ExportedPropertySet to send a signal from the properties interface of the
+  // exported object.
+  virtual void SetUpdateCallback(const OnUpdateCallback& cb) override;
+
+  // Implementation provided by specialization.
+  virtual void AppendValueToWriter(dbus::MessageWriter* writer) override;
+
+ private:
+  OnUpdateCallback on_update_;
+  T value_{};
+
+  DISALLOW_COPY_AND_ASSIGN(ExportedProperty);
+};
+
+extern template class ExportedProperty<bool>;
+extern template class ExportedProperty<uint8>;
+extern template class ExportedProperty<int16>;
+extern template class ExportedProperty<uint16>;
+extern template class ExportedProperty<int32>;
+extern template class ExportedProperty<uint32>;
+extern template class ExportedProperty<int64>;
+extern template class ExportedProperty<uint64>;
+extern template class ExportedProperty<double>;
+extern template class ExportedProperty<std::string>;
+extern template class ExportedProperty<dbus::ObjectPath>;
+extern template class ExportedProperty<std::vector<std::string>>;
+extern template class ExportedProperty<std::vector<dbus::ObjectPath>>;
+extern template class ExportedProperty<std::vector<uint8>>;
+
+}  // namespace dbus_utils
+
+}  // namespace buffet
+
+#endif  // BUFFET_EXPORTED_PROPERTY_SET_H_
diff --git a/buffet/exported_property_set_unittest.cc b/buffet/exported_property_set_unittest.cc
new file mode 100644
index 0000000..257614e
--- /dev/null
+++ b/buffet/exported_property_set_unittest.cc
@@ -0,0 +1,475 @@
+// 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/exported_property_set.h"
+
+#include <string>
+#include <vector>
+
+#include <base/basictypes.h>
+#include <base/bind.h>
+#include <dbus/message.h>
+#include <dbus/property.h>
+#include <dbus/object_path.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace buffet {
+
+namespace dbus_utils {
+
+namespace {
+
+const char kBoolPropName[] = "BoolProp";
+const char kUint8PropName[] = "Uint8Prop";
+const char kInt16PropName[] = "Int16Prop";
+const char kUint16PropName[] = "Uint16Prop";
+const char kInt32PropName[] = "Int32Prop";
+const char kUint32PropName[] = "Uint32Prop";
+const char kInt64PropName[] = "Int64Prop";
+const char kUint64PropName[] = "Uint64Prop";
+const char kDoublePropName[] = "DoubleProp";
+const char kStringPropName[] = "StringProp";
+const char kPathPropName[] = "PathProp";
+const char kStringListPropName[] = "StringListProp";
+const char kPathListPropName[] = "PathListProp";
+const char kUint8ListPropName[] = "Uint8ListProp";
+
+const char kTestInterface1[] = "org.chromium.TestInterface1";
+const char kTestInterface2[] = "org.chromium.TestInterface2";
+const char kTestInterface3[] = "org.chromium.TestInterface3";
+
+const std::string kTestString("lies");
+const dbus::ObjectPath kTestObjectPathInit(std::string("/path_init"));
+const dbus::ObjectPath kTestObjectPathUpdate(std::string("/path_update"));
+
+}  // namespace
+
+class ExportedPropertySetTest : public ::testing::Test {
+ public:
+  ExportedPropertySetTest() {}
+  struct Properties : public ExportedPropertySet {
+   public:
+    ExportedProperty<bool> bool_prop_;
+    ExportedProperty<uint8> uint8_prop_;
+    ExportedProperty<int16> int16_prop_;
+    ExportedProperty<uint16> uint16_prop_;
+    ExportedProperty<int32> int32_prop_;
+    ExportedProperty<uint32> uint32_prop_;
+    ExportedProperty<int64> int64_prop_;
+    ExportedProperty<uint64> uint64_prop_;
+    ExportedProperty<double> double_prop_;
+    ExportedProperty<std::string> string_prop_;
+    ExportedProperty<dbus::ObjectPath> path_prop_;
+    ExportedProperty<std::vector<std::string>> stringlist_prop_;
+    ExportedProperty<std::vector<dbus::ObjectPath>> pathlist_prop_;
+    ExportedProperty<std::vector<uint8>> uint8list_prop_;
+
+    Properties() : ExportedPropertySet(nullptr) {
+      // The empty string is not a valid value for an ObjectPath.
+      path_prop_.SetValue(kTestObjectPathInit);
+      RegisterProperty(kTestInterface1, kBoolPropName, &bool_prop_);
+      RegisterProperty(kTestInterface1, kUint8PropName, &uint8_prop_);
+      RegisterProperty(kTestInterface1, kInt16PropName, &int16_prop_);
+      // I chose this weird grouping because N=2 is about all the permutations
+      // of GetAll that I want to anticipate.
+      RegisterProperty(kTestInterface2, kUint16PropName, &uint16_prop_);
+      RegisterProperty(kTestInterface2, kInt32PropName, &int32_prop_);
+      RegisterProperty(kTestInterface3, kUint32PropName, &uint32_prop_);
+      RegisterProperty(kTestInterface3, kInt64PropName, &int64_prop_);
+      RegisterProperty(kTestInterface3, kUint64PropName, &uint64_prop_);
+      RegisterProperty(kTestInterface3, kDoublePropName, &double_prop_);
+      RegisterProperty(kTestInterface3, kStringPropName, &string_prop_);
+      RegisterProperty(kTestInterface3, kPathPropName, &path_prop_);
+      RegisterProperty(kTestInterface3, kStringListPropName, &stringlist_prop_);
+      RegisterProperty(kTestInterface3, kPathListPropName, &pathlist_prop_);
+      RegisterProperty(kTestInterface3, kUint8ListPropName, &uint8list_prop_);
+    }
+    virtual ~Properties() {}
+
+    void CallHandleGetAll(
+        dbus::MethodCall* method_call,
+        dbus::ExportedObject::ResponseSender response_sender) {
+      HandleGetAll(method_call, response_sender);
+    }
+
+    void CallHandleGet(dbus::MethodCall* method_call,
+                       dbus::ExportedObject::ResponseSender response_sender) {
+      HandleGet(method_call, response_sender);
+    }
+
+    void CallHandleSet(dbus::MethodCall* method_call,
+                       dbus::ExportedObject::ResponseSender response_sender) {
+      HandleSet(method_call, response_sender);
+    }
+
+    MOCK_METHOD3(PropertyUpdated, void(const std::string&, const std::string&,
+                                       const ExportedPropertyBase*));
+
+   private:
+    virtual void HandlePropertyUpdated(
+        const std::string& interface,
+        const std::string& name,
+        const ExportedPropertyBase* property) override {
+      PropertyUpdated(interface, name, property);
+    }
+  };
+
+  void StoreResponse(scoped_ptr<dbus::Response> method_response) {
+    last_response_.reset(method_response.release());
+  }
+
+  void AssertGetAllReturnsError(dbus::MethodCall* method_call) {
+    auto response_sender = base::Bind(&ExportedPropertySetTest::StoreResponse,
+                                      base::Unretained(this));
+    method_call->SetSerial(123);
+    p_.CallHandleGetAll(method_call, response_sender);
+    ASSERT_NE(dynamic_cast<dbus::ErrorResponse*>(last_response_.get()),
+              nullptr);
+  }
+
+  void AssertGetReturnsError(dbus::MethodCall* method_call) {
+    auto response_sender = base::Bind(&ExportedPropertySetTest::StoreResponse,
+                                      base::Unretained(this));
+    method_call->SetSerial(123);
+    p_.CallHandleGet(method_call, response_sender);
+    ASSERT_NE(dynamic_cast<dbus::ErrorResponse*>(last_response_.get()),
+              nullptr);
+  }
+
+  scoped_ptr<dbus::Response> GetPropertyOnInterface(
+      const std::string& interface_name, const std::string& property_name) {
+    dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                                 dbus::kPropertiesGet);
+    method_call.SetSerial(123);
+    dbus::MessageWriter writer(&method_call);
+    writer.AppendString(interface_name);
+    writer.AppendString(property_name);
+    auto response_sender = base::Bind(&ExportedPropertySetTest::StoreResponse,
+                                      base::Unretained(this));
+    p_.CallHandleGet(&method_call, response_sender);
+    return last_response_.Pass();
+  }
+
+  scoped_ptr<dbus::Response> last_response_;
+  Properties p_;
+};
+
+TEST_F(ExportedPropertySetTest, UpdateNotifications) {
+  ::testing::InSequence dummy;
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface1, kBoolPropName,
+                                  &p_.bool_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface1, kUint8PropName,
+                                  &p_.uint8_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface1, kInt16PropName,
+                                  &p_.int16_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface2, kUint16PropName,
+                                  &p_.uint16_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface2, kInt32PropName,
+                                  &p_.int32_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kUint32PropName,
+                                  &p_.uint32_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kInt64PropName,
+                                  &p_.int64_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kUint64PropName,
+                                  &p_.uint64_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kDoublePropName,
+                                  &p_.double_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kStringPropName,
+                                  &p_.string_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kPathPropName,
+                                  &p_.path_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kStringListPropName,
+                                  &p_.stringlist_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kPathListPropName,
+                                  &p_.pathlist_prop_));
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface3, kUint8ListPropName,
+                                  &p_.uint8list_prop_));
+  p_.bool_prop_.SetValue(true);
+  p_.uint8_prop_.SetValue(1);
+  p_.int16_prop_.SetValue(1);
+  p_.uint16_prop_.SetValue(1);
+  p_.int32_prop_.SetValue(1);
+  p_.uint32_prop_.SetValue(1);
+  p_.int64_prop_.SetValue(1);
+  p_.uint64_prop_.SetValue(1);
+  p_.double_prop_.SetValue(1.0);
+  p_.string_prop_.SetValue(kTestString);
+  p_.path_prop_.SetValue(kTestObjectPathUpdate);
+  p_.stringlist_prop_.SetValue({kTestString});
+  p_.pathlist_prop_.SetValue({kTestObjectPathUpdate});
+  p_.uint8list_prop_.SetValue({1});
+}
+
+TEST_F(ExportedPropertySetTest, UpdateToSameValue) {
+  EXPECT_CALL(p_, PropertyUpdated(kTestInterface1, kBoolPropName,
+                                  &p_.bool_prop_)).Times(1);
+  p_.bool_prop_.SetValue(true);
+  p_.bool_prop_.SetValue(true);
+}
+
+TEST_F(ExportedPropertySetTest, GetAllNoArgs) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGetAll);
+  AssertGetAllReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetAllInvalidInterface) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGetAll);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString("org.chromium.BadInterface");
+  AssertGetAllReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetAllExtraArgs) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGetAll);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(kTestInterface1);
+  writer.AppendString(kTestInterface1);
+  AssertGetAllReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetAllCorrectness) {
+  dbus::MethodCall method_call(
+      dbus::kPropertiesInterface, dbus::kPropertiesGetAll);
+  method_call.SetSerial(123);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(kTestInterface2);
+  auto response_sender = base::Bind(&ExportedPropertySetTest::StoreResponse,
+                                    base::Unretained(this));
+  p_.CallHandleGetAll(&method_call, response_sender);
+  dbus::MessageReader response_reader(last_response_.get());
+  dbus::MessageReader dict_reader(nullptr);
+  dbus::MessageReader entry_reader(nullptr);
+  ASSERT_TRUE(response_reader.PopArray(&dict_reader));
+  ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
+  std::string property_name;
+  ASSERT_TRUE(entry_reader.PopString(&property_name));
+  uint16 value16;
+  int32 value32;
+  if (property_name.compare(kUint16PropName) == 0) {
+    ASSERT_TRUE(entry_reader.PopVariantOfUint16(&value16));
+    ASSERT_FALSE(entry_reader.HasMoreData());
+    ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
+    ASSERT_TRUE(entry_reader.PopString(&property_name));
+    ASSERT_EQ(property_name.compare(kInt32PropName), 0);
+    ASSERT_TRUE(entry_reader.PopVariantOfInt32(&value32));
+  } else {
+    ASSERT_EQ(property_name.compare(kInt32PropName), 0);
+    ASSERT_TRUE(entry_reader.PopVariantOfInt32(&value32));
+    ASSERT_FALSE(entry_reader.HasMoreData());
+    ASSERT_TRUE(dict_reader.PopDictEntry(&entry_reader));
+    ASSERT_TRUE(entry_reader.PopString(&property_name));
+    ASSERT_EQ(property_name.compare(kUint16PropName), 0);
+    ASSERT_TRUE(entry_reader.PopVariantOfUint16(&value16));
+  }
+  ASSERT_FALSE(entry_reader.HasMoreData());
+  ASSERT_FALSE(dict_reader.HasMoreData());
+  ASSERT_FALSE(response_reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetNoArgs) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGet);
+  AssertGetReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetInvalidInterface) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGet);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString("org.chromium.BadInterface");
+  writer.AppendString(kInt16PropName);
+  AssertGetReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetBadPropertyName) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGet);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(kTestInterface1);
+  writer.AppendString("IAmNotAProperty");
+  AssertGetReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetPropIfMismatch) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGet);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(kTestInterface1);
+  writer.AppendString(kStringPropName);
+  AssertGetReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetNoPropertyName) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGet);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(kTestInterface1);
+  AssertGetReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetExtraArgs) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesGet);
+  dbus::MessageWriter writer(&method_call);
+  writer.AppendString(kTestInterface1);
+  writer.AppendString(kBoolPropName);
+  writer.AppendString("Extra param");
+  AssertGetReturnsError(&method_call);
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithBool) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface1, kBoolPropName);
+  dbus::MessageReader reader(response.get());
+  bool value;
+  ASSERT_TRUE(reader.PopVariantOfBool(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithUint8) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface1, kUint8PropName);
+  dbus::MessageReader reader(response.get());
+  uint8 value;
+  ASSERT_TRUE(reader.PopVariantOfByte(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithInt16) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface1, kInt16PropName);
+  dbus::MessageReader reader(response.get());
+  int16 value;
+  ASSERT_TRUE(reader.PopVariantOfInt16(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithUint16) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface2, kUint16PropName);
+  dbus::MessageReader reader(response.get());
+  uint16 value;
+  ASSERT_TRUE(reader.PopVariantOfUint16(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithInt32) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface2, kInt32PropName);
+  dbus::MessageReader reader(response.get());
+  int32 value;
+  ASSERT_TRUE(reader.PopVariantOfInt32(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithUint32) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kUint32PropName);
+  dbus::MessageReader reader(response.get());
+  uint32 value;
+  ASSERT_TRUE(reader.PopVariantOfUint32(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithInt64) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kInt64PropName);
+  dbus::MessageReader reader(response.get());
+  int64 value;
+  ASSERT_TRUE(reader.PopVariantOfInt64(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithUint64) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kUint64PropName);
+  dbus::MessageReader reader(response.get());
+  uint64 value;
+  ASSERT_TRUE(reader.PopVariantOfUint64(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithDouble) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kDoublePropName);
+  dbus::MessageReader reader(response.get());
+  double value;
+  ASSERT_TRUE(reader.PopVariantOfDouble(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithString) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kStringPropName);
+  dbus::MessageReader reader(response.get());
+  std::string value;
+  ASSERT_TRUE(reader.PopVariantOfString(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithPath) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kPathPropName);
+  dbus::MessageReader reader(response.get());
+  dbus::ObjectPath value;
+  ASSERT_TRUE(reader.PopVariantOfObjectPath(&value));
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithStringList) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kStringListPropName);
+  dbus::MessageReader reader(response.get());
+  dbus::MessageReader variant_reader(nullptr);
+  std::vector<std::string> value;
+  ASSERT_TRUE(reader.PopVariant(&variant_reader));
+  ASSERT_TRUE(variant_reader.PopArrayOfStrings(&value));
+  ASSERT_FALSE(variant_reader.HasMoreData());
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithPathList) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kPathListPropName);
+  dbus::MessageReader reader(response.get());
+  dbus::MessageReader variant_reader(nullptr);
+  std::vector<dbus::ObjectPath> value;
+  ASSERT_TRUE(reader.PopVariant(&variant_reader));
+  ASSERT_TRUE(variant_reader.PopArrayOfObjectPaths(&value));
+  ASSERT_FALSE(variant_reader.HasMoreData());
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, GetWorksWithUint8List) {
+  scoped_ptr<dbus::Response> response = GetPropertyOnInterface(
+      kTestInterface3, kPathListPropName);
+  dbus::MessageReader reader(response.get());
+  dbus::MessageReader variant_reader(nullptr);
+  uint8* buffer;
+  size_t buffer_len;
+  ASSERT_TRUE(reader.PopVariant(&variant_reader));
+  // |buffer| remains under the control of the MessageReader.
+  ASSERT_TRUE(variant_reader.PopArrayOfBytes(&buffer, &buffer_len));
+  ASSERT_FALSE(variant_reader.HasMoreData());
+  ASSERT_FALSE(reader.HasMoreData());
+}
+
+TEST_F(ExportedPropertySetTest, SetFailsGracefully) {
+  dbus::MethodCall method_call(dbus::kPropertiesInterface,
+                               dbus::kPropertiesSet);
+  method_call.SetSerial(123);
+  auto response_sender = base::Bind(&ExportedPropertySetTest::StoreResponse,
+                                    base::Unretained(this));
+  p_.CallHandleSet(&method_call, response_sender);
+  ASSERT_TRUE(
+      dynamic_cast<dbus::ErrorResponse*>(last_response_.get()) != nullptr);
+}
+
+}  // namespace dbus_utils
+
+}  // namespace buffet
diff --git a/buffet/http_request.cc b/buffet/http_request.cc
new file mode 100644
index 0000000..5d82473
--- /dev/null
+++ b/buffet/http_request.cc
@@ -0,0 +1,260 @@
+// 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_request.h"
+
+#include "buffet/http_transport_curl.h"
+#include "buffet/mime_utils.h"
+
+using namespace chromeos;
+using namespace chromeos::http;
+
+// request_type
+const char request_type::kOptions[]               = "OPTIONS";
+const char request_type::kGet[]                   = "GET";
+const char request_type::kHead[]                  = "HEAD";
+const char request_type::kPost[]                  = "POST";
+const char request_type::kPut[]                   = "PUT";
+const char request_type::kPatch[]                 = "PATCH";
+const char request_type::kDelete[]                = "DELETE";
+const char request_type::kTrace[]                 = "TRACE";
+const char request_type::kConnect[]               = "CONNECT";
+const char request_type::kCopy[]                  = "COPY";
+const char request_type::kMove[]                  = "MOVE";
+
+// request_header
+const char request_header::kAccept[]              = "Accept";
+const char request_header::kAcceptCharset[]       = "Accept-Charset";
+const char request_header::kAcceptEncoding[]      = "Accept-Encoding";
+const char request_header::kAcceptLanguage[]      = "Accept-Language";
+const char request_header::kAllow[]               = "Allow";
+const char request_header::kAuthorization[]       = "Authorization";
+const char request_header::kCacheControl[]        = "Cache-Control";
+const char request_header::kConnection[]          = "Connection";
+const char request_header::kContentEncoding[]     = "Content-Encoding";
+const char request_header::kContentLanguage[]     = "Content-Language";
+const char request_header::kContentLength[]       = "Content-Length";
+const char request_header::kContentLocation[]     = "Content-Location";
+const char request_header::kContentMd5[]          = "Content-MD5";
+const char request_header::kContentRange[]        = "Content-Range";
+const char request_header::kContentType[]         = "Content-Type";
+const char request_header::kCookie[]              = "Cookie";
+const char request_header::kDate[]                = "Date";
+const char request_header::kExpect[]              = "Expect";
+const char request_header::kExpires[]             = "Expires";
+const char request_header::kFrom[]                = "From";
+const char request_header::kHost[]                = "Host";
+const char request_header::kIfMatch[]             = "If-Match";
+const char request_header::kIfModifiedSince[]     = "If-Modified-Since";
+const char request_header::kIfNoneMatch[]         = "If-None-Match";
+const char request_header::kIfRange[]             = "If-Range";
+const char request_header::kIfUnmodifiedSince[]   = "If-Unmodified-Since";
+const char request_header::kLastModified[]        = "Last-Modified";
+const char request_header::kMaxForwards[]         = "Max-Forwards";
+const char request_header::kPragma[]              = "Pragma";
+const char request_header::kProxyAuthorization[]  = "Proxy-Authorization";
+const char request_header::kRange[]               = "Range";
+const char request_header::kReferer[]             = "Referer";
+const char request_header::kTE[]                  = "TE";
+const char request_header::kTrailer[]             = "Trailer";
+const char request_header::kTransferEncoding[]    = "Transfer-Encoding";
+const char request_header::kUpgrade[]             = "Upgrade";
+const char request_header::kUserAgent[]           = "User-Agent";
+const char request_header::kVia[]                 = "Via";
+const char request_header::kWarning[]             = "Warning";
+
+// response_header
+const char response_header::kAcceptRanges[]       = "Accept-Ranges";
+const char response_header::kAge[]                = "Age";
+const char response_header::kAllow[]              = "Allow";
+const char response_header::kCacheControl[]       = "Cache-Control";
+const char response_header::kConnection[]         = "Connection";
+const char response_header::kContentEncoding[]    = "Content-Encoding";
+const char response_header::kContentLanguage[]    = "Content-Language";
+const char response_header::kContentLength[]      = "Content-Length";
+const char response_header::kContentLocation[]    = "Content-Location";
+const char response_header::kContentMd5[]         = "Content-MD5";
+const char response_header::kContentRange[]       = "Content-Range";
+const char response_header::kContentType[]        = "Content-Type";
+const char response_header::kDate[]               = "Date";
+const char response_header::kETag[]               = "ETag";
+const char response_header::kExpires[]            = "Expires";
+const char response_header::kLastModified[]       = "Last-Modified";
+const char response_header::kLocation[]           = "Location";
+const char response_header::kPragma[]             = "Pragma";
+const char response_header::kProxyAuthenticate[]  = "Proxy-Authenticate";
+const char response_header::kRetryAfter[]         = "Retry-After";
+const char response_header::kServer[]             = "Server";
+const char response_header::kSetCookie[]          = "Set-Cookie";
+const char response_header::kTrailer[]            = "Trailer";
+const char response_header::kTransferEncoding[]   = "Transfer-Encoding";
+const char response_header::kUpgrade[]            = "Upgrade";
+const char response_header::kVary[]               = "Vary";
+const char response_header::kVia[]                = "Via";
+const char response_header::kWarning[]            = "Warning";
+const char response_header::kWwwAuthenticate[]    = "WWW-Authenticate";
+
+//**************************************************************************
+//********************** Request Class **********************
+//**************************************************************************
+Request::Request(std::string const& url, char const* method) :
+  transport_(new curl::Transport(url, method)) {
+}
+
+Request::Request(std::string const& url) :
+  transport_(new curl::Transport(url, nullptr)) {
+}
+
+Request::Request(std::shared_ptr<TransportInterface> transport) :
+  transport_(transport) {
+}
+
+void Request::AddRange(int64_t bytes) {
+  if (transport_)
+    transport_->AddRange(bytes);
+}
+
+void Request::AddRange(uint64_t from_byte, uint64_t to_byte) {
+  if (transport_)
+    transport_->AddRange(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>();
+}
+
+void Request::SetAccept(char const* accept_mime_types) {
+  if (transport_)
+    transport_->SetAccept(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();
+}
+
+void Request::SetContentType(char const* contentType) {
+  if (transport_)
+    transport_->SetContentType(contentType);
+}
+
+std::string Request::GetContentType() const {
+  return transport_ ? transport_->GetContentType() : std::string();
+}
+
+void Request::AddHeader(char const* header, char const* value) {
+  if (transport_)
+    transport_->AddHeader(header, value);
+}
+
+bool Request::AddRequestBody(void const* data, size_t size) {
+  return transport_ && transport_->AddRequestBody(data, size);
+}
+
+void Request::SetMethod(char const* method) {
+  if (transport_)
+    transport_->SetMethod(method);
+}
+
+std::string Request::GetMethod() const {
+  return transport_ ? transport_->GetMethod() : std::string();
+}
+
+void Request::SetReferer(char const* referer) {
+  if (transport_)
+    transport_->SetReferer(referer);
+}
+
+std::string Request::GetReferer() const {
+  return transport_ ? transport_->GetReferer() : std::string();
+}
+
+void Request::SetUserAgent(char const* user_agent) {
+  if (transport_)
+    transport_->SetUserAgent(user_agent);
+}
+
+std::string Request::GetUserAgent() const {
+  return transport_ ? transport_->GetUserAgent() : std::string();
+}
+
+std::string Request::GetErrorMessage() const {
+  if (transport_ &&
+      transport_->GetStage() == TransportInterface::Stage::failed) {
+    return transport_->GetErrorMessage();
+  }
+
+  return std::string();
+}
+
+//**************************************************************************
+//********************** Response Class **********************
+//**************************************************************************
+Response::Response(std::shared_ptr<TransportInterface> transport) :
+    transport_(transport) {
+}
+
+bool Response::IsSuccessful() const {
+  if (transport_ &&
+      transport_->GetStage() == TransportInterface::Stage::response_received) {
+    int code = GetStatusCode();
+    return code >= status_code::Continue && code < status_code::BadRequest;
+  }
+  return false;
+}
+
+int Response::GetStatusCode() const {
+  if (!transport_)
+    return -1;
+
+  return transport_->GetResponseStatusCode();
+}
+
+std::string Response::GetStatusText() const {
+  if (!transport_)
+    return std::string();
+
+  return transport_->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>();
+}
+
+std::string Response::GetDataAsString() const {
+  if (transport_) {
+    auto data = transport_->GetResponseData();
+    char const* data_buf = reinterpret_cast<char const*>(data.data());
+    return std::string(data_buf, data_buf + data.size());
+  }
+
+  return std::string();
+}
+
+std::string Response::GetHeader(char const* header_name) const {
+  if (transport_)
+    return transport_->GetResponseHeader(header_name);
+
+  return std::string();
+}
+
diff --git a/buffet/http_request.h b/buffet/http_request.h
new file mode 100644
index 0000000..acdab89
--- /dev/null
+++ b/buffet/http_request.h
@@ -0,0 +1,317 @@
+// 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_REQUEST_H_
+#define BUFFET_HTTP_REQUEST_H_
+
+#include <vector>
+#include <memory>
+#include <string>
+#include <base/basictypes.h>
+
+#include "buffet/transport_interface.h"
+
+namespace chromeos {
+namespace http {
+
+// HTTP request verbs
+namespace request_type {
+  extern const char kOptions[];
+  extern const char kGet[];
+  extern const char kHead[];
+  extern const char kPost[];
+  extern const char kPut[];
+  extern const char kPatch[];  // Not a standard HTTP/1.1 request method
+  extern const char kDelete[];
+  extern const char kTrace[];
+  extern const char kConnect[];
+  extern const char kCopy[];   // Not a standard HTTP/1.1 request method
+  extern const char kMove[];   // Not a standard HTTP/1.1 request method
+} // namespace request_type
+
+// HTTP request header names
+namespace request_header {
+  extern const char kAccept[];
+  extern const char kAcceptCharset[];
+  extern const char kAcceptEncoding[];
+  extern const char kAcceptLanguage[];
+  extern const char kAllow[];
+  extern const char kAuthorization[];
+  extern const char kCacheControl[];
+  extern const char kConnection[];
+  extern const char kContentEncoding[];
+  extern const char kContentLanguage[];
+  extern const char kContentLength[];
+  extern const char kContentLocation[];
+  extern const char kContentMd5[];
+  extern const char kContentRange[];
+  extern const char kContentType[];
+  extern const char kCookie[];
+  extern const char kDate[];
+  extern const char kExpect[];
+  extern const char kExpires[];
+  extern const char kFrom[];
+  extern const char kHost[];
+  extern const char kIfMatch[];
+  extern const char kIfModifiedSince[];
+  extern const char kIfNoneMatch[];
+  extern const char kIfRange[];
+  extern const char kIfUnmodifiedSince[];
+  extern const char kLastModified[];
+  extern const char kMaxForwards[];
+  extern const char kPragma[];
+  extern const char kProxyAuthorization[];
+  extern const char kRange[];
+  extern const char kReferer[];
+  extern const char kTE[];
+  extern const char kTrailer[];
+  extern const char kTransferEncoding[];
+  extern const char kUpgrade[];
+  extern const char kUserAgent[];
+  extern const char kVia[];
+  extern const char kWarning[];
+} // namespace request_header
+
+// HTTP response header names
+namespace response_header {
+  extern const char kAcceptRanges[];
+  extern const char kAge[];
+  extern const char kAllow[];
+  extern const char kCacheControl[];
+  extern const char kConnection[];
+  extern const char kContentEncoding[];
+  extern const char kContentLanguage[];
+  extern const char kContentLength[];
+  extern const char kContentLocation[];
+  extern const char kContentMd5[];
+  extern const char kContentRange[];
+  extern const char kContentType[];
+  extern const char kDate[];
+  extern const char kETag[];
+  extern const char kExpires[];
+  extern const char kLastModified[];
+  extern const char kLocation[];
+  extern const char kPragma[];
+  extern const char kProxyAuthenticate[];
+  extern const char kRetryAfter[];
+  extern const char kServer[];
+  extern const char kSetCookie[];
+  extern const char kTrailer[];
+  extern const char kTransferEncoding[];
+  extern const char kUpgrade[];
+  extern const char kVary[];
+  extern const char kVia[];
+  extern const char kWarning[];
+  extern const char kWwwAuthenticate[];
+} // namespace response_header
+
+// HTTP request status (error) codes
+namespace status_code {
+  // OK to continue with request
+  static const int Continue = 100;
+  // Server has switched protocols in upgrade header
+  static const int SwitchProtocols = 101;
+
+  // Request completed
+  static const int Ok = 200;
+  // Object created, reason = new URI
+  static const int Created = 201;
+  // Async completion (TBS)
+  static const int Accepted = 202;
+  // Partial completion
+  static const int Partial = 203;
+  // No info to return
+  static const int NoContent = 204;
+  // Request completed, but clear form
+  static const int ResetContent = 205;
+  // Partial GET furfilled
+  static const int PartialContent = 206;
+
+  // Server couldn't decide what to return
+  static const int Ambiguous = 300;
+  // Object permanently moved
+  static const int Moved = 301;
+  // Object temporarily moved
+  static const int Redirect = 302;
+  // Redirection w/ new access method
+  static const int RedirectMethod = 303;
+  // If-Modified-Since was not modified
+  static const int NotModified = 304;
+  // Redirection to proxy, location header specifies proxy to use
+  static const int UseProxy = 305;
+  // HTTP/1.1: keep same verb
+  static const int RedirectKeepVerb = 307;
+
+  // Invalid syntax
+  static const int BadRequest = 400;
+  // Access denied
+  static const int Denied = 401;
+  // Payment required
+  static const int PaymentRequired = 402;
+  // Request forbidden
+  static const int Forbidden = 403;
+  // Object not found
+  static const int NotFound = 404;
+  // Method is not allowed
+  static const int BadMethod = 405;
+  // No response acceptable to client found
+  static const int NoneAcceptable = 406;
+  // Proxy authentication required
+  static const int ProxyAuthRequired = 407;
+  // Server timed out waiting for request
+  static const int RequestTimeout = 408;
+  // User should resubmit with more info
+  static const int Conflict = 409;
+  // The resource is no longer available
+  static const int Gone = 410;
+  // The server refused to accept request w/o a length
+  static const int LengthRequired = 411;
+  // Precondition given in request failed
+  static const int PrecondionFailed = 412;
+  // Request entity was too large
+  static const int RequestTooLarge = 413;
+  // Request URI too long
+  static const int UriTooLong = 414;
+  // Unsupported media type
+  static const int UnsupportedMedia = 415;
+  // Retry after doing the appropriate action.
+  static const int RetryWith = 449;
+
+  // Internal server error
+  static const int InternalServerError = 500;
+  // Request not supported
+  static const int NotSupported = 501;
+  // Error response received from gateway
+  static const int BadGateway = 502;
+  // Temporarily overloaded
+  static const int ServiceUnavailable = 503;
+  // Timed out waiting for gateway
+  static const int GatewayTimeout = 504;
+  // HTTP version not supported
+  static const int VersionNotSupported = 505;
+} // namespace status_code
+
+class Response; // Just a forward-declarartion
+
+///////////////////////////////////////////////////////////////////////////////
+// Request class is the main object used to set up and initiate an HTTP
+// communication session. It is used to specify the HTTP request method,
+// request URL and many optional parameters (such as HTTP headers, user agent,
+// referer URL and so on.
+//
+// Once everything is setup, GetResponse() method is used to send the request
+// and obtain the server response. The returned Response onject can be
+// used to inspect the response code, HTTP headers and/or response body.
+///////////////////////////////////////////////////////////////////////////////
+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(std::string const& url, char const* method);
+  Request(std::string const& url);
+
+  // Custom constructor that allows non-default implementations
+  // of TransportInterface to be used.
+  Request(std::shared_ptr<TransportInterface> transport);
+
+  // Gets/Sets "Accept:" header value. The default value is "*/*" if not set.
+  void SetAccept(char const* accept_mime_types);
+  std::string GetAccept() const;
+
+  // Gets/Sets "Content-Type:" header value
+  void SetContentType(char const* content_type);
+  std::string GetContentType() const;
+
+  // Adds additional HTTP request header
+  void AddHeader(char const* header, char const* value);
+
+  // Removes HTTP request header
+  void RemoveHeader(char const* header);
+
+  // Adds a request body. This is not to be used with GET method
+  bool AddRequestBody(void const* data, size_t size);
+
+  // Makes a request for a subrange of data. Specifies a partial range with
+  // either from beginning of the data to the specified offset (if |bytes| is
+  // negative) or from the specified offset to the end of data (if |bytes| is
+  // positive).
+  // All individual ranges will be sent as part of "Range:" HTTP request header.
+  void AddRange(int64_t bytes);
+
+  // Makes a request for a subrange of data. Specifies a full range with
+  // start and end bytes from the beginning of the requested data.
+  // 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(char const* method);
+  std::string GetMethod() const;
+
+  // Returns the request URL
+  std::string GetRequestURL() const;
+
+  // Gets/Sets a request referer URL (sent as "Referer:" request header).
+  void SetReferer(char const* referer);
+  std::string GetReferer() const;
+
+  // Gets/Sets a user agent string (sent as "User-Agent:" request header).
+  void SetUserAgent(char const* user_agent);
+  std::string GetUserAgent() const;
+
+  // Sends the request to the server and returns the response object.
+  // In case the server couldn't be reached for whatever reason, returns
+  // empty unique_ptr (null). Calling GetErrorMessage() provides additional
+  // information in such as case.
+  std::unique_ptr<Response> GetResponse();
+
+  // If the request failed before reaching the server, returns additional
+  // information about the error occurred.
+  std::string GetErrorMessage() const;
+
+ private:
+  std::shared_ptr<TransportInterface> transport_;
+  DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+///////////////////////////////////////////////////////////////////////////////
+// Response class is returned from Request::GetResponse() and is a way
+// to get to response status, error codes, response HTTP headers and response
+// data (body) if available.
+///////////////////////////////////////////////////////////////////////////////
+class Response {
+ public:
+  Response(std::shared_ptr<TransportInterface> transport);
+
+  // Returns true if server returned a success code (status code below 400).
+  bool IsSuccessful() const;
+
+  // Returns the HTTP status code (e.g. 200 for success)
+  int GetStatusCode() const;
+
+  // Returns the status text (e.g. for error 403 it could be "NOT AUTHORIZED").
+  std::string GetStatusText() const;
+
+  // Returns the content type of the response data.
+  std::string GetContentType() const;
+
+  // Returns response data as a byte array
+  std::vector<unsigned char> GetData() const;
+
+  // Returns response data as a string
+  std::string GetDataAsString() const;
+
+  // Returns a value of a given response HTTP header.
+  std::string GetHeader(char const* header_name) const;
+
+ private:
+  std::shared_ptr<TransportInterface> transport_;
+  DISALLOW_COPY_AND_ASSIGN(Response);
+};
+
+} // namespace http
+} // namespace chromeos
+
+#endif // BUFFET_HTTP_REQUEST_H_
diff --git a/buffet/http_transport_curl.cc b/buffet/http_transport_curl.cc
new file mode 100644
index 0000000..47090e9
--- /dev/null
+++ b/buffet/http_transport_curl.cc
@@ -0,0 +1,259 @@
+// 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_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"
+
+using namespace chromeos;
+using namespace chromeos::http::curl;
+
+Transport::Transport(std::string const& url, char const* method) :
+    request_url_(url),
+    method_(method ? method : request_type::kGet) {
+  stage_ = Stage::initialized;
+}
+
+Transport::~Transport() {
+  Close();
+}
+
+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_;
+}
+
+std::vector<std::pair<std::string, std::string>> Transport::GetHeaders() const {
+  auto 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(char const* header, char const* value) {
+  headers_[header] = value;
+}
+
+void Transport::RemoveHeader(char const* header) {
+  AddHeader(header, "");
+}
+
+bool Transport::AddRequestBody(void const* data, size_t size) {
+  if (size == 0)
+    return true;
+
+  if (data == nullptr) {
+    LOG(ERROR) << "Invalid request body data pointer";
+    return false;
+  }
+
+  unsigned char const* data_ptr = reinterpret_cast<unsigned char const*>(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_) {
+    LOG(ERROR) << "Failed to initialize CURL";
+    return false;
+  }
+
+  curl_easy_setopt(curl_handle_, CURLOPT_URL, request_url_.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());
+  }
+
+  // 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::kPost) {
+    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()));
+  } 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);
+    }
+  }
+
+  // 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);
+  }
+
+  // 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);
+      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);
+  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(char const* headerName) const {
+  auto p = headers_.find(headerName);
+  return p != headers_.end() ? p->second : std::string();
+}
+
+
+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));
+  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;
+}
diff --git a/buffet/http_transport_curl.h b/buffet/http_transport_curl.h
new file mode 100644
index 0000000..ab2ea2b
--- /dev/null
+++ b/buffet/http_transport_curl.h
@@ -0,0 +1,166 @@
+// 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_CURL_H_
+#define BUFFET_HTTP_TRANSPORT_CURL_H_
+
+#include <map>
+#include <curl/curl.h>
+
+#include "buffet/http_request.h"
+
+namespace chromeos {
+namespace http {
+namespace curl {
+
+///////////////////////////////////////////////////////////////////////////////
+// A particular implementation of TransportInterface that uses libcurl for
+// HTTP communications. This class (as TransportInterface interface)
+// is used by http::Request and http::Response classes to provide HTTP
+// functionality to the clients.
+///////////////////////////////////////////////////////////////////////////////
+class Transport : public TransportInterface {
+ 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(std::string const& url, char const* method);
+  ~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(char const* 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(char const* content_type) override {
+    content_type_ = content_type;
+  }
+  virtual std::string GetContentType() const override { return content_type_; }
+
+  // Implementation of Request::AddHeader.
+  virtual void AddHeader(char const* header, char const* value) override;
+
+  // Implementation of Request::RemoveHeader.
+  virtual void RemoveHeader(char const* header) override;
+
+  // Implementation of Request::AddRequestBody.
+  virtual bool AddRequestBody(void const* data, size_t size) override;
+
+  // Implementation of Request::SetMethod/Request::GetMethod.
+  virtual void SetMethod(char const* method) override { method_ = method; }
+  virtual std::string GetMethod() const override { return method_; }
+
+  // Implementation of Request::SetReferer/Request::GetReferer.
+  virtual void SetReferer(char const* referer) override { referer_ = referer; }
+  virtual std::string GetReferer() const override { return referer_; }
+
+  // Implementation of Request::SetUserAgent/Request::GetUserAgent.
+  virtual void SetUserAgent(char const* 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(char const* header_name) const override;
+
+  // Implementation of Response::GetData.
+  virtual std::vector<unsigned char> const& GetResponseData() const override {
+    return response_data_;
+  }
+
+  // Implementation of Response::GetErrorMessage.
+  virtual std::string GetErrorMessage() const override { return error_; }
+
+  // Closes the connection and frees up internal data
+  virtual void Close() override;
+
+ private:
+  std::vector<std::pair<std::string, std::string>> 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);
+};
+
+} // namespace curl
+} // namespace http
+} // namespace chromeos
+
+#endif // BUFFET_HTTP_TRANSPORT_CURL_H_
diff --git a/buffet/http_utils.cc b/buffet/http_utils.cc
new file mode 100644
index 0000000..a921386
--- /dev/null
+++ b/buffet/http_utils.cc
@@ -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.
+
+#include "buffet/http_utils.h"
+
+#include <algorithm>
+#include <string.h>
+#include <base/values.h>
+#include <base/json/json_reader.h>
+#include <base/json/json_writer.h>
+
+#include "mime_utils.h"
+
+namespace chromeos {
+namespace http {
+
+std::unique_ptr<Response> Get(char const* url) {
+  Request request(url);
+  return request.GetResponse();
+}
+
+std::string GetAsString(char const* url) {
+  auto resp = Get(url);
+  return resp ? resp->GetDataAsString() : std::string();
+}
+
+std::unique_ptr<Response> Head(char const* url) {
+  Request request(url, request_type::kHead);
+  return request.GetResponse();
+}
+
+std::unique_ptr<Response> PostText(char const* url,
+                                   char const* data,
+                                   char const* mime_type) {
+  if (mime_type == nullptr) {
+    mime_type = chromeos::mime::application::kWwwFormUrlEncoded;
+  }
+
+  return PostBinary(url, data, strlen(data), mime_type);
+}
+
+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;
+  }
+
+  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<base::Value> ParseJsonResponse(Response const* response,
+                                               std::string* error_message) {
+  std::unique_ptr<base::Value> value;
+  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));
+      }
+      else if (error_message) {
+        *error_message = "Unexpected response content type: " + content_type;
+      }
+    }
+  } else if (error_message) {
+    *error_message = "NULL response.";
+  }
+  return value;
+}
+
+} // namespace http
+} // namespace chromeos
diff --git a/buffet/http_utils.h b/buffet/http_utils.h
new file mode 100644
index 0000000..0fff847
--- /dev/null
+++ b/buffet/http_utils.h
@@ -0,0 +1,79 @@
+// 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_UTILS_H_
+#define BUFFET_HTTP_UTILS_H_
+
+#include "buffet/http_request.h"
+
+namespace base { class Value; }
+
+namespace chromeos {
+namespace http {
+
+////////////////////////////////////////////////////////////////////////////////
+// The following are simple utility helper functions for common HTTP operations
+// that use http::Request object behind the scenes and set it up accordingly.
+//
+// For more advanced functionality you need to use Request/Response objects
+// directly.
+////////////////////////////////////////////////////////////////////////////////
+
+// Performs a simple GET request and returns the data as a string.
+std::string GetAsString(char 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);
+
+// 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);
+
+// 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,
+                                     void const* data,
+                                     size_t data_size,
+                                     char const* mime_type);
+
+inline std::unique_ptr<Response> PostBinary(char const* url,
+                                            void const* data,
+                                            size_t data_size) {
+  return PostBinary(url, data, data_size, nullptr);
+}
+
+// Performs a POST request with text 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/x-www-form-urlencoded"
+// is assumed
+std::unique_ptr<Response> PostText(char const* url,
+                                   char const* data,
+                                   char const* mime_type);
+
+inline std::unique_ptr<Response> PostText(char const* url, char const* data) {
+  return PostText(url, data, nullptr);
+}
+
+// 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);
+
+// 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);
+
+} // namespace http
+} // namespace chromeos
+
+#endif // BUFFET_HTTP_UTILS_H_
diff --git a/buffet/main.cc b/buffet/main.cc
new file mode 100644
index 0000000..79843ab
--- /dev/null
+++ b/buffet/main.cc
@@ -0,0 +1,91 @@
+// 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 <string>
+
+#include <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/file_util.h>
+#include <base/files/file_path.h>
+#include <base/logging.h>
+#include <base/message_loop/message_loop.h>
+#include <base/strings/string_util.h>
+#include <base/strings/stringprintf.h>
+#include <gflags/gflags.h>
+
+#include "buffet/dbus_manager.h"
+#include "buffet/manager.h"
+
+DEFINE_string(logsroot, "/var/log", "Root directory for buffet logs.");
+
+namespace {
+
+// Returns |utime| as a string
+std::string GetTimeAsString(time_t utime) {
+  struct tm tm;
+  CHECK_EQ(localtime_r(&utime, &tm), &tm);
+  char str[16];
+  CHECK_EQ(strftime(str, sizeof(str), "%Y%m%d-%H%M%S", &tm), 15UL);
+  return std::string(str);
+}
+
+// Sets up a symlink to point to log file.
+void SetupLogSymlink(const std::string& symlink_path,
+                     const std::string& log_path) {
+  base::DeleteFile(base::FilePath(symlink_path), true);
+  if (symlink(log_path.c_str(), symlink_path.c_str()) == -1) {
+    LOG(ERROR) << "Unable to create symlink " << symlink_path
+               << " pointing at " << log_path;
+  }
+}
+
+// Creates new log file based on timestamp in |logs_root|/buffet.
+std::string SetupLogFile(const std::string& logs_root) {
+  const auto log_symlink = logs_root + "/buffet.log";
+  const auto logs_dir = logs_root + "/buffet";
+  const auto log_path =
+      base::StringPrintf("%s/buffet.%s",
+                         logs_dir.c_str(),
+                         GetTimeAsString(::time(NULL)).c_str());
+  mkdir(logs_dir.c_str(), 0755);
+  SetupLogSymlink(log_symlink, log_path);
+  return log_symlink;
+}
+
+// Sets up logging for buffet.
+void SetupLogging(const std::string& logs_root) {
+  const auto log_file = SetupLogFile(logs_root);
+  logging::LoggingSettings settings;
+  settings.logging_dest = logging::LOG_TO_ALL;
+  settings.log_file = log_file.c_str();
+  settings.lock_log = logging::DONT_LOCK_LOG_FILE;
+  settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
+  logging::InitLogging(settings);
+}
+
+}  // namespace
+
+int main(int argc, char* argv[]) {
+  // Parse the args and check for extra args.
+  CommandLine::Init(argc, argv);
+  google::ParseCommandLineFlags(&argc, &argv, true);
+  CHECK_EQ(argc, 1) << "Unexpected arguments. Try --help";
+
+  SetupLogging(FLAGS_logsroot);
+
+  base::AtExitManager at_exit_manager;
+  base::MessageLoopForIO message_loop;
+
+  // Initialize the dbus_manager.
+  buffet::DBusManager dbus_manager;
+  dbus_manager.Init();
+  {
+    // The Manager needs the dbus_manager to remain in scope for its lifetime.
+    buffet::Manager manager(&dbus_manager);
+    message_loop.Run();
+  }
+
+  dbus_manager.Finalize();
+  return 0;
+}
diff --git a/buffet/manager.cc b/buffet/manager.cc
new file mode 100644
index 0000000..4daa9fc
--- /dev/null
+++ b/buffet/manager.cc
@@ -0,0 +1,98 @@
+// 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/manager.h"
+
+#include <base/bind.h>
+#include <base/bind_helpers.h>
+
+#include "buffet/dbus_constants.h"
+#include "buffet/dbus_manager.h"
+#include "buffet/dbus_utils.h"
+
+using buffet::dbus_utils::GetBadArgsError;
+
+namespace buffet {
+
+Manager::Manager(DBusManager* dbus_manager) : dbus_manager_(dbus_manager) {
+  dbus::ExportedObject* exported_object = dbus_manager_->GetExportedObject(
+      dbus_constants::kManagerServicePath);
+  dbus_manager_->ExportDBusMethod(exported_object,
+                                  dbus_constants::kManagerInterface,
+                                  dbus_constants::kManagerRegisterDeviceMethod,
+                                  base::Bind(&Manager::HandleRegisterDevice,
+                                             base::Unretained(this)));
+  dbus_manager_->ExportDBusMethod(exported_object,
+                                  dbus_constants::kManagerInterface,
+                                  dbus_constants::kManagerUpdateStateMethod,
+                                  base::Bind(&Manager::HandleUpdateState,
+                                             base::Unretained(this)));
+}
+
+Manager::~Manager() {
+  // Unregister ourselves from the Bus.  This prevents the bus from calling
+  // our callbacks in between the Manager's death and the bus unregistering
+  // our exported object on shutdown.  Unretained makes no promises of memory
+  // management.
+  auto exported_object = dbus_manager_->GetExportedObject(
+      dbus_constants::kManagerServicePath);
+  exported_object->Unregister();
+}
+
+scoped_ptr<dbus::Response> Manager::HandleRegisterDevice(
+    dbus::MethodCall* method_call) {
+  // Read the parameters to the method.
+  dbus::MessageReader reader(method_call);
+  if (!reader.HasMoreData()) {
+    return GetBadArgsError(method_call, "No parameters to RegisterDevice");
+  }
+  std::string client_id, client_secret, api_key;
+  if (!reader.PopString(&client_id)) {
+    return GetBadArgsError(method_call, "Failed to read client_id");
+  }
+  if (!reader.PopString(&client_secret)) {
+    return GetBadArgsError(method_call, "Failed to read client_secret");
+  }
+  if (!reader.PopString(&api_key)) {
+    return GetBadArgsError(method_call, "Failed to read api_key");
+  }
+  if (reader.HasMoreData()) {
+    return GetBadArgsError(
+        method_call, "Too many parameters to RegisterDevice");
+  }
+
+  LOG(INFO) << "Received call to Manager.RegisterDevice()";
+  // TODO(wiley): Do something with these parameters to register the device.
+
+  // Send back our response.
+  scoped_ptr<dbus::Response> response(
+      dbus::Response::FromMethodCall(method_call));
+  dbus::MessageWriter writer(response.get());
+  writer.AppendString("<registration ticket id>");
+  return response.Pass();
+}
+
+scoped_ptr<dbus::Response> Manager::HandleUpdateState(
+    dbus::MethodCall *method_call) {
+  // Read the parameters to the method.
+  dbus::MessageReader reader(method_call);
+  if (!reader.HasMoreData()) {
+    return GetBadArgsError(method_call, "No parameters to UpdateState");
+  }
+  std::string json_state_fragment;
+  if (!reader.PopString(&json_state_fragment)) {
+    return GetBadArgsError(method_call, "Failed to read json_state_fragment");
+  }
+  if (reader.HasMoreData()) {
+    return GetBadArgsError(method_call, "Too many parameters to UpdateState");
+  }
+
+  LOG(INFO) << "Received call to Manager.UpdateState()";
+  // TODO(wiley): Do something with these parameters to update state.
+
+  // Send back our response.
+  return dbus::Response::FromMethodCall(method_call);
+}
+
+}  // namespace buffet
diff --git a/buffet/manager.h b/buffet/manager.h
new file mode 100644
index 0000000..d373a5e
--- /dev/null
+++ b/buffet/manager.h
@@ -0,0 +1,39 @@
+// 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_MANAGER_H_
+#define BUFFET_MANAGER_H_
+
+#include <base/basictypes.h>
+#include <base/memory/scoped_ptr.h>
+#include <dbus/message.h>
+
+namespace buffet {
+
+class DBusManager;
+
+// The Manager is responsible for global state of Buffet.  It exposes
+// interfaces which affect the entire device such as device registration and
+// device state.
+class Manager {
+ public:
+  Manager(DBusManager* dbus_manager);
+  ~Manager();
+
+ private:
+  // Handles calls to org.chromium.Buffet.Manager.RegisterDevice().
+  scoped_ptr<dbus::Response> HandleRegisterDevice(
+      dbus::MethodCall* method_call);
+  // Handles calls to org.chromium.Buffet.Manager.UpdateState().
+  scoped_ptr<dbus::Response> HandleUpdateState(
+      dbus::MethodCall* method_call);
+
+  DBusManager* dbus_manager_;  // Weak;  DBusManager should outlive Manager.
+
+  DISALLOW_COPY_AND_ASSIGN(Manager);
+};
+
+}  // namespace buffet
+
+#endif  // BUFFET_MANAGER_H_
diff --git a/buffet/map_utils.h b/buffet/map_utils.h
new file mode 100644
index 0000000..676c558
--- /dev/null
+++ b/buffet/map_utils.h
@@ -0,0 +1,46 @@
+// 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_MAP_UTILS_H_
+#define BUFFET_MAP_UTILS_H_
+
+#include <map>
+#include <vector>
+
+namespace chromeos {
+
+// Given an STL map returns a vector containing all keys from the map
+template<typename T>
+std::vector<typename T::key_type> GetMapKeys(T const& map) {
+  std::vector<typename T::key_type> keys;
+  keys.reserve(map.size());
+  for (auto&& pair : map)
+    keys.push_back(pair.first);
+  return keys;
+}
+
+// Given an STL map returns a vector containing all values from the map
+template<typename T>
+std::vector<typename T::mapped_type> GetMapValues(T const& map) {
+  std::vector<typename T::mapped_type> values;
+  values.reserve(map.size());
+  for (auto&& pair : map)
+    values.push_back(pair.second);
+  return values;
+}
+
+// Given an STL map returns a vector of key-value pairs from the map
+template<typename T>
+std::vector<std::pair<typename T::key_type,
+                      typename T::mapped_type>> MapToVector(T const& map) {
+  std::vector<std::pair<typename T::key_type, typename T::mapped_type>> vector;
+  vector.reserve(map.size());
+  for (auto&& pair : map)
+    vector.push_back(pair);
+  return vector;
+}
+
+} // namespace chromeos
+
+#endif // BUFFET_MAP_UTILS_H_
diff --git a/buffet/mime_utils.cc b/buffet/mime_utils.cc
new file mode 100644
index 0000000..a4f29ca
--- /dev/null
+++ b/buffet/mime_utils.cc
@@ -0,0 +1,154 @@
+// 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/mime_utils.h"
+
+#include <algorithm>
+#include <base/strings/string_util.h>
+
+#include "buffet/string_utils.h"
+
+using namespace chromeos;
+
+//***************************************************************************
+//******************************* MIME types ********************************
+//***************************************************************************
+const char mime::types::kApplication[]             = "application";
+const char mime::types::kAudio[]                   = "audio";
+const char mime::types::kImage[]                   = "image";
+const char mime::types::kMessage[]                 = "message";
+const char mime::types::kMultipart[]               = "multipart";
+const char mime::types::kText[]                    = "text";
+const char mime::types::kVideo[]                   = "video";
+
+const char mime::parameters::kCharset[]            = "charset";
+
+const char mime::image::kJpeg[]                    = "image/jpeg";
+const char mime::image::kPng[]                     = "image/png";
+const char mime::image::kBmp[]                     = "image/bmp";
+const char mime::image::kTiff[]                    = "image/tiff";
+const char mime::image::kGif[]                     = "image/gif";
+
+const char mime::text::kPlain[]                    = "text/plain";
+const char mime::text::kHtml[]                     = "text/html";
+const char mime::text::kXml[]                      = "text/xml";
+
+const char mime::application::kOctet_stream[]      = "application/octet-stream";
+const char mime::application::kJson[]              = "application/json";
+const char mime::application::kWwwFormUrlEncoded[] =
+    "application/x-www-form-urlencoded";
+
+//***************************************************************************
+//**************************** Utility Functions ****************************
+//***************************************************************************
+static std::string EncodeParam(std::string const& param) {
+  // If the string contains one of "tspecials" characters as
+  // specified in RFC 1521, enclose it in quotes.
+  if (param.find_first_of("()<>@,;:\\\"/[]?=") != std::string::npos) {
+    return '"' + param + '"';
+  }
+  return param;
+}
+
+static std::string DecodeParam(std::string const& param) {
+  if (param.size() > 1 && param.front() == '"' && param.back() == '"') {
+    return param.substr(1, param.size() - 2);
+  }
+  return param;
+}
+
+//***************************************************************************
+//******************** Main MIME manipulation functions *********************
+//***************************************************************************
+
+bool mime::Split(std::string const& mime_string,
+                 std::string* type, std::string* subtype,
+                 mime::Parameters* parameters) {
+  std::vector<std::string> parts = string_utils::Split(mime_string, ';');
+  if (parts.empty())
+    return false;
+
+  if (!mime::Split(parts.front(), type, subtype))
+    return false;
+
+  if(parameters) {
+    parameters->clear();
+    parameters->reserve(parts.size() - 1);
+    for (size_t i = 1; i < parts.size(); i++) {
+      auto pair = string_utils::SplitAtFirst(parts[i], '=');
+      pair.second = DecodeParam(pair.second);
+      parameters->push_back(pair);
+    }
+  }
+  return true;
+}
+
+bool mime::Split(std::string const& mime_string,
+                 std::string* type, std::string* subtype) {
+  std::string mime = mime::RemoveParameters(mime_string);
+  auto types = string_utils::SplitAtFirst(mime, '/');
+
+  if(type)
+    *type = types.first;
+
+  if(subtype)
+    *subtype = types.second;
+
+  return !types.first.empty() && !types.second.empty();
+}
+
+std::string mime::Combine(std::string const& type, std::string const& subtype,
+                          mime::Parameters const& parameters) {
+  std::vector<std::string> parts;
+  parts.push_back(string_utils::Join('/', type, subtype));
+  for (std::pair<std::string, std::string> const& pair : parameters) {
+    parts.push_back(string_utils::Join('=', pair.first,
+                                       EncodeParam(pair.second)));
+  }
+  return string_utils::Join("; ", parts);
+}
+
+std::string mime::GetType(std::string const& mime_string) {
+  std::string mime = mime::RemoveParameters(mime_string);
+  return string_utils::SplitAtFirst(mime, '/').first;
+}
+
+std::string mime::GetSubtype(std::string const& mime_string) {
+  std::string mime = mime::RemoveParameters(mime_string);
+  return string_utils::SplitAtFirst(mime, '/').second;
+}
+
+mime::Parameters mime::GetParameters(std::string const& mime_string) {
+  std::string type;
+  std::string subtype;
+  mime::Parameters parameters;
+
+  if (mime::Split(mime_string, &type, &subtype, &parameters))
+    return std::move(parameters);
+
+  return mime::Parameters();
+}
+
+std::string mime::RemoveParameters(std::string const& mime_string) {
+  return string_utils::SplitAtFirst(mime_string, ';').first;
+}
+
+std::string mime::AppendParameter(std::string const& mime_string,
+                                  std::string const& paramName,
+                                  std::string const& paramValue) {
+  std::string mime(mime_string);
+  mime += "; ";
+  mime += string_utils::Join('=', paramName, EncodeParam(paramValue));
+  return mime;
+}
+
+std::string mime::GetParameterValue(std::string const& mime_string,
+                                    std::string const& paramName) {
+  mime::Parameters params = mime::GetParameters(mime_string);
+  for(auto const& pair : params) {
+    if (base::strcasecmp(pair.first.c_str(), paramName.c_str()) == 0)
+      return pair.second;
+  }
+  return std::string();
+}
diff --git a/buffet/mime_utils.h b/buffet/mime_utils.h
new file mode 100644
index 0000000..e1dc7da
--- /dev/null
+++ b/buffet/mime_utils.h
@@ -0,0 +1,102 @@
+// 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_MIME_UTILS_H_
+#define BUFFET_MIME_UTILS_H_
+
+#include <string>
+#include <vector>
+
+namespace chromeos {
+
+namespace mime {
+
+namespace types {
+  // Main MIME type categories
+  extern const char kApplication[];       // application
+  extern const char kAudio[];             // audio
+  extern const char kImage[];             // image
+  extern const char kMessage[];           // message
+  extern const char kMultipart[];         // multipart
+  extern const char kText[];              // test
+  extern const char kVideo[];             // video
+}
+
+namespace parameters {
+  // Common MIME parameters
+  extern const char kCharset[];           // charset=...
+}
+
+namespace image {
+  // Common image MIME types
+  extern const char kJpeg[];              // image/jpeg
+  extern const char kPng[];               // image/png
+  extern const char kBmp[];               // image/bmp
+  extern const char kTiff[];              // image/tiff
+  extern const char kGif[];               // image/gif
+}
+
+namespace text {
+  // Common text MIME types
+  extern const char kPlain[];             // text/plain
+  extern const char kHtml[];              // text/html
+  extern const char kXml[];               // text/xml
+}
+
+namespace application {
+  // Common application MIME types
+  extern const char kOctet_stream[];      // application/octet-stream
+  extern const char kJson[];              // application/json
+  extern const char kWwwFormUrlEncoded[]; // application/x-www-form-urlencoded
+}
+
+typedef std::vector<std::pair<std::string, std::string>> Parameters;
+
+// Combine a MIME type, subtype and parameters into a MIME string.
+// e.g. Combine("text", "plain", {{"charset", "utf-8"}}) will give:
+//      "text/plain; charset=utf-8"
+std::string Combine(std::string const& type, std::string const& subtype,
+                    Parameters const& parameters = Parameters());
+
+// Splits a MIME string into type and subtype.
+// "text/plain;charset=utf-8" => ("text", "plain")
+bool Split(std::string const& mime_string,
+           std::string* type, std::string* subtype);
+
+// Splits a MIME string into type, subtype, and parameters.
+// "text/plain;charset=utf-8" => ("text", "plain", {{"charset","utf-8"}})
+bool Split(std::string const& mime_string,
+           std::string* type, std::string* subtype, Parameters* parameters);
+
+// Returns the MIME type from MIME string.
+// "text/plain;charset=utf-8" => "text"
+std::string GetType(std::string const& mime_string);
+
+// Returns the MIME sub-type from MIME string.
+// "text/plain;charset=utf-8" => "plain"
+std::string GetSubtype(std::string const& mime_string);
+
+// Returns the MIME parameters from MIME string.
+// "text/plain;charset=utf-8" => {{"charset","utf-8"}}
+Parameters GetParameters(std::string const& mime_string);
+
+// Removes parameters from a MIME string
+// "text/plain;charset=utf-8" => "text/plain"
+std::string RemoveParameters(std::string const& mime_string);
+
+// Appends a parameter to a MIME string.
+// "text/plain" => "text/plain; charset=utf-8"
+std::string AppendParameter(std::string const& mime_string,
+                            std::string const& paramName,
+                            std::string const& paramValue);
+
+// Returns the value of a parameter on a MIME string (empty string if missing).
+// ("text/plain;charset=utf-8","charset") => "utf-8"
+std::string GetParameterValue(std::string const& mime_string,
+                              std::string const& paramName);
+
+} // namespace mime
+} // namespace chromeos
+
+#endif // BUFFET_MIME_UTILS_H_
diff --git a/buffet/mime_utils_unittest.cc b/buffet/mime_utils_unittest.cc
new file mode 100644
index 0000000..893976a
--- /dev/null
+++ b/buffet/mime_utils_unittest.cc
@@ -0,0 +1,65 @@
+// Copyright (c) 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/mime_utils.h"
+
+#include <gtest/gtest.h>
+
+using namespace chromeos;
+
+TEST(MimeUtils, Combine) {
+  std::string mime_string = mime::Combine(mime::types::kText, "xml");
+  EXPECT_EQ(mime::text::kXml, mime_string);
+  EXPECT_EQ("application/json; charset=utf-8",
+            mime::Combine(mime::types::kApplication, "json",
+                          {{"charset", "utf-8"}}));
+}
+
+TEST(MimeUtils, Split) {
+  std::string s1, s2;
+  EXPECT_TRUE(mime::Split(mime::image::kJpeg, &s1, &s2));
+  EXPECT_EQ(mime::types::kImage, s1);
+  EXPECT_EQ("jpeg", s2);
+
+  mime::Parameters parameters;
+  EXPECT_TRUE(mime::Split("application/json;charset=utf-8",
+    &s1, &s2, &parameters));
+  EXPECT_EQ(mime::types::kApplication, s1);
+  EXPECT_EQ("json", s2);
+  EXPECT_EQ(mime::application::kJson, mime::Combine(s1, s2));
+  EXPECT_EQ(1, parameters.size());
+  EXPECT_EQ(mime::parameters::kCharset, parameters.front().first);
+  EXPECT_EQ("utf-8", parameters.front().second);
+  EXPECT_EQ("application/json; charset=utf-8",
+            mime::Combine(s1, s2, parameters));
+}
+
+TEST(MimeUtils, ExtractParts) {
+  mime::Parameters parameters;
+
+  EXPECT_EQ(mime::types::kText, mime::GetType(mime::text::kPlain));
+  EXPECT_EQ("plain", mime::GetSubtype(mime::text::kPlain));
+
+  parameters = mime::GetParameters("text/plain; charset=iso-8859-1;foo=bar");
+  EXPECT_EQ(2, parameters.size());
+  EXPECT_EQ(mime::parameters::kCharset, parameters[0].first);
+  EXPECT_EQ("iso-8859-1", parameters[0].second);
+  EXPECT_EQ("foo", parameters[1].first);
+  EXPECT_EQ("bar", parameters[1].second);
+}
+
+TEST(MimeUtils, AppendRemoveParams) {
+  std::string mime_string = mime::AppendParameter(mime::text::kXml,
+                                                  mime::parameters::kCharset,
+                                                  "utf-8");
+  EXPECT_EQ("text/xml; charset=utf-8", mime_string);
+  mime_string = mime::AppendParameter(mime_string, "foo", "bar");
+  EXPECT_EQ("text/xml; charset=utf-8; foo=bar", mime_string);
+  EXPECT_EQ("utf-8", mime::GetParameterValue(mime_string,
+                                             mime::parameters::kCharset));
+  EXPECT_EQ("bar", mime::GetParameterValue(mime_string, "foo"));
+  EXPECT_EQ("", mime::GetParameterValue(mime_string, "baz"));
+  mime_string = mime::RemoveParameters(mime_string);
+  EXPECT_EQ(mime::text::kXml, mime_string);
+}
diff --git a/buffet/string_utils.cc b/buffet/string_utils.cc
new file mode 100644
index 0000000..49a7d84
--- /dev/null
+++ b/buffet/string_utils.cc
@@ -0,0 +1,87 @@
+// 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/string_utils.h"
+
+#include <algorithm>
+#include <string.h>
+#include <base/strings/string_util.h>
+
+namespace chromeos {
+namespace string_utils {
+
+std::vector<std::string> Split(std::string const& str,
+                               char delimiter,
+                               bool trim_whitespaces,
+                               bool purge_empty_strings) {
+  std::vector<std::string> tokens;
+  if (delimiter == 0)
+    return tokens;
+
+  char const* sz = str.c_str();
+  if (sz) {
+    char const* szNext = strchr(sz, delimiter);
+    while (szNext) {
+      if (szNext != sz || !purge_empty_strings)
+        tokens.emplace_back(sz, szNext - sz);
+      sz = szNext + 1;
+      szNext = strchr(sz, delimiter);
+    }
+    if (*sz != 0 || !purge_empty_strings)
+      tokens.emplace_back(sz);
+  }
+
+  if (trim_whitespaces) {
+    std::for_each(tokens.begin(), tokens.end(), [](std::string& str) {
+      TrimWhitespaceASCII(str, TRIM_ALL, &str); });
+  }
+
+  return tokens;
+}
+
+std::pair<std::string, std::string> SplitAtFirst(std::string const& str,
+                                                 char delimiter,
+                                                 bool trim_whitespaces) {
+  std::pair<std::string, std::string> pair;
+  if (delimiter == 0)
+    return pair;
+
+  char const* sz = str.c_str();
+  char const* szNext = strchr(sz, delimiter);
+  if (szNext) {
+    pair.first = std::string(sz, szNext);
+    pair.second = std::string(szNext + 1);
+  } else {
+    pair.first = str;
+  }
+
+  if (trim_whitespaces) {
+    TrimWhitespaceASCII(pair.first, TRIM_ALL, &pair.first);
+    TrimWhitespaceASCII(pair.second, TRIM_ALL, &pair.second);
+  }
+
+  return pair;
+}
+
+std::string Join(char delimiter, std::vector<std::string> const& strings) {
+  return JoinString(strings, delimiter);
+}
+
+std::string Join(std::string const& delimiter,
+                 std::vector<std::string> const& strings) {
+  return JoinString(strings, delimiter);
+}
+
+std::string Join(char delimiter,
+                 std::string const& str1, std::string const& str2) {
+  return str1 + delimiter + str2;
+}
+
+std::string Join(std::string const& delimiter,
+                 std::string const& str1, std::string const& str2) {
+  return str1 + delimiter + str2;
+}
+
+} // namespace string_utils
+} // namespace chromeos
diff --git a/buffet/string_utils.h b/buffet/string_utils.h
new file mode 100644
index 0000000..bad62f3
--- /dev/null
+++ b/buffet/string_utils.h
@@ -0,0 +1,41 @@
+// 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_STRING_UTILS_H_
+#define BUFFET_STRING_UTILS_H_
+
+#include <string>
+#include <vector>
+
+namespace chromeos {
+namespace string_utils {
+
+// Treats the string as a delimited list of substrings and returns the array
+// of original elements of the list.
+// By default, empty elements from the original string are omitted and
+// each element has all whitespaces trimmed off.
+std::vector<std::string> Split(std::string const& str,
+                               char delimiter,
+                               bool trim_whitespaces = true,
+                               bool purge_empty_strings = true);
+
+// Splits the string into two pieces at the first position of the specified
+// delimiter. By default, each part has all whitespaces trimmed off.
+std::pair<std::string, std::string> SplitAtFirst(std::string const& str,
+                                                 char delimiter,
+                                                 bool trim_whitespaces = true);
+
+// Joins an array of strings into a single string separated by |delimiter|.
+std::string Join(char delimiter, std::vector<std::string> const& strings);
+std::string Join(std::string const& delimiter,
+                 std::vector<std::string> const& strings);
+std::string Join(char delimiter,
+                 std::string const& str1, std::string const& str2);
+std::string Join(std::string const& delimiter,
+                 std::string const& str1, std::string const& str2);
+
+} // namespace string_utils
+} // namespace chromeos
+
+#endif // BUFFET_STRING_UTILS_H_
diff --git a/buffet/string_utils_unittest.cc b/buffet/string_utils_unittest.cc
new file mode 100644
index 0000000..23bcef1
--- /dev/null
+++ b/buffet/string_utils_unittest.cc
@@ -0,0 +1,109 @@
+// Copyright (c) 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/string_utils.h"
+
+#include <gtest/gtest.h>
+
+using namespace chromeos;
+
+TEST(StringUtils, Split) {
+  std::vector<std::string> parts;
+
+  parts = string_utils::Split(",a,bc , d,,e,", ',', true, true);
+  EXPECT_EQ(4, parts.size());
+  EXPECT_EQ("a", parts[0]);
+  EXPECT_EQ("bc", parts[1]);
+  EXPECT_EQ("d", parts[2]);
+  EXPECT_EQ("e", parts[3]);
+
+  parts = string_utils::Split(",a,bc , d,,e,", ',', false, true);
+  EXPECT_EQ(4, parts.size());
+  EXPECT_EQ("a", parts[0]);
+  EXPECT_EQ("bc ", parts[1]);
+  EXPECT_EQ(" d", parts[2]);
+  EXPECT_EQ("e", parts[3]);
+
+  parts = string_utils::Split(",a,bc , d,,e,", ',', true, false);
+  EXPECT_EQ(7, parts.size());
+  EXPECT_EQ("", parts[0]);
+  EXPECT_EQ("a", parts[1]);
+  EXPECT_EQ("bc", parts[2]);
+  EXPECT_EQ("d", parts[3]);
+  EXPECT_EQ("", parts[4]);
+  EXPECT_EQ("e", parts[5]);
+  EXPECT_EQ("", parts[6]);
+
+  parts = string_utils::Split(",a,bc , d,,e,", ',', false, false);
+  EXPECT_EQ(7, parts.size());
+  EXPECT_EQ("", parts[0]);
+  EXPECT_EQ("a", parts[1]);
+  EXPECT_EQ("bc ", parts[2]);
+  EXPECT_EQ(" d", parts[3]);
+  EXPECT_EQ("", parts[4]);
+  EXPECT_EQ("e", parts[5]);
+  EXPECT_EQ("", parts[6]);
+}
+
+TEST(StringUtils, SplitAtFirst) {
+  std::pair<std::string, std::string> pair;
+
+  pair = string_utils::SplitAtFirst(" 123 : 4 : 56 : 789 ", ':', true);
+  EXPECT_EQ("123", pair.first);
+  EXPECT_EQ("4 : 56 : 789", pair.second);
+
+  pair = string_utils::SplitAtFirst(" 123 : 4 : 56 : 789 ", ':', false);
+  EXPECT_EQ(" 123 ", pair.first);
+  EXPECT_EQ(" 4 : 56 : 789 ", pair.second);
+
+  pair = string_utils::SplitAtFirst("", '=');
+  EXPECT_EQ("", pair.first);
+  EXPECT_EQ("", pair.second);
+
+  pair = string_utils::SplitAtFirst("=", '=');
+  EXPECT_EQ("", pair.first);
+  EXPECT_EQ("", pair.second);
+
+  pair = string_utils::SplitAtFirst("a=", '=');
+  EXPECT_EQ("a", pair.first);
+  EXPECT_EQ("", pair.second);
+
+  pair = string_utils::SplitAtFirst("abc=", '=');
+  EXPECT_EQ("abc", pair.first);
+  EXPECT_EQ("", pair.second);
+
+  pair = string_utils::SplitAtFirst("=a", '=');
+  EXPECT_EQ("", pair.first);
+  EXPECT_EQ("a", pair.second);
+
+  pair = string_utils::SplitAtFirst("=abc=", '=');
+  EXPECT_EQ("", pair.first);
+  EXPECT_EQ("abc=", pair.second);
+
+  pair = string_utils::SplitAtFirst("abc", '=');
+  EXPECT_EQ("abc", pair.first);
+  EXPECT_EQ("", pair.second);
+}
+
+TEST(StringUtils, Join_Char) {
+  EXPECT_EQ("", string_utils::Join(',', {}));
+  EXPECT_EQ("abc", string_utils::Join(',', {"abc"}));
+  EXPECT_EQ("abc,defg", string_utils::Join(',', {"abc", "defg"}));
+  EXPECT_EQ("1:2:3", string_utils::Join(':', {"1", "2", "3"}));
+  EXPECT_EQ("192.168.0.1", string_utils::Join('.', {"192", "168", "0", "1"}));
+  EXPECT_EQ("ff02::1", string_utils::Join(':', {"ff02", "", "1"}));
+}
+
+TEST(StringUtils, Join_String) {
+  EXPECT_EQ("", string_utils::Join(",", {}));
+  EXPECT_EQ("abc", string_utils::Join(",", {"abc"}));
+  EXPECT_EQ("abc,defg", string_utils::Join(",", {"abc", "defg"}));
+  EXPECT_EQ("1 : 2 : 3", string_utils::Join(" : ", {"1", "2", "3"}));
+  EXPECT_EQ("123", string_utils::Join("", {"1", "2", "3"}));
+}
+
+TEST(StringUtils, Join_Pair) {
+  EXPECT_EQ("ab,cd", string_utils::Join(',', "ab", "cd"));
+  EXPECT_EQ("key = value", string_utils::Join(" = ", "key", "value"));
+}
diff --git a/buffet/transport_interface.h b/buffet/transport_interface.h
new file mode 100644
index 0000000..fa858c6
--- /dev/null
+++ b/buffet/transport_interface.h
@@ -0,0 +1,79 @@
+// 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 {
+
+///////////////////////////////////////////////////////////////////////////////
+// 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(char const* accept_mime_types) = 0;
+  virtual std::string GetAccept() const = 0;
+
+  virtual std::string GetRequestURL() const = 0;
+
+  virtual void SetContentType(char const* content_type) = 0;
+  virtual std::string GetContentType() const = 0;
+
+  virtual void AddHeader(char const* header, char const* value) = 0;
+  virtual void RemoveHeader(char const* header) = 0;
+
+  virtual bool AddRequestBody(void const* data, size_t size) = 0;
+
+  virtual void SetMethod(char const* method) = 0;
+  virtual std::string GetMethod() const = 0;
+
+  virtual void SetReferer(char const* referer) = 0;
+  virtual std::string GetReferer() const = 0;
+
+  virtual void SetUserAgent(char const* 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(char const* header_name) const = 0;
+  virtual std::vector<unsigned char> const& 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_