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/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