diff --git a/src/client.c b/src/client.c new file mode 100644 index 0000000..9a53ccd --- /dev/null +++ b/src/client.c @@ -0,0 +1,139 @@ +#include "consts.h" +#include "state.h" +#include "strings.h" +#include "client.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct client *clients; + +int accept_connection(struct app_state *app) { + struct sockaddr_in addr; + socklen_t addrlen = sizeof(struct sockaddr_in); + + struct epoll_event ev; + + int sock = accept(app->sock, (struct sockaddr*)&addr, &addrlen); + if (sock < 0) { + perror("Failed to accept connection"); + return -1; + } + + // add client to epoll instance + ev.events = EPOLLIN; + ev.data.fd = sock; + pthread_mutex_lock(&app->epoll_lock); + if (epoll_ctl(app->epoll, EPOLL_CTL_ADD, sock, &ev) == -1) { + perror("Failed to add client to epoll"); + pthread_mutex_unlock(&app->epoll_lock); + return -1; + } + pthread_mutex_unlock(&app->epoll_lock); + + struct client *client = &clients[sock % MAX_CLIENTS]; + + pthread_mutex_lock(&client->lock); + + memset(client, 0, sizeof(struct client)); + client->active = 1; + client->sock = sock; + + client->req_buffer = malloc(sizeof(char) * BUFFER_SIZE); + client->req_buffer_len = 0; + client->req_buffer_sz = BUFFER_SIZE; + + client->res_buffer = malloc(sizeof(char) * BUFFER_SIZE); + client->res_buffer_len = 0; + client->res_buffer_sz = BUFFER_SIZE; + + char *addr_str = inet_ntoa(addr.sin_addr); + size_t addr_strlen = snprintf(NULL, 0, "%s:%d", addr_str, addr.sin_port) + 1; + client->addr_str = malloc(addr_strlen * sizeof(char)); + snprintf(client->addr_str, addr_strlen, "%s:%d", addr_str, addr.sin_port); + + pthread_mutex_unlock(&client->lock); + + // printf("Connected: %s\n", client->addr_str); + + return 0; +} + +void close_client(struct app_state *app, struct client *client, int read) { + if (read == -1) { + fprintf(stderr, "Failed to read [%s]: %s\n", + client->addr_str, strerror(errno)); + } + + if (client->req_buffer_len > 300) + printf("%s\n", client->req_buffer); + + // printf("Disonnected: %s\n", client->addr_str); + + pthread_mutex_lock(&app->epoll_lock); + if (epoll_ctl(app->epoll, EPOLL_CTL_DEL, client->sock, NULL) == -1) + fprintf(stderr, "Failed to remove [%s] from epoll: %s\n", + client->addr_str, strerror(errno)); + pthread_mutex_unlock(&app->epoll_lock); + + if (shutdown(client->sock, SHUT_RDWR) == -1) + fprintf(stderr, "Failed to shutdown socket [%s]: %s\n", + client->addr_str, strerror(errno)); + + if (close(client->sock) == -1) + fprintf(stderr, "Failed to close socket [%s]: %s\n", + client->addr_str, strerror(errno)); + + client->active = 0; +} + +void handle_client(struct app_state *app, struct client *client) { + pthread_mutex_lock(&client->lock); + if (!client->active) { + pthread_mutex_unlock(&client->lock); + return; + } + + char buffer[BUFFER_SIZE + 1]; // ensure space for \0 + memset(buffer, 0, BUFFER_SIZE); + + int read = recv(client->sock, buffer, BUFFER_SIZE, 0); + if (read > 0 && client->req_buffer_len + read < MAX_BUFFER_SIZE) { + // increase buffer size if running out of space + if (client->req_buffer_len + read > client->req_buffer_sz) { + char *new_buffer = malloc(client->req_buffer_sz + BUFFER_SIZE); + memset(new_buffer, 0, client->req_buffer_sz + BUFFER_SIZE); + strncpy(new_buffer, client->req_buffer, client->req_buffer_sz); + client->req_buffer_sz += BUFFER_SIZE; + client->req_buffer = new_buffer; + } + + // copy buffer to client state + strncpy(client->req_buffer + client->req_buffer_len, buffer, read); + client->req_buffer_len += read; + + // printf("Read %d bytes from %s (%ld total)\n", + // read, client->addr_str, client->req_buffer_len); + + if (str_has_suffix(client->req_buffer, "\r\n\r\n")) { + if (send(client->sock, HTTP_RESPONSE, strlen(HTTP_RESPONSE), 0) == -1) + fprintf(stderr, "Failed to send [%s]: %s\n", + client->addr_str, strerror(errno)); + close_client(app, client, 0); + } + } else { + close_client(app, client, read); + } + + pthread_mutex_unlock(&client->lock); + return; +} diff --git a/src/client.h b/src/client.h new file mode 100644 index 0000000..574228c --- /dev/null +++ b/src/client.h @@ -0,0 +1,23 @@ +#ifndef _CLIENTS_H +#define _CLIENTS_H + +#include "state.h" +#include + +struct client { + char active; + int sock; + char *addr_str; + pthread_mutex_t lock; + char *req_buffer; + size_t req_buffer_sz; + size_t req_buffer_len; + char *res_buffer; + size_t res_buffer_sz; + size_t res_buffer_len; +}; + +int accept_connection(struct app_state *app); +void handle_client(struct app_state *app, struct client *client); + +#endif // _CLIENTS_H diff --git a/src/consts.h b/src/consts.h new file mode 100644 index 0000000..6fef2c3 --- /dev/null +++ b/src/consts.h @@ -0,0 +1,15 @@ +#ifndef _CONSTS_H +#define _CONSTS_H + +#define MAX_CLIENTS 1000 +#define BUFFER_SIZE 1000 +#define MAX_BUFFER_SIZE 1000000 + +#define HTTP_RESPONSE \ + "HTTP/1.1 200 OK\r\n" \ + "Content-Type: text/plain; charset=utf-8\r\n" \ + "Content-Length: 14\r\n" \ + "\r\n" \ + "bunny -> 🐇\n" \ + +#endif // _CONSTS_H diff --git a/src/main.c b/src/main.c index f74a773..5a8539e 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,7 @@ +#include "client.h" +#include "consts.h" +#include "state.h" #include -#include #include #include #include @@ -11,174 +13,11 @@ #include #include -#define MAX_CLIENTS 1000 -#define BUFFER_SIZE 1000 -#define MAX_BUFFER_SIZE 1000000 #define WORKER_THREADS 8 #define DEFAULT_HOST "0.0.0.0" -#define HTTP_RESPONSE \ - "HTTP/1.1 200 OK\r\n" \ - "Content-Type: text/plain; charset=utf-8\r\n" \ - "Content-Length: 14\r\n" \ - "\r\n" \ - "bunny -> 🐇\n" \ - -struct app_state { - struct sockaddr_in addr; - int sock; - int epoll; - pthread_mutex_t epoll_lock; - char *page_buffer; -}; - -struct client { - char active; - int sock; - char *addr_str; - pthread_mutex_t lock; - char *req_buffer; - size_t req_buffer_sz; - size_t req_buffer_len; - char *res_buffer; - size_t res_buffer_sz; - size_t res_buffer_len; -}; struct client clients[MAX_CLIENTS]; -int accept_connection(struct app_state *app) { - struct sockaddr_in addr; - socklen_t addrlen = sizeof(struct sockaddr_in); - - struct epoll_event ev; - - int sock = accept(app->sock, (struct sockaddr*)&addr, &addrlen); - if (sock < 0) { - perror("Failed to accept connection"); - return -1; - } - - // add client to epoll instance - ev.events = EPOLLIN; - ev.data.fd = sock; - pthread_mutex_lock(&app->epoll_lock); - if (epoll_ctl(app->epoll, EPOLL_CTL_ADD, sock, &ev) == -1) { - perror("Failed to add client to epoll"); - pthread_mutex_unlock(&app->epoll_lock); - return -1; - } - pthread_mutex_unlock(&app->epoll_lock); - - struct client *client = &clients[sock % MAX_CLIENTS]; - - pthread_mutex_lock(&client->lock); - - memset(client, 0, sizeof(struct client)); - client->active = 1; - client->sock = sock; - - client->req_buffer = malloc(sizeof(char) * BUFFER_SIZE); - client->req_buffer_len = 0; - client->req_buffer_sz = BUFFER_SIZE; - - client->res_buffer = malloc(sizeof(char) * BUFFER_SIZE); - client->res_buffer_len = 0; - client->res_buffer_sz = BUFFER_SIZE; - - char *addr_str = inet_ntoa(addr.sin_addr); - size_t addr_strlen = snprintf(NULL, 0, "%s:%d", addr_str, addr.sin_port) + 1; - client->addr_str = malloc(addr_strlen * sizeof(char)); - snprintf(client->addr_str, addr_strlen, "%s:%d", addr_str, addr.sin_port); - - pthread_mutex_unlock(&client->lock); - - // printf("Connected: %s\n", client->addr_str); - - return 0; -} - -void close_client(struct app_state *app, struct client *client, int read) { - if (read == -1) { - fprintf(stderr, "Failed to read [%s]: %s\n", - client->addr_str, strerror(errno)); - } - - if (client->req_buffer_len > 300) - printf("%s\n", client->req_buffer); - - // printf("Disonnected: %s\n", client->addr_str); - - pthread_mutex_lock(&app->epoll_lock); - if (epoll_ctl(app->epoll, EPOLL_CTL_DEL, client->sock, NULL) == -1) - fprintf(stderr, "Failed to remove [%s] from epoll: %s\n", - client->addr_str, strerror(errno)); - pthread_mutex_unlock(&app->epoll_lock); - - if (shutdown(client->sock, SHUT_RDWR) == -1) - fprintf(stderr, "Failed to shutdown socket [%s]: %s\n", - client->addr_str, strerror(errno)); - - if (close(client->sock) == -1) - fprintf(stderr, "Failed to close socket [%s]: %s\n", - client->addr_str, strerror(errno)); - - client->active = 0; -} - -int str_has_suffix(char *str, const char *suffix) { - if (str == NULL || suffix == NULL) - return 0; - - size_t lenstr = strlen(str); - size_t lensuffix = strlen(suffix); - if (lensuffix > lenstr) - return 0; - - return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; -} - -void handle_client(struct app_state *app, struct client *client) { - pthread_mutex_lock(&client->lock); - if (!client->active) { - pthread_mutex_unlock(&client->lock); - return; - } - - char buffer[BUFFER_SIZE + 1]; // ensure space for \0 - memset(buffer, 0, BUFFER_SIZE); - - int read = recv(client->sock, buffer, BUFFER_SIZE, 0); - if (read > 0 && client->req_buffer_len + read < MAX_BUFFER_SIZE) { - // increase buffer size if running out of space - if (client->req_buffer_len + read > client->req_buffer_sz) { - char *new_buffer = malloc(client->req_buffer_sz + BUFFER_SIZE); - memset(new_buffer, 0, client->req_buffer_sz + BUFFER_SIZE); - strncpy(new_buffer, client->req_buffer, client->req_buffer_sz); - client->req_buffer_sz += BUFFER_SIZE; - client->req_buffer = new_buffer; - } - - // copy buffer to client state - strncpy(client->req_buffer + client->req_buffer_len, buffer, read); - client->req_buffer_len += read; - - // printf("Read %d bytes from %s (%ld total)\n", - // read, client->addr_str, client->req_buffer_len); - - if (str_has_suffix(client->req_buffer, "\r\n\r\n")) { - if (send(client->sock, HTTP_RESPONSE, strlen(HTTP_RESPONSE), 0) == -1) - fprintf(stderr, "Failed to send [%s]: %s\n", - client->addr_str, strerror(errno)); - close_client(app, client, 0); - } - } else { - close_client(app, client, read); - } - - pthread_mutex_unlock(&client->lock); - return; -} - int main(int argc, char *argv[]) { struct app_state app; memset(&app, 0, sizeof(struct app_state)); diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..2e17d36 --- /dev/null +++ b/src/state.h @@ -0,0 +1,15 @@ +#ifndef _STATE_H +#define _STATE_H + +#include +#include + +struct app_state { + struct sockaddr_in addr; + int sock; + int epoll; + pthread_mutex_t epoll_lock; + char *page_buffer; +}; + +#endif // _STATE_H diff --git a/src/strings.c b/src/strings.c new file mode 100644 index 0000000..9a18322 --- /dev/null +++ b/src/strings.c @@ -0,0 +1,15 @@ +#include "strings.h" +#include +#include + +int str_has_suffix(char *str, const char *suffix) { + if (str == NULL || suffix == NULL) + return 0; + + size_t lenstr = strlen(str); + size_t lensuffix = strlen(suffix); + if (lensuffix > lenstr) + return 0; + + return strncmp(str + lenstr - lensuffix, suffix, lensuffix) == 0; +} diff --git a/src/strings.h b/src/strings.h new file mode 100644 index 0000000..44ef727 --- /dev/null +++ b/src/strings.h @@ -0,0 +1,6 @@ +#ifndef _STRINGS_H +#define _STRINGS_H + +int str_has_suffix(char *str, const char *suffix); + +#endif // _STRINGS_H