Buffet: Move buffet over to platform2 from src/platform/buffet.
This change also open-sources buffet. The only change in this CL
is the removal of the Makefile and addition of the buffet.gyp file.
BUG=chromium:355180
TEST=USE=buffet emerge-gizmo platform2
Change-Id: Ibf8d3ac3f38313f82a9c07d79932b6f30130f9c5
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..6b08e4c
--- /dev/null
+++ b/buffet/buffet.gyp
@@ -0,0 +1,77 @@
+{
+ '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_manager.cc',
+ 'http_request.cc',
+ 'http_transport_curl.cc',
+ 'http_utils.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',
+ ],
+ },
+ {
+ 'target_name': 'buffet_testrunner',
+ 'type': 'executable',
+ 'dependencies': [
+ 'buffet_common',
+ ],
+ 'includes': ['../../common-mk/common_test.gypi'],
+ 'sources': [
+ 'buffet_testrunner.cc',
+ 'data_encoding_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..83d5f9c
--- /dev/null
+++ b/buffet/buffet_client.cc
@@ -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.
+
+#include <string>
+
+#include <base/logging.h>
+#include <base/memory/scoped_ptr.h>
+#include <dbus/bus.h>
+#include <dbus/object_proxy.h>
+#include <gflags/gflags.h>
+
+#include "buffet/dbus_manager.h"
+
+DEFINE_bool(testmethod, false, "Call the Buffet Test Method.");
+
+namespace {
+
+dbus::ObjectProxy* GetBuffetDBusProxy(dbus::Bus *bus) {
+ return bus->GetObjectProxy(
+ buffet::kBuffetServiceName,
+ dbus::ObjectPath(buffet::kBuffetServicePath));
+}
+
+void CallTestMethod(dbus::ObjectProxy* proxy) {
+ int timeout_ms = 1000;
+ dbus::MethodCall method_call(buffet::kBuffetInterface,
+ buffet::kTestMethod);
+ scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call,
+ timeout_ms));
+ if (!response) {
+ LOG(ERROR) << "Failed to receive a response.";
+ return;
+ } else {
+ LOG(INFO) << "Received a response.";
+ }
+}
+
+} // end namespace
+
+int main(int argc, char** argv) {
+ google::ParseCommandLineFlags(&argc, &argv, true);
+
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SYSTEM;
+ scoped_refptr<dbus::Bus> bus = new dbus::Bus(options);
+
+ auto proxy = GetBuffetDBusProxy(bus);
+ if (FLAGS_testmethod) {
+ CallTestMethod(proxy);
+ }
+
+ LOG(INFO) << "Done.";
+ return 0;
+}
+
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_manager.cc b/buffet/dbus_manager.cc
new file mode 100644
index 0000000..c6031fc
--- /dev/null
+++ b/buffet/dbus_manager.cc
@@ -0,0 +1,80 @@
+// 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>
+
+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),
+ buffet_dbus_object_(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_ = bus_->GetExportedObject(
+ dbus::ObjectPath(kBuffetServicePath));
+ ExportDBusMethod(kTestMethod, &DBusManager::HandleTestMethod);
+
+ CHECK(bus_->RequestOwnershipAndBlock(kBuffetServiceName,
+ dbus::Bus::REQUIRE_PRIMARY))
+ << "Unable to take ownership of " << kBuffetServiceName;
+}
+
+void DBusManager::ShutDownDBus() {
+ bus_->ShutdownAndBlock();
+}
+
+void DBusManager::ExportDBusMethod(const std::string& method_name,
+ DBusMethodCallMemberFunction member) {
+ DCHECK(buffet_dbus_object_);
+ CHECK(buffet_dbus_object_->ExportMethodAndBlock(
+ kBuffetInterface, method_name,
+ base::Bind(&HandleSynchronousDBusMethodCall,
+ base::Bind(member, base::Unretained(this)))));
+}
+
+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..da91391
--- /dev/null
+++ b/buffet/dbus_manager.h
@@ -0,0 +1,61 @@
+// 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 {
+
+// TODO(sosa): Move to chromeos/system_api once we're ready.
+const char kBuffetInterface[] = "org.chromium.Buffet";
+const char kBuffetServicePath[] = "/org/chromium/Buffet";
+const char kBuffetServiceName[] = "org.chromium.Buffet";
+
+// Methods exposed by buffet.
+const char kTestMethod[] = "TestMethod";
+
+class DBusManager;
+
+// Pointer to a member function for handling D-Bus method calls. If an empty
+// scoped_ptr is returned, an empty (but successful) response will be sent.
+typedef scoped_ptr<dbus::Response> (DBusManager::*DBusMethodCallMemberFunction)(
+ dbus::MethodCall*);
+
+// Class that manages dbus interactions in buffet.
+class DBusManager {
+ public:
+ DBusManager();
+ virtual ~DBusManager();
+
+ void Init();
+ void Finalize();
+
+ private:
+ // Connects to the D-Bus system bus and exports methods.
+ void InitDBus();
+ void ShutDownDBus();
+
+ // Exports |method_name| and uses |member| to handle calls.
+ void ExportDBusMethod(const std::string& method_name,
+ DBusMethodCallMemberFunction member);
+
+ // Callbacks for handling D-Bus signals and method calls.
+ scoped_ptr<dbus::Response> HandleTestMethod(dbus::MethodCall* method_call);
+
+ scoped_refptr<dbus::Bus> bus_;
+ dbus::ExportedObject* buffet_dbus_object_; // weak; owned by |bus_|
+
+ DISALLOW_COPY_AND_ASSIGN(DBusManager);
+};
+
+} // namespace buffet
+
+#endif // BUFFET_DBUS_MANAGER_H_
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..0b2e493
--- /dev/null
+++ b/buffet/main.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 <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"
+
+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();
+
+ message_loop.Run();
+
+ dbus_manager.Finalize();
+ return 0;
+}
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, ¶meters))
+ 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, ¶meters));
+ 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_