xref: /aosp_15_r20/system/update_engine/libcurl_http_fetcher.cc (revision 5a9231315b4521097b8dc3750bc806fcafe0c72f)
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