#include <string.h> #include <status.h> #include <macros.h> #include <constants.h> #include <mongoose.h> #include <logger.h> #include <router.h> #include <handlers.h> #define HEADERS_FMT "Content-Type: %s" // -2 for "%s" -1 for \0 #define HEADERS_FMT_LEN (sizeof(HEADERS_FMT) - 3) static char* add_extra_headers(char *extra_headers) { char *result; size_t std_headers_len = strlen(global_config->http_server_opts.extra_headers); if(extra_headers == NULL) { result = malloc(sizeof(char) * (std_headers_len + 1)); strcpy(result, global_config->http_server_opts.extra_headers); result[std_headers_len] = '\0'; return result; } result = malloc(sizeof(char) * (std_headers_len + strlen(extra_headers) + 3)); sprintf(result, "%s\r\n%s", global_config->http_server_opts.extra_headers, extra_headers); return result; } static void send_response(struct mg_connection *nc, endpoint_response_t *response) { if(response->status_code) { char *response_headers = malloc(sizeof(char) * (HEADERS_FMT_LEN + strlen(response->content_type) + 1)); sprintf(response_headers, HEADERS_FMT, response->content_type); char *extra_headers = add_extra_headers(response_headers); mg_send_head(nc, response->status_code, response->content_length, extra_headers); mg_printf(nc, "%s", response->content); free(response_headers); free(extra_headers); } } static void handle_websocket_request(struct mg_connection *nc, struct http_message *hm) { struct mg_str method_websocket_str = mg_mk_str("WEBSOCKET"); endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &method_websocket_str); if(!endpoint || !endpoint->func) { nc->flags |= MG_F_CLOSE_IMMEDIATELY; } else { endpoint->func(nc, hm, endpoint->args, NULL); for(int i = 0; i < endpoint->args_count; ++i) { if(endpoint->args[i].type == ENDPOINT_ARG_TYPE_STR) { free((char*)endpoint->args[i].value.v_str); } } } } static void handle_http_request(struct mg_connection *nc, struct http_message *hm) { endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &hm->method); endpoint_response_t response; response.content = NULL; response.alloced_content = false; M_RESPONSE_MSG(LOGGER_NONE, &response, 500, "server did not create a response"); if(!endpoint) { /* Normalize path - resolve "." and ".." (in-place). */ if (!mg_normalize_uri_path(&hm->uri, &hm->uri)) { mg_http_send_error(nc, 400, global_config->http_server_opts.extra_headers); LOGGER_DEBUG("failed to normalize uri %.*s\n", hm->uri.len, hm->uri.p); endpoint_response_free_content(&response); return; } LOGGER_DEBUG("no endpoint found - serving file\n"); char *request_file_org = malloc(sizeof(char) * hm->uri.len); strncpy(request_file_org, hm->uri.p + 1, hm->uri.len); request_file_org[hm->uri.len - 1] = '\0'; char *request_file = request_file_org; while(request_file[0] == '/') { ++request_file; } char *request_file_path = malloc(sizeof(char) * (strlen(request_file) + strlen(global_config->content_dir) + 2)); sprintf(request_file_path, "%s/%s", global_config->content_dir, request_file); int access_result = access(request_file_path, R_OK); free(request_file_path); free(request_file_org); if(access_result != -1) { response.status_code = 0; mg_serve_http(nc, hm, global_config->http_server_opts); LOGGER_DEBUG("serving %.*s\n", hm->uri.len, hm->uri.p); endpoint_response_free_content(&response); return; } LOGGER_DEBUG("serving 'not found'\n"); endpoint = router_get_not_found_endpoint(); } if(endpoint->method == HTTP_METHOD_OPTIONS) { LOGGER_DEBUG("sending options for %s\n", endpoint->full_route); char options_header[128]; sprintf(options_header, "Allow: OPTIONS%s%s%s%s", endpoint->options & HTTP_METHOD_GET ? ", GET" : "", endpoint->options & HTTP_METHOD_POST ? ", POST" : "", endpoint->options & HTTP_METHOD_PUT ? ", PUT" : "", endpoint->options & HTTP_METHOD_DELETE ? ", DELETE" : "" ); char *extra_headers = add_extra_headers(options_header); mg_send_head(nc, 204, 0, extra_headers); free(extra_headers); } else { LOGGER_DEBUG("calling endpoint function for route %s\n", endpoint->full_route); endpoint->func(nc, hm, endpoint->args, &response); char addr[32]; mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP); LOGGER_DEBUG("sending response to %s\n", addr); send_response(nc, &response); } endpoint_response_free_content(&response); LOGGER_DEBUG("freeing endpoint args\n"); for(int i = 0; i < endpoint->args_count; ++i) { if(endpoint->args[i].type == ENDPOINT_ARG_TYPE_STR) { free((char*)endpoint->args[i].value.v_str); } } } void handler_http(struct mg_connection *nc, int ev, void *p) { if(ev == MG_EV_WEBSOCKET_HANDSHAKE_REQUEST) { struct http_message *hm = (struct http_message*)p; handle_websocket_request(nc, hm); } if(ev == MG_EV_WEBSOCKET_HANDSHAKE_DONE) { status_send(nc); } if(ev == MG_EV_HTTP_REQUEST) { char addr[32]; mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr), MG_SOCK_STRINGIFY_IP); struct http_message *hm = (struct http_message*)p; LOGGER_DEBUG("======================================\n"); LOGGER_DEBUG("new http %.*s request from %s for %.*s\n", hm->method.len, hm->method.p, addr, hm->uri.len, hm->uri.p); handle_http_request(nc, hm); } }