From 7a388dad85152a203033c14fee3c64607301865a Mon Sep 17 00:00:00 2001 From: "Anna (navi) Figueiredo Gomes" Date: Sat, 8 Jul 2023 15:06:33 -0300 Subject: libactivity: (tmp name) http request parse the parse works via callbacks, registered to a method and path. further work will be done to simplify extraction of path, and to make the request struct more private. missing the parsing and handling of reponses. Signed-off-by: Anna (navi) Figueiredo Gomes --- src/http.c | 203 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 src/http.c (limited to 'src/http.c') diff --git a/src/http.c b/src/http.c new file mode 100644 index 0000000..793f257 --- /dev/null +++ b/src/http.c @@ -0,0 +1,203 @@ +#include +#include +#include +#include +#include + +#include "http.h" +#include "util.h" +#include "buffer.h" + +struct http { + struct request_handler *requests; + size_t req_siz, n_requests; +}; + +struct request_handler { + char *method; + char *path; + void *data; + request_callback *callback; +}; + +struct header_item { + char *key; + char *value; + struct header_item *next; +}; + +bool parse_query(char *path, char **query) { + char *sep = strchr(path, '?'); + if (!sep) { + *query = NULL; + return false; + } + + *sep = '\0'; + *query = strdup(sep + 1); + + return true; +} + +void add_header(struct header_list *list, char *header, char *value) { + size_t index = hash(header, MAX_HEADERS); + struct header_item to_insert = {0}; + struct header_item *item = NULL; + to_insert.key = strdup(header); + to_insert.value = strdup(value); + + if (!list->items[index]) { + list->items[index] = malloc(sizeof(struct header_item)); + *(list->items[index]) = to_insert; + } else { + item = list->items[index]; + while (item->next != NULL) { + item = item->next; + } + item->next = malloc(sizeof(struct header_item)); + *(item->next) = to_insert; + } +} + +const char *http_get_header(struct request *req, const char *key) { + size_t index = hash(key, MAX_HEADERS); + + struct header_item *item = req->header.items[index]; + + while (item != NULL) { + if (strcmp(item->key, key) == 0) { + return item->value; + } + item = item->next; + } + + return NULL; +} + +enum parse_result { + PARSE_OK, + PARSE_ERR_UNSUPPORTED_HTTP_VERSION, + PARSE_MALFORMED_INPUT +}; + +static enum parse_result parse_request(struct request *req, const char *request) { + assert(req); + assert(request); + + char *copy = strdup(request); + char *saveptr, *line; + char *path, *query; + char *header; + + char *data = strstr(copy, "\r\n\r\n"); + if (data) { + *data = '\0'; + data += 4; + } + + line = strtok_r(copy, "\r\n", &saveptr); + + path = malloc(strlen(line) + 1); + req->method = malloc(strlen(line) + 1); + + if (sscanf(line, "%s %s HTTP/%s", req->method, path, req->http_version) != 3) { + free(copy); + free(path); + free(req->method); + return PARSE_MALFORMED_INPUT; + } + + if (parse_query(path, &query)) { + req->path = path; + req->query = query; + } else { + req->path = path; + req->query = strdup(""); + } + + for (line = strtok_r(NULL, "\r\n", &saveptr); + line != NULL && strlen(line) > 0; + line = strtok_r(NULL, "\r\n", &saveptr)) { + + header = strchr(line, ':'); + if (!header) + continue; + *(header++) = '\0'; + while (*header == ' ') + header++; + add_header(&req->header, line, header); + } + + if (data) + req->data = strdup(data); + + return PARSE_OK; +} + +struct buffer *http_build_reply(struct reply *reply) { + assert(reply); + + struct buffer *buf = buf_new(NULL); + + buf_printf(buf, + "HTTP/1.1 %d %s\r\n" + "Content-Length: %ld\r\n" + "Content-Type: %s\r\n" + "\r\n" + "%s", + reply->status, "OK", strlen(reply->body), "application/json", reply->body); + + return buf; +} + +struct reply *http_handle_request(struct http *http, struct buffer *req) { + struct request data = {0}; + struct reply *ret = NULL; + + if (parse_request(&data, req->data) != PARSE_OK) { + // todo: log + return NULL; + } + + for (size_t i = 0; i < http->n_requests; i++) { + if (strcmp(data.method, http->requests[i].method) == 0 && + strcmp(data.path, http->requests[i].path) == 0) { + ret = http->requests[i].callback(&data, http->requests[i].data); + } + } + + if (!ret) { + ret = malloc(sizeof(struct reply)); + ret->status = 404; + ret->body = strdup(""); + } + + return ret; +} + +struct http *http_init(void) { + struct http *http = malloc(sizeof(struct http)); + http->requests = malloc(sizeof(struct request_handler) * 5); + http->n_requests = 0; + http->req_siz = 5; + return http; +} + +bool http_register_handler(struct http *http, char *method, char *path, + void *data, request_callback *callback) { + if (http->n_requests + 1 >= http->req_siz) { + http->req_siz += 10; + http->requests = realloc(http->requests, + sizeof(struct request_handler) * http->req_siz); + memset(http->requests, 0, + sizeof(struct request_handler) * (http->req_siz - http->n_requests)); + } + struct request_handler *handle = &http->requests[http->n_requests++]; + + handle->method = strdup(method); + handle->path = strdup(path); + handle->data = data; + handle->callback = callback; + + return true; +} -- cgit v1.2.3