From 7c2fee9c73a1a283678c08e52aa87c947437c876 Mon Sep 17 00:00:00 2001 From: Stefan Ravn van Overeem Date: Tue, 4 Jul 2017 23:06:02 +0200 Subject: [PATCH] Split Http Server into multiple files. --- software/main/HttpServer.cpp | 461 +------------------------- software/main/HttpServer_get_api.cpp | 300 +++++++++++++++++ software/main/HttpServer_post_api.cpp | 177 ++++++++++ 3 files changed, 478 insertions(+), 460 deletions(-) create mode 100644 software/main/HttpServer_get_api.cpp create mode 100644 software/main/HttpServer_post_api.cpp diff --git a/software/main/HttpServer.cpp b/software/main/HttpServer.cpp index 4ce6549..0e694cb 100644 --- a/software/main/HttpServer.cpp +++ b/software/main/HttpServer.cpp @@ -5,6 +5,7 @@ #include "lws_server_structs.h" #include "cJSON.h" #include + static const char *TAG = "HTTP_SERVER"; /*We define this here as we need to access cpp functions from it. **Other relevant LWS structs are defined in lws_server_structs.c @@ -100,463 +101,3 @@ bool HttpServer::stop() this->running = false; return true; } - -int HttpServer::handle_get_switch_state(get_api_session_data *session_data, char *request_uri) -{ - //Send the current state of the switches as json. - uint8_t switch_count = this->switch_handler->get_switch_count(); - cJSON *root,*fmt; - root = cJSON_CreateObject(); - cJSON_AddNumberToObject(root, "count", switch_count); - for(uint8_t i = 0; i < switch_count; i++) - { //Send data for each switch - char switch_name[10]; - snprintf(switch_name, 10, "switch%d", i); - switch_state state = this->switch_handler->get_switch_state(i); - cJSON_AddItemToObject(root, switch_name, fmt=cJSON_CreateObject()); - cJSON_AddNumberToObject(fmt, "id", i); - cJSON_AddNumberToObject(fmt, "state", state); - } - session_data->json_str = (unsigned char *)cJSON_PrintBuffered(root, switch_count * 20 + 15, 1); - cJSON_Delete(root); - return 0; -} - -int HttpServer::handle_get_uptime(get_api_session_data *session_data, char *request_uri) -{ - cJSON *root = cJSON_CreateObject(); - cJSON_AddNumberToObject(root, "uptime", this->t_keeper->get_uptime()); - session_data->json_str = (unsigned char *)cJSON_PrintBuffered(root, 20, 1); - cJSON_Delete(root); - return 0; -} - - -int HttpServer::handle_get_calibrations(get_api_session_data *session_data, char *request_uri) -{ - cJSON *root,*fmt; - root = cJSON_CreateObject(); - uint8_t count = this->cur_measurer->get_current_count(); - cJSON_AddNumberToObject(root, "count", count); - - for(uint8_t i = 0; i < count; i++) - { //Send data for each calibration - char calib_name[10]; - CurrentCalibration cur_calib; - this->cur_measurer->load_current_calibration(i, cur_calib); - snprintf(calib_name, 10, "ccalib%d", i); - cJSON_AddItemToObject(root, calib_name, fmt=cJSON_CreateObject()); - cJSON_AddNumberToObject(fmt, "id", i); - cJSON_AddNumberToObject(fmt, "conversion", cur_calib.conversion); - cJSON_AddNumberToObject(fmt, "bias_on", cur_calib.bias_on.last.bias); - cJSON_AddNumberToObject(fmt, "bias_off", cur_calib.bias_off.last.bias); - cJSON_AddNumberToObject(fmt, "stddev_on", cur_calib.bias_on.last.stddev); - cJSON_AddNumberToObject(fmt, "stddev_off", cur_calib.bias_off.last.stddev); - cJSON_AddNumberToObject(fmt, "calibrated", cur_calib.bias_on.last.completed); - } - session_data->json_str = (unsigned char *)cJSON_PrintBuffered(root, count * 100 + 10, 1); - cJSON_Delete(root); - return 0; -} - -int HttpServer::handle_get_ip_info(get_api_session_data *session_data, char *request_uri, tcpip_adapter_if_t adapter) -{ - if(adapter != TCPIP_ADAPTER_IF_AP and adapter != TCPIP_ADAPTER_IF_STA) - return 1; - tcpip_adapter_ip_info_t ip_info; - ESP_ERROR_CHECK (tcpip_adapter_get_ip_info (adapter , &ip_info)); - char *ip_str, *gw_str, *nm_str, *ssid_name = nullptr; - uint8_t enabled = 0; - ip_str = (char *)malloc(16); - gw_str = (char *)malloc(16); - nm_str = (char *)malloc(16); - strcpy(ip_str,inet_ntoa(ip_info.ip)); - strcpy(gw_str,inet_ntoa(ip_info.gw)); - strcpy(nm_str,inet_ntoa(ip_info.netmask)); - - //Retrieve ssid settings - if(adapter == TCPIP_ADAPTER_IF_AP) - { - size_t ssid_len = 0; - ESP_ERROR_CHECK( this->s_handler->nvs_get("AP_SSID", (char *)nullptr, &ssid_len) ); - ssid_name = (char *)malloc(ssid_len); - ESP_ERROR_CHECK( this->s_handler->nvs_get("AP_SSID", ssid_name, &ssid_len) ); - //Se if the AP is enabled - wifi_mode_t mode; - ESP_ERROR_CHECK( this->s_handler->nvs_get("WIFI_MODE", (uint32_t *)(&mode))); - if(mode == WIFI_MODE_APSTA) - enabled = 1; - } - if(adapter == TCPIP_ADAPTER_IF_STA) - { - size_t ssid_len = 0; - ESP_ERROR_CHECK( this->s_handler->nvs_get("STA_SSID", (char *)nullptr, &ssid_len) ); - ssid_name = (char *)malloc(ssid_len); - ESP_ERROR_CHECK( this->s_handler->nvs_get("STA_SSID", ssid_name, &ssid_len) ); - //For now, station mode is always enabled - enabled = 1; - } - cJSON *root; - root = cJSON_CreateObject(); - cJSON_AddNumberToObject(root, "enabled", enabled); - cJSON_AddStringToObject(root, "ssid", ssid_name); - cJSON_AddStringToObject(root, "ip", ip_str); - cJSON_AddStringToObject(root, "gw", gw_str); - cJSON_AddStringToObject(root, "nm", nm_str); - free(ssid_name); //This is allocated in the above if - else blocks (adapter == something). - //This also means that the adapter NEEDS to be one of the types we are checking or, - //We will free an unallocated piece of memory (very bad!). - //Check at function entry if the adapter is one of the supported ones.. - free(ip_str); - free(gw_str); - free(nm_str); - session_data->json_str = (unsigned char *)cJSON_PrintBuffered(root, 100, 1); - cJSON_Delete(root); - return 0; -} - - -int HttpServer::create_get_callback_reply(get_api_session_data *session_data, char *request_uri) -{ - if(strcmp(request_uri, "/switch_state") == 0) - return handle_get_switch_state(session_data, request_uri); - if(strcmp(request_uri, "/current_calibrations") == 0) - return handle_get_calibrations(session_data, request_uri); - if(strcmp(request_uri, "/sta_info") == 0) - return handle_get_ip_info(session_data, request_uri, TCPIP_ADAPTER_IF_STA); - if(strcmp(request_uri, "/ap_info") == 0) - return handle_get_ip_info(session_data, request_uri, TCPIP_ADAPTER_IF_AP); - if(strcmp(request_uri, "/uptime") == 0) - return handle_get_uptime(session_data, request_uri); - return 2; //This results in a 404 being sent -} - - - -//Inspired by the test-server-http.c example. Check it out for further details of other features of lws. -int -HttpServer::get_callback(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - int n,m = 0; - unsigned char buffer[1024 + LWS_PRE]; - unsigned char *p, *end; - int json_result; - get_api_session_data *session_data = (get_api_session_data *)user; - switch (reason) { - case LWS_CALLBACK_HTTP: - { - p = buffer + LWS_PRE; - end = p + sizeof(buffer) - LWS_PRE; - ESP_LOGD(TAG, "get_callback: line %d\n", __LINE__); - ESP_LOGI(TAG, "lws_http_serve: %s\n", (const char *)in); - json_result = ((HttpServer *)lws_context_user(lws_get_context(wsi)))->create_get_callback_reply(session_data, (char *)in); - if( json_result != 0 || - session_data->json_str == nullptr) - { - if(json_result == 2) - lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, - "

INVALID URI!

"); - goto try_to_reuse; - goto header_failure; - } - session_data->len = strlen((char *)session_data->json_str); - session_data->sent = 0; - - if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) - goto header_failure; - if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, - (unsigned char *)"libwebsockets", - 13, &p, end)) - goto header_failure; - if (lws_add_http_header_by_token(wsi, - WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"text/json", - 9, &p, end)) - goto header_failure; - if (lws_add_http_header_content_length(wsi, - session_data->len, &p, - end)) - goto header_failure; - if (lws_finalize_http_header(wsi, &p, end)) - goto header_failure; - /* - * send the http headers... - * this won't block since it's the first payload sent - * on the connection since it was established - * (too small for partial) - * - * Notice they are sent using LWS_WRITE_HTTP_HEADERS - * which also means you can't send body too in one step, - * this is mandated by changes in HTTP2 - */ - - *p = '\0'; - lwsl_info("%s\n", buffer + LWS_PRE); - - n = lws_write(wsi, buffer + LWS_PRE, - p - (buffer + LWS_PRE), - LWS_WRITE_HTTP_HEADERS); - if (n < 0) { - if(session_data->json_str) free(session_data->json_str); - return -1; - } - /*Transfer the malloced json string to the buffered one. - *This means that we can free the memory op fairly quickly, and not worry anymore.*/ - - /* - * book us a LWS_CALLBACK_HTTP_WRITEABLE callback - */ - lws_callback_on_writable(wsi); - - break; - } - - case LWS_CALLBACK_HTTP_WRITEABLE: - ESP_LOGI(TAG, "LWS_CALLBACK_HTTP_WRITEABLE\n"); - /* - * we can send more of whatever it is we were sending - */ - do { - /* we'd like the send this much */ - n = session_data->len - session_data->sent; - /* but if the peer told us he wants less, we can adapt */ - m = lws_get_peer_write_allowance(wsi); - - /* -1 means not using a protocol that has this info */ - if (m == 0) - /* right now, peer can't handle anything */ - goto later; - - if (m != -1 && m < n) - /* he couldn't handle that much */ - n = m; - /* sent it all, close conn */ - if (n == 0) - goto penultimate; - /* - * To support HTTP2, must take care about preamble space - * - * identification of when we send the last payload frame - * is handled by the library itself if you sent a - * content-length header - */ - m = lws_write(wsi, session_data->json_str + session_data->sent, n, LWS_WRITE_HTTP); - if (m < 0) { - lwsl_err("write failed\n"); - /* write failed, close conn */ - goto bail; - } - if (m) /* while still active, extend timeout */ - lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5); - session_data->sent += m; - - } while (!lws_send_pipe_choked(wsi) && (session_data->sent < 1024 * 1024)); - later: - lws_callback_on_writable(wsi); - break; - penultimate: - if(session_data->json_str) free(session_data->json_str); - goto try_to_reuse; - bail: - ESP_LOGI(TAG, "LWS WRITE FAILED, BAILING\n") - - if(session_data->json_str) free(session_data->json_str); - return -1; - - case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: - /* if we returned non-zero from here, we kill the connection */ - break; - - case LWS_CALLBACK_SSL_INFO: - { - struct lws_ssl_info *si = (struct lws_ssl_info *)in; - - ESP_LOGI(TAG, "LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n", - si->where, si->ret); - } - break; - - default: - break; - } - return 0; - - header_failure: - ESP_LOGI(TAG, "SOMETHING WIERD HAPPENED WHILE SENDING THE HEADERS OR GENERATING THE JSON, BAILING\n"); - if(session_data->json_str) free(session_data->json_str); - return 1; - try_to_reuse: - if (lws_http_transaction_completed(wsi)) - return -1; - - return 0; -} - -int HttpServer::post_set_sta(post_api_session_data *session_data) -{ - cJSON *root, *fmt, *item; - root = cJSON_Parse(session_data->post_data); - if(!root) return -1; - fmt = cJSON_GetObjectItem(root, "sta"); - if(!fmt) goto post_set_sta_failure; - item = cJSON_GetObjectItem(fmt, "ssid"); - if(!item || item->type != cJSON_String) goto post_set_sta_failure; - ESP_ERROR_CHECK( this->s_handler->nvs_set("STA_SSID", item->valuestring)); - item = cJSON_GetObjectItem(fmt, "passwd"); - if(!item || item->type != cJSON_String ) goto post_set_sta_failure; - ESP_ERROR_CHECK( this->s_handler->nvs_set("STA_PASSWORD", item->valuestring)); - cJSON_Delete(root); - - post_set_sta_failure: - cJSON_Delete(root); - return -1; - - return 0; -} - -int HttpServer::post_set_ap(post_api_session_data *session_data) -{ - cJSON *root, *fmt, *item; - uint32_t tmp; - root = cJSON_Parse(session_data->post_data); - if(!root) return -1; - fmt = cJSON_GetObjectItem(root, "ap"); - if(!fmt) goto post_set_ap_failure; - item = cJSON_GetObjectItem(fmt, "ssid"); - if(!item || item->type != cJSON_String) goto post_set_ap_failure; - ESP_ERROR_CHECK( this->s_handler->nvs_set("AP_SSID", item->valuestring)); - item = cJSON_GetObjectItem(fmt, "enable"); - if(!item || item->type != cJSON_String) goto post_set_ap_failure; - if(strcmp(item->valuestring, "0") == 0) - { - ESP_ERROR_CHECK( this->s_handler->nvs_set("WIFI_MODE", (uint32_t)WIFI_MODE_STA)); - } - else if(strcmp(item->valuestring, "1") == 0) - { - ESP_ERROR_CHECK( this->s_handler->nvs_set("WIFI_MODE", (uint32_t)WIFI_MODE_APSTA)); - } - else goto post_set_ap_failure; - - item = cJSON_GetObjectItem(fmt, "ip"); - if(!item || item->type != cJSON_String) goto post_set_ap_failure; - if(!inet_aton(item->valuestring, &tmp)) goto post_set_ap_failure; - ESP_ERROR_CHECK( this->s_handler->nvs_set("AP_IP", tmp)); - item = cJSON_GetObjectItem(fmt, "gw"); - if(!item || item->type != cJSON_String) goto post_set_ap_failure; - if(!inet_aton(item->valuestring, &tmp)) goto post_set_ap_failure; - ESP_ERROR_CHECK( this->s_handler->nvs_set("AP_GATEWAY", tmp)); - item = cJSON_GetObjectItem(fmt, "netmask"); - if(!item || item->type != cJSON_String) goto post_set_ap_failure; - if(!inet_aton(item->valuestring, &tmp)) goto post_set_ap_failure; - ESP_ERROR_CHECK( this->s_handler->nvs_set("AP_NETMASK", tmp)); - cJSON_Delete(root); - - return 0; - post_set_ap_failure: - cJSON_Delete(root); - return -1; -} - -int HttpServer::post_set_switch_state(post_api_session_data *session_data) -{ - cJSON *root, *item; - uint32_t switch_num; - root = cJSON_Parse(session_data->post_data); - if(!root) return -1; - for (int i = 0; i < cJSON_GetArraySize(root); i++) - { - item = cJSON_GetArrayItem(root, i); - if(!(item->type == cJSON_Number)) continue; - if(sscanf(item->string,"switch%u", &switch_num) != 1) - { //Switch number grab failed - printf("failed switch str parse %s\n", item->string); - continue; - } - switch_state s_state = (switch_state)item->valueint; - //Now, set the switch state! - this->switch_handler->set_switch_state(switch_num, s_state); - } - cJSON_Delete(root); - - return 0; -} - - -int HttpServer::handle_post_data(post_api_session_data *session_data) -{ - session_data->post_data[session_data->total_post_length] = '\0'; //Make sure to zero terminate the string - printf("URI: %.*s\n", sizeof(session_data->post_uri), session_data->post_uri); - if(strcmp(session_data->post_uri, "/STA_SSID") == 0) - return this->post_set_sta(session_data); - else if(strcmp(session_data->post_uri, "/AP_SSID") == 0) - return this->post_set_ap(session_data); - else if(strcmp(session_data->post_uri, "/recalib_current") == 0) - this->cur_measurer->recalib_current_sensors(); - else if(strcmp(session_data->post_uri, "/reboot") == 0) - this->do_reboot = 1; - else if(strcmp(session_data->post_uri, "/reset") == 0) - this->s_handler->reset_settings(); - else if(strcmp(session_data->post_uri, "/toggle_switch") == 0) - return post_set_switch_state(session_data); - - return 0; -} - -int -HttpServer::post_callback(struct lws *wsi, enum lws_callback_reasons reason, - void *user, void *in, size_t len) -{ - post_api_session_data *session_data = (post_api_session_data *)user; - int post_result; - printf("post callback reason: %d\n", reason); - - switch (reason) { - case LWS_CALLBACK_HTTP: - strncpy(session_data->post_uri, (const char*)in, sizeof(session_data->post_uri)); - printf("LWS_CALLBACK_HTTP\n"); - break; - case LWS_CALLBACK_HTTP_BODY: - printf("LWS_CALLBACK_HTTP_BODY\n"); - - /* let it parse the POST data */ - if(sizeof(session_data->post_data) - 1 - session_data->total_post_length < len) //We substract 1 to make space for zero termination - return -1; - memcpy(session_data->post_data + session_data->total_post_length, in, len); - session_data->total_post_length += len; - break; - - case LWS_CALLBACK_HTTP_BODY_COMPLETION: - printf("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); - if(sizeof(session_data->post_data) - session_data->total_post_length < len) - return -1; - memcpy(session_data->post_data + session_data->total_post_length, in, len); - session_data->total_post_length += len; - - lws_callback_on_writable(wsi); - break; - - case LWS_CALLBACK_HTTP_WRITEABLE: - printf("LWS_CALLBACK_HTTP_WRITEABLE\n"); - post_result = ((HttpServer *)lws_context_user(lws_get_context(wsi)))->handle_post_data(session_data); - if(post_result == 0) - { - lws_return_http_status(wsi, HTTP_STATUS_OK, NULL); - goto try_to_reuse; - } - else - return -1; - - case LWS_CALLBACK_HTTP_DROP_PROTOCOL: - break; - - default: - break; - } - - return 0; -try_to_reuse: - if (lws_http_transaction_completed(wsi)) - return -1; - - return 0; -} diff --git a/software/main/HttpServer_get_api.cpp b/software/main/HttpServer_get_api.cpp new file mode 100644 index 0000000..b92c6a5 --- /dev/null +++ b/software/main/HttpServer_get_api.cpp @@ -0,0 +1,300 @@ +#include "HttpServer.h" +#include "cJSON.h" +#include "esp_err.h" +#include "esp_log.h" +#include + +static const char *TAG = "HTTP_SERVER_GET"; + + + +int HttpServer::handle_get_switch_state(get_api_session_data *session_data, char *request_uri) +{ + //Send the current state of the switches as json. + uint8_t switch_count = this->switch_handler->get_switch_count(); + cJSON *root,*fmt; + root = cJSON_CreateObject(); + cJSON_AddNumberToObject(root, "count", switch_count); + for(uint8_t i = 0; i < switch_count; i++) + { //Send data for each switch + char switch_name[10]; + snprintf(switch_name, 10, "switch%d", i); + switch_state state = this->switch_handler->get_switch_state(i); + cJSON_AddItemToObject(root, switch_name, fmt=cJSON_CreateObject()); + cJSON_AddNumberToObject(fmt, "id", i); + cJSON_AddNumberToObject(fmt, "state", state); + } + session_data->json_str = (unsigned char *)cJSON_PrintBuffered(root, switch_count * 20 + 15, 1); + cJSON_Delete(root); + return 0; +} + +int HttpServer::handle_get_uptime(get_api_session_data *session_data, char *request_uri) +{ + cJSON *root = cJSON_CreateObject(); + cJSON_AddNumberToObject(root, "uptime", this->t_keeper->get_uptime()); + session_data->json_str = (unsigned char *)cJSON_PrintBuffered(root, 20, 1); + cJSON_Delete(root); + return 0; +} + + +int HttpServer::handle_get_calibrations(get_api_session_data *session_data, char *request_uri) +{ + cJSON *root,*fmt; + root = cJSON_CreateObject(); + uint8_t count = this->cur_measurer->get_current_count(); + cJSON_AddNumberToObject(root, "count", count); + + for(uint8_t i = 0; i < count; i++) + { //Send data for each calibration + char calib_name[10]; + CurrentCalibration cur_calib; + this->cur_measurer->load_current_calibration(i, cur_calib); + snprintf(calib_name, 10, "ccalib%d", i); + cJSON_AddItemToObject(root, calib_name, fmt=cJSON_CreateObject()); + cJSON_AddNumberToObject(fmt, "id", i); + cJSON_AddNumberToObject(fmt, "conversion", cur_calib.conversion); + cJSON_AddNumberToObject(fmt, "bias_on", cur_calib.bias_on.last.bias); + cJSON_AddNumberToObject(fmt, "bias_off", cur_calib.bias_off.last.bias); + cJSON_AddNumberToObject(fmt, "stddev_on", cur_calib.bias_on.last.stddev); + cJSON_AddNumberToObject(fmt, "stddev_off", cur_calib.bias_off.last.stddev); + cJSON_AddNumberToObject(fmt, "calibrated", cur_calib.bias_on.last.completed); + } + session_data->json_str = (unsigned char *)cJSON_PrintBuffered(root, count * 100 + 10, 1); + cJSON_Delete(root); + return 0; +} + +int HttpServer::handle_get_ip_info(get_api_session_data *session_data, char *request_uri, tcpip_adapter_if_t adapter) +{ + if(adapter != TCPIP_ADAPTER_IF_AP and adapter != TCPIP_ADAPTER_IF_STA) + return 1; + tcpip_adapter_ip_info_t ip_info; + ESP_ERROR_CHECK (tcpip_adapter_get_ip_info (adapter , &ip_info)); + char *ip_str, *gw_str, *nm_str, *ssid_name = nullptr; + uint8_t enabled = 0; + ip_str = (char *)malloc(16); + gw_str = (char *)malloc(16); + nm_str = (char *)malloc(16); + strcpy(ip_str,inet_ntoa(ip_info.ip)); + strcpy(gw_str,inet_ntoa(ip_info.gw)); + strcpy(nm_str,inet_ntoa(ip_info.netmask)); + + //Retrieve ssid settings + if(adapter == TCPIP_ADAPTER_IF_AP) + { + size_t ssid_len = 0; + ESP_ERROR_CHECK( this->s_handler->nvs_get("AP_SSID", (char *)nullptr, &ssid_len) ); + ssid_name = (char *)malloc(ssid_len); + ESP_ERROR_CHECK( this->s_handler->nvs_get("AP_SSID", ssid_name, &ssid_len) ); + //Se if the AP is enabled + wifi_mode_t mode; + ESP_ERROR_CHECK( this->s_handler->nvs_get("WIFI_MODE", (uint32_t *)(&mode))); + if(mode == WIFI_MODE_APSTA) + enabled = 1; + } + if(adapter == TCPIP_ADAPTER_IF_STA) + { + size_t ssid_len = 0; + ESP_ERROR_CHECK( this->s_handler->nvs_get("STA_SSID", (char *)nullptr, &ssid_len) ); + ssid_name = (char *)malloc(ssid_len); + ESP_ERROR_CHECK( this->s_handler->nvs_get("STA_SSID", ssid_name, &ssid_len) ); + //For now, station mode is always enabled + enabled = 1; + } + cJSON *root; + root = cJSON_CreateObject(); + cJSON_AddNumberToObject(root, "enabled", enabled); + cJSON_AddStringToObject(root, "ssid", ssid_name); + cJSON_AddStringToObject(root, "ip", ip_str); + cJSON_AddStringToObject(root, "gw", gw_str); + cJSON_AddStringToObject(root, "nm", nm_str); + free(ssid_name); //This is allocated in the above if - else blocks (adapter == something). + //This also means that the adapter NEEDS to be one of the types we are checking or, + //We will free an unallocated piece of memory (very bad!). + //Check at function entry if the adapter is one of the supported ones.. + free(ip_str); + free(gw_str); + free(nm_str); + session_data->json_str = (unsigned char *)cJSON_PrintBuffered(root, 100, 1); + cJSON_Delete(root); + return 0; +} + + +int HttpServer::create_get_callback_reply(get_api_session_data *session_data, char *request_uri) +{ + if(strcmp(request_uri, "/switch_state") == 0) + return handle_get_switch_state(session_data, request_uri); + if(strcmp(request_uri, "/current_calibrations") == 0) + return handle_get_calibrations(session_data, request_uri); + if(strcmp(request_uri, "/sta_info") == 0) + return handle_get_ip_info(session_data, request_uri, TCPIP_ADAPTER_IF_STA); + if(strcmp(request_uri, "/ap_info") == 0) + return handle_get_ip_info(session_data, request_uri, TCPIP_ADAPTER_IF_AP); + if(strcmp(request_uri, "/uptime") == 0) + return handle_get_uptime(session_data, request_uri); + return 2; //This results in a 404 being sent +} + + + +//Inspired by the test-server-http.c example. Check it out for further details of other features of lws. +int +HttpServer::get_callback(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + int n,m = 0; + unsigned char buffer[1024 + LWS_PRE]; + unsigned char *p, *end; + int json_result; + get_api_session_data *session_data = (get_api_session_data *)user; + switch (reason) { + case LWS_CALLBACK_HTTP: + { + p = buffer + LWS_PRE; + end = p + sizeof(buffer) - LWS_PRE; + ESP_LOGD(TAG, "get_callback: line %d\n", __LINE__); + ESP_LOGI(TAG, "lws_http_serve: %s\n", (const char *)in); + json_result = ((HttpServer *)lws_context_user(lws_get_context(wsi)))->create_get_callback_reply(session_data, (char *)in); + if( json_result != 0 || + session_data->json_str == nullptr) + { + if(json_result == 2) + lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, + "

INVALID URI!

"); + goto try_to_reuse; + goto header_failure; + } + session_data->len = strlen((char *)session_data->json_str); + session_data->sent = 0; + + if (lws_add_http_header_status(wsi, HTTP_STATUS_OK, &p, end)) + goto header_failure; + if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, + (unsigned char *)"libwebsockets", + 13, &p, end)) + goto header_failure; + if (lws_add_http_header_by_token(wsi, + WSI_TOKEN_HTTP_CONTENT_TYPE, (unsigned char *)"text/json", + 9, &p, end)) + goto header_failure; + if (lws_add_http_header_content_length(wsi, + session_data->len, &p, + end)) + goto header_failure; + if (lws_finalize_http_header(wsi, &p, end)) + goto header_failure; + /* + * send the http headers... + * this won't block since it's the first payload sent + * on the connection since it was established + * (too small for partial) + * + * Notice they are sent using LWS_WRITE_HTTP_HEADERS + * which also means you can't send body too in one step, + * this is mandated by changes in HTTP2 + */ + + *p = '\0'; + lwsl_info("%s\n", buffer + LWS_PRE); + + n = lws_write(wsi, buffer + LWS_PRE, + p - (buffer + LWS_PRE), + LWS_WRITE_HTTP_HEADERS); + if (n < 0) { + if(session_data->json_str) free(session_data->json_str); + return -1; + } + /*Transfer the malloced json string to the buffered one. + *This means that we can free the memory op fairly quickly, and not worry anymore.*/ + + /* + * book us a LWS_CALLBACK_HTTP_WRITEABLE callback + */ + lws_callback_on_writable(wsi); + + break; + } + + case LWS_CALLBACK_HTTP_WRITEABLE: + ESP_LOGI(TAG, "LWS_CALLBACK_HTTP_WRITEABLE\n"); + /* + * we can send more of whatever it is we were sending + */ + do { + /* we'd like the send this much */ + n = session_data->len - session_data->sent; + /* but if the peer told us he wants less, we can adapt */ + m = lws_get_peer_write_allowance(wsi); + + /* -1 means not using a protocol that has this info */ + if (m == 0) + /* right now, peer can't handle anything */ + goto later; + + if (m != -1 && m < n) + /* he couldn't handle that much */ + n = m; + /* sent it all, close conn */ + if (n == 0) + goto penultimate; + /* + * To support HTTP2, must take care about preamble space + * + * identification of when we send the last payload frame + * is handled by the library itself if you sent a + * content-length header + */ + m = lws_write(wsi, session_data->json_str + session_data->sent, n, LWS_WRITE_HTTP); + if (m < 0) { + lwsl_err("write failed\n"); + /* write failed, close conn */ + goto bail; + } + if (m) /* while still active, extend timeout */ + lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5); + session_data->sent += m; + + } while (!lws_send_pipe_choked(wsi) && (session_data->sent < 1024 * 1024)); + later: + lws_callback_on_writable(wsi); + break; + penultimate: + if(session_data->json_str) free(session_data->json_str); + goto try_to_reuse; + bail: + ESP_LOGI(TAG, "LWS WRITE FAILED, BAILING\n") + + if(session_data->json_str) free(session_data->json_str); + return -1; + + case LWS_CALLBACK_FILTER_NETWORK_CONNECTION: + /* if we returned non-zero from here, we kill the connection */ + break; + + case LWS_CALLBACK_SSL_INFO: + { + struct lws_ssl_info *si = (struct lws_ssl_info *)in; + + ESP_LOGI(TAG, "LWS_CALLBACK_SSL_INFO: where: 0x%x, ret: 0x%x\n", + si->where, si->ret); + } + break; + + default: + break; + } + return 0; + + header_failure: + ESP_LOGI(TAG, "SOMETHING WIERD HAPPENED WHILE SENDING THE HEADERS OR GENERATING THE JSON, BAILING\n"); + if(session_data->json_str) free(session_data->json_str); + return 1; + try_to_reuse: + if (lws_http_transaction_completed(wsi)) + return -1; + + return 0; +} diff --git a/software/main/HttpServer_post_api.cpp b/software/main/HttpServer_post_api.cpp new file mode 100644 index 0000000..a3888df --- /dev/null +++ b/software/main/HttpServer_post_api.cpp @@ -0,0 +1,177 @@ +#include "HttpServer.h" +#include "cJSON.h" +#include "esp_err.h" +#include "esp_log.h" +#include + +static const char *TAG = "HTTP_SERVER_POST"; + + +int HttpServer::post_set_sta(post_api_session_data *session_data) +{ + cJSON *root, *fmt, *item; + root = cJSON_Parse(session_data->post_data); + if(!root) return -1; + fmt = cJSON_GetObjectItem(root, "sta"); + if(!fmt) goto post_set_sta_failure; + item = cJSON_GetObjectItem(fmt, "ssid"); + if(!item || item->type != cJSON_String) goto post_set_sta_failure; + ESP_ERROR_CHECK( this->s_handler->nvs_set("STA_SSID", item->valuestring)); + item = cJSON_GetObjectItem(fmt, "passwd"); + if(!item || item->type != cJSON_String ) goto post_set_sta_failure; + ESP_ERROR_CHECK( this->s_handler->nvs_set("STA_PASSWORD", item->valuestring)); + cJSON_Delete(root); + + post_set_sta_failure: + cJSON_Delete(root); + return -1; + + return 0; +} + +int HttpServer::post_set_ap(post_api_session_data *session_data) +{ + cJSON *root, *fmt, *item; + uint32_t tmp; + root = cJSON_Parse(session_data->post_data); + if(!root) return -1; + fmt = cJSON_GetObjectItem(root, "ap"); + if(!fmt) goto post_set_ap_failure; + item = cJSON_GetObjectItem(fmt, "ssid"); + if(!item || item->type != cJSON_String) goto post_set_ap_failure; + ESP_ERROR_CHECK( this->s_handler->nvs_set("AP_SSID", item->valuestring)); + item = cJSON_GetObjectItem(fmt, "enable"); + if(!item || item->type != cJSON_String) goto post_set_ap_failure; + if(strcmp(item->valuestring, "0") == 0) + { + ESP_ERROR_CHECK( this->s_handler->nvs_set("WIFI_MODE", (uint32_t)WIFI_MODE_STA)); + } + else if(strcmp(item->valuestring, "1") == 0) + { + ESP_ERROR_CHECK( this->s_handler->nvs_set("WIFI_MODE", (uint32_t)WIFI_MODE_APSTA)); + } + else goto post_set_ap_failure; + + item = cJSON_GetObjectItem(fmt, "ip"); + if(!item || item->type != cJSON_String) goto post_set_ap_failure; + if(!inet_aton(item->valuestring, &tmp)) goto post_set_ap_failure; + ESP_ERROR_CHECK( this->s_handler->nvs_set("AP_IP", tmp)); + item = cJSON_GetObjectItem(fmt, "gw"); + if(!item || item->type != cJSON_String) goto post_set_ap_failure; + if(!inet_aton(item->valuestring, &tmp)) goto post_set_ap_failure; + ESP_ERROR_CHECK( this->s_handler->nvs_set("AP_GATEWAY", tmp)); + item = cJSON_GetObjectItem(fmt, "netmask"); + if(!item || item->type != cJSON_String) goto post_set_ap_failure; + if(!inet_aton(item->valuestring, &tmp)) goto post_set_ap_failure; + ESP_ERROR_CHECK( this->s_handler->nvs_set("AP_NETMASK", tmp)); + cJSON_Delete(root); + + return 0; + post_set_ap_failure: + cJSON_Delete(root); + return -1; +} + +int HttpServer::post_set_switch_state(post_api_session_data *session_data) +{ + cJSON *root, *item; + uint32_t switch_num; + root = cJSON_Parse(session_data->post_data); + if(!root) return -1; + for (int i = 0; i < cJSON_GetArraySize(root); i++) + { + item = cJSON_GetArrayItem(root, i); + if(!(item->type == cJSON_Number)) continue; + if(sscanf(item->string,"switch%u", &switch_num) != 1) + { //Switch number grab failed + printf("failed switch str parse %s\n", item->string); + continue; + } + switch_state s_state = (switch_state)item->valueint; + //Now, set the switch state! + this->switch_handler->set_switch_state(switch_num, s_state); + } + cJSON_Delete(root); + + return 0; +} + + +int HttpServer::handle_post_data(post_api_session_data *session_data) +{ + session_data->post_data[session_data->total_post_length] = '\0'; //Make sure to zero terminate the string + printf("URI: %.*s\n", sizeof(session_data->post_uri), session_data->post_uri); + if(strcmp(session_data->post_uri, "/STA_SSID") == 0) + return this->post_set_sta(session_data); + else if(strcmp(session_data->post_uri, "/AP_SSID") == 0) + return this->post_set_ap(session_data); + else if(strcmp(session_data->post_uri, "/recalib_current") == 0) + this->cur_measurer->recalib_current_sensors(); + else if(strcmp(session_data->post_uri, "/reboot") == 0) + this->do_reboot = 1; + else if(strcmp(session_data->post_uri, "/reset") == 0) + this->s_handler->reset_settings(); + else if(strcmp(session_data->post_uri, "/toggle_switch") == 0) + return post_set_switch_state(session_data); + + return 0; +} + +int +HttpServer::post_callback(struct lws *wsi, enum lws_callback_reasons reason, + void *user, void *in, size_t len) +{ + post_api_session_data *session_data = (post_api_session_data *)user; + int post_result; + printf("post callback reason: %d\n", reason); + + switch (reason) { + case LWS_CALLBACK_HTTP: + strncpy(session_data->post_uri, (const char*)in, sizeof(session_data->post_uri)); + printf("LWS_CALLBACK_HTTP\n"); + break; + case LWS_CALLBACK_HTTP_BODY: + printf("LWS_CALLBACK_HTTP_BODY\n"); + + /* let it parse the POST data */ + if(sizeof(session_data->post_data) - 1 - session_data->total_post_length < len) //We substract 1 to make space for zero termination + return -1; + memcpy(session_data->post_data + session_data->total_post_length, in, len); + session_data->total_post_length += len; + break; + + case LWS_CALLBACK_HTTP_BODY_COMPLETION: + printf("LWS_CALLBACK_HTTP_BODY_COMPLETION\n"); + if(sizeof(session_data->post_data) - session_data->total_post_length < len) + return -1; + memcpy(session_data->post_data + session_data->total_post_length, in, len); + session_data->total_post_length += len; + + lws_callback_on_writable(wsi); + break; + + case LWS_CALLBACK_HTTP_WRITEABLE: + printf("LWS_CALLBACK_HTTP_WRITEABLE\n"); + post_result = ((HttpServer *)lws_context_user(lws_get_context(wsi)))->handle_post_data(session_data); + if(post_result == 0) + { + lws_return_http_status(wsi, HTTP_STATUS_OK, NULL); + goto try_to_reuse; + } + else + return -1; + + case LWS_CALLBACK_HTTP_DROP_PROTOCOL: + break; + + default: + break; + } + + return 0; +try_to_reuse: + if (lws_http_transaction_completed(wsi)) + return -1; + + return 0; +}