• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
5 #include <brillo/http/http_transport_curl.h>
6 
7 #include <base/at_exit.h>
8 #include <base/message_loop/message_loop.h>
9 #include <base/run_loop.h>
10 #include <brillo/bind_lambda.h>
11 #include <brillo/http/http_connection_curl.h>
12 #include <brillo/http/http_request.h>
13 #include <brillo/http/mock_curl_api.h>
14 #include <gmock/gmock.h>
15 #include <gtest/gtest.h>
16 
17 using testing::DoAll;
18 using testing::Invoke;
19 using testing::Return;
20 using testing::SaveArg;
21 using testing::SetArgPointee;
22 using testing::WithoutArgs;
23 using testing::_;
24 
25 namespace brillo {
26 namespace http {
27 namespace curl {
28 
29 class HttpCurlTransportTest : public testing::Test {
30  public:
SetUp()31   void SetUp() override {
32     curl_api_ = std::make_shared<MockCurlInterface>();
33     transport_ = std::make_shared<Transport>(curl_api_);
34     handle_ = reinterpret_cast<CURL*>(100);  // Mock handle value.
35     EXPECT_CALL(*curl_api_, EasyInit()).WillOnce(Return(handle_));
36     EXPECT_CALL(*curl_api_, EasySetOptStr(handle_, CURLOPT_CAPATH, _))
37         .WillOnce(Return(CURLE_OK));
38     EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_SSL_VERIFYPEER, 1))
39         .WillOnce(Return(CURLE_OK));
40     EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_SSL_VERIFYHOST, 2))
41         .WillOnce(Return(CURLE_OK));
42     EXPECT_CALL(*curl_api_, EasySetOptPtr(handle_, CURLOPT_PRIVATE, _))
43         .WillRepeatedly(Return(CURLE_OK));
44   }
45 
TearDown()46   void TearDown() override {
47     transport_.reset();
48     curl_api_.reset();
49   }
50 
51  protected:
52   std::shared_ptr<MockCurlInterface> curl_api_;
53   std::shared_ptr<Transport> transport_;
54   CURL* handle_{nullptr};
55 };
56 
TEST_F(HttpCurlTransportTest,RequestGet)57 TEST_F(HttpCurlTransportTest, RequestGet) {
58   EXPECT_CALL(*curl_api_,
59               EasySetOptStr(handle_, CURLOPT_URL, "http://foo.bar/get"))
60       .WillOnce(Return(CURLE_OK));
61   EXPECT_CALL(*curl_api_,
62               EasySetOptStr(handle_, CURLOPT_USERAGENT, "User Agent"))
63       .WillOnce(Return(CURLE_OK));
64   EXPECT_CALL(*curl_api_,
65               EasySetOptStr(handle_, CURLOPT_REFERER, "http://foo.bar/baz"))
66       .WillOnce(Return(CURLE_OK));
67   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_HTTPGET, 1))
68       .WillOnce(Return(CURLE_OK));
69   auto connection = transport_->CreateConnection("http://foo.bar/get",
70                                                  request_type::kGet,
71                                                  {},
72                                                  "User Agent",
73                                                  "http://foo.bar/baz",
74                                                  nullptr);
75   EXPECT_NE(nullptr, connection.get());
76 
77   EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1);
78   connection.reset();
79 }
80 
TEST_F(HttpCurlTransportTest,RequestHead)81 TEST_F(HttpCurlTransportTest, RequestHead) {
82   EXPECT_CALL(*curl_api_,
83               EasySetOptStr(handle_, CURLOPT_URL, "http://foo.bar/head"))
84       .WillOnce(Return(CURLE_OK));
85   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_NOBODY, 1))
86       .WillOnce(Return(CURLE_OK));
87   auto connection = transport_->CreateConnection(
88       "http://foo.bar/head", request_type::kHead, {}, "", "", nullptr);
89   EXPECT_NE(nullptr, connection.get());
90 
91   EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1);
92   connection.reset();
93 }
94 
TEST_F(HttpCurlTransportTest,RequestPut)95 TEST_F(HttpCurlTransportTest, RequestPut) {
96   EXPECT_CALL(*curl_api_,
97               EasySetOptStr(handle_, CURLOPT_URL, "http://foo.bar/put"))
98       .WillOnce(Return(CURLE_OK));
99   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_UPLOAD, 1))
100       .WillOnce(Return(CURLE_OK));
101   auto connection = transport_->CreateConnection(
102       "http://foo.bar/put", request_type::kPut, {}, "", "", nullptr);
103   EXPECT_NE(nullptr, connection.get());
104 
105   EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1);
106   connection.reset();
107 }
108 
TEST_F(HttpCurlTransportTest,RequestPost)109 TEST_F(HttpCurlTransportTest, RequestPost) {
110   EXPECT_CALL(*curl_api_,
111               EasySetOptStr(handle_, CURLOPT_URL, "http://www.foo.bar/post"))
112       .WillOnce(Return(CURLE_OK));
113   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_POST, 1))
114       .WillOnce(Return(CURLE_OK));
115   EXPECT_CALL(*curl_api_, EasySetOptPtr(handle_, CURLOPT_POSTFIELDS, nullptr))
116       .WillOnce(Return(CURLE_OK));
117   auto connection = transport_->CreateConnection(
118       "http://www.foo.bar/post", request_type::kPost, {}, "", "", nullptr);
119   EXPECT_NE(nullptr, connection.get());
120 
121   EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1);
122   connection.reset();
123 }
124 
TEST_F(HttpCurlTransportTest,RequestPatch)125 TEST_F(HttpCurlTransportTest, RequestPatch) {
126   EXPECT_CALL(*curl_api_,
127               EasySetOptStr(handle_, CURLOPT_URL, "http://www.foo.bar/patch"))
128       .WillOnce(Return(CURLE_OK));
129   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_POST, 1))
130       .WillOnce(Return(CURLE_OK));
131   EXPECT_CALL(*curl_api_, EasySetOptPtr(handle_, CURLOPT_POSTFIELDS, nullptr))
132       .WillOnce(Return(CURLE_OK));
133   EXPECT_CALL(
134       *curl_api_,
135       EasySetOptStr(handle_, CURLOPT_CUSTOMREQUEST, request_type::kPatch))
136       .WillOnce(Return(CURLE_OK));
137   auto connection = transport_->CreateConnection(
138       "http://www.foo.bar/patch", request_type::kPatch, {}, "", "", nullptr);
139   EXPECT_NE(nullptr, connection.get());
140 
141   EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1);
142   connection.reset();
143 }
144 
TEST_F(HttpCurlTransportTest,CurlFailure)145 TEST_F(HttpCurlTransportTest, CurlFailure) {
146   EXPECT_CALL(*curl_api_,
147               EasySetOptStr(handle_, CURLOPT_URL, "http://foo.bar/get"))
148       .WillOnce(Return(CURLE_OK));
149   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_HTTPGET, 1))
150       .WillOnce(Return(CURLE_OUT_OF_MEMORY));
151   EXPECT_CALL(*curl_api_, EasyStrError(CURLE_OUT_OF_MEMORY))
152       .WillOnce(Return("Out of Memory"));
153   EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1);
154   ErrorPtr error;
155   auto connection = transport_->CreateConnection(
156       "http://foo.bar/get", request_type::kGet, {}, "", "", &error);
157 
158   EXPECT_EQ(nullptr, connection.get());
159   EXPECT_EQ("curl_easy_error", error->GetDomain());
160   EXPECT_EQ(std::to_string(CURLE_OUT_OF_MEMORY), error->GetCode());
161   EXPECT_EQ("Out of Memory", error->GetMessage());
162 }
163 
164 class HttpCurlTransportAsyncTest : public testing::Test {
165  public:
SetUp()166   void SetUp() override {
167     curl_api_ = std::make_shared<MockCurlInterface>();
168     transport_ = std::make_shared<Transport>(curl_api_);
169     EXPECT_CALL(*curl_api_, EasyInit()).WillOnce(Return(handle_));
170     EXPECT_CALL(*curl_api_, EasySetOptStr(handle_, CURLOPT_CAPATH, _))
171         .WillOnce(Return(CURLE_OK));
172     EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_SSL_VERIFYPEER, 1))
173         .WillOnce(Return(CURLE_OK));
174     EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_SSL_VERIFYHOST, 2))
175         .WillOnce(Return(CURLE_OK));
176     EXPECT_CALL(*curl_api_, EasySetOptPtr(handle_, CURLOPT_PRIVATE, _))
177         .WillOnce(Return(CURLE_OK));
178   }
179 
180  protected:
181   std::shared_ptr<MockCurlInterface> curl_api_;
182   std::shared_ptr<Transport> transport_;
183   CURL* handle_{reinterpret_cast<CURL*>(123)};          // Mock handle value.
184   CURLM* multi_handle_{reinterpret_cast<CURLM*>(456)};  // Mock handle value.
185   curl_socket_t dummy_socket_{789};
186 };
187 
TEST_F(HttpCurlTransportAsyncTest,StartAsyncTransfer)188 TEST_F(HttpCurlTransportAsyncTest, StartAsyncTransfer) {
189   // This test is a bit tricky because it deals with asynchronous I/O which
190   // relies on a message loop to run all the async tasks.
191   // For this, create a temporary I/O message loop and run it ourselves for the
192   // duration of the test.
193   base::MessageLoopForIO message_loop;
194   base::RunLoop run_loop;
195 
196   // Initial expectations for creating a CURL connection.
197   EXPECT_CALL(*curl_api_,
198               EasySetOptStr(handle_, CURLOPT_URL, "http://foo.bar/get"))
199       .WillOnce(Return(CURLE_OK));
200   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_HTTPGET, 1))
201       .WillOnce(Return(CURLE_OK));
202   auto connection = transport_->CreateConnection(
203       "http://foo.bar/get", request_type::kGet, {}, "", "", nullptr);
204   ASSERT_NE(nullptr, connection.get());
205 
206   // Success/error callback needed to report the result of an async operation.
207   int success_call_count = 0;
208   auto success_callback = [](int* success_call_count,
209                              base::RunLoop* run_loop,
210                              RequestID /* request_id */,
211                              std::unique_ptr<http::Response> /* resp */) {
212     base::MessageLoop::current()->task_runner()->PostTask(
213         FROM_HERE, run_loop->QuitClosure());
214     (*success_call_count)++;
215   };
216 
217   auto error_callback = [](RequestID /* request_id */,
218                            const Error* /* error */) {
219     FAIL() << "This callback shouldn't have been called";
220   };
221 
222   EXPECT_CALL(*curl_api_, MultiInit()).WillOnce(Return(multi_handle_));
223   EXPECT_CALL(*curl_api_, EasyGetInfoInt(handle_, CURLINFO_RESPONSE_CODE, _))
224       .WillRepeatedly(DoAll(SetArgPointee<2>(200), Return(CURLE_OK)));
225 
226   curl_socket_callback socket_callback = nullptr;
227   EXPECT_CALL(*curl_api_,
228               MultiSetSocketCallback(multi_handle_, _, transport_.get()))
229       .WillOnce(DoAll(SaveArg<1>(&socket_callback), Return(CURLM_OK)));
230 
231   curl_multi_timer_callback timer_callback = nullptr;
232   EXPECT_CALL(*curl_api_,
233               MultiSetTimerCallback(multi_handle_, _, transport_.get()))
234       .WillOnce(DoAll(SaveArg<1>(&timer_callback), Return(CURLM_OK)));
235 
236   EXPECT_CALL(*curl_api_, MultiAddHandle(multi_handle_, handle_))
237       .WillOnce(Return(CURLM_OK));
238 
239   EXPECT_EQ(1,
240             transport_->StartAsyncTransfer(
241                 connection.get(),
242                 base::Bind(success_callback,
243                            base::Unretained(&success_call_count),
244                            base::Unretained(&run_loop)),
245                 base::Bind(error_callback)));
246   EXPECT_EQ(0, success_call_count);
247 
248   timer_callback(multi_handle_, 1, transport_.get());
249 
250   auto do_socket_action = [&socket_callback, this] {
251     EXPECT_CALL(*curl_api_, MultiAssign(multi_handle_, dummy_socket_, _))
252         .Times(2).WillRepeatedly(Return(CURLM_OK));
253     EXPECT_EQ(0, socket_callback(handle_, dummy_socket_, CURL_POLL_REMOVE,
254                                  transport_.get(), nullptr));
255   };
256 
257   EXPECT_CALL(*curl_api_,
258               MultiSocketAction(multi_handle_, CURL_SOCKET_TIMEOUT, 0, _))
259       .WillOnce(DoAll(SetArgPointee<3>(1),
260                       WithoutArgs(Invoke(do_socket_action)),
261                       Return(CURLM_OK)))
262       .WillRepeatedly(DoAll(SetArgPointee<3>(0), Return(CURLM_OK)));
263 
264   CURLMsg msg = {};
265   msg.msg = CURLMSG_DONE;
266   msg.easy_handle = handle_;
267   msg.data.result = CURLE_OK;
268 
269   EXPECT_CALL(*curl_api_, MultiInfoRead(multi_handle_, _))
270       .WillOnce(DoAll(SetArgPointee<1>(0), Return(&msg)))
271       .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(nullptr)));
272   EXPECT_CALL(*curl_api_, EasyGetInfoPtr(handle_, CURLINFO_PRIVATE, _))
273       .WillRepeatedly(DoAll(SetArgPointee<2>(connection.get()),
274                             Return(CURLE_OK)));
275 
276   EXPECT_CALL(*curl_api_, MultiRemoveHandle(multi_handle_, handle_))
277       .WillOnce(Return(CURLM_OK));
278 
279   // Just in case something goes wrong and |success_callback| isn't called,
280   // post a time-out quit closure to abort the message loop after 1 second.
281   message_loop.task_runner()->PostDelayedTask(
282       FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(1));
283   run_loop.Run();
284   EXPECT_EQ(1, success_call_count);
285 
286   EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1);
287   connection.reset();
288 
289   EXPECT_CALL(*curl_api_, MultiCleanup(multi_handle_))
290       .WillOnce(Return(CURLM_OK));
291   transport_.reset();
292 }
293 
TEST_F(HttpCurlTransportTest,RequestGetTimeout)294 TEST_F(HttpCurlTransportTest, RequestGetTimeout) {
295   transport_->SetDefaultTimeout(base::TimeDelta::FromMilliseconds(2000));
296   EXPECT_CALL(*curl_api_,
297               EasySetOptStr(handle_, CURLOPT_URL, "http://foo.bar/get"))
298       .WillOnce(Return(CURLE_OK));
299   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_TIMEOUT_MS, 2000))
300       .WillOnce(Return(CURLE_OK));
301   EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_HTTPGET, 1))
302       .WillOnce(Return(CURLE_OK));
303   auto connection = transport_->CreateConnection(
304       "http://foo.bar/get", request_type::kGet, {}, "", "", nullptr);
305   EXPECT_NE(nullptr, connection.get());
306 
307   EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1);
308   connection.reset();
309 }
310 
311 }  // namespace curl
312 }  // namespace http
313 }  // namespace brillo
314