Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: libcurl_http_fetcher.cc

Issue 2805027: AU: Changes for deltas on traditional bios machines. (Closed) Base URL: ssh://git@chromiumos-git/update_engine.git
Patch Set: fixes for review and merging in petkov's recent changes Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « libcurl_http_fetcher.h ('k') | omaha_request_action.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 LibcurlHttpFetcher::~LibcurlHttpFetcher() { 17 LibcurlHttpFetcher::~LibcurlHttpFetcher() {
18 CleanUp(); 18 CleanUp();
19 } 19 }
20 20
21 void LibcurlHttpFetcher::ResumeTransfer(const std::string& url) { 21 void LibcurlHttpFetcher::ResumeTransfer(const std::string& url) {
22 LOG(INFO) << "Starting/Resuming transfer";
22 CHECK(!transfer_in_progress_); 23 CHECK(!transfer_in_progress_);
23 url_ = url; 24 url_ = url;
24 curl_multi_handle_ = curl_multi_init(); 25 curl_multi_handle_ = curl_multi_init();
25 CHECK(curl_multi_handle_); 26 CHECK(curl_multi_handle_);
26 27
27 curl_handle_ = curl_easy_init(); 28 curl_handle_ = curl_easy_init();
28 CHECK(curl_handle_); 29 CHECK(curl_handle_);
29 30
30 if (post_data_set_) { 31 if (post_data_set_) {
31 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POST, 1), CURLE_OK); 32 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_POST, 1), CURLE_OK);
(...skipping 10 matching lines...) Expand all
42 resume_offset_ = bytes_downloaded_; 43 resume_offset_ = bytes_downloaded_;
43 CHECK_EQ(curl_easy_setopt(curl_handle_, 44 CHECK_EQ(curl_easy_setopt(curl_handle_,
44 CURLOPT_RESUME_FROM_LARGE, 45 CURLOPT_RESUME_FROM_LARGE,
45 bytes_downloaded_), CURLE_OK); 46 bytes_downloaded_), CURLE_OK);
46 } 47 }
47 48
48 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK); 49 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEDATA, this), CURLE_OK);
49 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION, 50 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_WRITEFUNCTION,
50 StaticLibcurlWrite), CURLE_OK); 51 StaticLibcurlWrite), CURLE_OK);
51 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()), CURLE_OK); 52 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_URL, url_.c_str()), CURLE_OK);
53
54 // If the connection drops under 10 bytes/sec for 90 seconds, reconnect.
55 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_LOW_SPEED_LIMIT, 10),
56 CURLE_OK);
57 CHECK_EQ(curl_easy_setopt(curl_handle_, CURLOPT_LOW_SPEED_TIME, 90),
58 CURLE_OK);
59
52 CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK); 60 CHECK_EQ(curl_multi_add_handle(curl_multi_handle_, curl_handle_), CURLM_OK);
53 transfer_in_progress_ = true; 61 transfer_in_progress_ = true;
54 CurlPerformOnce();
55 } 62 }
56 63
57 // Begins the transfer, which must not have already been started. 64 // Begins the transfer, which must not have already been started.
58 void LibcurlHttpFetcher::BeginTransfer(const std::string& url) { 65 void LibcurlHttpFetcher::BeginTransfer(const std::string& url) {
59 transfer_size_ = -1; 66 transfer_size_ = -1;
60 bytes_downloaded_ = 0; 67 bytes_downloaded_ = 0;
61 resume_offset_ = 0; 68 resume_offset_ = 0;
62 ResumeTransfer(url); 69 do {
70 ResumeTransfer(url);
71 } while (CurlPerformOnce());
63 } 72 }
64 73
65 void LibcurlHttpFetcher::TerminateTransfer() { 74 void LibcurlHttpFetcher::TerminateTransfer() {
66 CleanUp(); 75 CleanUp();
67 } 76 }
68 77
69 // TODO(adlr): detect network failures 78 bool LibcurlHttpFetcher::CurlPerformOnce() {
70 void LibcurlHttpFetcher::CurlPerformOnce() {
71 CHECK(transfer_in_progress_); 79 CHECK(transfer_in_progress_);
72 int running_handles = 0; 80 int running_handles = 0;
73 CURLMcode retcode = CURLM_CALL_MULTI_PERFORM; 81 CURLMcode retcode = CURLM_CALL_MULTI_PERFORM;
74 82
75 // libcurl may request that we immediately call curl_multi_perform after it 83 // libcurl may request that we immediately call curl_multi_perform after it
76 // returns, so we do. libcurl promises that curl_multi_perform will not block. 84 // returns, so we do. libcurl promises that curl_multi_perform will not block.
77 while (CURLM_CALL_MULTI_PERFORM == retcode) { 85 while (CURLM_CALL_MULTI_PERFORM == retcode) {
78 retcode = curl_multi_perform(curl_multi_handle_, &running_handles); 86 retcode = curl_multi_perform(curl_multi_handle_, &running_handles);
79 } 87 }
80 if (0 == running_handles) { 88 if (0 == running_handles) {
81 // we're done! 89 // we're done!
82 CleanUp(); 90 CleanUp();
83 91
84 if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) { 92 if ((transfer_size_ >= 0) && (bytes_downloaded_ < transfer_size_)) {
85 ResumeTransfer(url_); 93 // Need to restart transfer
94 return true;
86 } else { 95 } else {
87 if (delegate_) { 96 if (delegate_) {
88 delegate_->TransferComplete(this, true); // success 97 delegate_->TransferComplete(this, true); // success
89 } 98 }
90 } 99 }
91 } else { 100 } else {
92 // set up callback 101 // set up callback
93 SetupMainloopSources(); 102 SetupMainloopSources();
94 } 103 }
104 return false;
95 } 105 }
96 106
97 size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) { 107 size_t LibcurlHttpFetcher::LibcurlWrite(void *ptr, size_t size, size_t nmemb) {
98 { 108 {
99 double transfer_size_double; 109 double transfer_size_double;
100 CHECK_EQ(curl_easy_getinfo(curl_handle_, 110 CHECK_EQ(curl_easy_getinfo(curl_handle_,
101 CURLINFO_CONTENT_LENGTH_DOWNLOAD, 111 CURLINFO_CONTENT_LENGTH_DOWNLOAD,
102 &transfer_size_double), CURLE_OK); 112 &transfer_size_double), CURLE_OK);
103 off_t new_transfer_size = static_cast<off_t>(transfer_size_double); 113 off_t new_transfer_size = static_cast<off_t>(transfer_size_double);
104 if (new_transfer_size > 0) { 114 if (new_transfer_size > 0) {
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 if (io_channels_.find(i) != io_channels_.end()) { 166 if (io_channels_.find(i) != io_channels_.end()) {
157 g_source_remove(io_channels_[i].second); 167 g_source_remove(io_channels_[i].second);
158 g_io_channel_unref(io_channels_[i].first); 168 g_io_channel_unref(io_channels_[i].first);
159 io_channels_.erase(io_channels_.find(i)); 169 io_channels_.erase(io_channels_.find(i));
160 } 170 }
161 continue; 171 continue;
162 } 172 }
163 // If we are already tracking this fd, continue. 173 // If we are already tracking this fd, continue.
164 if (io_channels_.find(i) != io_channels_.end()) 174 if (io_channels_.find(i) != io_channels_.end())
165 continue; 175 continue;
166
167 // We must track a new fd 176 // We must track a new fd
168 GIOChannel *io_channel = g_io_channel_unix_new(i); 177 GIOChannel *io_channel = g_io_channel_unix_new(i);
169 guint tag = g_io_add_watch( 178 guint tag = g_io_add_watch(
170 io_channel, 179 io_channel,
171 static_cast<GIOCondition>(G_IO_IN | G_IO_OUT | G_IO_PRI | 180 static_cast<GIOCondition>(G_IO_IN | G_IO_OUT | G_IO_PRI |
172 G_IO_ERR | G_IO_HUP), 181 G_IO_ERR | G_IO_HUP),
173 &StaticFDCallback, 182 &StaticFDCallback,
174 this); 183 this);
175 io_channels_[i] = make_pair(io_channel, tag); 184 io_channels_[i] = make_pair(io_channel, tag);
185 static int io_counter = 0;
186 io_counter++;
187 if (io_counter % 50 == 0) {
188 LOG(INFO) << "io_counter = " << io_counter;
189 }
176 } 190 }
177 191
178 // Wet up a timeout callback for libcurl 192 // Wet up a timeout callback for libcurl
179 long ms = 0; 193 long ms = 0;
180 CHECK_EQ(curl_multi_timeout(curl_multi_handle_, &ms), CURLM_OK); 194 CHECK_EQ(curl_multi_timeout(curl_multi_handle_, &ms), CURLM_OK);
181 if (ms < 0) { 195 if (ms < 0) {
182 // From http://curl.haxx.se/libcurl/c/curl_multi_timeout.html: 196 // From http://curl.haxx.se/libcurl/c/curl_multi_timeout.html:
183 // if libcurl returns a -1 timeout here, it just means that libcurl 197 // if libcurl returns a -1 timeout here, it just means that libcurl
184 // currently has no stored timeout value. You must not wait too long 198 // currently has no stored timeout value. You must not wait too long
185 // (more than a few seconds perhaps) before you call 199 // (more than a few seconds perhaps) before you call
186 // curl_multi_perform() again. 200 // curl_multi_perform() again.
187 ms = idle_ms_; 201 ms = idle_ms_;
188 } 202 }
189 if (timeout_source_) { 203 if (!timeout_source_) {
190 g_source_destroy(timeout_source_); 204 LOG(INFO) << "setting up timeout source:" << ms;
191 timeout_source_ = NULL; 205 timeout_source_ = g_timeout_source_new(1000);
206 CHECK(timeout_source_);
207 g_source_set_callback(timeout_source_, StaticTimeoutCallback, this,
208 NULL);
209 g_source_attach(timeout_source_, NULL);
210 static int counter = 0;
211 counter++;
212 if (counter % 50 == 0) {
213 LOG(INFO) << "counter = " << counter;
214 }
192 } 215 }
193 timeout_source_ = g_timeout_source_new(ms);
194 CHECK(timeout_source_);
195 g_source_set_callback(timeout_source_, StaticTimeoutCallback, this,
196 NULL);
197 g_source_attach(timeout_source_, NULL);
198 } 216 }
199 217
200 bool LibcurlHttpFetcher::FDCallback(GIOChannel *source, 218 bool LibcurlHttpFetcher::FDCallback(GIOChannel *source,
201 GIOCondition condition) { 219 GIOCondition condition) {
202 // Figure out which source it was; hopefully there aren't too many b/c 220 while (CurlPerformOnce()) {
203 // this is a linear scan of our channels 221 ResumeTransfer(url_);
204 bool found_in_set = false;
205 for (IOChannels::iterator it = io_channels_.begin();
206 it != io_channels_.end(); ++it) {
207 if (it->second.first == source) {
208 // We will return false from this method, meaning that we shouldn't keep
209 // this g_io_channel around. So we remove it now from our collection of
210 // g_io_channels so that the other code in this class doens't mess with
211 // this (doomed) GIOChannel.
212 // TODO(adlr): optimize by seeing if we should reuse this GIOChannel
213 g_source_remove(it->second.second);
214 g_io_channel_unref(it->second.first);
215 io_channels_.erase(it);
216 found_in_set = true;
217 break;
218 }
219 } 222 }
220 CHECK(found_in_set); 223 // We handle removing of this source elsewhere, so we always return true.
221 CurlPerformOnce(); 224 // The docs say, "the function should return FALSE if the event source
222 return false; 225 // should be removed."
226 // http://www.gtk.org/api/2.6/glib/glib-IO-Channels.html#GIOFunc
227 return true;
223 } 228 }
224 229
225 bool LibcurlHttpFetcher::TimeoutCallback() { 230 gboolean LibcurlHttpFetcher::TimeoutCallback() {
231 if (!transfer_in_progress_)
232 return TRUE;
226 // Since we will return false from this function, which tells glib to 233 // Since we will return false from this function, which tells glib to
227 // destroy the timeout callback, we must NULL it out here. This way, when 234 // destroy the timeout callback, we must NULL it out here. This way, when
228 // setting up callback sources again, we won't try to delete this (doomed) 235 // setting up callback sources again, we won't try to delete this (doomed)
229 // timeout callback then. 236 // timeout callback then.
230 // TODO(adlr): optimize by checking if we can keep this timeout callback. 237 // TODO(adlr): optimize by checking if we can keep this timeout callback.
231 timeout_source_ = NULL; 238 //timeout_source_ = NULL;
232 CurlPerformOnce(); 239 while (CurlPerformOnce()) {
233 return false; 240 ResumeTransfer(url_);
241 }
242 return TRUE;
234 } 243 }
235 244
236 void LibcurlHttpFetcher::CleanUp() { 245 void LibcurlHttpFetcher::CleanUp() {
237 if (timeout_source_) { 246 if (timeout_source_) {
238 g_source_destroy(timeout_source_); 247 g_source_destroy(timeout_source_);
239 timeout_source_ = NULL; 248 timeout_source_ = NULL;
240 } 249 }
241 250
242 for (IOChannels::iterator it = io_channels_.begin(); 251 for (IOChannels::iterator it = io_channels_.begin();
243 it != io_channels_.end(); ++it) { 252 it != io_channels_.end(); ++it) {
(...skipping 11 matching lines...) Expand all
255 curl_handle_ = NULL; 264 curl_handle_ = NULL;
256 } 265 }
257 if (curl_multi_handle_) { 266 if (curl_multi_handle_) {
258 CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK); 267 CHECK_EQ(curl_multi_cleanup(curl_multi_handle_), CURLM_OK);
259 curl_multi_handle_ = NULL; 268 curl_multi_handle_ = NULL;
260 } 269 }
261 transfer_in_progress_ = false; 270 transfer_in_progress_ = false;
262 } 271 }
263 272
264 } // namespace chromeos_update_engine 273 } // namespace chromeos_update_engine
OLDNEW
« no previous file with comments | « libcurl_http_fetcher.h ('k') | omaha_request_action.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698