#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); if (sscanf(line, "%ms %ms 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; }