RTL00_WEB_WS2812/Firmware/RTLGDB/Project/web/web_srv.c

2077 lines
85 KiB
C
Raw Normal View History

2017-12-24 09:49:22 +00:00
/******************************************************************************
* 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>&copy</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