emsApplication/3rdPartner/libhv/examples/curl.cpp

331 lines
9.8 KiB
C++
Raw Normal View History

2024-05-24 12:19:45 +08:00
/*
* @build: make examples
* @server bin/httpd -s restart -d
* @usage: bin/curl -v www.baidu.com
* bin/curl -v 127.0.0.1:8080
* bin/curl -v 127.0.0.1:8080/ping
* bin/curl -v 127.0.0.1:8080/echo -d 'hello,world!'
*/
#include "HttpClient.h"
#include "hurl.h"
#ifdef _MSC_VER
#include "misc/win32_getopt.h"
#else
#include <getopt.h>
#endif
static bool verbose = false;
static const char* method = NULL;
static const char* url = "/";
static int http_version = 1;
static int grpc = 0;
static int send_count = 1;
static int retry_count = 0;
static int retry_delay = 3;
static int timeout = 0;
static int lopt = 0;
static const char* http_proxy = NULL;
static const char* https_proxy = NULL;
static const char* no_proxy = NULL;
static const char* options = "hVvX:H:r:d:F:n:";
static const struct option long_options[] = {
{"help", no_argument, NULL, 'h'},
{"verion", no_argument, NULL, 'V'},
{"verbose", no_argument, NULL, 'v'},
{"method", required_argument, NULL, 'X'},
{"header", required_argument, NULL, 'H'},
{"range", required_argument, NULL, 'r'},
{"data", required_argument, NULL, 'd'},
{"form", required_argument, NULL, 'F'},
{"count", required_argument, NULL, 'n'},
{"http2", no_argument, &http_version, 2},
{"grpc", no_argument, &grpc, 1},
\
{"http-proxy", required_argument, &lopt, 1},
{"https-proxy", required_argument, &lopt, 2},
{"no-proxy", required_argument, &lopt, 3},
{"retry", required_argument, &lopt, 4},
{"delay", required_argument, &lopt, 5},
{"timeout", required_argument, &lopt, 6},
\
{NULL, 0, NULL, 0}
};
static const char* help = R"(Options:
-h|--help Print this message.
-V|--version Print version.
-v|--verbose Show verbose infomation.
-X|--method Set http method.
-H|--header Add http header, -H "Content-Type: application/json"
-r|--range Add http header Range:bytes=0-1023
-d|--data Set http body.
-F|--form Set http form, -F "name=value" -F "file=@filename"
-n|--count Send request count, used for test keep-alive
--http2 Use http2
--grpc Use grpc over http2
--http-proxy Set http proxy
--https-proxy Set https proxy
--no-proxy Set no proxy
--retry Set fail retry count
--timeout Set timeout, unit(s)
Examples:
curl -v GET httpbin.org/get
curl -v POST httpbin.org/post user=admin pswd=123456
curl -v PUT httpbin.org/put user=admin pswd=123456
curl -v localhost:8080
curl -v localhost:8080 -r 0-9
curl -v localhost:8080/ping
curl -v localhost:8080/query?page_no=1\&page_size=10
curl -v localhost:8080/echo hello,world!
curl -v localhost:8080/kv user=admin\&pswd=123456
curl -v localhost:8080/json user=admin pswd=123456
curl -v localhost:8080/form -F file=@filename
curl -v localhost:8080/upload @filename
)";
static void print_usage() {
fprintf(stderr, "Usage: curl [%s] [METHOD] url [header_field:header_value] [body_key=body_value]\n", options);
}
static void print_version() {
fprintf(stderr, "curl version 1.0.0\n");
}
static void print_help() {
print_usage();
puts(help);
print_version();
}
static bool is_upper_string(const char* str) {
const char* p = str;
while (*p >= 'A' && *p <= 'Z') ++p;
return *p == '\0';
}
static int parse_data(char* arg, HttpRequest* req) {
char* pos = NULL;
// @filename
if (arg[0] == '@') {
req->File(arg + 1);
return 0;
}
// k1=v1&k2=v2
hv::KeyValue kvs = hv::splitKV(arg, '&', '=');
if (kvs.size() >= 2) {
if (req->ContentType() == CONTENT_TYPE_NONE) {
req->content_type = X_WWW_FORM_URLENCODED;
}
for (auto& kv : kvs) {
req->Set(kv.first.c_str(), kv.second);
}
return 0;
}
// k=v
if ((pos = strchr(arg, '=')) != NULL) {
*pos = '\0';
if (pos[1] == '@') {
// file=@filename
req->content_type = MULTIPART_FORM_DATA;
req->SetFormFile(optarg, pos + 2);
} else {
if (req->ContentType() == CONTENT_TYPE_NONE) {
req->content_type = APPLICATION_JSON;
}
req->Set(arg, pos + 1);
}
return 0;
}
if (req->ContentType() == CONTENT_TYPE_NONE) {
req->content_type = TEXT_PLAIN;
}
req->body = arg;
return 0;
}
static int parse_cmdline(int argc, char* argv[], HttpRequest* req) {
int opt;
int opt_idx;
char* pos = NULL;
while ((opt = getopt_long(argc, argv, options, long_options, &opt_idx)) != EOF) {
switch(opt) {
case 'h': print_help(); exit(0);
case 'V': print_version(); exit(0);
case 'v': verbose = true; break;
case 'X': method = optarg; break;
case 'H':
// -H "Content-Type: application/json"
pos = strchr(optarg, ':');
if (pos) {
*pos = '\0';
req->headers[optarg] = hv::trim(pos + 1);
*pos = ':';
}
break;
case 'r':
req->headers["Range"] = std::string("bytes=").append(optarg);
break;
case 'd':
parse_data(optarg, req);
break;
case 'F':
pos = strchr(optarg, '=');
if (pos) {
req->content_type = MULTIPART_FORM_DATA;
*pos = '\0';
if (pos[1] == '@') {
// -F file=@filename
req->SetFormFile(optarg, pos + 2);
} else {
// -F name=value
req->SetFormData(optarg, pos + 1);
}
*pos = '=';
}
break;
case 'n': send_count = atoi(optarg); break;
case 0 :
{
switch (lopt) {
case 1: http_proxy = optarg; break;
case 2: https_proxy = optarg; break;
case 3: no_proxy = optarg; break;
case 4: retry_count = atoi(optarg);break;
case 5: retry_delay = atoi(optarg);break;
case 6: timeout = atoi(optarg);break;
default: break;
}
}
default: break;
}
}
if (optind == argc) {
fprintf(stderr, "Missing url\n");
print_usage();
exit(-1);
}
if (is_upper_string(argv[optind])) {
method = argv[optind++];
}
url = argv[optind++];
for (int d = optind; d < argc; ++d) {
char* arg = argv[d];
if ((pos = strchr(arg, ':')) != NULL) {
*pos = '\0';
req->headers[arg] = pos + 1;
} else {
parse_data(arg, req);
}
}
// --grpc
if (grpc) {
http_version = 2;
req->content_type = APPLICATION_GRPC;
}
// --http2
if (http_version == 2) {
req->http_major = 2;
req->http_minor = 0;
}
// --timeout
if (timeout > 0) {
req->timeout = timeout;
}
return 0;
}
int main(int argc, char* argv[]) {
if (argc < 2) {
print_usage();
return 0;
}
int ret = 0;
HttpRequest req;
parse_cmdline(argc, argv, &req);
if (method) {
req.method = http_method_enum(method);
} else {
req.DumpBody();
if (req.body.empty()) {
req.method = HTTP_GET;
} else {
req.method = HTTP_POST;
}
}
req.url = hv::escapeURL(url);
req.http_cb = [](HttpMessage* res, http_parser_state state, const char* data, size_t size) {
if (state == HP_HEADERS_COMPLETE) {
if (verbose) {
fprintf(stderr, "%s", res->Dump(true, false).c_str());
}
} else if (state == HP_BODY) {
if (data && size) {
printf("%.*s", (int)size, data);
// This program no need to save data to body.
// res->body.append(data, size);
}
}
};
hv::HttpClient cli;
// http_proxy
if (http_proxy) {
hv::StringList ss = hv::split(http_proxy, ':');
const char* host = ss[0].c_str();
int port = ss.size() == 2 ? hv::from_string<int>(ss[1]) : DEFAULT_HTTP_PORT;
fprintf(stderr, "* http_proxy=%s:%d\n", host, port);
cli.setHttpProxy(host, port);
}
// https_proxy
if (https_proxy) {
hv::StringList ss = hv::split(https_proxy, ':');
const char* host = ss[0].c_str();
int port = ss.size() == 2 ? hv::from_string<int>(ss[1]) : DEFAULT_HTTPS_PORT;
fprintf(stderr, "* https_proxy=%s:%d\n", host, port);
cli.setHttpsProxy(host, port);
}
// no_proxy
if (no_proxy) {
hv::StringList ss = hv::split(no_proxy, ',');
fprintf(stderr, "* no_proxy=");
for (const auto& s : ss) {
fprintf(stderr, "%s,", s.c_str());
cli.addNoProxy(s.c_str());
}
fprintf(stderr, "\n");
}
send:
if (verbose) {
fprintf(stderr, "%s\n", req.Dump(true, true).c_str());
}
HttpResponse res;
ret = cli.send(&req, &res);
if (ret != 0) {
fprintf(stderr, "* Failed:%s:%d\n", http_client_strerror(ret), ret);
if (retry_count > 0) {
fprintf(stderr, "\nretry again later...%d\n", retry_count);
--retry_count;
hv_sleep(retry_delay);
goto send;
}
}
if (--send_count > 0) {
fprintf(stderr, "\nsend again later...%d\n", send_count);
hv_sleep(retry_delay);
goto send;
}
return ret;
}