curl.cpp 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330
  1. /*
  2. * @build: make examples
  3. * @server bin/httpd -s restart -d
  4. * @usage: bin/curl -v www.baidu.com
  5. * bin/curl -v 127.0.0.1:8080
  6. * bin/curl -v 127.0.0.1:8080/ping
  7. * bin/curl -v 127.0.0.1:8080/echo -d 'hello,world!'
  8. */
  9. #include "HttpClient.h"
  10. #include "hurl.h"
  11. #ifdef _MSC_VER
  12. #include "misc/win32_getopt.h"
  13. #else
  14. #include <getopt.h>
  15. #endif
  16. static bool verbose = false;
  17. static const char* method = NULL;
  18. static const char* url = "/";
  19. static int http_version = 1;
  20. static int grpc = 0;
  21. static int send_count = 1;
  22. static int retry_count = 0;
  23. static int retry_delay = 3;
  24. static int timeout = 0;
  25. static int lopt = 0;
  26. static const char* http_proxy = NULL;
  27. static const char* https_proxy = NULL;
  28. static const char* no_proxy = NULL;
  29. static const char* options = "hVvX:H:r:d:F:n:";
  30. static const struct option long_options[] = {
  31. {"help", no_argument, NULL, 'h'},
  32. {"verion", no_argument, NULL, 'V'},
  33. {"verbose", no_argument, NULL, 'v'},
  34. {"method", required_argument, NULL, 'X'},
  35. {"header", required_argument, NULL, 'H'},
  36. {"range", required_argument, NULL, 'r'},
  37. {"data", required_argument, NULL, 'd'},
  38. {"form", required_argument, NULL, 'F'},
  39. {"count", required_argument, NULL, 'n'},
  40. {"http2", no_argument, &http_version, 2},
  41. {"grpc", no_argument, &grpc, 1},
  42. \
  43. {"http-proxy", required_argument, &lopt, 1},
  44. {"https-proxy", required_argument, &lopt, 2},
  45. {"no-proxy", required_argument, &lopt, 3},
  46. {"retry", required_argument, &lopt, 4},
  47. {"delay", required_argument, &lopt, 5},
  48. {"timeout", required_argument, &lopt, 6},
  49. \
  50. {NULL, 0, NULL, 0}
  51. };
  52. static const char* help = R"(Options:
  53. -h|--help Print this message.
  54. -V|--version Print version.
  55. -v|--verbose Show verbose infomation.
  56. -X|--method Set http method.
  57. -H|--header Add http header, -H "Content-Type: application/json"
  58. -r|--range Add http header Range:bytes=0-1023
  59. -d|--data Set http body.
  60. -F|--form Set http form, -F "name=value" -F "file=@filename"
  61. -n|--count Send request count, used for test keep-alive
  62. --http2 Use http2
  63. --grpc Use grpc over http2
  64. --http-proxy Set http proxy
  65. --https-proxy Set https proxy
  66. --no-proxy Set no proxy
  67. --retry Set fail retry count
  68. --timeout Set timeout, unit(s)
  69. Examples:
  70. curl -v GET httpbin.org/get
  71. curl -v POST httpbin.org/post user=admin pswd=123456
  72. curl -v PUT httpbin.org/put user=admin pswd=123456
  73. curl -v localhost:8080
  74. curl -v localhost:8080 -r 0-9
  75. curl -v localhost:8080/ping
  76. curl -v localhost:8080/query?page_no=1\&page_size=10
  77. curl -v localhost:8080/echo hello,world!
  78. curl -v localhost:8080/kv user=admin\&pswd=123456
  79. curl -v localhost:8080/json user=admin pswd=123456
  80. curl -v localhost:8080/form -F file=@filename
  81. curl -v localhost:8080/upload @filename
  82. )";
  83. static void print_usage() {
  84. fprintf(stderr, "Usage: curl [%s] [METHOD] url [header_field:header_value] [body_key=body_value]\n", options);
  85. }
  86. static void print_version() {
  87. fprintf(stderr, "curl version 1.0.0\n");
  88. }
  89. static void print_help() {
  90. print_usage();
  91. puts(help);
  92. print_version();
  93. }
  94. static bool is_upper_string(const char* str) {
  95. const char* p = str;
  96. while (*p >= 'A' && *p <= 'Z') ++p;
  97. return *p == '\0';
  98. }
  99. static int parse_data(char* arg, HttpRequest* req) {
  100. char* pos = NULL;
  101. // @filename
  102. if (arg[0] == '@') {
  103. req->File(arg + 1);
  104. return 0;
  105. }
  106. // k1=v1&k2=v2
  107. hv::KeyValue kvs = hv::splitKV(arg, '&', '=');
  108. if (kvs.size() >= 2) {
  109. if (req->ContentType() == CONTENT_TYPE_NONE) {
  110. req->content_type = X_WWW_FORM_URLENCODED;
  111. }
  112. for (auto& kv : kvs) {
  113. req->Set(kv.first.c_str(), kv.second);
  114. }
  115. return 0;
  116. }
  117. // k=v
  118. if ((pos = strchr(arg, '=')) != NULL) {
  119. *pos = '\0';
  120. if (pos[1] == '@') {
  121. // file=@filename
  122. req->content_type = MULTIPART_FORM_DATA;
  123. req->SetFormFile(optarg, pos + 2);
  124. } else {
  125. if (req->ContentType() == CONTENT_TYPE_NONE) {
  126. req->content_type = APPLICATION_JSON;
  127. }
  128. req->Set(arg, pos + 1);
  129. }
  130. return 0;
  131. }
  132. if (req->ContentType() == CONTENT_TYPE_NONE) {
  133. req->content_type = TEXT_PLAIN;
  134. }
  135. req->body = arg;
  136. return 0;
  137. }
  138. static int parse_cmdline(int argc, char* argv[], HttpRequest* req) {
  139. int opt;
  140. int opt_idx;
  141. char* pos = NULL;
  142. while ((opt = getopt_long(argc, argv, options, long_options, &opt_idx)) != EOF) {
  143. switch(opt) {
  144. case 'h': print_help(); exit(0);
  145. case 'V': print_version(); exit(0);
  146. case 'v': verbose = true; break;
  147. case 'X': method = optarg; break;
  148. case 'H':
  149. // -H "Content-Type: application/json"
  150. pos = strchr(optarg, ':');
  151. if (pos) {
  152. *pos = '\0';
  153. req->headers[optarg] = hv::trim(pos + 1);
  154. *pos = ':';
  155. }
  156. break;
  157. case 'r':
  158. req->headers["Range"] = std::string("bytes=").append(optarg);
  159. break;
  160. case 'd':
  161. parse_data(optarg, req);
  162. break;
  163. case 'F':
  164. pos = strchr(optarg, '=');
  165. if (pos) {
  166. req->content_type = MULTIPART_FORM_DATA;
  167. *pos = '\0';
  168. if (pos[1] == '@') {
  169. // -F file=@filename
  170. req->SetFormFile(optarg, pos + 2);
  171. } else {
  172. // -F name=value
  173. req->SetFormData(optarg, pos + 1);
  174. }
  175. *pos = '=';
  176. }
  177. break;
  178. case 'n': send_count = atoi(optarg); break;
  179. case 0 :
  180. {
  181. switch (lopt) {
  182. case 1: http_proxy = optarg; break;
  183. case 2: https_proxy = optarg; break;
  184. case 3: no_proxy = optarg; break;
  185. case 4: retry_count = atoi(optarg);break;
  186. case 5: retry_delay = atoi(optarg);break;
  187. case 6: timeout = atoi(optarg);break;
  188. default: break;
  189. }
  190. }
  191. default: break;
  192. }
  193. }
  194. if (optind == argc) {
  195. fprintf(stderr, "Missing url\n");
  196. print_usage();
  197. exit(-1);
  198. }
  199. if (is_upper_string(argv[optind])) {
  200. method = argv[optind++];
  201. }
  202. url = argv[optind++];
  203. for (int d = optind; d < argc; ++d) {
  204. char* arg = argv[d];
  205. if ((pos = strchr(arg, ':')) != NULL) {
  206. *pos = '\0';
  207. req->headers[arg] = pos + 1;
  208. } else {
  209. parse_data(arg, req);
  210. }
  211. }
  212. // --grpc
  213. if (grpc) {
  214. http_version = 2;
  215. req->content_type = APPLICATION_GRPC;
  216. }
  217. // --http2
  218. if (http_version == 2) {
  219. req->http_major = 2;
  220. req->http_minor = 0;
  221. }
  222. // --timeout
  223. if (timeout > 0) {
  224. req->timeout = timeout;
  225. }
  226. return 0;
  227. }
  228. int main(int argc, char* argv[]) {
  229. if (argc < 2) {
  230. print_usage();
  231. return 0;
  232. }
  233. int ret = 0;
  234. HttpRequest req;
  235. parse_cmdline(argc, argv, &req);
  236. if (method) {
  237. req.method = http_method_enum(method);
  238. } else {
  239. req.DumpBody();
  240. if (req.body.empty()) {
  241. req.method = HTTP_GET;
  242. } else {
  243. req.method = HTTP_POST;
  244. }
  245. }
  246. req.url = hv::escapeURL(url);
  247. req.http_cb = [](HttpMessage* res, http_parser_state state, const char* data, size_t size) {
  248. if (state == HP_HEADERS_COMPLETE) {
  249. if (verbose) {
  250. fprintf(stderr, "%s", res->Dump(true, false).c_str());
  251. }
  252. } else if (state == HP_BODY) {
  253. if (data && size) {
  254. printf("%.*s", (int)size, data);
  255. // This program no need to save data to body.
  256. // res->body.append(data, size);
  257. }
  258. }
  259. };
  260. hv::HttpClient cli;
  261. // http_proxy
  262. if (http_proxy) {
  263. hv::StringList ss = hv::split(http_proxy, ':');
  264. const char* host = ss[0].c_str();
  265. int port = ss.size() == 2 ? hv::from_string<int>(ss[1]) : DEFAULT_HTTP_PORT;
  266. fprintf(stderr, "* http_proxy=%s:%d\n", host, port);
  267. cli.setHttpProxy(host, port);
  268. }
  269. // https_proxy
  270. if (https_proxy) {
  271. hv::StringList ss = hv::split(https_proxy, ':');
  272. const char* host = ss[0].c_str();
  273. int port = ss.size() == 2 ? hv::from_string<int>(ss[1]) : DEFAULT_HTTPS_PORT;
  274. fprintf(stderr, "* https_proxy=%s:%d\n", host, port);
  275. cli.setHttpsProxy(host, port);
  276. }
  277. // no_proxy
  278. if (no_proxy) {
  279. hv::StringList ss = hv::split(no_proxy, ',');
  280. fprintf(stderr, "* no_proxy=");
  281. for (const auto& s : ss) {
  282. fprintf(stderr, "%s,", s.c_str());
  283. cli.addNoProxy(s.c_str());
  284. }
  285. fprintf(stderr, "\n");
  286. }
  287. send:
  288. if (verbose) {
  289. fprintf(stderr, "%s\n", req.Dump(true, true).c_str());
  290. }
  291. HttpResponse res;
  292. ret = cli.send(&req, &res);
  293. if (ret != 0) {
  294. fprintf(stderr, "* Failed:%s:%d\n", http_client_strerror(ret), ret);
  295. if (retry_count > 0) {
  296. fprintf(stderr, "\nretry again later...%d\n", retry_count);
  297. --retry_count;
  298. hv_sleep(retry_delay);
  299. goto send;
  300. }
  301. }
  302. if (--send_count > 0) {
  303. fprintf(stderr, "\nsend again later...%d\n", send_count);
  304. hv_sleep(retry_delay);
  305. goto send;
  306. }
  307. return ret;
  308. }