add: status for mqtt
fix: refactor connection handlers
This commit is contained in:
parent
2bc11ee829
commit
6c6e5023da
19 changed files with 534 additions and 183 deletions
src/handlers
|
@ -1,134 +0,0 @@
|
|||
#include <string.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);
|
||||
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);
|
||||
|
||||
if(response->alloced_content)
|
||||
{
|
||||
free((char*)response->content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handler_connection(struct mg_connection *nc, int ev, void *p)
|
||||
{
|
||||
if (ev == MG_EV_HTTP_REQUEST)
|
||||
{
|
||||
struct http_message *hm = (struct http_message *) p;
|
||||
LOG_TRACE("new http %.*s request for %.*s\n", hm->method.len, hm->method.p, hm->uri.len, hm->uri.p);
|
||||
|
||||
endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &hm->method);
|
||||
|
||||
endpoint_response_t response;
|
||||
static const char content[] = "the server did not create a response";
|
||||
endpoint_response_text(&response, 500, content, STRLEN(content));
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
LOG_DEBUG("%s\n", request_file);
|
||||
int access_result = access(request_file, R_OK);
|
||||
free(request_file_org);
|
||||
if(access_result != -1)
|
||||
{
|
||||
response.status_code = 0;
|
||||
mg_serve_http(nc, hm, global_config.http_server_opts);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoint = router_get_not_found_endpoint();
|
||||
}
|
||||
}
|
||||
|
||||
if(!endpoint->func)
|
||||
{
|
||||
if(endpoint->method == HTTP_METHOD_OPTIONS)
|
||||
{
|
||||
char options_header[256]; // TODO make more generic
|
||||
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
|
||||
{
|
||||
mg_send_head(nc, 501, 0, "Content-Type: text/plain");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoint->func(nc, hm, endpoint->args, &response);
|
||||
send_response(nc, &response);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
176
src/handlers/http.c
Normal file
176
src/handlers/http.c
Normal file
|
@ -0,0 +1,176 @@
|
|||
#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);
|
||||
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);
|
||||
|
||||
if(response->alloced_content)
|
||||
{
|
||||
free((char*)response->content);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_websocket_request(struct mg_connection *nc, struct http_message *hm)
|
||||
{
|
||||
LOG_TRACE("new websocket %.*s request for %.*s\n", hm->method.len, hm->method.p, hm->uri.len, hm->uri.p);
|
||||
|
||||
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)
|
||||
{
|
||||
LOG_TRACE("new http %.*s request for %.*s\n", hm->method.len, hm->method.p, hm->uri.len, hm->uri.p);
|
||||
|
||||
endpoint_t *endpoint = router_find_endpoint(hm->uri.p, hm->uri.len, &hm->method);
|
||||
|
||||
endpoint_response_t response;
|
||||
static const char content[] = "the server did not create a response";
|
||||
endpoint_response_text(&response, 500, content, STRLEN(content));
|
||||
|
||||
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);
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
LOG_DEBUG("%s\n", request_file);
|
||||
int access_result = access(request_file, R_OK);
|
||||
free(request_file_org);
|
||||
if(access_result != -1)
|
||||
{
|
||||
response.status_code = 0;
|
||||
mg_serve_http(nc, hm, global_config.http_server_opts);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoint = router_get_not_found_endpoint();
|
||||
}
|
||||
}
|
||||
|
||||
if(!endpoint->func)
|
||||
{
|
||||
if(endpoint->method == HTTP_METHOD_OPTIONS)
|
||||
{
|
||||
char options_header[256]; // TODO make more generic
|
||||
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
|
||||
{
|
||||
mg_send_head(nc, 501, 0, "Content-Type: text/plain");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
endpoint->func(nc, hm, endpoint->args, &response);
|
||||
send_response(nc, &response);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct http_message *hm = (struct http_message*)p;
|
||||
handle_http_request(nc, hm);
|
||||
}
|
||||
}
|
95
src/handlers/mqtt.c
Normal file
95
src/handlers/mqtt.c
Normal file
|
@ -0,0 +1,95 @@
|
|||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <mongoose.h>
|
||||
#include <logger.h>
|
||||
#include <handlers.h>
|
||||
#include <status.h>
|
||||
#include <models/controller.h>
|
||||
#include <models/relay.h>
|
||||
|
||||
static void
|
||||
handle_mqtt_publish_controller(char **topic_save, int controller_id, char *payload)
|
||||
{
|
||||
(void)controller_id;
|
||||
(void)payload;
|
||||
char *topic_token = strtok_r(NULL, "/", topic_save);
|
||||
if(!topic_token)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(strcmp(topic_token, "relay") == 0)
|
||||
{
|
||||
char *relay_num_str = strtok_r(NULL, "/", topic_save);
|
||||
if(!relay_num_str)
|
||||
{
|
||||
return;
|
||||
}
|
||||
errno = 0;
|
||||
int relay_num = strtol(relay_num_str, NULL, 10);
|
||||
if(errno)
|
||||
{
|
||||
return;
|
||||
}
|
||||
relay_t *relay = relay_get_for_controller(controller_id, relay_num);
|
||||
if(!relay)
|
||||
{
|
||||
return;
|
||||
}
|
||||
status_update_entry(relay->id, payload[0] == '1');
|
||||
free(relay);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_mqtt_publish(struct mg_mqtt_message *msg)
|
||||
{
|
||||
char *topic = malloc(sizeof(char) * (msg->topic.len + 1));
|
||||
strncpy(topic, msg->topic.p, msg->topic.len);
|
||||
topic[msg->topic.len] = '\0';
|
||||
LOG_DEBUG("received mqtt publish for topic %s\n", topic);
|
||||
|
||||
char *payload = malloc(sizeof(char) * (msg->payload.len + 1));
|
||||
strncpy(payload, msg->payload.p, msg->payload.len);
|
||||
payload[msg->payload.len] = '\0';
|
||||
|
||||
char *topic_save_null = NULL;
|
||||
char **topic_save = &topic_save_null;
|
||||
char *topic_token = strtok_r(topic, "/", topic_save);
|
||||
if(topic_token)
|
||||
{
|
||||
if(strcmp(topic_token, "controller") == 0)
|
||||
{
|
||||
char *controller_uid_str = strtok_r(NULL, "/", topic_save);
|
||||
uuid_t controller_uid;
|
||||
if(uuid_parse(controller_uid_str, controller_uid) == 0)
|
||||
{
|
||||
controller_t *controller = controller_get_by_uid(controller_uid);
|
||||
if(controller)
|
||||
{
|
||||
handle_mqtt_publish_controller(topic_save, controller->id, payload);
|
||||
controller_free(controller);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(payload);
|
||||
free(topic);
|
||||
}
|
||||
|
||||
void
|
||||
handler_mqtt(struct mg_connection *nc, int ev, void *p)
|
||||
{
|
||||
if(ev == MG_EV_POLL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
mg_mqtt_broker(nc, ev, p);
|
||||
if(ev == MG_EV_MQTT_PUBLISH)
|
||||
{
|
||||
struct mg_mqtt_message *msg = (struct mg_mqtt_message*)p;
|
||||
handle_mqtt_publish(msg);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue