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 |