You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
438 lines
16 KiB
438 lines
16 KiB
// 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 <brillo/http/http_utils.h>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <base/bind.h>
|
|
#include <base/json/json_reader.h>
|
|
#include <base/json/json_writer.h>
|
|
#include <base/values.h>
|
|
#include <brillo/data_encoding.h>
|
|
#include <brillo/errors/error_codes.h>
|
|
#include <brillo/mime_utils.h>
|
|
#include <brillo/streams/memory_stream.h>
|
|
|
|
using brillo::mime::AppendParameter;
|
|
using brillo::mime::RemoveParameters;
|
|
|
|
namespace brillo {
|
|
namespace http {
|
|
|
|
std::unique_ptr<Response> GetAndBlock(const std::string& url,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
return SendRequestWithNoDataAndBlock(
|
|
request_type::kGet, url, headers, transport, error);
|
|
}
|
|
|
|
RequestID Get(const std::string& url,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
return SendRequestWithNoData(request_type::kGet,
|
|
url,
|
|
headers,
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
std::unique_ptr<Response> HeadAndBlock(const std::string& url,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
return SendRequestWithNoDataAndBlock(
|
|
request_type::kHead, url, {}, transport, error);
|
|
}
|
|
|
|
RequestID Head(const std::string& url,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
return SendRequestWithNoData(request_type::kHead,
|
|
url,
|
|
{},
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
std::unique_ptr<Response> PostTextAndBlock(const std::string& url,
|
|
const std::string& data,
|
|
const std::string& mime_type,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
return PostBinaryAndBlock(
|
|
url, data.data(), data.size(), mime_type, headers, transport, error);
|
|
}
|
|
|
|
RequestID PostText(const std::string& url,
|
|
const std::string& data,
|
|
const std::string& mime_type,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
return PostBinary(url,
|
|
data.data(),
|
|
data.size(),
|
|
mime_type,
|
|
headers,
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
std::unique_ptr<Response> SendRequestAndBlock(
|
|
const std::string& method,
|
|
const std::string& url,
|
|
const void* data,
|
|
size_t data_size,
|
|
const std::string& mime_type,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
Request request(url, method, transport);
|
|
request.AddHeaders(headers);
|
|
if (data_size > 0) {
|
|
CHECK(!mime_type.empty()) << "MIME type must be specified if request body "
|
|
"message is provided";
|
|
request.SetContentType(mime_type);
|
|
if (!request.AddRequestBody(data, data_size, error))
|
|
return std::unique_ptr<Response>();
|
|
}
|
|
return request.GetResponseAndBlock(error);
|
|
}
|
|
|
|
std::unique_ptr<Response> SendRequestWithNoDataAndBlock(
|
|
const std::string& method,
|
|
const std::string& url,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
return SendRequestAndBlock(
|
|
method, url, nullptr, 0, {}, headers, transport, error);
|
|
}
|
|
|
|
RequestID SendRequest(const std::string& method,
|
|
const std::string& url,
|
|
StreamPtr stream,
|
|
const std::string& mime_type,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
Request request(url, method, transport);
|
|
request.AddHeaders(headers);
|
|
if (stream && (!stream->CanGetSize() || stream->GetRemainingSize() > 0)) {
|
|
CHECK(!mime_type.empty()) << "MIME type must be specified if request body "
|
|
"message is provided";
|
|
request.SetContentType(mime_type);
|
|
brillo::ErrorPtr error;
|
|
if (!request.AddRequestBody(std::move(stream), &error)) {
|
|
transport->RunCallbackAsync(
|
|
FROM_HERE, base::Bind(error_callback,
|
|
0, base::Owned(error.release())));
|
|
return 0;
|
|
}
|
|
}
|
|
return request.GetResponse(success_callback, error_callback);
|
|
}
|
|
|
|
RequestID SendRequest(const std::string& method,
|
|
const std::string& url,
|
|
const void* data,
|
|
size_t data_size,
|
|
const std::string& mime_type,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
return SendRequest(method,
|
|
url,
|
|
MemoryStream::OpenCopyOf(data, data_size, nullptr),
|
|
mime_type,
|
|
headers,
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
RequestID SendRequestWithNoData(const std::string& method,
|
|
const std::string& url,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
return SendRequest(method,
|
|
url,
|
|
{},
|
|
{},
|
|
headers,
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
std::unique_ptr<Response> PostBinaryAndBlock(
|
|
const std::string& url,
|
|
const void* data,
|
|
size_t data_size,
|
|
const std::string& mime_type,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
return SendRequestAndBlock(request_type::kPost,
|
|
url,
|
|
data,
|
|
data_size,
|
|
mime_type,
|
|
headers,
|
|
transport,
|
|
error);
|
|
}
|
|
|
|
RequestID PostBinary(const std::string& url,
|
|
StreamPtr stream,
|
|
const std::string& mime_type,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
return SendRequest(request_type::kPost,
|
|
url,
|
|
std::move(stream),
|
|
mime_type,
|
|
headers,
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
RequestID PostBinary(const std::string& url,
|
|
const void* data,
|
|
size_t data_size,
|
|
const std::string& mime_type,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
return SendRequest(request_type::kPost,
|
|
url,
|
|
data,
|
|
data_size,
|
|
mime_type,
|
|
headers,
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
std::unique_ptr<Response> PostFormDataAndBlock(
|
|
const std::string& url,
|
|
const FormFieldList& data,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
std::string encoded_data = brillo::data_encoding::WebParamsEncode(data);
|
|
return PostBinaryAndBlock(url,
|
|
encoded_data.c_str(),
|
|
encoded_data.size(),
|
|
brillo::mime::application::kWwwFormUrlEncoded,
|
|
headers,
|
|
transport,
|
|
error);
|
|
}
|
|
|
|
std::unique_ptr<Response> PostFormDataAndBlock(
|
|
const std::string& url,
|
|
std::unique_ptr<FormData> form_data,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
Request request(url, request_type::kPost, transport);
|
|
request.AddHeaders(headers);
|
|
if (!request.AddRequestBodyAsFormData(std::move(form_data), error))
|
|
return std::unique_ptr<Response>();
|
|
return request.GetResponseAndBlock(error);
|
|
}
|
|
|
|
RequestID PostFormData(const std::string& url,
|
|
const FormFieldList& data,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
std::string encoded_data = brillo::data_encoding::WebParamsEncode(data);
|
|
return PostBinary(url,
|
|
encoded_data.c_str(),
|
|
encoded_data.size(),
|
|
brillo::mime::application::kWwwFormUrlEncoded,
|
|
headers,
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
RequestID PostFormData(const std::string& url,
|
|
std::unique_ptr<FormData> form_data,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
Request request(url, request_type::kPost, transport);
|
|
request.AddHeaders(headers);
|
|
brillo::ErrorPtr error;
|
|
if (!request.AddRequestBodyAsFormData(std::move(form_data), &error)) {
|
|
transport->RunCallbackAsync(
|
|
FROM_HERE, base::Bind(error_callback, 0, base::Owned(error.release())));
|
|
return 0;
|
|
}
|
|
return request.GetResponse(success_callback, error_callback);
|
|
}
|
|
|
|
std::unique_ptr<Response> PostJsonAndBlock(const std::string& url,
|
|
const base::Value* json,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
std::string data;
|
|
if (json)
|
|
base::JSONWriter::Write(*json, &data);
|
|
std::string mime_type = AppendParameter(brillo::mime::application::kJson,
|
|
brillo::mime::parameters::kCharset,
|
|
"utf-8");
|
|
return PostBinaryAndBlock(
|
|
url, data.c_str(), data.size(), mime_type, headers, transport, error);
|
|
}
|
|
|
|
RequestID PostJson(const std::string& url,
|
|
std::unique_ptr<base::Value> json,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
std::string data;
|
|
if (json)
|
|
base::JSONWriter::Write(*json, &data);
|
|
std::string mime_type = AppendParameter(brillo::mime::application::kJson,
|
|
brillo::mime::parameters::kCharset,
|
|
"utf-8");
|
|
return PostBinary(url,
|
|
data.c_str(),
|
|
data.size(),
|
|
mime_type,
|
|
headers,
|
|
transport,
|
|
success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
std::unique_ptr<Response> PatchJsonAndBlock(
|
|
const std::string& url,
|
|
const base::Value* json,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
brillo::ErrorPtr* error) {
|
|
std::string data;
|
|
if (json)
|
|
base::JSONWriter::Write(*json, &data);
|
|
std::string mime_type = AppendParameter(brillo::mime::application::kJson,
|
|
brillo::mime::parameters::kCharset,
|
|
"utf-8");
|
|
return SendRequestAndBlock(request_type::kPatch,
|
|
url,
|
|
data.c_str(),
|
|
data.size(),
|
|
mime_type,
|
|
headers,
|
|
transport,
|
|
error);
|
|
}
|
|
|
|
RequestID PatchJson(const std::string& url,
|
|
std::unique_ptr<base::Value> json,
|
|
const HeaderList& headers,
|
|
std::shared_ptr<Transport> transport,
|
|
const SuccessCallback& success_callback,
|
|
const ErrorCallback& error_callback) {
|
|
std::string data;
|
|
if (json)
|
|
base::JSONWriter::Write(*json, &data);
|
|
std::string mime_type =
|
|
AppendParameter(brillo::mime::application::kJson,
|
|
brillo::mime::parameters::kCharset, "utf-8");
|
|
return SendRequest(request_type::kPatch, url, data.c_str(), data.size(),
|
|
mime_type, headers, transport, success_callback,
|
|
error_callback);
|
|
}
|
|
|
|
std::unique_ptr<base::DictionaryValue> ParseJsonResponse(
|
|
Response* response,
|
|
int* status_code,
|
|
brillo::ErrorPtr* error) {
|
|
std::unique_ptr<base::DictionaryValue> result;
|
|
if (!response)
|
|
return result;
|
|
|
|
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 = RemoveParameters(response->GetContentType());
|
|
if (content_type != brillo::mime::application::kJson &&
|
|
content_type != brillo::mime::text::kPlain) {
|
|
brillo::Error::AddTo(error, FROM_HERE, brillo::errors::json::kDomain,
|
|
"non_json_content_type",
|
|
"Unexpected response content type: " + content_type);
|
|
return result;
|
|
}
|
|
|
|
std::string json = response->ExtractDataAsString();
|
|
std::string error_message;
|
|
auto value = base::JSONReader::ReadAndReturnError(json, base::JSON_PARSE_RFC,
|
|
nullptr, &error_message);
|
|
if (!value) {
|
|
brillo::Error::AddToPrintf(error, FROM_HERE, brillo::errors::json::kDomain,
|
|
brillo::errors::json::kParseError,
|
|
"Error '%s' occurred parsing JSON string '%s'",
|
|
error_message.c_str(), json.c_str());
|
|
return result;
|
|
}
|
|
result = base::DictionaryValue::From(std::move(value));
|
|
if (!result) {
|
|
brillo::Error::AddToPrintf(error, FROM_HERE, brillo::errors::json::kDomain,
|
|
brillo::errors::json::kObjectExpected,
|
|
"Response is not a valid JSON object: '%s'",
|
|
json.c_str());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string GetCanonicalHeaderName(const std::string& name) {
|
|
std::string canonical_name = name;
|
|
bool word_begin = true;
|
|
for (char& c : canonical_name) {
|
|
if (c == '-') {
|
|
word_begin = true;
|
|
} else {
|
|
if (word_begin) {
|
|
c = toupper(c);
|
|
} else {
|
|
c = tolower(c);
|
|
}
|
|
word_begin = false;
|
|
}
|
|
}
|
|
return canonical_name;
|
|
}
|
|
|
|
} // namespace http
|
|
} // namespace brillo
|