curl.cpp 9.8 KB

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