|
#include <arpa/inet.h> |
|
#include <stdlib.h> |
|
#include <stdio.h> |
|
#include <string.h> |
|
#include <fcntl.h> |
|
#include <err.h> |
|
|
|
#include "../include/ev.h" |
|
#include "../include/http-parser/http_parser.h" |
|
|
|
#define HTML_RESPONSE_ECHO "The timeout(30s) had passed, you are welcome ~!\r\n" |
|
#define SERVER_PORT 9000 |
|
|
|
typedef struct { |
|
int fd; |
|
ev_timer timeout; |
|
ev_io ev_read; |
|
http_parser parser; |
|
} client_t; |
|
|
|
http_parser_settings settings; |
|
struct ev_loop *loop; |
|
ev_io ev_accept; |
|
|
|
static void free_res(struct ev_loop *loop, client_t *client); |
|
|
|
int setnonblock(int fd) { |
|
int flags = fcntl(fd, F_GETFL); |
|
if (flags < 0) |
|
return flags; |
|
|
|
flags |= O_NONBLOCK; |
|
if (fcntl(fd, F_SETFL, flags) < 0) |
|
return -1; |
|
|
|
return 0; |
|
} |
|
|
|
static void write_msg(client_t *client, char *msg) { |
|
if (client == NULL) { |
|
fprintf(stderr, "the client is NULL !\n"); |
|
return; |
|
} |
|
write(client->fd, msg, strlen(msg)); |
|
} |
|
|
|
static void timeout_cb(EV_P_ struct ev_timer *timer, int revents) { |
|
if (EV_ERROR & revents) { |
|
fprintf(stderr, "error event in timer_beat\n"); |
|
return ; |
|
} |
|
|
|
if (timer == NULL) { |
|
fprintf(stderr, "the timer is NULL now !\n"); |
|
return; |
|
} |
|
|
|
client_t *client = timer->data; |
|
|
|
if (client == NULL) { |
|
fprintf(stderr, "Timeout the client is NULL !\n"); |
|
return; |
|
} |
|
|
|
write_msg(client, HTML_RESPONSE_ECHO); |
|
free_res(loop, client); |
|
} |
|
|
|
static void read_cb(struct ev_loop *loop, ev_io *w, int revents) { |
|
client_t *client = w->data; |
|
int len = 0; |
|
char rbuff[1024]; |
|
if (revents & EV_READ) { |
|
len = read(client->fd, &rbuff, 1024); |
|
} |
|
|
|
if (EV_ERROR & revents) { |
|
fprintf(stderr, "error event in read\n"); |
|
free_res(loop, client); |
|
return ; |
|
} |
|
|
|
if (len < 0) { |
|
fprintf(stderr, "read error\n"); |
|
ev_io_stop(EV_A_ w); |
|
free_res(loop, client); |
|
return; |
|
} |
|
|
|
if (len == 0) { |
|
fprintf(stderr, "client disconnected.\n"); |
|
ev_io_stop(EV_A_ w); |
|
free_res(loop, client); |
|
return; |
|
} |
|
|
|
rbuff[len] = '\0'; |
|
http_parser_init(&client->parser, HTTP_REQUEST); |
|
|
|
size_t parsed = http_parser_execute(&client->parser, &settings, rbuff, len); |
|
if (parsed < len) { |
|
fprintf(stderr, "parse error\n"); |
|
ev_io_stop(EV_A_ w); |
|
free_res(loop, client); |
|
} |
|
} |
|
|
|
static void accept_cb(struct ev_loop *loop, ev_io *w, int revents) { |
|
struct sockaddr_in client_addr; |
|
socklen_t client_len = sizeof(client_addr); |
|
int client_fd = accept(w->fd, (struct sockaddr *) &client_addr, &client_len); |
|
if (client_fd == -1) { |
|
fprintf(stderr, "the client_fd is NULL !\n"); |
|
return; |
|
} |
|
|
|
client_t *client = malloc(sizeof(client_t)); |
|
client->fd = client_fd; |
|
if (setnonblock(client->fd) < 0) |
|
err(1, "failed to set client socket to non-blocking"); |
|
|
|
client->ev_read.data = client; |
|
client->timeout.data = NULL; |
|
client->parser.data = client; |
|
|
|
ev_io_init(&client->ev_read, read_cb, client->fd, EV_READ); |
|
ev_io_start(loop, &client->ev_read); |
|
} |
|
|
|
int on_url_cb(http_parser *parser, const char *at, size_t length) { |
|
client_t *client = parser->data; |
|
|
|
char heaer_str[200] = ""; |
|
strcat(heaer_str, "HTTP/1.1 200 OK\r\n"); |
|
strcat(heaer_str, "Content-Type: text/plain; charset=UTF-8\r\n"); |
|
strcat(heaer_str, "Connection: keep-alive\r\n"); |
|
strcat(heaer_str, "\r\n"); |
|
|
|
write_msg(client, heaer_str); |
|
|
|
client->timeout.data = client; |
|
ev_timer_init(&client->timeout, timeout_cb, 30.0, 0); //30s |
|
ev_timer_start(loop, &client->timeout); |
|
|
|
return 0; |
|
} |
|
|
|
int main(int argc, char const *argv[]) { |
|
loop = ev_default_loop(0); |
|
|
|
settings.on_url = on_url_cb; |
|
|
|
struct sockaddr_in listen_addr; |
|
int reuseaddr_on = 1; |
|
int listen_fd = socket(AF_INET, SOCK_STREAM, 0); |
|
if (listen_fd < 0) |
|
err(1, "listen failed"); |
|
if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr_on, sizeof(reuseaddr_on)) == -1) |
|
err(1, "setsockopt failed"); |
|
|
|
memset(&listen_addr, 0, sizeof(listen_addr)); |
|
listen_addr.sin_family = AF_INET; |
|
listen_addr.sin_addr.s_addr = INADDR_ANY; |
|
listen_addr.sin_port = htons(SERVER_PORT); |
|
|
|
if (bind(listen_fd, (struct sockaddr *) &listen_addr, sizeof(listen_addr)) < 0) |
|
err(1, "bind failed"); |
|
if (listen(listen_fd, 5) < 0) |
|
err(1, "listen failed"); |
|
if (setnonblock(listen_fd) < 0) |
|
err(1, "failed to set server socket to non-blocking"); |
|
|
|
ev_io_init(&ev_accept, accept_cb, listen_fd, EV_READ); |
|
ev_io_start(loop, &ev_accept); |
|
ev_loop(loop, 0); |
|
|
|
return 0; |
|
} |
|
|
|
static void free_res(struct ev_loop *loop, client_t *client) { |
|
if (client == NULL) { |
|
fprintf(stderr, "the client is NULL !!!!!!"); |
|
return; |
|
} |
|
|
|
ev_io_stop(loop, &client->ev_read); |
|
|
|
ev_timer *timer = &client->timeout; |
|
if (timer != NULL && (timer->data != NULL)) { |
|
ev_timer_stop(loop, timer); |
|
} |
|
|
|
close(client->fd); |
|
|
|
free(client); |
|
} |