core-legacy/router.c
2020-05-06 22:49:22 +02:00

292 lines
9 KiB
C

#include <string.h>
#include <logger.h>
#include <router.h>
#include <endpoints/api_v1_schedules.h>
#include <endpoints/api_v1_controllers.h>
#include <endpoints/api_v1_relays.h>
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;
(void)hm;
mg_send_head(c, 404, 9, "Content-Type: text/plain");
mg_printf(c, "not found");
}
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);
router_register_endpoint("/api/v1/schedules/{str}/", HTTP_METHOD_PUT, api_v1_schedules_STR_PUT);
router_register_endpoint("/api/v1/schedules/{str}/", HTTP_METHOD_DELETE, api_v1_schedules_STR_DELETE);
router_register_endpoint("/api/v1/schedules/tag/{str}/", HTTP_METHOD_GET, api_v1_schedules_tag_STR_GET);
router_register_endpoint("/api/v1/controllers/discover/", HTTP_METHOD_POST, api_v1_controllers_discover_POST);
router_register_endpoint("/api/v1/controllers/", HTTP_METHOD_GET, api_v1_controllers_GET);
router_register_endpoint("/api/v1/controllers/{str}", HTTP_METHOD_GET, api_v1_controllers_STR_GET);
router_register_endpoint("/api/v1/controllers/{str}", HTTP_METHOD_PUT, api_v1_controllers_STR_PUT);
router_register_endpoint("/api/v1/controllers/{str}", HTTP_METHOD_DELETE, api_v1_controllers_STR_DELETE);
router_register_endpoint("/api/v1/controllers/{str}/relays/", HTTP_METHOD_GET, api_v1_controllers_STR_relays_GET);
router_register_endpoint("/api/v1/controllers/{str}/relays/{int}", HTTP_METHOD_GET, api_v1_controllers_STR_relays_INT_GET);
router_register_endpoint("/api/v1/controllers/{str}/relays/{int}", HTTP_METHOD_PUT, api_v1_controllers_STR_relays_INT_PUT);
router_register_endpoint("/api/v1/relays/", HTTP_METHOD_GET, api_v1_relays_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);
}
}
}