| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 |
- /*
- * @build: make examples
- * @server bin/httpd -s restart -d
- * @client bin/wget http://127.0.0.1:8080/
- */
- #include "HttpClient.h"
- #include "htime.h"
- using namespace hv;
- typedef std::function<void(size_t received_bytes, size_t total_bytes)> wget_progress_cb;
- static int wget(const char* url, const char* filepath, wget_progress_cb progress_cb = NULL, bool use_range = true) {
- int ret = 0;
- HttpClient cli;
- HttpRequest req;
- HttpResponse resp;
- // HEAD
- req.method = HTTP_HEAD;
- req.url = url;
- ret = cli.send(&req, &resp);
- if (ret != 0) {
- fprintf(stderr, "HEAD request error: %d\n", ret);
- return ret;
- }
- printd("%s", resp.Dump(true, false).c_str());
- if (resp.status_code == HTTP_STATUS_NOT_FOUND) {
- fprintf(stderr, "404 Not Found\n");
- return 404;
- }
- // use Range?
- int range_bytes = 1 << 20; // 1M
- long from = 0, to = 0;
- size_t content_length = hv::from_string<size_t>(resp.GetHeader("Content-Length"));
- if (use_range) {
- use_range = false;
- std::string accept_ranges = resp.GetHeader("Accept-Ranges");
- // use Range if server accept_ranges and content_length > 1M
- if (resp.status_code == 200 &&
- accept_ranges == "bytes" &&
- content_length > range_bytes) {
- use_range = true;
- }
- }
- // open file
- std::string filepath_download(filepath);
- filepath_download += ".download";
- HFile file;
- if (use_range) {
- ret = file.open(filepath_download.c_str(), "ab");
- from = file.size();
- } else {
- ret = file.open(filepath_download.c_str(), "wb");
- }
- if (ret != 0) {
- fprintf(stderr, "Failed to open file %s\n", filepath_download.c_str());
- return ret;
- }
- printf("Save file to %s ...\n", filepath);
- // GET
- req.method = HTTP_GET;
- req.timeout = 3600; // 1h
- if (!use_range) {
- size_t received_bytes = 0;
- req.http_cb = [&file, &content_length, &received_bytes, &progress_cb]
- (HttpMessage* resp, http_parser_state state, const char* data, size_t size) {
- if (!resp->headers["Location"].empty()) return;
- if (state == HP_HEADERS_COMPLETE) {
- content_length = hv::from_string<size_t>(resp->GetHeader("Content-Length"));
- printd("%s", resp->Dump(true, false).c_str());
- } else if (state == HP_BODY) {
- if (data && size) {
- file.write(data, size);
- received_bytes += size;
- if (progress_cb) {
- progress_cb(received_bytes, content_length);
- }
- }
- }
- };
- ret = cli.send(&req, &resp);
- if (ret != 0) {
- fprintf(stderr, "GET request error: %d\n", ret);
- goto error;
- }
- goto success;
- }
- // Range: bytes=from-to
- while (from < content_length) {
- to = from + range_bytes - 1;
- if (to >= content_length) to = content_length - 1;
- req.SetRange(from, to);
- printd("%s", req.Dump(true, false).c_str());
- ret = cli.send(&req, &resp);
- if (ret != 0) {
- fprintf(stderr, "GET Range: bytes=%ld-%ld request error: %d\n", from, to, ret);
- goto error;
- }
- printd("%s", resp.Dump(true, false).c_str());
- file.write(resp.body.data(), resp.body.size());
- // fix: resp.body.size != range_bytes on some server
- // from = to + 1;
- from += resp.body.size();
- if (progress_cb) {
- progress_cb(from, content_length);
- }
- }
- success:
- file.close();
- ret = file.rename(filepath);
- if (ret != 0) {
- fprintf(stderr, "mv %s => %s failed: %s:%d\n", filepath_download.c_str(), filepath, strerror(ret), ret);
- }
- return ret;
- error:
- file.close();
- // file.remove();
- return ret;
- }
- int main(int argc, char** argv) {
- if (argc < 2) {
- printf("Usage: %s [--use_range] url [filepath]\n", argv[0]);
- return -10;
- }
- int idx = 1;
- bool use_range = false;
- if (strcmp(argv[idx], "--use_range") == 0) {
- use_range = true;
- ++idx;
- }
- const char* url = argv[idx++];
- const char* filepath = "index.html";
- if (argv[idx]) {
- filepath = argv[idx];
- } else {
- const char* path = strrchr(url, '/');
- if (path && path[1]) {
- filepath = path + 1;
- }
- }
- unsigned int start_time = gettick_ms();
- int last_progress = 0;
- wget(url, filepath, [&last_progress](size_t received_bytes, size_t total_bytes) {
- // print progress
- if (total_bytes == 0) {
- printf("\rprogress: %lu/? = ?", (unsigned long)received_bytes);
- } else {
- int cur_progress = received_bytes * 100 / total_bytes;
- if (cur_progress > last_progress) {
- printf("\rprogress: %lu/%lu = %d%%", (unsigned long)received_bytes, (unsigned long)total_bytes, (int)cur_progress);
- last_progress = cur_progress;
- }
- }
- fflush(stdout);
- }, use_range);
- unsigned int end_time = gettick_ms();
- unsigned int cost_time = end_time - start_time;
- printf("\ncost time %u ms\n", cost_time);
- // 1B/ms = 1KB/s = 8Kbps
- printf("download rate = %lu KB/s\n", (unsigned long)hv_filesize(filepath) / cost_time);
- return 0;
- }
|