#include #include #include #include static const int HTTP_METHOD_GET = (1 << 0); static const int HTTP_METHOD_POST = (1 << 1); static const int HTTP_METHOD_PUT = (1 << 2); static const int HTTP_METHOD_DELETE = (1 << 3); static const int HTTP_METHOD_OPTIONS = (1 << 4); static endpoint_t endpoints[ENDPOINTS_MAX_COUNT]; static endpoint_t endpoint_index; static endpoint_t endpoint_not_found; static int endpoints_registered = 0; static const char delimiter[2] = "/"; static void endpoint_index_func(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm) { (void)args; mg_send_head(c, 200, hm->body.len, "Content-Type: text/plain"); //mg_printf(c, "%.*s", (int)hm->message.len, hm->message.p); mg_printf(c, "%.*s", (int)hm->body.len, hm->body.p); } static void endpoint_not_found_func(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm) { (void)args; mg_send_head(c, 404, hm->body.len, "Content-Type: text/plain"); //mg_printf(c, "%.*s", (int)hm->message.len, hm->message.p); mg_printf(c, "%.*s", (int)hm->body.len, hm->body.p); } void router_init() { // add index endpoint endpoint_index.route = NULL; endpoint_index.func = endpoint_index_func; endpoint_index.methods = 0; endpoint_index.args_count = 0; endpoint_index.args = NULL; // add 404 endpoint endpoint_not_found.route = NULL; endpoint_not_found.func = endpoint_not_found_func; endpoint_not_found.methods = 0; endpoint_not_found.args_count = 0; endpoint_not_found.args = NULL; router_register_endpoint("/api/v1/schedules/", HTTP_METHOD_GET, api_v1_schedules_GET); router_register_endpoint("/api/v1/schedules/", HTTP_METHOD_POST, api_v1_schedules_POST); router_register_endpoint("/api/v1/schedules/{str}/", HTTP_METHOD_GET, api_v1_schedules_STR_GET); } void router_register_endpoint(const char *route, int methods, endpoint_func_f func) { if(endpoints_registered >= ENDPOINTS_MAX_COUNT) { LOG_ERROR("can't register more than %d endpoints\n", ENDPOINTS_MAX_COUNT); return; } endpoint_t *endpoint = &endpoints[endpoints_registered]; int route_parts_count = 1; size_t route_len = strlen(route); for(size_t i = 0; i < route_len; ++i) { if(route[i] == delimiter[0] || route[i] == '\0') { ++route_parts_count; } } // +1 for NULL terminator endpoint->route = malloc(sizeof(char*) * (route_parts_count + 1)); endpoint->route_keeper = malloc(sizeof(char) * (route_len + 1)); strncpy(endpoint->route_keeper, route, route_len); endpoint->route_keeper[route_len] = '\0'; int route_part = 0; int route_args_count = 0; char *route_token = strtok(endpoint->route_keeper, delimiter); while(route_token) { if(strcmp(route_token, "{int}") == 0) { ++route_args_count; } if(strcmp(route_token, "{str}") == 0) { ++route_args_count; } endpoint->route[route_part] = route_token; ++route_part; route_token = strtok(NULL, delimiter); } endpoint->route[route_part] = NULL; endpoint->args_count = route_args_count; endpoint->args = NULL; if(route_args_count) { endpoint->args = malloc(sizeof(endpoint_args_t) * route_args_count); } endpoint->func = func; endpoint->methods = methods; ++endpoints_registered; } static int get_method_int_for_str(struct mg_str *method_str) { if(strncmp(method_str->p, "GET", method_str->len) == 0) { return HTTP_METHOD_GET; } if(strncmp(method_str->p, "POST", method_str->len) == 0) { return HTTP_METHOD_POST; } if(strncmp(method_str->p, "PUT", method_str->len) == 0) { return HTTP_METHOD_PUT; } if(strncmp(method_str->p, "DELETE", method_str->len) == 0) { return HTTP_METHOD_DELETE; } if(strncmp(method_str->p, "OPTIONS", method_str->len) == 0) { return HTTP_METHOD_OPTIONS; } return HTTP_METHOD_GET; } endpoint_t* router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_str) { (void)uri_str; (void)uri_len; char *uri = malloc(sizeof(char) * (uri_len + 1)); strncpy(uri, uri_str, uri_len); uri[uri_len] = '\0'; int method = get_method_int_for_str(method_str); for(int i = 0; i < endpoints_registered; ++i) { if(endpoints[i].methods & method) { endpoints[i].possible_route = 1; } else { endpoints[i].possible_route = 0; } endpoints[i].args_found = 0; } char *uri_token; char *uri_token_save; uri_token = strtok_r(uri, delimiter, &uri_token_save); int route_part = 0; if(!uri_token) { free(uri); return &endpoint_index; } while(uri_token) { for(int i = 0; i < endpoints_registered; ++i) { if(!endpoints[i].possible_route) { continue; } if(!endpoints[i].route[route_part]) { endpoints[i].possible_route = 0; continue; } if(uri_token_save[0] == '\0' && endpoints[i].route[route_part + 1]) { endpoints[i].possible_route = 0; continue; } if(strcmp(endpoints[i].route[route_part], uri_token) == 0) { endpoints[i].possible_route += 3; continue; } if(strcmp(endpoints[i].route[route_part], "{int}") == 0) { char *endptr; errno = 0; int found_arg_int = strtol(uri_token, &endptr, 10); if(errno || (endptr && *endptr != '\0')) { endpoints[i].possible_route = 0; continue; } endpoints[i].possible_route += 2; endpoints[i].args[endpoints[i].args_found].type = ENDPOINT_ARG_TYPE_INT; endpoints[i].args[endpoints[i].args_found].value.v_int = found_arg_int; ++endpoints[i].args_found; continue; } if(strcmp(endpoints[i].route[route_part], "{str}") == 0) { endpoints[i].possible_route += 1; endpoints[i].args[endpoints[i].args_found].type = ENDPOINT_ARG_TYPE_STR; endpoints[i].args[endpoints[i].args_found].value.v_str = uri_token; ++endpoints[i].args_found; continue; } endpoints[i].possible_route = 0; continue; } uri_token = strtok_r(NULL, delimiter, &uri_token_save); ++route_part; } endpoint_t *best_endpoint = &endpoint_not_found; for(int i = 0; i < endpoints_registered; ++i) { int rating = endpoints[i].possible_route; if(rating > best_endpoint->possible_route) { best_endpoint = &endpoints[i]; } } for(int i = 0; i < best_endpoint->args_count; ++i) { if(best_endpoint->args[i].type == ENDPOINT_ARG_TYPE_STR) { char *arg_value_str = malloc(sizeof(char) * (strlen(best_endpoint->args[i].value.v_str) + 1)); strcpy(arg_value_str, best_endpoint->args[i].value.v_str); best_endpoint->args[i].value.v_str = arg_value_str; } } free(uri); return best_endpoint; } void router_free() { for(int i = 0; i < endpoints_registered; ++i) { free(endpoints[i].route_keeper); free(endpoints[i].route); if(endpoints[i].args) { free(endpoints[i].args); } } }