RTL00_WEB_VS/RTLGDB/Project/web/web_srv.c
ADElectronics 764b020238 update
2017-11-28 22:31:40 +03:00

2076 lines
85 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/******************************************************************************
* 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