From 634e95305eee9359d8d8f4b624255a11197afcb8 Mon Sep 17 00:00:00 2001 From: Joey Vrba Date: Mon, 15 Jan 2024 14:14:14 -0500 Subject: [PATCH] crvr compiles again! Whew... --- crvr.c | 121 ++++++++++++++++++++++++++++++------------------------- http.c | 14 ++++--- http.h | 17 ++++++-- pool.h | 6 +-- str.h | 125 ++++++++++++++++++++++++++++++++++++++------------------- 5 files changed, 174 insertions(+), 109 deletions(-) diff --git a/crvr.c b/crvr.c index 84a48f5..6fb620b 100644 --- a/crvr.c +++ b/crvr.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -44,20 +45,21 @@ void print(struct sockaddr_in *address) static int parse_into_param(struct str *line, struct http_param *param) { - const struct str separator = ":"; + static const struct str separator = STR(":"); if (!line || !param) return EINVAL; const long sep_loc = str_find_substr(line, &separator); if (sep_loc == -1) return EPROTO; - param->key = {line.s, sep_loc}; - param->value = {line.s + sep_loc, line.len - sep_loc}; + param->key = (struct str){line->s, sep_loc}; + param->value = (struct str){line->s + sep_loc, line->len - sep_loc}; return 0; } static int add_param_to_request(struct request *r, struct http_param *param) { if (!r || !param) return EINVAL; - if (r->param_count >= r->param_cap) return ENOBUFS; + assert(LEN(r->params) <= LONG_MAX); + if (r->param_count >= (long)LEN(r->params)) return ENOBUFS; r->params[r->param_count] = *param; r->param_count++; return 0; @@ -75,29 +77,29 @@ static int add_param_to_request(struct request *r, struct http_param *param) * @return Returns 0 if the header is successfully parsed. Otherwise returns an * error code. */ -static int parse_header_options(struct str *rest_of_header, struct request *r) +static int parse_header_options(struct str rest_of_header, struct request *r) { int error = 0; - const str newline = STR("\r\n"); + static const struct str newline = STR("\r\n"); struct http_param param = {0}; - if (!rest_of_header || !r) { + if (!r) { return EINVAL; } - while (rest_of_header->len > 0) { + while (rest_of_header.len > 0) { struct str line = rest_of_header; - long eol = str_find_substr(&line, &new_line); + long eol = str_find_substr(&line, &newline); if (eol != -1) line.len = eol; - rest_of_header->s += line.len; - rest_of_header->len -= line.len; + rest_of_header.s += line.len; + rest_of_header.len -= line.len; error = parse_into_param(&line, ¶m); if (error) return error; error = add_param_to_request(r, ¶m); } - printf("Found %lu headers.\n", request->header_count); + printf("Found %lu headers.\n", r->param_count); return 0; } @@ -105,25 +107,22 @@ static int parse_header_options(struct str *rest_of_header, struct request *r) * Parse the buffer in the request. Determine if it is a POST or GET request * and parse its parameters storing the data in the request itself. */ -int parse_request_buffer(struct request *request) +int parse_request_buffer(struct request *request, struct pool *p) { - const struct str eol = STR("\r\n"); - const char index_page[] = "index.html"; - const struct str space = STR(" "); + static const struct str eol = STR("\r\n"); + static const struct str index_page = STR("index.html"); + static const struct str space = STR(" "); + static const struct str slash_only = STR("/"); // Type, path and format are all on the first line. - struct str req_buf = {request->buffer, strlen(request->buffer)}; - if (req_buf.len > LEN(buffer)) { - printf("request too big: %u. Forcing to %u.\n", req_buf.len, - LEN(buffer)); - req_buf.len = LEN(buffer); - } + struct str req_buf = request->buffer; long end_of_line = str_find_substr(&req_buf, &eol); if (end_of_line == -1) { // Uh... wut? We should have at least one EOL. - fprintf(stderr, "Did not find EOL in header. Header:\n%s\n", - request->buffer); + fputs("Did not find EOL in header. Header:\n", stderr); + str_print(stderr, &request->buffer); + fputs("\n", stderr); return EINVAL; } struct str header = {req_buf.s, end_of_line}; @@ -134,7 +133,7 @@ int parse_request_buffer(struct request *request) fputs("Did not find GET\\POST to path space in header. Header: \"", stderr); str_print(stderr, &header); - fputs("\"\n"); + fputs("\"\n", stderr); return EINVAL; } struct str req_type = {header.s, req_type_end}; @@ -147,7 +146,7 @@ int parse_request_buffer(struct request *request) } else { fputs("Unrecognized request type \"", stderr); str_print(stderr, &req_type); - fputs("\"\n"); + fputs("\"\n", stderr); return EINVAL; } @@ -163,22 +162,23 @@ int parse_request_buffer(struct request *request) path.len = path_end; // The rest of the line is the format. - struct str format = {path.s + path.len, header.len - type.len - - path.len}; + request->format = (struct str){ + path.s + path.len, + header.len - req_type.len - path.len + }; // Handle some special cases for path. If it is just /, change it to // just load index.html. Easiest to do this before copying it into the // request. - const struct str index_page = STR("index.html"); if (str_cmp(&path, &slash_only) == 0) { path = index_page; } // If it is a directory (ends in /) append "index.html". - if (path->s[path->len] == '/') { + if (path.s[path.len] == '/') { struct str actual_path = {0}; - long space_needed = path->len + index_path.len; + long space_needed = path.len + index_page.len; int error = alloc_str(p, space_needed, &actual_path); if (error) { fprintf(stderr, "Failed to allocate actual path: %i.\n", @@ -187,20 +187,23 @@ int parse_request_buffer(struct request *request) } assert(actual_path.len == space_needed); assert(actual_path.len >= path.len + index_page.len); - (void)memcpy(actual_path.s, path.s, path.len); + static_assert(sizeof(size_t) >= sizeof(path.len)); + static_assert(SIZE_MAX > LONG_MAX); + (void)memcpy(actual_path.s, path.s, (size_t)path.len); (void)strncat(actual_path.s + actual_path.len, index_page.s, - index_page.len); + (size_t)index_page.len); path = actual_path; } const struct str rest_of_header = {header.s + header.len, req_buf.len - header.len}; - parse_header_options(&rest_of_header, r); + parse_header_options(rest_of_header, request); return 0; } -int parse_request(char *data, long data_len, struct request *request) +int parse_request(char *data, long data_len, struct request *request, + struct pool *p) { if (!data || !request || (data_len <= 0)) return EINVAL; @@ -208,9 +211,13 @@ int parse_request(char *data, long data_len, struct request *request) const struct str header_separator = STR("\r\n\r\n"); + const size_t data_str_len = strlen(data); + if (data_str_len < (size_t)data_len) { + request->buffer.len = (long)data_str_len; + } else { + request->buffer.len = data_len; + } request->buffer.s = data; - request->buffer.len = strlen(data); - if (req_data.len < data_len) req_data.len = data_len; // Find the end of the header. long end_of_header = str_find_substr(&request->buffer, @@ -220,7 +227,7 @@ int parse_request(char *data, long data_len, struct request *request) // need a bigger buffer. if (end_of_header != -1) return ENOBUFS; - int err = parse_request_buffer(request); + int err = parse_request_buffer(request, p); if (err) { fprintf(stderr, "Failed to parse request buffer %d.\n", err); return err; @@ -236,15 +243,19 @@ int parse_request(char *data, long data_len, struct request *request) */ int handle_get_request(int client, struct request *request, struct pool *p) { - printf("Getting \"%s\"\n", request->path); - if (strcmp(request->path, "asl.html") == 0) { + static const struct str ASL_PAGE = STR("asl.html"); + printf("Getting \""); + str_print(stdout, &request->path); + printf("\"\n"); + + if (str_cmp(&request->path, &ASL_PAGE) == 0) { printf("Dynamic URI\n"); return asl_get(request, client); } FILE *f = NULL; char file_path[PATH_MAX] = {0}; - int err = copy_str_to_cstr(file_path, PATH_MAX, &request->path); + int err = str_copy_to_cstr(&request->path, file_path, PATH_MAX); if (err) return err; f = fopen(file_path, "r"); @@ -261,9 +272,8 @@ int handle_get_request(int client, struct request *request, struct pool *p) int handle_post_request(int client, struct request *r, struct pool *p, size_t bytes_received) { - char *value; - long total_len; - long bytes_needed; + long total_len = 0; + long bytes_needed = 0; (void)client; (void)p; @@ -278,8 +288,9 @@ int handle_post_request(int client, struct request *r, struct pool *p, } err = str_to_long(&content_len, &total_len, 10); if (err) { - fprintf(stderr, "Failed to convert %s to long. %u.\n", value, - err); + fputs("Failed to convert \"", stderr); + str_print(stderr, &content_len); + fprintf(stderr, "\" to long. err=%u.\n", err); return err; } if (total_len < 0) { @@ -287,17 +298,17 @@ int handle_post_request(int client, struct request *r, struct pool *p, return EINVAL; } printf("content length is %ld\n", total_len); - bytes_needed = (size_t)total_len; + bytes_needed = total_len; if (bytes_needed > pool_get_remaining_capacity(p)) { - fprintf(stderr, "Request too big: %lu. Max: %d.\n", + fprintf(stderr, "Request too big: %lu. Max: %li.\n", bytes_needed, pool_get_remaining_capacity(p)); return ENOBUFS; } printf("Have %lu bytes of content, Need to read in %lu more bytes\n", bytes_received, bytes_needed); - alloc_str(&r->post_params_buffer, bytes_needed); + alloc_str(p, bytes_needed, &r->post_params_buffer); long bytes_read = 0; while (bytes_read < bytes_needed) { // Need to update space below if this isn't the case. @@ -316,10 +327,12 @@ int handle_post_request(int client, struct request *r, struct pool *p, print_request(r); - if (strcmp(r->path, "asl.html") == 0) { - return asl_post(r, client); - } - printf("Don't know what to do with post to %s.\n", r->path); + if (str_cmp_cstr(&r->path, "asl.html") == 0) return asl_post(r, client); + + printf("Don't know what to do with post to \""); + str_print(stdout, &r->path); + printf("\"\n"); + return 0; } @@ -343,7 +356,7 @@ int handle_client(int client, struct sockaddr_in *client_addr, struct pool *p) struct request request; const long start = pool_get_position(p); - if (parse_request(buffer, LEN(buffer), &request) != 0) { + if (parse_request(buffer, LEN(buffer), &request, p) != 0) { fprintf(stderr, "Failed to parse client's request.\nBuffer was:\n%s\n", buffer); diff --git a/http.c b/http.c index 0718e90..dc210da 100644 --- a/http.c +++ b/http.c @@ -65,15 +65,17 @@ int find_param(struct param out[static 1], struct request r[static 1], return 0; } -char *header_find_value(struct request *r, const char *key) +int header_find_value(struct request *r, const char *key, struct str *value) { - size_t i; - for (i = 0; i < r->header_count; ++i) { - if (strcmp(key, r->headers[i].key) == 0) { - return r->headers[i].value; + if (!r || !key || !value) return EINVAL; + + for (long i = 0; i < r->param_count; ++i) { + if (str_cmp_cstr(&r->params[i].key, key) == 0) { + *value = r->params[i].value; + return 0; } } - return NULL; + return ENOENT; } void print_request(struct request *r) diff --git a/http.h b/http.h index 5043fd6..452d8b0 100644 --- a/http.h +++ b/http.h @@ -10,6 +10,7 @@ #include #include "pool.h" +#include "str.h" // The max length of an HTTP parameter. #define PARAM_NAME_MAX 256 @@ -45,7 +46,6 @@ struct request { struct str path; struct str format; long param_count; - long param_cap = MAX_HEADER_LINES; struct http_param params[MAX_HEADER_LINES]; struct str buffer; struct str post_params_buffer; @@ -65,10 +65,21 @@ extern const char ok_header[]; * * Returns zero if the parameter was found, or an error code otherwise. */ -int find_param(struct param out[static 1], struct request r[static 1], +int find_param(struct http_param *out, struct request *r, const char *param_name); -char *header_find_value(struct request *r, const char *key); +/** + * @brief Lookup a header parameter in the request. + * + * @param[in] r - The request to search. + * @param[in] key - The key to look up. + * @param[out] value - The location to store the value. + * + * @return Returns 0 if the value was found and stored in the output parameter. + * Returns ENOENT if the value wasn't found. Otherwise returns an error + * code. + */ +int header_find_value(struct request *r, const char *key, struct str *value); /* * Prints a HTTP request for debugging. diff --git a/pool.h b/pool.h index d535357..8d65b25 100644 --- a/pool.h +++ b/pool.h @@ -43,7 +43,7 @@ int pool_init(struct pool *p, unsigned long desired_size); * * @return Returns the space left in p. Returns -1 if p is NULL. */ -long pool_get_remaining_capacity(struct pool *p) +long pool_get_remaining_capacity(struct pool *p); /* * Free all memory allocated to the pool. @@ -158,9 +158,7 @@ void *pool_alloc(struct pool *p, long byte_amount) { unsigned long alignment = sizeof(void*) - byte_amount % sizeof(void*); byte_amount += alignment; - if ((p->offset + byte_amount) > p->cap) { - return NULL; - } + if ((p->offset + byte_amount) > p->cap) return NULL; assert(p->buffer); void *allocation = (void*)(p->buffer + p->offset); p->offset += byte_amount; diff --git a/str.h b/str.h index 0cceb65..1649584 100644 --- a/str.h +++ b/str.h @@ -15,6 +15,7 @@ #define BASE_STR_H #include +#include struct pool; @@ -77,7 +78,7 @@ int str_print(FILE *f, const struct str *s); * * @return Returns 0 if successful, otherwise returns an error code. */ -int alloc_str(struct pool *p, const unsigned long space_needed, struct str *s); +int alloc_str(struct pool *p, const long space_needed, struct str *s); /** * @brief Copies the c-string into an str. @@ -96,6 +97,31 @@ int alloc_str(struct pool *p, const unsigned long space_needed, struct str *s); int alloc_from_str(struct pool *p, const char *cstr, const long len, struct str *s); +/** + * @brief Converts the contents of an str to a long. + * + * @param[in] s - The str to attempt to convert. + * @param[out] l - The location to store the long values. + * @param[in] base - The base to use when converting the string representation + * to the number. + * + * @return Returns 0 if the conversion was successful and l is populated. + * Otherwise returns an error code. + */ +int str_to_long(const struct str *s, long *l, long base); + +/** + * @brief Copies the str data into a c-string. + * + * @param[in] s - The str to copy data from. + * @param[out] dest - The pre-allocated character array to save data to. + * @param[in] dest_len - The length of the array at dest, which will be checked + * to avoid exceeding the buffer length. + * + * @return Returns 0 if the data was successfully copied. Otherwise returns an + * error code. + */ +int str_copy_to_cstr(const struct str *s, char *dest, long dest_len); #ifdef DEFINE_STR @@ -104,33 +130,26 @@ int alloc_from_str(struct pool *p, const char *cstr, const long len, int str_cmp(const struct str *a, const struct str *b) { - if (a == b) - return 0; - for (long i = 0; i < a->len; ++i) { - for (long j = 0; j < b->len; ++j) { - if (a->s[i] < b->s[i]) { - return -1; - } else if (a->s[i] > b->s[i]) { - return 1; - } - } - } - return 0; + if (a == b) return 0; + for (long i = 0; i < a->len; ++i) { + for (long j = 0; j < b->len; ++j) { + if (a->s[i] < b->s[i]) return -1; + if (a->s[i] > b->s[i]) return 1; + } + } + return 0; } int str_cmp_cstr(const struct str *s, const char *cs) { - const size_t len = strlen(cs); - for (long i = 0; i < s->len; ++i) { - for (size_t j = 0; j < len; ++j) { - if (s->s[i] < cs[j]) { - return -1; - } else if (s->s[i] > cs[j]) { - return 1; - } - } - } - return 0; + const size_t len = strlen(cs); + for (long i = 0; i < s->len; ++i) { + for (size_t j = 0; j < len; ++j) { + if (s->s[i] < cs[j]) return -1; + if (s->s[i] > cs[j]) return 1; + } + } + return 0; } /* @@ -151,31 +170,26 @@ long str_find_substr(const struct str *haystack, const struct str *needle) break; } } - if (found) { - return i; - } + if (found) return i; } return -1; } int str_print(FILE *f, const struct str *s) { - const char end = s->s[s->len]; - s->s[s->len] = '\0'; - int result = fputs(s->s, f); - s->s[s->len] = end; - return result; + const char end = s->s[s->len]; + s->s[s->len] = '\0'; + int result = fputs(s->s, f); + s->s[s->len] = end; + return result; } int alloc_str(struct pool *p, const long space_needed, struct str *s) { - if (!p || !s || (space_needed == 0)) { - return EINVAL; - } + if (!p || !s || (space_needed <= 0)) return EINVAL; + s->s = pool_alloc(p, space_needed); - if (!s->s) { - return ENOMEM; - } + if (!s->s) return ENOMEM; s->len = space_needed; return 0; } @@ -183,14 +197,41 @@ int alloc_str(struct pool *p, const long space_needed, struct str *s) int alloc_from_str(struct pool *p, const char *cstr, const long len, struct str *s) { - int alloc_error = alloc_str(p, len, s); - if (alloc_error) { - return alloc_error; - } + int err = alloc_str(p, len, s); + if (err) return err; (void)memcpy(s->s, cstr, s->len); return 0; } +#define LONG_9 ((long)'9' - (long)'0'); + +int str_to_long(const struct str *s, long *l, long base) +{ + if (!s || !l || (base <= 0)) return EINVAL; + + const double max_value = round(powf((double)base, (double)s->len)); + if (max_value > (double)LONG_MAX) return ERANGE; + long value = 0; + long c = 0; + for (long i = 0; i < s->len; ++i) { + c = s->s[i]; + c -= '0'; + if (c > LONG_9) return ERANGE; + value *= base; + value += c; + } +} + +int str_copy_to_cstr(const struct str *s, char *dest, long dest_len) +{ + if (!s || !dest || (dest_len <= 0)) return EINVAL; + if (s->len >= dest_len) return ENOSPC; + + memcpy(dest, s->s, s->len); + dest[s->len + 1] = 0; + return 0; +} + #endif // DEFINE_STR #endif // BASE_STR_H