1*5a923131SAndroid Build Coastguard Worker //
2*5a923131SAndroid Build Coastguard Worker // Copyright (C) 2009 The Android Open Source Project
3*5a923131SAndroid Build Coastguard Worker //
4*5a923131SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
5*5a923131SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
6*5a923131SAndroid Build Coastguard Worker // You may obtain a copy of the License at
7*5a923131SAndroid Build Coastguard Worker //
8*5a923131SAndroid Build Coastguard Worker // http://www.apache.org/licenses/LICENSE-2.0
9*5a923131SAndroid Build Coastguard Worker //
10*5a923131SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
11*5a923131SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
12*5a923131SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*5a923131SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
14*5a923131SAndroid Build Coastguard Worker // limitations under the License.
15*5a923131SAndroid Build Coastguard Worker //
16*5a923131SAndroid Build Coastguard Worker
17*5a923131SAndroid Build Coastguard Worker #include "update_engine/libcurl_http_fetcher.h"
18*5a923131SAndroid Build Coastguard Worker
19*5a923131SAndroid Build Coastguard Worker #include <netinet/in.h>
20*5a923131SAndroid Build Coastguard Worker #include <resolv.h>
21*5a923131SAndroid Build Coastguard Worker #include <sys/types.h>
22*5a923131SAndroid Build Coastguard Worker #include <unistd.h>
23*5a923131SAndroid Build Coastguard Worker
24*5a923131SAndroid Build Coastguard Worker #include <algorithm>
25*5a923131SAndroid Build Coastguard Worker #include <string>
26*5a923131SAndroid Build Coastguard Worker
27*5a923131SAndroid Build Coastguard Worker #include <base/bind.h>
28*5a923131SAndroid Build Coastguard Worker #include <base/format_macros.h>
29*5a923131SAndroid Build Coastguard Worker #include <base/location.h>
30*5a923131SAndroid Build Coastguard Worker #include <base/logging.h>
31*5a923131SAndroid Build Coastguard Worker #include <base/strings/string_split.h>
32*5a923131SAndroid Build Coastguard Worker #include <android-base/stringprintf.h>
33*5a923131SAndroid Build Coastguard Worker #include <base/threading/thread_task_runner_handle.h>
34*5a923131SAndroid Build Coastguard Worker
35*5a923131SAndroid Build Coastguard Worker #ifdef __ANDROID__
36*5a923131SAndroid Build Coastguard Worker #include <cutils/qtaguid.h>
37*5a923131SAndroid Build Coastguard Worker #include <private/android_filesystem_config.h>
38*5a923131SAndroid Build Coastguard Worker #endif // __ANDROID__
39*5a923131SAndroid Build Coastguard Worker
40*5a923131SAndroid Build Coastguard Worker #include "update_engine/certificate_checker.h"
41*5a923131SAndroid Build Coastguard Worker #include "update_engine/common/hardware_interface.h"
42*5a923131SAndroid Build Coastguard Worker #include "update_engine/common/platform_constants.h"
43*5a923131SAndroid Build Coastguard Worker #include "update_engine/common/utils.h"
44*5a923131SAndroid Build Coastguard Worker
45*5a923131SAndroid Build Coastguard Worker using base::TimeDelta;
46*5a923131SAndroid Build Coastguard Worker using brillo::MessageLoop;
47*5a923131SAndroid Build Coastguard Worker using std::max;
48*5a923131SAndroid Build Coastguard Worker using std::string;
49*5a923131SAndroid Build Coastguard Worker
50*5a923131SAndroid Build Coastguard Worker // This is a concrete implementation of HttpFetcher that uses libcurl to do the
51*5a923131SAndroid Build Coastguard Worker // http work.
52*5a923131SAndroid Build Coastguard Worker
53*5a923131SAndroid Build Coastguard Worker namespace chromeos_update_engine {
54*5a923131SAndroid Build Coastguard Worker
55*5a923131SAndroid Build Coastguard Worker namespace {
56*5a923131SAndroid Build Coastguard Worker
57*5a923131SAndroid Build Coastguard Worker const int kNoNetworkRetrySeconds = 10;
58*5a923131SAndroid Build Coastguard Worker
59*5a923131SAndroid Build Coastguard Worker // libcurl's CURLOPT_SOCKOPTFUNCTION callback function. Called after the socket
60*5a923131SAndroid Build Coastguard Worker // is created but before it is connected. This callback tags the created socket
61*5a923131SAndroid Build Coastguard Worker // so the network usage can be tracked in Android.
LibcurlSockoptCallback(void *,curl_socket_t curlfd,curlsocktype)62*5a923131SAndroid Build Coastguard Worker int LibcurlSockoptCallback(void* /* clientp */,
63*5a923131SAndroid Build Coastguard Worker curl_socket_t curlfd,
64*5a923131SAndroid Build Coastguard Worker curlsocktype /* purpose */) {
65*5a923131SAndroid Build Coastguard Worker #ifdef __ANDROID__
66*5a923131SAndroid Build Coastguard Worker // Socket tag used by all network sockets. See qtaguid kernel module for
67*5a923131SAndroid Build Coastguard Worker // stats.
68*5a923131SAndroid Build Coastguard Worker const int kUpdateEngineSocketTag = 0x55417243; // "CrAU" in little-endian.
69*5a923131SAndroid Build Coastguard Worker qtaguid_tagSocket(curlfd, kUpdateEngineSocketTag, AID_OTA_UPDATE);
70*5a923131SAndroid Build Coastguard Worker #endif // __ANDROID__
71*5a923131SAndroid Build Coastguard Worker return CURL_SOCKOPT_OK;
72*5a923131SAndroid Build Coastguard Worker }
73*5a923131SAndroid Build Coastguard Worker
74*5a923131SAndroid Build Coastguard Worker } // namespace
75*5a923131SAndroid Build Coastguard Worker
76*5a923131SAndroid Build Coastguard Worker // static
LibcurlCloseSocketCallback(void * clientp,curl_socket_t item)77*5a923131SAndroid Build Coastguard Worker int LibcurlHttpFetcher::LibcurlCloseSocketCallback(void* clientp,
78*5a923131SAndroid Build Coastguard Worker curl_socket_t item) {
79*5a923131SAndroid Build Coastguard Worker #ifdef __ANDROID__
80*5a923131SAndroid Build Coastguard Worker qtaguid_untagSocket(item);
81*5a923131SAndroid Build Coastguard Worker #endif // __ANDROID__
82*5a923131SAndroid Build Coastguard Worker
83*5a923131SAndroid Build Coastguard Worker LibcurlHttpFetcher* fetcher = static_cast<LibcurlHttpFetcher*>(clientp);
84*5a923131SAndroid Build Coastguard Worker // Stop watching the socket before closing it.
85*5a923131SAndroid Build Coastguard Worker for (size_t t = 0; t < std::size(fetcher->fd_controller_maps_); ++t) {
86*5a923131SAndroid Build Coastguard Worker fetcher->fd_controller_maps_[t].erase(item);
87*5a923131SAndroid Build Coastguard Worker }
88*5a923131SAndroid Build Coastguard Worker
89*5a923131SAndroid Build Coastguard Worker // Documentation for this callback says to return 0 on success or 1 on error.
90*5a923131SAndroid Build Coastguard Worker if (!IGNORE_EINTR(close(item)))
91*5a923131SAndroid Build Coastguard Worker return 0;
92*5a923131SAndroid Build Coastguard Worker return 1;
93*5a923131SAndroid Build Coastguard Worker }
94*5a923131SAndroid Build Coastguard Worker
LibcurlHttpFetcher(HardwareInterface * hardware)95*5a923131SAndroid Build Coastguard Worker LibcurlHttpFetcher::LibcurlHttpFetcher(HardwareInterface* hardware)
96*5a923131SAndroid Build Coastguard Worker : hardware_(hardware) {
97*5a923131SAndroid Build Coastguard Worker // Dev users want a longer timeout (180 seconds) because they may
98*5a923131SAndroid Build Coastguard Worker // be waiting on the dev server to build an image.
99*5a923131SAndroid Build Coastguard Worker if (!hardware_->IsOfficialBuild())
100*5a923131SAndroid Build Coastguard Worker low_speed_time_seconds_ = kDownloadDevModeLowSpeedTimeSeconds;
101*5a923131SAndroid Build Coastguard Worker if (hardware_->IsOOBEEnabled() && !hardware_->IsOOBEComplete(nullptr))
102*5a923131SAndroid Build Coastguard Worker max_retry_count_ = kDownloadMaxRetryCountOobeNotComplete;
103*5a923131SAndroid Build Coastguard Worker }
104*5a923131SAndroid Build Coastguard Worker
~LibcurlHttpFetcher()105*5a923131SAndroid Build Coastguard Worker LibcurlHttpFetcher::~LibcurlHttpFetcher() {
106*5a923131SAndroid Build Coastguard Worker LOG_IF(ERROR, transfer_in_progress_)
107*5a923131SAndroid Build Coastguard Worker << "Destroying the fetcher while a transfer is in progress.";
108*5a923131SAndroid Build Coastguard Worker CleanUp();
109*5a923131SAndroid Build Coastguard Worker }
110*5a923131SAndroid Build Coastguard Worker
GetProxyType(const string & proxy_str,curl_proxytype * out_type)111*5a923131SAndroid Build Coastguard Worker bool LibcurlHttpFetcher::GetProxyType(const string& proxy_str,
112*5a923131SAndroid Build Coastguard Worker curl_proxytype* out_type) {
113*5a923131SAndroid Build Coastguard Worker auto proxy = ToLower(proxy_str);
114*5a923131SAndroid Build Coastguard Worker if (android::base::StartsWith(proxy, "socks5://") ||
115*5a923131SAndroid Build Coastguard Worker android::base::StartsWith(proxy, "socks://")) {
116*5a923131SAndroid Build Coastguard Worker *out_type = CURLPROXY_SOCKS5_HOSTNAME;
117*5a923131SAndroid Build Coastguard Worker return true;
118*5a923131SAndroid Build Coastguard Worker }
119*5a923131SAndroid Build Coastguard Worker if (android::base::StartsWith(proxy, "socks4://")) {
120*5a923131SAndroid Build Coastguard Worker *out_type = CURLPROXY_SOCKS4A;
121*5a923131SAndroid Build Coastguard Worker return true;
122*5a923131SAndroid Build Coastguard Worker }
123*5a923131SAndroid Build Coastguard Worker if (android::base::StartsWith(proxy, "http://") ||
124*5a923131SAndroid Build Coastguard Worker android::base::StartsWith(proxy, "https://")) {
125*5a923131SAndroid Build Coastguard Worker *out_type = CURLPROXY_HTTP;
126*5a923131SAndroid Build Coastguard Worker return true;
127*5a923131SAndroid Build Coastguard Worker }
128*5a923131SAndroid Build Coastguard Worker if (android::base::StartsWith(proxy, kNoProxy)) {
129*5a923131SAndroid Build Coastguard Worker // known failure case. don't log.
130*5a923131SAndroid Build Coastguard Worker return false;
131*5a923131SAndroid Build Coastguard Worker }
132*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Unknown proxy type: " << proxy;
133*5a923131SAndroid Build Coastguard Worker return false;
134*5a923131SAndroid Build Coastguard Worker }
135*5a923131SAndroid Build Coastguard Worker
ResumeTransfer(const string & url)136*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::ResumeTransfer(const string& url) {
137*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Starting/Resuming transfer";
138*5a923131SAndroid Build Coastguard Worker CHECK(!transfer_in_progress_);
139*5a923131SAndroid Build Coastguard Worker url_ = url;
140*5a923131SAndroid Build Coastguard Worker curl_multi_handle_ = curl_multi_init();
141*5a923131SAndroid Build Coastguard Worker CHECK(curl_multi_handle_);
142*5a923131SAndroid Build Coastguard Worker
143*5a923131SAndroid Build Coastguard Worker curl_handle_ = curl_easy_init();
144*5a923131SAndroid Build Coastguard Worker CHECK(curl_handle_);
145*5a923131SAndroid Build Coastguard Worker ignore_failure_ = false;
146*5a923131SAndroid Build Coastguard Worker
147*5a923131SAndroid Build Coastguard Worker // Tag and untag the socket for network usage stats.
148*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(
149*5a923131SAndroid Build Coastguard Worker curl_handle_, CURLOPT_SOCKOPTFUNCTION, LibcurlSockoptCallback);
150*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(
151*5a923131SAndroid Build Coastguard Worker curl_handle_, CURLOPT_CLOSESOCKETFUNCTION, LibcurlCloseSocketCallback);
152*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_CLOSESOCKETDATA, this);
153*5a923131SAndroid Build Coastguard Worker
154*5a923131SAndroid Build Coastguard Worker CHECK(HasProxy());
155*5a923131SAndroid Build Coastguard Worker bool is_direct = (GetCurrentProxy() == kNoProxy);
156*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Using proxy: " << (is_direct ? "no" : "yes");
157*5a923131SAndroid Build Coastguard Worker if (is_direct) {
158*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROXY, ""), CURLE_OK);
159*5a923131SAndroid Build Coastguard Worker } else {
160*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(
161*5a923131SAndroid Build Coastguard Worker curl_handle_, CURLOPT_PROXY, GetCurrentProxy().c_str()),
162*5a923131SAndroid Build Coastguard Worker CURLE_OK);
163*5a923131SAndroid Build Coastguard Worker // Curl seems to require us to set the protocol
164*5a923131SAndroid Build Coastguard Worker curl_proxytype type{};
165*5a923131SAndroid Build Coastguard Worker if (GetProxyType(GetCurrentProxy(), &type)) {
166*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROXYTYPE, type),
167*5a923131SAndroid Build Coastguard Worker CURLE_OK);
168*5a923131SAndroid Build Coastguard Worker }
169*5a923131SAndroid Build Coastguard Worker }
170*5a923131SAndroid Build Coastguard Worker
171*5a923131SAndroid Build Coastguard Worker if (post_data_set_) {
172*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POST, 1), CURLE_OK);
173*5a923131SAndroid Build Coastguard Worker CHECK_EQ(
174*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS, post_data_.data()),
175*5a923131SAndroid Build Coastguard Worker CURLE_OK);
176*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(
177*5a923131SAndroid Build Coastguard Worker curl_handle_, CURLOPT_POSTFIELDSIZE, post_data_.size()),
178*5a923131SAndroid Build Coastguard Worker CURLE_OK);
179*5a923131SAndroid Build Coastguard Worker }
180*5a923131SAndroid Build Coastguard Worker
181*5a923131SAndroid Build Coastguard Worker // Setup extra HTTP headers.
182*5a923131SAndroid Build Coastguard Worker if (curl_http_headers_) {
183*5a923131SAndroid Build Coastguard Worker curl_slist_free_all(curl_http_headers_);
184*5a923131SAndroid Build Coastguard Worker curl_http_headers_ = nullptr;
185*5a923131SAndroid Build Coastguard Worker }
186*5a923131SAndroid Build Coastguard Worker for (const auto& header : extra_headers_) {
187*5a923131SAndroid Build Coastguard Worker // curl_slist_append() copies the string.
188*5a923131SAndroid Build Coastguard Worker curl_http_headers_ =
189*5a923131SAndroid Build Coastguard Worker curl_slist_append(curl_http_headers_, header.second.c_str());
190*5a923131SAndroid Build Coastguard Worker }
191*5a923131SAndroid Build Coastguard Worker if (post_data_set_) {
192*5a923131SAndroid Build Coastguard Worker // Set the Content-Type HTTP header, if one was specifically set.
193*5a923131SAndroid Build Coastguard Worker if (post_content_type_ != kHttpContentTypeUnspecified) {
194*5a923131SAndroid Build Coastguard Worker const string content_type_attr = android::base::StringPrintf(
195*5a923131SAndroid Build Coastguard Worker "Content-Type: %s", GetHttpContentTypeString(post_content_type_));
196*5a923131SAndroid Build Coastguard Worker curl_http_headers_ =
197*5a923131SAndroid Build Coastguard Worker curl_slist_append(curl_http_headers_, content_type_attr.c_str());
198*5a923131SAndroid Build Coastguard Worker } else {
199*5a923131SAndroid Build Coastguard Worker LOG(WARNING) << "no content type set, using libcurl default";
200*5a923131SAndroid Build Coastguard Worker }
201*5a923131SAndroid Build Coastguard Worker }
202*5a923131SAndroid Build Coastguard Worker CHECK_EQ(
203*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_HTTPHEADER, curl_http_headers_),
204*5a923131SAndroid Build Coastguard Worker CURLE_OK);
205*5a923131SAndroid Build Coastguard Worker
206*5a923131SAndroid Build Coastguard Worker if (bytes_downloaded_ > 0 || download_length_) {
207*5a923131SAndroid Build Coastguard Worker // Resume from where we left off.
208*5a923131SAndroid Build Coastguard Worker resume_offset_ = bytes_downloaded_;
209*5a923131SAndroid Build Coastguard Worker CHECK_GE(resume_offset_, 0);
210*5a923131SAndroid Build Coastguard Worker
211*5a923131SAndroid Build Coastguard Worker // Compute end offset, if one is specified. As per HTTP specification, this
212*5a923131SAndroid Build Coastguard Worker // is an inclusive boundary. Make sure it doesn't overflow.
213*5a923131SAndroid Build Coastguard Worker size_t end_offset = 0;
214*5a923131SAndroid Build Coastguard Worker if (download_length_) {
215*5a923131SAndroid Build Coastguard Worker end_offset = static_cast<size_t>(resume_offset_) + download_length_ - 1;
216*5a923131SAndroid Build Coastguard Worker CHECK_LE((size_t)resume_offset_, end_offset);
217*5a923131SAndroid Build Coastguard Worker }
218*5a923131SAndroid Build Coastguard Worker
219*5a923131SAndroid Build Coastguard Worker // Create a string representation of the desired range.
220*5a923131SAndroid Build Coastguard Worker string range_str = android::base::StringPrintf(
221*5a923131SAndroid Build Coastguard Worker "%" PRIu64 "-", static_cast<uint64_t>(resume_offset_));
222*5a923131SAndroid Build Coastguard Worker if (end_offset)
223*5a923131SAndroid Build Coastguard Worker range_str += std::to_string(end_offset);
224*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_RANGE, range_str.c_str()),
225*5a923131SAndroid Build Coastguard Worker CURLE_OK);
226*5a923131SAndroid Build Coastguard Worker }
227*5a923131SAndroid Build Coastguard Worker
228*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK);
229*5a923131SAndroid Build Coastguard Worker CHECK_EQ(
230*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION, StaticLibcurlWrite),
231*5a923131SAndroid Build Coastguard Worker CURLE_OK);
232*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()), CURLE_OK);
233*5a923131SAndroid Build Coastguard Worker
234*5a923131SAndroid Build Coastguard Worker // If the connection drops under |low_speed_limit_bps_| (10
235*5a923131SAndroid Build Coastguard Worker // bytes/sec by default) for |low_speed_time_seconds_| (90 seconds,
236*5a923131SAndroid Build Coastguard Worker // 180 on non-official builds), reconnect.
237*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(
238*5a923131SAndroid Build Coastguard Worker curl_handle_, CURLOPT_LOW_SPEED_LIMIT, low_speed_limit_bps_),
239*5a923131SAndroid Build Coastguard Worker CURLE_OK);
240*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(
241*5a923131SAndroid Build Coastguard Worker curl_handle_, CURLOPT_LOW_SPEED_TIME, low_speed_time_seconds_),
242*5a923131SAndroid Build Coastguard Worker CURLE_OK);
243*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(
244*5a923131SAndroid Build Coastguard Worker curl_handle_, CURLOPT_CONNECTTIMEOUT, connect_timeout_seconds_),
245*5a923131SAndroid Build Coastguard Worker CURLE_OK);
246*5a923131SAndroid Build Coastguard Worker
247*5a923131SAndroid Build Coastguard Worker // By default, libcurl doesn't follow redirections. Allow up to
248*5a923131SAndroid Build Coastguard Worker // |kDownloadMaxRedirects| redirections.
249*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_FOLLOWLOCATION, 1), CURLE_OK);
250*5a923131SAndroid Build Coastguard Worker CHECK_EQ(
251*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_MAXREDIRS, kDownloadMaxRedirects),
252*5a923131SAndroid Build Coastguard Worker CURLE_OK);
253*5a923131SAndroid Build Coastguard Worker
254*5a923131SAndroid Build Coastguard Worker // Lock down the appropriate curl options for HTTP or HTTPS depending on
255*5a923131SAndroid Build Coastguard Worker // the url.
256*5a923131SAndroid Build Coastguard Worker if (hardware_->IsOfficialBuild()) {
257*5a923131SAndroid Build Coastguard Worker if (android::base::StartsWith(ToLower(url_), "http://")) {
258*5a923131SAndroid Build Coastguard Worker SetCurlOptionsForHttp();
259*5a923131SAndroid Build Coastguard Worker } else if (android::base::StartsWith(ToLower(url_), "https://")) {
260*5a923131SAndroid Build Coastguard Worker SetCurlOptionsForHttps();
261*5a923131SAndroid Build Coastguard Worker #ifdef __ANDROID__
262*5a923131SAndroid Build Coastguard Worker } else if (android::base::StartsWith(ToLower(url_), "file://")) {
263*5a923131SAndroid Build Coastguard Worker SetCurlOptionsForFile();
264*5a923131SAndroid Build Coastguard Worker #endif // __ANDROID__
265*5a923131SAndroid Build Coastguard Worker } else {
266*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Received invalid URI: " << url_;
267*5a923131SAndroid Build Coastguard Worker // Lock down to no protocol supported for the transfer.
268*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, 0), CURLE_OK);
269*5a923131SAndroid Build Coastguard Worker }
270*5a923131SAndroid Build Coastguard Worker } else {
271*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Not setting http(s) curl options because we are "
272*5a923131SAndroid Build Coastguard Worker << "running a dev/test image";
273*5a923131SAndroid Build Coastguard Worker }
274*5a923131SAndroid Build Coastguard Worker
275*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK);
276*5a923131SAndroid Build Coastguard Worker transfer_in_progress_ = true;
277*5a923131SAndroid Build Coastguard Worker }
278*5a923131SAndroid Build Coastguard Worker
279*5a923131SAndroid Build Coastguard Worker // Lock down only the protocol in case of HTTP.
SetCurlOptionsForHttp()280*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::SetCurlOptionsForHttp() {
281*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Setting up curl options for HTTP";
282*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, CURLPROTO_HTTP),
283*5a923131SAndroid Build Coastguard Worker CURLE_OK);
284*5a923131SAndroid Build Coastguard Worker CHECK_EQ(
285*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP),
286*5a923131SAndroid Build Coastguard Worker CURLE_OK);
287*5a923131SAndroid Build Coastguard Worker }
288*5a923131SAndroid Build Coastguard Worker
289*5a923131SAndroid Build Coastguard Worker // Security lock-down in official builds: makes sure that peer certificate
290*5a923131SAndroid Build Coastguard Worker // verification is enabled, restricts the set of trusted certificates,
291*5a923131SAndroid Build Coastguard Worker // restricts protocols to HTTPS, restricts ciphers to HIGH.
SetCurlOptionsForHttps()292*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::SetCurlOptionsForHttps() {
293*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Setting up curl options for HTTPS";
294*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYPEER, 1), CURLE_OK);
295*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_VERIFYHOST, 2), CURLE_OK);
296*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_CAINFO, nullptr), CURLE_OK);
297*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(
298*5a923131SAndroid Build Coastguard Worker curl_handle_, CURLOPT_CAPATH, constants::kCACertificatesPath),
299*5a923131SAndroid Build Coastguard Worker CURLE_OK);
300*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS),
301*5a923131SAndroid Build Coastguard Worker CURLE_OK);
302*5a923131SAndroid Build Coastguard Worker CHECK_EQ(
303*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS),
304*5a923131SAndroid Build Coastguard Worker CURLE_OK);
305*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_SSL_CIPHER_LIST, "HIGH:!ADH"),
306*5a923131SAndroid Build Coastguard Worker CURLE_OK);
307*5a923131SAndroid Build Coastguard Worker if (server_to_check_ != ServerToCheck::kNone) {
308*5a923131SAndroid Build Coastguard Worker CHECK_EQ(
309*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_SSL_CTX_DATA, &server_to_check_),
310*5a923131SAndroid Build Coastguard Worker CURLE_OK);
311*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_,
312*5a923131SAndroid Build Coastguard Worker CURLOPT_SSL_CTX_FUNCTION,
313*5a923131SAndroid Build Coastguard Worker CertificateChecker::ProcessSSLContext),
314*5a923131SAndroid Build Coastguard Worker CURLE_OK);
315*5a923131SAndroid Build Coastguard Worker }
316*5a923131SAndroid Build Coastguard Worker }
317*5a923131SAndroid Build Coastguard Worker
318*5a923131SAndroid Build Coastguard Worker // Lock down only the protocol in case of a local file.
SetCurlOptionsForFile()319*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::SetCurlOptionsForFile() {
320*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Setting up curl options for FILE";
321*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_PROTOCOLS, CURLPROTO_FILE),
322*5a923131SAndroid Build Coastguard Worker CURLE_OK);
323*5a923131SAndroid Build Coastguard Worker CHECK_EQ(
324*5a923131SAndroid Build Coastguard Worker curl_easy_setopt(curl_handle_, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_FILE),
325*5a923131SAndroid Build Coastguard Worker CURLE_OK);
326*5a923131SAndroid Build Coastguard Worker }
327*5a923131SAndroid Build Coastguard Worker
328*5a923131SAndroid Build Coastguard Worker // Begins the transfer, which must not have already been started.
BeginTransfer(const string & url)329*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::BeginTransfer(const string& url) {
330*5a923131SAndroid Build Coastguard Worker CHECK(!transfer_in_progress_);
331*5a923131SAndroid Build Coastguard Worker url_ = url;
332*5a923131SAndroid Build Coastguard Worker
333*5a923131SAndroid Build Coastguard Worker transfer_size_ = -1;
334*5a923131SAndroid Build Coastguard Worker resume_offset_ = 0;
335*5a923131SAndroid Build Coastguard Worker retry_count_ = 0;
336*5a923131SAndroid Build Coastguard Worker no_network_retry_count_ = 0;
337*5a923131SAndroid Build Coastguard Worker http_response_code_ = 0;
338*5a923131SAndroid Build Coastguard Worker terminate_requested_ = false;
339*5a923131SAndroid Build Coastguard Worker sent_byte_ = false;
340*5a923131SAndroid Build Coastguard Worker
341*5a923131SAndroid Build Coastguard Worker // If we are paused, we delay these two operations until Unpause is called.
342*5a923131SAndroid Build Coastguard Worker if (transfer_paused_) {
343*5a923131SAndroid Build Coastguard Worker restart_transfer_on_unpause_ = true;
344*5a923131SAndroid Build Coastguard Worker return;
345*5a923131SAndroid Build Coastguard Worker }
346*5a923131SAndroid Build Coastguard Worker ResumeTransfer(url_);
347*5a923131SAndroid Build Coastguard Worker CurlPerformOnce();
348*5a923131SAndroid Build Coastguard Worker }
349*5a923131SAndroid Build Coastguard Worker
ForceTransferTermination()350*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::ForceTransferTermination() {
351*5a923131SAndroid Build Coastguard Worker CleanUp();
352*5a923131SAndroid Build Coastguard Worker if (delegate_) {
353*5a923131SAndroid Build Coastguard Worker // Note that after the callback returns this object may be destroyed.
354*5a923131SAndroid Build Coastguard Worker delegate_->TransferTerminated(this);
355*5a923131SAndroid Build Coastguard Worker }
356*5a923131SAndroid Build Coastguard Worker }
357*5a923131SAndroid Build Coastguard Worker
TerminateTransfer()358*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::TerminateTransfer() {
359*5a923131SAndroid Build Coastguard Worker if (in_write_callback_) {
360*5a923131SAndroid Build Coastguard Worker terminate_requested_ = true;
361*5a923131SAndroid Build Coastguard Worker } else {
362*5a923131SAndroid Build Coastguard Worker ForceTransferTermination();
363*5a923131SAndroid Build Coastguard Worker }
364*5a923131SAndroid Build Coastguard Worker }
365*5a923131SAndroid Build Coastguard Worker
SetHeader(const string & header_name,const string & header_value)366*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::SetHeader(const string& header_name,
367*5a923131SAndroid Build Coastguard Worker const string& header_value) {
368*5a923131SAndroid Build Coastguard Worker string header_line = header_name + ": " + header_value;
369*5a923131SAndroid Build Coastguard Worker // Avoid the space if no data on the right side of the semicolon.
370*5a923131SAndroid Build Coastguard Worker if (header_value.empty())
371*5a923131SAndroid Build Coastguard Worker header_line = header_name + ":";
372*5a923131SAndroid Build Coastguard Worker TEST_AND_RETURN(header_line.find('\n') == string::npos);
373*5a923131SAndroid Build Coastguard Worker TEST_AND_RETURN(header_name.find(':') == string::npos);
374*5a923131SAndroid Build Coastguard Worker extra_headers_[ToLower(header_name)] = header_line;
375*5a923131SAndroid Build Coastguard Worker }
376*5a923131SAndroid Build Coastguard Worker
377*5a923131SAndroid Build Coastguard Worker // Inputs: header_name, header_value
378*5a923131SAndroid Build Coastguard Worker // Example:
379*5a923131SAndroid Build Coastguard Worker // extra_headers_ = { {"foo":"foo: 123"}, {"bar":"bar:"} }
380*5a923131SAndroid Build Coastguard Worker // string tmp = "gibberish";
381*5a923131SAndroid Build Coastguard Worker // Case 1:
382*5a923131SAndroid Build Coastguard Worker // GetHeader("foo", &tmp) -> tmp = "123", return true.
383*5a923131SAndroid Build Coastguard Worker // Case 2:
384*5a923131SAndroid Build Coastguard Worker // GetHeader("bar", &tmp) -> tmp = "", return true.
385*5a923131SAndroid Build Coastguard Worker // Case 3:
386*5a923131SAndroid Build Coastguard Worker // GetHeader("moo", &tmp) -> tmp = "", return false.
GetHeader(const string & header_name,string * header_value) const387*5a923131SAndroid Build Coastguard Worker bool LibcurlHttpFetcher::GetHeader(const string& header_name,
388*5a923131SAndroid Build Coastguard Worker string* header_value) const {
389*5a923131SAndroid Build Coastguard Worker // Initially clear |header_value| to handle both success and failures without
390*5a923131SAndroid Build Coastguard Worker // leaving |header_value| in a unclear state.
391*5a923131SAndroid Build Coastguard Worker header_value->clear();
392*5a923131SAndroid Build Coastguard Worker auto header_key = ToLower(header_name);
393*5a923131SAndroid Build Coastguard Worker auto header_line_itr = extra_headers_.find(header_key);
394*5a923131SAndroid Build Coastguard Worker // If the |header_name| was never set, indicate so by returning false.
395*5a923131SAndroid Build Coastguard Worker if (header_line_itr == extra_headers_.end())
396*5a923131SAndroid Build Coastguard Worker return false;
397*5a923131SAndroid Build Coastguard Worker // From |SetHeader()| the check for |header_name| to not include ":" is
398*5a923131SAndroid Build Coastguard Worker // verified, so finding the first index of ":" is a safe operation.
399*5a923131SAndroid Build Coastguard Worker auto header_line = header_line_itr->second;
400*5a923131SAndroid Build Coastguard Worker *header_value = header_line.substr(header_line.find(':') + 1);
401*5a923131SAndroid Build Coastguard Worker // The following is neccessary to remove the leading ' ' before the header
402*5a923131SAndroid Build Coastguard Worker // value that was place only if |header_value| passed to |SetHeader()| was
403*5a923131SAndroid Build Coastguard Worker // a non-empty string.
404*5a923131SAndroid Build Coastguard Worker header_value->erase(0, 1);
405*5a923131SAndroid Build Coastguard Worker return true;
406*5a923131SAndroid Build Coastguard Worker }
407*5a923131SAndroid Build Coastguard Worker
CurlPerformOnce()408*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::CurlPerformOnce() {
409*5a923131SAndroid Build Coastguard Worker CHECK(transfer_in_progress_);
410*5a923131SAndroid Build Coastguard Worker int running_handles = 0;
411*5a923131SAndroid Build Coastguard Worker CURLMcode retcode = CURLM_CALL_MULTI_PERFORM;
412*5a923131SAndroid Build Coastguard Worker
413*5a923131SAndroid Build Coastguard Worker // libcurl may request that we immediately call curl_multi_perform after it
414*5a923131SAndroid Build Coastguard Worker // returns, so we do. libcurl promises that curl_multi_perform will not block.
415*5a923131SAndroid Build Coastguard Worker while (CURLM_CALL_MULTI_PERFORM == retcode) {
416*5a923131SAndroid Build Coastguard Worker retcode = curl_multi_perform(curl_multi_handle_, &running_handles);
417*5a923131SAndroid Build Coastguard Worker if (terminate_requested_) {
418*5a923131SAndroid Build Coastguard Worker ForceTransferTermination();
419*5a923131SAndroid Build Coastguard Worker return;
420*5a923131SAndroid Build Coastguard Worker }
421*5a923131SAndroid Build Coastguard Worker }
422*5a923131SAndroid Build Coastguard Worker
423*5a923131SAndroid Build Coastguard Worker // When retcode is not |CURLM_OK| at this point, libcurl has an internal error
424*5a923131SAndroid Build Coastguard Worker // that it is less likely to recover from (libcurl bug, out-of-memory, etc.).
425*5a923131SAndroid Build Coastguard Worker // In case of an update check, we send UMA metrics and log the error.
426*5a923131SAndroid Build Coastguard Worker if (is_update_check_ &&
427*5a923131SAndroid Build Coastguard Worker (retcode == CURLM_OUT_OF_MEMORY || retcode == CURLM_INTERNAL_ERROR)) {
428*5a923131SAndroid Build Coastguard Worker auxiliary_error_code_ = ErrorCode::kInternalLibCurlError;
429*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "curl_multi_perform is in an unrecoverable error condition: "
430*5a923131SAndroid Build Coastguard Worker << retcode;
431*5a923131SAndroid Build Coastguard Worker } else if (retcode != CURLM_OK) {
432*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "curl_multi_perform returns error: " << retcode;
433*5a923131SAndroid Build Coastguard Worker }
434*5a923131SAndroid Build Coastguard Worker
435*5a923131SAndroid Build Coastguard Worker // If the transfer completes while paused, we should ignore the failure once
436*5a923131SAndroid Build Coastguard Worker // the fetcher is unpaused.
437*5a923131SAndroid Build Coastguard Worker if (running_handles == 0 && transfer_paused_ && !ignore_failure_) {
438*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Connection closed while paused, ignoring failure.";
439*5a923131SAndroid Build Coastguard Worker ignore_failure_ = true;
440*5a923131SAndroid Build Coastguard Worker }
441*5a923131SAndroid Build Coastguard Worker
442*5a923131SAndroid Build Coastguard Worker if (running_handles != 0 || transfer_paused_) {
443*5a923131SAndroid Build Coastguard Worker // There's either more work to do or we are paused, so we just keep the
444*5a923131SAndroid Build Coastguard Worker // file descriptors to watch up to date and exit, until we are done with the
445*5a923131SAndroid Build Coastguard Worker // work and we are not paused.
446*5a923131SAndroid Build Coastguard Worker //
447*5a923131SAndroid Build Coastguard Worker // When there's no |base::SingleThreadTaskRunner| on current thread, it's
448*5a923131SAndroid Build Coastguard Worker // not possible to watch file descriptors. Just poll it later. This usually
449*5a923131SAndroid Build Coastguard Worker // happens if |brillo::FakeMessageLoop| is used.
450*5a923131SAndroid Build Coastguard Worker if (!base::ThreadTaskRunnerHandle::IsSet()) {
451*5a923131SAndroid Build Coastguard Worker MessageLoop::current()->PostDelayedTask(
452*5a923131SAndroid Build Coastguard Worker FROM_HERE,
453*5a923131SAndroid Build Coastguard Worker base::Bind(&LibcurlHttpFetcher::CurlPerformOnce,
454*5a923131SAndroid Build Coastguard Worker base::Unretained(this)),
455*5a923131SAndroid Build Coastguard Worker TimeDelta::FromSeconds(1));
456*5a923131SAndroid Build Coastguard Worker return;
457*5a923131SAndroid Build Coastguard Worker }
458*5a923131SAndroid Build Coastguard Worker SetupMessageLoopSources();
459*5a923131SAndroid Build Coastguard Worker return;
460*5a923131SAndroid Build Coastguard Worker }
461*5a923131SAndroid Build Coastguard Worker
462*5a923131SAndroid Build Coastguard Worker // At this point, the transfer was completed in some way (error, connection
463*5a923131SAndroid Build Coastguard Worker // closed or download finished).
464*5a923131SAndroid Build Coastguard Worker
465*5a923131SAndroid Build Coastguard Worker GetHttpResponseCode();
466*5a923131SAndroid Build Coastguard Worker if (http_response_code_) {
467*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "HTTP response code: " << http_response_code_;
468*5a923131SAndroid Build Coastguard Worker no_network_retry_count_ = 0;
469*5a923131SAndroid Build Coastguard Worker unresolved_host_state_machine_.UpdateState(false);
470*5a923131SAndroid Build Coastguard Worker } else {
471*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Unable to get http response code.";
472*5a923131SAndroid Build Coastguard Worker CURLcode curl_code = GetCurlCode();
473*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Return code for the transfer: " << curl_code;
474*5a923131SAndroid Build Coastguard Worker if (curl_code == CURLE_COULDNT_RESOLVE_HOST) {
475*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "libcurl can not resolve host.";
476*5a923131SAndroid Build Coastguard Worker unresolved_host_state_machine_.UpdateState(true);
477*5a923131SAndroid Build Coastguard Worker auxiliary_error_code_ = ErrorCode::kUnresolvedHostError;
478*5a923131SAndroid Build Coastguard Worker }
479*5a923131SAndroid Build Coastguard Worker }
480*5a923131SAndroid Build Coastguard Worker
481*5a923131SAndroid Build Coastguard Worker // we're done!
482*5a923131SAndroid Build Coastguard Worker CleanUp();
483*5a923131SAndroid Build Coastguard Worker
484*5a923131SAndroid Build Coastguard Worker if (unresolved_host_state_machine_.GetState() ==
485*5a923131SAndroid Build Coastguard Worker UnresolvedHostStateMachine::State::kRetry) {
486*5a923131SAndroid Build Coastguard Worker // Based on
487*5a923131SAndroid Build Coastguard Worker // https://curl.haxx.se/docs/todo.html#updated_DNS_server_while_running,
488*5a923131SAndroid Build Coastguard Worker // update_engine process should call res_init() and unconditionally retry.
489*5a923131SAndroid Build Coastguard Worker res_init();
490*5a923131SAndroid Build Coastguard Worker no_network_max_retries_++;
491*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Will retry after reloading resolv.conf because last attempt "
492*5a923131SAndroid Build Coastguard Worker "failed to resolve host.";
493*5a923131SAndroid Build Coastguard Worker } else if (unresolved_host_state_machine_.GetState() ==
494*5a923131SAndroid Build Coastguard Worker UnresolvedHostStateMachine::State::kRetriedSuccess) {
495*5a923131SAndroid Build Coastguard Worker auxiliary_error_code_ = ErrorCode::kUnresolvedHostRecovered;
496*5a923131SAndroid Build Coastguard Worker }
497*5a923131SAndroid Build Coastguard Worker
498*5a923131SAndroid Build Coastguard Worker // TODO(petkov): This temporary code tries to deal with the case where the
499*5a923131SAndroid Build Coastguard Worker // update engine performs an update check while the network is not ready
500*5a923131SAndroid Build Coastguard Worker // (e.g., right after resume). Longer term, we should check if the network
501*5a923131SAndroid Build Coastguard Worker // is online/offline and return an appropriate error code.
502*5a923131SAndroid Build Coastguard Worker if (!sent_byte_ && http_response_code_ == 0 &&
503*5a923131SAndroid Build Coastguard Worker no_network_retry_count_ < no_network_max_retries_) {
504*5a923131SAndroid Build Coastguard Worker no_network_retry_count_++;
505*5a923131SAndroid Build Coastguard Worker retry_task_id_ = MessageLoop::current()->PostDelayedTask(
506*5a923131SAndroid Build Coastguard Worker FROM_HERE,
507*5a923131SAndroid Build Coastguard Worker base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
508*5a923131SAndroid Build Coastguard Worker base::Unretained(this)),
509*5a923131SAndroid Build Coastguard Worker TimeDelta::FromSeconds(kNoNetworkRetrySeconds));
510*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "No HTTP response, retry " << no_network_retry_count_;
511*5a923131SAndroid Build Coastguard Worker } else if ((!sent_byte_ && !IsHttpResponseSuccess()) ||
512*5a923131SAndroid Build Coastguard Worker IsHttpResponseError()) {
513*5a923131SAndroid Build Coastguard Worker // The transfer completed w/ error and we didn't get any bytes.
514*5a923131SAndroid Build Coastguard Worker // If we have another proxy to try, try that.
515*5a923131SAndroid Build Coastguard Worker //
516*5a923131SAndroid Build Coastguard Worker // TODO(garnold) in fact there are two separate cases here: one case is an
517*5a923131SAndroid Build Coastguard Worker // other-than-success return code (including no return code) and no
518*5a923131SAndroid Build Coastguard Worker // received bytes, which is necessary due to the way callbacks are
519*5a923131SAndroid Build Coastguard Worker // currently processing error conditions; the second is an explicit HTTP
520*5a923131SAndroid Build Coastguard Worker // error code, where some data may have been received (as in the case of a
521*5a923131SAndroid Build Coastguard Worker // semi-successful multi-chunk fetch). This is a confusing behavior and
522*5a923131SAndroid Build Coastguard Worker // should be unified into a complete, coherent interface.
523*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Transfer resulted in an error (" << http_response_code_
524*5a923131SAndroid Build Coastguard Worker << "), " << bytes_downloaded_ << " bytes downloaded";
525*5a923131SAndroid Build Coastguard Worker
526*5a923131SAndroid Build Coastguard Worker PopProxy(); // Delete the proxy we just gave up on.
527*5a923131SAndroid Build Coastguard Worker
528*5a923131SAndroid Build Coastguard Worker if (HasProxy()) {
529*5a923131SAndroid Build Coastguard Worker // We have another proxy. Retry immediately.
530*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Retrying with next proxy setting";
531*5a923131SAndroid Build Coastguard Worker retry_task_id_ = MessageLoop::current()->PostTask(
532*5a923131SAndroid Build Coastguard Worker FROM_HERE,
533*5a923131SAndroid Build Coastguard Worker base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
534*5a923131SAndroid Build Coastguard Worker base::Unretained(this)));
535*5a923131SAndroid Build Coastguard Worker } else {
536*5a923131SAndroid Build Coastguard Worker // Out of proxies. Give up.
537*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "No further proxies, indicating transfer complete";
538*5a923131SAndroid Build Coastguard Worker if (delegate_)
539*5a923131SAndroid Build Coastguard Worker delegate_->TransferComplete(this, false); // signal fail
540*5a923131SAndroid Build Coastguard Worker return;
541*5a923131SAndroid Build Coastguard Worker }
542*5a923131SAndroid Build Coastguard Worker } else if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) {
543*5a923131SAndroid Build Coastguard Worker if (!ignore_failure_)
544*5a923131SAndroid Build Coastguard Worker retry_count_++;
545*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Transfer interrupted after downloading " << bytes_downloaded_
546*5a923131SAndroid Build Coastguard Worker << " of " << transfer_size_ << " bytes. "
547*5a923131SAndroid Build Coastguard Worker << transfer_size_ - bytes_downloaded_ << " bytes remaining "
548*5a923131SAndroid Build Coastguard Worker << "after " << retry_count_ << " attempt(s)";
549*5a923131SAndroid Build Coastguard Worker
550*5a923131SAndroid Build Coastguard Worker if (retry_count_ > max_retry_count_) {
551*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Reached max attempts (" << retry_count_ << ")";
552*5a923131SAndroid Build Coastguard Worker if (delegate_)
553*5a923131SAndroid Build Coastguard Worker delegate_->TransferComplete(this, false); // signal fail
554*5a923131SAndroid Build Coastguard Worker return;
555*5a923131SAndroid Build Coastguard Worker }
556*5a923131SAndroid Build Coastguard Worker // Need to restart transfer
557*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Restarting transfer to download the remaining bytes";
558*5a923131SAndroid Build Coastguard Worker retry_task_id_ = MessageLoop::current()->PostDelayedTask(
559*5a923131SAndroid Build Coastguard Worker FROM_HERE,
560*5a923131SAndroid Build Coastguard Worker base::Bind(&LibcurlHttpFetcher::RetryTimeoutCallback,
561*5a923131SAndroid Build Coastguard Worker base::Unretained(this)),
562*5a923131SAndroid Build Coastguard Worker TimeDelta::FromSeconds(retry_seconds_));
563*5a923131SAndroid Build Coastguard Worker } else {
564*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Transfer completed (" << http_response_code_ << "), "
565*5a923131SAndroid Build Coastguard Worker << bytes_downloaded_ << " bytes downloaded";
566*5a923131SAndroid Build Coastguard Worker if (delegate_) {
567*5a923131SAndroid Build Coastguard Worker bool success = IsHttpResponseSuccess();
568*5a923131SAndroid Build Coastguard Worker delegate_->TransferComplete(this, success);
569*5a923131SAndroid Build Coastguard Worker }
570*5a923131SAndroid Build Coastguard Worker return;
571*5a923131SAndroid Build Coastguard Worker }
572*5a923131SAndroid Build Coastguard Worker // If we reach this point is because TransferComplete() was not called in any
573*5a923131SAndroid Build Coastguard Worker // of the previous branches. The delegate is allowed to destroy the object
574*5a923131SAndroid Build Coastguard Worker // once TransferComplete is called so this would be illegal.
575*5a923131SAndroid Build Coastguard Worker ignore_failure_ = false;
576*5a923131SAndroid Build Coastguard Worker }
577*5a923131SAndroid Build Coastguard Worker
LibcurlWrite(void * ptr,size_t size,size_t nmemb)578*5a923131SAndroid Build Coastguard Worker size_t LibcurlHttpFetcher::LibcurlWrite(void* ptr, size_t size, size_t nmemb) {
579*5a923131SAndroid Build Coastguard Worker // Update HTTP response first.
580*5a923131SAndroid Build Coastguard Worker GetHttpResponseCode();
581*5a923131SAndroid Build Coastguard Worker const size_t payload_size = size * nmemb;
582*5a923131SAndroid Build Coastguard Worker
583*5a923131SAndroid Build Coastguard Worker // Do nothing if no payload or HTTP response is an error.
584*5a923131SAndroid Build Coastguard Worker if (payload_size == 0 || !IsHttpResponseSuccess()) {
585*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "HTTP response unsuccessful (" << http_response_code_
586*5a923131SAndroid Build Coastguard Worker << ") or no payload (" << payload_size << "), nothing to do";
587*5a923131SAndroid Build Coastguard Worker return 0;
588*5a923131SAndroid Build Coastguard Worker }
589*5a923131SAndroid Build Coastguard Worker
590*5a923131SAndroid Build Coastguard Worker sent_byte_ = true;
591*5a923131SAndroid Build Coastguard Worker {
592*5a923131SAndroid Build Coastguard Worker double transfer_size_double{};
593*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_getinfo(curl_handle_,
594*5a923131SAndroid Build Coastguard Worker CURLINFO_CONTENT_LENGTH_DOWNLOAD,
595*5a923131SAndroid Build Coastguard Worker &transfer_size_double),
596*5a923131SAndroid Build Coastguard Worker CURLE_OK);
597*5a923131SAndroid Build Coastguard Worker off_t new_transfer_size = static_cast<off_t>(transfer_size_double);
598*5a923131SAndroid Build Coastguard Worker if (new_transfer_size > 0) {
599*5a923131SAndroid Build Coastguard Worker transfer_size_ = resume_offset_ + new_transfer_size;
600*5a923131SAndroid Build Coastguard Worker }
601*5a923131SAndroid Build Coastguard Worker }
602*5a923131SAndroid Build Coastguard Worker bytes_downloaded_ += payload_size;
603*5a923131SAndroid Build Coastguard Worker if (delegate_) {
604*5a923131SAndroid Build Coastguard Worker in_write_callback_ = true;
605*5a923131SAndroid Build Coastguard Worker auto should_terminate = !delegate_->ReceivedBytes(this, ptr, payload_size);
606*5a923131SAndroid Build Coastguard Worker in_write_callback_ = false;
607*5a923131SAndroid Build Coastguard Worker if (should_terminate) {
608*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "Requesting libcurl to terminate transfer.";
609*5a923131SAndroid Build Coastguard Worker // Returning an amount that differs from the received size signals an
610*5a923131SAndroid Build Coastguard Worker // error condition to libcurl, which will cause the transfer to be
611*5a923131SAndroid Build Coastguard Worker // aborted.
612*5a923131SAndroid Build Coastguard Worker return 0;
613*5a923131SAndroid Build Coastguard Worker }
614*5a923131SAndroid Build Coastguard Worker }
615*5a923131SAndroid Build Coastguard Worker return payload_size;
616*5a923131SAndroid Build Coastguard Worker }
617*5a923131SAndroid Build Coastguard Worker
Pause()618*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::Pause() {
619*5a923131SAndroid Build Coastguard Worker if (transfer_paused_) {
620*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Fetcher already paused.";
621*5a923131SAndroid Build Coastguard Worker return;
622*5a923131SAndroid Build Coastguard Worker }
623*5a923131SAndroid Build Coastguard Worker transfer_paused_ = true;
624*5a923131SAndroid Build Coastguard Worker if (!transfer_in_progress_) {
625*5a923131SAndroid Build Coastguard Worker // If pause before we started a connection, we don't need to notify curl
626*5a923131SAndroid Build Coastguard Worker // about that, we will simply not start the connection later.
627*5a923131SAndroid Build Coastguard Worker return;
628*5a923131SAndroid Build Coastguard Worker }
629*5a923131SAndroid Build Coastguard Worker CHECK(curl_handle_);
630*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_ALL), CURLE_OK);
631*5a923131SAndroid Build Coastguard Worker }
632*5a923131SAndroid Build Coastguard Worker
Unpause()633*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::Unpause() {
634*5a923131SAndroid Build Coastguard Worker if (!transfer_paused_) {
635*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Resume attempted when fetcher not paused.";
636*5a923131SAndroid Build Coastguard Worker return;
637*5a923131SAndroid Build Coastguard Worker }
638*5a923131SAndroid Build Coastguard Worker transfer_paused_ = false;
639*5a923131SAndroid Build Coastguard Worker if (restart_transfer_on_unpause_) {
640*5a923131SAndroid Build Coastguard Worker restart_transfer_on_unpause_ = false;
641*5a923131SAndroid Build Coastguard Worker ResumeTransfer(url_);
642*5a923131SAndroid Build Coastguard Worker CurlPerformOnce();
643*5a923131SAndroid Build Coastguard Worker return;
644*5a923131SAndroid Build Coastguard Worker }
645*5a923131SAndroid Build Coastguard Worker if (!transfer_in_progress_) {
646*5a923131SAndroid Build Coastguard Worker // If resumed before starting the connection, there's no need to notify
647*5a923131SAndroid Build Coastguard Worker // anybody. We will simply start the connection once it is time.
648*5a923131SAndroid Build Coastguard Worker return;
649*5a923131SAndroid Build Coastguard Worker }
650*5a923131SAndroid Build Coastguard Worker CHECK(curl_handle_);
651*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_CONT), CURLE_OK);
652*5a923131SAndroid Build Coastguard Worker // Since the transfer is in progress, we need to dispatch a CurlPerformOnce()
653*5a923131SAndroid Build Coastguard Worker // now to let the connection continue, otherwise it would be called by the
654*5a923131SAndroid Build Coastguard Worker // TimeoutCallback but with a delay.
655*5a923131SAndroid Build Coastguard Worker CurlPerformOnce();
656*5a923131SAndroid Build Coastguard Worker }
657*5a923131SAndroid Build Coastguard Worker
658*5a923131SAndroid Build Coastguard Worker // This method sets up callbacks with the MessageLoop.
SetupMessageLoopSources()659*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::SetupMessageLoopSources() {
660*5a923131SAndroid Build Coastguard Worker fd_set fd_read;
661*5a923131SAndroid Build Coastguard Worker fd_set fd_write;
662*5a923131SAndroid Build Coastguard Worker fd_set fd_exc;
663*5a923131SAndroid Build Coastguard Worker
664*5a923131SAndroid Build Coastguard Worker FD_ZERO(&fd_read);
665*5a923131SAndroid Build Coastguard Worker FD_ZERO(&fd_write);
666*5a923131SAndroid Build Coastguard Worker FD_ZERO(&fd_exc);
667*5a923131SAndroid Build Coastguard Worker
668*5a923131SAndroid Build Coastguard Worker int fd_max = 0;
669*5a923131SAndroid Build Coastguard Worker
670*5a923131SAndroid Build Coastguard Worker // Ask libcurl for the set of file descriptors we should track on its
671*5a923131SAndroid Build Coastguard Worker // behalf.
672*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_multi_fdset(
673*5a923131SAndroid Build Coastguard Worker curl_multi_handle_, &fd_read, &fd_write, &fd_exc, &fd_max),
674*5a923131SAndroid Build Coastguard Worker CURLM_OK);
675*5a923131SAndroid Build Coastguard Worker
676*5a923131SAndroid Build Coastguard Worker // We should iterate through all file descriptors up to libcurl's fd_max or
677*5a923131SAndroid Build Coastguard Worker // the highest one we're tracking, whichever is larger.
678*5a923131SAndroid Build Coastguard Worker for (size_t t = 0; t < std::size(fd_controller_maps_); ++t) {
679*5a923131SAndroid Build Coastguard Worker if (!fd_controller_maps_[t].empty())
680*5a923131SAndroid Build Coastguard Worker fd_max = max(fd_max, fd_controller_maps_[t].rbegin()->first);
681*5a923131SAndroid Build Coastguard Worker }
682*5a923131SAndroid Build Coastguard Worker
683*5a923131SAndroid Build Coastguard Worker // For each fd, if we're not tracking it, track it. If we are tracking it, but
684*5a923131SAndroid Build Coastguard Worker // libcurl doesn't care about it anymore, stop tracking it. After this loop,
685*5a923131SAndroid Build Coastguard Worker // there should be exactly as many tasks scheduled in
686*5a923131SAndroid Build Coastguard Worker // fd_controller_maps_[0|1] as there are read/write fds that we're tracking.
687*5a923131SAndroid Build Coastguard Worker for (int fd = 0; fd <= fd_max; ++fd) {
688*5a923131SAndroid Build Coastguard Worker // Note that fd_exc is unused in the current version of libcurl so is_exc
689*5a923131SAndroid Build Coastguard Worker // should always be false.
690*5a923131SAndroid Build Coastguard Worker bool is_exc = FD_ISSET(fd, &fd_exc) != 0;
691*5a923131SAndroid Build Coastguard Worker bool must_track[2] = {
692*5a923131SAndroid Build Coastguard Worker is_exc || (FD_ISSET(fd, &fd_read) != 0), // track 0 -- read
693*5a923131SAndroid Build Coastguard Worker is_exc || (FD_ISSET(fd, &fd_write) != 0) // track 1 -- write
694*5a923131SAndroid Build Coastguard Worker };
695*5a923131SAndroid Build Coastguard Worker
696*5a923131SAndroid Build Coastguard Worker for (size_t t = 0; t < std::size(fd_controller_maps_); ++t) {
697*5a923131SAndroid Build Coastguard Worker bool tracked =
698*5a923131SAndroid Build Coastguard Worker fd_controller_maps_[t].find(fd) != fd_controller_maps_[t].end();
699*5a923131SAndroid Build Coastguard Worker
700*5a923131SAndroid Build Coastguard Worker if (!must_track[t]) {
701*5a923131SAndroid Build Coastguard Worker // If we have an outstanding io_channel, remove it.
702*5a923131SAndroid Build Coastguard Worker fd_controller_maps_[t].erase(fd);
703*5a923131SAndroid Build Coastguard Worker continue;
704*5a923131SAndroid Build Coastguard Worker }
705*5a923131SAndroid Build Coastguard Worker
706*5a923131SAndroid Build Coastguard Worker // If we are already tracking this fd, continue -- nothing to do.
707*5a923131SAndroid Build Coastguard Worker if (tracked)
708*5a923131SAndroid Build Coastguard Worker continue;
709*5a923131SAndroid Build Coastguard Worker
710*5a923131SAndroid Build Coastguard Worker // Track a new fd.
711*5a923131SAndroid Build Coastguard Worker switch (t) {
712*5a923131SAndroid Build Coastguard Worker case 0: // Read
713*5a923131SAndroid Build Coastguard Worker fd_controller_maps_[t][fd] =
714*5a923131SAndroid Build Coastguard Worker base::FileDescriptorWatcher::WatchReadable(
715*5a923131SAndroid Build Coastguard Worker fd,
716*5a923131SAndroid Build Coastguard Worker base::BindRepeating(&LibcurlHttpFetcher::CurlPerformOnce,
717*5a923131SAndroid Build Coastguard Worker base::Unretained(this)));
718*5a923131SAndroid Build Coastguard Worker break;
719*5a923131SAndroid Build Coastguard Worker case 1: // Write
720*5a923131SAndroid Build Coastguard Worker fd_controller_maps_[t][fd] =
721*5a923131SAndroid Build Coastguard Worker base::FileDescriptorWatcher::WatchWritable(
722*5a923131SAndroid Build Coastguard Worker fd,
723*5a923131SAndroid Build Coastguard Worker base::BindRepeating(&LibcurlHttpFetcher::CurlPerformOnce,
724*5a923131SAndroid Build Coastguard Worker base::Unretained(this)));
725*5a923131SAndroid Build Coastguard Worker }
726*5a923131SAndroid Build Coastguard Worker static int io_counter = 0;
727*5a923131SAndroid Build Coastguard Worker io_counter++;
728*5a923131SAndroid Build Coastguard Worker if (io_counter % 50 == 0) {
729*5a923131SAndroid Build Coastguard Worker LOG(INFO) << "io_counter = " << io_counter;
730*5a923131SAndroid Build Coastguard Worker }
731*5a923131SAndroid Build Coastguard Worker }
732*5a923131SAndroid Build Coastguard Worker }
733*5a923131SAndroid Build Coastguard Worker
734*5a923131SAndroid Build Coastguard Worker // Set up a timeout callback for libcurl.
735*5a923131SAndroid Build Coastguard Worker if (timeout_id_ == MessageLoop::kTaskIdNull) {
736*5a923131SAndroid Build Coastguard Worker VLOG(1) << "Setting up timeout source: " << idle_seconds_ << " seconds.";
737*5a923131SAndroid Build Coastguard Worker timeout_id_ = MessageLoop::current()->PostDelayedTask(
738*5a923131SAndroid Build Coastguard Worker FROM_HERE,
739*5a923131SAndroid Build Coastguard Worker base::Bind(&LibcurlHttpFetcher::TimeoutCallback,
740*5a923131SAndroid Build Coastguard Worker base::Unretained(this)),
741*5a923131SAndroid Build Coastguard Worker TimeDelta::FromSeconds(idle_seconds_));
742*5a923131SAndroid Build Coastguard Worker }
743*5a923131SAndroid Build Coastguard Worker }
744*5a923131SAndroid Build Coastguard Worker
RetryTimeoutCallback()745*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::RetryTimeoutCallback() {
746*5a923131SAndroid Build Coastguard Worker retry_task_id_ = MessageLoop::kTaskIdNull;
747*5a923131SAndroid Build Coastguard Worker if (transfer_paused_) {
748*5a923131SAndroid Build Coastguard Worker restart_transfer_on_unpause_ = true;
749*5a923131SAndroid Build Coastguard Worker return;
750*5a923131SAndroid Build Coastguard Worker }
751*5a923131SAndroid Build Coastguard Worker ResumeTransfer(url_);
752*5a923131SAndroid Build Coastguard Worker CurlPerformOnce();
753*5a923131SAndroid Build Coastguard Worker }
754*5a923131SAndroid Build Coastguard Worker
TimeoutCallback()755*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::TimeoutCallback() {
756*5a923131SAndroid Build Coastguard Worker // We always re-schedule the callback, even if we don't want to be called
757*5a923131SAndroid Build Coastguard Worker // anymore. We will remove the event source separately if we don't want to
758*5a923131SAndroid Build Coastguard Worker // be called back.
759*5a923131SAndroid Build Coastguard Worker timeout_id_ = MessageLoop::current()->PostDelayedTask(
760*5a923131SAndroid Build Coastguard Worker FROM_HERE,
761*5a923131SAndroid Build Coastguard Worker base::Bind(&LibcurlHttpFetcher::TimeoutCallback, base::Unretained(this)),
762*5a923131SAndroid Build Coastguard Worker TimeDelta::FromSeconds(idle_seconds_));
763*5a923131SAndroid Build Coastguard Worker
764*5a923131SAndroid Build Coastguard Worker // CurlPerformOnce() may call CleanUp(), so we need to schedule our callback
765*5a923131SAndroid Build Coastguard Worker // first, since it could be canceled by this call.
766*5a923131SAndroid Build Coastguard Worker if (transfer_in_progress_)
767*5a923131SAndroid Build Coastguard Worker CurlPerformOnce();
768*5a923131SAndroid Build Coastguard Worker }
769*5a923131SAndroid Build Coastguard Worker
CleanUp()770*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::CleanUp() {
771*5a923131SAndroid Build Coastguard Worker MessageLoop::current()->CancelTask(retry_task_id_);
772*5a923131SAndroid Build Coastguard Worker retry_task_id_ = MessageLoop::kTaskIdNull;
773*5a923131SAndroid Build Coastguard Worker
774*5a923131SAndroid Build Coastguard Worker MessageLoop::current()->CancelTask(timeout_id_);
775*5a923131SAndroid Build Coastguard Worker timeout_id_ = MessageLoop::kTaskIdNull;
776*5a923131SAndroid Build Coastguard Worker
777*5a923131SAndroid Build Coastguard Worker for (size_t t = 0; t < std::size(fd_controller_maps_); ++t) {
778*5a923131SAndroid Build Coastguard Worker fd_controller_maps_[t].clear();
779*5a923131SAndroid Build Coastguard Worker }
780*5a923131SAndroid Build Coastguard Worker
781*5a923131SAndroid Build Coastguard Worker if (curl_http_headers_) {
782*5a923131SAndroid Build Coastguard Worker curl_slist_free_all(curl_http_headers_);
783*5a923131SAndroid Build Coastguard Worker curl_http_headers_ = nullptr;
784*5a923131SAndroid Build Coastguard Worker }
785*5a923131SAndroid Build Coastguard Worker if (curl_handle_) {
786*5a923131SAndroid Build Coastguard Worker if (curl_multi_handle_) {
787*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_multi_remove_handle(curl_multi_handle_, curl_handle_),
788*5a923131SAndroid Build Coastguard Worker CURLM_OK);
789*5a923131SAndroid Build Coastguard Worker }
790*5a923131SAndroid Build Coastguard Worker curl_easy_cleanup(curl_handle_);
791*5a923131SAndroid Build Coastguard Worker curl_handle_ = nullptr;
792*5a923131SAndroid Build Coastguard Worker }
793*5a923131SAndroid Build Coastguard Worker if (curl_multi_handle_) {
794*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK);
795*5a923131SAndroid Build Coastguard Worker curl_multi_handle_ = nullptr;
796*5a923131SAndroid Build Coastguard Worker }
797*5a923131SAndroid Build Coastguard Worker transfer_in_progress_ = false;
798*5a923131SAndroid Build Coastguard Worker transfer_paused_ = false;
799*5a923131SAndroid Build Coastguard Worker restart_transfer_on_unpause_ = false;
800*5a923131SAndroid Build Coastguard Worker }
801*5a923131SAndroid Build Coastguard Worker
GetHttpResponseCode()802*5a923131SAndroid Build Coastguard Worker void LibcurlHttpFetcher::GetHttpResponseCode() {
803*5a923131SAndroid Build Coastguard Worker long http_response_code = 0; // NOLINT(runtime/int) - curl needs long.
804*5a923131SAndroid Build Coastguard Worker if (android::base::StartsWith(ToLower(url_), "file://")) {
805*5a923131SAndroid Build Coastguard Worker // Fake out a valid response code for file:// URLs.
806*5a923131SAndroid Build Coastguard Worker http_response_code_ = 299;
807*5a923131SAndroid Build Coastguard Worker } else if (curl_easy_getinfo(curl_handle_,
808*5a923131SAndroid Build Coastguard Worker CURLINFO_RESPONSE_CODE,
809*5a923131SAndroid Build Coastguard Worker &http_response_code) == CURLE_OK) {
810*5a923131SAndroid Build Coastguard Worker http_response_code_ = static_cast<int>(http_response_code);
811*5a923131SAndroid Build Coastguard Worker } else {
812*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Unable to get http response code from curl_easy_getinfo";
813*5a923131SAndroid Build Coastguard Worker }
814*5a923131SAndroid Build Coastguard Worker }
815*5a923131SAndroid Build Coastguard Worker
GetCurlCode()816*5a923131SAndroid Build Coastguard Worker CURLcode LibcurlHttpFetcher::GetCurlCode() {
817*5a923131SAndroid Build Coastguard Worker CURLcode curl_code = CURLE_OK;
818*5a923131SAndroid Build Coastguard Worker while (true) {
819*5a923131SAndroid Build Coastguard Worker // Repeated calls to |curl_multi_info_read| will return a new struct each
820*5a923131SAndroid Build Coastguard Worker // time, until a NULL is returned as a signal that there is no more to get
821*5a923131SAndroid Build Coastguard Worker // at this point.
822*5a923131SAndroid Build Coastguard Worker int msgs_in_queue{};
823*5a923131SAndroid Build Coastguard Worker CURLMsg* curl_msg =
824*5a923131SAndroid Build Coastguard Worker curl_multi_info_read(curl_multi_handle_, &msgs_in_queue);
825*5a923131SAndroid Build Coastguard Worker if (curl_msg == nullptr)
826*5a923131SAndroid Build Coastguard Worker break;
827*5a923131SAndroid Build Coastguard Worker // When |curl_msg| is |CURLMSG_DONE|, a transfer of an easy handle is done,
828*5a923131SAndroid Build Coastguard Worker // and then data contains the return code for this transfer.
829*5a923131SAndroid Build Coastguard Worker if (curl_msg->msg == CURLMSG_DONE) {
830*5a923131SAndroid Build Coastguard Worker // Make sure |curl_multi_handle_| has one and only one easy handle
831*5a923131SAndroid Build Coastguard Worker // |curl_handle_|.
832*5a923131SAndroid Build Coastguard Worker CHECK_EQ(curl_handle_, curl_msg->easy_handle);
833*5a923131SAndroid Build Coastguard Worker // Transfer return code reference:
834*5a923131SAndroid Build Coastguard Worker // https://curl.haxx.se/libcurl/c/libcurl-errors.html
835*5a923131SAndroid Build Coastguard Worker curl_code = curl_msg->data.result;
836*5a923131SAndroid Build Coastguard Worker }
837*5a923131SAndroid Build Coastguard Worker }
838*5a923131SAndroid Build Coastguard Worker
839*5a923131SAndroid Build Coastguard Worker // Gets connection error if exists.
840*5a923131SAndroid Build Coastguard Worker long connect_error = 0; // NOLINT(runtime/int) - curl needs long.
841*5a923131SAndroid Build Coastguard Worker CURLcode res =
842*5a923131SAndroid Build Coastguard Worker curl_easy_getinfo(curl_handle_, CURLINFO_OS_ERRNO, &connect_error);
843*5a923131SAndroid Build Coastguard Worker if (res == CURLE_OK && connect_error) {
844*5a923131SAndroid Build Coastguard Worker LOG(ERROR) << "Connect error code from the OS: " << connect_error;
845*5a923131SAndroid Build Coastguard Worker }
846*5a923131SAndroid Build Coastguard Worker
847*5a923131SAndroid Build Coastguard Worker return curl_code;
848*5a923131SAndroid Build Coastguard Worker }
849*5a923131SAndroid Build Coastguard Worker
UpdateState(bool failed_to_resolve_host)850*5a923131SAndroid Build Coastguard Worker void UnresolvedHostStateMachine::UpdateState(bool failed_to_resolve_host) {
851*5a923131SAndroid Build Coastguard Worker switch (state_) {
852*5a923131SAndroid Build Coastguard Worker case State::kInit:
853*5a923131SAndroid Build Coastguard Worker if (failed_to_resolve_host) {
854*5a923131SAndroid Build Coastguard Worker state_ = State::kRetry;
855*5a923131SAndroid Build Coastguard Worker }
856*5a923131SAndroid Build Coastguard Worker break;
857*5a923131SAndroid Build Coastguard Worker case State::kRetry:
858*5a923131SAndroid Build Coastguard Worker if (failed_to_resolve_host) {
859*5a923131SAndroid Build Coastguard Worker state_ = State::kNotRetry;
860*5a923131SAndroid Build Coastguard Worker } else {
861*5a923131SAndroid Build Coastguard Worker state_ = State::kRetriedSuccess;
862*5a923131SAndroid Build Coastguard Worker }
863*5a923131SAndroid Build Coastguard Worker break;
864*5a923131SAndroid Build Coastguard Worker case State::kNotRetry:
865*5a923131SAndroid Build Coastguard Worker break;
866*5a923131SAndroid Build Coastguard Worker case State::kRetriedSuccess:
867*5a923131SAndroid Build Coastguard Worker break;
868*5a923131SAndroid Build Coastguard Worker default:
869*5a923131SAndroid Build Coastguard Worker NOTREACHED();
870*5a923131SAndroid Build Coastguard Worker break;
871*5a923131SAndroid Build Coastguard Worker }
872*5a923131SAndroid Build Coastguard Worker }
873*5a923131SAndroid Build Coastguard Worker
874*5a923131SAndroid Build Coastguard Worker } // namespace chromeos_update_engine
875