350 lines
9.8 KiB
C
350 lines
9.8 KiB
C
#include <getopt.h>
|
|
#include <easy_io.h>
|
|
#include "easy_http_handler.h"
|
|
|
|
// 命令行参数结构
|
|
typedef struct cmdline_param {
|
|
easy_addr_t address;
|
|
easy_io_handler_pt io_handler;
|
|
int io_thread_cnt;
|
|
int connect_cnt;
|
|
int islarge, file_len;
|
|
int64_t request_cnt;
|
|
int keep_alive;
|
|
easy_buf_t *raw_header;
|
|
easy_string_pair_t *headers;
|
|
easy_pool_t *pool;
|
|
char url[256];
|
|
int pos;
|
|
|
|
// count
|
|
ev_tstamp start_time;
|
|
easy_atomic_t send_cnt;
|
|
easy_atomic_t process_cnt;
|
|
easy_atomic_t error_cnt;
|
|
easy_atomic_t timeout_cnt;
|
|
} cmdline_param;
|
|
|
|
/*************************************************************************************************
|
|
* 函数定义部分
|
|
*************************************************************************************************/
|
|
cmdline_param cp;
|
|
static void print_usage(char *prog_name);
|
|
static int parse_cmd_line(int argc, char *const argv[], cmdline_param *cp);
|
|
static int http_client_on_process(easy_request_t *r);
|
|
static int http_client_new_packet(easy_connection_t *c);
|
|
static int http_client_disconnect(easy_connection_t *c);
|
|
static int http_client_set_data(easy_request_t *r, const char *data, int len);
|
|
static void print_speed();
|
|
|
|
/**
|
|
* 程序入口
|
|
*/
|
|
int main(int argc, char **argv)
|
|
{
|
|
int i, ret, size;
|
|
easy_string_pair_t *header;
|
|
easy_buf_t *b;
|
|
|
|
// default
|
|
memset(&cp, 0, sizeof(cmdline_param));
|
|
cp.io_thread_cnt = 1;
|
|
cp.connect_cnt = 1;
|
|
cp.request_cnt = 1000;
|
|
|
|
// parse cmd line
|
|
cp.pool = easy_pool_create(8192);
|
|
|
|
if (parse_cmd_line(argc, argv, &cp) == EASY_ERROR)
|
|
return EASY_ERROR;
|
|
|
|
// 检查必需参数
|
|
if (cp.address.family == 0) {
|
|
print_usage(argv[0]);
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
// 对easy_io初始化, 设置io的线程数, file的线程数
|
|
if (!easy_io_create(cp.io_thread_cnt)) {
|
|
easy_error_log("easy_io_init error.\n");
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
// 为监听端口设置处理函数,并增加一个监听端口
|
|
cp.io_handler.decode = easy_http_client_on_decode;
|
|
cp.io_handler.encode = easy_http_client_on_encode;
|
|
cp.io_handler.process = http_client_on_process;
|
|
cp.io_handler.new_packet = http_client_new_packet;
|
|
cp.io_handler.on_disconnect = http_client_disconnect;
|
|
|
|
if (cp.islarge) cp.io_handler.set_data = http_client_set_data;
|
|
|
|
// 准备发送内容
|
|
size = 128 + strlen(cp.url);
|
|
header = cp.headers;
|
|
|
|
while(header) {
|
|
size += header->name.len;
|
|
header = header->next;
|
|
}
|
|
|
|
b = easy_buf_create(cp.pool, size);
|
|
b->last += lnprintf(b->last, (b->end - b->last), "GET %s HTTP/1.1\r\nConnection: keep-alive\r\n", cp.url);
|
|
header = cp.headers;
|
|
|
|
while(header) {
|
|
b->last += lnprintf(b->last, (b->end - b->last),
|
|
"%s\r\n", easy_buf_string_ptr(&header->name));
|
|
header = header->next;
|
|
}
|
|
|
|
b->last += lnprintf(b->last, (b->end - b->last), "\r\nGET ");
|
|
cp.raw_header = b;
|
|
|
|
// 创建连接
|
|
easy_addr_t addr = cp.address;
|
|
|
|
for(i = 0; i < cp.connect_cnt; i++) {
|
|
addr.cidx = i;
|
|
|
|
if (easy_io_connect(addr, &cp.io_handler, 0, NULL) != EASY_OK) {
|
|
char buffer[32];
|
|
easy_error_log("connection failure: %s\n", easy_inet_addr_to_str(&cp.address, buffer, 32));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// 起线程并开始
|
|
cp.start_time = ev_time();
|
|
|
|
// 起处理速度统计定时器
|
|
ev_timer stat_watcher;
|
|
easy_io_stat_t iostat;
|
|
easy_io_stat_watcher_start(&stat_watcher, 5.0, &iostat, NULL);
|
|
|
|
if (easy_io_start()) {
|
|
easy_error_log("easy_io_start error.\n");
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
// 等待线程退出
|
|
ret = easy_io_wait();
|
|
|
|
// speed
|
|
print_speed();
|
|
easy_io_destroy();
|
|
easy_pool_destroy(cp.pool);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* 命令行帮助
|
|
*/
|
|
static void print_usage(char *prog_name)
|
|
{
|
|
fprintf(stderr, "%s -S host:port [-c conn_cnt] [-n req_cnt] [-t thread_cnt] url\n"
|
|
" -S, --server server address\n"
|
|
" -c, --conn_cnt connection count\n"
|
|
" -n, --req_cnt request count\n"
|
|
" -L, --large_file large file\n"
|
|
" -k, --keep_alive keep_alive\n"
|
|
" -t, --io_thread_cnt thread count for listen, default: 1\n"
|
|
" -H, --header add custom header\n"
|
|
" -h, --help display this help and exit\n"
|
|
" -V, --version version and build time\n\n"
|
|
"eg: %s http://localhost/a.html\n\n", prog_name, prog_name);
|
|
}
|
|
|
|
/**
|
|
* 解析命令行
|
|
*/
|
|
static int parse_cmd_line(int argc, char *const argv[], cmdline_param *cp)
|
|
{
|
|
int opt;
|
|
const char *opt_string = "hVS:c:n:t:H:kL";
|
|
easy_string_pair_t *header;
|
|
char *url;
|
|
struct option long_opts[] = {
|
|
{"server", 1, NULL, 'S'},
|
|
{"header", 1, NULL, 'H'},
|
|
{"conn_cnt", 1, NULL, 'c'},
|
|
{"req_cnt", 1, NULL, 'n'},
|
|
{"io_thread_cnt", 1, NULL, 't'},
|
|
{"keep_alive", 0, NULL, 'k'},
|
|
{"large_file", 0, NULL, 'L'},
|
|
{"help", 0, NULL, 'h'},
|
|
{"version", 0, NULL, 'V'},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
opterr = 0;
|
|
|
|
while ((opt = getopt_long(argc, argv, opt_string, long_opts, NULL)) != -1) {
|
|
switch (opt) {
|
|
case 'S':
|
|
cp->address = easy_inet_str_to_addr(optarg, 0);
|
|
break;
|
|
|
|
case 't':
|
|
cp->io_thread_cnt = atoi(optarg);
|
|
break;
|
|
|
|
case 'c':
|
|
cp->connect_cnt = atoi(optarg);
|
|
break;
|
|
|
|
case 'n':
|
|
cp->request_cnt = atoi(optarg);
|
|
break;
|
|
|
|
case 'k':
|
|
cp->keep_alive = 1;
|
|
break;
|
|
|
|
case 'L':
|
|
cp->islarge = 1;
|
|
break;
|
|
|
|
case 'H':
|
|
header = (easy_string_pair_t *) easy_pool_calloc(cp->pool, sizeof(easy_string_pair_t));
|
|
header->next = cp->headers;
|
|
easy_buf_string_set(&header->name, optarg);
|
|
cp->headers = header;
|
|
break;
|
|
|
|
case 'V':
|
|
fprintf(stderr, "BUILD_TIME: %s %s\n", __DATE__, __TIME__);
|
|
return EASY_ERROR;
|
|
|
|
case 'h':
|
|
print_usage(argv[0]);
|
|
return EASY_ERROR;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (optind != argc - 1) {
|
|
print_usage(argv[0]);
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
if (cp->islarge) {
|
|
cp->connect_cnt = 1;
|
|
cp->request_cnt = 1;
|
|
}
|
|
|
|
if (strncmp(argv[optind], "http://", 7) != 0) {
|
|
fprintf(stderr, "request url invalidate: %s\n", argv[optind]);
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
if ((url = strchr(argv[optind] + 7, '/')) != NULL) {
|
|
strncpy(cp->url, url, 256);
|
|
cp->url[255] = '\0';
|
|
}
|
|
|
|
// 处理url请求
|
|
if (url) *url = '\0';
|
|
|
|
if (cp->address.family == 0) {
|
|
cp->address = easy_inet_str_to_addr(argv[optind] + 7, 80);
|
|
}
|
|
|
|
header = (easy_string_pair_t *) easy_pool_calloc(cp->pool, sizeof(easy_string_pair_t));
|
|
header->next = cp->headers;
|
|
easy_buf_string_printf(cp->pool, &header->name, "Host: %s", argv[optind] + 7);
|
|
cp->headers = header;
|
|
|
|
if (url) *url = '/';
|
|
|
|
return EASY_OK;
|
|
}
|
|
|
|
static int http_client_set_data(easy_request_t *r, const char *data, int len)
|
|
{
|
|
easy_ignore(write(1, data, len));
|
|
cp.file_len += len;
|
|
return EASY_OK;
|
|
}
|
|
|
|
/**
|
|
* 处理函数
|
|
*/
|
|
static int http_client_on_process(easy_request_t *r)
|
|
{
|
|
easy_http_request_t *reply;
|
|
|
|
reply = (easy_http_request_t *) r->ipacket;
|
|
|
|
easy_atomic_inc(&cp.process_cnt);
|
|
|
|
if (reply) {
|
|
if (reply->parser.status_code != 200) {
|
|
easy_atomic_inc(&cp.error_cnt);
|
|
}
|
|
} else {
|
|
easy_atomic_inc(&cp.timeout_cnt);
|
|
}
|
|
|
|
if (cp.send_cnt >= cp.request_cnt && cp.process_cnt == cp.send_cnt) {
|
|
easy_io_stop();
|
|
}
|
|
|
|
//if (cp.keep_alive == 0) r->ms->c->wait_close = 1;
|
|
|
|
easy_session_destroy(r->ms);
|
|
|
|
return (reply ? EASY_OK : EASY_ERROR);
|
|
}
|
|
|
|
/**
|
|
* 新建packet
|
|
*/
|
|
static int http_client_new_packet(easy_connection_t *c)
|
|
{
|
|
easy_session_t *s;
|
|
easy_http_packet_t *packet;
|
|
easy_buf_t *b;
|
|
|
|
//easy_error_log("http_client_new_packet: %d, (%.*s)", cp.send_cnt, cp.raw_header->last - cp.raw_header->pos, cp.raw_header->pos);
|
|
if (cp.send_cnt >= cp.request_cnt)
|
|
return EASY_OK;
|
|
|
|
if (cp.keep_alive == 0 && c->con_summary->doing_request_count + c->con_summary->done_request_count > 0)
|
|
return EASY_OK;
|
|
|
|
if ((packet = easy_session_packet_create(easy_http_packet_t, s, 0)) == NULL)
|
|
return EASY_ERROR;
|
|
|
|
b = easy_buf_pack(s->pool, cp.raw_header->pos + cp.pos, cp.raw_header->last - cp.raw_header->pos - cp.pos);
|
|
cp.pos = 4;
|
|
packet->is_raw_header = 1;
|
|
easy_buf_chain_offer(&packet->output, b);
|
|
|
|
easy_atomic_inc(&cp.send_cnt);
|
|
|
|
easy_session_set_request(s, packet, 5000, NULL);
|
|
return easy_connection_send_session(c, s);
|
|
}
|
|
|
|
static int http_client_disconnect(easy_connection_t *c)
|
|
{
|
|
easy_io_stop();
|
|
return EASY_OK;
|
|
}
|
|
|
|
static void print_speed()
|
|
{
|
|
double t = ev_time() - cp.start_time;
|
|
// speed
|
|
easy_error_log("process_cnt: %" PRIdFAST32 "\nsend_cnt: %" PRIdFAST32
|
|
", error_cnt: %" PRIdFAST32 ", timeout_cnt: %" PRIdFAST32 "\n",
|
|
cp.process_cnt, cp.send_cnt, cp.error_cnt, cp.timeout_cnt);
|
|
easy_error_log("QPS: %.2f\n", 1.0 * cp.process_cnt / t);
|
|
|
|
if (cp.islarge) easy_error_log("cp.file_len=%d", cp.file_len);
|
|
}
|