httpp-benchmark

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

commit ff43950f859a30dad9dc41c2cf1a5838b3af8173
parent 473d1545056679d6600351e7576902718bde2821
Author: cebem1nt <mineewarik@gmail.com>
Date:   Wed, 19 Nov 2025 00:38:02 -0300

Initial things, needs a lot of improvements and updates

Diffstat:
A.gitignore | 2++
AREADME.md | 4++++
Ahttpp.h | 38++++++++++++++++++++++++++++++++++++++
Aimp.c | 193+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 237 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1 @@ +*.out +\ No newline at end of file diff --git a/README.md b/README.md @@ -0,0 +1,3 @@ +# httpp + +WIP +\ No newline at end of file diff --git a/httpp.h b/httpp.h @@ -0,0 +1,37 @@ +/* + * Tiny header only http parser library for + * http 1/1 version + */ + +#include <stddef.h> + +typedef enum { + GET, + POST, + DELETE +} http_method_t; + +typedef struct { + char* name; + char* body; +} http_header_t; + +typedef struct { + http_header_t* arr; + size_t capacity; + size_t length; +} http_headers_arr_t; + +typedef struct { + http_method_t method; + http_headers_arr_t* headers; + char* route; + char* body; +} http_req_t; + +http_req_t* http_parse_request(char* raw); + +#ifdef LIBPTTH_IMPLEMENTATION + + +#endif // LIBPTTH_IMPLEMENTATION +\ No newline at end of file diff --git a/imp.c b/imp.c @@ -0,0 +1,192 @@ +#include <assert.h> +#include <ctype.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "httpp.h" + +#define HTTP_DELIMETER "\r\n" +#define HTTP_DELIMETER_SIZE 2 +#define HTTP_VERSION "HTTP/1.1" + +#define LINE_BUFSIZE 4096 +#define INITIAL_HEADERS_ARR_CAP 20 + +#define MAX_ROUTE_LENGTH 100 /* Can and should be adjusted */ +#define MAX_METHOD_LENGTH 10 + 1 +#define VERISON_BUFSIZE 10 + 1 + +#define http_string_to_method(s) (strcmp(s, "GET") == 0 ? 0 : \ + strcmp(s, "POST") == 0 ? 1 : \ + strcmp(s, "DELETE") == 0 ? 2 : -1) + +#define nomem() do { fprintf(stderr, "No memory, see ya!\n"); exit(1); } while (0) +#define ltrim(str) while(*(str) && isspace(*(str))) { (str)++; } + +static inline void* emalloc(size_t size) +{ + void* ptr = malloc(size); + if (!ptr) + nomem(); + + return ptr; +} + +static inline void* erealloc(void* ptr, size_t size) +{ + void* new_ptr = realloc(ptr, size); + if (!ptr) + nomem(); + + return ptr; +} + +static void chop_until(char c, char** src, char* dest, size_t dest_len) +{ + char* pos = strchr(*src, c); + if (!pos) + return; + + size_t chopped_size = pos - *src; + if (chopped_size >= dest_len) + return; + + memcpy(dest, *src, chopped_size); + dest[chopped_size] = '\0'; + + *src = pos + 1; +} + +const char* http_method_to_string(http_method_t m) +{ + switch (m) { + case GET: return "GET"; + case POST: return "POST"; + case DELETE: return "DELETE"; + } +} + +http_req_t* http_req_new() +{ + http_req_t* new = emalloc(sizeof(http_req_t)); + + new->headers = emalloc(sizeof(http_headers_arr_t)); + new->headers->arr = emalloc(sizeof(http_header_t) * INITIAL_HEADERS_ARR_CAP); + new->headers->capacity = INITIAL_HEADERS_ARR_CAP; + new->headers->length = 0; + + return new; +} + +void http_req_arr_append(http_headers_arr_t* hs, http_header_t header) +{ + if (hs->length >= hs->capacity) { + size_t new_cap = (hs->capacity * 2) * sizeof(http_header_t); + hs->arr = erealloc(hs->arr, new_cap); + hs->capacity = new_cap; + } + + hs->arr[hs->length++] = header; +} + +void http_parse_header(http_req_t* req, char* line) +{ + char* delim_pos = strchr(line, ':'); + if (!delim_pos) + return; + + size_t name_len = (delim_pos - line); + if (name_len == 0) + return; + + char* name = emalloc(name_len + 1); + memcpy(name, line, name_len); + name[name_len] = '\0'; + + char* body_start = delim_pos + 1; + ltrim(body_start); + + char* body = strdup(body_start); + if (!body) + nomem(); + + http_header_t parsed = {name, body}; + http_req_arr_append(req->headers, parsed); +} + +void http_parse_start_line(char** itr, http_req_t* dest) +{ + char method_buf[MAX_METHOD_LENGTH]; + char route_buf[MAX_ROUTE_LENGTH]; + char version_buf[VERISON_BUFSIZE]; + + ltrim(*itr); + + chop_until(' ', itr, method_buf, MAX_METHOD_LENGTH); + chop_until(' ', itr, route_buf, MAX_ROUTE_LENGTH); + chop_until('\r', itr, version_buf, VERISON_BUFSIZE); + + dest->method = http_string_to_method(method_buf); + dest->route = strdup(route_buf); // Hmmmm + + assert(strcmp(version_buf, HTTP_VERSION) == 0 && "Unsupported protocol version"); + ltrim(*itr); // remove \n left after version +} + +http_req_t* http_parse_request(char* raw) +{ + http_req_t* out = http_req_new(); + + char* itr = raw; + char* end = raw + strlen(raw); + char line[LINE_BUFSIZE]; + + http_parse_start_line(&itr, out); + + while (itr < end) { + char* del_pos = strstr(itr, HTTP_DELIMETER); + if (!del_pos) + break; + + size_t line_size = del_pos - itr; + + if (line_size >= LINE_BUFSIZE) + return NULL; // Idk, inform user? + + memcpy(line, itr, line_size); + line[line_size] = '\0'; + + http_parse_header(out, line); + itr = del_pos + HTTP_DELIMETER_SIZE; + } + + return out; +} + +int main() +{ + char* req = + "POST /api/items HTTP/1.1\r\n" + "Host: api.example.com\r\n" + "User-Agent: MyClient/1.0\r\n" + "Content-Type: application/json\r\n" + "Content-Length: 48\r\n" + "\r\n" + "{\"name\":\"Widget\",\"quantity\":10,\"price\":9.99}"; + + http_req_t* parsed = http_parse_request(req); + + printf("Method: %i (%s)\n", parsed->method, http_method_to_string(parsed->method)); + printf("Route: %s\n", parsed->route); + + printf("Parsed headers length: %lu\n", parsed->headers->length); + for (size_t i = 0; i < parsed->headers->length; i++) { + printf("Header %lu:\n", i); + printf(" Name: %s\n", parsed->headers->arr[i].name); + printf(" Body: %s\n", parsed->headers->arr[i].body); + } + + return 0; +} +\ No newline at end of file