summaryrefslogtreecommitdiff
path: root/src/http.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/http.c')
-rw-r--r--src/http.c203
1 files changed, 203 insertions, 0 deletions
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 <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <cjson/cJSON.h>
+#include <stdio.h>
+
+#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;
+}