| 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 "base/logging.h" | |
| 6 #include "update_engine/libcurl_http_fetcher.h" | 5 #include "update_engine/libcurl_http_fetcher.h" |
| 6 #include <algorithm> |
| 7 #include "chromeos/obsolete_logging.h" |
| 8 |
| 9 using std::max; |
| 10 using std::make_pair; |
| 7 | 11 |
| 8 // 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 |
| 9 // http work. | 13 // http work. |
| 10 | 14 |
| 11 namespace chromeos_update_engine { | 15 namespace chromeos_update_engine { |
| 12 | 16 |
| 13 LibcurlHttpFetcher::~LibcurlHttpFetcher() { | 17 LibcurlHttpFetcher::~LibcurlHttpFetcher() { |
| 14 CleanUp(); | 18 CleanUp(); |
| 15 } | 19 } |
| 16 | 20 |
| 17 // Begins the transfer, which must not have already been started. | 21 void LibcurlHttpFetcher::ResumeTransfer(const std::string& url) { |
| 18 void LibcurlHttpFetcher::BeginTransfer(const std::string& url) { | |
| 19 CHECK(!transfer_in_progress_); | 22 CHECK(!transfer_in_progress_); |
| 20 url_ = url; | 23 url_ = url; |
| 21 curl_multi_handle_ = curl_multi_init(); | 24 curl_multi_handle_ = curl_multi_init(); |
| 22 CHECK(curl_multi_handle_); | 25 CHECK(curl_multi_handle_); |
| 23 | 26 |
| 24 curl_handle_ = curl_easy_init(); | 27 curl_handle_ = curl_easy_init(); |
| 25 CHECK(curl_handle_); | 28 CHECK(curl_handle_); |
| 26 | 29 |
| 27 if (post_data_set_) { | 30 if (post_data_set_) { |
| 28 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_POST, 1)); | 31 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POST, 1), CURLE_OK); |
| 29 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS, | 32 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDS, |
| 30 &post_data_[0])); | 33 &post_data_[0]), |
| 31 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE, | 34 CURLE_OK); |
| 32 post_data_.size())); | 35 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POSTFIELDSIZE, |
| 36 post_data_.size()), |
| 37 CURLE_OK); |
| 33 } | 38 } |
| 34 | 39 |
| 35 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this)); | 40 if (bytes_downloaded_ > 0) { |
| 36 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION, | 41 // Resume from where we left off |
| 37 StaticLibcurlWrite)); | 42 resume_offset_ = bytes_downloaded_; |
| 38 CHECK_EQ(CURLE_OK, curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str())); | 43 CHECK_EQ(curl_easy_setopt(curl_handle_, |
| 39 CHECK_EQ(CURLM_OK, curl_multi_add_handle(curl_multi_handle_, curl_handle_)); | 44 CURLOPT_RESUME_FROM_LARGE, |
| 45 bytes_downloaded_), CURLE_OK); |
| 46 } |
| 47 |
| 48 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK); |
| 49 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION, |
| 50 StaticLibcurlWrite), CURLE_OK); |
| 51 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()), CURLE_OK); |
| 52 CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK); |
| 40 transfer_in_progress_ = true; | 53 transfer_in_progress_ = true; |
| 41 CurlPerformOnce(); | 54 CurlPerformOnce(); |
| 42 } | 55 } |
| 43 | 56 |
| 57 // Begins the transfer, which must not have already been started. |
| 58 void LibcurlHttpFetcher::BeginTransfer(const std::string& url) { |
| 59 transfer_size_ = -1; |
| 60 bytes_downloaded_ = 0; |
| 61 resume_offset_ = 0; |
| 62 ResumeTransfer(url); |
| 63 } |
| 64 |
| 44 void LibcurlHttpFetcher::TerminateTransfer() { | 65 void LibcurlHttpFetcher::TerminateTransfer() { |
| 45 CleanUp(); | 66 CleanUp(); |
| 46 } | 67 } |
| 47 | 68 |
| 48 // TODO(adlr): detect network failures | 69 // TODO(adlr): detect network failures |
| 49 void LibcurlHttpFetcher::CurlPerformOnce() { | 70 void LibcurlHttpFetcher::CurlPerformOnce() { |
| 50 CHECK(transfer_in_progress_); | 71 CHECK(transfer_in_progress_); |
| 51 int running_handles = 0; | 72 int running_handles = 0; |
| 52 CURLMcode retcode = CURLM_CALL_MULTI_PERFORM; | 73 CURLMcode retcode = CURLM_CALL_MULTI_PERFORM; |
| 53 | 74 |
| 54 // libcurl may request that we immediately call curl_multi_perform after it | 75 // libcurl may request that we immediately call curl_multi_perform after it |
| 55 // returns, so we do. libcurl promises that curl_multi_perform will not block. | 76 // returns, so we do. libcurl promises that curl_multi_perform will not block. |
| 56 while (CURLM_CALL_MULTI_PERFORM == retcode) { | 77 while (CURLM_CALL_MULTI_PERFORM == retcode) { |
| 57 retcode = curl_multi_perform(curl_multi_handle_, &running_handles); | 78 retcode = curl_multi_perform(curl_multi_handle_, &running_handles); |
| 58 } | 79 } |
| 59 if (0 == running_handles) { | 80 if (0 == running_handles) { |
| 60 // we're done! | 81 // we're done! |
| 61 CleanUp(); | 82 CleanUp(); |
| 62 if (delegate_) | 83 |
| 63 delegate_->TransferComplete(this, true); // success | 84 if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) { |
| 85 ResumeTransfer(url_); |
| 86 } else { |
| 87 if (delegate_) { |
| 88 delegate_->TransferComplete(this, true); // success |
| 89 } |
| 90 } |
| 64 } else { | 91 } else { |
| 65 // set up callback | 92 // set up callback |
| 66 SetupMainloopSources(); | 93 SetupMainloopSources(); |
| 67 } | 94 } |
| 68 } | 95 } |
| 69 | 96 |
| 70 size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) { | 97 size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) { |
| 98 { |
| 99 double transfer_size_double; |
| 100 CHECK_EQ(curl_easy_getinfo(curl_handle_, |
| 101 CURLINFO_CONTENT_LENGTH_DOWNLOAD, |
| 102 &transfer_size_double), CURLE_OK); |
| 103 off_t new_transfer_size = static_cast<off_t>(transfer_size_double); |
| 104 if (new_transfer_size > 0) { |
| 105 transfer_size_ = resume_offset_ + new_transfer_size; |
| 106 } |
| 107 } |
| 108 bytes_downloaded_ += size * nmemb; |
| 71 if (delegate_) | 109 if (delegate_) |
| 72 delegate_->ReceivedBytes(this, reinterpret_cast<char*>(ptr), size * nmemb); | 110 delegate_->ReceivedBytes(this, reinterpret_cast<char*>(ptr), size * nmemb); |
| 73 return size * nmemb; | 111 return size * nmemb; |
| 74 } | 112 } |
| 75 | 113 |
| 76 void LibcurlHttpFetcher::Pause() { | 114 void LibcurlHttpFetcher::Pause() { |
| 77 CHECK(curl_handle_); | 115 CHECK(curl_handle_); |
| 78 CHECK(transfer_in_progress_); | 116 CHECK(transfer_in_progress_); |
| 79 CHECK_EQ(CURLE_OK, curl_easy_pause(curl_handle_, CURLPAUSE_ALL)); | 117 CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_ALL), CURLE_OK); |
| 80 } | 118 } |
| 81 | 119 |
| 82 void LibcurlHttpFetcher::Unpause() { | 120 void LibcurlHttpFetcher::Unpause() { |
| 83 CHECK(curl_handle_); | 121 CHECK(curl_handle_); |
| 84 CHECK(transfer_in_progress_); | 122 CHECK(transfer_in_progress_); |
| 85 CHECK_EQ(CURLE_OK, curl_easy_pause(curl_handle_, CURLPAUSE_CONT)); | 123 CHECK_EQ(curl_easy_pause(curl_handle_, CURLPAUSE_CONT), CURLE_OK); |
| 86 } | 124 } |
| 87 | 125 |
| 88 // This method sets up callbacks with the glib main loop. | 126 // This method sets up callbacks with the glib main loop. |
| 89 void LibcurlHttpFetcher::SetupMainloopSources() { | 127 void LibcurlHttpFetcher::SetupMainloopSources() { |
| 90 fd_set fd_read; | 128 fd_set fd_read; |
| 91 fd_set fd_write; | 129 fd_set fd_write; |
| 92 fd_set fd_exec; | 130 fd_set fd_exec; |
| 93 | 131 |
| 94 FD_ZERO(&fd_read); | 132 FD_ZERO(&fd_read); |
| 95 FD_ZERO(&fd_write); | 133 FD_ZERO(&fd_write); |
| 96 FD_ZERO(&fd_exec); | 134 FD_ZERO(&fd_exec); |
| 97 | 135 |
| 98 int fd_max = 0; | 136 int fd_max = 0; |
| 99 | 137 |
| 100 // Ask libcurl for the set of file descriptors we should track on its | 138 // Ask libcurl for the set of file descriptors we should track on its |
| 101 // behalf. | 139 // behalf. |
| 102 CHECK_EQ(CURLM_OK, curl_multi_fdset(curl_multi_handle_, &fd_read, &fd_write, | 140 CHECK_EQ(curl_multi_fdset(curl_multi_handle_, &fd_read, &fd_write, |
| 103 &fd_exec, &fd_max)); | 141 &fd_exec, &fd_max), CURLM_OK); |
| 104 | 142 |
| 105 // We should iterate through all file descriptors up to libcurl's fd_max or | 143 // We should iterate through all file descriptors up to libcurl's fd_max or |
| 106 // the highest one we're tracking, whichever is larger | 144 // the highest one we're tracking, whichever is larger |
| 107 if (!io_channels_.empty()) | 145 if (!io_channels_.empty()) |
| 108 fd_max = max(fd_max, io_channels_.rbegin()->first); | 146 fd_max = max(fd_max, io_channels_.rbegin()->first); |
| 109 | 147 |
| 110 // For each fd, if we're not tracking it, track it. If we are tracking it, | 148 // For each fd, if we're not tracking it, track it. If we are tracking it, |
| 111 // but libcurl doesn't care about it anymore, stop tracking it. | 149 // but libcurl doesn't care about it anymore, stop tracking it. |
| 112 // After this loop, there should be exactly as many GIOChannel objects | 150 // After this loop, there should be exactly as many GIOChannel objects |
| 113 // in io_channels_ as there are fds that we're tracking. | 151 // in io_channels_ as there are fds that we're tracking. |
| 114 for (int i = 0; i <= fd_max; i++) { | 152 for (int i = 0; i <= fd_max; i++) { |
| 115 if (!(FD_ISSET(i, &fd_read) || FD_ISSET(i, &fd_write) || | 153 if (!(FD_ISSET(i, &fd_read) || FD_ISSET(i, &fd_write) || |
| 116 FD_ISSET(i, &fd_exec))) { | 154 FD_ISSET(i, &fd_exec))) { |
| 117 // if we have an outstanding io_channel, remove it | 155 // if we have an outstanding io_channel, remove it |
| 118 if (io_channels_.find(i) != io_channels_.end()) { | 156 if (io_channels_.find(i) != io_channels_.end()) { |
| 119 g_source_remove(io_channels_[i].second); | 157 g_source_remove(io_channels_[i].second); |
| 120 g_io_channel_unref(io_channels_[i].first); | 158 g_io_channel_unref(io_channels_[i].first); |
| 121 io_channels_.erase(io_channels_.find(i)); | 159 io_channels_.erase(io_channels_.find(i)); |
| 122 } | 160 } |
| 123 continue; | 161 continue; |
| 124 } | 162 } |
| 125 // If we are already tracking this fd, continue. | 163 // If we are already tracking this fd, continue. |
| 126 if (io_channels_.find(i) != io_channels_.end()) | 164 if (io_channels_.find(i) != io_channels_.end()) |
| 127 continue; | 165 continue; |
| 128 | 166 |
| 129 // We must track a new fd | 167 // We must track a new fd |
| 130 GIOChannel *io_channel = g_io_channel_unix_new(i); | 168 GIOChannel *io_channel = g_io_channel_unix_new(i); |
| 131 guint tag = g_io_add_watch( | 169 guint tag = g_io_add_watch( |
| 132 io_channel, | 170 io_channel, |
| 133 static_cast<GIOCondition>(G_IO_IN | G_IO_OUT | G_IO_PRI | | 171 static_cast<GIOCondition>(G_IO_IN | G_IO_OUT | G_IO_PRI | |
| 134 G_IO_ERR | G_IO_HUP), | 172 G_IO_ERR | G_IO_HUP), |
| 135 &StaticFDCallback, | 173 &StaticFDCallback, |
| 136 this); | 174 this); |
| 137 io_channels_[i] = make_pair(io_channel, tag); | 175 io_channels_[i] = make_pair(io_channel, tag); |
| 138 } | 176 } |
| 139 | 177 |
| 140 // Wet up a timeout callback for libcurl | 178 // Wet up a timeout callback for libcurl |
| 141 long ms = 0; | 179 long ms = 0; |
| 142 CHECK_EQ(CURLM_OK, curl_multi_timeout(curl_multi_handle_, &ms)); | 180 CHECK_EQ(curl_multi_timeout(curl_multi_handle_, &ms), CURLM_OK); |
| 143 if (ms < 0) { | 181 if (ms < 0) { |
| 144 // From http://curl.haxx.se/libcurl/c/curl_multi_timeout.html: | 182 // From http://curl.haxx.se/libcurl/c/curl_multi_timeout.html: |
| 145 // if libcurl returns a -1 timeout here, it just means that libcurl | 183 // if libcurl returns a -1 timeout here, it just means that libcurl |
| 146 // currently has no stored timeout value. You must not wait too long | 184 // currently has no stored timeout value. You must not wait too long |
| 147 // (more than a few seconds perhaps) before you call | 185 // (more than a few seconds perhaps) before you call |
| 148 // curl_multi_perform() again. | 186 // curl_multi_perform() again. |
| 149 ms = idle_ms_; | 187 ms = idle_ms_; |
| 150 } | 188 } |
| 151 if (timeout_source_) { | 189 if (timeout_source_) { |
| 152 g_source_destroy(timeout_source_); | 190 g_source_destroy(timeout_source_); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 203 | 241 |
| 204 for (IOChannels::iterator it = io_channels_.begin(); | 242 for (IOChannels::iterator it = io_channels_.begin(); |
| 205 it != io_channels_.end(); ++it) { | 243 it != io_channels_.end(); ++it) { |
| 206 g_source_remove(it->second.second); | 244 g_source_remove(it->second.second); |
| 207 g_io_channel_unref(it->second.first); | 245 g_io_channel_unref(it->second.first); |
| 208 } | 246 } |
| 209 io_channels_.clear(); | 247 io_channels_.clear(); |
| 210 | 248 |
| 211 if (curl_handle_) { | 249 if (curl_handle_) { |
| 212 if (curl_multi_handle_) { | 250 if (curl_multi_handle_) { |
| 213 CHECK_EQ(CURLM_OK, | 251 CHECK_EQ(curl_multi_remove_handle(curl_multi_handle_, curl_handle_), |
| 214 curl_multi_remove_handle(curl_multi_handle_, curl_handle_)); | 252 CURLM_OK); |
| 215 } | 253 } |
| 216 curl_easy_cleanup(curl_handle_); | 254 curl_easy_cleanup(curl_handle_); |
| 217 curl_handle_ = NULL; | 255 curl_handle_ = NULL; |
| 218 } | 256 } |
| 219 if (curl_multi_handle_) { | 257 if (curl_multi_handle_) { |
| 220 CHECK_EQ(CURLM_OK, curl_multi_cleanup(curl_multi_handle_)); | 258 CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK); |
| 221 curl_multi_handle_ = NULL; | 259 curl_multi_handle_ = NULL; |
| 222 } | 260 } |
| 223 transfer_in_progress_ = false; | 261 transfer_in_progress_ = false; |
| 224 } | 262 } |
| 225 | 263 |
| 226 } // namespace chromeos_update_engine | 264 } // namespace chromeos_update_engine |
| OLD | NEW |