#include #include #include #include #include #include #include #include "event.h" #include "http.h" #include "list.h" #include "logging.h" #include "sockaddr.h" #include "tcpsocket.h" static LIST_HEAD(http_cb_list); static void destroy_http_con(struct http_con *con) { close(event_get_fd(con->event)); event_remove_fd(con->event); if (con->req_data != NULL) free(con->req_data); if (con->req_headers != NULL) free(con->req_headers); if (con->req_args != NULL) free(con->req_args); free(con); } int http_send_header(struct http_con *con, char *code, char *type) { char buf[256]; int len = snprintf(buf, sizeof(buf), "HTTP/1.0 %s\r\nContent-Type: %s\r\nConnection: close\r\n\r\n", code, type); write(con->fd, buf, len); return 0; } int http_send_error(struct http_con *con, char *code, char *msg) { http_send_header(con, code, "text/plain"); write(con->fd, msg, strlen(msg)); return 0; } static int parse_headers(struct http_con *con) { char *data = con->req_data; while ((data = strstr(data, "\r\n")) && data < con->req_data + con->req_size) { con->req_header_cnt++; data += 2; } con->req_headers = malloc(con->req_header_cnt * sizeof(char *)); if (con->req_headers == NULL) return -1; int i = 0; data = con->req_data; do { con->req_headers[i++] = data; data = strstr(data, "\r\n"); if (data == NULL) break; *data++ = '\0'; *data++ = '\0'; } while (data < con->req_data + con->req_size); return 0; } static int parse_args(struct http_con *con) { /* seperate GET (in req->data) */ char *req = strchr(con->req_data, ' '); *req++ = '\0'; /* HTTP/1.1 will go to header[0] */ char *tmp = strchr(req, ' '); *tmp++ = '\0'; con->req_headers[0] = tmp; /* count args */ tmp = req; while (*tmp != '\0' && tmp < con->req_data + con->req_size) { if (*tmp == '?' || *tmp == '&') con->req_arg_cnt++; tmp++; } con->req_arg_cnt++; /* alloc args */ con->req_args = malloc(con->req_arg_cnt * sizeof(char *)); if (con->req_args == NULL) return -1; int i = 0; tmp = req; con->req_args[i++] = tmp; while (*tmp != '\0' && tmp < con->req_data + con->req_size) { if (*tmp == '?' || *tmp == '&') { *tmp++ = '\0'; con->req_args[i++] = tmp; } tmp++; } return 0; } static int http_content_handler(int fd, void *privdata) { struct http_con *con = (struct http_con *)privdata; if (ioctl(fd, FIONREAD, &con->req_size) == -1) { log_print(LOG_WARN, "http_content_handler(): ioctl(FIONREAD)"); destroy_http_con(con); return -1; } con->req_data = malloc(con->req_size); if (con->req_data == NULL) { log_print(LOG_WARN, "http_content_handler(): out of memory"); destroy_http_con(con); return -1; } con->req_size = read(fd, con->req_data, con->req_size); if (con->req_size <= 0) { log_print(LOG_WARN, "http_content_handler(): read()"); destroy_http_con(con); return -1; } if (strncmp(con->req_data, "GET ", 4) != 0) { http_send_error(con, "400 Invalid Request", "Not a GET request\r\n"); destroy_http_con(con); return -1; } if (parse_headers(con) < 0) { http_send_error(con, "400 Invalid Request", "Error parsing headers\r\n"); destroy_http_con(con); return -1; } if (parse_args(con) < 0) { http_send_error(con, "400 Invalid Request", "Error parsing arguments\r\n"); destroy_http_con(con); return -1; } struct http_callback *entry; list_for_each_entry(entry, &http_cb_list, list) { if ((entry->wildcard == 1 && strncmp(entry->name, con->req_args[0], strlen(entry->name)) == 0) || (entry->wildcard == 0 && strcmp(entry->name, con->req_args[0]) == 0)) { entry->callback(con, entry->privdata); destroy_http_con(con); return 0; } } http_send_error(con, "404 Not Found", "File not found\r\n"); destroy_http_con(con); return 0; } int http_listen_handler(int fd, void *privdata) { struct http_con *con = malloc(sizeof(struct http_con)); if (con == NULL) { log_print(LOG_WARN, "http_listen_handler(): out of memory"); return 0; } memset(con, 0, sizeof(struct http_con)); unsigned int i = sizeof(con->addr); con->fd = accept(fd, (struct sockaddr *)&con->addr, &i); if (con->fd < 0) { free(con); return 0; } con->event = event_add_readfd(NULL, con->fd, http_content_handler, con); return 0; } struct http_callback * http_add_cb(const char *name, int wildcard, int (* callback)(struct http_con *con, void *privdata), void *privdata) { struct http_callback *hcb = malloc(sizeof(struct http_callback)); if (hcb == NULL) { log_print(LOG_WARN, "http_create_cb(): out of memory"); return NULL; } hcb->name = strdup(name); hcb->wildcard = wildcard; hcb->callback = callback; hcb->privdata = privdata; list_add_tail(&hcb->list, &http_cb_list); return hcb; } int http_remove_cb(struct http_callback *hcb) { list_del(&hcb->list); free(hcb->name); free(hcb); return 0; } int http_file_cb(struct http_con *con, void *privdata) { log_print(LOG_DEBUG, "req: %s", con->req_args[0]); http_send_header(con, "500 NOT OK", "text/plain"); return 0; }