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 17using testing::DoAll; 18using testing::Invoke; 19using testing::Return; 20using testing::SaveArg; 21using testing::SetArgPointee; 22using testing::WithoutArgs; 23using testing::_; 24 25namespace brillo { 26namespace http { 27namespace curl { 28 29class HttpCurlTransportTest : public testing::Test { 30 public: 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 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 57TEST_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 81TEST_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 95TEST_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 109TEST_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 125TEST_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 145TEST_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 164class HttpCurlTransportAsyncTest : public testing::Test { 165 public: 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 188TEST_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 = [&success_call_count, &run_loop]( 209 RequestID /* request_id */, std::unique_ptr<http::Response> /* resp */) { 210 base::MessageLoop::current()->PostTask(FROM_HERE, run_loop.QuitClosure()); 211 success_call_count++; 212 }; 213 214 auto error_callback = [](RequestID /* request_id */, 215 const Error* /* error */) { 216 FAIL() << "This callback shouldn't have been called"; 217 }; 218 219 EXPECT_CALL(*curl_api_, MultiInit()).WillOnce(Return(multi_handle_)); 220 EXPECT_CALL(*curl_api_, EasyGetInfoInt(handle_, CURLINFO_RESPONSE_CODE, _)) 221 .WillRepeatedly(DoAll(SetArgPointee<2>(200), Return(CURLE_OK))); 222 223 curl_socket_callback socket_callback = nullptr; 224 EXPECT_CALL(*curl_api_, 225 MultiSetSocketCallback(multi_handle_, _, transport_.get())) 226 .WillOnce(DoAll(SaveArg<1>(&socket_callback), Return(CURLM_OK))); 227 228 curl_multi_timer_callback timer_callback = nullptr; 229 EXPECT_CALL(*curl_api_, 230 MultiSetTimerCallback(multi_handle_, _, transport_.get())) 231 .WillOnce(DoAll(SaveArg<1>(&timer_callback), Return(CURLM_OK))); 232 233 EXPECT_CALL(*curl_api_, MultiAddHandle(multi_handle_, handle_)) 234 .WillOnce(Return(CURLM_OK)); 235 236 EXPECT_EQ(1, transport_->StartAsyncTransfer(connection.get(), 237 base::Bind(success_callback), 238 base::Bind(error_callback))); 239 EXPECT_EQ(0, success_call_count); 240 241 timer_callback(multi_handle_, 1, transport_.get()); 242 243 auto do_socket_action = [&socket_callback, this] { 244 EXPECT_CALL(*curl_api_, MultiAssign(multi_handle_, dummy_socket_, _)) 245 .Times(2).WillRepeatedly(Return(CURLM_OK)); 246 EXPECT_EQ(0, socket_callback(handle_, dummy_socket_, CURL_POLL_REMOVE, 247 transport_.get(), nullptr)); 248 }; 249 250 EXPECT_CALL(*curl_api_, 251 MultiSocketAction(multi_handle_, CURL_SOCKET_TIMEOUT, 0, _)) 252 .WillOnce(DoAll(SetArgPointee<3>(1), 253 WithoutArgs(Invoke(do_socket_action)), 254 Return(CURLM_OK))) 255 .WillRepeatedly(DoAll(SetArgPointee<3>(0), Return(CURLM_OK))); 256 257 CURLMsg msg = {}; 258 msg.msg = CURLMSG_DONE; 259 msg.easy_handle = handle_; 260 msg.data.result = CURLE_OK; 261 262 EXPECT_CALL(*curl_api_, MultiInfoRead(multi_handle_, _)) 263 .WillOnce(DoAll(SetArgPointee<1>(0), Return(&msg))) 264 .WillRepeatedly(DoAll(SetArgPointee<1>(0), Return(nullptr))); 265 EXPECT_CALL(*curl_api_, EasyGetInfoPtr(handle_, CURLINFO_PRIVATE, _)) 266 .WillRepeatedly(DoAll(SetArgPointee<2>(connection.get()), 267 Return(CURLE_OK))); 268 269 EXPECT_CALL(*curl_api_, MultiRemoveHandle(multi_handle_, handle_)) 270 .WillOnce(Return(CURLM_OK)); 271 272 // Just in case something goes wrong and |success_callback| isn't called, 273 // post a time-out quit closure to abort the message loop after 1 second. 274 message_loop.PostDelayedTask( 275 FROM_HERE, run_loop.QuitClosure(), base::TimeDelta::FromSeconds(1)); 276 run_loop.Run(); 277 EXPECT_EQ(1, success_call_count); 278 279 EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1); 280 connection.reset(); 281 282 EXPECT_CALL(*curl_api_, MultiCleanup(multi_handle_)) 283 .WillOnce(Return(CURLM_OK)); 284 transport_.reset(); 285} 286 287TEST_F(HttpCurlTransportTest, RequestGetTimeout) { 288 transport_->SetDefaultTimeout(base::TimeDelta::FromMilliseconds(2000)); 289 EXPECT_CALL(*curl_api_, 290 EasySetOptStr(handle_, CURLOPT_URL, "http://foo.bar/get")) 291 .WillOnce(Return(CURLE_OK)); 292 EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_TIMEOUT_MS, 2000)) 293 .WillOnce(Return(CURLE_OK)); 294 EXPECT_CALL(*curl_api_, EasySetOptInt(handle_, CURLOPT_HTTPGET, 1)) 295 .WillOnce(Return(CURLE_OK)); 296 auto connection = transport_->CreateConnection( 297 "http://foo.bar/get", request_type::kGet, {}, "", "", nullptr); 298 EXPECT_NE(nullptr, connection.get()); 299 300 EXPECT_CALL(*curl_api_, EasyCleanup(handle_)).Times(1); 301 connection.reset(); 302} 303 304} // namespace curl 305} // namespace http 306} // namespace brillo 307