From 3303c8cc7890d5b682020631099e58f3637a05a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 3 Jan 2026 01:16:12 -0300 Subject: [PATCH 1/3] samples: replacing the usage of printf by fprintf --- samples/http-server/main.c | 46 +++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index e80c4c6..fa5370c 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -16,7 +16,7 @@ #include "task.h" #define MAX_VERB_SIZE 7 -#define MAX_BUFFER_SIZE 4096 +#define MAX_BUFFER_SIZE 4096 * 50 #define DEFAULT_ROOT_DIR "./www" #define DEFAULT_PORT 8080 @@ -49,15 +49,16 @@ static void sig_handler(int signum) { static void on_client_connected(uint32_t tid, const void *param) { const client_t *cli = (client_t *)param; - printf("[+] task[%u]: client connected, ip=%s\n", tid, inet_ntoa(cli->addr)); + fprintf(stdout, "[+] task[%u]: client connected, ip=%s\n", tid, + inet_ntoa(cli->addr)); } static void on_client_disconnected(uint32_t tid, const void *param, void *ret_val) { const client_t *cli = (client_t *)param; - printf("[+] task[%u]: client disconnected ip=%s, result=%u\n", tid, - inet_ntoa(cli->addr), *((uint16_t *)ret_val)); + fprintf(stdout, "[+] task[%u]: client disconnected ip=%s, result=%u\n", tid, + inet_ntoa(cli->addr), *((uint16_t *)ret_val)); free(ret_val); } @@ -82,7 +83,8 @@ static char *get_resource(const char *res_name) { FILE *file = fopen(res_name, "r"); if (!file) { - printf("[-] fail to open the requested resource: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to open the requested resource: %s\n", + strerror(errno)); return NULL; } @@ -90,14 +92,14 @@ static char *get_resource(const char *res_name) { char *content = calloc(1, st.st_size + 1); if (!content) { - printf( + fprintf(stderr, "[-] no memory available to put the requested resource's content on\n"); fclose(file); return NULL; } if (!fread(content, 1, st.st_size, file)) { - printf("[-] fail to read the requested resource\n"); + fprintf(stderr, "[-] fail to read the requested resource\n"); free(content); fclose(file); return NULL; @@ -138,7 +140,7 @@ static void *handle_client(void *param) { if (received > 0) { parse_request(buffer, &req); - printf("[+] received request: %s %s\n", req.verb, req.resource); + fprintf(stdout, "[+] received request: %s %s\n", req.verb, req.resource); memset(buffer, 0, sizeof(buffer)); if (!strcmp(req.verb, "GET")) { @@ -188,7 +190,8 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { *sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (*sock_fd < 0) { - printf("[-] fail to create the server socket: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to create the server socket: %s\n", + strerror(errno)); return 1; } @@ -199,13 +202,15 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { addr.sin_port = htons(cfg->port); if (bind(*sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - printf("[-] fail to create the server socket: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to create the server socket: %s\n", + strerror(errno)); close(*sock_fd); return 1; } if (listen(*sock_fd, cfg->max_clients) < 0) { - printf("[-] fail to create the server socket: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to create the server socket: %s\n", + strerror(errno)); close(*sock_fd); return 1; } @@ -221,21 +226,22 @@ static int run_server(const server_cfg_t *cfg) { fd_set set; if (setup_socket(&server_fd, cfg) != 0) { - printf("[-] failed to setup server socket\n"); + fprintf(stderr, "[-] failed to setup server socket\n"); return 1; } if (!(pool = create_pool(cfg->max_clients))) { - printf("[-] fail to setup the server pool\n"); + fprintf(stderr, "[-] fail to setup the server pool\n"); close(server_fd); return 1; } - printf("[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", - cfg->max_clients, cfg->port, cfg->root_dir); + fprintf(stdout, + "[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", + cfg->max_clients, cfg->port, cfg->root_dir); if (chdir(cfg->root_dir)) { - printf("[-] fail to run the server: %s\n", strerror(errno)); + fprintf(stderr, "[-] fail to run the server: %s\n", strerror(errno)); return 1; } @@ -245,7 +251,7 @@ static int run_server(const server_cfg_t *cfg) { ret = select(server_fd + 1, &set, NULL, NULL, NULL); if ((ret == -1) && (errno == EINTR)) { - printf("[-] exiting server...\n"); + fprintf(stdout, "[+] exiting server...\n"); break; } @@ -253,8 +259,8 @@ static int run_server(const server_cfg_t *cfg) { int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); if (client_fd == -1) { - printf("[-] failed to accept the incoming client: %s\n", - strerror(errno)); + fprintf(stderr, "[-] failed to accept the incoming client: %s\n", + strerror(errno)); continue; } @@ -267,7 +273,7 @@ static int run_server(const server_cfg_t *cfg) { on_client_disconnected); if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { - printf("[-] failed to enqueue the request task\n"); + fprintf(stderr, "[-] failed to enqueue the request task\n"); destroy_task(task); close(client_fd); } From 67bdd8e26161df6e21dc6ff3e21a90f1efeeb9d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Sat, 3 Jan 2026 15:45:15 -0300 Subject: [PATCH 2/3] samples: funcionando essa porra --- samples/http-server/README.md | 2 +- samples/http-server/main.c | 245 ++++++++++++++++++++++++---------- src/pool_day.c | 7 +- 3 files changed, 182 insertions(+), 72 deletions(-) diff --git a/samples/http-server/README.md b/samples/http-server/README.md index ecee787..52fd953 100644 --- a/samples/http-server/README.md +++ b/samples/http-server/README.md @@ -1,6 +1,6 @@ ## http-server -This is an extremely simple static http server built with the library. For demonstration purposes, it only handles GET requests. Supported command line options: +This is an extremely simple static http server built with the library. Supported command line options: - `-m`: Maximum number of concurrent clients (default: 100) - `-p`: Port number to listen on (default: 8080) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index fa5370c..da9cb3d 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -15,8 +15,8 @@ #include "pool_day.h" #include "task.h" -#define MAX_VERB_SIZE 7 -#define MAX_BUFFER_SIZE 4096 * 50 +#define MAX_METHOD_SIZE 7 +#define MAX_BUFFER_SIZE 10 * 1024 #define DEFAULT_ROOT_DIR "./www" #define DEFAULT_PORT 8080 @@ -33,7 +33,7 @@ typedef struct { } server_cfg_t; typedef struct { - char verb[MAX_VERB_SIZE]; + char method[MAX_METHOD_SIZE]; char resource[PATH_MAX]; } request_t; @@ -42,6 +42,27 @@ typedef struct { struct in_addr addr; } client_t; +typedef enum { + SUCCESS, + NOT_FOUND, + INTERNAL_ERROR +} http_status_id_t; + +typedef struct { + http_status_id_t id; + int code; + char *msg; +} http_status_t; + +//static http_status_t status_table[] = { +// { SUCCESS, 200, "OK" }, +// { NOT_FOUND, 404, "Not Found" }, +// { INTERNAL_ERROR, 505, "Internal Error" } +//}; + +// TODO: lookup table for http errors +// TODO: 501 error? + static void sig_handler(int signum) { (void)signum; } @@ -56,32 +77,69 @@ static void on_client_connected(uint32_t tid, const void *param) { static void on_client_disconnected(uint32_t tid, const void *param, void *ret_val) { const client_t *cli = (client_t *)param; + int ret = ret_val ? *((uint16_t *)ret_val) : -1; fprintf(stdout, "[+] task[%u]: client disconnected ip=%s, result=%u\n", tid, - inet_ntoa(cli->addr), *((uint16_t *)ret_val)); - free(ret_val); + inet_ntoa(cli->addr), ret); + + if (ret_val) { + free(ret_val); + } } -static void parse_request(const char *buffer, - request_t *req) { +static void parse_request(const char *buffer, request_t *req) { // simple parsing logic for demonstration purposes - sscanf(buffer, "%s %s", req->verb, req->resource); + sscanf(buffer, "%s %s", req->method, req->resource); } -static void assemble_reply(char *buffer, size_t buffer_size, int status_code, - const char *status_str, const char *body) { - const char *reply_fmt = - "HTTP/1.1 %d %s\r\n" - "Content-Type: text/html\r\n" - "\r\n%s"; +static char *build_http_header(int status_code, const char *status_str, + size_t *header_len) { + const char *crlf = "\r\n"; + const char *version = "HTTP/1.1"; + const char *content_type = "Content-Type: text/html"; // TODO: and how about images? + const char *header_fmt = + "%s %d %s\r\n" + "%s\r\n" + "\r\n"; + + //const char *reply_fmt = + // "HTTP/1.1 %d %s\r\n" + // "Content-Type: text/html\r\n" + // "\r\n%s"; + + *header_len = strlen(version) + sizeof(int) + strlen(status_str) + 2 + + 3 * strlen(crlf) + strlen(content_type); + + char *header = calloc(1, *header_len); + if (!header) { + return NULL; + } - snprintf(buffer, buffer_size, reply_fmt, status_code, status_str, body); + snprintf(header, *header_len, header_fmt, version, status_code, status_str, + content_type); + + return header; +} + +static int assemble_reply(char **buffer, char *header, size_t header_len, + const char *body, size_t body_len) { + size_t buffer_len = header_len + body_len + 1; + + *buffer = calloc(1, buffer_len); + if (!(*buffer)) { + return 0; + } + + memcpy(*buffer, header, header_len); + memcpy(*buffer + header_len - 1, body, body_len); + + return buffer_len; } -static char *get_resource(const char *res_name) { +static char *get_resource(const char *res_name, size_t *resource_len) { struct stat st; - FILE *file = fopen(res_name, "r"); + FILE *file = fopen(res_name, "r"); // TODO: and how about the image files? if (!file) { fprintf(stderr, "[-] fail to open the requested resource: %s\n", strerror(errno)); @@ -92,13 +150,13 @@ static char *get_resource(const char *res_name) { char *content = calloc(1, st.st_size + 1); if (!content) { - fprintf(stderr, - "[-] no memory available to put the requested resource's content on\n"); + fprintf(stderr, "[-] fail to allocate memory for the requested resource\n"); fclose(file); return NULL; } - if (!fread(content, 1, st.st_size, file)) { + *resource_len = fread(content, 1, st.st_size, file); + if (!(*resource_len)) { fprintf(stderr, "[-] fail to read the requested resource\n"); free(content); fclose(file); @@ -110,52 +168,92 @@ static char *get_resource(const char *res_name) { return content; } -static int handle_get_request(char *reply_buffer, size_t buffer_size, +static int handle_get_request(char **reply_buffer, size_t *reply_buffer_size, const char *resource) { - char *res = get_resource(resource); int status_code; + size_t res_len; + char *header; + size_t header_len; + char *res = get_resource(resource, &res_len); if (res) { status_code = 200; - assemble_reply(reply_buffer, buffer_size, status_code, "OK", res); + header = build_http_header(status_code, "OK", &header_len); + *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, res, + res_len); free(res); } else { status_code = 404; - assemble_reply(reply_buffer, buffer_size, status_code, "Not Found", - MAKE_ERROR_BODY(404, Not Found)); + header = build_http_header(status_code, "Not Found", &header_len); + char *body = MAKE_ERROR_BODY(404, Not Found); + *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, + body, strlen(body)); } + free(header); + return status_code; } -static void *handle_client(void *param) { - char buffer[MAX_BUFFER_SIZE] = {0}; - client_t *cli = (client_t *)param; - int *ret = calloc(1, sizeof(int)); +static int handle_request(const char *req_buffer, char **reply_buffer, + size_t *reply_buffer_len) { request_t req; memset(&req, 0, sizeof(request_t)); - ssize_t received = recv(cli->fd, buffer, sizeof(buffer) - 1, 0); + parse_request(req_buffer, &req); + + fprintf(stdout, "[+] received request: %s %s\n", req.method, req.resource); + if (strcmp(req.method, "GET")) { + fprintf(stderr, "[-] unsupported request\n"); + // TODO: assemble 501 reply + return 501; + } + + return handle_get_request(reply_buffer, reply_buffer_len, &req.resource[1]); +} + +static void *handle_new_connection(void *param) { + client_t *cli = (client_t *)param; + int *ret = NULL; + char *req_buffer; + char *reply_buffer; + size_t reply_buffer_len; + + req_buffer = calloc(1, MAX_BUFFER_SIZE); + if (!req_buffer) { + fprintf(stderr, "[-] fail to allocate memory for request buffer\n"); + close(cli->fd); + return NULL; + } + + ssize_t received = recv(cli->fd, req_buffer, MAX_BUFFER_SIZE - 1, 0); if (received > 0) { - parse_request(buffer, &req); + ret = calloc(1, sizeof(int)); + if (ret) { + *ret = handle_request(req_buffer, &reply_buffer, &reply_buffer_len); - fprintf(stdout, "[+] received request: %s %s\n", req.verb, req.resource); + printf("reply_len = %lu\n", reply_buffer_len); + printf("reply = %s\n", reply_buffer); - memset(buffer, 0, sizeof(buffer)); - if (!strcmp(req.verb, "GET")) { - *ret = handle_get_request(buffer, MAX_BUFFER_SIZE, &req.resource[1]); + //for (size_t i = 0; i < reply_buffer_len; i++) { + // printf("%c\n", reply_buffer[i]); + //} - send(cli->fd, buffer, strlen(buffer), 0); + if (reply_buffer_len) { + send(cli->fd, reply_buffer, reply_buffer_len - 2, 0); // FIXME: tem coisa amais sendo copiado: GDB!!!!! + free(reply_buffer); + } } } close(cli->fd); + free(req_buffer); return (void *)ret; } -static void parse_args(int argc, char **argv, server_cfg_t *cfg) { +static void fill_cfg(int argc, char **argv, server_cfg_t *cfg) { int opt; while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { @@ -190,8 +288,6 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { *sock_fd = socket(AF_INET, SOCK_STREAM, 0); if (*sock_fd < 0) { - fprintf(stderr, "[-] fail to create the server socket: %s\n", - strerror(errno)); return 1; } @@ -202,15 +298,11 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { addr.sin_port = htons(cfg->port); if (bind(*sock_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) { - fprintf(stderr, "[-] fail to create the server socket: %s\n", - strerror(errno)); close(*sock_fd); return 1; } if (listen(*sock_fd, cfg->max_clients) < 0) { - fprintf(stderr, "[-] fail to create the server socket: %s\n", - strerror(errno)); close(*sock_fd); return 1; } @@ -218,32 +310,28 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { return 0; } -static int run_server(const server_cfg_t *cfg) { - int ret, server_fd; - pool_day_t pool; - struct sockaddr_in cli_addr; - socklen_t cli_len = sizeof(cli_addr); - fd_set set; - - if (setup_socket(&server_fd, cfg) != 0) { - fprintf(stderr, "[-] failed to setup server socket\n"); +static int setup_server(const server_cfg_t *cfg, int *server_fd, + pool_day_t *pool) { + if (setup_socket(server_fd, cfg) != 0) { + fprintf(stderr, "[-] fail to setup the server socket: %s\n", + strerror(errno)); return 1; } - if (!(pool = create_pool(cfg->max_clients))) { + if (!(*pool = create_pool(cfg->max_clients))) { fprintf(stderr, "[-] fail to setup the server pool\n"); - close(server_fd); + close(*server_fd); return 1; } - fprintf(stdout, - "[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", - cfg->max_clients, cfg->port, cfg->root_dir); + return 0; +} - if (chdir(cfg->root_dir)) { - fprintf(stderr, "[-] fail to run the server: %s\n", strerror(errno)); - return 1; - } +static int server_mainloop(int server_fd, pool_day_t pool) { + int ret; + fd_set set; + struct sockaddr_in cli_addr; + socklen_t cli_len = sizeof(cli_addr); while (1) { FD_ZERO(&set); @@ -259,12 +347,12 @@ static int run_server(const server_cfg_t *cfg) { int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); if (client_fd == -1) { - fprintf(stderr, "[-] failed to accept the incoming client: %s\n", + fprintf(stderr, "[-] fail to accept the incoming client: %s\n", strerror(errno)); continue; } - task_t task = create_async_task(client_fd, handle_client, + task_t task = create_async_task(client_fd, handle_new_connection, (void *)&((client_t) { .fd = (uint32_t)client_fd, .addr = cli_addr.sin_addr }), @@ -273,7 +361,7 @@ static int run_server(const server_cfg_t *cfg) { on_client_disconnected); if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { - fprintf(stderr, "[-] failed to enqueue the request task\n"); + fprintf(stderr, "[-] fail to enqueue the request task\n"); destroy_task(task); close(client_fd); } @@ -283,7 +371,28 @@ static int run_server(const server_cfg_t *cfg) { close(server_fd); destroy_pool(&pool); - return 0; + return EXIT_SUCCESS; +} + +static int run_server(const server_cfg_t *cfg) { + int server_fd; + pool_day_t pool; + + if (setup_server(cfg, &server_fd, &pool)) { + fprintf(stderr, "[-] fail to setup the server\n"); + return EXIT_FAILURE; + } + + if (chdir(cfg->root_dir)) { + fprintf(stderr, "[-] fail to run the server: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + + fprintf(stdout, + "[+] starting server with max_clients=%u, port=%u, root_dir=%s\n", + cfg->max_clients, cfg->port, cfg->root_dir); + + return server_mainloop(server_fd, pool); } int main(int argc, char **argv) { @@ -294,6 +403,8 @@ int main(int argc, char **argv) { signal(SIGINT, sig_handler); signal(SIGTERM, sig_handler); - parse_args(argc, argv, &cfg); - return run_server(&cfg); + fill_cfg(argc, argv, &cfg); + int ret = run_server(&cfg); + + exit(ret); } diff --git a/src/pool_day.c b/src/pool_day.c index ddb3028..b547246 100644 --- a/src/pool_day.c +++ b/src/pool_day.c @@ -81,9 +81,8 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t task) { return POOL_DAY_ERROR_NULL_PARAM; } - POOL_DAY_DEBUG("task ID: %u", task->id); - POOL_DAY_DEBUG("task callback: %p", task->task); - POOL_DAY_DEBUG("task parameter: %p", task->param); + POOL_DAY_DEBUG("task id=%u, callback=%p, parameter=%p", task->id, task->task, + task->param); task->ret_val = NULL; task->is_orphan = false; @@ -100,7 +99,7 @@ pool_day_retcode_t enqueue_task(pool_day_t pool, task_t task) { pool_day_t create_pool(uint32_t pool_size) { pool_day_t pool; - POOL_DAY_DEBUG("pool size: %u", pool_size); + POOL_DAY_DEBUG("pool_size=%u", pool_size); if (!pool_size) { POOL_DAY_ERROR("bad pool size"); From a2df3d51e57e693808c8ad643836d2561eea18fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20L=2E=20C=2E=20Moreira?= Date: Fri, 3 Apr 2026 19:31:44 -0300 Subject: [PATCH 3/3] samples: adding the content-type verification to the http-server sample --- samples/http-server/main.c | 288 ++++++++++++++++++++----------------- 1 file changed, 154 insertions(+), 134 deletions(-) diff --git a/samples/http-server/main.c b/samples/http-server/main.c index da9cb3d..1b29d0f 100644 --- a/samples/http-server/main.c +++ b/samples/http-server/main.c @@ -22,9 +22,16 @@ #define DEFAULT_PORT 8080 #define DEFAULT_MAX_CLIENTS 100 -#define STR(x) #x -#define MAKE_ERROR_BODY(status_code, msg) \ - "

" STR(status_code) " " #msg "

" +#define CONTENT_TYPE_TEXT_HTML "text/html" +#define CONTENT_TYPE_IMAGE_PNG "image/png" +#define CONTENT_TYPE_IMAGE_JPEG "image/jpeg" +#define CONTENT_TYPE_IMAGE_GIF "image/gif" +#define CONTENT_TYPE_TEXT_CSS "text/css" +#define CONTENT_TYPE_APP_JS "application/javascript" +#define CONTENT_TYPE_APP_OCTET "application/octet-stream" + +#define MAKE_ERROR_BODY(code, msg) \ + "

" #code " " #msg "

" typedef struct { uint32_t max_clients; @@ -42,27 +49,6 @@ typedef struct { struct in_addr addr; } client_t; -typedef enum { - SUCCESS, - NOT_FOUND, - INTERNAL_ERROR -} http_status_id_t; - -typedef struct { - http_status_id_t id; - int code; - char *msg; -} http_status_t; - -//static http_status_t status_table[] = { -// { SUCCESS, 200, "OK" }, -// { NOT_FOUND, 404, "Not Found" }, -// { INTERNAL_ERROR, 505, "Internal Error" } -//}; - -// TODO: lookup table for http errors -// TODO: 501 error? - static void sig_handler(int signum) { (void)signum; } @@ -87,168 +73,216 @@ static void on_client_disconnected(uint32_t tid, const void *param, } } +static const char *get_content_type(const char *path) { + const char *ext = strrchr(path, '.'); + + if (!ext) { + return CONTENT_TYPE_APP_OCTET; + } + + if (!strcmp(ext, ".html") || !strcmp(ext, ".htm")) { + return CONTENT_TYPE_TEXT_HTML; + } + + if (!strcmp(ext, ".png")) { + return CONTENT_TYPE_IMAGE_PNG; + } + + if (!strcmp(ext, ".jpg") || !strcmp(ext, ".jpeg")) { + return CONTENT_TYPE_IMAGE_JPEG; + } + + if (!strcmp(ext, ".gif")) { + return CONTENT_TYPE_IMAGE_GIF; + } + + if (!strcmp(ext, ".css")) { + return CONTENT_TYPE_TEXT_CSS; + } + + if (!strcmp(ext, ".js")) { + return CONTENT_TYPE_APP_JS; + } + + return CONTENT_TYPE_APP_OCTET; +} + static void parse_request(const char *buffer, request_t *req) { - // simple parsing logic for demonstration purposes sscanf(buffer, "%s %s", req->method, req->resource); } static char *build_http_header(int status_code, const char *status_str, + const char *content_type, size_t content_length, size_t *header_len) { - const char *crlf = "\r\n"; - const char *version = "HTTP/1.1"; - const char *content_type = "Content-Type: text/html"; // TODO: and how about images? - const char *header_fmt = - "%s %d %s\r\n" - "%s\r\n" - "\r\n"; - //const char *reply_fmt = - // "HTTP/1.1 %d %s\r\n" - // "Content-Type: text/html\r\n" - // "\r\n%s"; + const char *fmt = + "HTTP/1.1 %d %s\r\n" + "Content-Type: %s\r\n" + "Content-Length: %zu\r\n" + "Connection: close\r\n" + "\r\n"; - *header_len = strlen(version) + sizeof(int) + strlen(status_str) + 2 - + 3 * strlen(crlf) + strlen(content_type); + *header_len = snprintf(NULL, 0, fmt, status_code, status_str, content_type, + content_length); - char *header = calloc(1, *header_len); + char *header = malloc(*header_len + 1); if (!header) { return NULL; } - snprintf(header, *header_len, header_fmt, version, status_code, status_str, - content_type); + snprintf(header, *header_len + 1, fmt, status_code, status_str, content_type, + content_length); return header; } -static int assemble_reply(char **buffer, char *header, size_t header_len, +static int assemble_reply(char **buffer, const char *header, size_t header_len, const char *body, size_t body_len) { - size_t buffer_len = header_len + body_len + 1; + size_t total = header_len + body_len; - *buffer = calloc(1, buffer_len); + *buffer = malloc(total); if (!(*buffer)) { return 0; } memcpy(*buffer, header, header_len); - memcpy(*buffer + header_len - 1, body, body_len); + memcpy(*buffer + header_len, body, body_len); - return buffer_len; + return total; } -static char *get_resource(const char *res_name, size_t *resource_len) { +static char *get_resource(const char *path, size_t *len) { struct stat st; - FILE *file = fopen(res_name, "r"); // TODO: and how about the image files? + FILE *file = fopen(path, "rb"); if (!file) { - fprintf(stderr, "[-] fail to open the requested resource: %s\n", - strerror(errno)); return NULL; } - stat(res_name, &st); - - char *content = calloc(1, st.st_size + 1); - if (!content) { - fprintf(stderr, "[-] fail to allocate memory for the requested resource\n"); + if (stat(path, &st) < 0) { fclose(file); return NULL; } - *resource_len = fread(content, 1, st.st_size, file); - if (!(*resource_len)) { - fprintf(stderr, "[-] fail to read the requested resource\n"); - free(content); + char *data = malloc(st.st_size); + if (!data) { fclose(file); return NULL; } + size_t read_bytes = fread(data, 1, st.st_size, file); fclose(file); - return content; + if (read_bytes != (size_t)st.st_size) { + free(data); + return NULL; + } + + *len = read_bytes; + return data; } static int handle_get_request(char **reply_buffer, size_t *reply_buffer_size, const char *resource) { - int status_code; - size_t res_len; - char *header; size_t header_len; + + if (strstr(resource, "..")) { + return 400; + } + + size_t res_len; char *res = get_resource(resource, &res_len); if (res) { - status_code = 200; - header = build_http_header(status_code, "OK", &header_len); + const char *content_type = get_content_type(resource); + char *header = build_http_header(200, "OK", content_type, res_len, + &header_len); + *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, res, res_len); + + free(header); free(res); - } else { - status_code = 404; - header = build_http_header(status_code, "Not Found", &header_len); - char *body = MAKE_ERROR_BODY(404, Not Found); - *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, - body, strlen(body)); + + return 200; } + const char *body = MAKE_ERROR_BODY(404, Not Found); + size_t body_len = strlen(body); + char *header = build_http_header(404, "Not Found", CONTENT_TYPE_TEXT_HTML, + body_len, &header_len); + + *reply_buffer_size = assemble_reply(reply_buffer, header, header_len, body, + body_len); + free(header); - return status_code; + return 404; } -static int handle_request(const char *req_buffer, char **reply_buffer, +static int handle_request(const char *req_buffer, + char **reply_buffer, size_t *reply_buffer_len) { + request_t req; memset(&req, 0, sizeof(request_t)); parse_request(req_buffer, &req); - - fprintf(stdout, "[+] received request: %s %s\n", req.method, req.resource); - if (strcmp(req.method, "GET")) { - fprintf(stderr, "[-] unsupported request\n"); - // TODO: assemble 501 reply + if (strcmp(req.method, "GET") != 0) { return 501; } - return handle_get_request(reply_buffer, reply_buffer_len, &req.resource[1]); + fprintf(stdout, "[+] %s %s\n", req.method, req.resource); + + const char *resource = req.resource[0] == '/' ? + req.resource + 1 : + req.resource; + + return handle_get_request(reply_buffer, reply_buffer_len, resource); +} + +static void send_all(int fd, const char *buffer, size_t len) { + size_t total = 0; + + while (total < len) { + ssize_t sent = send(fd, buffer + total, len - total, 0); + + if (sent <= 0) { + break; + } + + total += sent; + } } static void *handle_new_connection(void *param) { client_t *cli = (client_t *)param; int *ret = NULL; - char *req_buffer; - char *reply_buffer; - size_t reply_buffer_len; - req_buffer = calloc(1, MAX_BUFFER_SIZE); + char *req_buffer = calloc(1, MAX_BUFFER_SIZE); if (!req_buffer) { - fprintf(stderr, "[-] fail to allocate memory for request buffer\n"); close(cli->fd); return NULL; } ssize_t received = recv(cli->fd, req_buffer, MAX_BUFFER_SIZE - 1, 0); - if (received > 0) { - ret = calloc(1, sizeof(int)); - if (ret) { - *ret = handle_request(req_buffer, &reply_buffer, &reply_buffer_len); - printf("reply_len = %lu\n", reply_buffer_len); - printf("reply = %s\n", reply_buffer); + if (received > 0) { + char *reply_buffer = NULL; + size_t reply_len = 0; - //for (size_t i = 0; i < reply_buffer_len; i++) { - // printf("%c\n", reply_buffer[i]); - //} + ret = calloc(1, sizeof(int)); + *ret = handle_request(req_buffer, &reply_buffer, &reply_len); - if (reply_buffer_len) { - send(cli->fd, reply_buffer, reply_buffer_len - 2, 0); // FIXME: tem coisa amais sendo copiado: GDB!!!!! - free(reply_buffer); - } + if (reply_buffer && reply_len > 0) { + send_all(cli->fd, reply_buffer, reply_len); + free(reply_buffer); } } - close(cli->fd); free(req_buffer); + close(cli->fd); return (void *)ret; } @@ -259,13 +293,13 @@ static void fill_cfg(int argc, char **argv, server_cfg_t *cfg) { while ((opt = getopt(argc, argv, "m:p:r:")) != -1) { switch (opt) { case 'm': - cfg->max_clients = (uint32_t)atoi(optarg); + cfg->max_clients = atoi(optarg); break; case 'p': - cfg->port = (uint16_t)atoi(optarg); + cfg->port = atoi(optarg); break; case 'r': - memcpy(cfg->root_dir, optarg, strlen(optarg) + 1); + strcpy(cfg->root_dir, optarg); break; } } @@ -278,8 +312,8 @@ static void fill_cfg(int argc, char **argv, server_cfg_t *cfg) { cfg->port = DEFAULT_PORT; } - if (cfg->root_dir[0] == '\0') { - memcpy(cfg->root_dir, DEFAULT_ROOT_DIR, strlen(DEFAULT_ROOT_DIR) + 1); + if (!cfg->root_dir[0]) { + strcpy(cfg->root_dir, DEFAULT_ROOT_DIR); } } @@ -310,17 +344,14 @@ static int setup_socket(int *sock_fd, const server_cfg_t *cfg) { return 0; } -static int setup_server(const server_cfg_t *cfg, int *server_fd, +static int setup_server(int *sock_fd, const server_cfg_t *cfg, pool_day_t *pool) { - if (setup_socket(server_fd, cfg) != 0) { - fprintf(stderr, "[-] fail to setup the server socket: %s\n", - strerror(errno)); + if (setup_socket(sock_fd, cfg) != 0) { return 1; } if (!(*pool = create_pool(cfg->max_clients))) { - fprintf(stderr, "[-] fail to setup the server pool\n"); - close(*server_fd); + close(*sock_fd); return 1; } @@ -328,27 +359,22 @@ static int setup_server(const server_cfg_t *cfg, int *server_fd, } static int server_mainloop(int server_fd, pool_day_t pool) { - int ret; fd_set set; - struct sockaddr_in cli_addr; - socklen_t cli_len = sizeof(cli_addr); while (1) { FD_ZERO(&set); FD_SET(server_fd, &set); - ret = select(server_fd + 1, &set, NULL, NULL, NULL); - if ((ret == -1) && (errno == EINTR)) { - fprintf(stdout, "[+] exiting server...\n"); + if (select(server_fd + 1, &set, NULL, NULL, NULL) < 0) { break; } if (FD_ISSET(server_fd, &set)) { - int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &cli_len); + struct sockaddr_in cli_addr; + socklen_t len = sizeof(cli_addr); - if (client_fd == -1) { - fprintf(stderr, "[-] fail to accept the incoming client: %s\n", - strerror(errno)); + int client_fd = accept(server_fd, (struct sockaddr *)&cli_addr, &len); + if (client_fd < 0) { continue; } @@ -360,32 +386,29 @@ static int server_mainloop(int server_fd, pool_day_t pool) { on_client_connected, on_client_disconnected); - if (enqueue_task(pool, task) != POOL_DAY_SUCCESS) { - fprintf(stderr, "[-] fail to enqueue the request task\n"); - destroy_task(task); - close(client_fd); - } + enqueue_task(pool, task); } } close(server_fd); destroy_pool(&pool); - return EXIT_SUCCESS; + return 0; } static int run_server(const server_cfg_t *cfg) { int server_fd; pool_day_t pool; - if (setup_server(cfg, &server_fd, &pool)) { + if (setup_server(&server_fd, cfg, &pool)) { fprintf(stderr, "[-] fail to setup the server\n"); - return EXIT_FAILURE; + return 1; } if (chdir(cfg->root_dir)) { - fprintf(stderr, "[-] fail to run the server: %s\n", strerror(errno)); - return EXIT_FAILURE; + fprintf(stderr, "[-] fail to run the server on '%s': %s\n", cfg->root_dir, + strerror(errno)); + return 1; } fprintf(stdout, @@ -398,13 +421,10 @@ static int run_server(const server_cfg_t *cfg) { int main(int argc, char **argv) { server_cfg_t cfg; - memset(&cfg, 0, sizeof(server_cfg_t)); - signal(SIGINT, sig_handler); - signal(SIGTERM, sig_handler); + memset(&cfg, 0, sizeof(server_cfg_t)); fill_cfg(argc, argv, &cfg); - int ret = run_server(&cfg); - exit(ret); + return run_server(&cfg); }