From cf8ddf14bf197af5ccc06d616dad165cc9533552 Mon Sep 17 00:00:00 2001 From: Alexander Rosenberg Date: Tue, 26 May 2026 00:54:34 -0700 Subject: [PATCH] Update stuff --- http.c | 80 +++++++++++++++++++++----------------- main.c | 121 +++++++++++++++++++++++++++++++++++++-------------------- 2 files changed, 124 insertions(+), 77 deletions(-) diff --git a/http.c b/http.c index f8c7efd..d36acd6 100644 --- a/http.c +++ b/http.c @@ -5,10 +5,11 @@ #include #include -static const char *EMPTY_STRING = ""; +static const char* EMPTY_STRING = ""; -HTTPHeaderList *http_header_list_push(HTTPHeaderList *list, const char *key, const char *value) { - HTTPHeaderList *new = malloc(sizeof(HTTPHeaderList)); +HTTPHeaderList* http_header_list_push(HTTPHeaderList* list, const char* key, const char* value) +{ + HTTPHeaderList* new = malloc(sizeof(HTTPHeaderList)); if (!new) { return NULL; } @@ -31,9 +32,10 @@ HTTPHeaderList *http_header_list_push(HTTPHeaderList *list, const char *key, con return new; } -void free_http_header_list(HTTPHeaderList *list) { +void free_http_header_list(HTTPHeaderList* list) +{ while (list) { - HTTPHeaderList *next = list->next; + HTTPHeaderList* next = list->next; free(list->key); free(list->value); free(list); @@ -41,7 +43,8 @@ void free_http_header_list(HTTPHeaderList *list) { } } -const char *http_header_list_search(HTTPHeaderList *list, const char *key, const char *def) { +const char* http_header_list_search(HTTPHeaderList* list, const char* key, const char* def) +{ while (list) { if (strcmp(list->key, key) == 0) { return list->value; @@ -51,21 +54,22 @@ const char *http_header_list_search(HTTPHeaderList *list, const char *key, const return def; } -#define MAX_REQUEST_LENGTH 16384 -#define MAX_METHOD_LENGTH 16 -#define MAX_URI_LENGTH 256 -#define MAX_VERSION_LENGTH 3 -#define MAX_HEADER_KEY_LENGTH 2048 +#define MAX_REQUEST_LENGTH 16384 +#define MAX_METHOD_LENGTH 16 +#define MAX_URI_LENGTH 256 +#define MAX_VERSION_LENGTH 3 +#define MAX_HEADER_KEY_LENGTH 2048 #define MAX_HEADER_VALUE_LENGTH 2048 -#define RETURN_IF_READ_ERROR(s) \ - if (ferror((s))) { \ - return HRPR_READ_FAILED; \ +#define RETURN_IF_READ_ERROR(s) \ + if (ferror((s))) { \ + return HRPR_READ_FAILED; \ } // if an error occured, req->uri is not allocated static HTTPRequestParseResult parse_method_uri_line( - FILE *stream, size_t *restrict bytes_read, HTTPRequest *restrict req) { + FILE* stream, size_t* restrict bytes_read, HTTPRequest* restrict req) +{ // allow for some leeway in passing incorrect methods char method[MAX_METHOD_LENGTH + 1]; char uri[MAX_URI_LENGTH + 1]; @@ -73,7 +77,7 @@ static HTTPRequestParseResult parse_method_uri_line( char whitespace[4]; ssize_t signed_bytes_read; #define S1(s) #s -#define S(s) S1(s) +#define S(s) S1(s) int nconv = fscanf(stream, // clang-format off "%" S(MAX_METHOD_LENGTH) "[A-Z]" @@ -109,8 +113,9 @@ static HTTPRequestParseResult parse_method_uri_line( // return true if there are more headers and no error occurred, false otherwise // this will *not* free LIST if an error occurs -static bool next_header(FILE *stream, size_t *restrict bytes_read, - HTTPRequestParseResult *restrict res, HTTPHeaderList *restrict *restrict list) { +static bool next_header(FILE* stream, size_t* restrict bytes_read, + HTTPRequestParseResult* restrict res, HTTPHeaderList* restrict* restrict list) +{ char c = fgetc(stream); RETURN_IF_READ_ERROR(stream); if (c == '\r') { @@ -130,7 +135,7 @@ static bool next_header(FILE *stream, size_t *restrict bytes_read, char whitespace[3]; ssize_t signed_bytes_read; #define S1(s) #s -#define S(s) S1(s) +#define S(s) S1(s) int nconv = fscanf(stream, // clang-format off "%" S(MAX_HEADER_KEY_LENGTH) "[a-zA-Z0-9.-]" @@ -154,16 +159,17 @@ static bool next_header(FILE *stream, size_t *restrict bytes_read, return true; } -HTTPRequestParseResult parse_http_request(FILE *stream, HTTPRequest *restrict out) { +HTTPRequestParseResult parse_http_request(FILE* stream, HTTPRequest* restrict out) +{ out->uri = EMPTY_STRING; out->path = EMPTY_STRING; out->method = EMPTY_STRING; + out->headers = NULL; size_t total_bytes; HTTPRequestParseResult res; if ((res = parse_method_uri_line(stream, &total_bytes, out)) != HRPR_OK) { return res; } - out->headers = NULL; size_t bytes_read; while (next_header(stream, &bytes_read, &res, &out->headers)) { if ((total_bytes += bytes_read) > MAX_REQUEST_LENGTH) { @@ -174,20 +180,22 @@ HTTPRequestParseResult parse_http_request(FILE *stream, HTTPRequest *restrict ou return res; } -void free_http_request(HTTPRequest *restrict req) { +void free_http_request(HTTPRequest* restrict req) +{ if (req->method != EMPTY_STRING) { - free((char *) req->method); + free((char*)req->method); } if (req->uri != EMPTY_STRING) { - free((char *) req->uri); + free((char*)req->uri); } free_http_header_list(req->headers); } -const char *status_code_to_message(int status, size_t *restrict length) { +const char* status_code_to_message(int status, size_t* restrict length) +{ static const struct { int code; - const char *msg; + const char* msg; size_t size; } CODES[] = { #define P(s, m) { s, m, sizeof(m) - 1 } @@ -213,15 +221,17 @@ const char *status_code_to_message(int status, size_t *restrict length) { return NULL; } -void format_http_response(FILE *stream, HTTPResponse *restrict resp) { +void format_http_response(FILE* stream, HTTPResponse* restrict resp) +{ assert(status_code_to_message(resp->status, NULL)); - fprintf(stream, "HTTP/1.1 %d %s\r\n", resp->status, status_code_to_message(resp->status, NULL)); - fprintf(stream, "Content-Length: %zu\r\n", resp->body_length); - for (HTTPHeaderList *h = resp->headers; h; h = h->next) { - fwrite(h->key, 1, h->key_length, stream); - fwrite(": ", 1, 2, stream); - fwrite(h->value, 1, h->value_length, stream); - fwrite("\r\n", 1, 2, stream); + dprintf(fileno(stream), "HTTP/1.1 %d %s\r\n", resp->status, + status_code_to_message(resp->status, NULL)); + dprintf(fileno(stream), "Content-Length: %zu\r\n", resp->body_length); + for (HTTPHeaderList* h = resp->headers; h; h = h->next) { + write(fileno(stream), h->key, h->key_length); + write(fileno(stream), ": ", 2); + write(fileno(stream), h->value, h->value_length); + write(fileno(stream), "\r\n", 2); } - fwrite("\r\n", 1, 2, stream); + write(fileno(stream), "\r\n", 2); } diff --git a/main.c b/main.c index 48dafd1..bde5907 100644 --- a/main.c +++ b/main.c @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -28,7 +30,7 @@ typedef struct { bool help_flag; uint32_t port; size_t parallelism; - const char *address; + const char* address; } GlobalFlags; static const GlobalFlags DEFAULT_FLAGS = { @@ -40,7 +42,8 @@ static const GlobalFlags DEFAULT_FLAGS = { }; // Return false on failure, true on success -static bool parse_uint(const char *str, uintmax_t min, uintmax_t max, uintmax_t *output) { +static bool parse_uint(const char* str, uintmax_t min, uintmax_t max, uintmax_t* output) +{ if (isspace(*str) || *str == '+' || *str == '-') { #ifdef BAD_ERROR_REPORTING_FOR_AUTOGRADER fprintf(stderr, "Invalid Port\n"); @@ -50,7 +53,7 @@ static bool parse_uint(const char *str, uintmax_t min, uintmax_t max, uintmax_t return false; } errno = 0; - char *endptr; + char* endptr; uintmax_t conv = strtoumax(str, &endptr, 10); if (!*str || *endptr) { #ifdef BAD_ERROR_REPORTING_FOR_AUTOGRADER @@ -71,13 +74,14 @@ static bool parse_uint(const char *str, uintmax_t min, uintmax_t max, uintmax_t return true; } -static void parse_cli_options(int argc, const char **argv, GlobalFlags *flags) { +static void parse_cli_options(int argc, const char** argv, GlobalFlags* flags) +{ #define MAX_PARALLELISM SIZE_MAX -#define MAX_PORT 65535 +#define MAX_PORT 65535 opterr = false; int c; char pretty_flag[8]; - while ((c = getopt(argc, (char *const *) argv, ":ha:p:")) >= 0) { + while ((c = getopt(argc, (char* const*)argv, ":ha:p:")) >= 0) { if (isprint(c)) { snprintf(pretty_flag, sizeof(pretty_flag), "%c", optopt); } else { @@ -89,7 +93,9 @@ static void parse_cli_options(int argc, const char **argv, GlobalFlags *flags) { #pragma GCC diagnostic pop } switch (c) { - case 'h': flags->help_flag = true; return; + case 'h': + flags->help_flag = true; + return; case 'p': { uintmax_t conv; if (!parse_uint(optarg, 1, MAX_PARALLELISM, &conv)) { @@ -97,7 +103,9 @@ static void parse_cli_options(int argc, const char **argv, GlobalFlags *flags) { } flags->parallelism = conv; } break; - case 'a': flags->address = optarg; break; + case 'a': + flags->address = optarg; + break; case ':': flags->opt_error = true; log_error("flag requires argument: '%s'", pretty_flag); @@ -124,7 +132,8 @@ static void parse_cli_options(int argc, const char **argv, GlobalFlags *flags) { } } -static void print_help(FILE *file) { +static void print_help(FILE* file) +{ fprintf(file, "usage: httpserver [-h] [-a ADDRESS] [-p PARALLELISM] \n"); fprintf(file, " -h print this message, then exit\n"); fprintf(file, " -p use PARALLELISM threads for processing requests (default: 1)\n"); @@ -135,14 +144,25 @@ static const int WORKER_BLOCKED_SIGNALS[] = { SIGTERM, SIGINT, SIGHUP }; static const size_t N_WORKER_BLOCKED_SIGNALS = sizeof(WORKER_BLOCKED_SIGNALS) / sizeof(int); static bool shutdown_flag = false; -static void signal_handler(int signal) { +static void signal_handler(int signal) +{ if (shutdown_flag) { _exit(signal == SIGTERM ? 0 : EXIT_FAILURE); } shutdown_flag = true; } -static void setup_signals() { +static void setup_signals() +{ + struct sigaction pipe_act = { + .sa_flags = 0, + .sa_handler = SIG_IGN, + }; + sigemptyset(&pipe_act.sa_mask); + if (sigaction(SIGPIPE, &pipe_act, NULL) < 0) { + log_errno("sigaction"); + exit(EXIT_FAILURE); + } struct sigaction act = { .sa_handler = signal_handler, .sa_flags = 0, @@ -156,40 +176,51 @@ static void setup_signals() { } } -static int parse_result_to_status(HTTPRequestParseResult res) { +static int parse_result_to_status(HTTPRequestParseResult res) +{ switch (res) { - case HRPR_OK: return 200; + case HRPR_OK: + return 200; case HRPR_NO_MEM: - case HRPR_READ_FAILED: return 500; - case HRPR_BAD_VERSION: return 505; - case HRPR_BAD_FORMAT: return 400; - default: abort(); + case HRPR_READ_FAILED: + return 500; + case HRPR_BAD_VERSION: + return 505; + case HRPR_BAD_FORMAT: + return 400; + default: + abort(); } } -static void send_simple_response(FILE *stream, int status) { +static void send_simple_response(FILE* stream, int status) +{ size_t status_msg_len; - const char *status_msg = status_code_to_message(status, &status_msg_len); + const char* status_msg = status_code_to_message(status, &status_msg_len); HTTPResponse resp = { .status = status, .headers = NULL, .body_length = status_msg_len + 1, }; format_http_response(stream, &resp); - fwrite(status_msg, 1, status_msg_len, stream); - fputc('\n', stream); + write(fileno(stream), status_msg, status_msg_len); + write(fileno(stream), "\n", 1); } -static void write_audit_log_entry(HTTPRequest *restrict req, int status) { +static void write_audit_log_entry(HTTPRequest* restrict req, int status) +{ fprintf(stderr, "%s,%s,%d,%s\n", req->method, req->uri, status, http_header_list_search(req->headers, "Request-ID", "0")); } -static void handle_get_request(FILE *conn, HTTPRequest *restrict req) { +static pthread_mutex_t file_access_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void handle_get_request(FILE* conn, HTTPRequest* restrict req) +{ int status = 200; struct stat statbuf; - flockfile(stderr); - FILE *file_handle = fopen(req->path, "r"); + pthread_mutex_lock(&file_access_mutex); + FILE* file_handle = fopen(req->path, "r"); if (!file_handle) { if (errno == ENOENT) { status = 404; @@ -203,7 +234,7 @@ static void handle_get_request(FILE *conn, HTTPRequest *restrict req) { status = errno == EACCES ? 403 : 500; } write_audit_log_entry(req, status); - funlockfile(stderr); + pthread_mutex_unlock(&file_access_mutex); if (status != 200) { if (file_handle) { fclose(file_handle); @@ -225,20 +256,21 @@ static void handle_get_request(FILE *conn, HTTPRequest *restrict req) { } size_t count = fread(read_buf, 1, sizeof(read_buf), file_handle); if (count) { - fwrite(read_buf, 1, count, conn); + write(fileno(conn), read_buf, count); } } fclose(file_handle); } -static ssize_t get_content_length(HTTPRequest *restrict req) { - const char *text = http_header_list_search(req->headers, "Content-Length", NULL); +static ssize_t get_content_length(HTTPRequest* restrict req) +{ + const char* text = http_header_list_search(req->headers, "Content-Length", NULL); if (!text) { return 0; } else if (isspace(*text) || *text == '-' || *text == '+') { return -1; } - char *endptr; + char* endptr; uintmax_t conv = strtoumax(text, &endptr, 10); if (*endptr || (conv == UINTMAX_MAX && errno == ERANGE) || conv > SIZE_MAX) { return -1; @@ -246,7 +278,8 @@ static ssize_t get_content_length(HTTPRequest *restrict req) { return conv; } -static void handle_put_request(FILE *conn, HTTPRequest *restrict req) { +static void handle_put_request(FILE* conn, HTTPRequest* restrict req) +{ ssize_t content_length = get_content_length(req); if (content_length < 0) { write_audit_log_entry(req, 400); @@ -264,8 +297,8 @@ static void handle_put_request(FILE *conn, HTTPRequest *restrict req) { char read_buff[4096]; while (content_length) { ssize_t read_size = fread(read_buff, 1, - (size_t) content_length < sizeof(read_buff) ? (size_t) content_length - : sizeof(read_buff), + (size_t)content_length < sizeof(read_buff) ? (size_t)content_length + : sizeof(read_buff), conn); if (ferror(conn) || write(temp_fd, read_buff, read_size) < 0) { write_audit_log_entry(req, 500); @@ -279,7 +312,7 @@ static void handle_put_request(FILE *conn, HTTPRequest *restrict req) { close(temp_fd); int status; bool need_unlink = true; - flockfile(stderr); + pthread_mutex_lock(&file_access_mutex); int creat_fd = open(req->path, O_RDONLY | O_CREAT | O_EXCL, 0644); if (creat_fd < 0 && errno == EEXIST) { // file existed @@ -300,15 +333,16 @@ static void handle_put_request(FILE *conn, HTTPRequest *restrict req) { need_unlink = false; write_status_and_unlock: write_audit_log_entry(req, status); - funlockfile(stderr); + pthread_mutex_unlock(&file_access_mutex); send_simple_response(conn, status); if (need_unlink) { unlink(temp_file); } } -static void handle_connection(void *arg) { - FILE *conn = arg; +static void handle_connection(void* arg) +{ + FILE* conn = arg; HTTPRequest req; HTTPRequestParseResult res = parse_http_request(conn, &req); if (res != HRPR_OK) { @@ -327,14 +361,17 @@ static void handle_connection(void *arg) { send_simple_response(conn, 501); } free_http_request(&req); + shutdown(fileno(conn), SHUT_RDWR); fclose(conn); } -static void fclose_free_func(void *file) { +static void fclose_free_func(void* file) +{ fclose(file); } -int main(int argc, const char **argv) { +int main(int argc, const char** argv) +{ setenv("POSIXLY_CORRECT", "1", true); GlobalFlags flags = DEFAULT_FLAGS; parse_cli_options(argc, argv, &flags); @@ -349,11 +386,11 @@ int main(int argc, const char **argv) { for (size_t i = 0; i < N_WORKER_BLOCKED_SIGNALS; ++i) { sigaddset(&worker_block_set, WORKER_BLOCKED_SIGNALS[i]); } - Server *server = make_server(flags.address, flags.port); + Server* server = make_server(flags.address, flags.port); if (!server) { return EXIT_FAILURE; } - ThreadPool *pool = make_thread_pool(flags.parallelism, worker_block_set); + ThreadPool* pool = make_thread_pool(flags.parallelism, worker_block_set); if (!pool) { destroy_server(server); return EXIT_FAILURE; @@ -369,7 +406,7 @@ int main(int argc, const char **argv) { } else if (conn_fd < 0) { continue; } - FILE *conn = fdopen(conn_fd, "w+"); + FILE* conn = fdopen(conn_fd, "r+"); if (!conn) { close(conn_fd); continue;