基于libevent的http服务器实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170

//libevent的http服务器简单实现方式

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//for struct evkeyvalq
#include <sys/queue.h>
#include <event.h>
#include <event2/http.h>
#include <event2/http_struct.h>
#include <event2/http_compat.h>
#include <event2/util.h>
#include <signal.h>

#define LOG_INFO printf
#define LOG_DBG printf
#define LOG_ERR printf

#define MYHTTPD_SIGNATURE "httpd v 0.0.1"

//处理模块
void httpd_handler(struct evhttp_request *req, void *arg) {
char output[2048] = "\0";
char tmp[1024];

//获取客户端请求的URI(使用evhttp_request_uri或直接req->uri)
const char *uri;
uri = evhttp_request_uri(req);
sprintf(tmp, "uri=%s\n", uri);
strcat(output, tmp);

sprintf(tmp, "uri=%s\n", req->uri);
strcat(output, tmp);
//decoded uri
char *decoded_uri;
decoded_uri = evhttp_decode_uri(uri);
sprintf(tmp, "decoded_uri=%s\n", decoded_uri);
strcat(output, tmp);

//解析URI的参数(即GET方法的参数)
struct evkeyvalq params;
//将URL数据封装成key-value格式,q=value1, s=value2
evhttp_parse_query(decoded_uri, &params);
//得到q所对应的value
sprintf(tmp, "q=%s\n", evhttp_find_header(&params, "q"));
strcat(output, tmp);
//得到s所对应的value
sprintf(tmp, "s=%s\n", evhttp_find_header(&params, "s"));
strcat(output, tmp);

free(decoded_uri);

//获取POST方法的数据
char *post_data = (char *) EVBUFFER_DATA(req->input_buffer);
sprintf(tmp, "post_data=%s\n", post_data);
strcat(output, tmp);

/* 输出到客户端 */

//HTTP header
evhttp_add_header(req->output_headers, "Server", MYHTTPD_SIGNATURE);
evhttp_add_header(req->output_headers, "Content-Type", "text/plain; charset=UTF-8");
evhttp_add_header(req->output_headers, "Connection", "close");
//输出的内容
struct evbuffer *buf;
buf = evbuffer_new();
evbuffer_add_printf(buf, "It works!\n%s\n", output);
evhttp_send_reply(req, HTTP_OK, "OK", buf);
evbuffer_free(buf);

}
void show_help() {
char *help = "http://localhost:8080\n"
"-l <ip_addr> interface to listen on, default is 0.0.0.0\n"
"-p <num> port number to listen on, default is 1984\n"
"-d run as a deamon\n"
"-t <second> timeout for a http request, default is 120 seconds\n"
"-h print this help and exit\n"
"\n";
fprintf(stderr,"%s",help);
}
//当向进程发出SIGTERM/SIGHUP/SIGINT/SIGQUIT的时候,终止event的事件侦听循环
void signal_handler(int sig) {
switch (sig) {
case SIGTERM:
case SIGHUP:
case SIGQUIT:
case SIGINT:
event_loopbreak(); //终止侦听event_dispatch()的事件侦听循环,执行之后的代码
break;
}
}

int main(int argc, char *argv[]) {

//自定义信号处理函数
signal(SIGHUP , signal_handler);
signal(SIGTERM , signal_handler);
signal(SIGINT , signal_handler);
signal(SIGQUIT , signal_handler);

//默认参数
char *httpd_option_listen = "0.0.0.0";
int httpd_option_port = 8080;
int httpd_option_daemon = 0;
int httpd_option_timeout = 120; //in seconds

LOG_INFO("http server start %s:%d\n", httpd_option_listen, httpd_option_port);

//获取参数
int c;
while ((c = getopt(argc, argv, "l:p:dt:h")) != -1) {
switch (c) {
case 'l' :
httpd_option_listen = optarg;
break;
case 'p' :
httpd_option_port = atoi(optarg);
break;
case 'd' :
httpd_option_daemon = 1;
break;
case 't' :
httpd_option_timeout = atoi(optarg);
break;
case 'h' :
default :
show_help();
exit(EXIT_SUCCESS);
}
}

//判断是否设置了-d,以daemon运行
if (httpd_option_daemon) {
pid_t pid;
pid = fork();
if (pid < 0) {
perror("fork failed");
exit(EXIT_FAILURE);
}
if (pid > 0) {
//生成子进程成功,退出父进程
exit(EXIT_SUCCESS);
}
}

/* 使用libevent创建HTTP Server */

//初始化event API
event_init();

//创建一个http server
struct evhttp *httpd;
httpd = evhttp_start(httpd_option_listen, httpd_option_port);
evhttp_set_timeout(httpd, httpd_option_timeout);

//指定generic callback
evhttp_set_gencb(httpd, httpd_handler, NULL);
//也可以为特定的URI指定callback
//evhttp_set_cb(httpd, "/", specific_handler, NULL);

//开始事件监听,分发
event_dispatch();

//释放资源
evhttp_free(httpd);
return 0;
}