diff --git a/README.md b/README.md index 49318e4..fea9762 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ And then open the `docs/html/index.html`. - HTTPS support with TLS ## Future + - Support for virtual hosts - Custom web pages (404) - CLI args @@ -44,3 +45,19 @@ And then open the `docs/html/index.html`. - Logging - Compression (Gzip) - Reverse proxy + +## Performance + +This test was performed using `wrk`. + +```bash +Running 30s test @ http://localhost:3030 + 12 threads and 400 connections + Thread Stats Avg Stdev Max +/- Stdev + Latency 3.45ms 2.37ms 91.92ms 95.45% + Req/Sec 10.08k 1.33k 19.57k 82.30% + 3621422 requests in 32.47s, 2.55GB read + Socket errors: connect 0, read 0, write 0, timeout 395 +Requests/sec: 111514.25 +Transfer/sec: 80.51MB +``` diff --git a/src/http/http.c b/src/http/http.c index 4663a3e..b6b2d35 100644 --- a/src/http/http.c +++ b/src/http/http.c @@ -213,6 +213,7 @@ void cws_http_send_response(cws_http *request, cws_http_status status) { } int cws_http_send_resource(cws_http *request) { + /* keep-alive by default */ int keepalive = 1; FILE *file = fopen(mcl_string_cstr(request->location_path), "rb"); diff --git a/src/server/server.c b/src/server/server.c index c9f5bdb..ccb02a5 100644 --- a/src/server/server.c +++ b/src/server/server.c @@ -81,13 +81,39 @@ cws_server_ret cws_server_start(cws_config *config) { return CWS_SERVER_OK; } +static cws_server_ret cws_server_setup_epoll(int server_fd, int *epfd_out) { + int epfd = epoll_create1(0); + if (epfd < 0) { + return epfd; + } + + cws_server_ret ret; + ret = cws_fd_set_nonblocking(server_fd); + if (ret != CWS_SERVER_OK) { + return ret; + } + + cws_epoll_add(epfd, server_fd, EPOLLIN); + *epfd_out = epfd; + + return CWS_SERVER_OK; +} + cws_server_ret cws_server_loop(int server_fd, cws_config *config) { + int epfd = 0; + cws_server_ret ret; + + ret = cws_server_setup_epoll(server_fd, &epfd); + if (ret != CWS_SERVER_OK) { + return ret; + } + mcl_hashmap *clients = mcl_hm_init(my_int_hash_fn, my_int_equal_fn, my_int_free_key_fn, my_str_free_fn, sizeof(int), sizeof(char) * INET_ADDRSTRLEN); if (clients == NULL) { return CWS_SERVER_HASHMAP_INIT; } - size_t workers_num = 4; + size_t workers_num = 6; size_t workers_index = 0; cws_worker **workers = cws_worker_init(workers_num, clients, config); if (workers == NULL) { @@ -95,20 +121,38 @@ cws_server_ret cws_server_loop(int server_fd, cws_config *config) { return CWS_SERVER_WORKER_ERROR; } + struct epoll_event events[128]; + memset(events, 0, sizeof(events)); int client_fd = 0; + while (cws_server_run) { - client_fd = cws_server_handle_new_client(server_fd, clients); - if (client_fd < 0) { + int nfds = epoll_wait(epfd, events, 128, -1); + + if (nfds < 0) { continue; } - /* Add client to worker */ - int random = 10; - mcl_hm_set(clients, &client_fd, &random); - write(workers[workers_index]->pipefd[1], &client_fd, sizeof(int)); - workers_index = (workers_index + 1) % workers_num; + if (nfds == 0) { + continue; + } + + for (int i = 0; i < nfds; ++i) { + if (events[i].data.fd == server_fd) { + client_fd = cws_server_handle_new_client(server_fd, clients); + if (client_fd < 0) { + continue; + } + + /* Add client to worker */ + int random = 10; + mcl_hm_set(clients, &client_fd, &random); + write(workers[workers_index]->pipefd[1], &client_fd, sizeof(int)); + workers_index = (workers_index + 1) % workers_num; + } + } } + close(epfd); cws_worker_free(workers, workers_num); mcl_hm_free(clients); diff --git a/src/server/worker.c b/src/server/worker.c index 75f4c9b..b2f3749 100644 --- a/src/server/worker.c +++ b/src/server/worker.c @@ -55,6 +55,7 @@ cws_worker **cws_worker_init(size_t workers_num, mcl_hashmap *clients, cws_confi memset(workers[i], 0, sizeof(cws_worker)); workers[i]->config = config; + workers[i]->clients = clients; /* Communicate though threads */ pipe(workers[i]->pipefd); @@ -98,18 +99,16 @@ void *cws_worker_loop(void *arg) { continue; } - for (size_t i = 0; i < nfds; ++i) { + for (int i = 0; i < nfds; ++i) { if (events[i].data.fd == worker->pipefd[0]) { /* Handle new client */ int client_fd; read(worker->pipefd[0], &client_fd, sizeof(int)); - // CWS_LOG_DEBUG("Data from main, add client: %d", client_fd); cws_fd_set_nonblocking(client_fd); cws_epoll_add(worker->epfd, client_fd, EPOLLIN | EPOLLET); } else { /* Handle client data */ int client_fd = events[i].data.fd; - // CWS_LOG_DEBUG("Data from client (thread: %ld)", worker->thread); cws_server_handle_client_data(worker->epfd, client_fd, worker->clients, worker->config); } } @@ -169,6 +168,8 @@ static size_t cws_read_data(int sockfd, mcl_string **str) { if (bytes_read == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { break; + } else if (errno == ECONNRESET) { + return 0; } CWS_LOG_ERROR("recv(): %s", strerror(errno));