2020-05-05 09:42:02 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <logger.h>
|
|
|
|
#include <router.h>
|
2020-05-20 23:33:18 +00:00
|
|
|
#include <macros.h>
|
2020-05-30 22:23:57 +00:00
|
|
|
#include <endpoint.h>
|
2020-06-25 23:01:46 +00:00
|
|
|
#include <status.h>
|
2020-05-05 09:42:02 +00:00
|
|
|
|
|
|
|
#include <endpoints/api_v1_schedules.h>
|
2020-05-05 23:05:36 +00:00
|
|
|
#include <endpoints/api_v1_controllers.h>
|
2020-05-05 23:16:43 +00:00
|
|
|
#include <endpoints/api_v1_relays.h>
|
2020-06-13 17:21:32 +00:00
|
|
|
#include <endpoints/api_v1_tags.h>
|
2020-06-25 23:01:46 +00:00
|
|
|
#include <endpoints/api_v1_ws.h>
|
2020-05-05 09:42:02 +00:00
|
|
|
|
2020-05-30 22:23:57 +00:00
|
|
|
static endpoint_t endpoints[ROUTER_ENDPOINTS_MAX_COUNT];
|
2020-05-05 09:42:02 +00:00
|
|
|
static endpoint_t endpoint_not_found;
|
|
|
|
static int endpoints_registered = 0;
|
|
|
|
static const char delimiter[2] = "/";
|
|
|
|
|
2020-05-13 23:37:08 +00:00
|
|
|
static struct mg_str
|
|
|
|
get_method_str_for_int(int method_int)
|
|
|
|
{
|
|
|
|
if(method_int == HTTP_METHOD_GET)
|
|
|
|
{
|
|
|
|
return mg_mk_str("GET");
|
|
|
|
}
|
|
|
|
if(method_int == HTTP_METHOD_POST)
|
|
|
|
{
|
|
|
|
return mg_mk_str("POST");
|
|
|
|
}
|
|
|
|
if(method_int == HTTP_METHOD_PUT)
|
|
|
|
{
|
|
|
|
return mg_mk_str("PUT");
|
|
|
|
}
|
|
|
|
if(method_int == HTTP_METHOD_DELETE)
|
|
|
|
{
|
|
|
|
return mg_mk_str("DELETE");
|
|
|
|
}
|
|
|
|
if(method_int == HTTP_METHOD_OPTIONS)
|
|
|
|
{
|
|
|
|
return mg_mk_str("OPTIONS");
|
|
|
|
}
|
2020-06-25 23:01:46 +00:00
|
|
|
if(method_int == HTTP_METHOD_WEBSOCKET)
|
|
|
|
{
|
|
|
|
return mg_mk_str("WEBSOCKET");
|
|
|
|
}
|
2020-05-13 23:37:08 +00:00
|
|
|
return mg_mk_str("GET");
|
|
|
|
}
|
|
|
|
|
2020-06-02 22:47:49 +00:00
|
|
|
endpoint_t*
|
|
|
|
router_get_not_found_endpoint()
|
|
|
|
{
|
|
|
|
return &endpoint_not_found;
|
|
|
|
}
|
|
|
|
|
2020-05-05 09:42:02 +00:00
|
|
|
void
|
|
|
|
router_init()
|
|
|
|
{
|
|
|
|
// add 404 endpoint
|
|
|
|
endpoint_not_found.route = NULL;
|
2020-05-30 22:23:57 +00:00
|
|
|
endpoint_not_found.func = endpoint_func_not_found;
|
2020-05-13 23:37:08 +00:00
|
|
|
endpoint_not_found.method = 0;
|
2020-05-05 09:42:02 +00:00
|
|
|
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);
|
2020-06-13 22:14:13 +00:00
|
|
|
router_register_endpoint("/api/v1/schedules/list/", HTTP_METHOD_POST, api_v1_schedules_list_POST);
|
2020-05-13 23:37:08 +00:00
|
|
|
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);
|
2020-05-05 23:05:36 +00:00
|
|
|
|
|
|
|
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);
|
2020-05-06 20:49:22 +00:00
|
|
|
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);
|
2020-06-27 16:31:36 +00:00
|
|
|
router_register_endpoint("/api/v1/controllers/{str}/relays/{int}/pulse", HTTP_METHOD_POST, api_v1_controllers_STR_relays_INT_pulse_POST);
|
2020-05-05 23:16:43 +00:00
|
|
|
|
|
|
|
router_register_endpoint("/api/v1/relays/", HTTP_METHOD_GET, api_v1_relays_GET);
|
2020-05-13 23:37:08 +00:00
|
|
|
router_register_endpoint("/api/v1/relays/tag/{str}", HTTP_METHOD_GET, api_v1_relays_tag_STR_GET);
|
2020-06-13 17:21:32 +00:00
|
|
|
|
|
|
|
router_register_endpoint("/api/v1/tags/", HTTP_METHOD_GET, api_v1_tags_GET);
|
2020-08-11 21:33:04 +00:00
|
|
|
router_register_endpoint("/api/v1/tags/", HTTP_METHOD_POST, api_v1_tags_POST);
|
2020-06-19 14:26:08 +00:00
|
|
|
router_register_endpoint("/api/v1/tags/{str}", HTTP_METHOD_GET, api_v1_tags_STR_GET);
|
|
|
|
router_register_endpoint("/api/v1/tags/{str}", HTTP_METHOD_DELETE, api_v1_tags_STR_DELETE);
|
2020-06-25 23:01:46 +00:00
|
|
|
|
|
|
|
router_register_endpoint("/api/v1/ws/relays", HTTP_METHOD_WEBSOCKET, api_v1_ws_relays);
|
2020-05-05 09:42:02 +00:00
|
|
|
}
|
|
|
|
|
2020-05-13 23:37:08 +00:00
|
|
|
endpoint_t*
|
|
|
|
router_register_endpoint(const char *route, int method, endpoint_func_f func)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
2020-05-13 23:37:08 +00:00
|
|
|
endpoint_t *options_endpoint = NULL;
|
|
|
|
if(method != HTTP_METHOD_OPTIONS)
|
|
|
|
{
|
|
|
|
struct mg_str method_str = get_method_str_for_int(HTTP_METHOD_OPTIONS);
|
|
|
|
options_endpoint = router_find_endpoint(route, strlen(route), &method_str);
|
2020-06-02 22:47:49 +00:00
|
|
|
if(options_endpoint == NULL)
|
2020-05-13 23:37:08 +00:00
|
|
|
{
|
|
|
|
options_endpoint = router_register_endpoint(route, HTTP_METHOD_OPTIONS, NULL);
|
|
|
|
}
|
2020-05-20 23:33:18 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
for(int i = 0; i < options_endpoint->args_count; ++i)
|
|
|
|
{
|
|
|
|
if(options_endpoint->args[i].type == ENDPOINT_ARG_TYPE_STR)
|
|
|
|
{
|
|
|
|
free((char*)options_endpoint->args[i].value.v_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-13 23:37:08 +00:00
|
|
|
}
|
|
|
|
|
2020-05-30 22:23:57 +00:00
|
|
|
if(endpoints_registered >= ROUTER_ENDPOINTS_MAX_COUNT)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
2020-07-26 19:00:05 +00:00
|
|
|
LOGGER_ERR("can't register more than %d endpoints\n", ROUTER_ENDPOINTS_MAX_COUNT);
|
2020-05-13 23:37:08 +00:00
|
|
|
return NULL;
|
2020-05-05 09:42:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
endpoint_t *endpoint = &endpoints[endpoints_registered];
|
|
|
|
|
2020-05-13 23:37:08 +00:00
|
|
|
endpoint->full_route = route;
|
|
|
|
endpoint->trailing_slash = 0; // unused because trailing slashes are optional. TODO make option
|
|
|
|
if(route[strlen(route)] == delimiter[0])
|
|
|
|
{
|
|
|
|
endpoint->trailing_slash = 1;
|
|
|
|
}
|
|
|
|
|
2020-05-05 09:42:02 +00:00
|
|
|
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;
|
2020-05-13 23:37:08 +00:00
|
|
|
endpoint->method = method;
|
|
|
|
endpoint->options = 0;
|
|
|
|
|
|
|
|
if(options_endpoint)
|
|
|
|
{
|
|
|
|
options_endpoint->options |= method;
|
|
|
|
}
|
|
|
|
|
2020-05-05 09:42:02 +00:00
|
|
|
++endpoints_registered;
|
2020-05-13 23:37:08 +00:00
|
|
|
return endpoint;
|
2020-05-05 09:42:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
get_method_int_for_str(struct mg_str *method_str)
|
|
|
|
{
|
2020-06-25 23:01:46 +00:00
|
|
|
if(mg_vcmp(method_str, "GET") == 0)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
|
|
|
return HTTP_METHOD_GET;
|
|
|
|
}
|
2020-06-25 23:01:46 +00:00
|
|
|
if(mg_vcmp(method_str, "POST") == 0)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
|
|
|
return HTTP_METHOD_POST;
|
|
|
|
}
|
2020-06-25 23:01:46 +00:00
|
|
|
if(mg_vcmp(method_str, "PUT") == 0)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
|
|
|
return HTTP_METHOD_PUT;
|
|
|
|
}
|
2020-06-25 23:01:46 +00:00
|
|
|
if(mg_vcmp(method_str, "DELETE") == 0)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
|
|
|
return HTTP_METHOD_DELETE;
|
|
|
|
}
|
2020-06-25 23:01:46 +00:00
|
|
|
if(mg_vcmp(method_str, "OPTIONS") == 0)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
|
|
|
return HTTP_METHOD_OPTIONS;
|
|
|
|
}
|
2020-06-25 23:01:46 +00:00
|
|
|
|
|
|
|
if(mg_vcmp(method_str, "WEBSOCKET") == 0)
|
|
|
|
{
|
|
|
|
return HTTP_METHOD_WEBSOCKET;
|
|
|
|
}
|
2020-05-05 09:42:02 +00:00
|
|
|
return HTTP_METHOD_GET;
|
|
|
|
}
|
|
|
|
|
|
|
|
endpoint_t*
|
|
|
|
router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_str)
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2020-05-13 23:37:08 +00:00
|
|
|
if(endpoints[i].method & method)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
|
|
|
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);
|
2020-06-02 22:47:49 +00:00
|
|
|
return NULL;
|
2020-05-05 09:42:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-05-18 11:52:29 +00:00
|
|
|
if(strcmp(endpoints[i].route[route_part], "{str}") == 0)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
2020-05-18 11:52:29 +00:00
|
|
|
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;
|
2020-05-05 09:42:02 +00:00
|
|
|
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;
|
|
|
|
}
|
2020-05-18 11:52:29 +00:00
|
|
|
if(strcmp(endpoints[i].route[route_part], uri_token) == 0)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
2020-05-18 11:52:29 +00:00
|
|
|
endpoints[i].possible_route += 3;
|
2020-05-05 09:42:02 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
endpoints[i].possible_route = 0;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
uri_token = strtok_r(NULL, delimiter, &uri_token_save);
|
|
|
|
++route_part;
|
|
|
|
}
|
|
|
|
|
2020-06-02 22:47:49 +00:00
|
|
|
int best_rating = 0;
|
|
|
|
endpoint_t *best_endpoint = NULL;
|
2020-05-05 09:42:02 +00:00
|
|
|
for(int i = 0; i < endpoints_registered; ++i)
|
|
|
|
{
|
|
|
|
int rating = endpoints[i].possible_route;
|
2020-06-02 22:47:49 +00:00
|
|
|
if(rating > best_rating)
|
2020-05-05 09:42:02 +00:00
|
|
|
{
|
|
|
|
best_endpoint = &endpoints[i];
|
2020-06-02 22:47:49 +00:00
|
|
|
best_rating = best_endpoint->possible_route;
|
2020-05-05 09:42:02 +00:00
|
|
|
}
|
|
|
|
}
|
2020-06-02 22:47:49 +00:00
|
|
|
|
|
|
|
if(best_endpoint == NULL)
|
|
|
|
{
|
2020-06-13 17:21:32 +00:00
|
|
|
free(uri);
|
2020-06-02 22:47:49 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2020-05-05 09:42:02 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|