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