402 lines
12 KiB
C
402 lines
12 KiB
C
#include <getopt.h>
|
|
#include <easy_io.h>
|
|
#include <easy_time.h>
|
|
#include <easy_simple_handler.h>
|
|
#include <easy_http_handler.h>
|
|
|
|
// 命令行参数结构
|
|
typedef struct cmdline_param {
|
|
int port;
|
|
int io_thread_cnt;
|
|
easy_io_t *pio;
|
|
easy_io_t *cio;
|
|
easy_io_t *sio;
|
|
easy_io_handler_pt phandler;
|
|
easy_io_handler_pt chandler;
|
|
easy_io_handler_pt shandler;
|
|
easy_addr_t addr;
|
|
easy_atomic32_t chid;
|
|
} cmdline_param;
|
|
|
|
static 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 proxy_server_on_process(easy_request_t *r);
|
|
static int proxy_client_on_process(easy_request_t *r);
|
|
static int server_on_process(easy_request_t *r);
|
|
static int server_on_simple_encode(easy_request_t *r, void *data);
|
|
static void *client_on_simple_decode(easy_message_t *m);
|
|
|
|
|
|
/**
|
|
* 程序入口
|
|
*/
|
|
int main(int argc, char **argv)
|
|
{
|
|
// default
|
|
memset(&cp, 0, sizeof(cmdline_param));
|
|
cp.io_thread_cnt = 1;
|
|
cp.chid = 0x12345678;
|
|
|
|
// parse cmd line
|
|
if (parse_cmd_line(argc, argv, &cp) == EASY_ERROR)
|
|
return EASY_ERROR;
|
|
|
|
// 检查必需参数
|
|
if (cp.port == 0) {
|
|
print_usage(argv[0]);
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
// create
|
|
cp.pio = easy_eio_create(NULL, cp.io_thread_cnt);
|
|
cp.cio = easy_eio_create(NULL, cp.io_thread_cnt);
|
|
cp.sio = easy_eio_create(NULL, cp.io_thread_cnt);
|
|
|
|
if (cp.pio == NULL || cp.cio == NULL || cp.sio == NULL) {
|
|
easy_error_log("easy_io_init error.\n");
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
cp.phandler.decode = easy_http_server_on_decode;
|
|
cp.phandler.encode = easy_http_server_on_encode;
|
|
cp.phandler.process = proxy_server_on_process;
|
|
cp.chandler.decode = client_on_simple_decode;
|
|
cp.chandler.encode = easy_simple_encode;
|
|
cp.chandler.get_packet_id = easy_simple_packet_id;
|
|
cp.chandler.process = proxy_client_on_process;
|
|
cp.shandler.decode = easy_simple_decode;
|
|
cp.shandler.encode = server_on_simple_encode;
|
|
cp.shandler.get_packet_id = easy_simple_packet_id;
|
|
cp.shandler.process = server_on_process;
|
|
easy_listen_t *listen;
|
|
|
|
if (easy_connection_add_listen(cp.pio, "", cp.port, &cp.phandler) == NULL ||
|
|
(listen = easy_connection_add_listen(cp.sio, "", 0, &cp.shandler)) == NULL) {
|
|
easy_error_log("easy_io_add_listen error, port: %d, %s\n", cp.port, strerror(errno));
|
|
return EASY_ERROR;
|
|
} else {
|
|
easy_inet_myip(&cp.addr);
|
|
cp.addr.port = listen->addr.port;
|
|
easy_error_log("listen start, port = %d, backend port = %d\n", cp.port, cp.addr.port);
|
|
}
|
|
|
|
// start
|
|
if (easy_eio_start(cp.pio) || easy_eio_start(cp.cio) || easy_eio_start(cp.sio)) {
|
|
easy_error_log("easy_io_start error.\n");
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
// 等待线程退出
|
|
easy_eio_wait(cp.pio);
|
|
easy_eio_wait(cp.cio);
|
|
easy_eio_wait(cp.sio);
|
|
easy_eio_destroy(cp.pio);
|
|
easy_eio_destroy(cp.cio);
|
|
easy_eio_destroy(cp.sio);
|
|
|
|
return EASY_OK;
|
|
}
|
|
|
|
/**
|
|
* 命令行帮助
|
|
*/
|
|
static void print_usage(char *prog_name)
|
|
{
|
|
fprintf(stderr, "%s -p port\n"
|
|
" -p, --port server port\n"
|
|
" -t, --io_thread_cnt thread count for listen, default: 1\n"
|
|
" -h, --help display this help and exit\n"
|
|
" -V, --version version and build time\n\n"
|
|
"eg: %s -p 5000\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 = "hVp:t:";
|
|
struct option long_opts[] = {
|
|
{"port", 1, NULL, 'p'},
|
|
{"io_thread_cnt", 1, NULL, 't'},
|
|
{"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 'p':
|
|
cp->port = atoi(optarg);
|
|
break;
|
|
|
|
case 't':
|
|
cp->io_thread_cnt = atoi(optarg);
|
|
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;
|
|
}
|
|
}
|
|
|
|
return EASY_OK;
|
|
}
|
|
|
|
static int64_t tonumber(char *ptr)
|
|
{
|
|
int64_t datalen = 0;
|
|
char *end = ptr + strlen(ptr) - 1;
|
|
|
|
if (*end == 'm' || *end == 'M') {
|
|
datalen = atoi(ptr) * 1024 * 1024;
|
|
} else if (*end == 'k' || *end == 'k') {
|
|
datalen = atoi(ptr) * 1024;
|
|
} else {
|
|
datalen = atoi(ptr);
|
|
}
|
|
|
|
return datalen;
|
|
}
|
|
|
|
static int proxy_server_on_process(easy_request_t *r)
|
|
{
|
|
int64_t datalen = 16;
|
|
int64_t limit = 0;
|
|
easy_http_request_t *hr = (easy_http_request_t *)r->ipacket;
|
|
char *ptr = (char *)easy_http_get_args(hr, "datalen");
|
|
|
|
if (ptr) datalen = tonumber(ptr);
|
|
|
|
ptr = (char *)easy_http_get_args(hr, "limit");
|
|
|
|
if (ptr) limit = tonumber(ptr);
|
|
|
|
if (datalen <= 0) {
|
|
char header[128];
|
|
const char *error = "datalen is error. <= 0";
|
|
int hlen = lnprintf(header, 128, "HTTP/1.1 400 Bad Request\r\nContent-Length: %d\r\n\r\n%s", (int)strlen(error), error);
|
|
easy_connection_write_buffer(r->ms->c, header, hlen);
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
if (datalen >= (1LL << 32)) {
|
|
easy_connection_write_buffer(r->ms->c, "datalen is error. > 0x7fffffff", -1);
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
// send to backend
|
|
easy_session_t *s;
|
|
easy_simple_packet_t *packet;
|
|
|
|
if ((packet = easy_simple_new(&s, sizeof(datalen))) == NULL)
|
|
return EASY_ERROR;
|
|
|
|
easy_session_set_request(s, packet, 5000, NULL);
|
|
packet->data = &packet->buffer[0];
|
|
packet->len = sizeof(datalen);
|
|
packet->chid = easy_atomic32_add_return(&cp.chid, 1);
|
|
*((int64_t *)packet->data) = datalen;
|
|
|
|
// dispatch
|
|
easy_request_sleeping(r);
|
|
s->process = proxy_client_on_process;
|
|
easy_session_set_handler(s, &cp.chandler, r);
|
|
easy_session_set_request(s, packet, 5000, (void *)(long)limit);
|
|
|
|
if (easy_client_dispatch(cp.cio, cp.addr, s) == EASY_ERROR) {
|
|
easy_session_destroy(s);
|
|
easy_request_sleepless(r);
|
|
easy_error_log("easy_client_dispatch failure.");
|
|
easy_connection_write_buffer(r->ms->c, "easy_client_dispatch failure.", -1);
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
return EASY_AGAIN;
|
|
}
|
|
|
|
// 分多次返回
|
|
typedef struct test_server_request_t {
|
|
int datalen;
|
|
easy_buf_t *buf;
|
|
easy_simple_packet_t *opacket;
|
|
} test_server_request_t;
|
|
static int server_on_process(easy_request_t *r)
|
|
{
|
|
easy_simple_packet_t *packet;
|
|
easy_simple_packet_t *opacket;
|
|
int64_t datalen;
|
|
test_server_request_t *tsr = NULL;
|
|
int size;
|
|
|
|
packet = (easy_simple_packet_t *)r->ipacket;
|
|
tsr = (test_server_request_t *)r->user_data;
|
|
|
|
if (tsr == NULL) {
|
|
if (packet->len != sizeof(datalen)) {
|
|
easy_error_log("server_on_process len is error: %d", packet->len);
|
|
return EASY_ERROR;
|
|
}
|
|
|
|
tsr = (test_server_request_t *)easy_pool_calloc(r->ms->pool, sizeof(test_server_request_t));
|
|
r->user_data = tsr;
|
|
datalen = *((int64_t *)packet->data);
|
|
size = easy_min(datalen, 1024 * 1024);
|
|
opacket = easy_simple_rnew(r, size);
|
|
tsr->opacket = opacket;
|
|
tsr->buf = easy_buf_create(r->ms->pool, 0);
|
|
opacket->data = opacket->buffer - sizeof(int) * 2;
|
|
uint32_t *header = (uint32_t *)opacket->data;
|
|
header[0] = datalen;
|
|
header[1] = packet->chid;
|
|
easy_buf_set_data(r->ms->pool, tsr->buf, opacket->data, size + 8);
|
|
} else {
|
|
opacket = tsr->opacket;
|
|
datalen = tsr->datalen;
|
|
size = easy_min(datalen, 1024 * 1024);
|
|
opacket->data = opacket->buffer;
|
|
easy_buf_set_data(r->ms->pool, tsr->buf, opacket->data, size);
|
|
}
|
|
|
|
datalen -= size;
|
|
opacket->len = size;
|
|
tsr->datalen = datalen;
|
|
|
|
memset(opacket->buffer, 'A', size);
|
|
r->opacket = tsr;
|
|
|
|
if (datalen == 0) {
|
|
r->retcode = EASY_OK;
|
|
} else {
|
|
r->retcode = EASY_AGAIN;
|
|
}
|
|
|
|
return EASY_OK;
|
|
}
|
|
|
|
static int server_on_simple_encode(easy_request_t *r, void *data)
|
|
{
|
|
test_server_request_t *tsr = (test_server_request_t *)data;
|
|
easy_request_addbuf(r, tsr->buf);
|
|
return EASY_OK;
|
|
}
|
|
|
|
typedef struct test_client_response_t {
|
|
int datalen;
|
|
int chid;
|
|
easy_session_t *s;
|
|
easy_request_t *cr;
|
|
easy_connection_t *cc;
|
|
int64_t limit;
|
|
int64_t start_time;
|
|
int64_t diff;
|
|
} test_client_response_t;
|
|
|
|
static void *client_on_simple_decode(easy_message_t *m)
|
|
{
|
|
int len = 0;
|
|
|
|
if ((len = m->input->last - m->input->pos) <= 8)
|
|
return NULL;
|
|
|
|
test_client_response_t *tcr = (test_client_response_t *)m->user_data;
|
|
|
|
if (tcr == NULL) {
|
|
tcr = (test_client_response_t *)easy_pool_calloc(m->pool, sizeof(test_client_response_t));
|
|
m->user_data = tcr;
|
|
memcpy(tcr, m->input->pos, 8);
|
|
tcr->s = easy_hash_find(m->c->send_queue, tcr->chid);
|
|
|
|
if (tcr->s == NULL) {
|
|
easy_error_log("no found packet_id: %d\n", tcr->chid);
|
|
m->status = EASY_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
tcr->cr = (easy_request_t *) tcr->s->r.user_data;
|
|
tcr->limit = (long) tcr->s->r.args;
|
|
tcr->cc = tcr->cr->ms->c;
|
|
char header[128];
|
|
int hlen = lnprintf(header, 128, "HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n", tcr->datalen);
|
|
easy_connection_write_buffer(tcr->cc, header, hlen);
|
|
m->next_read_len = easy_min(tcr->datalen, 1024 * 1024);
|
|
|
|
if (tcr->limit > 1024) m->next_read_len = easy_min(m->next_read_len, tcr->limit / 10);
|
|
|
|
ev_timer_set(&tcr->s->timeout_watcher, 0.0, 1.0);
|
|
} else {
|
|
tcr->s = easy_hash_find(m->c->send_queue, tcr->chid);
|
|
|
|
if (tcr->s == NULL) {
|
|
easy_error_log("no found packet_id: %d\n", tcr->chid);
|
|
m->status = EASY_ERROR;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
// write data
|
|
len -= 8;
|
|
len = easy_min(tcr->datalen, len);
|
|
tcr->datalen -= len;
|
|
m->input->last = m->input->pos + 8;
|
|
ev_timer_again(m->c->loop, &tcr->s->timeout_watcher);
|
|
|
|
if (easy_connection_write_buffer(tcr->cc, m->input->last, len) == EASY_ERROR) {
|
|
m->status = EASY_ERROR;
|
|
return NULL;
|
|
}
|
|
|
|
if (tcr->datalen > 0) {
|
|
// QOS
|
|
if (tcr->limit > 0) {
|
|
int64_t end = easy_time_now();
|
|
|
|
if (tcr->start_time > 0) {
|
|
int64_t need = 1000000L * len / tcr->limit;
|
|
int64_t cost = end - tcr->start_time;
|
|
tcr->diff += (need - cost);
|
|
|
|
int ms = tcr->diff / 1000;
|
|
|
|
if (ms > 500) {
|
|
ms = 500;
|
|
m->next_read_len = easy_max(1024, m->next_read_len / 2);
|
|
}
|
|
|
|
if (tcr->diff > 1000) easy_connection_pause(m->c, ms);
|
|
}
|
|
|
|
tcr->start_time = end;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
m->input->pos = m->input->last;
|
|
return tcr;
|
|
}
|
|
|
|
static int proxy_client_on_process(easy_request_t *r)
|
|
{
|
|
easy_request_t *cr = (easy_request_t *) r->user_data;
|
|
|
|
if (r->ipacket == NULL) cr->retcode = EASY_ERROR;
|
|
|
|
easy_request_wakeup(cr);
|
|
easy_session_destroy(r->ms);
|
|
return EASY_OK;
|
|
}
|
|
|