libchromeos: Move http_utils from Buffet to libchromeos http_utils have dependency on libcurl, so not to add this dependency onto libchromeos-core, created a new sub-library, libchromeos-http where all http_utils production code goes. There are some fake classes used for testing purposes, so added them to a new static library, libchromeos-test-271506.a and created a separate package for it, so that other components can link to this test library when then needed, independently from the rest of libchromeos. BUG=chromium:405714 TEST=USE=buffet ./build_packages CQ-DEPEND=CL:213562 Change-Id: I37fab53fd5ccfc824b62555869e7581f99aca41c Reviewed-on: https://chromium-review.googlesource.com/213366 Tested-by: Alex Vakulenko <avakulenko@chromium.org> Reviewed-by: Mike Frysinger <vapier@chromium.org> Reviewed-by: Ben Chan <benchan@chromium.org> Commit-Queue: Alex Vakulenko <avakulenko@chromium.org>
diff --git a/buffet/buffet.gyp b/buffet/buffet.gyp index f02e494..63fde02 100644 --- a/buffet/buffet.gyp +++ b/buffet/buffet.gyp
@@ -5,7 +5,6 @@ 'dbus-1', 'libchrome-<(libbase_ver)', 'libchromeos-<(libbase_ver)', - 'libcurl', 'libmetrics-<(libbase_ver)', ], }, @@ -29,10 +28,6 @@ 'commands/schema_utils.cc', 'dbus_constants.cc', 'device_registration_info.cc', - 'http_request.cc', - 'http_connection_curl.cc', - 'http_transport_curl.cc', - 'http_utils.cc', 'manager.cc', 'storage_impls.cc', ], @@ -71,6 +66,7 @@ 'variables': { 'deps': [ 'libchrome-test-<(libbase_ver)', + 'libchromeos-test-<(libbase_ver)', ], }, 'includes': ['../common-mk/common_test.gypi'], @@ -86,9 +82,6 @@ 'commands/schema_utils_unittest.cc', 'commands/unittest_utils.cc', 'device_registration_info_unittest.cc', - 'http_connection_fake.cc', - 'http_transport_fake.cc', - 'http_utils_unittest.cc', ], }, ],
diff --git a/buffet/device_registration_info.cc b/buffet/device_registration_info.cc index a1cb374..7677a89 100644 --- a/buffet/device_registration_info.cc +++ b/buffet/device_registration_info.cc
@@ -11,6 +11,8 @@ #include <base/json/json_writer.h> #include <base/values.h> #include <chromeos/data_encoding.h> +#include <chromeos/http_transport_curl.h> +#include <chromeos/http_utils.h> #include <chromeos/mime_utils.h> #include <chromeos/string_utils.h> #include <chromeos/url_utils.h> @@ -18,8 +20,6 @@ #include "buffet/commands/command_definition.h" #include "buffet/commands/command_manager.h" #include "buffet/device_registration_storage_keys.h" -#include "buffet/http_transport_curl.h" -#include "buffet/http_utils.h" #include "buffet/storage_impls.h" const char buffet::kErrorDomainOAuth2[] = "oauth2"; @@ -69,14 +69,14 @@ const std::string& access_token) { std::string authorization = chromeos::string_utils::Join(' ', access_token_type, access_token); - return {buffet::http::request_header::kAuthorization, authorization}; + return {chromeos::http::request_header::kAuthorization, authorization}; } std::unique_ptr<base::DictionaryValue> ParseOAuthResponse( - const buffet::http::Response* response, chromeos::ErrorPtr* error) { + const chromeos::http::Response* response, chromeos::ErrorPtr* error) { int code = 0; - auto resp = buffet::http::ParseJsonResponse(response, &code, error); - if (resp && code >= buffet::http::status_code::BadRequest) { + auto resp = chromeos::http::ParseJsonResponse(response, &code, error); + if (resp && code >= chromeos::http::status_code::BadRequest) { if (error) { std::string error_code, error_message; if (resp->GetString("error", &error_code) && @@ -143,7 +143,7 @@ DeviceRegistrationInfo::DeviceRegistrationInfo( const std::shared_ptr<CommandManager>& command_manager) - : transport_(new http::curl::Transport()), + : transport_(new chromeos::http::curl::Transport()), // TODO(avakulenko): Figure out security implications of storing // this data unencrypted. storage_(new FileStorage(base::FilePath(kDeviceInfoFilePath))), @@ -152,7 +152,7 @@ DeviceRegistrationInfo::DeviceRegistrationInfo( const std::shared_ptr<CommandManager>& command_manager, - const std::shared_ptr<http::Transport>& transport, + const std::shared_ptr<chromeos::http::Transport>& transport, const std::shared_ptr<StorageInterface>& storage) : transport_(transport), storage_(storage), @@ -269,7 +269,7 @@ return true; } - auto response = http::PostFormData(GetOAuthURL("token"), { + auto response = chromeos::http::PostFormData(GetOAuthURL("token"), { {"refresh_token", refresh_token_}, {"client_id", client_id_}, {"client_secret", client_secret_}, @@ -307,13 +307,13 @@ if (!CheckRegistration(error)) return std::unique_ptr<base::Value>(); - auto response = http::Get(GetDeviceURL(), - {GetAuthorizationHeader()}, transport_, error); + auto response = chromeos::http::Get( + GetDeviceURL(), {GetAuthorizationHeader()}, transport_, error); int status_code = 0; std::unique_ptr<base::DictionaryValue> json = - http::ParseJsonResponse(response.get(), &status_code, error); + chromeos::http::ParseJsonResponse(response.get(), &status_code, error); if (json) { - if (status_code >= http::status_code::BadRequest) { + if (status_code >= chromeos::http::status_code::BadRequest) { LOG(WARNING) << "Failed to retrieve the device info. Response code = " << status_code; ParseGCDError(json.get(), error); @@ -377,8 +377,9 @@ req_json.Set("deviceDraft.commandDefs", commands.release()); std::string url = GetServiceURL("registrationTickets", {{"key", api_key_}}); - auto resp_json = http::ParseJsonResponse( - http::PostJson(url, &req_json, transport_, error).get(), nullptr, error); + auto resp_json = chromeos::http::ParseJsonResponse( + chromeos::http::PostJson(url, &req_json, transport_, error).get(), + nullptr, error); if (!resp_json) return std::string(); @@ -415,9 +416,9 @@ } std::string url = GetServiceURL("registrationTickets/" + ticket_id_); - std::unique_ptr<http::Response> response; + std::unique_ptr<chromeos::http::Response> response; if (!user_auth_code.empty()) { - response = http::PostFormData(GetOAuthURL("token"), { + response = chromeos::http::PostFormData(GetOAuthURL("token"), { {"code", user_auth_code}, {"client_id", client_id_}, {"client_secret", client_secret_}, @@ -442,11 +443,12 @@ base::DictionaryValue user_info; user_info.SetString("userEmail", "me"); - response = http::PatchJson( + response = chromeos::http::PatchJson( url, &user_info, {BuildAuthHeader(token_type, user_access_token)}, transport_, error); - auto json = http::ParseJsonResponse(response.get(), nullptr, error); + auto json = chromeos::http::ParseJsonResponse(response.get(), nullptr, + error); if (!json) return false; } @@ -454,10 +456,11 @@ std::string auth_code; url += "/finalize?key=" + api_key_; LOG(INFO) << "Sending request to: " << url; - response = http::PostBinary(url, nullptr, 0, transport_, error); + response = chromeos::http::PostBinary(url, nullptr, 0, transport_, error); if (!response) return false; - auto json_resp = http::ParseJsonResponse(response.get(), nullptr, error); + auto json_resp = chromeos::http::ParseJsonResponse(response.get(), nullptr, + error); if (!json_resp) return false; if (!response->IsSuccessful()) { @@ -473,7 +476,7 @@ } // Now get access_token and refresh_token - response = http::PostFormData(GetOAuthURL("token"), { + response = chromeos::http::PostFormData(GetOAuthURL("token"), { {"code", auth_code}, {"client_id", client_id_}, {"client_secret", client_secret_},
diff --git a/buffet/device_registration_info.h b/buffet/device_registration_info.h index 1f7d1fb..dc58eda 100644 --- a/buffet/device_registration_info.h +++ b/buffet/device_registration_info.h
@@ -14,8 +14,8 @@ #include <base/time/time.h> #include <chromeos/data_encoding.h> #include <chromeos/error.h> +#include <chromeos/http_transport.h> -#include "buffet/http_transport.h" #include "buffet/storage_interface.h" namespace base { @@ -41,9 +41,10 @@ const std::shared_ptr<CommandManager>& command_manager); // This constructor allows to pass in a custom HTTP transport // (mainly for testing). - DeviceRegistrationInfo(const std::shared_ptr<CommandManager>& command_manager, - const std::shared_ptr<http::Transport>& transport, - const std::shared_ptr<StorageInterface>& storage); + DeviceRegistrationInfo( + const std::shared_ptr<CommandManager>& command_manager, + const std::shared_ptr<chromeos::http::Transport>& transport, + const std::shared_ptr<StorageInterface>& storage); // Returns the authorization HTTP header that can be used to talk // to GCD server for authenticated device communication. @@ -138,7 +139,7 @@ std::string display_name_ = "Coffee Pot"; // HTTP transport used for communications. - std::shared_ptr<http::Transport> transport_; + std::shared_ptr<chromeos::http::Transport> transport_; // Serialization interface to save and load device registration info. std::shared_ptr<StorageInterface> storage_; // Global command manager.
diff --git a/buffet/device_registration_info_unittest.cc b/buffet/device_registration_info_unittest.cc index 779e29e..27aeaa0 100644 --- a/buffet/device_registration_info_unittest.cc +++ b/buffet/device_registration_info_unittest.cc
@@ -5,6 +5,8 @@ #include <base/json/json_reader.h> #include <base/values.h> #include <chromeos/bind_lambda.h> +#include <chromeos/http_request.h> +#include <chromeos/http_transport_fake.h> #include <chromeos/mime_utils.h> #include <gtest/gtest.h> @@ -12,12 +14,13 @@ #include "buffet/commands/unittest_utils.h" #include "buffet/device_registration_info.h" #include "buffet/device_registration_storage_keys.h" -#include "buffet/http_request.h" -#include "buffet/http_transport_fake.h" #include "buffet/storage_impls.h" -using namespace buffet; // NOLINT(build/namespaces) -using namespace buffet::http; // NOLINT(build/namespaces) +using chromeos::http::request_header::kAuthorization; +using chromeos::http::fake::ServerRequest; +using chromeos::http::fake::ServerResponse; + +namespace buffet { namespace { @@ -69,8 +72,7 @@ data->SetString(storage_keys::kRobotAccount, test_data::kRobotAccountEmail); } -void OAuth2Handler(const fake::ServerRequest& request, - fake::ServerResponse* response) { +void OAuth2Handler(const ServerRequest& request, ServerResponse* response) { base::DictionaryValue json; if (request.GetFormField("grant_type") == "refresh_token") { // Refresh device access token. @@ -109,15 +111,15 @@ FAIL() << "Unexpected grant type"; } json.SetInteger("expires_in", 3600); - response->ReplyJson(status_code::Ok, &json); + response->ReplyJson(chromeos::http::status_code::Ok, &json); } -void DeviceInfoHandler(const fake::ServerRequest& request, - fake::ServerResponse* response) { +void DeviceInfoHandler(const ServerRequest& request, ServerResponse* response) { std::string auth = "Bearer "; auth += test_data::kAccessToken; - EXPECT_EQ(auth, request.GetHeader(http::request_header::kAuthorization)); - response->ReplyJson(status_code::Ok, { + EXPECT_EQ(auth, + request.GetHeader(chromeos::http::request_header::kAuthorization)); + response->ReplyJson(chromeos::http::status_code::Ok, { {"channel.supportedType", "xmpp"}, {"deviceKind", "vendor"}, {"id", test_data::kDeviceId}, @@ -125,12 +127,12 @@ }); } -void FinalizeTicketHandler(const fake::ServerRequest& request, - fake::ServerResponse* response) { +void FinalizeTicketHandler(const ServerRequest& request, + ServerResponse* response) { EXPECT_EQ(test_data::kApiKey, request.GetFormField("key")); EXPECT_TRUE(request.GetData().empty()); - response->ReplyJson(status_code::Ok, { + response->ReplyJson(chromeos::http::status_code::Ok, { {"id", test_data::kClaimTicketId}, {"kind", "clouddevices#registrationTicket"}, {"oauthClientId", test_data::kClientId}, @@ -161,7 +163,7 @@ InitDefaultStorage(&data_); storage_ = std::make_shared<MemStorage>(); storage_->Save(&data_); - transport_ = std::make_shared<fake::Transport>(); + transport_ = std::make_shared<chromeos::http::fake::Transport>(); command_manager_ = std::make_shared<CommandManager>(); dev_reg_ = std::unique_ptr<DeviceRegistrationInfo>( new DeviceRegistrationInfo(command_manager_, transport_, storage_)); @@ -169,7 +171,7 @@ base::DictionaryValue data_; std::shared_ptr<MemStorage> storage_; - std::shared_ptr<fake::Transport> transport_; + std::shared_ptr<chromeos::http::fake::Transport> transport_; std::unique_ptr<DeviceRegistrationInfo> dev_reg_; std::shared_ptr<CommandManager> command_manager_; }; @@ -219,7 +221,8 @@ storage_->Save(&data_); EXPECT_TRUE(dev_reg_->Load()); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), + chromeos::http::request_type::kPost, base::Bind(OAuth2Handler)); transport_->ResetRequestCount(); EXPECT_TRUE(dev_reg_->CheckRegistration(nullptr)); @@ -231,9 +234,11 @@ storage_->Save(&data_); EXPECT_TRUE(dev_reg_->Load()); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), + chromeos::http::request_type::kPost, base::Bind(OAuth2Handler)); - transport_->AddHandler(dev_reg_->GetDeviceURL(), request_type::kGet, + transport_->AddHandler(dev_reg_->GetDeviceURL(), + chromeos::http::request_type::kGet, base::Bind(DeviceInfoHandler)); transport_->ResetRequestCount(); auto device_info = dev_reg_->GetDeviceInfo(nullptr); @@ -251,9 +256,11 @@ storage_->Save(&data_); EXPECT_TRUE(dev_reg_->Load()); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), + chromeos::http::request_type::kPost, base::Bind(OAuth2Handler)); - transport_->AddHandler(dev_reg_->GetDeviceURL(), request_type::kGet, + transport_->AddHandler(dev_reg_->GetDeviceURL(), + chromeos::http::request_type::kGet, base::Bind(DeviceInfoHandler)); std::string id = dev_reg_->GetDeviceId(nullptr); EXPECT_EQ(test_data::kDeviceId, id); @@ -262,8 +269,8 @@ TEST_F(DeviceRegistrationInfoTest, StartRegistration) { EXPECT_TRUE(dev_reg_->Load()); - auto create_ticket = [](const fake::ServerRequest& request, - fake::ServerResponse* response) { + auto create_ticket = [](const ServerRequest& request, + ServerResponse* response) { EXPECT_EQ(test_data::kApiKey, request.GetFormField("key")); auto json = request.GetDataAsJson(); EXPECT_NE(nullptr, json.get()); @@ -294,7 +301,7 @@ device_draft->SetString("kind", "clouddevices#device"); json_resp.Set("deviceDraft", device_draft); - response->ReplyJson(status_code::Ok, &json_resp); + response->ReplyJson(chromeos::http::status_code::Ok, &json_resp); }; auto json_base = buffet::unittests::CreateDictionaryValue(R"({ @@ -323,7 +330,7 @@ EXPECT_TRUE(command_manager_->LoadCommands(*json_cmds, "", nullptr)); transport_->AddHandler(dev_reg_->GetServiceURL("registrationTickets"), - request_type::kPost, + chromeos::http::request_type::kPost, base::Bind(create_ticket)); std::map<std::string, std::string> params; std::string json_resp = dev_reg_->StartRegistration(params, nullptr); @@ -345,10 +352,12 @@ std::string ticket_url = dev_reg_->GetServiceURL("registrationTickets/" + std::string(test_data::kClaimTicketId)); - transport_->AddHandler(ticket_url + "/finalize", request_type::kPost, + transport_->AddHandler(ticket_url + "/finalize", + chromeos::http::request_type::kPost, base::Bind(FinalizeTicketHandler)); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), + chromeos::http::request_type::kPost, base::Bind(OAuth2Handler)); storage_->reset_save_count(); @@ -389,26 +398,28 @@ std::string ticket_url = dev_reg_->GetServiceURL("registrationTickets/" + std::string(test_data::kClaimTicketId)); - transport_->AddHandler(ticket_url + "/finalize", request_type::kPost, + transport_->AddHandler(ticket_url + "/finalize", + chromeos::http::request_type::kPost, base::Bind(FinalizeTicketHandler)); - transport_->AddHandler(dev_reg_->GetOAuthURL("token"), request_type::kPost, + transport_->AddHandler(dev_reg_->GetOAuthURL("token"), + chromeos::http::request_type::kPost, base::Bind(OAuth2Handler)); // Handle patching in the user email onto the device record. - auto email_patch_handler = [](const fake::ServerRequest& request, - fake::ServerResponse* response) { + auto email_patch_handler = [](const ServerRequest& request, + ServerResponse* response) { std::string auth_header = "Bearer "; auth_header += test_data::kUserAccessToken; EXPECT_EQ(auth_header, - request.GetHeader(http::request_header::kAuthorization)); + request.GetHeader(chromeos::http::request_header::kAuthorization)); auto json = request.GetDataAsJson(); EXPECT_NE(nullptr, json.get()); std::string value; EXPECT_TRUE(json->GetString("userEmail", &value)); EXPECT_EQ("me", value); - response->ReplyJson(status_code::Ok, { + response->ReplyJson(chromeos::http::status_code::Ok, { {"id", test_data::kClaimTicketId}, {"kind", "clouddevices#registrationTicket"}, {"oauthClientId", test_data::kClientId}, @@ -418,7 +429,7 @@ {"deviceDraft.channel.supportedType", "xmpp"}, }); }; - transport_->AddHandler(ticket_url, request_type::kPatch, + transport_->AddHandler(ticket_url, chromeos::http::request_type::kPatch, base::Bind(email_patch_handler)); storage_->reset_save_count(); @@ -429,3 +440,5 @@ storage_->save_count()); // The device info must have been saved. EXPECT_EQ(4, transport_->GetRequestCount()); } + +} // namespace buffet
diff --git a/buffet/http_connection.h b/buffet/http_connection.h deleted file mode 100644 index 893b992..0000000 --- a/buffet/http_connection.h +++ /dev/null
@@ -1,87 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BUFFET_HTTP_CONNECTION_H_ -#define BUFFET_HTTP_CONNECTION_H_ - -#include <string> -#include <vector> - -#include <base/basictypes.h> -#include <chromeos/error.h> - -#include "buffet/http_transport.h" - -namespace buffet { -namespace http { - -/////////////////////////////////////////////////////////////////////////////// -// 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 -// connection (such as the URL, request method, etc - see -// Transport::CreateConnection() for more details). But most implementations -// 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 communication. -/////////////////////////////////////////////////////////////////////////////// -class Connection { - public: - 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, - chromeos::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, - chromeos::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(chromeos::ErrorPtr* error) = 0; - - // Returns the HTTP status code (e.g. 200 for success). - virtual int GetResponseStatusCode() const = 0; - // Returns the status text (e.g. for error 403 it could be "NOT AUTHORIZED"). - virtual std::string GetResponseStatusText() const = 0; - // Returns the HTTP protocol version (e.g. "HTTP/1.1"). - virtual std::string GetProtocolVersion() const = 0; - // Returns the value of particular response header, or empty string if the - // headers wasn't received. - virtual std::string GetResponseHeader( - const std::string& header_name) const = 0; - // Returns the response data size, if known. For chunked (streaming) - // transmission this might not be known until all the data is sent. - // In this case GetResponseDataSize() will return 0. - virtual uint64_t GetResponseDataSize() const = 0; - // This function is called to read a block of response data. - // It needs to be called repeatedly until it returns false or |size_read| is - // set to 0. |data| is the destination buffer to read the data into. - // |buffer_size| is the size of the buffer (amount of data to read). - // |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, - chromeos::ErrorPtr* error) = 0; - - protected: - // |transport_| is mainly used to keep the object alive as long as the - // connection exists. But some implementations of Connection could use - // the Transport-derived class for their own needs as well. - std::shared_ptr<Transport> transport_; - - private: - DISALLOW_COPY_AND_ASSIGN(Connection); -}; - -} // namespace http -} // namespace buffet - -#endif // BUFFET_HTTP_CONNECTION_H_
diff --git a/buffet/http_connection_curl.cc b/buffet/http_connection_curl.cc deleted file mode 100644 index 86715ab..0000000 --- a/buffet/http_connection_curl.cc +++ /dev/null
@@ -1,233 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "buffet/http_connection_curl.h" - -#include <base/logging.h> -#include <chromeos/string_utils.h> - -#include "buffet/http_request.h" -#include "buffet/http_transport_curl.h" - -namespace buffet { -namespace http { -namespace curl { - -static int curl_trace(CURL *handle, curl_infotype type, - char *data, size_t size, void *userp) { - std::string msg(data, size); - - switch (type) { - case CURLINFO_TEXT: - VLOG(3) << "== Info: " << msg; - break; - case CURLINFO_HEADER_OUT: - VLOG(3) << "=> Send headers:\n" << msg; - break; - case CURLINFO_DATA_OUT: - VLOG(3) << "=> Send data:\n" << msg; - break; - case CURLINFO_SSL_DATA_OUT: - VLOG(3) << "=> Send SSL data" << msg; - break; - case CURLINFO_HEADER_IN: - VLOG(3) << "<= Recv header: " << msg; - break; - case CURLINFO_DATA_IN: - VLOG(3) << "<= Recv data:\n" << msg; - break; - case CURLINFO_SSL_DATA_IN: - VLOG(3) << "<= Recv SSL data" << msg; - break; - default: - break; - } - return 0; -} - -Connection::Connection(CURL* curl_handle, const std::string& method, - std::shared_ptr<http::Transport> transport) : - http::Connection(transport), method_(method), curl_handle_(curl_handle) { - VLOG(1) << "curl::Connection created: " << method_; -} - -Connection::~Connection() { - VLOG(1) << "curl::Connection destroyed"; -} - -bool Connection::SendHeaders(const HeaderList& headers, - chromeos::ErrorPtr* error) { - headers_.insert(headers.begin(), headers.end()); - return true; -} - -bool Connection::WriteRequestData(const void* data, size_t size, - chromeos::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); - } - return true; -} - -bool Connection::FinishRequest(chromeos::ErrorPtr* error) { - if (VLOG_IS_ON(3)) { - curl_easy_setopt(curl_handle_, CURLOPT_DEBUGFUNCTION, curl_trace); - curl_easy_setopt(curl_handle_, CURLOPT_VERBOSE, 1L); - } - - // Set up HTTP request data. - if (method_ == request_type::kPut) { - curl_easy_setopt(curl_handle_, CURLOPT_INFILESIZE_LARGE, - curl_off_t(request_data_.size())); - } else { - curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE_LARGE, - curl_off_t(request_data_.size())); - } - if (!request_data_.empty()) { - curl_easy_setopt(curl_handle_, - CURLOPT_READFUNCTION, &Connection::read_callback); - curl_easy_setopt(curl_handle_, CURLOPT_READDATA, this); - VLOG(2) << "Raw request data: " - << std::string(reinterpret_cast<const char*>(request_data_.data()), - request_data_.size()); - } - - curl_slist* header_list = nullptr; - if (!headers_.empty()) { - for (auto pair : headers_) { - std::string header = chromeos::string_utils::Join(": ", - pair.first, - pair.second); - VLOG(2) << "Request header: " << header; - header_list = curl_slist_append(header_list, header.c_str()); - } - curl_easy_setopt(curl_handle_, CURLOPT_HTTPHEADER, header_list); - } - - headers_.clear(); - - // Set up HTTP response data. - if (method_ != request_type::kHead) { - curl_easy_setopt(curl_handle_, - CURLOPT_WRITEFUNCTION, &Connection::write_callback); - curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this); - } - - // HTTP response headers - curl_easy_setopt(curl_handle_, - CURLOPT_HEADERFUNCTION, &Connection::header_callback); - curl_easy_setopt(curl_handle_, CURLOPT_HEADERDATA, this); - - CURLcode ret = curl_easy_perform(curl_handle_); - if (header_list) - curl_slist_free_all(header_list); - if (ret != CURLE_OK) { - chromeos::Error::AddTo(error, http::curl::kErrorDomain, - chromeos::string_utils::ToString(ret), - curl_easy_strerror(ret)); - } else { - LOG(INFO) << "Response: " << GetResponseStatusCode() << " (" - << GetResponseStatusText() << ")"; - VLOG(2) << "Response data (" << response_data_.size() << "): " - << std::string(reinterpret_cast<const char*>(response_data_.data()), - response_data_.size()); - } - return (ret == CURLE_OK); -} - -int Connection::GetResponseStatusCode() const { - 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; -} - -std::string Connection::GetResponseStatusText() const { - return status_text_; -} - -std::string Connection::GetProtocolVersion() const { - return protocol_version_; -} - -std::string Connection::GetResponseHeader( - const std::string& header_name) const { - auto p = headers_.find(header_name); - return p != headers_.end() ? p->second : std::string(); -} - -uint64_t Connection::GetResponseDataSize() const { - return response_data_.size(); -} - -bool Connection::ReadResponseData(void* data, - size_t buffer_size, - size_t* size_read, - chromeos::ErrorPtr* error) { - size_t size_to_read = response_data_.size() - response_data_ptr_; - if (size_to_read > buffer_size) - size_to_read = buffer_size; - memcpy(data, response_data_.data() + response_data_ptr_, size_to_read); - if (size_read) - *size_read = size_to_read; - response_data_ptr_ += size_to_read; - return true; -} - -size_t Connection::write_callback(char* ptr, size_t size, - size_t num, void* data) { - Connection* me = reinterpret_cast<Connection*>(data); - size_t data_len = size * num; - me->response_data_.insert(me->response_data_.end(), ptr, ptr + data_len); - return data_len; -} - -size_t Connection::read_callback(char* ptr, size_t size, - size_t num, void* data) { - Connection* me = reinterpret_cast<Connection*>(data); - size_t data_len = size * num; - - if (me->request_data_ptr_ >= me->request_data_.size()) - return 0; - - if (me->request_data_ptr_ + data_len > me->request_data_.size()) - data_len = me->request_data_.size() - me->request_data_ptr_; - - memcpy(ptr, me->request_data_.data() + me->request_data_ptr_, data_len); - me->request_data_ptr_ += data_len; - - return data_len; -} - -size_t Connection::header_callback(char* ptr, size_t size, - size_t num, void* data) { - using chromeos::string_utils::SplitAtFirst; - Connection* me = reinterpret_cast<Connection*>(data); - size_t hdr_len = size * num; - 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(); - } - - VLOG(2) << "Response header: " << header; - - if (!me->status_text_set_) { - // First header - response code as "HTTP/1.1 200 OK". - // Need to extract the OK part - auto pair = SplitAtFirst(header, ' '); - me->protocol_version_ = pair.first; - me->status_text_ = SplitAtFirst(pair.second, ' ').second; - me->status_text_set_ = true; - } else { - auto pair = SplitAtFirst(header, ':'); - if (!pair.second.empty()) - me->headers_.insert(pair); - } - return hdr_len; -} - -} // namespace curl -} // namespace http -} // namespace buffet
diff --git a/buffet/http_connection_curl.h b/buffet/http_connection_curl.h deleted file mode 100644 index 67d7eac..0000000 --- a/buffet/http_connection_curl.h +++ /dev/null
@@ -1,86 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BUFFET_HTTP_CONNECTION_CURL_H_ -#define BUFFET_HTTP_CONNECTION_CURL_H_ - -#include <map> -#include <string> -#include <vector> - -#include <base/basictypes.h> -#include <curl/curl.h> - -#include "buffet/http_connection.h" - -namespace buffet { -namespace http { -namespace curl { - -// This is a libcurl-based implementation of http::Connection. -class Connection : public http::Connection { - public: - Connection(CURL* curl_handle, const std::string& method, - std::shared_ptr<http::Transport> transport); - virtual ~Connection(); - - // Overrides from http::Connection. - // See http_connection.h for description of these methods. - bool SendHeaders(const HeaderList& headers, - chromeos::ErrorPtr* error) override; - bool WriteRequestData(const void* data, size_t size, - chromeos::ErrorPtr* error) override; - bool FinishRequest(chromeos::ErrorPtr* error) override; - - int GetResponseStatusCode() const override; - std::string GetResponseStatusText() const override; - std::string GetProtocolVersion() const override; - std::string GetResponseHeader(const std::string& header_name) const override; - uint64_t GetResponseDataSize() const override; - bool ReadResponseData(void* data, size_t buffer_size, - size_t* size_read, chromeos::ErrorPtr* error) override; - - protected: - // Write data callback. Used by CURL when receiving response data. - static size_t write_callback(char* ptr, size_t size, size_t num, void* data); - // Read data callback. Used by CURL when sending request body data. - static size_t read_callback(char* ptr, size_t size, size_t num, void* data); - // Write header data callback. Used by CURL when receiving response headers. - static size_t header_callback(char* ptr, size_t size, size_t num, void* data); - - // HTTP request verb, such as "GET", "POST", "PUT", ... - std::string method_; - - // Binary data for request body. - std::vector<unsigned char> request_data_; - // Read pointer for request data. Used when streaming data to the server. - size_t request_data_ptr_ = 0; - - // Received response data. - std::vector<unsigned char> response_data_; - size_t response_data_ptr_ = 0; - - // List of optional request headers provided by the caller. - // After request has been sent, contains the received response headers. - std::map<std::string, std::string> headers_; - - // HTTP protocol version, such as HTTP/1.1 - std::string protocol_version_; - // 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. - bool status_text_set_ = false; - - CURL* curl_handle_ = nullptr; - - private: - DISALLOW_COPY_AND_ASSIGN(Connection); -}; - -} // namespace curl -} // namespace http -} // namespace buffet - -#endif // BUFFET_HTTP_CONNECTION_CURL_H_
diff --git a/buffet/http_connection_fake.cc b/buffet/http_connection_fake.cc deleted file mode 100644 index 6cfdde3..0000000 --- a/buffet/http_connection_fake.cc +++ /dev/null
@@ -1,98 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "buffet/http_connection_fake.h" - -#include <base/logging.h> -#include <chromeos/mime_utils.h> -#include <chromeos/string_utils.h> - -#include "buffet/http_request.h" - -namespace buffet { -namespace http { -namespace fake { - -Connection::Connection(const std::string& url, const std::string& method, - std::shared_ptr<http::Transport> transport) : - http::Connection(transport), request_(url, method) { - VLOG(1) << "fake::Connection created: " << method; -} - -Connection::~Connection() { - VLOG(1) << "fake::Connection destroyed"; -} - -bool Connection::SendHeaders(const HeaderList& headers, - chromeos::ErrorPtr* error) { - request_.AddHeaders(headers); - return true; -} - -bool Connection::WriteRequestData(const void* data, size_t size, - chromeos::ErrorPtr* error) { - request_.AddData(data, size); - return true; -} - -bool Connection::FinishRequest(chromeos::ErrorPtr* error) { - using chromeos::string_utils::ToString; - request_.AddHeaders({{request_header::kContentLength, - ToString(request_.GetData().size())}}); - 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()) { - LOG(ERROR) << "Received unexpected " << request_.GetMethod() - << " request at " << request_.GetURL(); - response_.ReplyText(status_code::NotFound, - "<html><body>Not found</body></html>", - chromeos::mime::text::kHtml); - } else { - handler.Run(request_, &response_); - } - return true; -} - -int Connection::GetResponseStatusCode() const { - return response_.GetStatusCode(); -} - -std::string Connection::GetResponseStatusText() const { - return response_.GetStatusText(); -} - -std::string Connection::GetProtocolVersion() const { - return response_.GetProtocolVersion(); -} - -std::string Connection::GetResponseHeader( - const std::string& header_name) const { - return response_.GetHeader(header_name); -} - -uint64_t Connection::GetResponseDataSize() const { - // HEAD requests must not return body. - return (request_.GetMethod() != request_type::kHead) ? - response_.GetData().size() : 0; -} - -bool Connection::ReadResponseData(void* data, - size_t buffer_size, - size_t* size_read, - chromeos::ErrorPtr* error) { - size_t size_to_read = GetResponseDataSize() - response_data_ptr_; - if (size_to_read > buffer_size) - size_to_read = buffer_size; - if (size_to_read > 0) - memcpy(data, response_.GetData().data() + response_data_ptr_, size_to_read); - if (size_read) - *size_read = size_to_read; - response_data_ptr_ += size_to_read; - return true; -} - -} // namespace fake -} // namespace http -} // namespace buffet
diff --git a/buffet/http_connection_fake.h b/buffet/http_connection_fake.h deleted file mode 100644 index 513228a..0000000 --- a/buffet/http_connection_fake.h +++ /dev/null
@@ -1,62 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BUFFET_HTTP_CONNECTION_FAKE_H_ -#define BUFFET_HTTP_CONNECTION_FAKE_H_ - -#include <map> -#include <string> -#include <vector> - -#include <base/basictypes.h> - -#include "buffet/http_connection.h" -#include "buffet/http_transport_fake.h" - -namespace buffet { -namespace http { -namespace fake { - -// This is a fake implementation of http::Connection for unit testing. -class Connection : public http::Connection { - public: - Connection(const std::string& url, const std::string& method, - std::shared_ptr<http::Transport> transport); - virtual ~Connection(); - - // Overrides from http::Connection. - // See http_connection.h for description of these methods. - bool SendHeaders(const HeaderList& headers, - chromeos::ErrorPtr* error) override; - bool WriteRequestData(const void* data, size_t size, - chromeos::ErrorPtr* error) override; - bool FinishRequest(chromeos::ErrorPtr* error) override; - - int GetResponseStatusCode() const override; - std::string GetResponseStatusText() const override; - std::string GetProtocolVersion() const override; - std::string GetResponseHeader(const std::string& header_name) const override; - uint64_t GetResponseDataSize() const override; - bool ReadResponseData(void* data, size_t buffer_size, - size_t* size_read, chromeos::ErrorPtr* error) override; - - private: - // 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 - // the handler in response to the request. - ServerRequest request_; - ServerResponse response_; - - // Internal read data pointer needed for ReadResponseData() implementation. - size_t response_data_ptr_ = 0; - - DISALLOW_COPY_AND_ASSIGN(Connection); -}; - -} // namespace fake -} // namespace http -} // namespace buffet - -#endif // BUFFET_HTTP_CONNECTION_FAKE_H_
diff --git a/buffet/http_request.cc b/buffet/http_request.cc deleted file mode 100644 index dfeac7f..0000000 --- a/buffet/http_request.cc +++ /dev/null
@@ -1,301 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "buffet/http_request.h" - -#include <base/logging.h> -#include <chromeos/map_utils.h> -#include <chromeos/mime_utils.h> -#include <chromeos/string_utils.h> - -#include "buffet/http_connection_curl.h" -#include "buffet/http_transport_curl.h" - -namespace buffet { -namespace http { - -// request_type -const char request_type::kOptions[] = "OPTIONS"; -const char request_type::kGet[] = "GET"; -const char request_type::kHead[] = "HEAD"; -const char request_type::kPost[] = "POST"; -const char request_type::kPut[] = "PUT"; -const char request_type::kPatch[] = "PATCH"; -const char request_type::kDelete[] = "DELETE"; -const char request_type::kTrace[] = "TRACE"; -const char request_type::kConnect[] = "CONNECT"; -const char request_type::kCopy[] = "COPY"; -const char request_type::kMove[] = "MOVE"; - -// request_header -const char request_header::kAccept[] = "Accept"; -const char request_header::kAcceptCharset[] = "Accept-Charset"; -const char request_header::kAcceptEncoding[] = "Accept-Encoding"; -const char request_header::kAcceptLanguage[] = "Accept-Language"; -const char request_header::kAllow[] = "Allow"; -const char request_header::kAuthorization[] = "Authorization"; -const char request_header::kCacheControl[] = "Cache-Control"; -const char request_header::kConnection[] = "Connection"; -const char request_header::kContentEncoding[] = "Content-Encoding"; -const char request_header::kContentLanguage[] = "Content-Language"; -const char request_header::kContentLength[] = "Content-Length"; -const char request_header::kContentLocation[] = "Content-Location"; -const char request_header::kContentMd5[] = "Content-MD5"; -const char request_header::kContentRange[] = "Content-Range"; -const char request_header::kContentType[] = "Content-Type"; -const char request_header::kCookie[] = "Cookie"; -const char request_header::kDate[] = "Date"; -const char request_header::kExpect[] = "Expect"; -const char request_header::kExpires[] = "Expires"; -const char request_header::kFrom[] = "From"; -const char request_header::kHost[] = "Host"; -const char request_header::kIfMatch[] = "If-Match"; -const char request_header::kIfModifiedSince[] = "If-Modified-Since"; -const char request_header::kIfNoneMatch[] = "If-None-Match"; -const char request_header::kIfRange[] = "If-Range"; -const char request_header::kIfUnmodifiedSince[] = "If-Unmodified-Since"; -const char request_header::kLastModified[] = "Last-Modified"; -const char request_header::kMaxForwards[] = "Max-Forwards"; -const char request_header::kPragma[] = "Pragma"; -const char request_header::kProxyAuthorization[] = "Proxy-Authorization"; -const char request_header::kRange[] = "Range"; -const char request_header::kReferer[] = "Referer"; -const char request_header::kTE[] = "TE"; -const char request_header::kTrailer[] = "Trailer"; -const char request_header::kTransferEncoding[] = "Transfer-Encoding"; -const char request_header::kUpgrade[] = "Upgrade"; -const char request_header::kUserAgent[] = "User-Agent"; -const char request_header::kVia[] = "Via"; -const char request_header::kWarning[] = "Warning"; - -// response_header -const char response_header::kAcceptRanges[] = "Accept-Ranges"; -const char response_header::kAge[] = "Age"; -const char response_header::kAllow[] = "Allow"; -const char response_header::kCacheControl[] = "Cache-Control"; -const char response_header::kConnection[] = "Connection"; -const char response_header::kContentEncoding[] = "Content-Encoding"; -const char response_header::kContentLanguage[] = "Content-Language"; -const char response_header::kContentLength[] = "Content-Length"; -const char response_header::kContentLocation[] = "Content-Location"; -const char response_header::kContentMd5[] = "Content-MD5"; -const char response_header::kContentRange[] = "Content-Range"; -const char response_header::kContentType[] = "Content-Type"; -const char response_header::kDate[] = "Date"; -const char response_header::kETag[] = "ETag"; -const char response_header::kExpires[] = "Expires"; -const char response_header::kLastModified[] = "Last-Modified"; -const char response_header::kLocation[] = "Location"; -const char response_header::kPragma[] = "Pragma"; -const char response_header::kProxyAuthenticate[] = "Proxy-Authenticate"; -const char response_header::kRetryAfter[] = "Retry-After"; -const char response_header::kServer[] = "Server"; -const char response_header::kSetCookie[] = "Set-Cookie"; -const char response_header::kTrailer[] = "Trailer"; -const char response_header::kTransferEncoding[] = "Transfer-Encoding"; -const char response_header::kUpgrade[] = "Upgrade"; -const char response_header::kVary[] = "Vary"; -const char response_header::kVia[] = "Via"; -const char response_header::kWarning[] = "Warning"; -const char response_header::kWwwAuthenticate[] = "WWW-Authenticate"; - -// *********************************************************** -// ********************** Request Class ********************** -// *********************************************************** -Request::Request(const std::string& url, const char* method, - std::shared_ptr<Transport> transport) : - transport_(transport), request_url_(url), method_(method) { - VLOG(1) << "http::Request created"; - if (!transport_) - transport_.reset(new http::curl::Transport()); -} - -Request::~Request() { - VLOG(1) << "http::Request destroyed"; -} - -void Request::AddRange(int64_t bytes) { - if (bytes < 0) { - ranges_.emplace_back(Request::range_value_omitted, -bytes); - } else { - ranges_.emplace_back(bytes, Request::range_value_omitted); - } -} - -void Request::AddRange(uint64_t from_byte, uint64_t to_byte) { - ranges_.emplace_back(from_byte, to_byte); -} - -std::unique_ptr<Response> Request::GetResponse(chromeos::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 - return response; -} - -void Request::SetAccept(const char* accept_mime_types) { - accept_ = accept_mime_types; -} - -std::string Request::GetAccept() const { - return accept_; -} - -void Request::SetContentType(const char* contentType) { - content_type_ = contentType; -} - -std::string Request::GetContentType() const { - return content_type_; -} - -void Request::AddHeader(const char* header, const char* value) { - headers_[header] = value; -} - -void Request::AddHeaders(const HeaderList& headers) { - headers_.insert(headers.begin(), headers.end()); -} - -bool Request::AddRequestBody(const void* data, - size_t size, - chromeos::ErrorPtr* error) { - if (!SendRequestIfNeeded(error)) - return false; - return connection_->WriteRequestData(data, size, error); -} - -void Request::SetReferer(const char* referer) { - referer_ = referer; -} - -std::string Request::GetReferer() const { - return referer_; -} - -void Request::SetUserAgent(const char* user_agent) { - user_agent_ = user_agent; -} - -std::string Request::GetUserAgent() const { - return user_agent_; -} - -bool Request::SendRequestIfNeeded(chromeos::ErrorPtr* error) { - if (transport_) { - if (!connection_) { - http::HeaderList headers = chromeos::MapToVector(headers_); - std::vector<std::string> ranges; - if (method_ != request_type::kHead) { - ranges.reserve(ranges_.size()); - for (auto p : ranges_) { - if (p.first != range_value_omitted || - p.second != range_value_omitted) { - std::string range; - if (p.first != range_value_omitted) { - range = chromeos::string_utils::ToString(p.first); - } - range += '-'; - if (p.second != range_value_omitted) { - range += chromeos::string_utils::ToString(p.second); - } - ranges.push_back(range); - } - } - } - if (!ranges.empty()) - headers.emplace_back(request_header::kRange, - "bytes=" + - chromeos::string_utils::Join(',', ranges)); - - headers.emplace_back(request_header::kAccept, GetAccept()); - if (method_ != request_type::kGet && method_ != request_type::kHead) { - if (!content_type_.empty()) - headers.emplace_back(request_header::kContentType, content_type_); - } - connection_ = transport_->CreateConnection(transport_, request_url_, - method_, headers, - user_agent_, referer_, - error); - } - - if (connection_) - return true; - } else { - chromeos::Error::AddTo(error, 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)) { - 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. - if (connection_) { - size_t size = static_cast<size_t>(connection_->GetResponseDataSize()); - response_data_.reserve(size); - unsigned char buffer[1024]; - size_t read = 0; - while (connection_->ReadResponseData(buffer, sizeof(buffer), - &read, nullptr) && read > 0) { - response_data_.insert(response_data_.end(), buffer, buffer + read); - } - } -} - -Response::~Response() { - VLOG(1) << "http::Response destroyed"; -} - -bool Response::IsSuccessful() const { - int code = GetStatusCode(); - return code >= status_code::Continue && code < status_code::BadRequest; -} - -int Response::GetStatusCode() const { - if (!connection_) - return -1; - - return connection_->GetResponseStatusCode(); -} - -std::string Response::GetStatusText() const { - if (!connection_) - return std::string(); - - return connection_->GetResponseStatusText(); -} - -std::string Response::GetContentType() const { - return GetHeader(response_header::kContentType); -} - -const std::vector<unsigned char>& Response::GetData() const { - return response_data_; -} - -std::string Response::GetDataAsString() const { - if (response_data_.empty()) - return std::string(); - - const char* data_buf = reinterpret_cast<const char*>(response_data_.data()); - return std::string(data_buf, data_buf + response_data_.size()); -} - -std::string Response::GetHeader(const char* header_name) const { - if (connection_) - return connection_->GetResponseHeader(header_name); - - return std::string(); -} - -} // namespace http -} // namespace buffet
diff --git a/buffet/http_request.h b/buffet/http_request.h deleted file mode 100644 index d9e73a4..0000000 --- a/buffet/http_request.h +++ /dev/null
@@ -1,352 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BUFFET_HTTP_REQUEST_H_ -#define BUFFET_HTTP_REQUEST_H_ - -#include <limits> -#include <map> -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include <base/basictypes.h> -#include <chromeos/error.h> - -#include "buffet/http_connection.h" -#include "buffet/http_transport.h" - -namespace buffet { -namespace http { - -// HTTP request verbs -namespace request_type { - extern const char kOptions[]; - extern const char kGet[]; - extern const char kHead[]; - extern const char kPost[]; - extern const char kPut[]; - extern const char kPatch[]; // Not a standard HTTP/1.1 request method - extern const char kDelete[]; - extern const char kTrace[]; - extern const char kConnect[]; - extern const char kCopy[]; // Not a standard HTTP/1.1 request method - extern const char kMove[]; // Not a standard HTTP/1.1 request method -} // namespace request_type - -// HTTP request header names -namespace request_header { - extern const char kAccept[]; - extern const char kAcceptCharset[]; - extern const char kAcceptEncoding[]; - extern const char kAcceptLanguage[]; - extern const char kAllow[]; - extern const char kAuthorization[]; - extern const char kCacheControl[]; - extern const char kConnection[]; - extern const char kContentEncoding[]; - extern const char kContentLanguage[]; - extern const char kContentLength[]; - extern const char kContentLocation[]; - extern const char kContentMd5[]; - extern const char kContentRange[]; - extern const char kContentType[]; - extern const char kCookie[]; - extern const char kDate[]; - extern const char kExpect[]; - extern const char kExpires[]; - extern const char kFrom[]; - extern const char kHost[]; - extern const char kIfMatch[]; - extern const char kIfModifiedSince[]; - extern const char kIfNoneMatch[]; - extern const char kIfRange[]; - extern const char kIfUnmodifiedSince[]; - extern const char kLastModified[]; - extern const char kMaxForwards[]; - extern const char kPragma[]; - extern const char kProxyAuthorization[]; - extern const char kRange[]; - extern const char kReferer[]; - extern const char kTE[]; - extern const char kTrailer[]; - extern const char kTransferEncoding[]; - extern const char kUpgrade[]; - extern const char kUserAgent[]; - extern const char kVia[]; - extern const char kWarning[]; -} // namespace request_header - -// HTTP response header names -namespace response_header { - extern const char kAcceptRanges[]; - extern const char kAge[]; - extern const char kAllow[]; - extern const char kCacheControl[]; - extern const char kConnection[]; - extern const char kContentEncoding[]; - extern const char kContentLanguage[]; - extern const char kContentLength[]; - extern const char kContentLocation[]; - extern const char kContentMd5[]; - extern const char kContentRange[]; - extern const char kContentType[]; - extern const char kDate[]; - extern const char kETag[]; - extern const char kExpires[]; - extern const char kLastModified[]; - extern const char kLocation[]; - extern const char kPragma[]; - extern const char kProxyAuthenticate[]; - extern const char kRetryAfter[]; - extern const char kServer[]; - extern const char kSetCookie[]; - extern const char kTrailer[]; - extern const char kTransferEncoding[]; - extern const char kUpgrade[]; - extern const char kVary[]; - extern const char kVia[]; - extern const char kWarning[]; - extern const char kWwwAuthenticate[]; -} // namespace response_header - -// HTTP request status (error) codes -namespace status_code { - // OK to continue with request - static const int Continue = 100; - // Server has switched protocols in upgrade header - static const int SwitchProtocols = 101; - - // Request completed - static const int Ok = 200; - // Object created, reason = new URI - static const int Created = 201; - // Async completion (TBS) - static const int Accepted = 202; - // Partial completion - static const int Partial = 203; - // No info to return - static const int NoContent = 204; - // Request completed, but clear form - static const int ResetContent = 205; - // Partial GET fulfilled - static const int PartialContent = 206; - - // Server couldn't decide what to return - static const int Ambiguous = 300; - // Object permanently moved - static const int Moved = 301; - // Object temporarily moved - static const int Redirect = 302; - // Redirection w/ new access method - static const int RedirectMethod = 303; - // If-Modified-Since was not modified - static const int NotModified = 304; - // Redirection to proxy, location header specifies proxy to use - static const int UseProxy = 305; - // HTTP/1.1: keep same verb - static const int RedirectKeepVerb = 307; - - // Invalid syntax - static const int BadRequest = 400; - // Access denied - static const int Denied = 401; - // Payment required - static const int PaymentRequired = 402; - // Request forbidden - static const int Forbidden = 403; - // Object not found - static const int NotFound = 404; - // Method is not allowed - static const int BadMethod = 405; - // No response acceptable to client found - static const int NoneAcceptable = 406; - // Proxy authentication required - static const int ProxyAuthRequired = 407; - // Server timed out waiting for request - static const int RequestTimeout = 408; - // User should resubmit with more info - static const int Conflict = 409; - // The resource is no longer available - static const int Gone = 410; - // The server refused to accept request w/o a length - static const int LengthRequired = 411; - // Precondition given in request failed - static const int PrecondionFailed = 412; - // Request entity was too large - static const int RequestTooLarge = 413; - // Request URI too long - static const int UriTooLong = 414; - // Unsupported media type - static const int UnsupportedMedia = 415; - // Retry after doing the appropriate action. - static const int RetryWith = 449; - - // Internal server error - static const int InternalServerError = 500; - // Request not supported - static const int NotSupported = 501; - // Error response received from gateway - static const int BadGateway = 502; - // Temporarily overloaded - static const int ServiceUnavailable = 503; - // Timed out waiting for gateway - static const int GatewayTimeout = 504; - // HTTP version not supported - static const int VersionNotSupported = 505; -} // namespace status_code - -class Response; // Just a forward declaration. - -/////////////////////////////////////////////////////////////////////////////// -// Request class is the main object used to set up and initiate an HTTP -// communication session. It is used to specify the HTTP request method, -// request URL and many optional parameters (such as HTTP headers, user agent, -// referer URL and so on. -// -// Once everything is setup, GetResponse() method is used to send the request -// and obtain the server response. The returned Response object can be -// used to inspect the response code, HTTP headers and/or response body. -/////////////////////////////////////////////////////////////////////////////// -class Request { - public: - // The main constructor. |url| specifies the remote host address/path - // to send the request to. |method| is the HTTP request verb and - // |transport| is the HTTP transport implementation for server communications. - Request(const std::string& url, const char* method, - std::shared_ptr<Transport> transport); - ~Request(); - - // Gets/Sets "Accept:" header value. The default value is "*/*" if not set. - void SetAccept(const char* accept_mime_types); - std::string GetAccept() const; - - // Gets/Sets "Content-Type:" header value - void SetContentType(const char* content_type); - std::string GetContentType() const; - - // Adds additional HTTP request header - void AddHeader(const char* header, const char* value); - void AddHeaders(const HeaderList& headers); - - // Removes HTTP request header - 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, chromeos::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 - // negative) or from the specified offset to the end of data (if |bytes| is - // positive). - // All individual ranges will be sent as part of "Range:" HTTP request header. - void AddRange(int64_t bytes); - - // Makes a request for a subrange of data. Specifies a full range with - // start and end bytes from the beginning of the requested data. - // All individual ranges will be sent as part of "Range:" HTTP request header. - void AddRange(uint64_t from_byte, uint64_t to_byte); - - // Returns the request URL - std::string GetRequestURL() const; - - // Gets/Sets a request referer URL (sent as "Referer:" request header). - void SetReferer(const char* referer); - std::string GetReferer() const; - - // Gets/Sets a user agent string (sent as "User-Agent:" request header). - void SetUserAgent(const char* user_agent); - std::string GetUserAgent() const; - - // Sends the request to the server and returns the response object. - // In case the server couldn't be reached for whatever reason, returns - // empty unique_ptr (null). In such a case, the additional error information - // can be returned through the optional supplied |error| parameter. - std::unique_ptr<Response> GetResponse(chromeos::ErrorPtr* error); - - private: - // Helper function to create an http::Connection and send off request headers. - bool SendRequestIfNeeded(chromeos::ErrorPtr* error); - - // Implementation that provides particular HTTP transport. - std::shared_ptr<Transport> transport_; - - // An established connection for adding request body. This connection - // is maintained by the request object after the headers have been - // sent and before the response is requested. - std::unique_ptr<Connection> connection_; - - // Full request URL, such as "http://www.host.com/path/to/object" - std::string request_url_; - // HTTP request verb, such as "GET", "POST", "PUT", ... - std::string method_; - - // Referrer URL, if any. Sent to the server via "Referer: " header. - std::string referer_; - // User agent string, if any. Sent to the server via "User-Agent: " header. - std::string user_agent_; - // Content type of the request body data. - // Sent to the server via "Content-Type: " header. - std::string content_type_; - // List of acceptable response data types. - // Sent to the server via "Accept: " header. - std::string accept_ = "*/*"; - - // List of optional request headers provided by the caller. - // After request has been sent, contains the received response headers. - std::map<std::string, std::string> headers_; - // List of optional data ranges to request partial content from the server. - // Sent to 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. - // E.g. range (10,range_value_omitted) represents bytes from 10 to the end - // of the data stream. - const uint64_t range_value_omitted = std::numeric_limits<uint64_t>::max(); - - DISALLOW_COPY_AND_ASSIGN(Request); -}; - -/////////////////////////////////////////////////////////////////////////////// -// Response class is returned from Request::GetResponse() and is a way -// to get to response status, error codes, response HTTP headers and response -// data (body) if available. -/////////////////////////////////////////////////////////////////////////////// -class Response { - public: - explicit Response(std::unique_ptr<Connection> connection); - ~Response(); - - // Returns true if server returned a success code (status code below 400). - bool IsSuccessful() const; - - // Returns the HTTP status code (e.g. 200 for success) - int GetStatusCode() const; - - // Returns the status text (e.g. for error 403 it could be "NOT AUTHORIZED"). - std::string GetStatusText() const; - - // Returns the content type of the response data. - std::string GetContentType() const; - - // Returns response data as a byte array - const std::vector<unsigned char>& GetData() const; - - // Returns response data as a string - std::string GetDataAsString() const; - - // Returns a value of a given response HTTP header. - std::string GetHeader(const char* header_name) const; - - private: - std::unique_ptr<Connection> connection_; - std::vector<unsigned char> response_data_; - DISALLOW_COPY_AND_ASSIGN(Response); -}; - -} // namespace http -} // namespace buffet - -#endif // BUFFET_HTTP_REQUEST_H_
diff --git a/buffet/http_transport.h b/buffet/http_transport.h deleted file mode 100644 index 15b0bd6..0000000 --- a/buffet/http_transport.h +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BUFFET_HTTP_TRANSPORT_H_ -#define BUFFET_HTTP_TRANSPORT_H_ - -#include <memory> -#include <string> -#include <utility> -#include <vector> - -#include <base/basictypes.h> -#include <chromeos/error.h> - -namespace buffet { -namespace http { - -typedef std::vector<std::pair<std::string, std::string>> HeaderList; - -class Request; -class Connection; - -/////////////////////////////////////////////////////////////////////////////// -// Transport is a base class for specific implementation of HTTP communication. -// This class (and its underlying implementation) is used by http::Request and -// http::Response classes to provide HTTP functionality to the clients. -/////////////////////////////////////////////////////////////////////////////// -class Transport { - public: - Transport() = default; - virtual ~Transport() = default; - - // Creates a connection object and initializes it with the specified data. - // |transport| is a shared pointer to this transport object instance, - // used to maintain the object alive as long as the connection exists. - virtual std::unique_ptr<Connection> CreateConnection( - std::shared_ptr<Transport> transport, - const std::string& url, - const std::string& method, - const HeaderList& headers, - const std::string& user_agent, - const std::string& referer, - chromeos::ErrorPtr* error) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(Transport); -}; - -} // namespace http -} // namespace buffet - -#endif // BUFFET_HTTP_TRANSPORT_H_
diff --git a/buffet/http_transport_curl.cc b/buffet/http_transport_curl.cc deleted file mode 100644 index d5a30fe..0000000 --- a/buffet/http_transport_curl.cc +++ /dev/null
@@ -1,81 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "buffet/http_transport_curl.h" - -#include <base/logging.h> - -#include "buffet/http_connection_curl.h" -#include "buffet/http_request.h" - -namespace buffet { -namespace http { -namespace curl { - -const char kErrorDomain[] = "http_transport"; - -Transport::Transport() { - VLOG(1) << "curl::Transport created"; -} - -Transport::~Transport() { - VLOG(1) << "curl::Transport destroyed"; -} - -std::unique_ptr<http::Connection> Transport::CreateConnection( - std::shared_ptr<http::Transport> transport, - const std::string& url, - const std::string& method, - const HeaderList& headers, - const std::string& user_agent, - const std::string& referer, - chromeos::ErrorPtr* error) { - CURL* curl_handle = curl_easy_init(); - if (!curl_handle) { - LOG(ERROR) << "Failed to initialize CURL"; - chromeos::Error::AddTo(error, http::curl::kErrorDomain, "curl_init_failed", - "Failed to initialize CURL"); - return std::unique_ptr<http::Connection>(); - } - - LOG(INFO) << "Sending a " << method << " request to " << url; - curl_easy_setopt(curl_handle, CURLOPT_URL, url.c_str()); - - if (!user_agent.empty()) { - curl_easy_setopt(curl_handle, - CURLOPT_USERAGENT, user_agent.c_str()); - } - - if (!referer.empty()) { - curl_easy_setopt(curl_handle, - CURLOPT_REFERER, referer.c_str()); - } - - // Setup HTTP request method and optional request body. - if (method == request_type::kGet) { - curl_easy_setopt(curl_handle, CURLOPT_HTTPGET, 1L); - } else if (method == request_type::kHead) { - curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1L); - } else if (method == request_type::kPut) { - curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L); - } else { - // POST and custom request methods - curl_easy_setopt(curl_handle, CURLOPT_POST, 1L); - curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, nullptr); - if (method != request_type::kPost) - curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, method.c_str()); - } - - 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, error)) { - connection.reset(); - } - return connection; -} - -} // namespace curl -} // namespace http -} // namespace buffet
diff --git a/buffet/http_transport_curl.h b/buffet/http_transport_curl.h deleted file mode 100644 index 17b6acc..0000000 --- a/buffet/http_transport_curl.h +++ /dev/null
@@ -1,47 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BUFFET_HTTP_TRANSPORT_CURL_H_ -#define BUFFET_HTTP_TRANSPORT_CURL_H_ - -#include <string> - -#include "buffet/http_transport.h" - -namespace buffet { -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) -// is used by http::Request and http::Response classes to provide HTTP -// functionality to the clients. -// See http_transport.h for more details. -/////////////////////////////////////////////////////////////////////////////// -class Transport : public http::Transport { - public: - Transport(); - virtual ~Transport(); - - std::unique_ptr<http::Connection> CreateConnection( - std::shared_ptr<http::Transport> transport, - const std::string& url, - const std::string& method, - const HeaderList& headers, - const std::string& user_agent, - const std::string& referer, - chromeos::ErrorPtr* error) override; - - private: - DISALLOW_COPY_AND_ASSIGN(Transport); -}; - -} // namespace curl -} // namespace http -} // namespace buffet - -#endif // BUFFET_HTTP_TRANSPORT_CURL_H_
diff --git a/buffet/http_transport_fake.cc b/buffet/http_transport_fake.cc deleted file mode 100644 index 9aefbe3..0000000 --- a/buffet/http_transport_fake.cc +++ /dev/null
@@ -1,264 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "buffet/http_transport_fake.h" - -#include <utility> - -#include <base/json/json_reader.h> -#include <base/json/json_writer.h> -#include <base/logging.h> -#include <chromeos/bind_lambda.h> -#include <chromeos/mime_utils.h> -#include <chromeos/string_utils.h> -#include <chromeos/url_utils.h> - -#include "buffet/http_connection_fake.h" -#include "buffet/http_request.h" - -namespace buffet { - -using http::fake::Transport; -using http::fake::ServerRequestResponseBase; -using http::fake::ServerRequest; -using http::fake::ServerResponse; - -Transport::Transport() { - VLOG(1) << "fake::Transport created"; -} - -Transport::~Transport() { - VLOG(1) << "fake::Transport destroyed"; -} - -std::unique_ptr<http::Connection> Transport::CreateConnection( - std::shared_ptr<http::Transport> transport, - const std::string& url, - const std::string& method, - const HeaderList& headers, - const std::string& user_agent, - const std::string& referer, - chromeos::ErrorPtr* error) { - HeaderList headers_copy = headers; - if (!user_agent.empty()) { - headers_copy.push_back(std::make_pair(http::request_header::kUserAgent, - user_agent)); - } - if (!referer.empty()) { - headers_copy.push_back(std::make_pair(http::request_header::kReferer, - referer)); - } - 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, error)) - connection.reset(); - request_count_++; - return connection; -} - -static inline std::string GetHandlerMapKey(const std::string& url, - const std::string& method) { - return method + ":" + url; -} - -void Transport::AddHandler(const std::string& url, const std::string& method, - const HandlerCallback& handler) { - handlers_.insert(std::make_pair(GetHandlerMapKey(url, method), handler)); -} - -void Transport::AddSimpleReplyHandler(const std::string& url, - const std::string& method, - int status_code, - const std::string& reply_text, - const std::string& mime_type) { - auto handler = [status_code, reply_text, mime_type]( - const ServerRequest& request, ServerResponse* response) { - response->ReplyText(status_code, reply_text, mime_type.c_str()); - }; - AddHandler(url, method, base::Bind(handler)); -} - -Transport::HandlerCallback Transport::GetHandler( - const std::string& url, const std::string& method) const { - // First try the exact combination of URL/Method - auto p = handlers_.find(GetHandlerMapKey(url, method)); - if (p != handlers_.end()) - return p->second; - // If not found, try URL/* - p = handlers_.find(GetHandlerMapKey(url, "*")); - if (p != handlers_.end()) - return p->second; - // If still not found, try */method - p = handlers_.find(GetHandlerMapKey("*", method)); - if (p != handlers_.end()) - return p->second; - // Finally, try */* - p = handlers_.find(GetHandlerMapKey("*", "*")); - return (p != handlers_.end()) ? p->second : HandlerCallback(); -} - -void ServerRequestResponseBase::AddData(const void* data, size_t data_size) { - auto bytes = reinterpret_cast<const unsigned char*>(data); - data_.insert(data_.end(), bytes, bytes + data_size); -} - -std::string ServerRequestResponseBase::GetDataAsString() const { - if (data_.empty()) - return std::string(); - auto chars = reinterpret_cast<const char*>(data_.data()); - return std::string(chars, data_.size()); -} - -std::unique_ptr<base::DictionaryValue> - ServerRequestResponseBase::GetDataAsJson() const { - if (chromeos::mime::RemoveParameters( - GetHeader(request_header::kContentType)) == - chromeos::mime::application::kJson) { - auto value = base::JSONReader::Read(GetDataAsString()); - if (value) { - base::DictionaryValue* dict = nullptr; - if (value->GetAsDictionary(&dict)) { - return std::unique_ptr<base::DictionaryValue>(dict); - } else { - delete value; - } - } - } - return std::unique_ptr<base::DictionaryValue>(); -} - -void ServerRequestResponseBase::AddHeaders(const HeaderList& headers) { - for (const auto& pair : headers) { - if (pair.second.empty()) - headers_.erase(pair.first); - else - headers_.insert(pair); - } -} - -std::string ServerRequestResponseBase::GetHeader( - const std::string& header_name) const { - auto p = headers_.find(header_name); - return p != headers_.end() ? p->second : std::string(); -} - -ServerRequest::ServerRequest(const std::string& url, - const std::string& method) : method_(method) { - auto params = chromeos::url::GetQueryStringParameters(url); - url_ = chromeos::url::RemoveQueryString(url, true); - form_fields_.insert(params.begin(), params.end()); -} - -std::string ServerRequest::GetFormField(const std::string& field_name) const { - if (!form_fields_parsed_) { - std::string mime_type = chromeos::mime::RemoveParameters( - GetHeader(request_header::kContentType)); - if (mime_type == chromeos::mime::application::kWwwFormUrlEncoded && - !GetData().empty()) { - auto fields = chromeos::data_encoding::WebParamsDecode(GetDataAsString()); - form_fields_.insert(fields.begin(), fields.end()); - } - form_fields_parsed_ = true; - } - auto p = form_fields_.find(field_name); - return p != form_fields_.end() ? p->second : std::string(); -} - -void ServerResponse::Reply(int status_code, const void* data, size_t data_size, - const char* mime_type) { - data_.clear(); - status_code_ = status_code; - AddData(data, data_size); - AddHeaders({ - {response_header::kContentLength, - chromeos::string_utils::ToString(data_size)}, - {response_header::kContentType, mime_type} - }); -} - -void ServerResponse::ReplyText(int status_code, const std::string& text, - const char* mime_type) { - Reply(status_code, text.data(), text.size(), mime_type); -} - -void ServerResponse::ReplyJson(int status_code, const base::Value* json) { - std::string text; - base::JSONWriter::WriteWithOptions(json, - base::JSONWriter::OPTIONS_PRETTY_PRINT, - &text); - std::string mime_type = chromeos::mime::AppendParameter( - chromeos::mime::application::kJson, - chromeos::mime::parameters::kCharset, - "utf-8"); - ReplyText(status_code, text, mime_type.c_str()); -} - -void ServerResponse::ReplyJson(int status_code, - const http::FormFieldList& fields) { - base::DictionaryValue json; - for (const auto& pair : fields) { - json.SetString(pair.first, pair.second); - } - ReplyJson(status_code, &json); -} - -std::string ServerResponse::GetStatusText() const { - static std::vector<std::pair<int, const char*>> status_text_map = { - {100, "Continue"}, - {101, "Switching Protocols"}, - {102, "Processing"}, - {200, "OK"}, - {201, "Created"}, - {202, "Accepted"}, - {203, "Non-Authoritative Information"}, - {204, "No Content"}, - {205, "Reset Content"}, - {206, "Partial Content"}, - {207, "Multi-Status"}, - {208, "Already Reported"}, - {226, "IM Used"}, - {300, "Multiple Choices"}, - {301, "Moved Permanently"}, - {302, "Found"}, - {303, "See Other"}, - {304, "Not Modified"}, - {305, "Use Proxy"}, - {306, "Switch Proxy"}, - {307, "Temporary Redirect"}, - {308, "Permanent Redirect"}, - {400, "Bad Request"}, - {401, "Unauthorized"}, - {402, "Payment Required"}, - {403, "Forbidden"}, - {404, "Not Found"}, - {405, "Method Not Allowed"}, - {406, "Not Acceptable"}, - {407, "Proxy Authentication Required"}, - {408, "Request Timeout"}, - {409, "Conflict"}, - {410, "Gone"}, - {411, "Length Required"}, - {412, "Precondition Failed"}, - {413, "Request Entity Too Large"}, - {414, "Request - URI Too Long"}, - {415, "Unsupported Media Type"}, - {429, "Too Many Requests"}, - {431, "Request Header Fields Too Large"}, - {500, "Internal Server Error"}, - {501, "Not Implemented"}, - {502, "Bad Gateway"}, - {503, "Service Unavailable"}, - {504, "Gateway Timeout"}, - {505, "HTTP Version Not Supported"}, - }; - - for (const auto& pair : status_text_map) { - if (pair.first == status_code_) - return pair.second; - } - return std::string(); -} - -} // namespace buffet
diff --git a/buffet/http_transport_fake.h b/buffet/http_transport_fake.h deleted file mode 100644 index 1e58c65..0000000 --- a/buffet/http_transport_fake.h +++ /dev/null
@@ -1,223 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BUFFET_HTTP_TRANSPORT_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> - -#include "buffet/http_transport.h" -#include "buffet/http_utils.h" - -namespace buffet { -namespace http { -namespace fake { - -class ServerRequest; -class ServerResponse; -class Connection; - -/////////////////////////////////////////////////////////////////////////////// -// A fake implementation of http::Transport that simulates HTTP communication -// with a server. -/////////////////////////////////////////////////////////////////////////////// -class Transport : public http::Transport { - public: - Transport(); - virtual ~Transport(); - - // Server handler callback signature. - typedef base::Callback<void(const ServerRequest&, ServerResponse*)> - HandlerCallback; - - // This method allows the test code to provide a callback to handle requests - // for specific URL/HTTP-verb combination. When a specific |method| request - // is made on the given |url|, the |handler| will be invoked and all the - // request data will be filled in the |ServerRequest| parameter. Any server - // response should be returned through the |ServerResponse| parameter. - // Either |method| or |url| (or both) can be specified as "*" to handle - // any requests. So, ("http://localhost","*") will handle any request type - // on that URL and ("*","GET") will handle any GET requests. - // The lookup starts with the most specific data pair to the catch-all (*,*). - void AddHandler(const std::string& url, const std::string& method, - const HandlerCallback& handler); - // Simple version of AddHandler. AddSimpleReplyHandler just returns the - // specified text response of given MIME type. - void AddSimpleReplyHandler(const std::string& url, - const std::string& method, - int status_code, - const std::string& reply_text, - const std::string& mime_type); - // Retrieve a handler for specific |url| and request |method|. - HandlerCallback GetHandler(const std::string& url, - const std::string& method) const; - - // For tests that want to assert on the number of HTTP requests sent, - // these methods can be used to do just that. - int GetRequestCount() const { return request_count_; } - void ResetRequestCount() { request_count_ = 0; } - - // Overload from http::Transport - std::unique_ptr<http::Connection> CreateConnection( - std::shared_ptr<http::Transport> transport, - const std::string& url, - const std::string& method, - const HeaderList& headers, - const std::string& user_agent, - const std::string& referer, - chromeos::ErrorPtr* error) override; - - private: - // 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); -}; - -/////////////////////////////////////////////////////////////////////////////// -// A base class for ServerRequest and ServerResponse. It provides common -// functionality to work with request/response HTTP headers and data. -/////////////////////////////////////////////////////////////////////////////// -class ServerRequestResponseBase { - public: - ServerRequestResponseBase() = default; - - // Add/retrieve request/response body data. - void AddData(const void* data, size_t data_size); - const std::vector<unsigned char>& GetData() const { return data_; } - std::string GetDataAsString() const; - std::unique_ptr<base::DictionaryValue> GetDataAsJson() const; - - // Add/retrieve request/response HTTP headers. - void AddHeaders(const HeaderList& headers); - std::string GetHeader(const std::string& header_name) const; - const std::map<std::string, std::string>& GetHeaders() const { - return headers_; - } - - protected: - // Data buffer. - std::vector<unsigned char> data_; - // Header map. - std::map<std::string, std::string> headers_; - - private: - DISALLOW_COPY_AND_ASSIGN(ServerRequestResponseBase); -}; - -/////////////////////////////////////////////////////////////////////////////// -// A container class that encapsulates all the HTTP server request information. -/////////////////////////////////////////////////////////////////////////////// -class ServerRequest : public ServerRequestResponseBase { - public: - ServerRequest(const std::string& url, const std::string& method); - - // Get the actual request URL. Does not include the query string or fragment. - const std::string& GetURL() const { return url_; } - // Get the request method. - const std::string& GetMethod() const { return method_; } - // Get the POST/GET request parameters. These are parsed query string - // parameters from the URL. In addition, for POST requests with - // application/x-www-form-urlencoded content type, the request body is also - // parsed and individual fields can be accessed through this method. - std::string GetFormField(const std::string& field_name) const; - - private: - // Request URL (without query string or URL fragment). - std::string url_; - // Request method - std::string method_; - // List of available request data form fields. - mutable std::map<std::string, std::string> form_fields_; - // 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 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 -// following response headers: -// Content-Length -// Content-Type -/////////////////////////////////////////////////////////////////////////////// -class ServerResponse : public ServerRequestResponseBase { - public: - ServerResponse() = default; - - // Generic reply method. - void Reply(int status_code, const void* data, size_t data_size, - const char* mime_type); - // Reply with text body. - void ReplyText(int status_code, const std::string& text, - const char* mime_type); - // Reply with JSON object. The content type will be "application/json". - void ReplyJson(int status_code, const base::Value* json); - // Special form for JSON response for simple objects that have a flat - // list of key-value pairs of string type. - void ReplyJson(int status_code, const FormFieldList& fields); - - // Specialized overload to send the binary data as an array of simple - // data elements. Only trivial data types (scalars, POD structures, etc) - // can be used. - template<typename T> - void Reply(int status_code, const std::vector<T>& data, - const char* mime_type) { - // Make sure T doesn't have virtual functions, custom constructors, etc. - static_assert(std::is_trivial<T>::value, "Only simple data is supported"); - Reply(status_code, data.data(), data.size() * sizeof(T), mime_type); - } - - // Specialized overload to send the binary data. - // Only trivial data types (scalars, POD structures, etc) can be used. - template<typename T> - void Reply(int status_code, const T& data, const char* mime_type) { - // Make sure T doesn't have virtual functions, custom constructors, etc. - static_assert(std::is_trivial<T>::value, "Only simple data is supported"); - Reply(status_code, &data, sizeof(T), mime_type); - } - - // For handlers that want to simulate versions of HTTP protocol other - // than HTTP/1.1, call this method with the custom version string, - // for example "HTTP/1.0". - void SetProtocolVersion(const std::string& protocol_version) { - protocol_version_ = protocol_version; - } - - protected: - // These methods are helpers to implement corresponding functionality - // of fake::Connection. - friend class Connection; - // Helper for fake::Connection::GetResponseStatusCode(). - int GetStatusCode() const { return status_code_; } - // Helper for fake::Connection::GetResponseStatusText(). - std::string GetStatusText() const; - // Helper for fake::Connection::GetProtocolVersion(). - std::string GetProtocolVersion() const { return protocol_version_; } - - private: - int status_code_ = 0; - std::string protocol_version_ = "HTTP/1.1"; - - DISALLOW_COPY_AND_ASSIGN(ServerResponse); -}; - -} // namespace fake -} // namespace http -} // namespace buffet - -#endif // BUFFET_HTTP_TRANSPORT_FAKE_H_
diff --git a/buffet/http_utils.cc b/buffet/http_utils.cc deleted file mode 100644 index d61d176..0000000 --- a/buffet/http_utils.cc +++ /dev/null
@@ -1,171 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "buffet/http_utils.h" - -#include <algorithm> - -#include <base/json/json_reader.h> -#include <base/json/json_writer.h> -#include <base/values.h> -#include <chromeos/data_encoding.h> -#include <chromeos/error_codes.h> -#include <chromeos/mime_utils.h> - -using chromeos::mime::AppendParameter; -using chromeos::mime::RemoveParameters; - -namespace buffet { -namespace http { - -std::unique_ptr<Response> Get(const std::string& url, - const HeaderList& headers, - std::shared_ptr<Transport> transport, - chromeos::ErrorPtr* error) { - return SendRequest(request_type::kGet, url, nullptr, 0, nullptr, - headers, transport, error); -} - -std::string GetAsString(const std::string& url, - const HeaderList& headers, - std::shared_ptr<Transport> transport, - chromeos::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, - chromeos::ErrorPtr* error) { - Request request(url, request_type::kHead, transport); - 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, - chromeos::ErrorPtr* error) { - if (mime_type == nullptr) { - mime_type = chromeos::mime::application::kWwwFormUrlEncoded; - } - - return PostBinary(url, data, strlen(data), mime_type, headers, transport, - error); -} - -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, - chromeos::ErrorPtr* error) { - Request request(url, method, transport); - request.AddHeaders(headers); - if (data_size > 0) { - if (mime_type == nullptr) { - mime_type = chromeos::mime::application::kOctet_stream; - } - request.SetContentType(mime_type); - if (!request.AddRequestBody(data, data_size, error)) - return std::unique_ptr<Response>(); - } - 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, - chromeos::ErrorPtr* error) { - return SendRequest(request_type::kPost, url, - 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, - chromeos::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, error); -} - - -std::unique_ptr<Response> PostJson(const std::string& url, - const base::Value* json, - const HeaderList& headers, - std::shared_ptr<Transport> transport, - chromeos::ErrorPtr* error) { - std::string data; - if (json) - base::JSONWriter::Write(json, &data); - std::string mime_type = AppendParameter(chromeos::mime::application::kJson, - chromeos::mime::parameters::kCharset, - "utf-8"); - return PostBinary(url, data.c_str(), data.size(), - 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, - chromeos::ErrorPtr* error) { - std::string data; - if (json) - base::JSONWriter::Write(json, &data); - std::string mime_type = AppendParameter(chromeos::mime::application::kJson, - chromeos::mime::parameters::kCharset, - "utf-8"); - return SendRequest(request_type::kPatch, url, data.c_str(), data.size(), - mime_type.c_str(), headers, transport, error); -} - -std::unique_ptr<base::DictionaryValue> ParseJsonResponse( - const Response* response, int* status_code, chromeos::ErrorPtr* error) { - if (!response) - return std::unique_ptr<base::DictionaryValue>(); - - 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 != chromeos::mime::application::kJson && - content_type != chromeos::mime::text::kPlain) { - chromeos::Error::AddTo(error, chromeos::errors::json::kDomain, - "non_json_content_type", - "Unexpected response content type: " + content_type); - return std::unique_ptr<base::DictionaryValue>(); - } - - 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) { - chromeos::Error::AddTo(error, chromeos::errors::json::kDomain, - chromeos::errors::json::kParseError, error_message); - return std::unique_ptr<base::DictionaryValue>(); - } - base::DictionaryValue* dict_value = nullptr; - if (!value->GetAsDictionary(&dict_value)) { - delete value; - chromeos::Error::AddTo(error, chromeos::errors::json::kDomain, - chromeos::errors::json::kObjectExpected, - "Response is not a valid JSON object"); - return std::unique_ptr<base::DictionaryValue>(); - } - return std::unique_ptr<base::DictionaryValue>(dict_value); -} - -} // namespace http -} // namespace buffet
diff --git a/buffet/http_utils.h b/buffet/http_utils.h deleted file mode 100644 index 938905d..0000000 --- a/buffet/http_utils.h +++ /dev/null
@@ -1,180 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef BUFFET_HTTP_UTILS_H_ -#define BUFFET_HTTP_UTILS_H_ - -#include <string> -#include <utility> -#include <vector> - -#include <chromeos/error.h> - -#include "buffet/http_request.h" - -namespace base { -class Value; -class DictionaryValue; -} // namespace base - -namespace buffet { -namespace http { - -typedef std::vector<std::pair<std::string, std::string>> FormFieldList; - -//////////////////////////////////////////////////////////////////////////////// -// The following are simple utility helper functions for common HTTP operations -// that use http::Request object behind the scenes and set it up accordingly. -// -// For more advanced functionality you need to use Request/Response objects -// directly. -//////////////////////////////////////////////////////////////////////////////// - -// Performs a generic HTTP request with binary data. Success status, -// returned data and additional information (such as returned HTTP headers) -// can be obtained from the returned Response object. -// If data MIME type is not specified, "application/octet-stream" is assumed. -std::unique_ptr<Response> 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, - chromeos::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, - chromeos::ErrorPtr* error); -inline std::string GetAsString(const std::string& url, - std::shared_ptr<Transport> transport, - chromeos::ErrorPtr* error) { - return GetAsString(url, HeaderList(), transport, error); -} - -// Performs a GET request. Success status, returned data and additional -// information (such as returned HTTP headers) can be obtained from -// the returned Response object. -std::unique_ptr<Response> Get(const std::string& url, - const HeaderList& headers, - std::shared_ptr<Transport> transport, - chromeos::ErrorPtr* error); -inline std::unique_ptr<Response> Get( - const std::string& url, std::shared_ptr<Transport> transport, - chromeos::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, - chromeos::ErrorPtr* error); - -// Performs a POST request with binary data. Success status, returned data -// and additional information (such as returned HTTP headers) can be obtained -// from the returned Response object. -// If data MIME type is not specified, "application/octet-stream" is assumed -std::unique_ptr<Response> PostBinary(const std::string& url, - const void* data, - size_t data_size, - const char* mime_type, - const HeaderList& headers, - std::shared_ptr<Transport> transport, - chromeos::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, - chromeos::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, chromeos::ErrorPtr* error) { - return PostBinary(url, data, data_size, nullptr, transport, error); -} - -// Performs a POST request with text data. Success status, returned data -// and additional information (such as returned HTTP headers) can be obtained -// from the returned Response object. -// If data MIME type is not specified, "application/x-www-form-urlencoded" -// is assumed. -std::unique_ptr<Response> PostText(const std::string& url, - const char* data, - const char* mime_type, - const HeaderList& headers, - std::shared_ptr<Transport> transport, - chromeos::ErrorPtr* error); - -inline std::unique_ptr<Response> PostText( - const std::string& url, const char* data, const char* mime_type, - std::shared_ptr<Transport> transport, chromeos::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, chromeos::ErrorPtr* error) { - return PostText(url, data, nullptr, transport, error); -} - -// Performs a POST request with form data. Success status, returned data -// and additional information (such as returned HTTP headers) can be obtained -// from the returned Response object. The form data is a list of key/value -// 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, - chromeos::ErrorPtr* error); - -inline std::unique_ptr<Response> PostFormData( - const std::string& url, const FormFieldList& data, - std::shared_ptr<Transport> transport, chromeos::ErrorPtr* error) { - return PostFormData(url, data, HeaderList(), transport, error); -} - -// Performs a POST request with JSON data. Success status, returned data -// and additional information (such as returned HTTP headers) can be obtained -// from the returned Response object. If a JSON response is expected, -// use ParseJsonResponse() method on the returned Response object. -std::unique_ptr<Response> PostJson(const std::string& url, - const base::Value* json, - const HeaderList& headers, - std::shared_ptr<Transport> transport, - chromeos::ErrorPtr* error); - -inline std::unique_ptr<Response> PostJson( - const std::string& url, const base::Value* json, - std::shared_ptr<Transport> transport, chromeos::ErrorPtr* error) { - return PostJson(url, json, HeaderList(), transport, error); -} - -// Performs a PATCH request with JSON data. Success status, returned data -// and additional information (such as returned HTTP headers) can be obtained -// from the returned Response object. If a JSON response is expected, -// use ParseJsonResponse() method on the returned Response object. -std::unique_ptr<Response> PatchJson(const std::string& url, - const base::Value* json, - const HeaderList& headers, - std::shared_ptr<Transport> transport, - chromeos::ErrorPtr* error); - -inline std::unique_ptr<Response> PatchJson( - const std::string& url, const base::Value* json, - std::shared_ptr<Transport> transport, chromeos::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| 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, chromeos::ErrorPtr* error); - -} // namespace http -} // namespace buffet - -#endif // BUFFET_HTTP_UTILS_H_
diff --git a/buffet/http_utils_unittest.cc b/buffet/http_utils_unittest.cc deleted file mode 100644 index 8f40ad7..0000000 --- a/buffet/http_utils_unittest.cc +++ /dev/null
@@ -1,341 +0,0 @@ -// Copyright 2014 The Chromium OS Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <string> -#include <vector> - -#include <base/values.h> -#include <chromeos/bind_lambda.h> -#include <chromeos/mime_utils.h> -#include <chromeos/string_utils.h> -#include <chromeos/url_utils.h> -#include <gtest/gtest.h> - -#include "buffet/http_transport_fake.h" -#include "buffet/http_utils.h" - -using namespace buffet; // NOLINT(build/namespaces) -using namespace buffet::http; // NOLINT(build/namespaces) - -static const char kFakeUrl[] = "http://localhost"; -static const char kEchoUrl[] = "http://localhost/echo"; -static const char kMethodEchoUrl[] = "http://localhost/echo/method"; - -///////////////////// Generic helper request handlers ///////////////////////// -// Returns the request data back with the same content type. -static void EchoDataHandler(const fake::ServerRequest& request, - fake::ServerResponse* response) { - response->Reply(status_code::Ok, request.GetData(), - request.GetHeader(request_header::kContentType).c_str()); -} - -// Returns the request method as a plain text response. -static void EchoMethodHandler(const fake::ServerRequest& request, - fake::ServerResponse* response) { - response->ReplyText(status_code::Ok, request.GetMethod(), - chromeos::mime::text::kPlain); -} - -/////////////////////////////////////////////////////////////////////////////// -TEST(HttpUtils, SendRequest_BinaryData) { - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kEchoUrl, request_type::kPost, - base::Bind(EchoDataHandler)); - - // Test binary data round-tripping. - 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(), - chromeos::mime::application::kOctet_stream, - HeaderList(), transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::application::kOctet_stream, - response->GetContentType()); - EXPECT_EQ(custom_data.size(), response->GetData().size()); - EXPECT_EQ(custom_data, response->GetData()); -} - -TEST(HttpUtils, SendRequest_Post) { - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler)); - - // Test binary data round-tripping. - 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(), - chromeos::mime::application::kOctet_stream, - HeaderList(), transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::text::kPlain, response->GetContentType()); - EXPECT_EQ(request_type::kPost, response->GetDataAsString()); -} - -TEST(HttpUtils, SendRequest_Get) { - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler)); - - auto response = http::SendRequest(request_type::kGet, kMethodEchoUrl, - nullptr, 0, nullptr, - HeaderList(), transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::text::kPlain, response->GetContentType()); - EXPECT_EQ(request_type::kGet, response->GetDataAsString()); -} - -TEST(HttpUtils, SendRequest_Put) { - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler)); - - auto response = http::SendRequest(request_type::kPut, kMethodEchoUrl, - nullptr, 0, nullptr, - HeaderList(), transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::text::kPlain, response->GetContentType()); - EXPECT_EQ(request_type::kPut, response->GetDataAsString()); -} - -TEST(HttpUtils, SendRequest_NotFound) { - std::shared_ptr<fake::Transport> transport(new fake::Transport); - // Test failed response (URL not found). - auto response = http::SendRequest(request_type::kGet, "http://blah.com", - nullptr, 0, nullptr, - HeaderList(), transport, nullptr); - EXPECT_FALSE(response->IsSuccessful()); - EXPECT_EQ(status_code::NotFound, response->GetStatusCode()); -} - -TEST(HttpUtils, SendRequest_Headers) { - std::shared_ptr<fake::Transport> transport(new fake::Transport); - - static const char json_echo_url[] = "http://localhost/echo/json"; - auto JsonEchoHandler = [](const fake::ServerRequest& request, - fake::ServerResponse* response) { - base::DictionaryValue json; - json.SetString("method", request.GetMethod()); - json.SetString("data", request.GetDataAsString()); - for (const auto& pair : request.GetHeaders()) { - json.SetString("header." + pair.first, pair.second); - } - response->ReplyJson(status_code::Ok, &json); - }; - transport->AddHandler(json_echo_url, "*", - base::Bind(JsonEchoHandler)); - auto response = http::SendRequest( - request_type::kPost, json_echo_url, "abcd", 4, - chromeos::mime::application::kOctet_stream, { - {request_header::kCookie, "flavor=vanilla"}, - {request_header::kIfMatch, "*"}, - }, transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::application::kJson, - chromeos::mime::RemoveParameters(response->GetContentType())); - auto json = ParseJsonResponse(response.get(), nullptr, nullptr); - std::string value; - EXPECT_TRUE(json->GetString("method", &value)); - EXPECT_EQ(request_type::kPost, value); - EXPECT_TRUE(json->GetString("data", &value)); - EXPECT_EQ("abcd", value); - EXPECT_TRUE(json->GetString("header.Cookie", &value)); - EXPECT_EQ("flavor=vanilla", value); - EXPECT_TRUE(json->GetString("header.Content-Type", &value)); - EXPECT_EQ(chromeos::mime::application::kOctet_stream, value); - EXPECT_TRUE(json->GetString("header.Content-Length", &value)); - EXPECT_EQ("4", value); - EXPECT_TRUE(json->GetString("header.If-Match", &value)); - EXPECT_EQ("*", value); -} - -TEST(HttpUtils, Get) { - // Sends back the "?test=..." portion of URL. - // So if we do GET "http://localhost?test=blah", this handler responds - // with "blah" as text/plain. - auto GetHandler = [](const fake::ServerRequest& request, - fake::ServerResponse* response) { - EXPECT_EQ(request_type::kGet, request.GetMethod()); - EXPECT_EQ("0", request.GetHeader(request_header::kContentLength)); - EXPECT_EQ("", request.GetHeader(request_header::kContentType)); - response->ReplyText(status_code::Ok, request.GetFormField("test"), - chromeos::mime::text::kPlain); - }; - - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kFakeUrl, request_type::kGet, base::Bind(GetHandler)); - transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler)); - - // Make sure Get/GetAsString actually do the GET request - auto response = http::Get(kMethodEchoUrl, transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::text::kPlain, response->GetContentType()); - EXPECT_EQ(request_type::kGet, response->GetDataAsString()); - EXPECT_EQ(request_type::kGet, - http::GetAsString(kMethodEchoUrl, transport, nullptr)); - - for (std::string data : {"blah", "some data", ""}) { - std::string url = chromeos::url::AppendQueryParam(kFakeUrl, "test", data); - EXPECT_EQ(data, http::GetAsString(url, transport, nullptr)); - } -} - -TEST(HttpUtils, Head) { - auto HeadHandler = [](const fake::ServerRequest& request, - fake::ServerResponse* response) { - EXPECT_EQ(request_type::kHead, request.GetMethod()); - EXPECT_EQ("0", request.GetHeader(request_header::kContentLength)); - EXPECT_EQ("", request.GetHeader(request_header::kContentType)); - response->ReplyText(status_code::Ok, "blah", - chromeos::mime::text::kPlain); - }; - - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kFakeUrl, request_type::kHead, base::Bind(HeadHandler)); - - auto response = http::Head(kFakeUrl, transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::text::kPlain, response->GetContentType()); - EXPECT_EQ("", response->GetDataAsString()); // Must not have actual body. - EXPECT_EQ("4", response->GetHeader(request_header::kContentLength)); -} - -TEST(HttpUtils, PostBinary) { - auto Handler = [](const fake::ServerRequest& request, - fake::ServerResponse* response) { - EXPECT_EQ(request_type::kPost, request.GetMethod()); - EXPECT_EQ("256", request.GetHeader(request_header::kContentLength)); - EXPECT_EQ(chromeos::mime::application::kOctet_stream, - request.GetHeader(request_header::kContentType)); - const auto& data = request.GetData(); - EXPECT_EQ(256, data.size()); - - // Sum up all the bytes. - int sum = std::accumulate(data.begin(), data.end(), 0); - EXPECT_EQ(32640, sum); // sum(i, i => [0, 255]) = 32640. - response->ReplyText(status_code::Ok, "", chromeos::mime::text::kPlain); - }; - - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kFakeUrl, request_type::kPost, base::Bind(Handler)); - - /// Fill the data buffer with bytes from 0x00 to 0xFF. - std::vector<unsigned char> data(256); - std::iota(data.begin(), data.end(), 0); - - auto response = http::PostBinary(kFakeUrl, data.data(), data.size(), - transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); -} - -TEST(HttpUtils, PostText) { - std::string fake_data = "Some data"; - auto PostHandler = [fake_data](const fake::ServerRequest& request, - fake::ServerResponse* response) { - EXPECT_EQ(request_type::kPost, request.GetMethod()); - EXPECT_EQ(fake_data.size(), - std::stoul(request.GetHeader(request_header::kContentLength))); - EXPECT_EQ(chromeos::mime::text::kPlain, - request.GetHeader(request_header::kContentType)); - response->ReplyText(status_code::Ok, request.GetDataAsString(), - chromeos::mime::text::kPlain); - }; - - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kFakeUrl, request_type::kPost, base::Bind(PostHandler)); - - auto response = http::PostText(kFakeUrl, fake_data.c_str(), - chromeos::mime::text::kPlain, - transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::text::kPlain, response->GetContentType()); - EXPECT_EQ(fake_data, response->GetDataAsString()); -} - -TEST(HttpUtils, PostFormData) { - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kFakeUrl, request_type::kPost, - base::Bind(EchoDataHandler)); - - auto response = http::PostFormData(kFakeUrl, { - {"key", "value"}, - {"field", "field value"}, - }, transport, nullptr); - EXPECT_TRUE(response->IsSuccessful()); - EXPECT_EQ(chromeos::mime::application::kWwwFormUrlEncoded, - response->GetContentType()); - EXPECT_EQ("key=value&field=field+value", response->GetDataAsString()); -} - -TEST(HttpUtils, PostPatchJson) { - auto JsonHandler = [](const fake::ServerRequest& request, - fake::ServerResponse* response) { - auto mime_type = chromeos::mime::RemoveParameters( - request.GetHeader(request_header::kContentType)); - EXPECT_EQ(chromeos::mime::application::kJson, mime_type); - response->ReplyJson(status_code::Ok, { - {"method", request.GetMethod()}, - {"data", request.GetDataAsString()}, - }); - }; - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kFakeUrl, "*", base::Bind(JsonHandler)); - - base::DictionaryValue json; - json.SetString("key1", "val1"); - json.SetString("key2", "val2"); - std::string value; - - // Test POST - 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)); - EXPECT_EQ(request_type::kPost, value); - EXPECT_TRUE(resp_json->GetString("data", &value)); - EXPECT_EQ("{\"key1\":\"val1\",\"key2\":\"val2\"}", value); - - // Test PATCH - 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)); - EXPECT_EQ(request_type::kPatch, value); - EXPECT_TRUE(resp_json->GetString("data", &value)); - EXPECT_EQ("{\"key1\":\"val1\",\"key2\":\"val2\"}", value); -} - -TEST(HttpUtils, ParseJsonResponse) { - auto JsonHandler = [](const fake::ServerRequest& request, - fake::ServerResponse* response) { - int status_code = std::stoi(request.GetFormField("code")); - response->ReplyJson(status_code, {{"data", request.GetFormField("value")}}); - }; - std::shared_ptr<fake::Transport> transport(new fake::Transport); - transport->AddHandler(kFakeUrl, request_type::kPost, base::Bind(JsonHandler)); - - // Test valid JSON responses (with success or error codes). - for (auto item : {"200;data", "400;wrong", "500;Internal Server error"}) { - auto pair = chromeos::string_utils::SplitAtFirst(item, ';'); - auto response = http::PostFormData(kFakeUrl, { - {"code", pair.first}, - {"value", pair.second}, - }, transport, nullptr); - int code = 0; - auto json = http::ParseJsonResponse(response.get(), &code, nullptr); - EXPECT_NE(nullptr, json.get()); - std::string value; - EXPECT_TRUE(json->GetString("data", &value)); - EXPECT_EQ(pair.first, chromeos::string_utils::ToString(code)); - EXPECT_EQ(pair.second, value); - } - - // Test invalid (non-JSON) response. - auto response = http::Get("http://bad.url", transport, nullptr); - EXPECT_EQ(status_code::NotFound, response->GetStatusCode()); - EXPECT_EQ(chromeos::mime::text::kHtml, response->GetContentType()); - int code = 0; - auto json = http::ParseJsonResponse(response.get(), &code, nullptr); - EXPECT_EQ(nullptr, json.get()); - EXPECT_EQ(status_code::NotFound, code); -} -