Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium OS Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "update_engine/libcurl_http_fetcher.h" | 5 #include "update_engine/libcurl_http_fetcher.h" |
| 6 #include <algorithm> | 6 #include <algorithm> |
| 7 #include "chromeos/obsolete_logging.h" | 7 #include "chromeos/obsolete_logging.h" |
| 8 | 8 |
| 9 using std::max; | 9 using std::max; |
| 10 using std::make_pair; | 10 using std::make_pair; |
| 11 | 11 |
| 12 // This is a concrete implementation of HttpFetcher that uses libcurl to do the | 12 // This is a concrete implementation of HttpFetcher that uses libcurl to do the |
| 13 // http work. | 13 // http work. |
| 14 | 14 |
| 15 namespace chromeos_update_engine { | 15 namespace chromeos_update_engine { |
| 16 | 16 |
| 17 namespace { | |
| 18 const int kMaxRetriesCount = 20; | |
| 19 } | |
| 20 | |
| 17 LibcurlHttpFetcher::~LibcurlHttpFetcher() { | 21 LibcurlHttpFetcher::~LibcurlHttpFetcher() { |
| 18 CleanUp(); | 22 CleanUp(); |
| 19 } | 23 } |
| 20 | 24 |
| 21 void LibcurlHttpFetcher::ResumeTransfer(const std::string& url) { | 25 void LibcurlHttpFetcher::ResumeTransfer(const std::string& url) { |
| 22 LOG(INFO) << "Starting/Resuming transfer"; | 26 LOG(INFO) << "Starting/Resuming transfer"; |
| 23 CHECK(!transfer_in_progress_); | 27 CHECK(!transfer_in_progress_); |
| 24 url_ = url; | 28 url_ = url; |
| 25 curl_multi_handle_ = curl_multi_init(); | 29 curl_multi_handle_ = curl_multi_init(); |
| 26 CHECK(curl_multi_handle_); | 30 CHECK(curl_multi_handle_); |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 59 | 63 |
| 60 CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK); | 64 CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK); |
| 61 transfer_in_progress_ = true; | 65 transfer_in_progress_ = true; |
| 62 } | 66 } |
| 63 | 67 |
| 64 // Begins the transfer, which must not have already been started. | 68 // Begins the transfer, which must not have already been started. |
| 65 void LibcurlHttpFetcher::BeginTransfer(const std::string& url) { | 69 void LibcurlHttpFetcher::BeginTransfer(const std::string& url) { |
| 66 transfer_size_ = -1; | 70 transfer_size_ = -1; |
| 67 bytes_downloaded_ = 0; | 71 bytes_downloaded_ = 0; |
| 68 resume_offset_ = 0; | 72 resume_offset_ = 0; |
| 69 do { | 73 retry_count_ = 0; |
| 70 ResumeTransfer(url); | 74 ResumeTransfer(url); |
| 71 } while (CurlPerformOnce()); | 75 CurlPerformOnce(); |
| 72 } | 76 } |
| 73 | 77 |
| 74 void LibcurlHttpFetcher::TerminateTransfer() { | 78 void LibcurlHttpFetcher::TerminateTransfer() { |
| 75 CleanUp(); | 79 CleanUp(); |
| 76 } | 80 } |
| 77 | 81 |
| 78 bool LibcurlHttpFetcher::CurlPerformOnce() { | 82 bool LibcurlHttpFetcher::CurlPerformOnce() { |
|
petkov
2010/07/16 21:47:45
You could make this method void.
| |
| 79 CHECK(transfer_in_progress_); | 83 CHECK(transfer_in_progress_); |
| 80 int running_handles = 0; | 84 int running_handles = 0; |
| 81 CURLMcode retcode = CURLM_CALL_MULTI_PERFORM; | 85 CURLMcode retcode = CURLM_CALL_MULTI_PERFORM; |
| 82 | 86 |
| 83 // libcurl may request that we immediately call curl_multi_perform after it | 87 // libcurl may request that we immediately call curl_multi_perform after it |
| 84 // returns, so we do. libcurl promises that curl_multi_perform will not block. | 88 // returns, so we do. libcurl promises that curl_multi_perform will not block. |
| 85 while (CURLM_CALL_MULTI_PERFORM == retcode) { | 89 while (CURLM_CALL_MULTI_PERFORM == retcode) { |
| 86 retcode = curl_multi_perform(curl_multi_handle_, &running_handles); | 90 retcode = curl_multi_perform(curl_multi_handle_, &running_handles); |
| 87 } | 91 } |
| 88 if (0 == running_handles) { | 92 if (0 == running_handles) { |
| 93 long http_response_code = 0; | |
| 94 if (curl_easy_getinfo(curl_handle_, | |
|
petkov
2010/07/16 21:47:45
This is definitely better than the CHECK_EQ...
| |
| 95 CURLINFO_RESPONSE_CODE, | |
| 96 &http_response_code) == CURLE_OK) { | |
| 97 LOG(INFO) << "HTTP response code: " << http_response_code; | |
| 98 } else { | |
| 99 LOG(ERROR) << "Unable to get http response code."; | |
| 100 } | |
| 101 | |
| 89 // we're done! | 102 // we're done! |
| 90 CleanUp(); | 103 CleanUp(); |
| 91 | 104 |
| 92 if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) { | 105 if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) { |
| 93 // Need to restart transfer | 106 // Need to restart transfer |
| 94 return true; | 107 retry_count_++; |
| 108 LOG(INFO) << "Restarting transfer b/c we finished, had downloaded " | |
| 109 << bytes_downloaded_ << " bytes, but transfer_size_ is " | |
| 110 << transfer_size_ << ". retry_count: " << retry_count_; | |
| 111 if (retry_count_ > kMaxRetriesCount) { | |
| 112 if (delegate_) | |
| 113 delegate_->TransferComplete(this, false); // success | |
| 114 } else { | |
| 115 g_timeout_add(5 * 1000, | |
| 116 &LibcurlHttpFetcher::StaticRetryTimeoutCallback, | |
| 117 this); | |
| 118 } | |
| 119 return false; | |
| 95 } else { | 120 } else { |
| 96 if (delegate_) { | 121 if (delegate_) { |
| 97 delegate_->TransferComplete(this, true); // success | 122 // success is when http_response_code is 200 |
| 123 delegate_->TransferComplete(this, http_response_code == 200); | |
| 98 } | 124 } |
| 99 } | 125 } |
| 100 } else { | 126 } else { |
| 101 // set up callback | 127 // set up callback |
| 102 SetupMainloopSources(); | 128 SetupMainloopSources(); |
| 103 } | 129 } |
| 104 return false; | 130 return false; |
| 105 } | 131 } |
| 106 | 132 |
| 107 size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) { | 133 size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) { |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 210 static int counter = 0; | 236 static int counter = 0; |
| 211 counter++; | 237 counter++; |
| 212 if (counter % 50 == 0) { | 238 if (counter % 50 == 0) { |
| 213 LOG(INFO) << "counter = " << counter; | 239 LOG(INFO) << "counter = " << counter; |
| 214 } | 240 } |
| 215 } | 241 } |
| 216 } | 242 } |
| 217 | 243 |
| 218 bool LibcurlHttpFetcher::FDCallback(GIOChannel *source, | 244 bool LibcurlHttpFetcher::FDCallback(GIOChannel *source, |
| 219 GIOCondition condition) { | 245 GIOCondition condition) { |
| 220 while (CurlPerformOnce()) { | 246 CurlPerformOnce(); |
| 221 ResumeTransfer(url_); | |
| 222 } | |
| 223 // We handle removing of this source elsewhere, so we always return true. | 247 // We handle removing of this source elsewhere, so we always return true. |
| 224 // The docs say, "the function should return FALSE if the event source | 248 // The docs say, "the function should return FALSE if the event source |
| 225 // should be removed." | 249 // should be removed." |
| 226 // http://www.gtk.org/api/2.6/glib/glib-IO-Channels.html#GIOFunc | 250 // http://www.gtk.org/api/2.6/glib/glib-IO-Channels.html#GIOFunc |
| 227 return true; | 251 return true; |
| 228 } | 252 } |
| 229 | 253 |
| 254 gboolean LibcurlHttpFetcher::RetryTimeoutCallback() { | |
| 255 ResumeTransfer(url_); | |
| 256 CurlPerformOnce(); | |
| 257 return FALSE; // Don't have glib auto call this callback again | |
| 258 } | |
| 259 | |
| 230 gboolean LibcurlHttpFetcher::TimeoutCallback() { | 260 gboolean LibcurlHttpFetcher::TimeoutCallback() { |
| 231 if (!transfer_in_progress_) | 261 if (!transfer_in_progress_) |
| 232 return TRUE; | 262 return TRUE; |
| 233 // Since we will return false from this function, which tells glib to | 263 // Since we will return false from this function, which tells glib to |
|
petkov
2010/07/16 21:47:45
Actually, it seems we're returning TRUE from this
| |
| 234 // destroy the timeout callback, we must NULL it out here. This way, when | 264 // destroy the timeout callback, we must NULL it out here. This way, when |
| 235 // setting up callback sources again, we won't try to delete this (doomed) | 265 // setting up callback sources again, we won't try to delete this (doomed) |
| 236 // timeout callback then. | 266 // timeout callback then. |
| 237 // TODO(adlr): optimize by checking if we can keep this timeout callback. | 267 // TODO(adlr): optimize by checking if we can keep this timeout callback. |
| 238 //timeout_source_ = NULL; | 268 //timeout_source_ = NULL; |
| 239 while (CurlPerformOnce()) { | 269 CurlPerformOnce(); |
| 240 ResumeTransfer(url_); | |
| 241 } | |
| 242 return TRUE; | 270 return TRUE; |
| 243 } | 271 } |
| 244 | 272 |
| 245 void LibcurlHttpFetcher::CleanUp() { | 273 void LibcurlHttpFetcher::CleanUp() { |
| 246 if (timeout_source_) { | 274 if (timeout_source_) { |
| 247 g_source_destroy(timeout_source_); | 275 g_source_destroy(timeout_source_); |
| 248 timeout_source_ = NULL; | 276 timeout_source_ = NULL; |
| 249 } | 277 } |
| 250 | 278 |
| 251 for (IOChannels::iterator it = io_channels_.begin(); | 279 for (IOChannels::iterator it = io_channels_.begin(); |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 264 curl_handle_ = NULL; | 292 curl_handle_ = NULL; |
| 265 } | 293 } |
| 266 if (curl_multi_handle_) { | 294 if (curl_multi_handle_) { |
| 267 CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK); | 295 CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK); |
| 268 curl_multi_handle_ = NULL; | 296 curl_multi_handle_ = NULL; |
| 269 } | 297 } |
| 270 transfer_in_progress_ = false; | 298 transfer_in_progress_ = false; |
| 271 } | 299 } |
| 272 | 300 |
| 273 } // namespace chromeos_update_engine | 301 } // namespace chromeos_update_engine |
| OLD | NEW |