curl.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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 int http_version = 1;
  16. static int grpc = 0;
  17. static bool verbose = false;
  18. static const char* url = NULL;
  19. static const char* method = NULL;
  20. static const char* headers = NULL;
  21. static const char* range = NULL;
  22. static const char* data = NULL;
  23. static const char* form = NULL;
  24. static int send_count = 1;
  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. \
  47. {NULL, 0, NULL, 0}
  48. };
  49. static const char* help = R"(Options:
  50. -h|--help Print this message.
  51. -V|--version Print version.
  52. -v|--verbose Show verbose infomation.
  53. -X|--method Set http method.
  54. -H|--header Add http headers, -H "Content-Type:application/json Accept:*/*"
  55. -r|--range Add http header Range:bytes=0-1023
  56. -d|--data Set http body.
  57. -F|--form Set http form, -F "name1=content name2=@filename"
  58. -n|--count Send request count, used for test keep-alive
  59. --http2 Use http2
  60. --grpc Use grpc over http2
  61. --http-proxy Set http proxy
  62. --https-proxy Set https proxy
  63. --no-proxy Set no proxy
  64. Examples:
  65. curl -v GET httpbin.org/get
  66. curl -v POST httpbin.org/post user=admin pswd=123456
  67. curl -v PUT httpbin.org/put user=admin pswd=123456
  68. curl -v localhost:8080
  69. curl -v localhost:8080 -r 0-9
  70. curl -v localhost:8080/ping
  71. curl -v localhost:8080/query?page_no=1\&page_size=10
  72. curl -v localhost:8080/echo hello,world!
  73. curl -v localhost:8080/kv user=admin\&pswd=123456
  74. curl -v localhost:8080/json user=admin pswd=123456
  75. curl -v localhost:8080/form -F file=@filename
  76. )";
  77. static void print_usage() {
  78. printf("Usage: curl [%s] [METHOD] url [header_field:header_value] [body_key=body_value]\n", options);
  79. }
  80. static void print_version() {
  81. printf("curl version 1.0.0\n");
  82. }
  83. static void print_help() {
  84. print_usage();
  85. puts(help);
  86. print_version();
  87. }
  88. static bool is_upper_string(const char* str) {
  89. const char* p = str;
  90. while (*p >= 'A' && *p <= 'Z') ++p;
  91. return *p == '\0';
  92. }
  93. int parse_cmdline(int argc, char* argv[]) {
  94. int opt;
  95. int opt_idx;
  96. while ((opt = getopt_long(argc, argv, options, long_options, &opt_idx)) != EOF) {
  97. switch(opt) {
  98. case 'h': print_help(); exit(0);
  99. case 'V': print_version(); exit(0);
  100. case 'v': verbose = true; break;
  101. case 'X': method = optarg; break;
  102. case 'H': headers = optarg; break;
  103. case 'r': range = optarg; break;
  104. case 'd': data = optarg; break;
  105. case 'F': form = optarg; break;
  106. case 'n': send_count = atoi(optarg); break;
  107. case 0 :
  108. {
  109. switch (lopt) {
  110. case 1: http_proxy = optarg; break;
  111. case 2: https_proxy = optarg; break;
  112. case 3: no_proxy = optarg; break;
  113. default: break;
  114. }
  115. }
  116. default: break;
  117. }
  118. }
  119. if (optind == argc) {
  120. printf("Missing url\n");
  121. print_usage();
  122. exit(-1);
  123. }
  124. if (is_upper_string(argv[optind])) {
  125. method = argv[optind++];
  126. }
  127. url = argv[optind++];
  128. return 0;
  129. }
  130. int main(int argc, char* argv[]) {
  131. if (argc < 2) {
  132. print_usage();
  133. return 0;
  134. }
  135. parse_cmdline(argc, argv);
  136. int ret = 0;
  137. HttpRequest req;
  138. if (grpc) {
  139. http_version = 2;
  140. req.content_type = APPLICATION_GRPC;
  141. }
  142. if (http_version == 2) {
  143. req.http_major = 2;
  144. req.http_minor = 0;
  145. }
  146. req.url = url;
  147. if (method) {
  148. req.method = http_method_enum(method);
  149. }
  150. enum {
  151. s_key,
  152. s_value,
  153. } state = s_key;
  154. if (headers) {
  155. const char* p = headers;
  156. const char* key = p;
  157. const char* value = NULL;
  158. int key_len = 0;
  159. int value_len = 0;
  160. state = s_key;
  161. while (*p != '\0') {
  162. if (*p == ' ') {
  163. if (key_len && value_len) {
  164. req.headers[std::string(key,key_len)] = std::string(value,value_len);
  165. key_len = value_len = 0;
  166. state = s_key;
  167. }
  168. }
  169. else if (*p == ':') {
  170. state = s_value;
  171. }
  172. else {
  173. if (state == s_key) {
  174. if (++key_len == 1) key = p;
  175. }
  176. else {
  177. if (++value_len == 1) value = p;
  178. }
  179. }
  180. ++p;
  181. }
  182. if (key_len && value_len) {
  183. req.headers[std::string(key,key_len)] = std::string(value,value_len);
  184. key_len = value_len = 0;
  185. }
  186. }
  187. if (range) {
  188. req.headers["Range"] = std::string("bytes=").append(range);
  189. }
  190. if (data || form) {
  191. if (method == NULL) {
  192. req.method = HTTP_POST;
  193. }
  194. if (data) {
  195. req.body = data;
  196. }
  197. else if (form) {
  198. const char* p = form;
  199. const char* key = p;
  200. const char* value = NULL;
  201. int key_len = 0;
  202. int value_len = 0;
  203. state = s_key;
  204. while (*p != '\0') {
  205. if (*p == ' ') {
  206. if (key_len && value_len) {
  207. FormData data;
  208. if (*value == '@') {
  209. data.filename = std::string(value+1, value_len-1);
  210. }
  211. else {
  212. data.content = std::string(value, value_len);
  213. }
  214. req.form[std::string(key,key_len)] = data;
  215. key_len = value_len = 0;
  216. state = s_key;
  217. }
  218. }
  219. else if (*p == '=') {
  220. state = s_value;
  221. }
  222. else {
  223. if (state == s_key) {
  224. if (++key_len == 1) key = p;
  225. }
  226. else {
  227. if (++value_len == 1) value = p;
  228. }
  229. }
  230. ++p;
  231. }
  232. if (key_len && value_len) {
  233. // printf("key=%.*s value=%.*s\n", key_len, key, value_len, value);
  234. FormData data;
  235. if (*value == '@') {
  236. data.filename = std::string(value+1, value_len-1);
  237. }
  238. else {
  239. data.content = std::string(value, value_len);
  240. }
  241. req.form[std::string(key,key_len)] = data;
  242. }
  243. }
  244. }
  245. for (int d = optind; d < argc; ++d) {
  246. const char* arg = argv[d];
  247. const char* pos = NULL;
  248. if ((pos = strchr(arg, ':')) != NULL) {
  249. // header_field:header_value
  250. *(char*)pos = '\0';
  251. req.headers[arg] = pos + 1;
  252. } else {
  253. if (method == NULL) {
  254. req.method = HTTP_POST;
  255. }
  256. if ((pos = strchr(arg, '&')) != NULL) {
  257. if (req.ContentType() == CONTENT_TYPE_NONE) {
  258. req.content_type = X_WWW_FORM_URLENCODED;
  259. }
  260. req.body = arg;
  261. }
  262. else if ((pos = strchr(arg, '=')) != NULL) {
  263. // body_key=body_value
  264. if (req.ContentType() == CONTENT_TYPE_NONE) {
  265. req.content_type = APPLICATION_JSON;
  266. }
  267. *(char*)pos = '\0';
  268. req.Set(arg, pos + 1);
  269. }
  270. else {
  271. if (req.ContentType() == CONTENT_TYPE_NONE) {
  272. req.content_type = TEXT_PLAIN;
  273. }
  274. req.body = arg;
  275. }
  276. }
  277. }
  278. HttpResponse res;
  279. /*
  280. res.head_cb = [](const http_headers& headers){
  281. for (auto& header : headers) {
  282. printf("%s: %s\r\n", header.first.c_str(), header.second.c_str());
  283. }
  284. printf("\r\n");
  285. };
  286. res.body_cb = [](const char* data, size_t size){
  287. printf("%.*s", (int)size, data);
  288. };
  289. */
  290. res.chunked_cb = [](const char* data, size_t size){
  291. printf("%.*s", (int)size, data);
  292. };
  293. http_client_t* cli = http_client_new();
  294. // http_proxy
  295. if (http_proxy) {
  296. hv::StringList ss = hv::split(http_proxy, ':');
  297. const char* host = ss[0].c_str();
  298. int port = ss.size() == 2 ? hv::from_string<int>(ss[1]) : DEFAULT_HTTP_PORT;
  299. printf("* http_proxy=%s:%d\n", host, port);
  300. http_client_set_http_proxy(cli, host, port);
  301. }
  302. // https_proxy
  303. if (https_proxy) {
  304. hv::StringList ss = hv::split(https_proxy, ':');
  305. const char* host = ss[0].c_str();
  306. int port = ss.size() == 2 ? hv::from_string<int>(ss[1]) : DEFAULT_HTTPS_PORT;
  307. printf("* https_proxy=%s:%d\n", host, port);
  308. http_client_set_https_proxy(cli, host, port);
  309. }
  310. // no_proxy
  311. if (no_proxy) {
  312. hv::StringList ss = hv::split(no_proxy, ',');
  313. printf("* no_proxy=");
  314. for (const auto& s : ss) {
  315. printf("%s,", s.c_str());
  316. http_client_add_no_proxy(cli, s.c_str());
  317. }
  318. printf("\n");
  319. }
  320. send:
  321. if (verbose) {
  322. printf("%s\n", req.Dump(true,true).c_str());
  323. }
  324. ret = http_client_send(cli, &req, &res);
  325. if (ret != 0) {
  326. printf("* Failed:%s:%d\n", http_client_strerror(ret), ret);
  327. } else {
  328. if (verbose) {
  329. printf("%s\n", res.Dump(true,true).c_str());
  330. }
  331. else {
  332. printf("%s\n", res.body.c_str());
  333. }
  334. }
  335. if (--send_count > 0) {
  336. printf("send again later...%d\n", send_count);
  337. #ifdef _WIN32
  338. Sleep(3*1000);
  339. #else
  340. sleep(3);
  341. #endif
  342. goto send;
  343. }
  344. http_client_del(cli);
  345. return ret;
  346. }