diff --git a/esphttpdconfig.mk.example b/esphttpdconfig.mk.example index cb494b7..d95d005 100644 --- a/esphttpdconfig.mk.example +++ b/esphttpdconfig.mk.example @@ -39,6 +39,7 @@ OUTPUT_TYPE = combined ESP_SPI_FLASH_SIZE_K = 1024 GLOBAL_CFLAGS = \ + -DASYNC_LOG=1 \ -DDEBUG_D2D=0 \ -DDEBUG_ROUTER=0 \ -DDEBUG_CAPTDNS=0 \ diff --git a/front-end b/front-end index 29b8134..6f165da 160000 --- a/front-end +++ b/front-end @@ -1 +1 @@ -Subproject commit 29b813457c7a185c41e050d358b001fadd2498e4 +Subproject commit 6f165da9b6237fd88efc836a663a429ed8aadb6e diff --git a/libesphttpd b/libesphttpd index 8f4db52..3484209 160000 --- a/libesphttpd +++ b/libesphttpd @@ -1 +1 @@ -Subproject commit 8f4db520bce2ecdc147dd6625e05d8dda45c813a +Subproject commit 348420959b919d95412daeafe5014f4255854f80 diff --git a/user/ansi_parser_callbacks.c b/user/ansi_parser_callbacks.c index 37c9914..014c968 100644 --- a/user/ansi_parser_callbacks.c +++ b/user/ansi_parser_callbacks.c @@ -11,6 +11,7 @@ #include "version.h" #include "uart_buffer.h" #include "screen.h" +#include "uart_driver.h" volatile bool enquiry_suppressed = false; ETSTimer enqTimer; @@ -26,7 +27,8 @@ void ICACHE_FLASH_ATTR enqTimerCb(void *unused) void ICACHE_FLASH_ATTR apars_respond(const char *str) { - UART_SendAsync(str, -1); + UART_WriteString(UART0, str, UART_TIMEOUT_US); + //UART_SendAsync(str, -1); } /** diff --git a/user/api.h b/user/api.h new file mode 100644 index 0000000..7f16a8b --- /dev/null +++ b/user/api.h @@ -0,0 +1,15 @@ +// +// Created by MightyPork on 2017/10/01. +// + +#ifndef ESPTERM_API_H +#define ESPTERM_API_H + +// TODO use X-MACRO for access restrictions etc + +#define API_D2D_MSG "/api/v1/msg" +#define API_REBOOT "/api/v1/reboot" +#define API_PING "/api/v1/ping" +#define API_CLEAR "/api/v1/clear" + +#endif //ESPTERM_API_H diff --git a/user/cgi_d2d.c b/user/cgi_d2d.c index d5cbb34..931920d 100644 --- a/user/cgi_d2d.c +++ b/user/cgi_d2d.c @@ -4,39 +4,70 @@ #include #include "cgi_d2d.h" -#include "apars_logging.h" #include "version.h" #include "ansi_parser_callbacks.h" +#include "api.h" #include #include #define D2D_TIMEOUT_MS 2000 #define D2D_HEADERS \ - "User-Agent: ESPTerm "FIRMWARE_VERSION"\r\n" \ + "User-Agent: ESPTerm "FIRMWARE_VERSION" like curl wget HTTPie\r\n" \ "Content-Type: text/plain; charset=utf-8\r\n" \ - "Cache-Control: max-age=0\r\n" + "Accept-Encoding: identity\r\n" \ + "Accept-Charset: utf-8\r\n" \ + "Accept: text/*, application/json\r\n" \ + "Cache-Control: no-cache,private,max-age=0\r\n" struct d2d_request_opts { bool want_body; bool want_head; + size_t max_result_len; char *nonce; }; +volatile bool request_pending = false; + +static void ICACHE_FLASH_ATTR +requestNoopCb(int http_status, + char *response_headers, + char *response_body, + size_t body_size, + void *userArg) +{ + request_pending = false; + if (userArg != NULL) free(userArg); +} + static void ICACHE_FLASH_ATTR requestCb(int http_status, - const char *response_headers, - const char *response_body, + char *response_headers, + char *response_body, size_t body_size, void *userArg) { - if (userArg == NULL) return; + if (userArg == NULL) { + request_pending = false; + return; + } + struct d2d_request_opts *opts = userArg; + d2d_dbg("Rx url response, code %d, nonce \"%s\"", http_status, opts->nonce?opts->nonce:""); + + // ensure positive - would be hard to parse + if (http_status < 0) http_status = -http_status; + char buff100[100]; int len = 0; - if (opts->want_head) len += strlen(response_headers); + size_t headers_size = strlen(response_headers); + + if (opts->want_head) len += headers_size; if (opts->want_body) len += body_size + (opts->want_head*2); + if (opts->max_result_len > 0 && len > opts->max_result_len) + len = (int) opts->max_result_len; + char *bb = buff100; bb += sprintf(bb, "\x1b^h;%d;", http_status); const char *comma = ""; @@ -66,24 +97,35 @@ requestCb(int http_status, apars_respond(buff100); - d2d_dbg("Response %d, nonce \"%s\"", http_status, opts->nonce?opts->nonce:""); - d2d_dbg("Headers %s", response_headers); - d2d_dbg("Body %s", response_body); + //d2d_dbg("Headers (part) %100s", response_headers); + //d2d_dbg("Body (part) %100s", response_body); // head and payload separated by \r\n\r\n (one \r\n is at the end of head - maybe) if (opts->want_head) { + // truncate + if (headers_size > len) { + response_headers[len] = 0; + opts->want_body = false; // soz, it wouldn't fit + } apars_respond(response_headers); if(opts->want_body) apars_respond("\r\n"); } if(opts->want_body) { + // truncate + if (opts->want_head*(headers_size+2)+body_size > len) { + response_body[len - (opts->want_head*(headers_size+2))] = 0; + } + apars_respond(response_body); } apars_respond("\a"); free(opts->nonce); - free(userArg); + free(opts); + + request_pending = false; } bool ICACHE_FLASH_ATTR @@ -101,6 +143,8 @@ d2d_parse_command(char *msg) } while(0) \ if (strstarts(msg, "M;")) { + if (request_pending) return false; + // Send a esp-esp message msg += 2; const char *ip; @@ -108,7 +152,7 @@ d2d_parse_command(char *msg) const char *payload = msg; d2d_dbg("D2D Tx,dest=%s,msg=%s", ip, payload); - sprintf(buff40, "http://%s" D2D_MSG_ENDPOINT, ip); + sprintf(buff40, "http://%s" API_D2D_MSG, ip); httpclient_args args; httpclient_args_init(&args); @@ -117,10 +161,14 @@ d2d_parse_command(char *msg) args.headers = D2D_HEADERS; args.timeout = D2D_TIMEOUT_MS; args.url = buff40; // "escapes scope" warning - can ignore, strdup is used - http_request(&args, NULL); + + request_pending = true; + http_request(&args, requestNoopCb); return true; } else if (strstarts(msg, "H;")) { + if (request_pending) return false; + // Send a esp-esp message msg += 2; const char *method = NULL; @@ -128,7 +176,7 @@ d2d_parse_command(char *msg) const char *nonce = NULL; const char *url = NULL; const char *payload = NULL; - httpd_method methodNum = HTTPD_METHOD_GET; + httpd_method methodNum; FIND_NEXT(method, ';'); @@ -140,7 +188,8 @@ d2d_parse_command(char *msg) else if (streq(method, "PATCH")) methodNum = HTTPD_METHOD_PATCH; else if (streq(method, "HEAD")) methodNum = HTTPD_METHOD_HEAD; else { - d2d_warn("BAD METHOD: %s, using GET", method); + d2d_warn("BAD METHOD: %s", method); + return false; } FIND_NEXT(params, ';'); @@ -148,7 +197,8 @@ d2d_parse_command(char *msg) d2d_dbg("Method %s", method); d2d_dbg("Params %s", params); - size_t max_len = HTTPCLIENT_DEF_MAX_LEN; + size_t max_buf_len = HTTPCLIENT_DEF_MAX_LEN; + size_t max_result_len = 0; // 0 = no truncate uint timeout = HTTPCLIENT_DEF_TIMEOUT_MS; bool want_body = 0; bool want_head = 0; @@ -163,8 +213,10 @@ d2d_parse_command(char *msg) if(streq(param, "H")) want_head = 1; // Return head else if(streq(param, "B")) want_body = 1; // Return body else if(streq(param, "X")) no_resp = 1; // X - no response, no callback - else if(strstarts(param, "L=")) { // max length - max_len = (size_t) atoi(param + 2); + else if(strstarts(param, "l=")) { // max buffer length + max_buf_len = (size_t) atoi(param + 2); + } else if(strstarts(param, "L=")) { // max length + max_result_len = (size_t) atoi(param + 2); } else if(strstarts(param, "T=")) { // timeout timeout = (uint) atoi(param + 2); } else if(strstarts(param, "N=")) { // Nonce @@ -198,18 +250,20 @@ d2d_parse_command(char *msg) args.body = payload; args.headers = D2D_HEADERS; args.timeout = timeout; - args.max_response_len = max_len; + args.max_response_len = max_buf_len; args.url = url; if (!no_resp) { struct d2d_request_opts *opts = malloc(sizeof(struct d2d_request_opts)); opts->want_body = want_body; opts->want_head = want_head; + opts->max_result_len = max_result_len; opts->nonce = esp_strdup(nonce); args.userData = opts; } - http_request(&args, no_resp ? NULL : requestCb); + request_pending = true; + http_request(&args, no_resp ? requestNoopCb : requestCb); d2d_dbg("Done"); return true; @@ -217,3 +271,46 @@ d2d_parse_command(char *msg) return false; } + +httpd_cgi_state ICACHE_FLASH_ATTR cgiD2DMessage(HttpdConnData *connData) +{ + if (connData->conn==NULL) { + //Connection aborted. Clean up. + return HTTPD_CGI_DONE; + } + + size_t len = 0; + if (connData->post && connData->post->buff) + len = strlen(connData->post->buff); + else if (connData->getArgs) + len = strlen(connData->getArgs); + else + len = 0; + + u8 *ip = connData->remote_ip; + char buf[20]; + sprintf(buf, "\x1b^m;"IPSTR";L=%d;", ip[0], ip[1], ip[2], ip[3], (int)len); + apars_respond(buf); + + if (connData->post && connData->post->buff) + apars_respond(connData->post->buff); + else if (connData->getArgs) + apars_respond(connData->getArgs); + + apars_respond("\a"); + + d2d_dbg("D2D Rx src="IPSTR",len=%d", ip[0], ip[1], ip[2], ip[3],len); + + // Received a msg + + httdResponseOptions(connData, 0); + httdSetTransferMode(connData, HTTPD_TRANSFER_CLOSE); + + httpdStartResponse(connData, 200); + httpdHeader(connData, "Content-Type", "text/plain"); + httpdEndHeaders(connData); + + httpdSend(connData, "message received\r\n", -1); + + return HTTPD_CGI_DONE; +} diff --git a/user/cgi_d2d.h b/user/cgi_d2d.h index 70a95f2..4eb3a74 100644 --- a/user/cgi_d2d.h +++ b/user/cgi_d2d.h @@ -6,6 +6,7 @@ #define ESPTERM_CGI_D2D_H #include +#include #if DEBUG_D2D #define d2d_warn warn @@ -17,8 +18,8 @@ #define d2d_info(fmt, ...) #endif -#define D2D_MSG_ENDPOINT "/api/v1/msg" - bool d2d_parse_command(char *msg); +httpd_cgi_state cgiD2DMessage(HttpdConnData *connData); + #endif //ESPTERM_CGI_D2D_H diff --git a/user/cgi_system.c b/user/cgi_system.c index 8418c5d..d23d20b 100755 --- a/user/cgi_system.c +++ b/user/cgi_system.c @@ -50,7 +50,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiResetDevice(HttpdConnData *connData) os_timer_setfn(&tmr, tmrCb, NULL); os_timer_arm(&tmr, 100, false); - httpdSend(connData, "system reset\n", -1); + httpdSend(connData, "system reset\r\n", -1); return HTTPD_CGI_DONE; } @@ -66,7 +66,7 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiPing(HttpdConnData *connData) httpdHeader(connData, "Content-Type", "text/plain"); httpdEndHeaders(connData); - httpdSend(connData, "pong\n", -1); + httpdSend(connData, "pong\r\n", -1); return HTTPD_CGI_DONE; } diff --git a/user/routes.c b/user/routes.c index 9dfd8a5..4b6b502 100644 --- a/user/routes.c +++ b/user/routes.c @@ -14,6 +14,8 @@ #include "cgi_persist.h" #include "syscfg.h" #include "persist.h" +#include "api.h" +#include "cgi_d2d.h" /** * Password for WiFi config @@ -45,7 +47,8 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData) break; case PWLOCK_SETTINGS_NOTERM: - protect = strstarts(connData->url, "/cfg") && !strstarts(connData->url, "/cfg/term"); + protect = strstarts(connData->url, "/cfg") && + !strstarts(connData->url, "/cfg/term"); break; case PWLOCK_SETTINGS_ALL: @@ -53,7 +56,9 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData) break; case PWLOCK_MENUS: - protect = strstarts(connData->url, "/cfg") || strstarts(connData->url, "/about") || strstarts(connData->url, "/help"); + protect = strstarts(connData->url, "/cfg") || + strstarts(connData->url, "/about") || + strstarts(connData->url, "/help"); break; default: @@ -64,11 +69,11 @@ httpd_cgi_state ICACHE_FLASH_ATTR cgiOptionalPwLock(HttpdConnData *connData) // pages outside the normal scope if (sysconf->pwlock > PWLOCK_NONE) { - if (strstarts(connData->url, "/system/reset")) protect = true; + if (strstarts(connData->url, "/api/v1/reboot")) protect = true; } if (sysconf->pwlock > PWLOCK_SETTINGS_NOTERM) { - if (strstarts(connData->url, "/system/cls")) protect = true; + if (strstarts(connData->url, "/api/v1/clear")) protect = true; } if (sysconf->access_pw[0] == 0) { @@ -103,9 +108,12 @@ const HttpdBuiltInUrl routes[] ESP_CONST_DATA = { ROUTE_WS(URL_WS_UPDATE, updateSockConnect), // --- System control --- - ROUTE_CGI("/system/reset/?", cgiResetDevice), - ROUTE_CGI("/system/ping/?", cgiPing), - ROUTE_CGI("/system/cls/?", cgiResetScreen), + + // API endpoints + ROUTE_CGI(API_REBOOT"/?", cgiResetDevice), + ROUTE_CGI(API_PING"/?", cgiPing), + ROUTE_CGI(API_CLEAR"/?", cgiResetScreen), + ROUTE_CGI(API_D2D_MSG"/?", cgiD2DMessage), ROUTE_REDIRECT("/cfg/?", "/cfg/wifi"), diff --git a/user/serial.c b/user/serial.c index 0a960cc..9c9edce 100644 --- a/user/serial.c +++ b/user/serial.c @@ -40,10 +40,10 @@ buf_pop(void *unused) } } -//LOCAL void my_putc(char c) -//{ -// UART_WriteCharCRLF(UART1, (u8) c, 10); -//} +LOCAL void my_putc(char c) +{ + UART_WriteCharCRLF(UART1, (u8) c, 10); +} /** * Init the serial ports @@ -56,8 +56,11 @@ void ICACHE_FLASH_ATTR serialInitBase(void) UART_SetStopBits(UART1, ONE_STOP_BIT); UART_SetBaudrate(UART1, BIT_RATE_115200); UART_SetPrintPort(UART1); +#if ASYNC_LOG os_install_putc1(buf_putc); - //os_install_putc1(my_putc); +#else + os_install_putc1(my_putc); +#endif UART_SetupAsyncReceiver(); // 1 ms timer