blob: 882752b228e4600a9783fa55df86983eccb98042 [file] [log] [blame]
Alex Vakulenkoa3062c52014-04-21 17:05:51 -07001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Alex Vakulenkob3aac252014-05-07 17:35:24 -07005#include <string>
6#include <vector>
7
Alex Vakulenko28b31f52014-04-28 12:40:05 -07008#include <base/values.h>
Alex Vakulenkoa3062c52014-04-21 17:05:51 -07009#include <gtest/gtest.h>
10
Alex Vakulenko9cd5e272014-04-25 17:26:11 -070011#include "buffet/bind_lambda.h"
12#include "buffet/http_utils.h"
13#include "buffet/http_transport_fake.h"
14#include "buffet/mime_utils.h"
Alex Vakulenko28b31f52014-04-28 12:40:05 -070015#include "buffet/string_utils.h"
Alex Vakulenko9cd5e272014-04-25 17:26:11 -070016#include "buffet/url_utils.h"
17
Alex Vakulenkoaf23b322014-05-08 16:25:45 -070018using namespace buffet; // NOLINT(build/namespaces)
19using namespace buffet::http; // NOLINT(build/namespaces)
Alex Vakulenkoa3062c52014-04-21 17:05:51 -070020
Alex Vakulenko28b31f52014-04-28 12:40:05 -070021static const char kFakeUrl[] = "http://localhost";
22static const char kEchoUrl[] = "http://localhost/echo";
23static const char kMethodEchoUrl[] = "http://localhost/echo/method";
24
25///////////////////// Generic helper request handlers /////////////////////////
26// Returns the request data back with the same content type.
27static void EchoDataHandler(const fake::ServerRequest& request,
28 fake::ServerResponse* response) {
29 response->Reply(status_code::Ok, request.GetData(),
30 request.GetHeader(request_header::kContentType).c_str());
Alex Vakulenko96c84d32014-06-06 11:07:32 -070031}
Alex Vakulenko28b31f52014-04-28 12:40:05 -070032
33// Returns the request method as a plain text response.
34static void EchoMethodHandler(const fake::ServerRequest& request,
35 fake::ServerResponse* response) {
36 response->ReplyText(status_code::Ok, request.GetMethod(), mime::text::kPlain);
Alex Vakulenko96c84d32014-06-06 11:07:32 -070037}
Alex Vakulenko28b31f52014-04-28 12:40:05 -070038
39///////////////////////////////////////////////////////////////////////////////
40TEST(HttpUtils, SendRequest_BinaryData) {
41 std::shared_ptr<fake::Transport> transport(new fake::Transport);
42 transport->AddHandler(kEchoUrl, request_type::kPost,
43 base::Bind(EchoDataHandler));
44
45 // Test binary data round-tripping.
Alex Vakulenko96c84d32014-06-06 11:07:32 -070046 std::vector<unsigned char> custom_data{0xFF, 0x00, 0x80, 0x40, 0xC0, 0x7F};
Alex Vakulenko28b31f52014-04-28 12:40:05 -070047 auto response = http::SendRequest(request_type::kPost, kEchoUrl,
48 custom_data.data(), custom_data.size(),
49 mime::application::kOctet_stream,
Alex Vakulenkob3aac252014-05-07 17:35:24 -070050 HeaderList(), transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -070051 EXPECT_TRUE(response->IsSuccessful());
52 EXPECT_EQ(mime::application::kOctet_stream, response->GetContentType());
53 EXPECT_EQ(custom_data.size(), response->GetData().size());
54 EXPECT_EQ(custom_data, response->GetData());
55}
56
57TEST(HttpUtils, SendRequest_Post) {
58 std::shared_ptr<fake::Transport> transport(new fake::Transport);
59 transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler));
60
61 // Test binary data round-tripping.
Alex Vakulenkoa0424dd2014-06-13 16:10:17 -070062 std::vector<unsigned char> custom_data{0xFF, 0x00, 0x80, 0x40, 0xC0, 0x7F};
Alex Vakulenko28b31f52014-04-28 12:40:05 -070063
64 // Check the correct HTTP method used.
65 auto response = http::SendRequest(request_type::kPost, kMethodEchoUrl,
66 custom_data.data(), custom_data.size(),
67 mime::application::kOctet_stream,
Alex Vakulenkob3aac252014-05-07 17:35:24 -070068 HeaderList(), transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -070069 EXPECT_TRUE(response->IsSuccessful());
70 EXPECT_EQ(mime::text::kPlain, response->GetContentType());
71 EXPECT_EQ(request_type::kPost, response->GetDataAsString());
72}
73
74TEST(HttpUtils, SendRequest_Get) {
75 std::shared_ptr<fake::Transport> transport(new fake::Transport);
76 transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler));
77
78 auto response = http::SendRequest(request_type::kGet, kMethodEchoUrl,
79 nullptr, 0, nullptr,
Alex Vakulenkob3aac252014-05-07 17:35:24 -070080 HeaderList(), transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -070081 EXPECT_TRUE(response->IsSuccessful());
82 EXPECT_EQ(mime::text::kPlain, response->GetContentType());
83 EXPECT_EQ(request_type::kGet, response->GetDataAsString());
84}
85
86TEST(HttpUtils, SendRequest_Put) {
87 std::shared_ptr<fake::Transport> transport(new fake::Transport);
88 transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler));
89
90 auto response = http::SendRequest(request_type::kPut, kMethodEchoUrl,
91 nullptr, 0, nullptr,
Alex Vakulenkob3aac252014-05-07 17:35:24 -070092 HeaderList(), transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -070093 EXPECT_TRUE(response->IsSuccessful());
94 EXPECT_EQ(mime::text::kPlain, response->GetContentType());
95 EXPECT_EQ(request_type::kPut, response->GetDataAsString());
96}
97
98TEST(HttpUtils, SendRequest_NotFound) {
99 std::shared_ptr<fake::Transport> transport(new fake::Transport);
100 // Test failed response (URL not found).
101 auto response = http::SendRequest(request_type::kGet, "http://blah.com",
102 nullptr, 0, nullptr,
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700103 HeaderList(), transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700104 EXPECT_FALSE(response->IsSuccessful());
105 EXPECT_EQ(status_code::NotFound, response->GetStatusCode());
106}
107
108TEST(HttpUtils, SendRequest_Headers) {
109 std::shared_ptr<fake::Transport> transport(new fake::Transport);
110
111 static const char json_echo_url[] = "http://localhost/echo/json";
112 auto JsonEchoHandler = [](const fake::ServerRequest& request,
113 fake::ServerResponse* response) {
114 base::DictionaryValue json;
115 json.SetString("method", request.GetMethod());
116 json.SetString("data", request.GetDataAsString());
Alex Vakulenko96c84d32014-06-06 11:07:32 -0700117 for (const auto& pair : request.GetHeaders()) {
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700118 json.SetString("header." + pair.first, pair.second);
119 }
120 response->ReplyJson(status_code::Ok, &json);
121 };
122 transport->AddHandler(json_echo_url, "*",
123 base::Bind(JsonEchoHandler));
124 auto response = http::SendRequest(
125 request_type::kPost, json_echo_url, "abcd", 4,
126 mime::application::kOctet_stream, {
127 {request_header::kCookie, "flavor=vanilla"},
128 {request_header::kIfMatch, "*"},
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700129 }, transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700130 EXPECT_TRUE(response->IsSuccessful());
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700131 EXPECT_EQ(mime::application::kJson,
132 mime::RemoveParameters(response->GetContentType()));
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700133 auto json = ParseJsonResponse(response.get(), nullptr, nullptr);
134 std::string value;
135 EXPECT_TRUE(json->GetString("method", &value));
136 EXPECT_EQ(request_type::kPost, value);
137 EXPECT_TRUE(json->GetString("data", &value));
138 EXPECT_EQ("abcd", value);
139 EXPECT_TRUE(json->GetString("header.Cookie", &value));
140 EXPECT_EQ("flavor=vanilla", value);
141 EXPECT_TRUE(json->GetString("header.Content-Type", &value));
142 EXPECT_EQ(mime::application::kOctet_stream, value);
143 EXPECT_TRUE(json->GetString("header.Content-Length", &value));
144 EXPECT_EQ("4", value);
145 EXPECT_TRUE(json->GetString("header.If-Match", &value));
146 EXPECT_EQ("*", value);
147}
148
149TEST(HttpUtils, Get) {
150 // Sends back the "?test=..." portion of URL.
151 // So if we do GET "http://localhost?test=blah", this handler responds
152 // with "blah" as text/plain.
153 auto GetHandler = [](const fake::ServerRequest& request,
154 fake::ServerResponse* response) {
155 EXPECT_EQ(request_type::kGet, request.GetMethod());
156 EXPECT_EQ("0", request.GetHeader(request_header::kContentLength));
157 EXPECT_EQ("", request.GetHeader(request_header::kContentType));
158 response->ReplyText(status_code::Ok, request.GetFormField("test"),
159 mime::text::kPlain);
160 };
161
162 std::shared_ptr<fake::Transport> transport(new fake::Transport);
163 transport->AddHandler(kFakeUrl, request_type::kGet, base::Bind(GetHandler));
164 transport->AddHandler(kMethodEchoUrl, "*", base::Bind(EchoMethodHandler));
165
166 // Make sure Get/GetAsString actually do the GET request
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700167 auto response = http::Get(kMethodEchoUrl, transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700168 EXPECT_TRUE(response->IsSuccessful());
169 EXPECT_EQ(mime::text::kPlain, response->GetContentType());
170 EXPECT_EQ(request_type::kGet, response->GetDataAsString());
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700171 EXPECT_EQ(request_type::kGet,
172 http::GetAsString(kMethodEchoUrl, transport, nullptr));
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700173
174 for (std::string data : {"blah", "some data", ""}) {
175 std::string url = url::AppendQueryParam(kFakeUrl, "test", data);
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700176 EXPECT_EQ(data, http::GetAsString(url, transport, nullptr));
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700177 }
178}
179
180TEST(HttpUtils, Head) {
181 auto HeadHandler = [](const fake::ServerRequest& request,
182 fake::ServerResponse* response) {
183 EXPECT_EQ(request_type::kHead, request.GetMethod());
184 EXPECT_EQ("0", request.GetHeader(request_header::kContentLength));
185 EXPECT_EQ("", request.GetHeader(request_header::kContentType));
186 response->ReplyText(status_code::Ok, "blah",
187 mime::text::kPlain);
188 };
189
190 std::shared_ptr<fake::Transport> transport(new fake::Transport);
191 transport->AddHandler(kFakeUrl, request_type::kHead, base::Bind(HeadHandler));
192
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700193 auto response = http::Head(kFakeUrl, transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700194 EXPECT_TRUE(response->IsSuccessful());
195 EXPECT_EQ(mime::text::kPlain, response->GetContentType());
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700196 EXPECT_EQ("", response->GetDataAsString()); // Must not have actual body.
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700197 EXPECT_EQ("4", response->GetHeader(request_header::kContentLength));
198}
199
200TEST(HttpUtils, PostBinary) {
201 auto Handler = [](const fake::ServerRequest& request,
202 fake::ServerResponse* response) {
203 EXPECT_EQ(request_type::kPost, request.GetMethod());
204 EXPECT_EQ("256", request.GetHeader(request_header::kContentLength));
205 EXPECT_EQ(mime::application::kOctet_stream,
206 request.GetHeader(request_header::kContentType));
Alex Vakulenko96c84d32014-06-06 11:07:32 -0700207 const auto& data = request.GetData();
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700208 EXPECT_EQ(256, data.size());
209
210 // Sum up all the bytes.
211 int sum = std::accumulate(data.begin(), data.end(), 0);
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700212 EXPECT_EQ(32640, sum); // sum(i, i => [0, 255]) = 32640.
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700213 response->ReplyText(status_code::Ok, "", mime::text::kPlain);
214 };
215
216 std::shared_ptr<fake::Transport> transport(new fake::Transport);
217 transport->AddHandler(kFakeUrl, request_type::kPost, base::Bind(Handler));
218
219 /// Fill the data buffer with bytes from 0x00 to 0xFF.
220 std::vector<unsigned char> data(256);
Alex Vakulenko96c84d32014-06-06 11:07:32 -0700221 std::iota(data.begin(), data.end(), 0);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700222
223 auto response = http::PostBinary(kFakeUrl, data.data(), data.size(),
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700224 transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700225 EXPECT_TRUE(response->IsSuccessful());
226}
Alex Vakulenko9cd5e272014-04-25 17:26:11 -0700227
228TEST(HttpUtils, PostText) {
229 std::string fake_data = "Some data";
230 auto PostHandler = [fake_data](const fake::ServerRequest& request,
231 fake::ServerResponse* response) {
232 EXPECT_EQ(request_type::kPost, request.GetMethod());
233 EXPECT_EQ(fake_data.size(),
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700234 std::stoul(request.GetHeader(request_header::kContentLength)));
Alex Vakulenko9cd5e272014-04-25 17:26:11 -0700235 EXPECT_EQ(mime::text::kPlain,
236 request.GetHeader(request_header::kContentType));
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700237 response->ReplyText(status_code::Ok, request.GetDataAsString(),
238 mime::text::kPlain);
Alex Vakulenko9cd5e272014-04-25 17:26:11 -0700239 };
240
241 std::shared_ptr<fake::Transport> transport(new fake::Transport);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700242 transport->AddHandler(kFakeUrl, request_type::kPost, base::Bind(PostHandler));
Alex Vakulenko9cd5e272014-04-25 17:26:11 -0700243
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700244 auto response = http::PostText(kFakeUrl, fake_data.c_str(),
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700245 mime::text::kPlain, transport, nullptr);
Alex Vakulenko9cd5e272014-04-25 17:26:11 -0700246 EXPECT_TRUE(response->IsSuccessful());
247 EXPECT_EQ(mime::text::kPlain, response->GetContentType());
248 EXPECT_EQ(fake_data, response->GetDataAsString());
249}
250
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700251TEST(HttpUtils, PostFormData) {
Alex Vakulenko9cd5e272014-04-25 17:26:11 -0700252 std::shared_ptr<fake::Transport> transport(new fake::Transport);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700253 transport->AddHandler(kFakeUrl, request_type::kPost,
254 base::Bind(EchoDataHandler));
Alex Vakulenko9cd5e272014-04-25 17:26:11 -0700255
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700256 auto response = http::PostFormData(kFakeUrl, {
257 {"key", "value"},
258 {"field", "field value"},
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700259 }, transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700260 EXPECT_TRUE(response->IsSuccessful());
261 EXPECT_EQ(mime::application::kWwwFormUrlEncoded, response->GetContentType());
262 EXPECT_EQ("key=value&field=field+value", response->GetDataAsString());
Alex Vakulenkoa3062c52014-04-21 17:05:51 -0700263}
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700264
265TEST(HttpUtils, PostPatchJson) {
266 auto JsonHandler = [](const fake::ServerRequest& request,
267 fake::ServerResponse* response) {
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700268 auto mime_type = mime::RemoveParameters(
269 request.GetHeader(request_header::kContentType));
270 EXPECT_EQ(mime::application::kJson, mime_type);
271 response->ReplyJson(status_code::Ok, {
272 {"method", request.GetMethod()},
273 {"data", request.GetDataAsString()},
274 });
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700275 };
276 std::shared_ptr<fake::Transport> transport(new fake::Transport);
277 transport->AddHandler(kFakeUrl, "*", base::Bind(JsonHandler));
278
279 base::DictionaryValue json;
280 json.SetString("key1", "val1");
281 json.SetString("key2", "val2");
282 std::string value;
283
284 // Test POST
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700285 auto response = http::PostJson(kFakeUrl, &json, transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700286 auto resp_json = http::ParseJsonResponse(response.get(), nullptr, nullptr);
287 EXPECT_NE(nullptr, resp_json.get());
288 EXPECT_TRUE(resp_json->GetString("method", &value));
289 EXPECT_EQ(request_type::kPost, value);
290 EXPECT_TRUE(resp_json->GetString("data", &value));
291 EXPECT_EQ("{\"key1\":\"val1\",\"key2\":\"val2\"}", value);
292
293 // Test PATCH
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700294 response = http::PatchJson(kFakeUrl, &json, transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700295 resp_json = http::ParseJsonResponse(response.get(), nullptr, nullptr);
296 EXPECT_NE(nullptr, resp_json.get());
297 EXPECT_TRUE(resp_json->GetString("method", &value));
298 EXPECT_EQ(request_type::kPatch, value);
299 EXPECT_TRUE(resp_json->GetString("data", &value));
300 EXPECT_EQ("{\"key1\":\"val1\",\"key2\":\"val2\"}", value);
301}
302
303TEST(HttpUtils, ParseJsonResponse) {
304 auto JsonHandler = [](const fake::ServerRequest& request,
305 fake::ServerResponse* response) {
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700306 int status_code = std::stoi(request.GetFormField("code"));
Alex Vakulenko8e34d392014-04-29 11:02:56 -0700307 response->ReplyJson(status_code, {{"data", request.GetFormField("value")}});
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700308 };
309 std::shared_ptr<fake::Transport> transport(new fake::Transport);
310 transport->AddHandler(kFakeUrl, request_type::kPost, base::Bind(JsonHandler));
311
312 // Test valid JSON responses (with success or error codes).
Alex Vakulenko96c84d32014-06-06 11:07:32 -0700313 for (auto item : {"200;data", "400;wrong", "500;Internal Server error"}) {
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700314 auto pair = string_utils::SplitAtFirst(item, ';');
315 auto response = http::PostFormData(kFakeUrl, {
316 {"code", pair.first},
317 {"value", pair.second},
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700318 }, transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700319 int code = 0;
320 auto json = http::ParseJsonResponse(response.get(), &code, nullptr);
321 EXPECT_NE(nullptr, json.get());
322 std::string value;
323 EXPECT_TRUE(json->GetString("data", &value));
Alex Vakulenko96c84d32014-06-06 11:07:32 -0700324 EXPECT_EQ(pair.first, string_utils::ToString(code));
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700325 EXPECT_EQ(pair.second, value);
326 }
327
Alex Vakulenkob3aac252014-05-07 17:35:24 -0700328 // Test invalid (non-JSON) response.
329 auto response = http::Get("http://bad.url", transport, nullptr);
Alex Vakulenko28b31f52014-04-28 12:40:05 -0700330 EXPECT_EQ(status_code::NotFound, response->GetStatusCode());
331 EXPECT_EQ(mime::text::kHtml, response->GetContentType());
332 int code = 0;
333 auto json = http::ParseJsonResponse(response.get(), &code, nullptr);
334 EXPECT_EQ(nullptr, json.get());
335 EXPECT_EQ(status_code::NotFound, code);
336}
337