buffet: Added advanced error reporting
Created chromeos::Error class that encapsulates rich error
information from various system domains.
Swept GCD device registration, HTTP transport and utilities
to always return additional error information when the caller
requests it. This includes internal errors, general HTTP errors
as well as parsing and returning specific GCD and OAuth2 server
error responses.
Also fixed a number of existing linter warnings.
BUG=chromium:366709
TEST=All existing and new unit tests pass.
Change-Id: Ic01622a8efa3dc365ec106e595b09536818b9b23
Reviewed-on: https://chromium-review.googlesource.com/198772
Tested-by: Alex Vakulenko <avakulenko@chromium.org>
Reviewed-by: Christopher Wiley <wiley@chromium.org>
Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp
index 3a62f13..7e5c71a 100644
--- a/buffet/buffet.gyp
+++ b/buffet/buffet.gyp
@@ -27,17 +27,18 @@
'target_name': 'buffet_common',
'type': 'static_library',
'sources': [
+ 'async_event_sequencer.cc',
'data_encoding.cc',
'dbus_constants.cc',
'dbus_utils.cc',
'device_registration_info.cc',
+ 'error.cc',
'exported_object_manager.cc',
'exported_property_set.cc',
'http_request.cc',
'http_connection_curl.cc',
'http_transport_curl.cc',
'http_utils.cc',
- 'async_event_sequencer.cc',
'manager.cc',
'mime_utils.cc',
'storage_impls.cc',
@@ -78,6 +79,7 @@
'buffet_testrunner.cc',
'data_encoding_unittest.cc',
'device_registration_info_unittest.cc',
+ 'error_unittest.cc',
'exported_object_manager_unittest.cc',
'exported_property_set_unittest.cc',
'http_connection_fake.cc',
diff --git a/buffet/dbus_utils.cc b/buffet/dbus_utils.cc
index 673afe3..4924ef5 100644
--- a/buffet/dbus_utils.cc
+++ b/buffet/dbus_utils.cc
@@ -37,6 +37,22 @@
return scoped_ptr<dbus::Response>(resp.release());
}
+scoped_ptr<dbus::Response> GetDBusError(dbus::MethodCall* method_call,
+ const chromeos::Error* error) {
+ std::string message;
+ while (error) {
+ // Format error string as "domain/code:message".
+ if (!message.empty())
+ message += ';';
+ message += error->GetDomain() + '/' + error->GetCode() + ':' +
+ error->GetMessage();
+ error = error->GetInnerError();
+ }
+ scoped_ptr<dbus::ErrorResponse> resp(dbus::ErrorResponse::FromMethodCall(
+ method_call, "org.freedesktop.DBus.Error.Failed", message));
+ return scoped_ptr<dbus::Response>(resp.release());
+}
+
dbus::ExportedObject::MethodCallCallback GetExportableDBusMethod(
base::Callback<scoped_ptr<dbus::Response>(dbus::MethodCall*)> handler) {
return base::Bind(&HandleSynchronousDBusMethodCall, handler);
diff --git a/buffet/dbus_utils.h b/buffet/dbus_utils.h
index d4d5799..cc25844 100644
--- a/buffet/dbus_utils.h
+++ b/buffet/dbus_utils.h
@@ -11,6 +11,8 @@
#include <dbus/exported_object.h>
#include <dbus/message.h>
+#include "buffet/error.h"
+
namespace buffet {
namespace dbus_utils {
@@ -18,6 +20,10 @@
scoped_ptr<dbus::Response> GetBadArgsError(dbus::MethodCall* method_call,
const std::string& message);
+scoped_ptr<dbus::Response> GetDBusError(dbus::MethodCall* method_call,
+ const chromeos::Error* error);
+
+
dbus::ExportedObject::MethodCallCallback GetExportableDBusMethod(
base::Callback<scoped_ptr<dbus::Response>(dbus::MethodCall*)> handler);
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc
index e1cadbf..6a7f813 100644
--- a/buffet/device_registration_info.cc
+++ b/buffet/device_registration_info.cc
@@ -5,6 +5,8 @@
#include "buffet/device_registration_info.h"
#include <memory>
+#include <utility>
+#include <vector>
#include <base/json/json_writer.h>
#include <base/values.h>
@@ -18,8 +20,12 @@
#include "buffet/string_utils.h"
#include "buffet/url_utils.h"
-using namespace chromeos;
-using namespace chromeos::data_encoding;
+using namespace chromeos; // NOLINT(build/namespaces)
+
+const char buffet::kErrorDomainOAuth2[] = "oauth2";
+const char buffet::kErrorDomainGCD[] = "gcd";
+const char buffet::kErrorDomainGCDServer[] = "gcd_server";
+const char buffet::kErrorDomainBuffet[] = "buffet";
namespace buffet {
namespace storage_keys {
@@ -63,22 +69,24 @@
std::string authorization = string_utils::Join(' ',
access_token_type,
access_token);
- return {http::request_header::kAuthorization, authorization};
+ // Linter doesn't like the ; after } on the following line...
+ return {http::request_header::kAuthorization, authorization}; // NOLINT
}
std::unique_ptr<base::DictionaryValue> ParseOAuthResponse(
- const http::Response* response, std::string* error_message) {
+ const http::Response* response, ErrorPtr* error) {
int code = 0;
- auto resp = http::ParseJsonResponse(response, &code, error_message);
+ auto resp = http::ParseJsonResponse(response, &code, error);
if (resp && code >= http::status_code::BadRequest) {
- if (error_message) {
- error_message->clear();
- std::string error_code, error;
+ if (error) {
+ std::string error_code, error_message;
if (resp->GetString("error", &error_code) &&
- resp->GetString("error_description", &error)) {
- *error_message = error_code + " (" + error + ")";
+ resp->GetString("error_description", &error_message)) {
+ Error::AddTo(error, buffet::kErrorDomainOAuth2, error_code,
+ error_message);
} else {
- *error_message = "Unexpected OAuth error";
+ Error::AddTo(error, buffet::kErrorDomainOAuth2,
+ "unexpected_response", "Unexpected OAuth error");
}
}
return std::unique_ptr<base::DictionaryValue>();
@@ -86,9 +94,45 @@
return resp;
}
+inline void SetUnexpectedError(ErrorPtr* error) {
+ Error::AddTo(error, buffet::kErrorDomainGCD, "unexpected_response",
+ "Unexpected GCD error");
+}
+
+void ParseGCDError(const base::DictionaryValue* json, ErrorPtr* error) {
+ if (!error)
+ return;
+
+ const base::Value* list_value = nullptr;
+ const base::ListValue* error_list = nullptr;
+ if (!json->Get("error.errors", &list_value) ||
+ !list_value->GetAsList(&error_list)) {
+ SetUnexpectedError(error);
+ return;
+ }
+
+ for (size_t i = 0; i < error_list->GetSize(); i++) {
+ const base::Value* error_value = nullptr;
+ const base::DictionaryValue* error_object = nullptr;
+ if (!error_list->Get(i, &error_value) ||
+ !error_value->GetAsDictionary(&error_object)) {
+ SetUnexpectedError(error);
+ continue;
+ }
+ std::string error_code, error_message;
+ if (error_object->GetString("reason", &error_code) &&
+ error_object->GetString("message", &error_message)) {
+ Error::AddTo(error, buffet::kErrorDomainGCDServer,
+ error_code, error_message);
+ } else {
+ SetUnexpectedError(error);
+ }
+ }
+}
+
std::string BuildURL(const std::string& url,
const std::vector<std::string>& subpaths,
- const WebParamList& params) {
+ const data_encoding::WebParamList& params) {
std::string result = url::CombineMultiple(url, subpaths);
return url::AppendQueryParams(result, params);
}
@@ -116,23 +160,26 @@
}
std::string DeviceRegistrationInfo::GetServiceURL(
- const std::string& subpath, const WebParamList& params) const {
+ const std::string& subpath,
+ const data_encoding::WebParamList& params) const {
return BuildURL(service_url_, {subpath}, params);
}
std::string DeviceRegistrationInfo::GetDeviceURL(
- const std::string& subpath, const WebParamList& params) const {
+ const std::string& subpath,
+ const data_encoding::WebParamList& params) const {
CHECK(!device_id_.empty()) << "Must have a valid device ID";
return BuildURL(service_url_, {"devices", device_id_, subpath}, params);
}
-std::string DeviceRegistrationInfo::GetOAuthURL(const std::string& subpath,
- const WebParamList& params) const {
+std::string DeviceRegistrationInfo::GetOAuthURL(
+ const std::string& subpath,
+ const data_encoding::WebParamList& params) const {
return BuildURL(oauth_url_, {subpath}, params);
}
-std::string DeviceRegistrationInfo::GetDeviceId() {
- return CheckRegistration() ? device_id_ : std::string();
+std::string DeviceRegistrationInfo::GetDeviceId(ErrorPtr* error) {
+ return CheckRegistration(error) ? device_id_ : std::string();
}
bool DeviceRegistrationInfo::Load() {
@@ -192,20 +239,22 @@
return storage_->Save(&dict);
}
-bool DeviceRegistrationInfo::CheckRegistration() {
+bool DeviceRegistrationInfo::CheckRegistration(ErrorPtr* error) {
LOG(INFO) << "Checking device registration record.";
if (refresh_token_.empty() ||
device_id_.empty() ||
device_robot_account_.empty()) {
LOG(INFO) << "No valid device registration record found.";
+ Error::AddTo(error, kErrorDomainGCD, "device_not_registered",
+ "No valid device registration record found");
return false;
}
LOG(INFO) << "Device registration record found.";
- return ValidateAndRefreshAccessToken();
+ return ValidateAndRefreshAccessToken(error);
}
-bool DeviceRegistrationInfo::ValidateAndRefreshAccessToken() {
+bool DeviceRegistrationInfo::ValidateAndRefreshAccessToken(ErrorPtr* error) {
LOG(INFO) << "Checking access token expiration.";
if (!access_token_.empty() &&
!access_token_expiration_.is_null() &&
@@ -219,16 +268,13 @@
{"client_id", client_id_},
{"client_secret", client_secret_},
{"grant_type", "refresh_token"},
- }, transport_);
+ }, transport_, error);
if (!response)
return false;
- std::string error;
- auto json = ParseOAuthResponse(response.get(), &error);
- if (!json) {
- LOG(ERROR) << "Unable to refresh access token: " << error;
+ auto json = ParseOAuthResponse(response.get(), error);
+ if (!json)
return false;
- }
int expires_in = 0;
if (!json->GetString("access_token", &access_token_) ||
@@ -236,6 +282,8 @@
access_token_.empty() ||
expires_in <= 0) {
LOG(ERROR) << "Access token unavailable.";
+ Error::AddTo(error, kErrorDomainOAuth2, "unexpected_server_response",
+ "Access token unavailable");
return false;
}
@@ -247,40 +295,41 @@
return true;
}
-std::unique_ptr<base::Value> DeviceRegistrationInfo::GetDeviceInfo() {
- if (!CheckRegistration())
+std::unique_ptr<base::Value> DeviceRegistrationInfo::GetDeviceInfo(
+ ErrorPtr* error) {
+ if (!CheckRegistration(error))
return std::unique_ptr<base::Value>();
auto response = http::Get(GetDeviceURL(),
- {GetAuthorizationHeader()}, transport_);
+ {GetAuthorizationHeader()}, transport_, error);
int status_code = 0;
- std::unique_ptr<base::Value> device_info =
- http::ParseJsonResponse(response.get(), &status_code, nullptr);
-
- if (device_info) {
+ std::unique_ptr<base::DictionaryValue> json =
+ http::ParseJsonResponse(response.get(), &status_code, error);
+ if (json) {
if (status_code >= http::status_code::BadRequest) {
LOG(WARNING) << "Failed to retrieve the device info. Response code = "
<< status_code;
+ ParseGCDError(json.get(), error);
return std::unique_ptr<base::Value>();
}
}
- return device_info;
+ return std::unique_ptr<base::Value>(json.release());
}
bool CheckParam(const std::string& param_name,
const std::string& param_value,
- std::string* error_msg) {
+ ErrorPtr* error) {
if (!param_value.empty())
return true;
- if (error_msg)
- *error_msg = "Parameter " + param_name + " not specified";
+ Error::AddTo(error, kErrorDomainBuffet, "missing_parameter",
+ "Parameter " + param_name + " not specified");
return false;
}
std::string DeviceRegistrationInfo::StartRegistration(
const std::map<std::string, std::shared_ptr<base::Value>>& params,
- std::string* error_msg) {
+ ErrorPtr* error) {
GetParamValue(params, storage_keys::kClientId, &client_id_);
GetParamValue(params, storage_keys::kClientSecret, &client_secret_);
GetParamValue(params, storage_keys::kApiKey, &api_key_);
@@ -291,19 +340,19 @@
GetParamValue(params, storage_keys::kOAuthURL, &oauth_url_);
GetParamValue(params, storage_keys::kServiceURL, &service_url_);
- if (!CheckParam(storage_keys::kClientId, client_id_, error_msg))
+ if (!CheckParam(storage_keys::kClientId, client_id_, error))
return std::string();
- if (!CheckParam(storage_keys::kClientSecret, client_secret_, error_msg))
+ if (!CheckParam(storage_keys::kClientSecret, client_secret_, error))
return std::string();
- if (!CheckParam(storage_keys::kApiKey, api_key_, error_msg))
+ if (!CheckParam(storage_keys::kApiKey, api_key_, error))
return std::string();
- if (!CheckParam(storage_keys::kDeviceKind, device_kind_, error_msg))
+ if (!CheckParam(storage_keys::kDeviceKind, device_kind_, error))
return std::string();
- if (!CheckParam(storage_keys::kSystemName, system_name_, error_msg))
+ if (!CheckParam(storage_keys::kSystemName, system_name_, error))
return std::string();
- if (!CheckParam(storage_keys::kOAuthURL, oauth_url_, error_msg))
+ if (!CheckParam(storage_keys::kOAuthURL, oauth_url_, error))
return std::string();
- if (!CheckParam(storage_keys::kServiceURL, service_url_, error_msg))
+ if (!CheckParam(storage_keys::kServiceURL, service_url_, error))
return std::string();
std::vector<std::pair<std::string, std::vector<std::string>>> commands = {
@@ -339,20 +388,16 @@
std::string url = GetServiceURL("registrationTickets", {{"key", api_key_}});
auto resp_json = http::ParseJsonResponse(
- http::PostJson(url, &req_json, transport_).get(), nullptr, error_msg);
+ http::PostJson(url, &req_json, transport_, error).get(), nullptr, error);
if (!resp_json)
return std::string();
- const base::DictionaryValue* resp_dict = nullptr;
- if (!resp_json->GetAsDictionary(&resp_dict)) {
- if (error_msg)
- *error_msg = "Invalid response received";
+ if (!resp_json->GetString("id", &ticket_id_)) {
+ Error::AddTo(error, kErrorDomainGCD, "unexpected_response",
+ "Device ID missing");
return std::string();
}
- if (!resp_dict->GetString("id", &ticket_id_))
- return std::string();
-
std::string auth_url = GetOAuthURL("auth", {
{"scope", "https://www.googleapis.com/auth/clouddevices"},
{"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
@@ -370,90 +415,103 @@
}
bool DeviceRegistrationInfo::FinishRegistration(
- const std::string& user_auth_code) {
+ const std::string& user_auth_code, ErrorPtr* error) {
if (ticket_id_.empty()) {
LOG(ERROR) << "Finish registration without ticket ID";
+ Error::AddTo(error, kErrorDomainBuffet, "registration_not_started",
+ "Device registration not started");
return false;
}
std::string url = GetServiceURL("registrationTickets/" + ticket_id_);
std::unique_ptr<http::Response> response;
if (!user_auth_code.empty()) {
- std::string user_access_token;
response = http::PostFormData(GetOAuthURL("token"), {
{"code", user_auth_code},
{"client_id", client_id_},
{"client_secret", client_secret_},
{"redirect_uri", "urn:ietf:wg:oauth:2.0:oob"},
{"grant_type", "authorization_code"}
- }, transport_);
+ }, transport_, error);
if (!response)
return false;
- std::string error;
- auto json_resp = ParseOAuthResponse(response.get(), &error);
- if (!json_resp ||
- !json_resp->GetString("access_token", &user_access_token)) {
- LOG(ERROR) << "Error parsing OAuth response: " << error;
+ auto json_resp = ParseOAuthResponse(response.get(), error);
+ if (!json_resp)
+ return false;
+
+ std::string user_access_token;
+ std::string token_type;
+ if (!json_resp->GetString("access_token", &user_access_token) ||
+ !json_resp->GetString("token_type", &token_type)) {
+ Error::AddTo(error, kErrorDomainOAuth2, "unexpected_response",
+ "User access_token is missing in response");
return false;
}
base::DictionaryValue user_info;
user_info.SetString("userEmail", "me");
response = http::PatchJson(
- url, &user_info, {BuildAuthHeader("Bearer", user_access_token)},
- transport_);
+ url, &user_info, {BuildAuthHeader(token_type, user_access_token)},
+ transport_, error);
- auto json = http::ParseJsonResponse(response.get(), nullptr, &error);
- if (!json) {
- LOG(ERROR) << "Error populating user info: " << error;
+ auto json = http::ParseJsonResponse(response.get(), nullptr, error);
+ if (!json)
return false;
- }
}
std::string auth_code;
url += "/finalize?key=" + api_key_;
LOG(INFO) << "Sending request to: " << url;
- response = http::PostBinary(url, nullptr, 0, transport_);
- if (response && response->IsSuccessful()) {
- auto json_resp = http::ParseJsonResponse(response.get(), nullptr, nullptr);
- if (json_resp &&
- json_resp->GetString("robotAccountEmail", &device_robot_account_) &&
- json_resp->GetString("robotAccountAuthorizationCode", &auth_code) &&
- json_resp->GetString("deviceDraft.id", &device_id_)) {
- // Now get access_token and refresh_token
- response = http::PostFormData(GetOAuthURL("token"), {
- {"code", auth_code},
- {"client_id", client_id_},
- {"client_secret", client_secret_},
- {"redirect_uri", "oob"},
- {"scope", "https://www.googleapis.com/auth/clouddevices"},
- {"grant_type", "authorization_code"}
- }, transport_);
- if (!response)
- return false;
-
- json_resp = ParseOAuthResponse(response.get(), nullptr);
- int expires_in = 0;
- if (!json_resp ||
- !json_resp->GetString("access_token", &access_token_) ||
- !json_resp->GetString("refresh_token", &refresh_token_) ||
- !json_resp->GetInteger("expires_in", &expires_in) ||
- access_token_.empty() ||
- refresh_token_.empty() ||
- expires_in <= 0) {
- LOG(ERROR) << "Access token unavailable";
- return false;
- }
-
- access_token_expiration_ = base::Time::Now() +
- base::TimeDelta::FromSeconds(expires_in);
-
- Save();
- }
- return true;
+ response = http::PostBinary(url, nullptr, 0, transport_, error);
+ if (!response)
+ return false;
+ auto json_resp = http::ParseJsonResponse(response.get(), nullptr, error);
+ if (!json_resp)
+ return false;
+ if (!response->IsSuccessful()) {
+ ParseGCDError(json_resp.get(), error);
+ return false;
}
- return false;
+ if (!json_resp->GetString("robotAccountEmail", &device_robot_account_) ||
+ !json_resp->GetString("robotAccountAuthorizationCode", &auth_code) ||
+ !json_resp->GetString("deviceDraft.id", &device_id_)) {
+ Error::AddTo(error, kErrorDomainGCD, "unexpected_response",
+ "Device account missing in response");
+ return false;
+ }
+
+ // Now get access_token and refresh_token
+ response = http::PostFormData(GetOAuthURL("token"), {
+ {"code", auth_code},
+ {"client_id", client_id_},
+ {"client_secret", client_secret_},
+ {"redirect_uri", "oob"},
+ {"scope", "https://www.googleapis.com/auth/clouddevices"},
+ {"grant_type", "authorization_code"}
+ }, transport_, error);
+ if (!response)
+ return false;
+
+ json_resp = ParseOAuthResponse(response.get(), error);
+ int expires_in = 0;
+ if (!json_resp ||
+ !json_resp->GetString("access_token", &access_token_) ||
+ !json_resp->GetString("refresh_token", &refresh_token_) ||
+ !json_resp->GetInteger("expires_in", &expires_in) ||
+ access_token_.empty() ||
+ refresh_token_.empty() ||
+ expires_in <= 0) {
+ Error::AddTo(error, kErrorDomainGCD, "unexpected_response",
+ "Device access_token missing in response");
+ return false;
+ }
+
+ access_token_expiration_ = base::Time::Now() +
+ base::TimeDelta::FromSeconds(expires_in);
+
+ Save();
+ return true;
}
} // namespace buffet
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h
index f48bae9..5b2b374 100644
--- a/buffet/device_registration_info.h
+++ b/buffet/device_registration_info.h
@@ -2,17 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef BUFFET_DEVICE_INFO_H_
-#define BUFFET_DEVICE_INFO_H_
+#ifndef BUFFET_DEVICE_REGISTRATION_INFO_H_
+#define BUFFET_DEVICE_REGISTRATION_INFO_H_
#include <string>
#include <map>
#include <memory>
+#include <utility>
#include <base/basictypes.h>
#include <base/time/time.h>
#include "buffet/data_encoding.h"
+#include "buffet/error.h"
#include "buffet/http_transport.h"
#include "buffet/storage_interface.h"
@@ -22,6 +24,11 @@
namespace buffet {
+extern const char kErrorDomainOAuth2[];
+extern const char kErrorDomainGCD[];
+extern const char kErrorDomainGCDServer[];
+extern const char kErrorDomainBuffet[];
+
// The DeviceRegistrationInfo class represents device registration information.
class DeviceRegistrationInfo {
public:
@@ -67,18 +74,18 @@
const chromeos::data_encoding::WebParamList& params = {}) const;
// Returns the registered device ID (GUID) or empty string if failed
- std::string GetDeviceId();
+ std::string GetDeviceId(chromeos::ErrorPtr* error);
// Loads the device registration information from cache.
bool Load();
// Checks for the valid device registration as well as refreshes
// the device access token, if available.
- bool CheckRegistration();
+ bool CheckRegistration(chromeos::ErrorPtr* error);
// Gets the full device description JSON object, or nullptr if
// the device is not registered or communication failure.
- std::unique_ptr<base::Value> GetDeviceInfo();
+ std::unique_ptr<base::Value> GetDeviceInfo(chromeos::ErrorPtr* error);
// Starts device registration procedure. |params| are a list of
// key-value pairs of device information, such as client_id, client_secret,
@@ -86,20 +93,21 @@
// is used when possible. Returns a device claim ID on success.
std::string StartRegistration(
const std::map<std::string, std::shared_ptr<base::Value>>& params,
- std::string* error_msg);
+ chromeos::ErrorPtr* error);
// Finalizes the device registration. If |user_auth_code| is provided, then
// the device record is populated with user email on user's behalf. Otherwise
// the user is responsible to issue a PATCH request to provide a valid
// email address before calling FinishRegistration.
- bool FinishRegistration(const std::string& user_auth_code);
+ bool FinishRegistration(const std::string& user_auth_code,
+ chromeos::ErrorPtr* error);
private:
// Saves the device registration to cache.
bool Save() const;
// Makes sure the access token is available and up-to-date.
- bool ValidateAndRefreshAccessToken();
+ bool ValidateAndRefreshAccessToken(chromeos::ErrorPtr* error);
// Persistent data. Some of default values for testing purposes are used.
// TODO(avakulenko): remove these default values in the future.
@@ -135,4 +143,4 @@
} // namespace buffet
-#endif // BUFFET_DEVICE_INFO_H_
+#endif // BUFFET_DEVICE_REGISTRATION_INFO_H_
diff --git a/buffet/device_registration_info_unittest.cc b/buffet/device_registration_info_unittest.cc
index 93584b4..5456ca0 100644
--- a/buffet/device_registration_info_unittest.cc
+++ b/buffet/device_registration_info_unittest.cc
@@ -14,9 +14,9 @@
#include "buffet/mime_utils.h"
#include "buffet/storage_impls.h"
-using namespace buffet;
-using namespace chromeos;
-using namespace chromeos::http;
+using namespace buffet; // NOLINT(build/namespaces)
+using namespace chromeos; // NOLINT(build/namespaces)
+using namespace chromeos::http; // NOLINT(build/namespaces)
namespace {
@@ -102,10 +102,10 @@
json.SetString("token_type", "Bearer");
json.SetString("refresh_token", test_data::kRefreshToken);
} else {
- ASSERT_TRUE(false); // Unexpected authorization code.
+ FAIL() << "Unexpected authorization code";
}
} else {
- ASSERT_TRUE(false); // Unexpected grant type.
+ FAIL() << "Unexpected grant type";
}
json.SetInteger("expires_in", 3600);
response->ReplyJson(status_code::Ok, &json);
@@ -209,7 +209,7 @@
TEST_F(DeviceRegistrationInfoTest, CheckRegistration) {
EXPECT_TRUE(dev_reg->Load());
- EXPECT_FALSE(dev_reg->CheckRegistration());
+ EXPECT_FALSE(dev_reg->CheckRegistration(nullptr));
EXPECT_EQ(0, transport->GetRequestCount());
SetDefaultDeviceRegistration(&data);
@@ -219,7 +219,7 @@
transport->AddHandler(dev_reg->GetOAuthURL("token"), request_type::kPost,
base::Bind(OAuth2Handler));
transport->ResetRequestCount();
- EXPECT_TRUE(dev_reg->CheckRegistration());
+ EXPECT_TRUE(dev_reg->CheckRegistration(nullptr));
EXPECT_EQ(1, transport->GetRequestCount());
}
@@ -233,7 +233,7 @@
transport->AddHandler(dev_reg->GetDeviceURL(), request_type::kGet,
base::Bind(DeviceInfoHandler));
transport->ResetRequestCount();
- auto device_info = dev_reg->GetDeviceInfo();
+ auto device_info = dev_reg->GetDeviceInfo(nullptr);
EXPECT_EQ(2, transport->GetRequestCount());
EXPECT_NE(nullptr, device_info.get());
base::DictionaryValue* dict = nullptr;
@@ -252,7 +252,7 @@
base::Bind(OAuth2Handler));
transport->AddHandler(dev_reg->GetDeviceURL(), request_type::kGet,
base::Bind(DeviceInfoHandler));
- std::string id = dev_reg->GetDeviceId();
+ std::string id = dev_reg->GetDeviceId(nullptr);
EXPECT_EQ(test_data::kDeviceId, id);
}
@@ -317,8 +317,8 @@
storage->reset_save_count();
DeviceRegistrationInfo::TestHelper::SetTestTicketId(dev_reg.get());
- EXPECT_TRUE(dev_reg->FinishRegistration(""));
- EXPECT_EQ(1, storage->save_count()); // The device info must have been saved.
+ EXPECT_TRUE(dev_reg->FinishRegistration("", nullptr));
+ EXPECT_EQ(1, storage->save_count()); // The device info must have been saved.
EXPECT_EQ(2, transport->GetRequestCount());
// Validate the device info saved to storage...
@@ -386,7 +386,8 @@
storage->reset_save_count();
DeviceRegistrationInfo::TestHelper::SetTestTicketId(dev_reg.get());
- EXPECT_TRUE(dev_reg->FinishRegistration(test_data::kUserAccountAuthCode));
- EXPECT_EQ(1, storage->save_count()); // The device info must have been saved.
+ EXPECT_TRUE(dev_reg->FinishRegistration(test_data::kUserAccountAuthCode,
+ nullptr));
+ EXPECT_EQ(1, storage->save_count()); // The device info must have been saved.
EXPECT_EQ(4, transport->GetRequestCount());
}
diff --git a/buffet/error.cc b/buffet/error.cc
new file mode 100644
index 0000000..b04bbb6
--- /dev/null
+++ b/buffet/error.cc
@@ -0,0 +1,63 @@
+// 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/error.h"
+
+#include <base/logging.h>
+
+using chromeos::Error;
+using chromeos::ErrorPtr;
+
+ErrorPtr Error::Create(const std::string& domain,
+ const std::string& code,
+ const std::string& message) {
+ return Create(domain, code, message, ErrorPtr());
+}
+
+ErrorPtr Error::Create(const std::string& domain,
+ const std::string& code,
+ const std::string& message,
+ ErrorPtr inner_error) {
+ LOG(ERROR) << "Error::Create: Domain=" << domain
+ << ", Code=" << code << ", Message=" << message;
+ return ErrorPtr(new Error(domain, code, message, std::move(inner_error)));
+}
+
+void Error::AddTo(ErrorPtr* error, const std::string& domain,
+ const std::string& code, const std::string& message) {
+ if (error) {
+ *error = Create(domain, code, message, std::move(*error));
+ } else {
+ // Create already logs the error, but if |error| is nullptr,
+ // we still want to log the error...
+ LOG(ERROR) << "Error::Create: Domain=" << domain
+ << ", Code=" << code << ", Message=" << message;
+ }
+}
+
+bool Error::HasDomain(const std::string& domain) const {
+ const Error* err = this;
+ while (err) {
+ if (err->GetDomain() == domain)
+ return true;
+ err = err->GetInnerError();
+ }
+ return false;
+}
+
+bool Error::HasError(const std::string& domain, const std::string& code) const {
+ const Error* err = this;
+ while (err) {
+ if (err->GetDomain() == domain && err->GetCode() == code)
+ return true;
+ err = err->GetInnerError();
+ }
+ return false;
+}
+
+Error::Error(const std::string& domain, const std::string& code,
+ const std::string& message, ErrorPtr inner_error) :
+ domain_(domain), code_(code), message_(message),
+ inner_error_(std::move(inner_error)) {
+}
diff --git a/buffet/error.h b/buffet/error.h
new file mode 100644
index 0000000..97c48ee
--- /dev/null
+++ b/buffet/error.h
@@ -0,0 +1,71 @@
+// 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_ERROR_H_
+#define BUFFET_ERROR_H_
+
+#include <memory>
+#include <string>
+
+#include <base/basictypes.h>
+
+namespace chromeos {
+
+class Error; // Forward declaration.
+
+typedef std::unique_ptr<Error> ErrorPtr;
+
+class Error {
+ public:
+ virtual ~Error() = default;
+
+ // Creates an instance of Error class.
+ static ErrorPtr Create(const std::string& domain, const std::string& code,
+ const std::string& message);
+ static ErrorPtr Create(const std::string& domain, const std::string& code,
+ const std::string& message, ErrorPtr inner_error);
+ // If |error| is not nullptr, creates another instance of Error class,
+ // initializes it with specified arguments and adds it to the head of
+ // the error chain pointed to by |error|.
+ static void AddTo(ErrorPtr* error, const std::string& domain,
+ const std::string& code, const std::string& message);
+
+ // Returns the error domain, code and message
+ const std::string& GetDomain() const { return domain_; }
+ const std::string& GetCode() const { return code_; }
+ const std::string& GetMessage() const { return message_; }
+
+ // Checks if this or any of the inner error in the chain has the specified
+ // error domain.
+ bool HasDomain(const std::string& domain) const;
+ // Checks if this or any of the inner error in the chain matches the specified
+ // error domain and code.
+ bool HasError(const std::string& domain, const std::string& code) const;
+
+ // Gets a pointer to the inner error, if present. Returns nullptr otherwise.
+ const Error* GetInnerError() const { return inner_error_.get(); }
+
+ protected:
+ // Constructor is protected since this object is supposed to be
+ // created via the Create factory methods.
+ Error(const std::string& domain, const std::string& code,
+ const std::string& message, ErrorPtr inner_error);
+
+ // Error domain. The domain defines the scopes for error codes.
+ // Two errors with the same code but different domains are different errors.
+ std::string domain_;
+ // Error code. A unique error code identifier within the given domain.
+ std::string code_;
+ // Human-readable error message.
+ std::string message_;
+ // Pointer to inner error, if any. This forms a chain of errors.
+ ErrorPtr inner_error_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Error);
+};
+
+} // namespace chromeos
+
+#endif // BUFFET_ERROR_H_
diff --git a/buffet/error_unittest.cc b/buffet/error_unittest.cc
new file mode 100644
index 0000000..3963315
--- /dev/null
+++ b/buffet/error_unittest.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 <gtest/gtest.h>
+#include <base/files/file_path.h>
+
+#include "buffet/error.h"
+
+using chromeos::Error;
+
+namespace {
+
+chromeos::ErrorPtr GenerateNetworkError() {
+ return Error::Create("network", "not_found", "Resource not found");
+}
+
+chromeos::ErrorPtr GenerateHttpError() {
+ auto inner = GenerateNetworkError();
+ return Error::Create("HTTP", "404", "Not found", std::move(inner));
+}
+
+} // namespace
+
+TEST(Error, Single) {
+ auto err = GenerateNetworkError();
+ EXPECT_EQ("network", err->GetDomain());
+ EXPECT_EQ("not_found", err->GetCode());
+ EXPECT_EQ("Resource not found", err->GetMessage());
+ EXPECT_EQ(nullptr, err->GetInnerError());
+ EXPECT_TRUE(err->HasDomain("network"));
+ EXPECT_FALSE(err->HasDomain("HTTP"));
+ EXPECT_FALSE(err->HasDomain("foo"));
+ EXPECT_TRUE(err->HasError("network", "not_found"));
+ EXPECT_FALSE(err->HasError("network", "404"));
+ EXPECT_FALSE(err->HasError("HTTP", "404"));
+ EXPECT_FALSE(err->HasError("HTTP", "not_found"));
+ EXPECT_FALSE(err->HasError("foo", "bar"));
+}
+
+TEST(Error, Nested) {
+ auto err = GenerateHttpError();
+ EXPECT_EQ("HTTP", err->GetDomain());
+ EXPECT_EQ("404", err->GetCode());
+ EXPECT_EQ("Not found", err->GetMessage());
+ EXPECT_NE(nullptr, err->GetInnerError());
+ EXPECT_EQ("network", err->GetInnerError()->GetDomain());
+ EXPECT_TRUE(err->HasDomain("network"));
+ EXPECT_TRUE(err->HasDomain("HTTP"));
+ EXPECT_FALSE(err->HasDomain("foo"));
+ EXPECT_TRUE(err->HasError("network", "not_found"));
+ EXPECT_FALSE(err->HasError("network", "404"));
+ EXPECT_TRUE(err->HasError("HTTP", "404"));
+ EXPECT_FALSE(err->HasError("HTTP", "not_found"));
+ EXPECT_FALSE(err->HasError("foo", "bar"));
+}
diff --git a/buffet/http_connection.h b/buffet/http_connection.h
index 318afdc..5ddd2ac 100644
--- a/buffet/http_connection.h
+++ b/buffet/http_connection.h
@@ -10,13 +10,14 @@
#include <base/basictypes.h>
+#include "buffet/error.h"
#include "buffet/http_transport.h"
namespace chromeos {
namespace http {
///////////////////////////////////////////////////////////////////////////////
-// Connection class is the base class for HTTP comminication session.
+// Connection class is the base class for HTTP communication session.
// It abstracts the implementation of underlying transport library (ex libcurl).
// When the Connection-derived class is constructed, it is pre-set up with
// basic initialization information necessary to initiate the server request
@@ -25,22 +26,24 @@
// would not probably initiate the physical connection until SendHeaders
// is called.
// You normally shouldn't worry about using this class directly.
-// http::Request and http::Response classes use it for communictaion.
+// http::Request and http::Response classes use it for communication.
///////////////////////////////////////////////////////////////////////////////
class Connection {
public:
- Connection(std::shared_ptr<Transport> transport) : transport_(transport) {}
+ explicit Connection(std::shared_ptr<Transport> transport)
+ : transport_(transport) {}
virtual ~Connection() = default;
// Called by http::Request to initiate the connection with the server.
// This normally opens the socket and sends the request headers.
- virtual bool SendHeaders(const HeaderList& headers) = 0;
+ virtual bool SendHeaders(const HeaderList& headers, ErrorPtr* error) = 0;
// If needed, this function can be called to send the request body data.
// This function can be called repeatedly until all data is sent.
- virtual bool WriteRequestData(const void* data, size_t size) = 0;
+ virtual bool WriteRequestData(const void* data, size_t size,
+ ErrorPtr* error) = 0;
// This function is called when all the data is sent off and it's time
// to receive the response data.
- virtual bool FinishRequest() = 0;
+ virtual bool FinishRequest(ErrorPtr* error) = 0;
// Returns the HTTP status code (e.g. 200 for success).
virtual int GetResponseStatusCode() const = 0;
@@ -63,9 +66,7 @@
// |read_size| is the amount of data actually read, which could be less than
// the size requested or 0 if there is no more data available.
virtual bool ReadResponseData(void* data, size_t buffer_size,
- size_t* size_read) = 0;
- // Returns additional error information if any of the above functions fail.
- virtual std::string GetErrorMessage() const = 0;
+ size_t* size_read, ErrorPtr* error) = 0;
protected:
// |transport_| is mainly used to keep the object alive as long as the
@@ -77,7 +78,7 @@
DISALLOW_COPY_AND_ASSIGN(Connection);
};
-} // namespace http
-} // namespace chromeos
+} // namespace http
+} // namespace chromeos
-#endif // BUFFET_HTTP_CONNECTION_H_
+#endif // BUFFET_HTTP_CONNECTION_H_
diff --git a/buffet/http_connection_curl.cc b/buffet/http_connection_curl.cc
index 59edc15..4e9ea94 100644
--- a/buffet/http_connection_curl.cc
+++ b/buffet/http_connection_curl.cc
@@ -7,10 +7,12 @@
#include <base/logging.h>
#include "buffet/http_request.h"
+#include "buffet/http_transport_curl.h"
#include "buffet/string_utils.h"
-using namespace chromeos;
-using namespace chromeos::http::curl;
+namespace chromeos {
+namespace http {
+namespace curl {
static int curl_trace(CURL *handle, curl_infotype type,
char *data, size_t size, void *userp) {
@@ -54,12 +56,13 @@
VLOG(1) << "curl::Connection destroyed";
}
-bool Connection::SendHeaders(const HeaderList& headers) {
+bool Connection::SendHeaders(const HeaderList& headers, ErrorPtr* error) {
headers_.insert(headers.begin(), headers.end());
return true;
}
-bool Connection::WriteRequestData(const void* data, size_t size) {
+bool Connection::WriteRequestData(const void* data, size_t size,
+ ErrorPtr* error) {
if (size > 0) {
auto data_ptr = reinterpret_cast<const unsigned char*>(data);
request_data_.insert(request_data_.end(), data_ptr, data_ptr + size);
@@ -67,7 +70,7 @@
return true;
}
-bool Connection::FinishRequest() {
+bool Connection::FinishRequest(ErrorPtr* error) {
if (VLOG_IS_ON(3)) {
curl_easy_setopt(curl_handle_, CURLOPT_DEBUGFUNCTION, curl_trace);
curl_easy_setopt(curl_handle_, CURLOPT_VERBOSE, 1L);
@@ -118,8 +121,8 @@
if (header_list)
curl_slist_free_all(header_list);
if (ret != CURLE_OK) {
- error_ = curl_easy_strerror(ret);
- LOG(ERROR) << "CURL request failed: " << error_;
+ Error::AddTo(error, http::curl::kErrorDomain, std::to_string(ret),
+ curl_easy_strerror(ret));
} else {
LOG(INFO) << "Response: " << GetResponseStatusCode() << " ("
<< GetResponseStatusText() << ")";
@@ -131,7 +134,7 @@
}
int Connection::GetResponseStatusCode() const {
- long status_code = 0;
+ long status_code = 0; // NOLINT(runtime/int) - curl expects a long here.
curl_easy_getinfo(curl_handle_, CURLINFO_RESPONSE_CODE, &status_code);
return status_code;
}
@@ -155,7 +158,7 @@
}
bool Connection::ReadResponseData(void* data, size_t buffer_size,
- size_t* size_read) {
+ size_t* size_read, ErrorPtr* error) {
size_t size_to_read = response_data_.size() - response_data_ptr_;
if (size_to_read > buffer_size)
size_to_read = buffer_size;
@@ -166,10 +169,6 @@
return true;
}
-std::string Connection::GetErrorMessage() const {
- return error_;
-}
-
size_t Connection::write_callback(char* ptr, size_t size,
size_t num, void* data) {
Connection* me = reinterpret_cast<Connection*>(data);
@@ -199,7 +198,7 @@
size_t num, void* data) {
Connection* me = reinterpret_cast<Connection*>(data);
size_t hdr_len = size * num;
- std::string header(ptr, int(hdr_len));
+ std::string header(ptr, hdr_len);
// Remove newlines at the end of header line.
while (!header.empty() && (header.back() == '\r' || header.back() == '\n')) {
header.pop_back();
@@ -221,3 +220,7 @@
}
return hdr_len;
}
+
+} // namespace curl
+} // namespace http
+} // namespace chromeos
diff --git a/buffet/http_connection_curl.h b/buffet/http_connection_curl.h
index 0cf34e8..d588854 100644
--- a/buffet/http_connection_curl.h
+++ b/buffet/http_connection_curl.h
@@ -27,9 +27,10 @@
// Overrides from http::Connection.
// See http_connection.h for description of these methods.
- virtual bool SendHeaders(const HeaderList& headers) override;
- virtual bool WriteRequestData(const void* data, size_t size) override;
- virtual bool FinishRequest() override;
+ virtual bool SendHeaders(const HeaderList& headers, ErrorPtr* error) override;
+ virtual bool WriteRequestData(const void* data, size_t size,
+ ErrorPtr* error) override;
+ virtual bool FinishRequest(ErrorPtr* error) override;
virtual int GetResponseStatusCode() const override;
virtual std::string GetResponseStatusText() const override;
@@ -38,8 +39,7 @@
const std::string& header_name) const override;
virtual uint64_t GetResponseDataSize() const override;
virtual bool ReadResponseData(void* data, size_t buffer_size,
- size_t* size_read) override;
- virtual std::string GetErrorMessage() const override;
+ size_t* size_read, ErrorPtr* error) override;
protected:
// Write data callback. Used by CURL when receiving response data.
@@ -65,11 +65,9 @@
// After request has been sent, contains the received response headers.
std::map<std::string, std::string> headers_;
- // CURL error message in case request fails completely.
- std::string error_;
// HTTP protocol version, such as HTTP/1.1
std::string protocol_version_;
- // Reponse status text, such as "OK" for 200, or "Forbidden" for 403
+ // Response 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.
@@ -85,4 +83,4 @@
} // namespace http
} // namespace chromeos
-#endif // BUFFET_HTTP_CONNECTION_CURL_H_
+#endif // BUFFET_HTTP_CONNECTION_CURL_H_
diff --git a/buffet/http_connection_fake.cc b/buffet/http_connection_fake.cc
index 6a1c3b9..4372f0d 100644
--- a/buffet/http_connection_fake.cc
+++ b/buffet/http_connection_fake.cc
@@ -10,8 +10,9 @@
#include "buffet/mime_utils.h"
#include "buffet/string_utils.h"
-using namespace chromeos;
-using namespace chromeos::http::fake;
+namespace chromeos {
+namespace http {
+namespace fake {
Connection::Connection(const std::string& url, const std::string& method,
std::shared_ptr<http::Transport> transport) :
@@ -23,20 +24,21 @@
VLOG(1) << "fake::Connection destroyed";
}
-bool Connection::SendHeaders(const HeaderList& headers) {
+bool Connection::SendHeaders(const HeaderList& headers, ErrorPtr* error) {
request_.AddHeaders(headers);
return true;
}
-bool Connection::WriteRequestData(const void* data, size_t size) {
+bool Connection::WriteRequestData(const void* data, size_t size,
+ ErrorPtr* error) {
request_.AddData(data, size);
return true;
}
-bool Connection::FinishRequest() {
+bool Connection::FinishRequest(ErrorPtr* error) {
request_.AddHeaders({{request_header::kContentLength,
std::to_string(request_.GetData().size())}});
- fake::Transport* transport = dynamic_cast<fake::Transport*>(transport_.get());
+ fake::Transport* transport = static_cast<fake::Transport*>(transport_.get());
CHECK(transport) << "Expecting a fake transport";
auto handler = transport->GetHandler(request_.GetURL(), request_.GetMethod());
if (handler.is_null()) {
@@ -75,7 +77,7 @@
}
bool Connection::ReadResponseData(void* data, size_t buffer_size,
- size_t* size_read) {
+ size_t* size_read, ErrorPtr* error) {
size_t size_to_read = GetResponseDataSize() - response_data_ptr_;
if (size_to_read > buffer_size)
size_to_read = buffer_size;
@@ -87,6 +89,6 @@
return true;
}
-std::string Connection::GetErrorMessage() const {
- return std::string();
-}
+} // namespace fake
+} // namespace http
+} // namespace chromeos
diff --git a/buffet/http_connection_fake.h b/buffet/http_connection_fake.h
index 26ca307..b412d75 100644
--- a/buffet/http_connection_fake.h
+++ b/buffet/http_connection_fake.h
@@ -27,9 +27,10 @@
// Overrides from http::Connection.
// See http_connection.h for description of these methods.
- virtual bool SendHeaders(const HeaderList& headers) override;
- virtual bool WriteRequestData(const void* data, size_t size) override;
- virtual bool FinishRequest() override;
+ virtual bool SendHeaders(const HeaderList& headers, ErrorPtr* error) override;
+ virtual bool WriteRequestData(const void* data, size_t size,
+ ErrorPtr* error) override;
+ virtual bool FinishRequest(ErrorPtr* error) override;
virtual int GetResponseStatusCode() const override;
virtual std::string GetResponseStatusText() const override;
@@ -38,12 +39,9 @@
const std::string& header_name) const override;
virtual uint64_t GetResponseDataSize() const override;
virtual bool ReadResponseData(void* data, size_t buffer_size,
- size_t* size_read) override;
- virtual std::string GetErrorMessage() const override;
+ size_t* size_read, ErrorPtr* error) override;
private:
- DISALLOW_COPY_AND_ASSIGN(Connection);
-
// Request and response objects passed to the user-provided request handler
// callback. The request object contains all the request information.
// The response object is the server response that is created by
@@ -53,10 +51,12 @@
// Internal read data pointer needed for ReadResponseData() implementation.
size_t response_data_ptr_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Connection);
};
} // namespace fake
} // namespace http
} // namespace chromeos
-#endif // BUFFET_HTTP_CONNECTION_FAKE_H_
+#endif // BUFFET_HTTP_CONNECTION_FAKE_H_
diff --git a/buffet/http_request.cc b/buffet/http_request.cc
index 11dafff..05860c5 100644
--- a/buffet/http_request.cc
+++ b/buffet/http_request.cc
@@ -12,8 +12,8 @@
#include "buffet/mime_utils.h"
#include "buffet/string_utils.h"
-using namespace chromeos;
-using namespace chromeos::http;
+namespace chromeos {
+namespace http {
// request_type
const char request_type::kOptions[] = "OPTIONS";
@@ -100,9 +100,9 @@
const char response_header::kWarning[] = "Warning";
const char response_header::kWwwAuthenticate[] = "WWW-Authenticate";
-//**************************************************************************
-//********************** Request Class **********************
-//**************************************************************************
+// ***********************************************************
+// ********************** Request Class **********************
+// ***********************************************************
Request::Request(const std::string& url, const char* method,
std::shared_ptr<Transport> transport) :
transport_(transport), request_url_(url), method_(method) {
@@ -127,11 +127,11 @@
ranges_.emplace_back(from_byte, to_byte);
}
-std::unique_ptr<Response> Request::GetResponse() {
- if (!SendRequestIfNeeded() || !connection_->FinishRequest())
+std::unique_ptr<Response> Request::GetResponse(ErrorPtr* error) {
+ if (!SendRequestIfNeeded(error) || !connection_->FinishRequest(error))
return std::unique_ptr<Response>();
std::unique_ptr<Response> response(new Response(std::move(connection_)));
- transport_.reset(); // Indicate that the response has been received
+ transport_.reset(); // Indicate that the response has been received
return response;
}
@@ -159,13 +159,10 @@
headers_.insert(headers.begin(), headers.end());
}
-bool Request::AddRequestBody(const void* data, size_t size) {
- if (!SendRequestIfNeeded())
+bool Request::AddRequestBody(const void* data, size_t size, ErrorPtr* error) {
+ if (!SendRequestIfNeeded(error))
return false;
- bool ret = connection_->WriteRequestData(data, size);
- if (!ret)
- error_ = "Failed to send request data";
- return ret;
+ return connection_->WriteRequestData(data, size, error);
}
void Request::SetReferer(const char* referer) {
@@ -184,11 +181,7 @@
return user_agent_;
}
-std::string Request::GetErrorMessage() const {
- return error_;
-}
-
-bool Request::SendRequestIfNeeded() {
+bool Request::SendRequestIfNeeded(ErrorPtr* error) {
if (transport_) {
if (!connection_) {
chromeos::http::HeaderList headers = MapToVector(headers_);
@@ -222,24 +215,23 @@
connection_ = transport_->CreateConnection(transport_, request_url_,
method_, headers,
user_agent_, referer_,
- &error_);
+ error);
}
if (connection_)
return true;
} else {
- error_ = "HTTP response already received";
- LOG(ERROR) << error_;
+ Error::AddTo(error, chromeos::http::curl::kErrorDomain,
+ "request_already_received", "HTTP response already received");
}
return false;
}
-
-//**************************************************************************
-//********************** Response Class **********************
-//**************************************************************************
-Response::Response(std::unique_ptr<Connection> connection) :
- connection_(std::move(connection)) {
+// ************************************************************
+// ********************** Response Class **********************
+// ************************************************************
+Response::Response(std::unique_ptr<Connection> connection)
+ : connection_(std::move(connection)) {
VLOG(1) << "http::Response created";
// Response object doesn't have streaming interface for response data (yet),
// so read the data into a buffer and cache it.
@@ -248,8 +240,8 @@
response_data_.reserve(size);
unsigned char buffer[1024];
size_t read = 0;
- while (connection_->ReadResponseData(buffer, sizeof(buffer), &read) &&
- read > 0) {
+ while (connection_->ReadResponseData(buffer, sizeof(buffer),
+ &read, nullptr) && read > 0) {
response_data_.insert(response_data_.end(), buffer, buffer + read);
}
}
@@ -301,3 +293,5 @@
return std::string();
}
+} // namespace http
+} // namespace chromeos
diff --git a/buffet/http_request.h b/buffet/http_request.h
index 62f4a01..fd84e7a 100644
--- a/buffet/http_request.h
+++ b/buffet/http_request.h
@@ -8,12 +8,14 @@
#include <map>
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include <base/basictypes.h>
#include "buffet/http_connection.h"
#include "buffet/http_transport.h"
+#include "buffet/error.h"
namespace chromeos {
namespace http {
@@ -31,7 +33,7 @@
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
+} // namespace request_type
// HTTP request header names
namespace request_header {
@@ -74,7 +76,7 @@
extern const char kUserAgent[];
extern const char kVia[];
extern const char kWarning[];
-} // namespace request_header
+} // namespace request_header
// HTTP response header names
namespace response_header {
@@ -107,7 +109,7 @@
extern const char kVia[];
extern const char kWarning[];
extern const char kWwwAuthenticate[];
-} // namespace response_header
+} // namespace response_header
// HTTP request status (error) codes
namespace status_code {
@@ -128,7 +130,7 @@
static const int NoContent = 204;
// Request completed, but clear form
static const int ResetContent = 205;
- // Partial GET furfilled
+ // Partial GET fulfilled
static const int PartialContent = 206;
// Server couldn't decide what to return
@@ -193,9 +195,9 @@
static const int GatewayTimeout = 504;
// HTTP version not supported
static const int VersionNotSupported = 505;
-} // namespace status_code
+} // namespace status_code
-class Response; // Just a forward declarartion.
+class Response; // Just a forward declaration.
///////////////////////////////////////////////////////////////////////////////
// Request class is the main object used to set up and initiate an HTTP
@@ -204,7 +206,7 @@
// 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
+// and obtain the server response. The returned Response object can be
// used to inspect the response code, HTTP headers and/or response body.
///////////////////////////////////////////////////////////////////////////////
class Request {
@@ -232,7 +234,7 @@
void RemoveHeader(const char* header);
// Adds a request body. This is not to be used with GET method
- bool AddRequestBody(const void* data, size_t size);
+ bool AddRequestBody(const void* data, size_t size, ErrorPtr* error);
// 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
@@ -259,17 +261,13 @@
// 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;
+ // empty unique_ptr (null). In such a case, the additional error information
+ // can be returned through the optional supplied |error| parameter.
+ std::unique_ptr<Response> GetResponse(ErrorPtr* error);
private:
// Helper function to create an http::Connection and send off request headers.
- bool SendRequestIfNeeded();
+ bool SendRequestIfNeeded(ErrorPtr* error);
// Implementation that provides particular HTTP transport.
std::shared_ptr<Transport> transport_;
@@ -299,7 +297,7 @@
// 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.
+ // Sent to the server as "Range: " header.
std::vector<std::pair<uint64_t, uint64_t>> ranges_;
// range_value_omitted is used in |ranges_| list to indicate omitted value.
@@ -307,9 +305,6 @@
// of the data stream.
const uint64_t range_value_omitted = (uint64_t)-1;
- // Error message in case request fails completely.
- std::string error_;
-
DISALLOW_COPY_AND_ASSIGN(Request);
};
@@ -320,7 +315,7 @@
///////////////////////////////////////////////////////////////////////////////
class Response {
public:
- Response(std::unique_ptr<Connection> connection);
+ explicit Response(std::unique_ptr<Connection> connection);
~Response();
// Returns true if server returned a success code (status code below 400).
@@ -350,7 +345,7 @@
DISALLOW_COPY_AND_ASSIGN(Response);
};
-} // namespace http
-} // namespace chromeos
+} // namespace http
+} // namespace chromeos
-#endif // BUFFET_HTTP_REQUEST_H_
+#endif // BUFFET_HTTP_REQUEST_H_
diff --git a/buffet/http_transport.h b/buffet/http_transport.h
index 752dcbe..e9ea581 100644
--- a/buffet/http_transport.h
+++ b/buffet/http_transport.h
@@ -7,10 +7,13 @@
#include <memory>
#include <string>
+#include <utility>
#include <vector>
#include <base/basictypes.h>
+#include "buffet/error.h"
+
namespace chromeos {
namespace http {
@@ -39,13 +42,13 @@
const HeaderList& headers,
const std::string& user_agent,
const std::string& referer,
- std::string* error_msg) = 0;
+ ErrorPtr* error) = 0;
private:
DISALLOW_COPY_AND_ASSIGN(Transport);
};
-} // namespace http
-} // namespace chromeos
+} // namespace http
+} // namespace chromeos
-#endif // BUFFET_HTTP_TRANSPORT_H_
+#endif // BUFFET_HTTP_TRANSPORT_H_
diff --git a/buffet/http_transport_curl.cc b/buffet/http_transport_curl.cc
index 8c4b946..c15baae 100644
--- a/buffet/http_transport_curl.cc
+++ b/buffet/http_transport_curl.cc
@@ -9,8 +9,12 @@
#include "buffet/http_connection_curl.h"
#include "buffet/http_request.h"
-using namespace chromeos;
-using namespace chromeos::http::curl;
+using chromeos::http::curl::Transport;
+using chromeos::Error;
+
+namespace chromeos {
+
+const char http::curl::kErrorDomain[] = "http_transport";
Transport::Transport() {
VLOG(1) << "curl::Transport created";
@@ -27,12 +31,12 @@
const HeaderList& headers,
const std::string& user_agent,
const std::string& referer,
- std::string* error_msg) {
+ ErrorPtr* error) {
CURL* curl_handle = curl_easy_init();
if (!curl_handle) {
LOG(ERROR) << "Failed to initialize CURL";
- if (error_msg)
- *error_msg = "Failed to initialize CURL";
+ Error::AddTo(error, http::curl::kErrorDomain, "curl_init_failed",
+ "Failed to initialize CURL");
return std::unique_ptr<http::Connection>();
}
@@ -67,10 +71,10 @@
std::unique_ptr<http::Connection> connection(
new http::curl::Connection(curl_handle, method, transport));
CHECK(connection) << "Unable to create Connection object";
- if (!connection->SendHeaders(headers)) {
+ if (!connection->SendHeaders(headers, error)) {
connection.reset();
- if (error_msg)
- *error_msg = "Failed to send request headers";
}
return connection;
}
+
+} // namespace chromeos
diff --git a/buffet/http_transport_curl.h b/buffet/http_transport_curl.h
index f63f815..5295794 100644
--- a/buffet/http_transport_curl.h
+++ b/buffet/http_transport_curl.h
@@ -5,12 +5,16 @@
#ifndef BUFFET_HTTP_TRANSPORT_CURL_H_
#define BUFFET_HTTP_TRANSPORT_CURL_H_
+#include <string>
+
#include "buffet/http_transport.h"
namespace chromeos {
namespace http {
namespace curl {
+extern const char kErrorDomain[];
+
///////////////////////////////////////////////////////////////////////////////
// An implementation of http::Transport that uses libcurl for
// HTTP communications. This class (as http::Transport base)
@@ -30,14 +34,14 @@
const HeaderList& headers,
const std::string& user_agent,
const std::string& referer,
- std::string* error_msg) override;
+ ErrorPtr* error) override;
private:
DISALLOW_COPY_AND_ASSIGN(Transport);
};
-} // namespace curl
-} // namespace http
-} // namespace chromeos
+} // namespace curl
+} // namespace http
+} // namespace chromeos
-#endif // BUFFET_HTTP_TRANSPORT_CURL_H_
+#endif // BUFFET_HTTP_TRANSPORT_CURL_H_
diff --git a/buffet/http_transport_fake.cc b/buffet/http_transport_fake.cc
index 4a3a781..782404a 100644
--- a/buffet/http_transport_fake.cc
+++ b/buffet/http_transport_fake.cc
@@ -4,6 +4,8 @@
#include "buffet/http_transport_fake.h"
+#include <utility>
+
#include <base/json/json_reader.h>
#include <base/json/json_writer.h>
#include <base/logging.h>
@@ -14,8 +16,12 @@
#include "buffet/mime_utils.h"
#include "buffet/url_utils.h"
-using namespace chromeos;
-using namespace chromeos::http::fake;
+namespace chromeos {
+
+using http::fake::Transport;
+using http::fake::ServerRequestResponseBase;
+using http::fake::ServerRequest;
+using http::fake::ServerResponse;
Transport::Transport() {
VLOG(1) << "fake::Transport created";
@@ -32,7 +38,7 @@
const HeaderList& headers,
const std::string& user_agent,
const std::string& referer,
- std::string* error_msg) {
+ ErrorPtr* error) {
HeaderList headers_copy = headers;
if (!user_agent.empty()) {
headers_copy.push_back(std::make_pair(http::request_header::kUserAgent,
@@ -45,11 +51,8 @@
std::unique_ptr<http::Connection> connection(
new http::fake::Connection(url, method, transport));
CHECK(connection) << "Unable to create Connection object";
- if (!connection->SendHeaders(headers_copy)) {
+ if (!connection->SendHeaders(headers_copy, error))
connection.reset();
- if (error_msg)
- *error_msg = "Failed to send request headers";
- }
request_count_++;
return connection;
}
@@ -253,3 +256,5 @@
}
return std::string();
}
+
+} // namespace chromeos
diff --git a/buffet/http_transport_fake.h b/buffet/http_transport_fake.h
index a2fb04d..590cc66 100644
--- a/buffet/http_transport_fake.h
+++ b/buffet/http_transport_fake.h
@@ -5,7 +5,10 @@
#ifndef BUFFET_HTTP_TRANSPORT_FAKE_H_
#define BUFFET_HTTP_TRANSPORT_FAKE_H_
+#include <map>
+#include <string>
#include <type_traits>
+#include <vector>
#include <base/callback.h>
#include <base/values.h>
@@ -69,15 +72,15 @@
const HeaderList& headers,
const std::string& user_agent,
const std::string& referer,
- std::string* error_msg) override;
+ ErrorPtr* error) override;
private:
- DISALLOW_COPY_AND_ASSIGN(Transport);
-
// A list of user-supplied request handlers.
std::map<std::string, HandlerCallback> handlers_;
// Counter incremented each time a request is made.
int request_count_ = 0;
+
+ DISALLOW_COPY_AND_ASSIGN(Transport);
};
///////////////////////////////////////////////////////////////////////////////
@@ -129,8 +132,6 @@
std::string GetFormField(const std::string& field_name) const;
private:
- DISALLOW_COPY_AND_ASSIGN(ServerRequest);
-
// Request URL (without query string or URL fragment).
std::string url_;
// Request method
@@ -140,20 +141,22 @@
// Flag used on first request to GetFormField to parse the body of HTTP POST
// request with application/x-www-form-urlencoded content.
mutable bool form_fields_parsed_ = false;
+
+ DISALLOW_COPY_AND_ASSIGN(ServerRequest);
};
///////////////////////////////////////////////////////////////////////////////
// A container class that encapsulates all the HTTP server response information.
// The request handler will use this class to provide a response to the caller.
-// Call the Reply() or the approriate ReplyNNN() specialization to provide
+// Call the Reply() or the appropriate ReplyNNN() specialization to provide
// the response data. Additional calls to AddHeaders() can be made to provide
// custom response headers. The Reply-methods will already provide the
-// followig response headers:
+// following response headers:
// Content-Length
// Content-Type
///////////////////////////////////////////////////////////////////////////////
class ServerResponse : public ServerRequestResponseBase {
-public:
+ public:
ServerResponse() = default;
// Generic reply method.
@@ -207,14 +210,14 @@
std::string GetProtocolVersion() const { return protocol_version_; }
private:
- DISALLOW_COPY_AND_ASSIGN(ServerResponse);
-
int status_code_ = 0;
std::string protocol_version_ = "HTTP/1.1";
+
+ DISALLOW_COPY_AND_ASSIGN(ServerResponse);
};
-} // namespace fake
-} // namespace http
-} // namespace chromeos
+} // namespace fake
+} // namespace http
+} // namespace chromeos
-#endif // BUFFET_HTTP_TRANSPORT_FAKE_H_
+#endif // BUFFET_HTTP_TRANSPORT_FAKE_H_
diff --git a/buffet/http_utils.cc b/buffet/http_utils.cc
index d711cf8..9aba6e5 100644
--- a/buffet/http_utils.cc
+++ b/buffet/http_utils.cc
@@ -5,10 +5,10 @@
#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 <base/values.h>
#include "buffet/mime_utils.h"
#include "buffet/data_encoding.h"
@@ -16,36 +16,43 @@
namespace chromeos {
namespace http {
+const char kErrorDomainJSON[] = "json_parser";
+
std::unique_ptr<Response> Get(const std::string& url,
const HeaderList& headers,
- std::shared_ptr<Transport> transport) {
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
return SendRequest(request_type::kGet, url, nullptr, 0, nullptr,
- headers, transport);
+ headers, transport, error);
}
std::string GetAsString(const std::string& url,
const HeaderList& headers,
- std::shared_ptr<Transport> transport) {
- auto resp = Get(url, headers, transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
+ auto resp = Get(url, headers, transport, error);
return resp ? resp->GetDataAsString() : std::string();
}
std::unique_ptr<Response> Head(const std::string& url,
- std::shared_ptr<Transport> transport) {
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
Request request(url, request_type::kHead, transport);
- return request.GetResponse();
+ return request.GetResponse(error);
}
std::unique_ptr<Response> PostText(const std::string& url,
const char* data,
const char* mime_type,
const HeaderList& headers,
- std::shared_ptr<Transport> transport) {
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
if (mime_type == nullptr) {
mime_type = chromeos::mime::application::kWwwFormUrlEncoded;
}
- return PostBinary(url, data, strlen(data), mime_type, headers, transport);
+ return PostBinary(url, data, strlen(data), mime_type, headers, transport,
+ error);
}
std::unique_ptr<Response> SendRequest(const char * method,
@@ -54,7 +61,8 @@
size_t data_size,
const char* mime_type,
const HeaderList& headers,
- std::shared_ptr<Transport> transport) {
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
Request request(url, method, transport);
request.AddHeaders(headers);
if (data_size > 0) {
@@ -62,34 +70,38 @@
mime_type = chromeos::mime::application::kOctet_stream;
}
request.SetContentType(mime_type);
- request.AddRequestBody(data, data_size);
+ if (!request.AddRequestBody(data, data_size, error))
+ return std::unique_ptr<Response>();
}
- return request.GetResponse();
+ return request.GetResponse(error);
}
std::unique_ptr<Response> PostBinary(const std::string & url, const void* data,
size_t data_size, const char* mime_type,
const HeaderList& headers,
- std::shared_ptr<Transport> transport) {
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
return SendRequest(request_type::kPost, url,
- data, data_size, mime_type, headers, transport);
+ data, data_size, mime_type, headers, transport, error);
}
std::unique_ptr<Response> PostFormData(const std::string& url,
const FormFieldList& data,
const HeaderList& headers,
- std::shared_ptr<Transport> transport) {
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
std::string encoded_data = chromeos::data_encoding::WebParamsEncode(data);
return PostBinary(url, encoded_data.c_str(), encoded_data.size(),
chromeos::mime::application::kWwwFormUrlEncoded,
- headers, transport);
+ headers, transport, error);
}
std::unique_ptr<Response> PostJson(const std::string& url,
const base::Value* json,
const HeaderList& headers,
- std::shared_ptr<Transport> transport) {
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
std::string data;
if (json)
base::JSONWriter::Write(json, &data);
@@ -97,13 +109,14 @@
mime::parameters::kCharset,
"utf-8");
return PostBinary(url, data.c_str(), data.size(),
- mime_type.c_str(), headers, transport);
+ mime_type.c_str(), headers, transport, error);
}
std::unique_ptr<Response> PatchJson(const std::string& url,
const base::Value* json,
const HeaderList& headers,
- std::shared_ptr<Transport> transport) {
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
std::string data;
if (json)
base::JSONWriter::Write(json, &data);
@@ -111,46 +124,44 @@
mime::parameters::kCharset,
"utf-8");
return SendRequest(request_type::kPatch, url, data.c_str(), data.size(),
- mime_type.c_str(), headers, transport);
+ mime_type.c_str(), headers, transport, error);
}
std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
- const Response* response, int* status_code, std::string* error_message) {
- std::unique_ptr<base::DictionaryValue> dict;
- if (response) {
- if (status_code)
- *status_code = response->GetStatusCode();
+ const Response* response, int* status_code, ErrorPtr* error) {
+ if (!response)
+ return std::unique_ptr<base::DictionaryValue>();
- // Make sure we have a correct content type. Do not try to parse
- // binary files, or HTML output. Limit to application/json and text/plain.
- auto content_type = mime::RemoveParameters(response->GetContentType());
- if (content_type == mime::application::kJson ||
- content_type == mime::text::kPlain) {
- std::string json = response->GetDataAsString();
- auto value = base::JSONReader::ReadAndReturnError(json,
- base::JSON_PARSE_RFC,
- nullptr,
- error_message);
- if (value) {
- base::DictionaryValue* dict_value = nullptr;
- if (value->GetAsDictionary(&dict_value)) {
- dict.reset(dict_value);
- } else {
- delete value;
- if (error_message) {
- *error_message = "Reponse is not a valid JSON object";
- }
- }
- }
- }
- else if (error_message) {
- *error_message = "Unexpected response content type: " + content_type;
- }
- } else if (error_message) {
- *error_message = "NULL response.";
+ if (status_code)
+ *status_code = response->GetStatusCode();
+
+ // Make sure we have a correct content type. Do not try to parse
+ // binary files, or HTML output. Limit to application/json and text/plain.
+ auto content_type = mime::RemoveParameters(response->GetContentType());
+ if (content_type != mime::application::kJson &&
+ content_type != mime::text::kPlain) {
+ Error::AddTo(error, kErrorDomainJSON, "non_json_content_type",
+ "Unexpected response content type: " + content_type);
+ return std::unique_ptr<base::DictionaryValue>();
}
- return dict;
+
+ std::string json = response->GetDataAsString();
+ std::string error_message;
+ base::Value* value = base::JSONReader::ReadAndReturnError(
+ json, base::JSON_PARSE_RFC, nullptr, &error_message);
+ if (!value) {
+ Error::AddTo(error, kErrorDomainJSON, "json_parse_error", error_message);
+ return std::unique_ptr<base::DictionaryValue>();
+ }
+ base::DictionaryValue* dict_value = nullptr;
+ if (!value->GetAsDictionary(&dict_value)) {
+ delete value;
+ Error::AddTo(error, kErrorDomainJSON, "json_object_error",
+ "Response is not a valid JSON object");
+ return std::unique_ptr<base::DictionaryValue>();
+ }
+ return std::unique_ptr<base::DictionaryValue>(dict_value);
}
-} // namespace http
-} // namespace chromeos
+} // namespace http
+} // namespace chromeos
diff --git a/buffet/http_utils.h b/buffet/http_utils.h
index c10805a..e44b378 100644
--- a/buffet/http_utils.h
+++ b/buffet/http_utils.h
@@ -5,8 +5,13 @@
#ifndef BUFFET_HTTP_UTILS_H_
#define BUFFET_HTTP_UTILS_H_
+#include "buffet/error.h"
#include "buffet/http_request.h"
+#include <string>
+#include <utility>
+#include <vector>
+
namespace base {
class Value;
class DictionaryValue;
@@ -15,6 +20,8 @@
namespace chromeos {
namespace http {
+extern const char kErrorDomainJSON[];
+
typedef std::vector<std::pair<std::string, std::string>> FormFieldList;
////////////////////////////////////////////////////////////////////////////////
@@ -32,14 +39,17 @@
std::unique_ptr<Response> SendRequest(
const char* method, const std::string& url,
const void* data, size_t data_size, const char* mime_type,
- const HeaderList& headers, std::shared_ptr<Transport> transport);
+ const HeaderList& headers, std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
// Performs a simple GET request and returns the data as a string.
std::string GetAsString(const std::string& url, const HeaderList& headers,
- std::shared_ptr<Transport> transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
inline std::string GetAsString(const std::string& url,
- std::shared_ptr<Transport> transport) {
- return GetAsString(url, HeaderList(), transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
+ return GetAsString(url, HeaderList(), transport, error);
}
// Performs a GET request. Success status, returned data and additional
@@ -47,17 +57,20 @@
// the returned Response object.
std::unique_ptr<Response> Get(const std::string& url,
const HeaderList& headers,
- std::shared_ptr<Transport> transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
inline std::unique_ptr<Response> Get(
- const std::string& url, std::shared_ptr<Transport> transport) {
- return Get(url, HeaderList(), transport);
+ const std::string& url, std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
+ return Get(url, HeaderList(), transport, error);
}
// 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(const std::string& url,
- std::shared_ptr<Transport> transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
// Performs a POST request with binary data. Success status, returned data
// and additional information (such as returned HTTP headers) can be obtained
@@ -68,18 +81,21 @@
size_t data_size,
const char* mime_type,
const HeaderList& headers,
- std::shared_ptr<Transport> transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
inline std::unique_ptr<Response> PostBinary(
const std::string& url, const void* data, size_t data_size,
- const char* mime_type, std::shared_ptr<Transport> transport) {
- return PostBinary(url, data, data_size, mime_type, HeaderList(), transport);
+ const char* mime_type, std::shared_ptr<Transport> transport,
+ ErrorPtr* error) {
+ return PostBinary(url, data, data_size, mime_type, HeaderList(), transport,
+ error);
}
inline std::unique_ptr<Response> PostBinary(
const std::string& url, const void* data, size_t data_size,
- std::shared_ptr<Transport> transport) {
- return PostBinary(url, data, data_size, nullptr, transport);
+ std::shared_ptr<Transport> transport, ErrorPtr* error) {
+ return PostBinary(url, data, data_size, nullptr, transport, error);
}
// Performs a POST request with text data. Success status, returned data
@@ -91,18 +107,19 @@
const char* data,
const char* mime_type,
const HeaderList& headers,
- std::shared_ptr<Transport> transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
inline std::unique_ptr<Response> PostText(
const std::string& url, const char* data, const char* mime_type,
- std::shared_ptr<Transport> transport) {
- return PostText(url, data, mime_type, HeaderList(), transport);
+ std::shared_ptr<Transport> transport, ErrorPtr* error) {
+ return PostText(url, data, mime_type, HeaderList(), transport, error);
}
inline std::unique_ptr<Response> PostText(
const std::string& url, const char* data,
- std::shared_ptr<Transport> transport) {
- return PostText(url, data, nullptr, transport);
+ std::shared_ptr<Transport> transport, ErrorPtr* error) {
+ return PostText(url, data, nullptr, transport, error);
}
// Performs a POST request with form data. Success status, returned data
@@ -111,12 +128,13 @@
// pairs. The data is posed as "application/x-www-form-urlencoded".
std::unique_ptr<Response> PostFormData(
const std::string& url, const FormFieldList& data,
- const HeaderList& headers, std::shared_ptr<Transport> transport);
+ const HeaderList& headers, std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
inline std::unique_ptr<Response> PostFormData(
const std::string& url, const FormFieldList& data,
- std::shared_ptr<Transport> transport) {
- return PostFormData(url, data, HeaderList(), transport);
+ std::shared_ptr<Transport> transport, ErrorPtr* error) {
+ return PostFormData(url, data, HeaderList(), transport, error);
}
// Performs a POST request with JSON data. Success status, returned data
@@ -126,12 +144,13 @@
std::unique_ptr<Response> PostJson(const std::string& url,
const base::Value* json,
const HeaderList& headers,
- std::shared_ptr<Transport> transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
inline std::unique_ptr<Response> PostJson(
const std::string& url, const base::Value* json,
- std::shared_ptr<Transport> transport) {
- return PostJson(url, json, HeaderList(), transport);
+ std::shared_ptr<Transport> transport, ErrorPtr* error) {
+ return PostJson(url, json, HeaderList(), transport, error);
}
// Performs a PATCH request with JSON data. Success status, returned data
@@ -141,21 +160,22 @@
std::unique_ptr<Response> PatchJson(const std::string& url,
const base::Value* json,
const HeaderList& headers,
- std::shared_ptr<Transport> transport);
+ std::shared_ptr<Transport> transport,
+ ErrorPtr* error);
inline std::unique_ptr<Response> PatchJson(
const std::string& url, const base::Value* json,
- std::shared_ptr<Transport> transport) {
- return PatchJson(url, json, HeaderList(), transport);
+ std::shared_ptr<Transport> transport, ErrorPtr* error) {
+ return PatchJson(url, json, HeaderList(), transport, error);
}
// Given an http::Response object, parse the body data into Json object.
-// Returns null if failed. Optional |error_message| can be passed in to
+// Returns null if failed. Optional |error| can be passed in to
// get the extended error information as to why the parse failed.
std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
- const Response* response, int* status_code, std::string* error_message);
+ const Response* response, int* status_code, ErrorPtr* error);
-} // namespace http
-} // namespace chromeos
+} // namespace http
+} // namespace chromeos
-#endif // BUFFET_HTTP_UTILS_H_
+#endif // BUFFET_HTTP_UTILS_H_
diff --git a/buffet/http_utils_unittest.cc b/buffet/http_utils_unittest.cc
index 6438660..4a415d2 100644
--- a/buffet/http_utils_unittest.cc
+++ b/buffet/http_utils_unittest.cc
@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <string>
+#include <vector>
+
#include <base/values.h>
#include <gtest/gtest.h>
@@ -12,8 +15,8 @@
#include "buffet/string_utils.h"
#include "buffet/url_utils.h"
-using namespace chromeos;
-using namespace chromeos::http;
+using namespace chromeos; // NOLINT(build/namespaces)
+using namespace chromeos::http; // NOLINT(build/namespaces)
static const char kFakeUrl[] = "http://localhost";
static const char kEchoUrl[] = "http://localhost/echo";
@@ -40,11 +43,11 @@
base::Bind(EchoDataHandler));
// Test binary data round-tripping.
- std::vector<unsigned char> custom_data{0xFF, 0x00, 0x80, 0x40, 0xC0, 0x7F};
+ std::vector<unsigned char> custom_data({0xFF, 0x00, 0x80, 0x40, 0xC0, 0x7F});
auto response = http::SendRequest(request_type::kPost, kEchoUrl,
custom_data.data(), custom_data.size(),
mime::application::kOctet_stream,
- HeaderList(), transport);
+ HeaderList(), transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::application::kOctet_stream, response->GetContentType());
EXPECT_EQ(custom_data.size(), response->GetData().size());
@@ -56,13 +59,13 @@
transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler));
// Test binary data round-tripping.
- std::vector<unsigned char> custom_data{0xFF, 0x00, 0x80, 0x40, 0xC0, 0x7F};
+ std::vector<unsigned char> custom_data({0xFF, 0x00, 0x80, 0x40, 0xC0, 0x7F});
// Check the correct HTTP method used.
auto response = http::SendRequest(request_type::kPost, kMethodEchoUrl,
custom_data.data(), custom_data.size(),
mime::application::kOctet_stream,
- HeaderList(), transport);
+ HeaderList(), transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::text::kPlain, response->GetContentType());
EXPECT_EQ(request_type::kPost, response->GetDataAsString());
@@ -74,7 +77,7 @@
auto response = http::SendRequest(request_type::kGet, kMethodEchoUrl,
nullptr, 0, nullptr,
- HeaderList(), transport);
+ HeaderList(), transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::text::kPlain, response->GetContentType());
EXPECT_EQ(request_type::kGet, response->GetDataAsString());
@@ -86,7 +89,7 @@
auto response = http::SendRequest(request_type::kPut, kMethodEchoUrl,
nullptr, 0, nullptr,
- HeaderList(), transport);
+ HeaderList(), transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::text::kPlain, response->GetContentType());
EXPECT_EQ(request_type::kPut, response->GetDataAsString());
@@ -97,7 +100,7 @@
// Test failed response (URL not found).
auto response = http::SendRequest(request_type::kGet, "http://blah.com",
nullptr, 0, nullptr,
- HeaderList(), transport);
+ HeaderList(), transport, nullptr);
EXPECT_FALSE(response->IsSuccessful());
EXPECT_EQ(status_code::NotFound, response->GetStatusCode());
}
@@ -123,7 +126,7 @@
mime::application::kOctet_stream, {
{request_header::kCookie, "flavor=vanilla"},
{request_header::kIfMatch, "*"},
- }, transport);
+ }, transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::application::kJson,
mime::RemoveParameters(response->GetContentType()));
@@ -161,15 +164,16 @@
transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler));
// Make sure Get/GetAsString actually do the GET request
- auto response = http::Get(kMethodEchoUrl, transport);
+ auto response = http::Get(kMethodEchoUrl, transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::text::kPlain, response->GetContentType());
EXPECT_EQ(request_type::kGet, response->GetDataAsString());
- EXPECT_EQ(request_type::kGet, http::GetAsString(kMethodEchoUrl, transport));
+ EXPECT_EQ(request_type::kGet,
+ http::GetAsString(kMethodEchoUrl, transport, nullptr));
for (std::string data : {"blah", "some data", ""}) {
std::string url = url::AppendQueryParam(kFakeUrl, "test", data);
- EXPECT_EQ(data, http::GetAsString(url, transport));
+ EXPECT_EQ(data, http::GetAsString(url, transport, nullptr));
}
}
@@ -186,10 +190,10 @@
std::shared_ptr<fake::Transport> transport(new fake::Transport);
transport->AddHandler(kFakeUrl, request_type::kHead, base::Bind(HeadHandler));
- auto response = http::Head(kFakeUrl, transport);
+ auto response = http::Head(kFakeUrl, transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::text::kPlain, response->GetContentType());
- EXPECT_EQ("", response->GetDataAsString()); // Must not have actual body.
+ EXPECT_EQ("", response->GetDataAsString()); // Must not have actual body.
EXPECT_EQ("4", response->GetHeader(request_header::kContentLength));
}
@@ -205,7 +209,7 @@
// Sum up all the bytes.
int sum = std::accumulate(data.begin(), data.end(), 0);
- EXPECT_EQ(32640, sum); // sum(i, i => [0, 255]) = 32640.
+ EXPECT_EQ(32640, sum); // sum(i, i => [0, 255]) = 32640.
response->ReplyText(status_code::Ok, "", mime::text::kPlain);
};
@@ -218,7 +222,7 @@
std::generate(data.begin(), data.end(), [&counter]() { return ++counter; });
auto response = http::PostBinary(kFakeUrl, data.data(), data.size(),
- transport);
+ transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
}
@@ -239,7 +243,7 @@
transport->AddHandler(kFakeUrl, request_type::kPost, base::Bind(PostHandler));
auto response = http::PostText(kFakeUrl, fake_data.c_str(),
- mime::text::kPlain, transport);
+ mime::text::kPlain, transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::text::kPlain, response->GetContentType());
EXPECT_EQ(fake_data, response->GetDataAsString());
@@ -253,7 +257,7 @@
auto response = http::PostFormData(kFakeUrl, {
{"key", "value"},
{"field", "field value"},
- }, transport);
+ }, transport, nullptr);
EXPECT_TRUE(response->IsSuccessful());
EXPECT_EQ(mime::application::kWwwFormUrlEncoded, response->GetContentType());
EXPECT_EQ("key=value&field=field+value", response->GetDataAsString());
@@ -279,7 +283,7 @@
std::string value;
// Test POST
- auto response = http::PostJson(kFakeUrl, &json, transport);
+ auto response = http::PostJson(kFakeUrl, &json, transport, nullptr);
auto resp_json = http::ParseJsonResponse(response.get(), nullptr, nullptr);
EXPECT_NE(nullptr, resp_json.get());
EXPECT_TRUE(resp_json->GetString("method", &value));
@@ -288,7 +292,7 @@
EXPECT_EQ("{\"key1\":\"val1\",\"key2\":\"val2\"}", value);
// Test PATCH
- response = http::PatchJson(kFakeUrl, &json, transport);
+ response = http::PatchJson(kFakeUrl, &json, transport, nullptr);
resp_json = http::ParseJsonResponse(response.get(), nullptr, nullptr);
EXPECT_NE(nullptr, resp_json.get());
EXPECT_TRUE(resp_json->GetString("method", &value));
@@ -312,7 +316,7 @@
auto response = http::PostFormData(kFakeUrl, {
{"code", pair.first},
{"value", pair.second},
- }, transport);
+ }, transport, nullptr);
int code = 0;
auto json = http::ParseJsonResponse(response.get(), &code, nullptr);
EXPECT_NE(nullptr, json.get());
@@ -322,8 +326,8 @@
EXPECT_EQ(pair.second, value);
}
- // Test invalid (non-JSON) reponse.
- auto response = http::Get("http://bad.url", transport);
+ // Test invalid (non-JSON) response.
+ auto response = http::Get("http://bad.url", transport, nullptr);
EXPECT_EQ(status_code::NotFound, response->GetStatusCode());
EXPECT_EQ(mime::text::kHtml, response->GetContentType());
int code = 0;
diff --git a/buffet/manager.cc b/buffet/manager.cc
index 7e64223..2195b83 100644
--- a/buffet/manager.cc
+++ b/buffet/manager.cc
@@ -4,6 +4,9 @@
#include "buffet/manager.h"
+#include <map>
+#include <string>
+
#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/json/json_writer.h>
@@ -14,8 +17,10 @@
#include "buffet/async_event_sequencer.h"
#include "buffet/dbus_constants.h"
#include "buffet/dbus_utils.h"
+#include "buffet/error.h"
using buffet::dbus_utils::GetBadArgsError;
+using buffet::dbus_utils::GetDBusError;
namespace buffet {
@@ -122,13 +127,26 @@
LOG(INFO) << "Received call to Manager.CheckDeviceRegistered()";
- bool registered = device_info_.CheckRegistration();
+ chromeos::ErrorPtr error;
+ bool registered = device_info_.CheckRegistration(&error);
+ // If it fails due to any reason other than 'device not registered',
+ // treat it as a real error and report it to the caller.
+ if (!registered &&
+ !error->HasError(kErrorDomainGCD, "device_not_registered")) {
+ return GetDBusError(method_call, error.get());
+ }
+ std::string device_id;
+ if (registered) {
+ device_id = device_info_.GetDeviceId(&error);
+ if (device_id.empty())
+ return GetDBusError(method_call, error.get());
+ }
// Send back our response.
scoped_ptr<dbus::Response> response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter writer(response.get());
- writer.AppendString(registered ? device_info_.GetDeviceId() : std::string());
+ writer.AppendString(device_id);
return response.Pass();
}
@@ -144,9 +162,12 @@
LOG(INFO) << "Received call to Manager.GetDeviceInfo()";
std::string device_info_str;
- std::unique_ptr<base::Value> device_info = device_info_.GetDeviceInfo();
- if (device_info)
- base::JSONWriter::Write(device_info.get(), &device_info_str);
+ chromeos::ErrorPtr error;
+ auto device_info = device_info_.GetDeviceInfo(&error);
+ if (!device_info)
+ return GetDBusError(method_call, error.get());
+
+ base::JSONWriter::Write(device_info.get(), &device_info_str);
// Send back our response.
scoped_ptr<dbus::Response> response(
@@ -186,10 +207,10 @@
LOG(INFO) << "Received call to Manager.StartRegisterDevice()";
- std::string error_msg;
- std::string id = device_info_.StartRegistration(params, &error_msg);
- if(id.empty())
- return GetBadArgsError(method_call, error_msg);
+ chromeos::ErrorPtr error;
+ std::string id = device_info_.StartRegistration(params, &error);
+ if (id.empty())
+ return GetDBusError(method_call, error.get());
// Send back our response.
scoped_ptr<dbus::Response> response(
@@ -217,13 +238,19 @@
}
LOG(INFO) << "Received call to Manager.FinishRegisterDevice()";
- bool success = device_info_.FinishRegistration(user_auth_code);
+ chromeos::ErrorPtr error;
+ if (!device_info_.FinishRegistration(user_auth_code, &error))
+ return GetDBusError(method_call, error.get());
+
+ std::string device_id = device_info_.GetDeviceId(&error);
+ if (device_id.empty())
+ return GetDBusError(method_call, error.get());
// Send back our response.
scoped_ptr<dbus::Response> response(
dbus::Response::FromMethodCall(method_call));
dbus::MessageWriter writer(response.get());
- writer.AppendString(success ? device_info_.GetDeviceId() : std::string());
+ writer.AppendString(device_id);
return response.Pass();
}