From 420bdeb2aacc60745fd65dd510a936c6679c107e Mon Sep 17 00:00:00 2001 From: Tobias Reisinger Date: Thu, 14 May 2020 01:37:08 +0200 Subject: [PATCH] add: OPTIONS method support add: relay tag endpoint --- endpoints/api_v1_relays_tag_STR.c | 57 +++++++++++++ ..._tags_STR.c => api_v1_schedules_tag_STR.c} | 0 handlers/connection.c | 19 ++++- include/endpoints/api_v1_relays.h | 3 + include/router.h | 22 +++-- router.c | 83 ++++++++++++++----- 6 files changed, 158 insertions(+), 26 deletions(-) create mode 100644 endpoints/api_v1_relays_tag_STR.c rename endpoints/{api_v1_schedules_tags_STR.c => api_v1_schedules_tag_STR.c} (100%) diff --git a/endpoints/api_v1_relays_tag_STR.c b/endpoints/api_v1_relays_tag_STR.c new file mode 100644 index 0000000..223bcfd --- /dev/null +++ b/endpoints/api_v1_relays_tag_STR.c @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include +#include +#include + +void +api_v1_relays_tag_STR_GET(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm) +{ + (void)hm; + + int tag_id = tag_get_id(args[0].value.v_str); + int *relays_ids = junction_tag_get_relays_for_tag_id(tag_id); + if(relays_ids == NULL) + { + LOG_ERROR("failed to print relays json\n"); + mg_send_head(c, 500, 2, "Content-Type: application/json\r\n" STANDARD_HEADERS); + mg_printf(c, "[]"); + return; + } + + cJSON *json = cJSON_CreateArray(); + + for(int i = 0; relays_ids[i] != 0; ++i) + { + relay_t* relay = relay_get_by_id(relays_ids[i]); + + if(!relay) + { + continue; + } + cJSON *json_relay = relay_to_json(relay); + + cJSON_AddItemToArray(json, json_relay); + + relay_free(relay); + } + + char *json_str = cJSON_Print(json); + if (json_str == NULL) + { + LOG_ERROR("failed to print relays json\n"); + mg_send_head(c, 500, 2, "Content-Type: application/json\r\n" STANDARD_HEADERS); + mg_printf(c, "[]"); + } + else + { + mg_send_head(c, 200, strlen(json_str), "Content-Type: application/json\r\n" STANDARD_HEADERS); + mg_printf(c, "%s", json_str); + free(json_str); + } + + cJSON_Delete(json); + free(relays_ids); +} diff --git a/endpoints/api_v1_schedules_tags_STR.c b/endpoints/api_v1_schedules_tag_STR.c similarity index 100% rename from endpoints/api_v1_schedules_tags_STR.c rename to endpoints/api_v1_schedules_tag_STR.c diff --git a/handlers/connection.c b/handlers/connection.c index af949cc..58fa9c3 100644 --- a/handlers/connection.c +++ b/handlers/connection.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -13,7 +14,7 @@ handler_connection(struct mg_connection *c, int ev, void *p) endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &hm->method); - if(endpoint && endpoint->func) + if(endpoint) { if(endpoint->func) { @@ -29,7 +30,21 @@ handler_connection(struct mg_connection *c, int ev, void *p) } else { - mg_send_head(c, 501, 0, "Content-Type: text/plain"); + if(endpoint->method == HTTP_METHOD_OPTIONS) + { + char options_header[256]; // TODO make more generic + sprintf(options_header, "Allow: OPTIONS%s%s%s%s\r\n" STANDARD_HEADERS, + endpoint->options & HTTP_METHOD_GET ? ", GET" : "", + endpoint->options & HTTP_METHOD_POST ? ", POST" : "", + endpoint->options & HTTP_METHOD_PUT ? ", PUT" : "", + endpoint->options & HTTP_METHOD_DELETE ? ", DELETE" : "" + ); + mg_send_head(c, 204, 0, options_header); + } + else + { + mg_send_head(c, 501, 0, "Content-Type: text/plain"); + } } } else diff --git a/include/endpoints/api_v1_relays.h b/include/endpoints/api_v1_relays.h index cc1dd7a..5a66822 100644 --- a/include/endpoints/api_v1_relays.h +++ b/include/endpoints/api_v1_relays.h @@ -6,4 +6,7 @@ void api_v1_relays_GET(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm); +void +api_v1_relays_tag_STR_GET(struct mg_connection *c, endpoint_args_t *args, struct http_message *hm); + #endif /* CORE_ENDPOINTS_API_V1_RELAYS_H */ diff --git a/include/router.h b/include/router.h index 5eea413..96cff7a 100644 --- a/include/router.h +++ b/include/router.h @@ -3,7 +3,7 @@ #include -#define ENDPOINTS_MAX_COUNT 16 +#define ENDPOINTS_MAX_COUNT 128 typedef enum { @@ -11,6 +11,15 @@ typedef enum ENDPOINT_ARG_TYPE_STR } endpoint_arg_type_e; +typedef enum +{ + HTTP_METHOD_GET = (1 << 0), + HTTP_METHOD_POST = (1 << 1), + HTTP_METHOD_PUT = (1 << 2), + HTTP_METHOD_DELETE = (1 << 3), + HTTP_METHOD_OPTIONS = (1 << 4) +} http_method_e; + typedef struct { endpoint_arg_type_e type; @@ -25,10 +34,13 @@ typedef void (*endpoint_func_f)(struct mg_connection *c, endpoint_args_t *args, typedef struct { + const char *full_route; char **route; char *route_keeper; - int methods; + int method; + int options; endpoint_func_f func; + int trailing_slash; int args_count; endpoint_args_t *args; @@ -40,11 +52,11 @@ typedef struct void router_init(); -void -router_register_endpoint(const char *route, int methods, endpoint_func_f func); +endpoint_t* +router_register_endpoint(const char *route, int method, endpoint_func_f func); endpoint_t* -router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method); +router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_str); void router_free(); diff --git a/router.c b/router.c index c386c98..997b41d 100644 --- a/router.c +++ b/router.c @@ -7,12 +7,6 @@ #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; @@ -37,29 +31,55 @@ endpoint_not_found_func(struct mg_connection *c, endpoint_args_t *args, struct h mg_printf(c, "not found"); } +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"); + } + return mg_mk_str("GET"); +} + void router_init() { // add index endpoint endpoint_index.route = NULL; endpoint_index.func = endpoint_index_func; - endpoint_index.methods = 0; + endpoint_index.method = 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.method = 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/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); @@ -72,19 +92,38 @@ router_init() 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); + router_register_endpoint("/api/v1/relays/tag/{str}", HTTP_METHOD_GET, api_v1_relays_tag_STR_GET); } -void -router_register_endpoint(const char *route, int methods, endpoint_func_f func) +endpoint_t* +router_register_endpoint(const char *route, int method, endpoint_func_f func) { + 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); + if(options_endpoint == &endpoint_not_found) + { + options_endpoint = router_register_endpoint(route, HTTP_METHOD_OPTIONS, NULL); + } + } + if(endpoints_registered >= ENDPOINTS_MAX_COUNT) { LOG_ERROR("can't register more than %d endpoints\n", ENDPOINTS_MAX_COUNT); - return; + return NULL; } endpoint_t *endpoint = &endpoints[endpoints_registered]; + 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; + } + int route_parts_count = 1; size_t route_len = strlen(route); @@ -130,8 +169,16 @@ router_register_endpoint(const char *route, int methods, endpoint_func_f func) } endpoint->func = func; - endpoint->methods = methods; + endpoint->method = method; + endpoint->options = 0; + + if(options_endpoint) + { + options_endpoint->options |= method; + } + ++endpoints_registered; + return endpoint; } static int @@ -163,8 +210,6 @@ get_method_int_for_str(struct mg_str *method_str) 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'; @@ -173,7 +218,7 @@ router_find_endpoint(const char *uri_str, size_t uri_len, struct mg_str *method_ for(int i = 0; i < endpoints_registered; ++i) { - if(endpoints[i].methods & method) + if(endpoints[i].method & method) { endpoints[i].possible_route = 1; }