initial commit
This commit is contained in:
330
third_party/libhv/examples/curl.cpp
vendored
Executable file
330
third_party/libhv/examples/curl.cpp
vendored
Executable file
@@ -0,0 +1,330 @@
|
||||
/*
|
||||
* @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;
|
||||
}
|
||||
Reference in New Issue
Block a user