wget.cpp 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*
  2. * @build: make examples
  3. * @server bin/httpd -s restart -d
  4. * @client bin/wget http://127.0.0.1:8080/
  5. */
  6. #include "http_client.h"
  7. #include "htime.h"
  8. using namespace hv;
  9. typedef std::function<void(size_t received_bytes, size_t total_bytes)> wget_progress_cb;
  10. static int wget(const char* url, const char* filepath, wget_progress_cb progress_cb = NULL) {
  11. HFile file;
  12. if (file.open(filepath, "wb") != 0) {
  13. fprintf(stderr, "Failed to open file %s\n", filepath);
  14. return -20;
  15. }
  16. printf("Save file to %s ...\n", filepath);
  17. HttpClient cli;
  18. HttpRequest req;
  19. req.url = url;
  20. HttpResponse resp;
  21. // HEAD
  22. req.method = HTTP_HEAD;
  23. int ret = cli.send(&req, &resp);
  24. if (ret != 0) {
  25. fprintf(stderr, "request error: %d\n", ret);
  26. return -1;
  27. }
  28. printd("%s", resp.Dump(true, false).c_str());
  29. if (resp.status_code == HTTP_STATUS_NOT_FOUND) {
  30. fprintf(stderr, "404 Not Found\n");
  31. return -1;
  32. }
  33. bool use_range = false;
  34. int range_bytes = 1 << 20; // 1M
  35. std::string accept_ranges = resp.GetHeader("Accept-Ranges");
  36. size_t content_length = hv::from_string<size_t>(resp.GetHeader("Content-Length"));
  37. // use Range if server accept_ranges and content_length > 1M
  38. if (resp.status_code == 200 &&
  39. accept_ranges == "bytes" &&
  40. content_length > range_bytes) {
  41. use_range = true;
  42. }
  43. // GET
  44. req.method = HTTP_GET;
  45. req.timeout = 3600; // 1h
  46. if (!use_range) {
  47. size_t received_bytes = 0;
  48. req.http_cb = [&file, &content_length, &received_bytes, &progress_cb]
  49. (HttpMessage* resp, http_parser_state state, const char* data, size_t size) {
  50. if (state == HP_HEADERS_COMPLETE) {
  51. content_length = hv::from_string<size_t>(resp->GetHeader("Content-Length"));
  52. printd("%s", resp->Dump(true, false).c_str());
  53. } else if (state == HP_BODY) {
  54. if (data && size) {
  55. file.write(data, size);
  56. received_bytes += size;
  57. if (progress_cb) {
  58. progress_cb(received_bytes, content_length);
  59. }
  60. }
  61. }
  62. };
  63. ret = cli.send(&req, &resp);
  64. if (ret != 0) {
  65. fprintf(stderr, "request error: %d\n", ret);
  66. return -1;
  67. }
  68. return 0;
  69. }
  70. // Range: bytes=from-to
  71. long from = 0, to = 0;
  72. while (from < content_length) {
  73. to = from + range_bytes - 1;
  74. if (to >= content_length) to = content_length - 1;
  75. req.SetRange(from, to);
  76. printd("%s", req.Dump(true, false).c_str());
  77. ret = cli.send(&req, &resp);
  78. if (ret != 0) {
  79. fprintf(stderr, "request error: %d\n", ret);
  80. return -1;
  81. }
  82. printd("%s", resp.Dump(true, false).c_str());
  83. file.write(resp.body.data(), resp.body.size());
  84. from = to + 1;
  85. if (progress_cb) {
  86. progress_cb(from, content_length);
  87. }
  88. }
  89. return 0;
  90. }
  91. int main(int argc, char** argv) {
  92. if (argc < 2) {
  93. printf("Usage: %s url [filepath]\n", argv[0]);
  94. return -10;
  95. }
  96. const char* url = argv[1];
  97. const char* filepath = "index.html";
  98. if (argc > 2) {
  99. filepath = argv[2];
  100. } else {
  101. const char* path = strrchr(url, '/');
  102. if (path && path[1]) {
  103. filepath = path + 1;
  104. }
  105. }
  106. unsigned int start_time = gettick_ms();
  107. int last_progress = 0;
  108. wget(url, filepath, [&last_progress](size_t received_bytes, size_t total_bytes) {
  109. // print progress
  110. if (total_bytes == 0) {
  111. printf("\rprogress: %lu/? = ?", (unsigned long)received_bytes);
  112. } else {
  113. int cur_progress = received_bytes * 100 / total_bytes;
  114. if (cur_progress > last_progress) {
  115. printf("\rprogress: %lu/%lu = %d%%", (unsigned long)received_bytes, (unsigned long)total_bytes, (int)cur_progress);
  116. last_progress = cur_progress;
  117. }
  118. }
  119. fflush(stdout);
  120. });
  121. unsigned int end_time = gettick_ms();
  122. printf("\ncost time %u ms\n", end_time - start_time);
  123. return 0;
  124. }