mirror of
https://github.com/ADElectronics/RTL00_WEB_VS.git
synced 2024-11-22 07:14:14 +00:00
2076 lines
85 KiB
C
2076 lines
85 KiB
C
/******************************************************************************
|
||
* FileName: webserver.c
|
||
* Description: Small WEB server + WebSocket
|
||
* Author: pvvx
|
||
* ver1.0 25/12/2014 SDK 0.9.4
|
||
* ver1.1 02/04/2015 SDK 1.0.0
|
||
* ver2.0 14/14/2017 RTL871x
|
||
*******************************************************************************/
|
||
#include "user_config.h"
|
||
#ifdef USE_WEB
|
||
#include "autoconf.h"
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
#include "diag.h"
|
||
#include "lwip/tcp.h"
|
||
#include "tcpsrv/tcp_srv_conn.h"
|
||
#include "web_srv_int.h"
|
||
#include "web_utils.h"
|
||
#include "flash_eep.h"
|
||
#include "device_lock.h"
|
||
#include "webfs/webfs.h"
|
||
#include "sys_cfg.h"
|
||
#include "wifi_api.h"
|
||
#include "rtl8195a/rtl_libc.h"
|
||
#include "esp_comp.h"
|
||
|
||
#ifdef WEBSOCKET_ENA
|
||
#include "web_websocket.h"
|
||
#endif
|
||
|
||
#ifdef USE_CAPTDNS
|
||
#include "captdns.h"
|
||
#endif
|
||
|
||
#ifdef USE_OVERLAY
|
||
#include "overlay.h"
|
||
#endif
|
||
|
||
#define USE_WEB_NAGLE // https://en.wikipedia.org/wiki/Nagle%27s_algorithm
|
||
#define MIN_REQ_LEN 7 // Minimum length for a valid HTTP/0.9 request: "GET /\r\n" -> 7 bytes
|
||
#define CRLF "\r\n"
|
||
|
||
#define max_len_buf_write_flash 2048 // размер буфера при записи flash. Увеличение/уменньшение размера (до сектора 4096) ускорения не дает (1..2%)
|
||
|
||
//#define mMIN(a, b) ((a<b)?a:b)
|
||
//#define mMAX(a, b) ((a>b)?a:b)
|
||
#undef atoi
|
||
#define atoi(s) rom_atoi(s)
|
||
|
||
static void web_print_headers(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) ICACHE_FLASH_ATTR ;
|
||
|
||
//static void webserver_discon(void *arg) ICACHE_FLASH_ATTR;
|
||
//static void webserver_recon(void *arg, sint8 err) ICACHE_FLASH_ATTR;
|
||
static void webserver_send_fdata(TCP_SERV_CONN *ts_conn) ICACHE_FLASH_ATTR;
|
||
static void web_int_disconnect(TCP_SERV_CONN *ts_conn) ICACHE_FLASH_ATTR;
|
||
static bool webserver_open_file(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn) ICACHE_FLASH_ATTR;
|
||
static void webserver_file_ext(HTTP_CONN *CurHTTP, uint8 *pfname) ICACHE_FLASH_ATTR;
|
||
|
||
const char http_default_file[] ICACHE_RODATA_ATTR = "index.html";
|
||
const char web_cgi_fname[] ICACHE_RODATA_ATTR = "web.cgi";
|
||
const char fsupload_fname[] ICACHE_RODATA_ATTR = "fsupload";
|
||
#ifdef USE_CAPTDNS
|
||
const char ncsi_txt_fname[] ICACHE_RODATA_ATTR = "ncsi.txt";
|
||
//const char generate_204_fname[] ICACHE_RODATA_ATTR = "generate_204";
|
||
const char *HTTPHost ="Host:";
|
||
#define sizeHTTPHost 5
|
||
#endif
|
||
#define ProtectedFilesName "protect"
|
||
|
||
#define MAX_NO_DATA_BUF_SIZE (8192) // if(ts_conn->sizei > MAX_NO_DATA_BUF_SIZE) CurHTTP->httpStatus = 418; // 418: Out of Coffee
|
||
|
||
QueueHandle_t xQueueWebSrv;
|
||
|
||
/****************************************************************************
|
||
Section:
|
||
File and Content Type Settings
|
||
***************************************************************************/
|
||
// File type extensions corresponding to HTTP_FILE_TYPE
|
||
static const char *httpFileExtensions[] = {
|
||
"txt", // HTTP_TXT
|
||
"html", // HTTP_HTML
|
||
"cgi", // HTTP_CGI
|
||
"xml", // HTTP_XML
|
||
"css", // HTTP_CSS
|
||
"ico", // HTTP_ICO
|
||
"gif", // HTTP_GIF
|
||
"png", // HTTP_PNG
|
||
"jpg", // HTTP_JPG
|
||
"svg", // HTTP_SVG
|
||
"js", // HTTP_JAVA
|
||
"swf", // HTTP_SWF
|
||
"wav", // HTTP_WAV
|
||
"pdf", // HTTP_PDF
|
||
"zip", // HTTP_ZIP
|
||
"bin", // HTTP_BIN
|
||
"\0\0\0" // HTTP_UNKNOWN
|
||
};
|
||
|
||
// Content-type strings corresponding to HTTP_FILE_TYPE
|
||
static const char *httpContentTypes[] = {
|
||
"text/plain", // HTTP_TXT "txt",
|
||
"text/html", // HTTP_HTM "htm",
|
||
"magnus-internal/cgi", // HTTP_CGI "cgi",
|
||
"text/xml", // HTTP_XML "xml",
|
||
"text/css", // HTTP_CSS "css",
|
||
"image/vnd.microsoft.icon", // HTTP_ICO "ico",
|
||
"image/gif", // HTTP_GIF "gif",
|
||
"image/png", // HTTP_PNG "png",
|
||
"image/jpeg", // HTTP_JPG "jpg",
|
||
"image/svg+xml", // HTTP_SVG "svg",
|
||
"text/javascript", // HTTP_JAVA "js",
|
||
"application/x-shockwave-flash", // HTTP_SWF "swf",
|
||
"audio/x-wave", // HTTP_WAV "wav",
|
||
"application/pdf", // HTTP_PDF "pdf",
|
||
"application/zip", // HTTP_ZIP "zip",
|
||
"application/octet-stream", // HTTP_BIN "bin",
|
||
"" // HTTP_UNKNOWN
|
||
};
|
||
/****************************************************************************
|
||
Section:
|
||
Commands and Server Responses
|
||
***************************************************************************/
|
||
const char HTTPresponse_200_head[] ICACHE_RODATA_ATTR = "OK";
|
||
const char HTTPresponse_302_head[] ICACHE_RODATA_ATTR = "Found";
|
||
const char HTTPresponse_304_head[] ICACHE_RODATA_ATTR = "Not Modified";
|
||
const char HTTPresponse_400_head[] ICACHE_RODATA_ATTR = "Bad Request";
|
||
const char HTTPresponse_401_head[] ICACHE_RODATA_ATTR = "Unauthorized\r\nWWW-Authenticate: Basic realm=\"Protected%u\"";
|
||
const char HTTPresponse_404_head[] ICACHE_RODATA_ATTR = "Not found";
|
||
const char HTTPresponse_411_head[] ICACHE_RODATA_ATTR = "Length Required";
|
||
const char HTTPresponse_413_head[] ICACHE_RODATA_ATTR = "Request Entity Too Large";
|
||
const char HTTPresponse_414_head[] ICACHE_RODATA_ATTR = "Request-URI Too Long";
|
||
const char HTTPresponse_418_head[] ICACHE_RODATA_ATTR = "I'm a teapot";
|
||
const char HTTPresponse_429_head[] ICACHE_RODATA_ATTR = "Too Many Requests\r\nRetry-After: 30";
|
||
const char HTTPresponse_500_head[] ICACHE_RODATA_ATTR = "Internal Server Error";
|
||
const char HTTPresponse_501_head[] ICACHE_RODATA_ATTR = "Not Implemented\r\nAllow: GET, POST";
|
||
|
||
const char HTTPresponse_401_content[] ICACHE_RODATA_ATTR = "401 Unauthorized: Password required\r\n";
|
||
const char HTTPresponse_404_content[] ICACHE_RODATA_ATTR = "404: File not found\r\n";
|
||
const char HTTPresponse_411_content[] ICACHE_RODATA_ATTR = "411 The request must have a content length\r\n";
|
||
const char HTTPresponse_413_content[] ICACHE_RODATA_ATTR = "413 Request Entity Too Large: There's too many letters :)\r\n";
|
||
const char HTTPresponse_414_content[] ICACHE_RODATA_ATTR = "414 Request-URI Too Long: Buffer overflow detected\r\n";
|
||
const char HTTPresponse_418_content[] ICACHE_RODATA_ATTR = "418: Out of Coffee\r\n";
|
||
const char HTTPresponse_500_content[] ICACHE_RODATA_ATTR = "500 Internal Server Error\r\n";
|
||
const char HTTPresponse_501_content[] ICACHE_RODATA_ATTR = "501 Not Implemented: Only GET and POST supported\r\n";
|
||
|
||
// Initial response strings (Corresponding to HTTP_STATUS)
|
||
static const HTTP_RESPONSE ICACHE_RODATA_ATTR HTTPResponse[] ICACHE_RODATA_ATTR = {
|
||
{ 200, HTTP_RESP_FLG_NONE,
|
||
HTTPresponse_200_head,
|
||
NULL },
|
||
// успешный запрос. Если клиентом были запрошены какие-либо данные, то они находятся в заголовке и/или теле сообщения.
|
||
{ 302, HTTP_RESP_FLG_NONE | HTTP_RESP_FLG_REDIRECT,
|
||
HTTPresponse_302_head,
|
||
NULL },
|
||
// "HTTP/1.1 302 Found\r\nConnection: close\r\nLocation: ",
|
||
// 302 Found, 302 Moved Temporarily - запрошенный документ временно
|
||
// доступен по другому URI, указанному в заголовке в поле Location.
|
||
// Этот код может быть использован, например, при управляемом сервером
|
||
// согласовании содержимого. Некоторые клиенты некорректно ведут себя
|
||
// при обработке данного кода.
|
||
{ 304, HTTP_RESP_FLG_NONE,
|
||
HTTPresponse_304_head,
|
||
NULL },
|
||
///"304 Redirect: ", // If-Modified-Since If-None-Match
|
||
// сервер возвращает такой код, если клиент запросил документ методом GET,
|
||
// использовал заголовок If-Modified-Since или If-None-Match и документ
|
||
// не изменился с указанного момента. При этом сообщение сервера не должно содержать тела.
|
||
{ 400, HTTP_RESP_FLG_FINDFILE,
|
||
HTTPresponse_400_head,
|
||
NULL} ,
|
||
// сервер обнаружил в запросе клиента синтаксическую ошибку.
|
||
{ 401, HTTP_RESP_FLG_FINDFILE,
|
||
HTTPresponse_401_head,
|
||
HTTPresponse_401_content },
|
||
// для доступа к запрашиваемому ресурсу требуется аутентификация.
|
||
// В заголовке ответ должен содержать поле WWW-Authenticate с перечнем
|
||
// условий аутентификации. Клиент может повторить запрос,
|
||
// включив в заголовок сообщения поле Authorization с требуемыми для аутентификации данными.
|
||
//"HTTP/1.1 403 Forbidden\r\nConnection: close\r\n\r\n403 Forbidden: SSL Required - use HTTPS\r\n"
|
||
{ 404, HTTP_RESP_FLG_FINDFILE,
|
||
HTTPresponse_404_head,
|
||
HTTPresponse_404_content },
|
||
// Сервер понял запрос, но не нашёл соответствующего ресурса по указанному URI.
|
||
{ 411, HTTP_RESP_FLG_FINDFILE,
|
||
HTTPresponse_411_head,
|
||
HTTPresponse_411_content },
|
||
// для указанного ресурса клиент должен указать Content-Length в заголовке запроса.
|
||
// Без указания этого поля не стоит делать повторную попытку запроса к серверу по данному URI.
|
||
// Такой ответ естественен для запросов типа POST и PUT.
|
||
// Например, если по указанному URI производится загрузка файлов, а на сервере стоит
|
||
// ограничение на их объём. Тогда разумней будет проверить в самом начале заголовок
|
||
// Content-Length и сразу отказать в загрузке, чем провоцировать бессмысленную нагрузку,
|
||
// разрывая соединение, когда клиент действительно пришлёт слишком объёмное сообщение.
|
||
{ 413, HTTP_RESP_FLG_FINDFILE,
|
||
HTTPresponse_413_head,
|
||
HTTPresponse_413_content },
|
||
// возвращается в случае, если сервер отказывается обработать запрос
|
||
// по причине слишком большого размера тела запроса. Сервер может закрыть соединение,
|
||
// чтобы прекратить дальнейшую передачу запроса.
|
||
{ 414, HTTP_RESP_FLG_FINDFILE,
|
||
HTTPresponse_414_head,
|
||
HTTPresponse_414_content },
|
||
// сервер не может обработать запрос из-за слишком длинного указанного URL.
|
||
// Такую ошибку можно спровоцировать, например, когда клиент пытается передать длинные
|
||
// параметры через метод GET, а не POST.
|
||
{ 429, HTTP_RESP_FLG_NONE,
|
||
HTTPresponse_429_head,
|
||
NULL },
|
||
// клиент попытался отправить слишком много запросов за короткое время, что может указывать,
|
||
// например, на попытку DoS-атаки. Может сопровождаться заголовком Retry-After, указывающим,
|
||
// через какое время можно повторить запрос.
|
||
{ 501, HTTP_RESP_FLG_FINDFILE,
|
||
HTTPresponse_501_head,
|
||
HTTPresponse_501_content },
|
||
// сервер не поддерживает возможностей, необходимых для обработки запроса.
|
||
// Типичный ответ для случаев, когда сервер не понимает указанный в запросе метод. + см 405
|
||
{ 418, HTTP_RESP_FLG_FINDFILE,
|
||
HTTPresponse_418_head,
|
||
HTTPresponse_418_content },
|
||
// http://en.wikipedia.org/wiki/Hyper_Text_Coffee_Pot_Control_Protocol
|
||
{ 500, HTTP_RESP_FLG_END,
|
||
HTTPresponse_500_head,
|
||
HTTPresponse_500_content }
|
||
// любая внутренняя ошибка сервера, которая не входит в рамки остальных ошибок класса.
|
||
};
|
||
const char HTTPfsupload[] ICACHE_RODATA_ATTR = "<html><body style='margin:100px'><form method='post' action='/fsupload' enctype='multipart/form-data'><b>File Upload</b><p><input type='file' name='file' size=40> <input type='submit' value='Upload'></form></body></html>";
|
||
#define sizeHTTPfsupload 220
|
||
const char HTTPdefault[] ICACHE_RODATA_ATTR = "<html><h3>RTL871X Built-in Web server <sup><i>©</i></sup></h3></html>";
|
||
#define sizeHTTPdefault 73
|
||
const char HTTPfserror[] ICACHE_RODATA_ATTR = "<html><h3>Web-disk error. Upload the WEBFiles.bin!</h3></html>";
|
||
#define sizeHTTPfserror 62
|
||
|
||
const char HTTPAccessControlAllowOrigin[] ICACHE_RODATA_ATTR = "Access-Control-Allow-Origin: *\r\n";
|
||
// const uint8 *HTTPCacheControl = "Cache-Control:";
|
||
const char *HTTPContentLength = "Content-Length:";
|
||
#define sizeHTTPContentLength 15
|
||
// const uint8 *HTTPConnection = "Connection: ";
|
||
// #define sizeHTTPConnection 12
|
||
// const uint8 *HTTPkeepalive = "keep-alive";
|
||
// #define sizeHTTPkeepalive 10
|
||
// const uint8 *HTTPIfNoneMatch = "If-None-Match:"
|
||
// #define sizeHTTPIfNoneMatch 14
|
||
const char *HTTPContentType = "Content-Type:";
|
||
#define sizeHTTPContentType 13
|
||
const char *HTTPmultipartformdata = "multipart/form-data";
|
||
#define sizeHTTPmultipartformdata 19
|
||
const char *HTTPboundary = "boundary=";
|
||
#define sizeHTTPboundary 9
|
||
const char *HTTPAuthorization = "Authorization:";
|
||
#define sizeHTTPAuthorization 14
|
||
const char *HTTPCookie = "Cookie:";
|
||
#define sizeHTTPCookie 7
|
||
|
||
/******************************************************************************
|
||
* FunctionName : Close_web_conn
|
||
* Description : Free ts_conn
|
||
* Parameters : struct TCP_SERV_CONN
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
static void ICACHE_FLASH_ATTR Close_web_conn(TCP_SERV_CONN *ts_conn)
|
||
{
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
int i = 0;
|
||
do {
|
||
if(web_conn->bffiles[i] != WEBFS_INVALID_HANDLE) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("cf%d ", web_conn->bffiles[i]);
|
||
#endif
|
||
if(web_conn->bffiles[i] <= WEBFS_MAX_HANDLE) WEBFSClose(web_conn->bffiles[i]);
|
||
web_conn->bffiles[i] = WEBFS_INVALID_HANDLE;
|
||
};
|
||
i++;
|
||
}while(i < 4);
|
||
ClrSCB(SCB_FOPEN | SCB_FGZIP | SCB_FCALBACK);
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : ReNew_web_conn
|
||
* Description :
|
||
* Parameters : struct TCP_SERV_CONN
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
static WEB_SRV_CONN * ICACHE_FLASH_ATTR ReNew_web_conn(TCP_SERV_CONN *ts_conn)
|
||
{
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
if(web_conn == NULL) {
|
||
web_conn = (WEB_SRV_CONN *)os_zalloc(sizeof(WEB_SRV_CONN));
|
||
if(web_conn != NULL) {
|
||
web_conn->bffiles[0] = WEBFS_INVALID_HANDLE;
|
||
web_conn->bffiles[1] = WEBFS_INVALID_HANDLE;
|
||
web_conn->bffiles[2] = WEBFS_INVALID_HANDLE;
|
||
web_conn->bffiles[3] = WEBFS_INVALID_HANDLE;
|
||
// web_conn->webflag = 0; //zalloc
|
||
// web_conn->func_web_cb = NULL; //zalloc
|
||
OpenSCB(); // сбросить флаги
|
||
ts_conn->linkd = (void *)web_conn;
|
||
};
|
||
}
|
||
return web_conn;
|
||
}
|
||
//=============================================================================
|
||
// Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\n"
|
||
// The resulting string is then encoded using the RFC2045-MIME variant of Base64,
|
||
// except not limited to 76 uint8/line
|
||
// /ssl/crypto/ssl_crypto_misc.c:
|
||
// EXP_FUNC int STDCALL base64_decode(const uint8 *in, int len, uint8_t *out, int *outlen);
|
||
// Username and password are combined into a string "username:password"
|
||
static uint8 ICACHE_FLASH_ATTR CheckAuthorization(uint8* base64str)
|
||
{
|
||
uint8 *pcmp = base64str;
|
||
int len = 0;
|
||
while(*pcmp++ >= '+') len++;
|
||
// struct softap_config apcfg;
|
||
uint8 pbuf[77];
|
||
int declen = 76;
|
||
if((len >= 4)&&(len <= 128)
|
||
&&(base64decode(base64str, len, pbuf, &declen))) {
|
||
pbuf[declen]='\0';
|
||
#if DEBUGSOO > 1
|
||
os_printf("'%s' ", pbuf);
|
||
#endif
|
||
return UserAuthorization(pbuf, declen);
|
||
};
|
||
return 0;
|
||
}
|
||
//=============================================================================
|
||
#define web_parse_cookie(CurHTTP, ts_conn) web_parse_vars(ts_conn, (CurHTTP)->pcookie, (CurHTTP)->cookie_len, '\0', ';')
|
||
#define web_parse_uri_vars(CurHTTP, ts_conn) web_parse_vars(ts_conn, (CurHTTP)->puri, (CurHTTP)->uri_len, '?', '&')
|
||
#define web_parse_content(CurHTTP, ts_conn) web_parse_vars(ts_conn, (CurHTTP)->pcontent, (CurHTTP)->content_len, '\0', '&')
|
||
static void ICACHE_FLASH_ATTR web_parse_vars(TCP_SERV_CONN *ts_conn, uint8 *vars, uint32 vars_len, uint8 start_char, uint8 end_char)
|
||
{
|
||
if(vars == NULL || vars_len == 0) return;
|
||
uint8 *pcmp;
|
||
if(start_char) {
|
||
pcmp = cmpcpystr(NULL, vars, '\0', start_char, vars_len); // find start_char if available
|
||
start_char = '\0';
|
||
} else pcmp = vars - 1;
|
||
while(pcmp != NULL) {
|
||
uint16 len = vars_len - (pcmp - vars);
|
||
uint8 *pcmd = pcmp;
|
||
pcmp = cmpcpystr(pcmp, pcmp + 1, start_char, '=', len); // skip spaces before variable name
|
||
if(pcmp == NULL) break;
|
||
urldecode(pcmd, pcmd, len, len);
|
||
len = vars_len - (pcmp - vars);
|
||
uint8 *pvar = pcmp;
|
||
pcmp = cmpcpystr(pcmp, pcmp + 1, '\0', end_char, len);
|
||
if(pcmd[0] != '\0') {
|
||
urldecode(pvar, pvar, len, len);
|
||
web_int_vars(ts_conn, pcmd, pvar);
|
||
}
|
||
}
|
||
}
|
||
//=============================================================================
|
||
// Разбор имени файла и перевод в вид относительного URI.
|
||
// (выкидывание HTTP://Host)
|
||
// Проверка на обращение в папку или имя файла требующее пароль
|
||
//=============================================================================
|
||
static void ICACHE_FLASH_ATTR
|
||
web_parse_fname(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn)
|
||
{
|
||
if(CurHTTP->puri == NULL)
|
||
return;
|
||
|
||
if(CurHTTP->uri_len < 2)
|
||
{ // = "/"?
|
||
CurHTTP->pFilename[0] = CurHTTP->puri[0];
|
||
return;
|
||
}
|
||
|
||
{
|
||
uint8 cbuf[FileNameSize+16];
|
||
uint8 *pcbuf = cbuf;
|
||
urldecode(pcbuf, CurHTTP->puri, sizeof(cbuf) - 1, CurHTTP->uri_len);
|
||
if(rom_xstrcmp((char *)pcbuf, "HTTP://")||(rom_xstrcmp((char *)pcbuf, "http://"))) {
|
||
pcbuf += 7;
|
||
uint8 *pcmp = os_strchr((char *)pcbuf, '/');
|
||
if(pcmp != NULL) pcbuf = pcmp;
|
||
};
|
||
cmpcpystr(CurHTTP->pFilename, pcbuf, '\0', '?', FileNameSize);
|
||
}
|
||
|
||
{ // Проверка на обращение в папку или имя файла требующее пароль
|
||
uint8 *pcmp = web_strnstr(CurHTTP->pFilename, ProtectedFilesName, os_strlen(CurHTTP->pFilename));
|
||
if(pcmp != NULL)
|
||
{
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
#if USE_WEB_AUTH_LEVEL
|
||
pcmp += sizeof(ProtectedFilesName) - 1;
|
||
web_conn->auth_realm = atoi(pcmp) + 1;
|
||
printf("[%s] ar%d ", pcmp, web_conn->auth_realm);
|
||
// web_conn->auth_realm = WEB_AUTH_LEVEL_USER;
|
||
#endif
|
||
SetSCB(SCB_AUTH);
|
||
}
|
||
}
|
||
}
|
||
//=============================================================================
|
||
//=============================================================================
|
||
uint8 * ICACHE_FLASH_ATTR head_find_ctr(HTTP_CONN *CurHTTP, const uint8 * c, int clen, int dlen)
|
||
{
|
||
if(CurHTTP->head_len < clen + dlen + 2) return NULL; // + "\r\n"
|
||
uint8 * pstr = web_strnstr((char *)CurHTTP->phead, c, CurHTTP->head_len);
|
||
if(pstr != NULL) {
|
||
pstr += clen;
|
||
uint8 *pend = web_strnstr(pstr, CRLF, CurHTTP->phead + CurHTTP->head_len - pstr);
|
||
if(pend == NULL) {
|
||
CurHTTP->httpStatus = 400; // 400 Bad Request
|
||
return NULL;
|
||
}
|
||
while(*pstr == ' ' && pstr < pend) pstr++;
|
||
if(pend - pstr < dlen) {
|
||
CurHTTP->httpStatus = 400; // 400 Bad Request
|
||
return NULL;
|
||
}
|
||
}
|
||
return pstr;
|
||
}
|
||
//=============================================================================
|
||
// Func: parse_header
|
||
// Разбирает докачан или нет заголовок HTTP, что там принято, GET или POST,
|
||
// открывает файл и проверяет content, если это POST и не прием файла.
|
||
//=============================================================================
|
||
static bool ICACHE_FLASH_ATTR
|
||
parse_header(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn)
|
||
{
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
CurHTTP->httpStatus = 501; // 501 Not Implemented (not a GET or POST command)
|
||
|
||
uint8 *pstr = ts_conn->pbufi;
|
||
uint8 *pend = &ts_conn->pbufi[ts_conn->sizei];
|
||
CurHTTP->pcontent = pend;
|
||
|
||
if(pstr == NULL) {
|
||
CurHTTP->httpStatus = 500; // 500 Internal Server Error
|
||
return false;
|
||
};
|
||
if(ts_conn->sizei < MIN_REQ_LEN) return false; // 501 Not Implemented (not a GET or POST command)
|
||
uint8 *pnext = web_strnstr(pstr, CRLF, ts_conn->sizei); // "\r\n"
|
||
// if(pnext != NULL) *pnext = '\0';
|
||
if(pnext == NULL) {
|
||
CurHTTP->httpStatus = 400; // 400 Bad Request
|
||
return false;
|
||
};
|
||
pnext += 2;
|
||
if(pnext - pstr < MIN_REQ_LEN) return false; // 501 размер строки запроса менее "GET /"
|
||
if(os_strncmp(pstr, "GET ", 4) == 0) {
|
||
SetSCB(SCB_GET);
|
||
CurHTTP->httpStatus = 200;
|
||
pstr += 4;
|
||
}
|
||
else if(os_strncmp(pstr, "POST ", 5) == 0) {
|
||
SetSCB(SCB_POST);
|
||
CurHTTP->httpStatus = 200;
|
||
pstr += 5;
|
||
}
|
||
else return false; // 501 Not Implemented (not a GET or POST command)
|
||
CurHTTP->puri = pstr;
|
||
CurHTTP->uri_len = pnext - pstr;
|
||
|
||
if(CurHTTP->uri_len > 10) { // "/ HTTP/1.0\r\n"
|
||
pstr = web_strnstr(CurHTTP->puri, " HTTP/", CurHTTP->uri_len);
|
||
if(pstr != NULL) {
|
||
if((pstr[7] == '.')&&(pstr[6] <= '9')&&(pstr[6] >= '0')&&(pstr[8] >= '0')&&(pstr[8] <= '9'))
|
||
CurHTTP->httpver = ((pstr[6]-'0')<<4) + pstr[8]-'0';
|
||
// else CurHTTP->ver = 0x00;
|
||
};
|
||
};
|
||
#if DEBUGSOO > 3
|
||
os_printf("http_ver=%02x ", CurHTTP->httpver);
|
||
#endif
|
||
if(CurHTTP->httpver < 0x10) { // HTTP/0.9 ?
|
||
if(CheckSCB(SCB_POST)) {
|
||
CurHTTP->httpStatus = 400; // 400 HTTP/0.9 does not support POST
|
||
return false; // HTTP/0.9
|
||
};
|
||
};
|
||
// здесь уже надо глядеть - следует или нет докачивать данные
|
||
pstr = web_strnstr(pnext-2, CRLF CRLF, pend - pnext + 2 ); // find "\r\n\r\n"
|
||
if(pstr == NULL) return true; // докачивать!
|
||
// разбираем дальше Header, раз уже скачан
|
||
pstr += 2;
|
||
if(pstr != pnext) { // есть Headers
|
||
CurHTTP->phead = pnext;
|
||
CurHTTP->head_len = pstr - pnext;
|
||
if(CheckSCB(SCB_POST)){
|
||
pstr += 2;
|
||
CurHTTP->pcontent = pstr;
|
||
CurHTTP->content_len = pend - pstr;
|
||
};
|
||
};
|
||
if(!CheckSCB(SCB_FOPEN)) { // файл уже открыт? нет
|
||
web_parse_fname(CurHTTP, ts_conn);
|
||
if(!webserver_open_file(CurHTTP, ts_conn)) {
|
||
CurHTTP->httpStatus = 404; // "404: File not found"
|
||
return false; //
|
||
};
|
||
};
|
||
if((CurHTTP->phead == NULL)||(CurHTTP->head_len == 0)) {
|
||
// если требуется авторизация, но нет передачи пароля...
|
||
if(CheckSCB(SCB_AUTH)) CurHTTP->httpStatus = 401; // 401 Unauthorized
|
||
return false; // нет Header
|
||
};
|
||
if(CheckSCB(SCB_POST)) {
|
||
pstr = head_find_ctr(CurHTTP, HTTPContentLength, sizeHTTPContentLength, 1);
|
||
if(pstr == NULL || CurHTTP->httpStatus != 200) {
|
||
CurHTTP->httpStatus = 411; // no "Content Length:", 411 Length Required
|
||
return false;
|
||
}
|
||
uint32 cnlen = atoi(pstr);
|
||
#if DEBUGSOO > 1
|
||
os_printf("content_len = %d of %d ", cnlen, CurHTTP->content_len);
|
||
#endif
|
||
if(cnlen) {
|
||
web_conn->content_len = cnlen; // запомнить размер, для приема файла
|
||
if(!CheckSCB(SCB_BNDR) && (CurHTTP->head_len > sizeHTTPContentType + sizeHTTPmultipartformdata + sizeHTTPboundary + 2 + 2)) { //"x\r\n"
|
||
pstr = head_find_ctr(CurHTTP, HTTPContentType, sizeHTTPContentType, sizeHTTPmultipartformdata + sizeHTTPboundary + 2);
|
||
if(CurHTTP->httpStatus != 200) return false;
|
||
if(pstr != NULL) {
|
||
pend = web_strnstr(pstr, CRLF, CurHTTP->phead + CurHTTP->head_len - pstr);
|
||
pstr = web_strnstr(pstr, HTTPmultipartformdata, pend - pstr);
|
||
if(pstr != NULL) {
|
||
pstr += sizeHTTPmultipartformdata;
|
||
pstr = web_strnstr(pstr, HTTPboundary, pend - pstr);
|
||
if(pstr != NULL) {
|
||
// сохраним этот "мультипаспорт" (с) 5-ый элемент :)
|
||
pstr += sizeHTTPboundary;
|
||
HTTP_UPLOAD *pupload = (HTTP_UPLOAD *)os_zalloc(sizeof(HTTP_UPLOAD));
|
||
if(pupload == NULL) {
|
||
CurHTTP->httpStatus = 500; // 500 Internal Server Error
|
||
return false;
|
||
}
|
||
uint8 x = *pend;
|
||
*pend = '\0';
|
||
#if DEBUGSOO > 4
|
||
os_printf("[%s] ", pstr);
|
||
#endif
|
||
rtl_memcpy(pupload->boundary, pstr, MAXLENBOUNDARY);
|
||
*pend = x;
|
||
pupload->sizeboundary = os_strlen(pupload->boundary);
|
||
ts_conn->pbufo = (uint8 *)pupload;
|
||
SetSCB(SCB_BNDR);
|
||
// if(cnlen > ((pupload->sizeboundary * 2) + 18)) {
|
||
SetSCB(SCB_RXDATA);
|
||
// }
|
||
};
|
||
};
|
||
};
|
||
};
|
||
if((!CheckSCB(SCB_BNDR)) && cnlen > CurHTTP->content_len) { // обычный контент и недокачан заголовок? да.
|
||
CurHTTP->content_len = cnlen;
|
||
#if DEBUGSOO > 2
|
||
os_printf("wait content ");
|
||
#endif
|
||
CurHTTP->httpStatus = 413; // 413 Request Entity Too Large // пока так
|
||
return true; // докачивать
|
||
};
|
||
}
|
||
else CurHTTP->content_len = cnlen; // уточнить, что Content Length = 0
|
||
};
|
||
if(CheckSCB(SCB_AUTH)) {
|
||
pstr = head_find_ctr(CurHTTP, HTTPAuthorization, sizeHTTPAuthorization, 5 + 3); // "Authorization: Basic 1234\r\n"
|
||
if(pstr == NULL || CurHTTP->httpStatus != 200) {
|
||
CurHTTP->httpStatus = 401; // 401 Unauthorized
|
||
return false;
|
||
}
|
||
if(os_strncmp(pstr, "Basic", 5) == 0) { // The authorization method and a space i.e. "Basic" is then put before the encoded string.
|
||
pstr += 5;
|
||
while(*pstr == ' ') pstr++;
|
||
#if USE_WEB_AUTH_LEVEL
|
||
web_conn->auth_level = CheckAuthorization(pstr);
|
||
#if DEBUGSOO > 1
|
||
os_printf("%u?%u ", web_conn->auth_level, web_conn->auth_realm);
|
||
#endif
|
||
if(web_conn->auth_level >= web_conn->auth_realm)
|
||
ClrSCB(SCB_AUTH);
|
||
#else
|
||
if(CheckAuthorization(pstr))
|
||
ClrSCB(SCB_AUTH);
|
||
#endif
|
||
else {
|
||
CurHTTP->httpStatus = 401; // 401 Unauthorized
|
||
return false;
|
||
};
|
||
}
|
||
else {
|
||
CurHTTP->httpStatus = 401; // 401 Unauthorized
|
||
return false;
|
||
};
|
||
};
|
||
|
||
if(CurHTTP->head_len > sizeHTTPCookie + 4) { // "Cookie: a=\r\n"
|
||
pstr = head_find_ctr(CurHTTP, HTTPCookie, sizeHTTPCookie, 2);
|
||
if(pstr != NULL) {
|
||
pend = web_strnstr(pstr, CRLF, CurHTTP->phead + CurHTTP->head_len - pstr);
|
||
if(pend != NULL) {
|
||
CurHTTP->pcookie = pstr;
|
||
CurHTTP->cookie_len = pend - pstr;
|
||
#if DEBUGSOO > 3
|
||
*pend = '\0';
|
||
os_printf("cookie:[%s] ", pstr);
|
||
*pend = '\r';
|
||
#endif
|
||
}
|
||
#if DEBUGSOO > 3
|
||
else os_printf("cookie not crlf! ");
|
||
#endif
|
||
};
|
||
};
|
||
#ifdef WEBSOCKET_ENA
|
||
if(CheckSCB(SCB_GET) && web_conn->bffiles[0] == WEBFS_WEBCGI_HANDLE) {
|
||
#if DEBUGSOO > 3
|
||
os_printf("hdlen=%d ", CurHTTP->head_len);
|
||
#endif
|
||
if(CurHTTP->head_len > sizeHTTPUpgrade + sizeHTTPwebsocket + 2 + sizeHTTPSecWebSocketKey + minsizeWebSocketKey + 2) { // + "\r\n"
|
||
pstr = head_find_ctr(CurHTTP, HTTPUpgrade, sizeHTTPUpgrade, sizeHTTPwebsocket);
|
||
if(CurHTTP->httpStatus != 200) return false;
|
||
if(pstr != NULL) {
|
||
if(!rom_xstrcmp(word_to_lower_case(pstr), HTTPwebsocket)) {
|
||
CurHTTP->httpStatus = 400; // 400 Bad Request
|
||
return false;
|
||
}
|
||
pstr = head_find_ctr(CurHTTP, HTTPSecWebSocketKey, sizeHTTPSecWebSocketKey, minsizeWebSocketKey);
|
||
if(pstr == NULL || CurHTTP->httpStatus != 200) return false;
|
||
{
|
||
if(WebSocketAcceptKey(CurHTTP->pFilename, pstr)) SetSCB(SCB_WEBSOC);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
#endif
|
||
return false;
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : web_inc_fp
|
||
* Parameters : fp
|
||
*******************************************************************************/
|
||
static void ICACHE_FLASH_ATTR web_inc_fp(WEB_SRV_CONN *web_conn, WEBFS_HANDLE fp)
|
||
{
|
||
if(web_conn->bffiles[3] != WEBFS_INVALID_HANDLE) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("cf%d ", web_conn->bffiles[3]);
|
||
#endif
|
||
if(web_conn->bffiles[3] <= WEBFS_MAX_HANDLE) {
|
||
web_conn->content_len -= WEBFSGetBytesRem(web_conn->bffiles[3]);
|
||
WEBFSClose(web_conn->bffiles[3]);
|
||
}
|
||
};
|
||
web_conn->bffiles[3] = web_conn->bffiles[2];
|
||
web_conn->bffiles[2] = web_conn->bffiles[1];
|
||
web_conn->bffiles[1] = web_conn->bffiles[0];
|
||
web_conn->bffiles[0] = fp;
|
||
SetSCB(SCB_FOPEN); // файл открыт
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : web_inc_fopen
|
||
* Description : web include file open
|
||
* Parameters : struct
|
||
* Returns : true - open OK
|
||
*******************************************************************************/
|
||
bool ICACHE_FLASH_ATTR web_inc_fopen(TCP_SERV_CONN *ts_conn, uint8 *cFile)
|
||
{
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
if(CheckSCB(SCB_FOPEN) && (!CheckSCB(SCB_FCALBACK))) { // файл уже открыт и он не парсится?
|
||
return false; // такое не поддерживается в "~inc:filename~"
|
||
};
|
||
WEBFS_HANDLE fp = WEBFSOpen(cFile);
|
||
#if DEBUGSOO > 1
|
||
os_printf("of%d[%s] ", fp, cFile);
|
||
#endif
|
||
if(fp != WEBFS_INVALID_HANDLE) {
|
||
if(fatCache.flags & WEBFS_FLAG_HASINDEX) SetSCB(SCB_FCALBACK); // файл надо парсить
|
||
web_conn->content_len += WEBFSGetBytesRem(fp); // указать размер файла для вывода
|
||
if(fatCache.flags & WEBFS_FLAG_ISZIPPED) {
|
||
if(CheckSCB(SCB_FOPEN)) { // файл уже открыт и "~inc:filename~" не поддерживает GZIP!
|
||
WEBFSClose(fp);
|
||
#if DEBUGSOO > 1
|
||
os_printf("Not inc GZIP! ");
|
||
#endif
|
||
return false;
|
||
};
|
||
SetSCB(SCB_FGZIP); // файл сжат GZIP
|
||
}
|
||
}
|
||
else { // File not found
|
||
return false;
|
||
};
|
||
web_inc_fp(web_conn, fp);
|
||
return true;
|
||
};
|
||
/******************************************************************************
|
||
* FunctionName : web_inc_file
|
||
* Description : web include file close
|
||
* Parameters : struct
|
||
* Returns : true - все файлы закрыты
|
||
*******************************************************************************/
|
||
bool ICACHE_FLASH_ATTR web_inc_fclose(WEB_SRV_CONN *web_conn)
|
||
{
|
||
if(web_conn->bffiles[0] != WEBFS_INVALID_HANDLE) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("cf%d ", web_conn->bffiles[0]);
|
||
#endif
|
||
if(web_conn->bffiles[0] <= WEBFS_MAX_HANDLE) {
|
||
WEBFSClose(web_conn->bffiles[0]);
|
||
ClrSCB(SCB_FGZIP);
|
||
}
|
||
web_conn->bffiles[0] = web_conn->bffiles[1];
|
||
web_conn->bffiles[1] = web_conn->bffiles[2];
|
||
web_conn->bffiles[2] = web_conn->bffiles[3];
|
||
web_conn->bffiles[3] = WEBFS_INVALID_HANDLE;
|
||
if(web_conn->bffiles[0] != WEBFS_INVALID_HANDLE) return false;
|
||
};
|
||
ClrSCB(SCB_FOPEN | SCB_FGZIP | SCB_FCALBACK);
|
||
return true; // больше нет файлов
|
||
};
|
||
/******************************************************************************
|
||
* FunctionName : webserver_open_file
|
||
* Description : Compare to known extensions to determine Content-Type
|
||
* Parameters : filename -- file name
|
||
* Returns : 1 - open, 0 - no
|
||
*******************************************************************************/
|
||
static void ICACHE_FLASH_ATTR webserver_file_ext(HTTP_CONN *CurHTTP, uint8 *pfname)
|
||
{
|
||
uint8 *pfext = NULL;
|
||
while(*pfname >= ' ') if(*pfname++ == '.') pfext = pfname;
|
||
if(pfext != NULL) {
|
||
for(CurHTTP->fileType = HTTP_TXT; CurHTTP->fileType < HTTP_UNKNOWN; CurHTTP->fileType++)
|
||
if(rom_xstrcmp(pfext, httpFileExtensions[CurHTTP->fileType])) break;
|
||
};
|
||
}
|
||
/*----------------------------------------------------------------------*/
|
||
#ifdef USE_CAPTDNS
|
||
/* = flase, если включен redirect, и запрос от ip адреса из подсети AP,
|
||
* и Host name не равен aesp8266 или ip AP. */
|
||
static bool ICACHE_FLASH_ATTR web_cdns_no_redir(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn)
|
||
{
|
||
if(syscfg.cfg.b.cdns_ena
|
||
&& pcb_cdns != NULL
|
||
&&((ts_conn->pcb->remote_ip.addr ^ info.ap_ip) & info.ap_mask) == 0
|
||
&& CurHTTP->phead != NULL
|
||
&& CurHTTP->head_len != 0) {
|
||
uint8 * ps = head_find_ctr(CurHTTP, HTTPHost, sizeHTTPHost, 7);
|
||
if(ps != NULL) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("Host: '%s' ", ps);
|
||
#endif
|
||
uint8 strip[4*4];
|
||
os_sprintf_fd(strip, IPSTR, IP2STR(&info.ap_ip));
|
||
if((rom_xstrcmp(ps, HostNameLocal) == 0) && (rom_xstrcmp(ps, strip) == 0)) {
|
||
rtl_sprintf(CurHTTP->pFilename, httpHostNameLocal, HostNameLocal); // "http://esp8266/"
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
SetSCB(SCB_REDIR);
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
#endif
|
||
/******************************************************************************
|
||
* FunctionName : webserver_open_file
|
||
* Description : Open file
|
||
* Parameters : filename -- file name
|
||
* Returns : 1 - open, 0 - no
|
||
*******************************************************************************/
|
||
static bool ICACHE_FLASH_ATTR webserver_open_file(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn)
|
||
{
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
uint8 pbuf[MAX_FILE_NAME_SIZE];
|
||
uint8 *pstr = pbuf;
|
||
if(CurHTTP->pFilename[0] == '/')
|
||
{
|
||
if(CurHTTP->pFilename[1] == '\0')
|
||
{
|
||
if(isWEBFSLocked)
|
||
{
|
||
web_inc_fp(web_conn, WEBFS_NODISK_HANDLE); // желательно дописать ответ, что нет диска.
|
||
web_conn->content_len = sizeHTTPfserror;
|
||
CurHTTP->fileType = HTTP_HTML;
|
||
#if DEBUGSOO > 1
|
||
os_printf("of%d[%s] ", web_conn->webfile, CurHTTP->pFilename);
|
||
#endif
|
||
return true;
|
||
}
|
||
else {
|
||
#ifdef USE_CAPTDNS
|
||
if(web_cdns_no_redir(CurHTTP, ts_conn)) rom_xstrcpy(pstr, http_default_file);
|
||
else return false;
|
||
#else
|
||
rom_xstrcpy(pstr, http_default_file);
|
||
#endif
|
||
}
|
||
}
|
||
else
|
||
{
|
||
rtl_memcpy(pstr, &CurHTTP->pFilename[1], MAX_FILE_NAME_SIZE-1);
|
||
if(rom_xstrcmp(pstr, web_cgi_fname))
|
||
{
|
||
web_inc_fp(web_conn, WEBFS_WEBCGI_HANDLE);
|
||
web_conn->content_len = sizeHTTPdefault;
|
||
#if USE_WEB_AUTH_LEVEL
|
||
// web_conn->auth_realm = WEB_AUTH_LEVEL_USER;
|
||
// SetSCB(SCB_AUTH);
|
||
#endif
|
||
CurHTTP->fileType = HTTP_HTML;
|
||
#if DEBUGSOO > 1
|
||
os_printf("of%d[%s] ", web_conn->webfile, CurHTTP->pFilename);
|
||
#endif
|
||
return true;
|
||
}
|
||
else if(rom_xstrcmp(pstr, fsupload_fname))
|
||
{
|
||
#if USE_WEB_AUTH_LEVEL
|
||
web_conn->auth_realm = WEB_AUTH_LEVEL_WEBFS;
|
||
#endif
|
||
SetSCB(SCB_AUTH);
|
||
web_inc_fp(web_conn, WEBFS_UPLOAD_HANDLE);
|
||
web_conn->content_len = sizeHTTPfsupload;
|
||
CurHTTP->fileType = HTTP_HTML;
|
||
#if DEBUGSOO > 1
|
||
os_printf("of%d[%s] ", web_conn->webfile, CurHTTP->pFilename);
|
||
#endif
|
||
return true;
|
||
}
|
||
}
|
||
if(isWEBFSLocked) return false;
|
||
// поиск файла на диске
|
||
if(!web_inc_fopen(ts_conn, pstr)) {
|
||
uint32 i = os_strlen(pbuf);
|
||
if(i + sizeof(http_default_file) < MAX_FILE_NAME_SIZE - 1)
|
||
{
|
||
// добавить к имени папки "/index.htm"
|
||
pbuf[i] = '/';
|
||
rom_xstrcpy(&pbuf[i+1], http_default_file);
|
||
if(!web_inc_fopen(ts_conn, pstr))
|
||
{
|
||
#ifdef USE_CAPTDNS
|
||
web_cdns_no_redir(CurHTTP, ts_conn);
|
||
#endif
|
||
return false;
|
||
}
|
||
};
|
||
};
|
||
// Compare to known extensions to determine Content-Type
|
||
webserver_file_ext(CurHTTP, pstr);
|
||
return true;
|
||
};
|
||
return false; // файл не открыт
|
||
}
|
||
/******************************************************************************
|
||
*******************************************************************************/
|
||
static void ICACHE_FLASH_ATTR web_send_fnohanle(TCP_SERV_CONN *ts_conn) {
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
uint32 pdata = 0;
|
||
// uint8 pbuf[mMAX(mMAX(sizeHTTPdefault,sizeHTTPfserror), sizeHTTPfsupload)];
|
||
uint32 size = 0;
|
||
switch(web_conn->webfile) {
|
||
case WEBFS_WEBCGI_HANDLE:
|
||
pdata = (uint32)((void *)HTTPdefault);
|
||
size = sizeHTTPdefault;
|
||
break;
|
||
case WEBFS_UPLOAD_HANDLE:
|
||
pdata = (uint32)((void *)HTTPfsupload);
|
||
size = sizeHTTPfsupload;
|
||
break;
|
||
case WEBFS_NODISK_HANDLE:
|
||
pdata = (uint32)((void *)HTTPfserror);
|
||
size = sizeHTTPfserror;
|
||
break;
|
||
}
|
||
if(pdata != 0 && size != 0) {
|
||
// spi_flash_read(pdata & MASK_ADDR_FLASH_ICACHE_DATA, pbuf, size);
|
||
tcpsrv_int_sent_data(ts_conn, (uint8 *) pdata, size);
|
||
}
|
||
#if DEBUGSOO > 1
|
||
os_printf("%u ", size);
|
||
#endif
|
||
SetSCB(SCB_FCLOSE|SCB_DISCONNECT);
|
||
}
|
||
/******************************************************************************
|
||
*******************************************************************************/
|
||
static int ICACHE_FLASH_ATTR web_find_cbs(uint8 * chrbuf, uint32 len) {
|
||
uint32 i;
|
||
for(i = 0; i < len; i++) if(chrbuf[i] == '~') return i;
|
||
return -1;
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : webserver_send_fdata
|
||
* Description : Sent callback function to call for this espconn when data
|
||
* is successfully sent
|
||
* Parameters : arg -- Additional argument to pass to the callback function
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
static void ICACHE_FLASH_ATTR webserver_send_fdata(TCP_SERV_CONN *ts_conn) {
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
if(web_conn->webfile == WEBFS_INVALID_HANDLE) {
|
||
SetSCB(SCB_FCLOSE|SCB_DISCONNECT);
|
||
return;
|
||
}
|
||
#if DEBUGSOO > 1
|
||
os_printf("send: ");
|
||
#endif
|
||
#ifdef SET_CPU_CLK_SPEED
|
||
// set_cpu_clk();
|
||
#endif
|
||
web_conn->msgbufsize = tcp_sndbuf(ts_conn->pcb);
|
||
|
||
#if DEBUGSOO > 5
|
||
os_printf("sndbuf=%u ", web_conn->msgbufsize);
|
||
#endif
|
||
|
||
if (web_conn->msgbufsize < MIN_SEND_SIZE) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("sndbuf=%u! ", web_conn->msgbufsize);
|
||
if(ts_conn->flag.wait_sent) os_printf("wait_sent! "); // блок передан?
|
||
#endif
|
||
ts_conn->pcb->flags &= ~TF_NODELAY;
|
||
tcpsrv_int_sent_data(ts_conn, (uint8 *)ts_conn, 0);
|
||
return;
|
||
}
|
||
if((web_conn->webfile > WEBFS_MAX_HANDLE)&&(!CheckSCB(SCB_RETRYCB))) {
|
||
web_send_fnohanle(ts_conn);
|
||
return;
|
||
}
|
||
web_conn->msgbufsize = mMIN(MAX_SEND_SIZE, web_conn->msgbufsize);
|
||
uint8 *pbuf = (uint8 *) os_malloc(web_conn->msgbufsize);
|
||
if (pbuf == NULL) {
|
||
#if DEBUGSOO > 0
|
||
os_printf("out of memory - disconnect!\n");
|
||
#endif
|
||
SetSCB(SCB_FCLOSE|SCB_DISCONNECT);
|
||
return;
|
||
};
|
||
web_conn->msgbuf = pbuf;
|
||
web_conn->msgbuflen = 0;
|
||
if (CheckSCB(SCB_CHUNKED)) { // is chunked
|
||
web_conn->msgbuf += RESCHKS_SEND_SIZE;
|
||
web_conn->msgbufsize -= RESCHK_SEND_SIZE;
|
||
};
|
||
if(CheckSCB(SCB_FCALBACK) == 0) { // передача файла без парсинга
|
||
// Get/put as many bytes as possible
|
||
web_conn->msgbuflen = WEBFSGetArray(web_conn->webfile, web_conn->msgbuf, web_conn->msgbufsize);
|
||
if(web_conn->msgbuflen < web_conn->msgbufsize ) SetSCB(SCB_FCLOSE | SCB_DISCONNECT);
|
||
}
|
||
else { // парсинг потока передачи
|
||
do { // начинаем с пустого буфера
|
||
if(CheckSCB(SCB_RETRYCB)) { // повторный callback? да
|
||
#if DEBUGSOO > 2
|
||
os_printf("rcb ");
|
||
#endif
|
||
if(web_conn->func_web_cb != NULL) web_conn->func_web_cb(ts_conn);
|
||
if(CheckSCB(SCB_RETRYCB)) break; // повторить ещё раз? да.
|
||
}
|
||
else {
|
||
uint8 *pstr = &web_conn->msgbuf[web_conn->msgbuflen]; // указатель в буфере
|
||
// запомнить указатель в файле. ftell(fp)
|
||
uint32 max = mMIN(web_conn->msgbufsize - web_conn->msgbuflen, SCB_SEND_SIZE); // читаем по 128 байт ?
|
||
uint32 len = WEBFSGetArray(web_conn->webfile, pstr, max);
|
||
// прочитано len байт в буфер по указателю &sendbuf[msgbuflen]
|
||
if(len) { // есть байты для передачи, ищем string "~calback~"
|
||
int cmp = web_find_cbs(pstr, len);
|
||
if(cmp >= 0) { // найден calback
|
||
// откат файла
|
||
WEBFSStubs[web_conn->webfile].addr -= len;
|
||
WEBFSStubs[web_conn->webfile].bytesRem += len;
|
||
// передвинуть указатель в файле на считанные байты с учетом маркера, без добавки длины для передачи
|
||
WEBFSStubs[web_conn->webfile].addr += cmp+1;
|
||
WEBFSStubs[web_conn->webfile].bytesRem -= cmp+1;
|
||
// это второй маркер?
|
||
if(CheckSCB(SCB_FINDCB)) { // в файле найден закрывающий маркер calback
|
||
ClrSCB(SCB_FINDCB); // прочитали string calback-а
|
||
if(cmp != 0) { // это дубль маркера ? нет.
|
||
// запустить calback
|
||
pstr[cmp] = '\0'; // закрыть string calback-а
|
||
if(!os_memcmp((void*)pstr, "inc:", 4)) { // "inc:file_name"
|
||
if(!web_inc_fopen(ts_conn, &pstr[4])) {
|
||
tcp_strcpy_fd("file not found!");
|
||
};
|
||
}
|
||
else web_int_callback(ts_conn, pstr);
|
||
}
|
||
else { // Дубль маркера.
|
||
web_conn->msgbuflen++; // передать только маркер ('~')
|
||
};
|
||
}
|
||
else {
|
||
SetSCB(SCB_FINDCB); // в файле найден стартовый маркер calback
|
||
web_conn->msgbuflen += cmp; // передать до стартового маркера calback
|
||
};
|
||
}
|
||
else { // просто данные
|
||
ClrSCB(SCB_FINDCB);
|
||
if(len < max) {
|
||
if(web_inc_fclose(web_conn)) SetSCB(SCB_FCLOSE | SCB_DISCONNECT); // файл(ы) закончилсь совсем? да.
|
||
};
|
||
web_conn->msgbuflen += len; // добавить кол-во считанных байт для передачи.
|
||
};
|
||
}
|
||
else if(web_inc_fclose(web_conn)) SetSCB(SCB_FCLOSE | SCB_DISCONNECT); // файл(ы) закончилсь совсем? да.
|
||
}; // not SCB_RETRYCB
|
||
} // набираем буфер
|
||
while((web_conn->msgbufsize - web_conn->msgbuflen >= SCB_SEND_SIZE)&&(!CheckSCB(SCB_FCLOSE | SCB_RETRYCB | SCB_DISCONNECT)));
|
||
};
|
||
#if DEBUGSOO > 3
|
||
os_printf("#%04x %d ", web_conn->webflag, web_conn->msgbuflen);
|
||
#elif DEBUGSOO > 1
|
||
os_printf("%u ", web_conn->msgbuflen);
|
||
#endif
|
||
if(web_conn->msgbuflen) {
|
||
web_conn->content_len -= web_conn->msgbuflen; // пока только для инфы
|
||
if(CheckSCB(SCB_CHUNKED)) { // greate chunked
|
||
uint8 cbuf[16];
|
||
static const char chunks[] ICACHE_RODATA_ATTR = "\r\n%X\r\n";
|
||
unsigned int len = rtl_sprintf(cbuf, chunks, web_conn->msgbuflen);
|
||
web_conn->msgbuf -= len;
|
||
rtl_memcpy(web_conn->msgbuf, cbuf, len);
|
||
web_conn->msgbuflen += len;
|
||
if(CheckSCB(SCB_FCLOSE)) { // close file? -> add 'end chunked'
|
||
tcp_strcpy_fd("\r\n0\r\n\r\n");
|
||
};
|
||
};
|
||
ts_conn->pcb->flags |= TF_NODELAY;
|
||
tcpsrv_int_sent_data(ts_conn, web_conn->msgbuf, web_conn->msgbuflen);
|
||
};
|
||
os_free(pbuf);
|
||
web_conn->msgbuf = NULL;
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : web_print_headers
|
||
* Description : Print HTTP Response Header
|
||
* Parameters : *
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
static void ICACHE_FLASH_ATTR
|
||
web_print_headers(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn)
|
||
{
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
HTTP_RESPONSE *CurResp = (HTTP_RESPONSE *)HTTPResponse;
|
||
#if DEBUGSOO > 3
|
||
os_printf("prh#%04x,%d,%d ", web_conn->webflag, CurHTTP->httpStatus, CurHTTP->fileType);
|
||
#endif
|
||
web_conn->msgbuf = (uint8 *)os_malloc(HTTP_SEND_SIZE);
|
||
if(web_conn->msgbuf == NULL)
|
||
{
|
||
#if DEBUGSOO == 1
|
||
os_printf("web: out of memory!\n");
|
||
#elif DEBUGSOO > 1
|
||
os_printf("out of memory! ");
|
||
#endif
|
||
SetSCB(SCB_FCLOSE | SCB_DISCONNECT);
|
||
return;
|
||
}
|
||
web_conn->msgbufsize = HTTP_SEND_SIZE;
|
||
web_conn->msgbuflen = 0;
|
||
if(CheckSCB(SCB_REDIR)) {
|
||
CurHTTP->httpStatus = 302; // редирект
|
||
}
|
||
#ifdef WEBSOCKET_ENA
|
||
if(CheckSCB(SCB_WEBSOC) && CurHTTP->httpStatus == 200) {
|
||
#if DEBUGSOO > 1
|
||
CurHTTP->httpStatus = 101;
|
||
#endif
|
||
tcp_puts(WebSocketHTTPOkKey, CurHTTP->pFilename);
|
||
}
|
||
else {
|
||
#endif
|
||
while(!(CurResp->flag & HTTP_RESP_FLG_END)) {
|
||
if(CurResp->status == CurHTTP->httpStatus) break;
|
||
CurResp++;
|
||
};
|
||
tcp_puts_fd("HTTP/1.1 %u ", CurResp->status);
|
||
#if USE_WEB_AUTH_LEVEL
|
||
if(CurResp->status == 401) tcp_puts_fd(CurResp->headers, web_conn->auth_realm);
|
||
#else
|
||
if (CurResp->status == 401) tcp_puts_fd(CurResp->headers);
|
||
#endif
|
||
else tcp_strcpy(CurResp->headers);
|
||
tcp_strcpy_fd("\r\nServer: " WEB_NAME_VERSION "\r\nConnection: close\r\n");
|
||
if(CheckSCB(SCB_REDIR)) {
|
||
tcp_puts_fd("Location: %s\r\n\r\n", CurHTTP->pFilename);
|
||
ts_conn->flag.pcb_time_wait_free = 1; // закрыть соединение
|
||
SetSCB(SCB_DISCONNECT);
|
||
}
|
||
else {
|
||
if(CurResp->status != 200) {
|
||
web_inc_fclose(web_conn);
|
||
ClrSCB(SCB_FCALBACK | SCB_FGZIP | SCB_CHUNKED | SCB_RXDATA | SCB_FCLOSE);
|
||
if(CurResp->flag & HTTP_RESP_FLG_FINDFILE) {
|
||
os_sprintf_fd(CurHTTP->pFilename, "/%u.htm", CurResp->status);
|
||
webserver_open_file(CurHTTP, ts_conn);
|
||
// CurHTTP->httpStatus = CurResp->status; // вернуть статус!
|
||
};
|
||
}
|
||
if((!CheckSCB(SCB_FOPEN)) && (CurResp->default_content != NULL) ) {
|
||
tcp_puts_fd("%s %u\r\n%s %s\r\n\r\n", HTTPContentLength, rtl_strlen(CurResp->default_content),
|
||
HTTPContentType, httpContentTypes[HTTP_TXT]);
|
||
tcp_strcpy(CurResp->default_content);
|
||
SetSCB(SCB_DISCONNECT);
|
||
}
|
||
else if(CheckSCB(SCB_FOPEN)) {
|
||
if(web_conn->content_len) {
|
||
// Указать, что данные могут пользовать все (очень актуально для XML, ...)
|
||
tcp_strcpy_fd("Access-Control-Allow-Origin: *\r\n");
|
||
if(CurHTTP->fileType != HTTP_UNKNOWN) {
|
||
if(web_conn->bffiles[0] == WEBFS_WEBCGI_HANDLE && CheckSCB(SCB_FCALBACK)) CurHTTP->fileType = web_conn->fileType;
|
||
tcp_puts_fd("Content-Type: %s\r\n", httpContentTypes[CurHTTP->fileType]);
|
||
};
|
||
// Output the cache-control + ContentLength
|
||
if(CheckSCB(SCB_FCALBACK)) { // длина неизветсна
|
||
// file is callback index
|
||
tcp_strcpy_fd("Cache-Control: no-store, no-cache, must-revalidate, max-age=0\r\n");
|
||
if(CurHTTP->httpver >= 0x11) SetSCB(SCB_CHUNKED);
|
||
}
|
||
else { // длина изветсна
|
||
tcp_puts_fd("%s %d\r\n", HTTPContentLength, web_conn->content_len);
|
||
if(CurResp->status == 200 && (!isWEBFSLocked) && web_conn->bffiles[0] != WEBFS_WEBCGI_HANDLE) {
|
||
// lifetime (sec) of static responses as string 60*60*24*14=1209600"
|
||
tcp_puts_fd("Cache-Control: smax-age=%d\r\n", FILE_CACHE_MAX_AGE_SEC);
|
||
}
|
||
else {
|
||
tcp_strcpy_fd("Cache-Control: no-store, no-cache, must-revalidate, max-age=0\r\n");
|
||
}
|
||
};
|
||
if(CheckSCB(SCB_FGZIP)) {
|
||
// Output the gzip encoding header if needed
|
||
tcp_strcpy_fd("Content-Encoding: gzip\r\n");
|
||
}
|
||
else if(CheckSCB(SCB_CHUNKED)) {
|
||
tcp_strcpy_fd("Transfer-Encoding: chunked\r\n");
|
||
}
|
||
if(!CheckSCB(SCB_CHUNKED)) tcp_strcpy_fd(CRLF);
|
||
}
|
||
else {
|
||
tcp_puts_fd("%s 0\r\n\r\n", HTTPContentLength);
|
||
SetSCB(SCB_FCLOSE|SCB_DISCONNECT);
|
||
}
|
||
}
|
||
else SetSCB(SCB_DISCONNECT);
|
||
} // CheckSCB(SCB_REDIR)
|
||
#ifdef WEBSOCKET_ENA
|
||
}
|
||
#endif
|
||
#if DEBUGSOO > 3
|
||
os_printf("#%04x (%d) %d ", web_conn->webflag, web_conn->msgbuflen, CurHTTP->httpStatus);
|
||
web_conn->msgbuf[web_conn->msgbuflen] = 0;
|
||
os_printf("\n2?%d[%s]\n", web_conn->msgbuflen, web_conn->msgbuf);
|
||
#elif DEBUGSOO > 1
|
||
os_printf("head[%d]:%d ", web_conn->msgbuflen, CurHTTP->httpStatus);
|
||
#endif
|
||
if(web_conn->msgbuflen) {
|
||
if(CheckSCB(SCB_DISCONNECT)) SetSCB(SCB_CLOSED);
|
||
tcpsrv_int_sent_data(ts_conn, web_conn->msgbuf, web_conn->msgbuflen);
|
||
#ifdef USE_WEB_NAGLE
|
||
ts_conn->flag.nagle_disabled = 1;
|
||
#endif
|
||
};
|
||
os_free(web_conn->msgbuf);
|
||
web_conn->msgbuf = NULL;
|
||
}
|
||
/******************************************************************************/
|
||
//-----------------------------------------------------------------------------
|
||
//-----------------------------------------------------------------------------
|
||
// поиск boundary
|
||
// 0 - разделитель (boundary) не найден, докачивать или ...
|
||
// 1 - boundary найден
|
||
// 200 - найден завершаюший boundary
|
||
// 400 - неверный формат
|
||
// ...
|
||
//-----------------------------------------------------------------------------
|
||
/* Пример M-Explorer: Load blk len: 399
|
||
-----------------------------7df22f37711be\r\n
|
||
Content-Disposition: form-data; name="test"; filename="readme.txt"\r\n
|
||
Content-Type: text/plain\r\n\r\n
|
||
1234567890\r\n
|
||
-----------------------------7df22f37711be\r\n
|
||
Content-Disposition: form-data; name="start"\r\n\r\n
|
||
0x1B000\r\n
|
||
-----------------------------7df22f37711be\r\n
|
||
Content-Disposition: form-data; name="stop"\r\n\r\n
|
||
0x1B000\r\n
|
||
-----------------------------7df22f37711be--\r\n */
|
||
/* Пример Google Chrome: Load blk len: 391
|
||
------WebKitFormBoundaryugGNBVFOk6qxfe22\r\n
|
||
Content-Disposition: form-data; name="test"; filename="readme.txt"\r\n
|
||
Content-Type: text/plain\r\n\r\n
|
||
1234567890\r\n
|
||
------WebKitFormBoundaryugGNBVFOk6qxfe22\r\n
|
||
Content-Disposition: form-data; name="start"\r\n\r\n
|
||
0x1B000\r\n
|
||
------WebKitFormBoundaryugGNBVFOk6qxfe22\r\n
|
||
Content-Disposition: form-data; name="stop"\r\n\r\n
|
||
0x1B000\r\n
|
||
------WebKitFormBoundaryugGNBVFOk6qxfe22--\r\n */
|
||
//-----------------------------------------------------------------------------
|
||
const char crlf_end_boundary[] ICACHE_RODATA_ATTR = "--" CRLF;
|
||
static int ICACHE_FLASH_ATTR find_boundary(HTTP_UPLOAD *pupload, uint8 *pstr, uint32 len)
|
||
{
|
||
int x = len - 6 - pupload->sizeboundary;
|
||
if(x <= 0) return 0; // разделитель (boundary) не найден - докачивать буфер
|
||
int i;
|
||
uint8 *pcmp;
|
||
for(i = 0; i <= x; i++) {
|
||
if(pstr[i] == '-' && pstr[i+1] == '-') {
|
||
pcmp = pstr + i;
|
||
// if((pstr + len - pcmp) < pupload->sizeboundary + 6) return 0; // разделитель (boundary) не найден - докачивать буфер
|
||
pupload->pbndr = pcmp; // указатель на заголовок boundary (конец блока данных);
|
||
pcmp += 2;
|
||
if(os_memcmp(pcmp, pupload->boundary, pupload->sizeboundary)) return 0; // разделитель (boundary) не найден
|
||
pcmp += pupload->sizeboundary;
|
||
if(rom_xstrcmp(pcmp, crlf_end_boundary)) {
|
||
pcmp += 4;
|
||
pupload->pnext = pcmp; // указатель в заголовке boundary (описание новых данных);
|
||
return 200; // найден завершающий разделитель
|
||
}
|
||
if(pcmp[0] != '\r' || pcmp[1] != '\n') return 400; // неверный формат
|
||
pcmp += 2;
|
||
pupload->pnext = pcmp; // указатель в заголовке boundary (описание новых данных);
|
||
return 1;
|
||
};
|
||
};
|
||
return 0; // разделитель (boundary) не найден - докачивать буфер
|
||
}
|
||
//-----------------------------------------------------------------------------
|
||
// Function: cmp_next_boundary
|
||
// return:
|
||
// 0 - разделитель (boundary) не найден, докачивать
|
||
// 1 - далее обработка данных
|
||
// 200 - найден завершающий разделитель: "\r\n--boundary--"
|
||
// 400 - неизвестный формат content-а
|
||
//-----------------------------------------------------------------------------
|
||
const char disk_ok_filename[] ICACHE_RODATA_ATTR = "/disk_ok.htm";
|
||
const char disk_err1_filename[] ICACHE_RODATA_ATTR = "/disk_er1.htm";
|
||
const char disk_err2_filename[] ICACHE_RODATA_ATTR = "/disk_er2.htm";
|
||
const char disk_err3_filename[] ICACHE_RODATA_ATTR = "/disk_er3.htm";
|
||
const char sysconst_filename[] ICACHE_RODATA_ATTR = "sysconst";
|
||
#ifdef USE_OVERLAY
|
||
const char overlay_filename[] ICACHE_RODATA_ATTR = "overlay";
|
||
#endif
|
||
const char sector_filename[] ICACHE_RODATA_ATTR = "fsec_";
|
||
#define sector_filename_size 5
|
||
const char file_label[] ICACHE_RODATA_ATTR = "file";
|
||
|
||
static int ICACHE_FLASH_ATTR upload_boundary(TCP_SERV_CONN *ts_conn) // HTTP_UPLOAD pupload, uint8 pstr, uint16 len)
|
||
{
|
||
HTTP_UPLOAD *pupload = (HTTP_UPLOAD *)ts_conn->pbufo;
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
if(pupload == NULL) return 500; // ошибка сервера
|
||
uint32 ret;
|
||
uint32 len;
|
||
uint8 *pnext;
|
||
uint8 *pstr;
|
||
while(web_conn->content_len && ts_conn->pbufi != NULL) {
|
||
pstr = ts_conn->pbufi;
|
||
len = ts_conn->sizei;
|
||
#if DEBUGSOO > 4
|
||
os_printf("bufi[%u]%u, cont:%u ", ts_conn->sizei, ts_conn->cntri, web_conn->content_len);
|
||
#endif
|
||
if(len < (8 + pupload->sizeboundary)) return 0; // разделитель (boundary) не влезет - докачивать буфер
|
||
switch(pupload->status) {
|
||
case 0: // поиск boundary
|
||
{
|
||
#if DEBUGSOO > 4
|
||
os_printf("find_bndr ");
|
||
#endif
|
||
pnext = web_strnstr(pstr, CRLF CRLF , len);
|
||
if(pnext == NULL) return 0; // докачивать
|
||
len = pnext - pstr;
|
||
ret = find_boundary(pupload, pstr, len);
|
||
#if DEBUGSOO > 4
|
||
os_printf("len=%u,ret=%u ", len, ret );
|
||
#endif
|
||
if(ret != 1) return ret;
|
||
pstr = pupload->pnext; // +"\r\n" адрес за заголовком boundary
|
||
pupload->name[0] = '\0';
|
||
pupload->filename[0] = '\0';
|
||
pstr = web_strnstr(pstr, "name=", pnext - pstr);
|
||
if(pstr == NULL) return 400; // неизвестный формат content-а
|
||
pstr += 5;
|
||
if(pstr >= pnext) return 400; // неизвестный формат content-а
|
||
uint8 *pcmp = cmpcpystr(pupload->name, pstr, '"', '"', VarNameSize);
|
||
if(pcmp == NULL) {
|
||
pcmp = cmpcpystr(pupload->name, pstr, 0x22, 0x22, VarNameSize);
|
||
if(pcmp == NULL) return 400; // неизвестный формат content-а
|
||
};
|
||
pstr = pcmp;
|
||
#if DEBUGSOO > 4
|
||
os_printf("name:'%s' ", pupload->name);
|
||
#endif
|
||
if(pstr >= pnext) return 400; // неизвестный формат content-а
|
||
pcmp = web_strnstr(pstr, "filename=", pnext - pstr);
|
||
if(pcmp != NULL) {
|
||
pcmp += 9;
|
||
if(pcmp < pnext) {
|
||
if(cmpcpystr(pupload->filename, pcmp, '"', '"', VarNameSize) == NULL)
|
||
cmpcpystr(pupload->filename, pcmp, 0x22, 0x22, VarNameSize);
|
||
};
|
||
#if DEBUGSOO > 1
|
||
if(pupload->filename[0]!= '\0') os_printf("filename:'%s' ", pupload->filename);
|
||
#endif
|
||
};
|
||
len += 4;
|
||
pupload->status++;
|
||
#if DEBUGSOO > 4
|
||
os_printf("trim#%u\n", len );
|
||
#endif
|
||
ts_conn->cntri += len; // далее идут данные
|
||
if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500;
|
||
web_conn->content_len -= len;
|
||
break;
|
||
}
|
||
case 1: // прием данных, первый заход, проверка форматов и т.д.
|
||
{
|
||
#if DEBUGSOO > 4
|
||
os_printf("tst,fn='%s' ", pupload->filename);
|
||
#endif
|
||
if(pupload->filename[0]!='\0') { // загрузка файла?
|
||
if(rom_xstrcmp(pupload->name, file_label)) { // !os_memcmp((void*)pupload->name, "file", 4)
|
||
if(len < sizeof(WEBFS_DISK_HEADER)) return 0; // докачивать
|
||
WEBFS_DISK_HEADER *dhead = (WEBFS_DISK_HEADER *)pstr;
|
||
if(dhead->id != WEBFS_DISK_ID || dhead->ver != WEBFS_DISK_VER
|
||
|| (web_conn->content_len - pupload->sizeboundary - 8 < dhead->disksize)) {
|
||
if(isWEBFSLocked) return 400;
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // неверный формат
|
||
return 200;
|
||
};
|
||
if(dhead->disksize > WEBFS_max_size()) {
|
||
if(isWEBFSLocked) return 400;
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_err2_filename); // rtl_memcpy(pupload->filename,"/disk_er2.htm\0",14); // не влезет
|
||
return 200;
|
||
};
|
||
pupload->fsize = dhead->disksize;
|
||
pupload->faddr = WEBFS_base_addr();
|
||
#if DEBUGSOO > 4
|
||
os_printf("updisk[%u]=ok ", dhead->disksize);
|
||
#endif
|
||
pupload->status = 3; // = 3 загрузка WebFileSystem во flash
|
||
isWEBFSLocked = true;
|
||
break;
|
||
}
|
||
#ifdef USE_OVERLAY
|
||
else if(rom_xstrcmp(pupload->name, overlay_filename)) {
|
||
if(len < sizeof(struct SPIFlashHeader)) return 0; // докачивать
|
||
struct SPIFlashHeader *fhead = (struct SPIFlashHeader *)pstr;
|
||
if(web_conn->content_len - pupload->sizeboundary < sizeof(fhead)
|
||
|| fhead->head.id != LOADER_HEAD_ID) {
|
||
if(isWEBFSLocked) return 400;
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // неверный формат
|
||
return 200;
|
||
};
|
||
if(fhead->entry_point >= IRAM_BASE && ovl_call != NULL) {
|
||
ovl_call(0); // close прошлый оверлей
|
||
ovl_call = NULL;
|
||
}
|
||
pupload->start = fhead->entry_point;
|
||
pupload->segs = fhead->head.number_segs;
|
||
if(pupload->segs) {
|
||
pupload->fsize = sizeof(struct SPIFlashHeadSegment);
|
||
pupload->status = 5; // = 5 загрузка файла оверлея, начать с загрузки заголовка сегмента
|
||
}
|
||
else {
|
||
pupload->fsize = 0;
|
||
pupload->status = 4; // = 4 загрузка файла оверлея, запуск entry_point
|
||
}
|
||
//
|
||
len = sizeof(struct SPIFlashHeader);
|
||
ts_conn->cntri += len;
|
||
if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500;
|
||
web_conn->content_len -= len;
|
||
//
|
||
break;
|
||
}
|
||
#endif
|
||
else if(rom_xstrcmp(pupload->name, sysconst_filename)) {
|
||
pupload->fsize = FLASH_SECTOR_SIZE;
|
||
pupload->faddr = FLASH_RESERVED_DATA_BASE; // FLASH_SYSTEM_DATA_ADDR
|
||
pupload->status = 2; // = 2 загрузка файла во flash
|
||
break;
|
||
}
|
||
else if(rom_xstrcmp(pupload->name, sector_filename)) {
|
||
pupload->fsize = FLASH_SECTOR_SIZE;
|
||
pupload->faddr = ahextoul(&pupload->name[sector_filename_size]) << 12;
|
||
pupload->status = 2; // = 2 загрузка файла сектора во flash
|
||
break;
|
||
};
|
||
if(isWEBFSLocked) return 400;
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_err3_filename); // rtl_memcpy(pupload->filename,"/disk_er3.htm\0",14); // неизвестный тип
|
||
return 200;
|
||
}
|
||
else {
|
||
uint8 *pcmp = web_strnstr(pstr, CRLF, len);
|
||
if(pcmp == NULL) return 0; // докачивать
|
||
ret = find_boundary(pupload, pstr, len);
|
||
#if DEBUGSOO > 4
|
||
os_printf("ret=%u ", ret );
|
||
#endif
|
||
if((ret != 1 && ret != 200)) { // не найден конец или новый boundary?
|
||
return ret; // догружать
|
||
}
|
||
*pcmp = '\0';
|
||
web_int_vars(ts_conn, pupload->name, pstr);
|
||
if(ret == 200) return ret;
|
||
// найден следующий boundary
|
||
len = pupload->pbndr - ts_conn->pbufi;
|
||
pupload->status = 0; // = 0 найден следующий boundary
|
||
#if DEBUGSOO > 4
|
||
os_printf("trim#%u\n", len );
|
||
#endif
|
||
ts_conn->cntri += len; // далее идут данные
|
||
if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500;
|
||
web_conn->content_len -= len;
|
||
break;
|
||
}
|
||
// return 400;
|
||
}
|
||
// default:
|
||
case 2: // загрузка файла во flash
|
||
case 3: // загрузка WebFileSystem во flash (скорость записи W25Q128 ~175 килобайт в сек, полный диск на 15,5МБ пишется 90..100 сек )
|
||
{
|
||
#if DEBUGSOO > 4
|
||
os_printf("fdata ");
|
||
#endif
|
||
uint32 block_size = mMIN(max_len_buf_write_flash + 8 + pupload->sizeboundary, web_conn->content_len);
|
||
if(ts_conn->sizei < block_size) return 0; // докачивать
|
||
ret = find_boundary(pupload, pstr, block_size);
|
||
#if DEBUGSOO > 4
|
||
os_printf("ret=%u ", ret);
|
||
#endif
|
||
if((ret == 1 || ret == 200)) { // найден конец или новый boundary?
|
||
len = mMIN(block_size, pupload->pbndr - 2 - ts_conn->pbufi);
|
||
}
|
||
else {
|
||
len = mMIN(max_len_buf_write_flash, web_conn->content_len - 8 - pupload->sizeboundary);
|
||
}
|
||
#if DEBUGSOO > 4
|
||
os_printf("\nlen=%d, block_size=%d, content_len=%d, sizeboundary= %d, ret=%d, data = %d, load=%d", len, block_size, web_conn->content_len, pupload->sizeboundary, ret, pupload->pbndr - ts_conn->pbufi, ts_conn->sizei);
|
||
#endif
|
||
if(pupload->fsize < len) block_size = pupload->fsize;
|
||
else block_size = len;
|
||
if(block_size) { // идут данные файла
|
||
// tcpsrv_unrecved_win(ts_conn); // для ускорения, пока стрирается-пишется уже обновит окно (включено в web_rx_buf)
|
||
|
||
device_mutex_lock(RT_DEV_LOCK_FLASH);
|
||
if(pupload->faddr >= flash_get_size(&flashobj) && pupload->status == 3) {
|
||
if((pupload->faddr & 0x0000FFFF)==0) {
|
||
|
||
#if DEBUGSOO > 2
|
||
os_printf("Clear flash page addr %p... ", pupload->faddr);
|
||
#endif
|
||
flash_erase_block(&flashobj, pupload->faddr);
|
||
}
|
||
}
|
||
else if((pupload->faddr & 0x00000FFF) == 0) {
|
||
#if DEBUGSOO > 2
|
||
os_printf("Clear flash sector addr %p... ", pupload->faddr);
|
||
#endif
|
||
flash_erase_sector(&flashobj, pupload->faddr);
|
||
}
|
||
#if DEBUGSOO > 2
|
||
os_printf("Write flash addr:%p[0x%04x]\n", pupload->faddr, block_size);
|
||
#endif
|
||
flash_stream_write(&flashobj, pupload->faddr, (block_size + 3)&(~3), (uint8_t *)pstr);
|
||
|
||
device_mutex_unlock(RT_DEV_LOCK_FLASH);
|
||
|
||
pupload->fsize -= block_size;
|
||
pupload->faddr += block_size;
|
||
}
|
||
#if DEBUGSOO > 4
|
||
os_printf("trim#%u\n", len);
|
||
#endif
|
||
if(len) {
|
||
ts_conn->cntri += len;
|
||
if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500;
|
||
web_conn->content_len -= len;
|
||
}
|
||
#ifdef SET_CPU_CLK_SPEED
|
||
// if(syscfg.cfg.b.hi_speed_enable) set_cpu_clk();
|
||
#endif
|
||
if((ret == 1 || ret == 200)) { // найден конец или новый boundary?
|
||
if(pupload->status == 3) WEBFSInit();
|
||
if(pupload->fsize != 0) {
|
||
if(!isWEBFSLocked) {
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // не всё передано или неверный формат
|
||
return 200;
|
||
}
|
||
return 400; // не всё передано или неверный формат
|
||
}
|
||
else {
|
||
if(!isWEBFSLocked) {
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_ok_filename); // rtl_memcpy(pupload->filename,"/disk_ok.htm\0",13);
|
||
};
|
||
};
|
||
if(ret == 1) pupload->status = 0; // = 0 найден следующий boundary
|
||
if(ret == 200) return ret;
|
||
}
|
||
break;
|
||
}
|
||
#ifdef USE_OVERLAY
|
||
case 4: // загрузка данных/кода оверлея
|
||
case 5: // загрузка заголовка данных оверлея
|
||
{
|
||
uint32 block_size = mMIN(max_len_buf_write_flash + 8 + pupload->sizeboundary, web_conn->content_len);
|
||
if(ts_conn->sizei < block_size) return 0; // докачивать
|
||
ret = find_boundary(pupload, pstr, block_size);
|
||
if((ret == 1 || ret == 200)) { // найден конец или новый boundary?
|
||
len = mMIN(block_size, pupload->pbndr - 2 - ts_conn->pbufi);
|
||
}
|
||
else {
|
||
len = mMIN(max_len_buf_write_flash, web_conn->content_len - 8 - pupload->sizeboundary);
|
||
}
|
||
block_size = len;
|
||
while(block_size) {
|
||
#if DEBUGSOO > 5
|
||
os_printf("blk:%d,st:%d,fs:%d,%d ", block_size, pupload->status, pupload->fsize, pupload->segs);
|
||
#endif
|
||
if(pupload->status == 5) {
|
||
if(block_size >= sizeof(struct SPIFlashHeadSegment)) { // размер данных
|
||
if(pupload->segs) { //
|
||
rtl_memcpy(&pupload->faddr, pstr, 4);
|
||
rtl_memcpy(&pupload->fsize, &pstr[4], 4);
|
||
#if DEBUGSOO > 4
|
||
os_printf("New seg ovl addr:%p[%p] ", pupload->faddr, pupload->fsize);
|
||
#endif
|
||
}
|
||
}
|
||
else if(ret != 1 && ret != 200) { // не найден конец или boundary?
|
||
return 0; // докачивать
|
||
}
|
||
else {
|
||
#if DEBUGSOO > 5
|
||
os_printf("err_load_fseg ");
|
||
#endif
|
||
// if(block_size < sizeof(struct SPIFlashHeadSegment)
|
||
// || pupload->segs == 0 //
|
||
// || pupload->fsize > USE_OVERLAY) {
|
||
if(!isWEBFSLocked) {
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // не всё передано или неверный формат
|
||
return 200;
|
||
}
|
||
return 400; // не всё передано или неверный формат
|
||
}
|
||
pupload->segs--; // счет сегментов
|
||
pupload->status = 4; // загрузка данных/кода оверлея
|
||
pstr += sizeof(struct SPIFlashHeadSegment);
|
||
block_size -= sizeof(struct SPIFlashHeadSegment);
|
||
};
|
||
uint32 i = mMIN(pupload->fsize, block_size);
|
||
if(i) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("Wr:%p[%p] ", pupload->faddr, i);
|
||
#endif
|
||
copy_s1d4((void *)pupload->faddr, pstr, i);
|
||
block_size -= i;
|
||
pupload->faddr += i;
|
||
pstr += i;
|
||
pupload->fsize -= i;
|
||
};
|
||
if(pupload->fsize == 0) {
|
||
if(pupload->segs) { // все сегменты загружены?
|
||
pupload->status = 5; // загрузка заголовка данных оверлея
|
||
}
|
||
else { // все сегменты загружены
|
||
block_size = 0;
|
||
break; // break while(block_size)
|
||
}
|
||
};
|
||
}; // while(block_size)
|
||
if(len) {
|
||
ts_conn->cntri += len;
|
||
if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[len], ts_conn->sizei - len)) return 500;
|
||
web_conn->content_len -= len;
|
||
};
|
||
if((ret == 1 || ret == 200)) { // найден конец или новый boundary?
|
||
#if DEBUGSOO > 5
|
||
os_printf("fs:%d,%d ", pupload->fsize, pupload->segs);
|
||
#endif
|
||
if(pupload->fsize != 0 || pupload->segs != 0) { //
|
||
if(!isWEBFSLocked) {
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_err1_filename); // rtl_memcpy(pupload->filename,"/disk_er1.htm\0",14); // не всё передано или неверный формат
|
||
return 200;
|
||
}
|
||
return 400; // не всё передано или неверный формат
|
||
}
|
||
else {
|
||
#if DEBUGSOO > 1
|
||
os_printf("Run%p ", pupload->start);
|
||
#endif
|
||
if(pupload->start >= IRAM_BASE) {
|
||
ovl_call = (tovl_call *)pupload->start;
|
||
web_conn->web_disc_cb = (web_func_disc_cb)pupload->start; // адрес старта оверлея
|
||
web_conn->web_disc_par = 1; // параметр функции - инициализация
|
||
}
|
||
if(!isWEBFSLocked) {
|
||
SetSCB(SCB_REDIR);
|
||
rom_xstrcpy(pupload->filename, disk_ok_filename); // rtl_memcpy(pupload->filename,"/disk_ok.htm\0",13);
|
||
};
|
||
};
|
||
if(ret == 1) pupload->status = 0; // = 0 найден следующий boundary
|
||
if(ret == 200) return ret;
|
||
};
|
||
break;
|
||
};
|
||
#endif
|
||
};
|
||
};
|
||
return 0; //
|
||
}
|
||
//-----------------------------------------------------------------------------
|
||
// web_rx_buf
|
||
//
|
||
//-----------------------------------------------------------------------------
|
||
static bool ICACHE_FLASH_ATTR web_rx_buf(HTTP_CONN *CurHTTP, TCP_SERV_CONN *ts_conn)
|
||
{
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
ts_conn->flag.rx_buf = 1; // указать, что всегда в режиме докачивать
|
||
// CurHTTP->fileType = HTTP_UNKNOWN;
|
||
// ts_conn->pbufi, ts_conn->cntri;
|
||
#if DEBUGSOO > 3
|
||
os_printf("rx:%u[%u] ", web_conn->content_len, ts_conn->sizei);
|
||
#endif
|
||
if(ts_conn->sizei == 0) return true; // докачивать
|
||
tcpsrv_unrecved_win(ts_conn);
|
||
int ret = upload_boundary(ts_conn);
|
||
if(ret > 1) {
|
||
CurHTTP->httpStatus = ret;
|
||
web_conn->content_len = 0;
|
||
if(ret == 200) {
|
||
if(CheckSCB(SCB_REDIR)) {
|
||
HTTP_UPLOAD *pupload = (HTTP_UPLOAD *)ts_conn->pbufo;
|
||
if(pupload != NULL) {
|
||
rtl_memcpy(CurHTTP->pFilename, pupload->filename, VarNameSize);
|
||
// SetSCB(SCB_DISCONNECT);
|
||
}
|
||
}
|
||
else if((!isWEBFSLocked) && CheckSCB(SCB_FOPEN)
|
||
&& web_conn->webfile <= WEBFS_MAX_HANDLE
|
||
&& WEBFSGetFilename(web_conn->webfile, CurHTTP->pFilename, FileNameSize)) {
|
||
SetSCB(SCB_REDIR);
|
||
// web_conn->content_len = WEBFSGetBytesRem(web_conn->webfile); // WEBFSGetSize(web_conn->webfile);
|
||
// webserver_file_ext(CurHTTP, CurHTTP->pFilename);
|
||
// return false; // ok 200 + file
|
||
}
|
||
}
|
||
SetSCB(SCB_DISCONNECT);
|
||
return false; // неизвестный content или end
|
||
}
|
||
else {
|
||
#if DEBUGSOO > 2
|
||
os_printf("no boundary ");
|
||
#endif
|
||
if(ts_conn->sizei > MAX_NO_DATA_BUF_SIZE) {
|
||
CurHTTP->httpStatus = 418; // 418: Out of Coffee
|
||
SetSCB(SCB_DISCONNECT);
|
||
return false; // неизвестный content или end
|
||
}
|
||
};
|
||
if(web_conn->content_len > ts_conn->cntri) return true; // докачивать
|
||
CurHTTP->httpStatus = 400;
|
||
SetSCB(SCB_DISCONNECT);
|
||
web_conn->content_len = 0;
|
||
return false; // неизвестный content
|
||
}
|
||
//-----------------------------------------------------------------------------
|
||
//--- web_trim_bufi -----------------------------------------------------------
|
||
//-----------------------------------------------------------------------------
|
||
bool ICACHE_FLASH_ATTR web_trim_bufi(TCP_SERV_CONN *ts_conn, uint8 *pdata, uint32 data_len)
|
||
{
|
||
if(pdata != NULL && data_len != 0 && ts_conn->sizei > data_len) {
|
||
rtl_memcpy(ts_conn->pbufi, pdata, data_len); // переместим кусок в начало буфера
|
||
ts_conn->pbufi = (uint8 *)os_realloc(ts_conn->pbufi, data_len + 1); // mem_trim(ts_conn->pbufi, data_len + 1);
|
||
if(ts_conn->pbufi != NULL) {
|
||
ts_conn->sizei = data_len; // размер куска
|
||
ts_conn->cntri = 0;
|
||
}
|
||
else return false; // CurHTTP.httpStatus = 500; // 500 Internal Server Error
|
||
}
|
||
else if(ts_conn->pbufi != NULL) {
|
||
os_free(ts_conn->pbufi);
|
||
ts_conn->pbufi = NULL;
|
||
ts_conn->sizei = 0;
|
||
ts_conn->cntri = 0;
|
||
};
|
||
return true;
|
||
}
|
||
/******************************************************************************
|
||
* web_feee_bufi
|
||
* освободить приемный буфер
|
||
*******************************************************************************/
|
||
bool ICACHE_FLASH_ATTR web_feee_bufi(TCP_SERV_CONN *ts_conn)
|
||
{
|
||
if(ts_conn->pbufi != NULL) {
|
||
os_free(ts_conn->pbufi);
|
||
ts_conn->pbufi = NULL;
|
||
ts_conn->sizei = 0;
|
||
ts_conn->cntri = 0;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : webserver_recv
|
||
* Description : Processing the received data from the server
|
||
* Parameters : arg -- Additional argument to pass to the callback function
|
||
* pusrdata -- The received data (or NULL when the connection has been closed!)
|
||
* length -- The length of received data
|
||
* Returns : none
|
||
*
|
||
* For HTTP 1.0, this should normally only happen once (if the request fits in one packet).
|
||
*
|
||
*******************************************************************************/
|
||
static err_t ICACHE_FLASH_ATTR webserver_received_data(TCP_SERV_CONN *ts_conn)
|
||
{
|
||
#if DEBUGSOO > 1
|
||
tcpsrv_print_remote_info(ts_conn);
|
||
os_printf("read: %d ", ts_conn->sizei);
|
||
#endif
|
||
HTTP_CONN CurHTTP; // Current HTTP connection state
|
||
WEB_SRV_CONN *web_conn = ReNew_web_conn(ts_conn);
|
||
if(web_conn == NULL) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("err mem!\n");
|
||
#endif
|
||
return ERR_MEM;
|
||
}
|
||
if(CheckSCB(SCB_CLOSED | SCB_DISCONNECT | SCB_FCLOSE )) // обрабатывать нечего
|
||
return ERR_OK;
|
||
if(!CheckSCB(SCB_WEBSOC)) {
|
||
web_conn->udata_start = 0;
|
||
web_conn->udata_stop = 0;
|
||
}
|
||
os_memset(&CurHTTP, 0, sizeof(CurHTTP));
|
||
CurHTTP.httpStatus = 200; // OK
|
||
CurHTTP.fileType = HTTP_UNKNOWN;
|
||
// прием и обработка заголовка HHTP
|
||
if(!CheckSCB(SCB_HEAD_OK)) { // заголовок уже принят и обработан? нет
|
||
ts_conn->flag.rx_buf = 1; // докачивать буфер
|
||
tcpsrv_unrecved_win(ts_conn);
|
||
// разбираем докачан или нет заголовок HTTP, что там принято GET или POST и открываем файл и прием content, если это POST и не прием файла.
|
||
if(parse_header(&CurHTTP, ts_conn)) { // заголовок полный? нет
|
||
if(ts_conn->sizei < MAX_HTTP_HEAD_BUF) {
|
||
#if DEBUGSOO > 4
|
||
os_printf("buf");
|
||
#endif
|
||
#if DEBUGSOO > 1
|
||
os_printf("...\n");
|
||
#endif
|
||
return ERR_OK; // будем принимать ещё.
|
||
};
|
||
CurHTTP.httpStatus = 413; // 413 Request Entity Too Large // пока так
|
||
};
|
||
// разбор заголовка
|
||
#if DEBUGSOO > 1
|
||
#ifdef WEBSOCKET_ENA
|
||
os_printf("%s f[%s] ", (CheckSCB(SCB_POST))? "POST" : (CheckSCB(SCB_WEBSOC))? "WEBSOC" : "GET", CurHTTP.pFilename);
|
||
#else
|
||
os_printf("%s f[%s] ", (CheckSCB(SCB_POST))? "POST" : "GET", CurHTTP.pFilename);
|
||
#endif
|
||
#endif
|
||
#if DEBUGSOO > 3
|
||
os_printf("hcn:%p[%d],wcn:%d ", CurHTTP.pcontent, CurHTTP.content_len, web_conn->content_len);
|
||
#endif
|
||
if(CurHTTP.httpStatus == 200) { // && CheckSCB(SCB_FOPEN)) { // если файл открыт и всё OK
|
||
if(CurHTTP.cookie_len != 0) web_parse_cookie(&CurHTTP, ts_conn);
|
||
web_parse_uri_vars(&CurHTTP, ts_conn);
|
||
if(CurHTTP.pcontent != NULL) {
|
||
if(CheckSCB(SCB_RXDATA)) {
|
||
if(web_conn->content_len) { // с заголовком приняли кусок данных файла?
|
||
#if DEBUGSOO > 3
|
||
os_printf("trim:%u[%u] ", web_conn->content_len, CurHTTP.content_len);
|
||
#endif
|
||
if(!web_trim_bufi(ts_conn, CurHTTP.pcontent, CurHTTP.content_len)) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("trim error!\n");
|
||
#endif
|
||
CurHTTP.httpStatus = 500;
|
||
};
|
||
};
|
||
}
|
||
else {
|
||
if(CurHTTP.content_len != 0) web_parse_content(&CurHTTP, ts_conn);
|
||
};
|
||
};
|
||
};
|
||
SetSCB(SCB_HEAD_OK); // заголовок принят и обработан
|
||
};
|
||
#if DEBUGSOO > 3
|
||
os_printf("tst_rx: %u, %u, %u ", CurHTTP.httpStatus, (CheckSCB(SCB_RXDATA) != 0), web_conn->content_len );
|
||
#endif
|
||
// проверка на прием данных (content)
|
||
if(CurHTTP.httpStatus == 200 && CheckSCB(SCB_RXDATA) && (web_conn->content_len) && web_rx_buf(&CurHTTP, ts_conn)) {
|
||
#if DEBUGSOO > 1
|
||
os_printf("...\n");
|
||
#endif
|
||
return ERR_OK; // докачивать content
|
||
};
|
||
#ifdef WEBSOCKET_ENA
|
||
if(CheckSCB(SCB_WEBSOC) && CurHTTP.httpStatus == 200 && (!CheckSCB(SCB_REDIR))) {
|
||
if(!CheckSCB(SCB_WSDATA)) {
|
||
// создание и вывод заголовка ответа websock
|
||
ClrSCB(SCB_RXDATA);
|
||
Close_web_conn(ts_conn); // закрыть все файлы
|
||
web_print_headers(&CurHTTP, ts_conn);
|
||
if(CheckSCB(SCB_DISCONNECT)) {
|
||
ts_conn->flag.rx_null = 1; // всё - больше не принимаем!
|
||
ts_conn->flag.rx_buf = 0; // не докачивать буфер
|
||
if(web_feee_bufi(ts_conn)) tcpsrv_unrecved_win(ts_conn); // уничтожим буфер
|
||
}
|
||
else {
|
||
SetSCB(SCB_WSDATA);
|
||
ts_conn->flag.rx_buf = 1; // указать, что всегда в режиме докачивать
|
||
tcpsrv_unrecved_win(ts_conn);
|
||
tcp_output(ts_conn->pcb);
|
||
|
||
if(web_feee_bufi(ts_conn)) tcpsrv_unrecved_win(ts_conn); // уничтожим буфер
|
||
/*
|
||
if(ts_conn->pbufi != NULL && ts_conn->sizei != 0) { // что-то ещё есть в буфере?
|
||
#if DEBUGSOO > 1
|
||
os_printf("ws_rx[%u]? ", ts_conn->sizei);
|
||
#endif
|
||
websock_rx_data(ts_conn);
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
else {
|
||
websock_rx_data(ts_conn);
|
||
}
|
||
}
|
||
else
|
||
#endif
|
||
{
|
||
ts_conn->flag.rx_null = 1; // всё - больше не принимаем!
|
||
ts_conn->flag.rx_buf = 0; // не докачивать буфер
|
||
if(web_feee_bufi(ts_conn)) tcpsrv_unrecved_win(ts_conn); // уничтожим буфер
|
||
if(tcp_sndbuf(ts_conn->pcb) >= HTTP_SEND_SIZE) { // возможна втавка ответа?
|
||
// создание и вывод заголовка ответа.
|
||
web_print_headers(&CurHTTP, ts_conn);
|
||
// начало предачи файла, если есть
|
||
if((!CheckSCB(SCB_CLOSED | SCB_DISCONNECT | SCB_FCLOSE))&&CheckSCB(SCB_FOPEN)) webserver_send_fdata(ts_conn);
|
||
}
|
||
else {
|
||
#if DEBUGSOO > 1
|
||
os_printf("sndbuf=%u! ", tcp_sndbuf(ts_conn->pcb));
|
||
#endif
|
||
SetSCB(SCB_FCLOSE | SCB_DISCONNECT);
|
||
};
|
||
}
|
||
if(CheckSCB(SCB_FCLOSE)) {
|
||
tcp_output(ts_conn->pcb);
|
||
Close_web_conn(ts_conn);
|
||
SetSCB(SCB_DISCONNECT);
|
||
}
|
||
if(CheckSCB(SCB_DISCONNECT)) web_int_disconnect(ts_conn);
|
||
#if DEBUGSOO > 1
|
||
else os_printf("...\n");
|
||
#endif
|
||
return ERR_OK;
|
||
}
|
||
/******************************************************************************
|
||
* web_int_disconnect
|
||
*******************************************************************************/
|
||
static void ICACHE_FLASH_ATTR web_int_disconnect(TCP_SERV_CONN *ts_conn)
|
||
{
|
||
#if DEBUGSOO > 1
|
||
os_printf("dis\n");
|
||
#endif
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
ts_conn->flag.tx_null = 1;
|
||
ts_conn->flag.rx_null = 1;
|
||
tcpsrv_unrecved_win(ts_conn);
|
||
if(ts_conn->flag.pcb_time_wait_free) tcpsrv_disconnect(ts_conn);
|
||
SetSCB(SCB_CLOSED);
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : webserver_sent_cb
|
||
* Description : Sent callback function to call for this espconn when data
|
||
* is successfully sent
|
||
* Parameters : arg -- Additional argument to pass to the callback function
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
static err_t ICACHE_FLASH_ATTR webserver_sent_callback(TCP_SERV_CONN *ts_conn)
|
||
{
|
||
#if DEBUGSOO > 1
|
||
tcpsrv_print_remote_info(ts_conn);
|
||
#endif
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
if(web_conn == NULL) return ERR_ARG;
|
||
if(CheckSCB(SCB_CLOSED) == 0) { // No SCB_CLOSED
|
||
if(!CheckSCB(SCB_DISCONNECT)) {
|
||
#ifdef WEBSOCKET_ENA
|
||
if(CheckSCB(SCB_WSDATA)) {
|
||
websock_rx_data(ts_conn);
|
||
}
|
||
else
|
||
#endif
|
||
if((!CheckSCB(SCB_FCLOSE))&&CheckSCB(SCB_FOPEN)) webserver_send_fdata(ts_conn);
|
||
}
|
||
if(CheckSCB(SCB_FCLOSE)) {
|
||
Close_web_conn(ts_conn);
|
||
SetSCB(SCB_DISCONNECT);
|
||
}
|
||
if(CheckSCB(SCB_DISCONNECT)) web_int_disconnect(ts_conn);
|
||
#if DEBUGSOO > 1
|
||
else os_printf("...\n");
|
||
#endif
|
||
}
|
||
else { // SCB_CLOSED
|
||
#if DEBUGSOO > 1
|
||
os_printf("#%04x ?\n", web_conn->webflag);
|
||
#endif
|
||
ts_conn->flag.tx_null = 1;
|
||
ts_conn->flag.rx_null = 1;
|
||
};
|
||
return ERR_OK;
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : webserver_disconnect
|
||
* Description : calback disconnect
|
||
* Parameters : arg -- Additional argument to pass to the callback function
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
static void ICACHE_FLASH_ATTR webserver_disconnect(TCP_SERV_CONN *ts_conn)
|
||
{
|
||
#if DEBUGSOO > 1
|
||
tcpsrv_disconnect_calback_default(ts_conn);
|
||
#endif
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
if(web_conn == NULL) return;
|
||
Close_web_conn(ts_conn);
|
||
if(CheckSCB(SCB_SYSSAVE)) {
|
||
ClrSCB(SCB_SYSSAVE);
|
||
sys_write_cfg();
|
||
};
|
||
}
|
||
|
||
/******************************************************************************
|
||
******************************************************************************/
|
||
BaseType_t webserver_qfn(web_ex_func_cb fnc, void * param, uint16 pause_ms) {
|
||
WEB_SRV_QFNK qfn;
|
||
qfn.fnc = fnc;
|
||
qfn.param = param;
|
||
qfn.pause_ms = pause_ms;
|
||
return xQueueSendToBack(xQueueWebSrv, &qfn, 0);
|
||
}
|
||
/******************************************************************************
|
||
* todo: временная затычка, необходимо переделать...
|
||
******************************************************************************/
|
||
void qfnk_task(void)
|
||
{
|
||
WEB_SRV_QFNK qfn;
|
||
WEB_SRV_QFNK qfnt;
|
||
TickType_t timetick;
|
||
qfnt.fnc = NULL;
|
||
qfnt.pause_ms = 0;
|
||
while(1) {
|
||
if(xQueueReceive(xQueueWebSrv, &qfn, 5) == pdPASS) { // portMAX_DELAY
|
||
if(qfn.fnc) {
|
||
#if DEBUGSOO > 2
|
||
os_printf("qfn: %p(%p),%d\n", qfn.fnc, qfn.param, qfn.pause_ms);
|
||
#endif
|
||
if(qfn.pause_ms) {
|
||
timetick = xTaskGetTickCount();
|
||
qfnt = qfn;
|
||
}
|
||
else qfn.fnc((uint32) qfn.param);
|
||
}
|
||
}
|
||
else if(qfnt.fnc) {
|
||
if(xTaskGetTickCount() - timetick > qfnt.pause_ms) {
|
||
#if DEBUGSOO > 3
|
||
os_printf("qfnt: %p(%p),%d\n", qfnt.fnc, qfnt.param, qfnt.pause_ms);
|
||
#endif
|
||
qfnt.fnc((uint32) qfnt.param);
|
||
qfnt.fnc = NULL;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : webserver_init
|
||
* Description : Открытие сервера
|
||
* Parameters : arg -- port N
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
err_t ICACHE_FLASH_ATTR webserver_init(uint16 portn)
|
||
{
|
||
// WEBFSInit(); // файловая система
|
||
err_t err = ERR_MEM;
|
||
xQueueWebSrv = xQueueCreate(5, sizeof( WEB_SRV_QFNK )); // Create a queue...
|
||
if(xQueueWebSrv) {
|
||
if(xTaskCreate(qfnk_task, "web_qfn", 1024, NULL, tskIDLE_PRIORITY + 1 + PRIORITIE_OFFSET, NULL) == pdPASS)
|
||
{
|
||
TCP_SERV_CFG *p = tcpsrv_init(portn);
|
||
if (p != NULL) {
|
||
// изменим конфиг на наше усмотрение:
|
||
if(syscfg.cfg.b.web_time_wait_delete) p->flag.pcb_time_wait_free = 1; // пусть убивает, для теста и проксей
|
||
p->max_conn = 99; // сработает по heap_size
|
||
#if DEBUGSOO > 3
|
||
os_printf("Max connection %d, time waits %d & %d, min heap size %d\n",
|
||
p->max_conn, p->time_wait_rec, p->time_wait_cls, p->min_heap);
|
||
#endif
|
||
p->time_wait_rec = syscfg.web_twrec; // if =0 -> вечное ожидание
|
||
p->time_wait_cls = syscfg.web_twcls; // if =0 -> вечное ожидание
|
||
// слинкуем с желаемыми процедурами:
|
||
p->func_discon_cb = webserver_disconnect;
|
||
// p->func_listen = webserver_listen; // не требуется
|
||
p->func_sent_cb = webserver_sent_callback;
|
||
p->func_recv = webserver_received_data;
|
||
err = tcpsrv_start(p);
|
||
if (err != ERR_OK) {
|
||
tcpsrv_close(p);
|
||
p = NULL;
|
||
}
|
||
else {
|
||
#if DEBUGSOO > 1
|
||
os_printf("WEB: init port %u\n", portn);
|
||
#endif
|
||
}
|
||
|
||
}
|
||
// else err = ERR_MEM;
|
||
}
|
||
// else err = ERR_MEM;
|
||
}
|
||
// else err = ERR_MEM;
|
||
return err;
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : webserver_close
|
||
* Description : закрытие сервера
|
||
* Parameters : arg -- port N
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
err_t ICACHE_FLASH_ATTR webserver_close(uint16 portn)
|
||
{
|
||
err_t err = ERR_ARG;
|
||
if(portn != 0) err = tcpsrv_close(tcpsrv_server_port2pcfg(portn));
|
||
#if DEBUGSOO > 1
|
||
if(err == ERR_OK) os_printf("WEB: close\n");
|
||
#endif
|
||
if(xQueueWebSrv) {
|
||
WEB_SRV_QFNK qfn;
|
||
qfn.fnc = (web_ex_func_cb) vTaskDelete;
|
||
qfn.param = NULL;
|
||
qfn.pause_ms = 0;
|
||
if(xQueueSendToBack(xQueueWebSrv, &qfn, 1000) == pdPASS) {
|
||
while(uxQueueMessagesWaiting(xQueueWebSrv)) {
|
||
vTaskDelay(10);
|
||
};
|
||
}
|
||
xQueueWebSrv = NULL;
|
||
};
|
||
return err;
|
||
}
|
||
/******************************************************************************
|
||
* FunctionName : webserver_reinit
|
||
* Description : закрытие сервера и открытие нового
|
||
* Parameters : arg -- port N открытого порта
|
||
* Returns : none
|
||
*******************************************************************************/
|
||
err_t ICACHE_FLASH_ATTR webserver_reinit(uint16 portn)
|
||
{
|
||
err_t err = ERR_OK;
|
||
// if(portn == syscfg.web_port) return err;
|
||
if(portn) err = tcpsrv_close(tcpsrv_server_port2pcfg(portn)); // закрыть старый порт
|
||
if(syscfg.web_port) err = webserver_init(syscfg.web_port); // открыть новый
|
||
return err;
|
||
}
|
||
|
||
#endif // USE_WEB
|