| // 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 <gtest/gtest.h> |
| |
| #include "buffet/bind_lambda.h" |
| #include "buffet/http_transport_fake.h" |
| #include "buffet/http_utils.h" |
| #include "buffet/mime_utils.h" |
| #include "buffet/string_utils.h" |
| #include "buffet/url_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(), 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(), |
| mime::application::kOctet_stream, |
| HeaderList(), transport, nullptr); |
| EXPECT_TRUE(response->IsSuccessful()); |
| EXPECT_EQ(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(), |
| mime::application::kOctet_stream, |
| HeaderList(), transport, nullptr); |
| EXPECT_TRUE(response->IsSuccessful()); |
| EXPECT_EQ(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(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(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, |
| mime::application::kOctet_stream, { |
| {request_header::kCookie, "flavor=vanilla"}, |
| {request_header::kIfMatch, "*"}, |
| }, transport, nullptr); |
| EXPECT_TRUE(response->IsSuccessful()); |
| EXPECT_EQ(mime::application::kJson, |
| 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(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"), |
| 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(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 = 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", |
| 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(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(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, "", 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(mime::text::kPlain, |
| request.GetHeader(request_header::kContentType)); |
| response->ReplyText(status_code::Ok, request.GetDataAsString(), |
| 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(), |
| mime::text::kPlain, transport, nullptr); |
| EXPECT_TRUE(response->IsSuccessful()); |
| EXPECT_EQ(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(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 = mime::RemoveParameters( |
| request.GetHeader(request_header::kContentType)); |
| EXPECT_EQ(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 = 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, 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(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); |
| } |
| |