TODO(jieyu): Add a comment here.
| 253 | |
| 254 | // TODO(jieyu): Add a comment here. |
| 255 | static Future<int> download( |
| 256 | const string& uri, |
| 257 | const string& blobPath, |
| 258 | const http::Headers& headers, |
| 259 | const Option<Duration>& stallTimeout) |
| 260 | { |
| 261 | vector<string> argv = { |
| 262 | "curl", |
| 263 | "-s", // Don't show progress meter or error messages. |
| 264 | "-S", // Make curl show an error message if it fails. |
| 265 | "-w", "%{http_code}\n%{redirect_url}", // Display HTTP response code and the redirected URL. // NOLINT(whitespace/line_length) |
| 266 | "-o", blobPath // Write output to the file. |
| 267 | }; |
| 268 | |
| 269 | // Add additional headers. |
| 270 | foreachpair (const string& key, const string& value, headers) { |
| 271 | argv.push_back("-H"); |
| 272 | argv.push_back(key + ": " + value); |
| 273 | } |
| 274 | |
| 275 | // Add a timeout for curl to abort when the download speed keeps below |
| 276 | // 1 byte per second. See: https://curl.haxx.se/docs/manpage.html#-y |
| 277 | if (stallTimeout.isSome()) { |
| 278 | argv.push_back("-y"); |
| 279 | argv.push_back(std::to_string(static_cast<long>(stallTimeout->secs()))); |
| 280 | } |
| 281 | |
| 282 | argv.push_back(uri); |
| 283 | |
| 284 | string cmd = strings::join(" ", argv); |
| 285 | |
| 286 | Try<Subprocess> s = subprocess( |
| 287 | "curl", |
| 288 | argv, |
| 289 | Subprocess::PATH(os::DEV_NULL), |
| 290 | Subprocess::PIPE(), |
| 291 | Subprocess::PIPE()); |
| 292 | |
| 293 | if (s.isError()) { |
| 294 | return Failure("Failed to exec the curl subprocess: " + s.error()); |
| 295 | } |
| 296 | |
| 297 | return await( |
| 298 | s->status(), |
| 299 | io::read(s->out().get()), |
| 300 | io::read(s->err().get())) |
| 301 | .then([=](const tuple< |
| 302 | Future<Option<int>>, |
| 303 | Future<string>, |
| 304 | Future<string>>& t) -> Future<int> { |
| 305 | const Future<Option<int>>& status = std::get<0>(t); |
| 306 | if (!status.isReady()) { |
| 307 | return Failure( |
| 308 | "Failed to get the exit status of the curl subprocess for '" + uri + |
| 309 | "': " + (status.isFailed() ? status.failure() : "discarded")); |
| 310 | } |
| 311 | |
| 312 | if (status->isNone()) { |