diff --git a/http.c b/http.c index da547e8..005c31f 100644 --- a/http.c +++ b/http.c @@ -5,11 +5,10 @@ #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; } @@ -32,10 +31,9 @@ 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); @@ -43,8 +41,7 @@ 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; @@ -54,33 +51,32 @@ 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]; char version_str[MAX_VERSION_LENGTH + 1]; - char whitespace[4]; + char whitespace[4] = { 0, 0, 0, 0 }; 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]" + "%" S(MAX_METHOD_LENGTH) "[A-Z0-9]" "%c" "%" S(MAX_URI_LENGTH) "[^ \n\r]" "%c" @@ -93,29 +89,32 @@ static HTTPRequestParseResult parse_method_uri_line( #undef S *bytes_read = signed_bytes_read; RETURN_IF_READ_ERROR(stream); - if (nconv != 7 || memcmp(whitespace, " \r\n", 4) != 0) { + if (nconv >= 1 && (nconv == 1 || whitespace[0] == ' ')) { + req->method = strdup(method); + if (!req->method) { + return HRPR_NO_MEM; + } + if (nconv >= 3 && (nconv == 3 || whitespace[1] == ' ')) { + req->uri = strdup(uri); + if (!req->uri) { + return HRPR_NO_MEM; + } + req->path = req->uri; + while (*req->path == '/') { + ++req->path; + } + } + } + if (nconv != 7 || *uri != '/' || memcmp(whitespace, " \r\n", 4) != 0) { return HRPR_BAD_FORMAT; } - req->method = strdup(method); - if (!req->method) { - return HRPR_NO_MEM; - } - req->uri = strdup(uri); - if (!req->uri) { - return HRPR_NO_MEM; - } - req->path = req->uri; - while (*req->path == '/') { - ++req->path; - } return strcmp(version_str, "1.1") == 0 ? HRPR_OK : HRPR_BAD_VERSION; } // 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') { @@ -128,6 +127,9 @@ static bool next_header(FILE* stream, size_t* restrict bytes_read, *bytes_read = 2; *res = HRPR_OK; return false; + } else if (feof(stream)) { + *res = HRPR_BAD_FORMAT; + return false; } ungetc(c, stream); char key[MAX_HEADER_KEY_LENGTH + 1]; @@ -135,14 +137,14 @@ 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.-]" - ":%c" - "%" S(MAX_HEADER_VALUE_LENGTH) "[ -~]" - "%c%c" - "%zn", + "%" S(MAX_HEADER_KEY_LENGTH) "[a-zA-Z0-9.-]" + ":%c" + "%" S(MAX_HEADER_VALUE_LENGTH) "[ -~]" + "%c%c" + "%zn", // clang-format on key, &whitespace[0], value, &whitespace[1], &whitespace[2], &signed_bytes_read); #undef S @@ -159,8 +161,7 @@ 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; @@ -180,26 +181,26 @@ 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 } + // can't get my local copy of clang-format (v18) to work with the autograder + // clang-format off +#define P(s, m) { s, m, sizeof(m) - 1 } + // clang-format on P(200, "OK"), P(201, "Created"), P(400, "Bad Request"), @@ -222,13 +223,12 @@ 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)); 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) { + 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);