httpp-benchmark

httpp-benchmark.git
git clone git://git.lenczewski.org/httpp-benchmark.git
Log | Files | Refs | README | LICENSE

commit 68f1f8f566348136c6f1d5e769708cb148229efa
parent 223a7eee1a93f61f1cd381cdc790faacfc6fd3be
Author: cebem1nt <mineewarik@gmail.com>
Date:   Tue, 25 Nov 2025 14:59:21 -0300

Added responses handling, bugfixes and improvements

Diffstat:
Mhttpp.h | 296+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Mtest.c | 18++++++++++++++++--
2 files changed, 252 insertions(+), 62 deletions(-)

diff --git a/httpp.h b/httpp.h @@ -1,6 +1,6 @@ /* - * Tiny header only http parser library for - * http 1/1 version + * Tiny header only http parser library for + * http 1/1 version (enums don't respect your namespace much, sorry) */ #include <stddef.h> @@ -14,53 +14,84 @@ #define HTTPP_MAX_METHOD_LENGTH (10 + 1) #define HTTPP_VERSION_BUFSIZE (10 + 1) +#define HTTPP_SUPPORTED_VERSION "HTTP/1.1" + +#define _HTTP_DELIMITER "\r\n" +#define _HTTP_DELIMITER_SIZE 2 +#define _HTTP_MAX_STATUS_CODE_SIZE 3 #define httpp_string_to_method(s) (strcmp(s, "GET") == 0 ? 0 : \ strcmp(s, "POST") == 0 ? 1 : \ strcmp(s, "DELETE") == 0 ? 2 : -1) #define HTTPP_ERRMEMRY 3 -#define HTTPP_ERRLOGIC 1 +#define HTTPP_ERRLOGIC 2 +#define HTTPP_ERRDEFLT 1 typedef enum { GET, POST, DELETE, UNKNOWN = -1 -} http_method_t; +} httpp_method_t; + +typedef enum { + Continue = 100, + Switching_Protocols, + + Ok = 200, + Created, + Accepted, + + Bad_Request = 400, + Unauthorized, + Payment_Required, // Ahahaha + Forbiden, + Not_Found, + Unspecified = -1 +} httpp_status_t; typedef struct { char* name; char* value; -} http_header_t; +} httpp_header_t; typedef struct { - http_header_t* arr; + httpp_header_t* arr; size_t capacity; size_t length; -} http_headers_arr_t; +} httpp_headers_arr_t; typedef struct { - http_method_t method; - http_headers_arr_t* headers; + httpp_method_t method; + httpp_headers_arr_t* headers; char* route; char* body; -} http_req_t; +} httpp_req_t; -const char* httpp_method_to_string(http_method_t m); -http_req_t* httpp_req_new(); -http_req_t* httpp_parse_request(char* raw); +typedef struct { + httpp_headers_arr_t* headers; + httpp_status_t code; + char* body; +} httpp_res_t; + +const char* httpp_method_to_string(httpp_method_t m); +httpp_req_t* httpp_req_new(); +httpp_req_t* httpp_parse_request(char* raw); + +int httpp_parse_header(httpp_headers_arr_t* hs, char* line); +int httpp_headers_append(httpp_headers_arr_t* hs, httpp_header_t header); +int httpp_headers_add(httpp_headers_arr_t* hs, char* name, char* value); +void httpp_headers_arr_free(httpp_headers_arr_t* hs); +void httpp_req_free(httpp_req_t* req); -int httpp_parse_header(http_headers_arr_t* hs, char* line); -int httpp_headers_append(http_headers_arr_t* hs, http_header_t header); -void http_headers_arr_free(http_headers_arr_t* hs); -void httpp_req_free(http_req_t* req); +httpp_res_t* httpp_res_new(); +void httpp_res_free(httpp_res_t* res); + +int httpp_res_set_body(httpp_res_t* res, char* body); +char* httpp_res_to_raw(httpp_res_t* res); -#define HTTPP_IMPLEMENTATION #ifdef HTTPP_IMPLEMENTATION -#define HTTP_DELIMITER "\r\n" -#define HTTP_DELIMITER_SIZE 2 -#define HTTP_VERSION "HTTP/1.1" #define trim(str) do { ltrim(str); rtrim(str); } while (0) @@ -84,11 +115,11 @@ static int chop_until(char c, char** src, char* dest, size_t n) { char* pos = strchr(*src, c); if (!pos) - return 1; + return HTTPP_ERRDEFLT; size_t chopped_size = pos - *src; if (chopped_size >= n) - return 1; + return HTTPP_ERRDEFLT; memcpy(dest, *src, chopped_size); dest[chopped_size] = '\0'; @@ -115,56 +146,84 @@ static char* dchop_until(char c, char** src) return out; } -const char* httpp_method_to_string(http_method_t m) +const char* httpp_method_to_string(httpp_method_t m) { switch (m) { - case GET: return "GET"; - case POST: return "POST"; - case DELETE: return "DELETE"; - default: return "UNKNOWN"; + case GET: return "GET"; + case POST: return "POST"; + case DELETE: return "DELETE"; + default: return "UNKNOWN"; } } -http_req_t* httpp_req_new() +const char* httpp_status_to_string(httpp_status_t s) { - http_req_t* out = (http_req_t*) malloc(sizeof(http_req_t)); - if (!out) - return NULL; + switch (s) { + case Continue: return "Continue"; + case Switching_Protocols: return "Switching Protocols"; + + case Ok: return "OK"; + case Created: return "Created"; + case Accepted: return "Accepted"; + + case Bad_Request: return "Bad Request"; + case Unauthorized: return "Unauthorized"; + case Payment_Required: return "Payment Required"; + case Forbiden: return "Forbidden"; + case Not_Found: return "Not Found"; + + case Unspecified: + default: return "Unspecified"; + } +} - out->headers = (http_headers_arr_t*) malloc(sizeof(http_headers_arr_t)); - if (!out->headers) { + +httpp_headers_arr_t* httpp_headers_arr_new() +{ + httpp_headers_arr_t* out = (httpp_headers_arr_t*) malloc(sizeof(httpp_headers_arr_t)); + + out->arr = (httpp_header_t*) malloc(sizeof(httpp_header_t) * HTTPP_INITIAL_HEADERS_ARR_CAP); + if (!out->arr) { free(out); return NULL; } - out->headers->arr = (http_header_t*) malloc(sizeof(http_header_t) * HTTPP_INITIAL_HEADERS_ARR_CAP); - if (!out->headers->arr) { - free(out->headers); + out->capacity = HTTPP_INITIAL_HEADERS_ARR_CAP; + out->length = 0; + return out; +} + +httpp_req_t* httpp_req_new() +{ + httpp_req_t* out = (httpp_req_t*) malloc(sizeof(httpp_req_t)); + if (!out) + return NULL; + + out->headers = httpp_headers_arr_new(); + if (!out->headers) { free(out); return NULL; } - out->headers->capacity = HTTPP_INITIAL_HEADERS_ARR_CAP; - out->headers->length = 0; out->route = NULL; out->body = NULL; - return out; } -void http_headers_arr_free(http_headers_arr_t* hs) +void httpp_headers_arr_free(httpp_headers_arr_t* hs) { if (!hs) return; + for (size_t i = 0; i < hs->length; i++) { free(hs->arr[i].name); free(hs->arr[i].value); } } -void httpp_req_free(http_req_t* req) +void httpp_req_free(httpp_req_t* req) { if (!req) return; - http_headers_arr_free(req->headers); + httpp_headers_arr_free(req->headers); free(req->headers->arr); free(req->headers); @@ -173,25 +232,41 @@ void httpp_req_free(http_req_t* req) free(req); } -int httpp_headers_append(http_headers_arr_t* hs, http_header_t header) +int httpp_headers_append(httpp_headers_arr_t* hs, httpp_header_t header) { if (hs->length >= hs->capacity) { size_t new_cap = hs->capacity * 2; if (new_cap <= hs->capacity) // Doesn't free on failure. Make a note about it return HTTPP_ERRMEMRY; - hs->arr = (http_header_t*) realloc(hs->arr, new_cap * sizeof(http_header_t)); + hs->arr = (httpp_header_t*) realloc(hs->arr, new_cap * sizeof(httpp_header_t)); if (!hs->arr) return HTTPP_ERRMEMRY; hs->capacity = new_cap; } + if (!header.name || !header.value) + return HTTPP_ERRLOGIC; + hs->arr[hs->length++] = header; return 0; } -int httpp_parse_header(http_headers_arr_t* hs, char* line) +int httpp_headers_add(httpp_headers_arr_t* hs, char* name, char* value) +{ + if (!name || !value) + return HTTPP_ERRLOGIC; + + httpp_header_t h = { + strdup(name), + strdup(value) + }; + + return httpp_headers_append(hs, h); +} + +int httpp_parse_header(httpp_headers_arr_t* hs, char* line) { char* delim_pos = strchr(line, ':'); if (!delim_pos) @@ -216,13 +291,13 @@ int httpp_parse_header(http_headers_arr_t* hs, char* line) if (!value) return HTTPP_ERRMEMRY; - if (httpp_headers_append(hs, (http_header_t){name, value}) != 0) + if (httpp_headers_append(hs, (httpp_header_t){name, value}) != 0) return HTTPP_ERRMEMRY; return 0; } -static int http_parse_start_line(char** itr, http_req_t* dest) +static int parse_start_line(char** itr, httpp_req_t* dest) { char method_buf[HTTPP_MAX_METHOD_LENGTH]; char version_buf[HTTPP_VERSION_BUFSIZE]; @@ -230,50 +305,50 @@ static int http_parse_start_line(char** itr, http_req_t* dest) ltrim(*itr); if (chop_until(' ', itr, method_buf, HTTPP_MAX_METHOD_LENGTH) != 0) - return 1; + return HTTPP_ERRDEFLT; ltrim(*itr); // Route might have extra spaces at the bginning, for our implementation thats fine if ((route = dchop_until(' ', itr)) == NULL) - return 1; + return HTTPP_ERRDEFLT; ltrim(*itr); if (chop_until('\n', itr, version_buf, HTTPP_VERSION_BUFSIZE) != 0) { free(route); - return 1; + return HTTPP_ERRDEFLT; } trim(version_buf); - if (strcmp(version_buf, HTTP_VERSION) != 0) { + if (strcmp(version_buf, HTTPP_SUPPORTED_VERSION) != 0) { free(route); - return 1; + return HTTPP_ERRDEFLT; } - dest->method = (http_method_t) httpp_string_to_method(method_buf); + dest->method = (httpp_method_t) httpp_string_to_method(method_buf); dest->route = route; return 0; } -http_req_t* httpp_parse_request(char* raw) +httpp_req_t* httpp_parse_request(char* raw) { - http_req_t* out = httpp_req_new(); + httpp_req_t* out = httpp_req_new(); char* itr = raw; char* end = raw + strlen(raw); char line[HTTPP_LINE_BUFSIZE]; - if (http_parse_start_line(&itr, out) != 0) { + if (parse_start_line(&itr, out) != 0) { httpp_req_free(out); return NULL; } while (itr < end) { - char* del_pos = strstr(itr, HTTP_DELIMITER); + char* del_pos = strstr(itr, _HTTP_DELIMITER); if (!del_pos) break; size_t line_size = del_pos - itr; if (line_size == 0) { - itr = del_pos + HTTP_DELIMITER_SIZE; + itr = del_pos + _HTTP_DELIMITER_SIZE; break; } @@ -290,7 +365,7 @@ http_req_t* httpp_parse_request(char* raw) return NULL; } - itr = del_pos + HTTP_DELIMITER_SIZE; + itr = del_pos + _HTTP_DELIMITER_SIZE; } // TODO, there is a thing called chunked transfer, which is @@ -303,4 +378,105 @@ http_req_t* httpp_parse_request(char* raw) return out; } + +// --- Responses part --- +httpp_res_t* httpp_res_new() +{ + httpp_res_t* out = (httpp_res_t*) malloc(sizeof(httpp_res_t)); + if (!out) + return NULL; + + out->headers = httpp_headers_arr_new(); + if (!out->headers) + return NULL; + + out->code = Unspecified; + return out; +} + +void httpp_res_free(httpp_res_t* res) +{ + if (!res) + return; + + httpp_headers_arr_free(res->headers); + free(res->body); +} + +int httpp_res_set_body(httpp_res_t* res, char* body) +{ + res->body = strdup(body); + if (!res->body) + return 1; + + return 0; +} + +char* httpp_res_to_raw(httpp_res_t* res) +{ + if (res == NULL || res->headers == NULL) + return NULL; + + if (res->code == -1) + return NULL; + + const char* status_msg = httpp_status_to_string(res->code); + size_t out_size = + strlen(HTTPP_SUPPORTED_VERSION) + + 1 // Space + + _HTTP_MAX_STATUS_CODE_SIZE + + 1 // Space + + strlen(status_msg) + + _HTTP_DELIMITER_SIZE; + + for (size_t i = 0; i < res->headers->length; i++) { + httpp_header_t header = res->headers->arr[i]; + if (!header.name || !header.value) + continue; + + out_size += strlen(header.name) + 2 // : + + strlen(header.value) + + _HTTP_DELIMITER_SIZE; + } + + out_size += _HTTP_DELIMITER_SIZE; + if (res->body) + out_size += strlen(res->body); + + out_size += 1; // '\0' + char* out = (char*) malloc(out_size); + if (!out) + return NULL; + + int written = snprintf(out, out_size, "%s %d %s\r\n", HTTPP_SUPPORTED_VERSION, res->code, status_msg); + if (written < 0 || (size_t) written >= out_size) { + free(out); + return NULL; + } + + size_t offset = written; + + for (size_t i = 0; i < res->headers->length; i++) { + httpp_header_t header = res->headers->arr[i]; + if (!header.name || !header.value) + continue; + + int n = snprintf(out + offset, out_size - offset, + "%s: %s\r\n", header.name, header.value); + + if (n < 0 || (size_t) n >= out_size - offset) { + free(out); + return NULL; + } + + offset += n; + } + + strcat(out, _HTTP_DELIMITER); + if (res->body) + strcat(out, res->body); + + return out; +} + #endif // HTTPP_IMPLEMENTATION \ No newline at end of file diff --git a/test.c b/test.c @@ -1,8 +1,9 @@ #include <stdio.h> + #define HTTPP_IMPLEMENTATION #include "httpp.h" -void print(http_req_t* parsed) +void print(httpp_req_t* parsed) { if (parsed == NULL) { printf("Couldn't parse!\n"); @@ -35,7 +36,7 @@ int main() "\r\n" "{\"name\":\"Widget\",\"quantity\":10,\"price\":9.99}"; - http_req_t* parsed = httpp_parse_request(req1); + httpp_req_t* parsed = httpp_parse_request(req1); print(parsed); httpp_req_free(parsed); @@ -67,5 +68,18 @@ int main() parsed = httpp_parse_request(req3); print(parsed); + httpp_res_t* res = httpp_res_new(); + + res->code = 200; + httpp_headers_add(res->headers, "Host", "idk.me.com"); + httpp_headers_add(res->headers, "Home", "pkeofkwekgfwktokwt9wt293430592304"); + httpp_headers_add(res->headers, "SOmethin", "afkofkeokfoekfo"); + httpp_res_set_body(res, "{\"hello\": 123}\n"); + + char* raw = httpp_res_to_raw(res); + printf("-----------\n"); + printf("%s", raw); + printf("-----------\n"); + return 0; } \ No newline at end of file