mirror of
https://github.com/ADElectronics/RTL00_WEB_VS.git
synced 2024-11-22 07:44:15 +00:00
352 lines
11 KiB
C
352 lines
11 KiB
C
/******************************************************************************
|
||
* FileName: web_websocket.c
|
||
* Description: websocket for web
|
||
* Author: pvvx
|
||
* 2016
|
||
*******************************************************************************/
|
||
#include "user_config.h"
|
||
#ifdef WEBSOCKET_ENA
|
||
#include "autoconf.h"
|
||
#include "FreeRTOS.h"
|
||
#include "task.h"
|
||
#include "diag.h"
|
||
//#include "bios.h"
|
||
//#include "osapi.h"
|
||
//#include "sdk/rom2ram.h"
|
||
#include "lwip/tcp.h"
|
||
#include "tcpsrv/tcp_srv_conn.h"
|
||
#include "web_srv_int.h"
|
||
#include "web_srv.h"
|
||
#include "web_utils.h"
|
||
#include "web_websocket.h"
|
||
#include "websock.h"
|
||
#include "rtl8195a/rtl_libc.h"
|
||
#include "esp_comp.h"
|
||
|
||
#if 0
|
||
#undef DEBUGSOO
|
||
#define DEBUGSOO 4
|
||
#endif
|
||
|
||
#define copy_s4d1 rtl_memcpy
|
||
|
||
//#define mMIN(a, b) ((a<b)?a:b)
|
||
|
||
#define MAX_RX_BUF_SIZE 8192
|
||
|
||
const char txt_wsping[] ICACHE_RODATA_ATTR = "ws:ping";
|
||
const char txt_wspong[] ICACHE_RODATA_ATTR = "ws:pong";
|
||
|
||
//=============================================================================
|
||
// websock_tx_frame() - передача фрейма
|
||
//=============================================================================
|
||
err_t ICACHE_FLASH_ATTR
|
||
websock_tx_frame(TCP_SERV_CONN *ts_conn, uint32 opcode, uint8 *raw_data, uint32 raw_len)
|
||
{
|
||
err_t err = WebsocketTxFrame(ts_conn, opcode, raw_data, raw_len);
|
||
if(err != ERR_OK) {
|
||
#if DEBUGSOO > 3
|
||
os_printf("ws%utx[%u] error %d!\n", opcode, raw_len, err);
|
||
#endif
|
||
((WEB_SRV_CONN *)ts_conn->linkd)->webflag |= SCB_DISCONNECT;
|
||
}
|
||
else {
|
||
if((opcode & WS_OPCODE_BITS) == WS_OPCODE_CLOSE) {
|
||
((WEB_SRV_CONN *)ts_conn->linkd)->ws.flg |= WS_FLG_CLOSE;
|
||
}
|
||
}
|
||
return err;
|
||
}
|
||
//=============================================================================
|
||
// websock_tx_close_err() - вывод сообщения закрытия или ошибки
|
||
//=============================================================================
|
||
err_t ICACHE_FLASH_ATTR
|
||
websock_tx_close_err(TCP_SERV_CONN *ts_conn, uint32 err)
|
||
{
|
||
uint8 uc[2];
|
||
uc[1] = err;
|
||
uc[0] = err>>8;
|
||
return websock_tx_frame(ts_conn, WS_OPCODE_CLOSE | WS_FRAGMENT_FIN, uc, 2);
|
||
}
|
||
//=============================================================================
|
||
// websock_rx_data() прием данных
|
||
//=============================================================================
|
||
#define MAX_WS_DATA_BLK_SIZE (tcp_sndbuf(ts_conn->pcb)-8) // (TCP_MSS - 8)
|
||
//=============================================================================
|
||
bool ICACHE_FLASH_ATTR
|
||
websock_rx_data(TCP_SERV_CONN *ts_conn)
|
||
{
|
||
// HTTP_CONN *CurHTTP;
|
||
WEB_SRV_CONN *web_conn = (WEB_SRV_CONN *)ts_conn->linkd;
|
||
if(web_conn == NULL) return false;
|
||
WS_FRSTAT *ws = &web_conn->ws;
|
||
uint16 len;
|
||
uint8 *pstr;
|
||
|
||
#if DEBUGSOO > 3
|
||
os_printf("ws_rx[%u]%u ", ts_conn->sizei, ts_conn->cntri);
|
||
#endif
|
||
if(ts_conn->sizei == 0) return true; // докачивать
|
||
tcpsrv_unrecved_win(ts_conn);
|
||
if((ws->flg & WS_FLG_CLOSE) != 0) {
|
||
// убить буфер ts_conn->pbufi, конец давно :)
|
||
web_feee_bufi(ts_conn);
|
||
SetSCB(SCB_DISCONNECT);
|
||
return false;
|
||
}
|
||
if(ts_conn->sizei > MAX_RX_BUF_SIZE) {
|
||
#if DEBUGSOO > 0
|
||
os_printf("ws:rxbuf_full! ");
|
||
#endif
|
||
// убить буфер ts_conn->pbufi и ответить ошибкой WS_CLOSE_UNEXPECTED_ERROR
|
||
web_feee_bufi(ts_conn);
|
||
websock_tx_close_err(ts_conn, WS_CLOSE_MESSAGE_TOO_BIG); // WS_CLOSE_UNEXPECTED_ERROR);
|
||
SetSCB(SCB_DISCONNECT);
|
||
return false;
|
||
}
|
||
pstr = ts_conn->pbufi;// + ts_conn->cntri;
|
||
len = ts_conn->sizei;// - ts_conn->cntri;
|
||
while(ts_conn->cntri < ts_conn->sizei || (ws->flg & WS_FLG_FIN) != 0) {
|
||
pstr = ts_conn->pbufi;// + ts_conn->cntri;
|
||
len = ts_conn->sizei;// - ts_conn->cntri;
|
||
if((ws->flg & WS_FLG_FIN) != 0 // обработка
|
||
|| ws->frame_len > ws->cur_len) {
|
||
ws->flg &= ~WS_FLG_FIN;
|
||
len = mMIN(ws->frame_len - ws->cur_len, mMIN(MAX_WS_DATA_BLK_SIZE, len));
|
||
// размаскировать
|
||
if((ws->flg & WS_FLG_MASK) != 0) WebsocketMask(ws, pstr, len);
|
||
#if DEBUGSOO > 3
|
||
os_printf("wsfr[%u]blk[%u]at:%u ", ws->frame_len, len, ws->cur_len);
|
||
#endif
|
||
switch(ws->status) {
|
||
case sw_frs_binary:
|
||
#if DEBUGSOO > 1
|
||
os_printf("ws:bin ");
|
||
#endif
|
||
if(ws->frame_len != 0) {
|
||
// пока просто эхо
|
||
uint32 opcode = WS_OPCODE_BINARY;
|
||
if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE;
|
||
if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN;
|
||
if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) {
|
||
return false; // не докачивать, ошибка или закрытие
|
||
}
|
||
}
|
||
ws->cur_len += len;
|
||
ts_conn->cntri += len;
|
||
break;
|
||
case sw_frs_text:
|
||
#if DEBUGSOO > 1
|
||
os_printf("ws:txt ");
|
||
|
||
#if DEBUGSOO > 2
|
||
if(ws->frame_len != 0) {
|
||
uint8 tt = pstr[len];
|
||
pstr[len] = 0;
|
||
os_printf("'%s' ", pstr);
|
||
pstr[len] = tt;
|
||
}
|
||
#endif
|
||
#endif
|
||
if(ws->frame_len == ws->cur_len + len && ws->frame_len != 0) { // полное соо
|
||
web_conn->msgbufsize = tcp_sndbuf(ts_conn->pcb); // сколько можем выввести сейчас?
|
||
if (web_conn->msgbufsize < MIN_SEND_SIZE) {
|
||
#if DEBUGSOO > 0
|
||
os_printf("ws:sndbuf=%u! ", web_conn->msgbufsize);
|
||
#endif
|
||
websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR);
|
||
SetSCB(SCB_FCLOSE|SCB_DISCONNECT);
|
||
return false;
|
||
}
|
||
web_conn->msgbufsize -= 16;
|
||
if(ws->frame_len == (sizeof(txt_wsping)-1) && rom_xstrcmp(pstr, txt_wsping) != 0){
|
||
copy_s4d1(pstr, (void *)txt_wspong, sizeof(txt_wspong) - 1);
|
||
if(websock_tx_frame(ts_conn, WS_OPCODE_TEXT | WS_FRAGMENT_FIN, pstr, sizeof(txt_wspong) - 1) != ERR_OK) {
|
||
return false; // не докачивать, ошибка или закрытие
|
||
}
|
||
}
|
||
else {
|
||
if(web_conn->msgbuf) os_free(web_conn->msgbuf);
|
||
web_conn->msgbuf = (uint8 *) os_malloc(web_conn->msgbufsize);
|
||
if (web_conn->msgbuf == NULL) {
|
||
#if DEBUGSOO > 0
|
||
os_printf("ws:mem!\n");
|
||
#endif
|
||
websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR);
|
||
SetSCB(SCB_FCLOSE|SCB_DISCONNECT);
|
||
return false;
|
||
};
|
||
web_conn->msgbuflen = 0;
|
||
uint32 opcode;
|
||
if(CheckSCB(SCB_RETRYCB)) { // повторный callback? да
|
||
if(web_conn->func_web_cb != NULL) web_conn->func_web_cb(ts_conn);
|
||
if(!CheckSCB(SCB_RETRYCB)) {
|
||
// ClrSCB(SCB_FCLOSE | SCB_DISCONNECT);
|
||
opcode = WS_OPCODE_CONTINUE | WS_FRAGMENT_FIN;
|
||
}
|
||
else opcode = WS_OPCODE_CONTINUE;
|
||
}
|
||
else {
|
||
pstr[len] = '\0';
|
||
uint8 *vstr = os_strchr(pstr, '=');
|
||
if(vstr != NULL) {
|
||
*vstr++ = '\0';
|
||
web_int_vars(ts_conn, pstr, vstr);
|
||
}
|
||
else {
|
||
web_conn->msgbuf[0] = 0;
|
||
web_int_callback(ts_conn, pstr);
|
||
}
|
||
if(CheckSCB(SCB_RETRYCB)) opcode = WS_OPCODE_TEXT;
|
||
else {
|
||
// ClrSCB(SCB_FCLOSE | SCB_DISCONNECT);
|
||
opcode = WS_OPCODE_TEXT | WS_FRAGMENT_FIN;
|
||
}
|
||
}
|
||
if(web_conn->msgbuflen != 0) {
|
||
if(websock_tx_frame(ts_conn, opcode, web_conn->msgbuf, web_conn->msgbuflen) != ERR_OK) {
|
||
if(web_conn->msgbuf) os_free(web_conn->msgbuf);
|
||
web_conn->msgbuf = NULL;
|
||
return false; // не докачивать, ошибка или закрытие
|
||
}
|
||
}
|
||
if(web_conn->msgbuf) os_free(web_conn->msgbuf);
|
||
web_conn->msgbuf = NULL;
|
||
if(CheckSCB(SCB_RETRYCB)) return false;
|
||
}
|
||
}
|
||
/*
|
||
if(0) {
|
||
uint32 opcode = WS_OPCODE_TEXT;
|
||
if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE;
|
||
if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN;
|
||
if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) {
|
||
return false; // не докачивать, ошибка или закрытие
|
||
}
|
||
}
|
||
*/
|
||
ws->cur_len += len;
|
||
ts_conn->cntri += len;
|
||
return true; // докачивать
|
||
// break;
|
||
// break;
|
||
case sw_frs_ping:
|
||
#if DEBUGSOO > 1
|
||
os_printf("ws:ping ");
|
||
#endif
|
||
{
|
||
uint32 opcode = WS_OPCODE_PONG;
|
||
if(ws->cur_len != 0) opcode = WS_OPCODE_CONTINUE;
|
||
if(ws->frame_len == ws->cur_len + len) opcode |= WS_FRAGMENT_FIN;
|
||
if(websock_tx_frame(ts_conn, opcode, pstr, len) != ERR_OK) {
|
||
return false; // не докачивать, ошибка или закрытие
|
||
}
|
||
}
|
||
ws->cur_len += len;
|
||
ts_conn->cntri += len;
|
||
return true; // докачивать
|
||
// break;
|
||
case sw_frs_pong:
|
||
#if DEBUGSOO > 1
|
||
os_printf("ws:pong ");
|
||
#endif
|
||
ws->cur_len += len;
|
||
ts_conn->cntri += len;
|
||
break;
|
||
// return true;
|
||
case sw_frs_close:
|
||
#if DEBUGSOO > 1
|
||
os_printf("ws:close ");
|
||
#endif
|
||
// if((ws->flg & WS_FLG_CLOSE) == 0) {
|
||
{
|
||
if(len >= 2) {
|
||
uint32 close_code = (pstr[0]<<8) | pstr[1];
|
||
#if DEBUGSOO > 1
|
||
os_printf("code:%d ", close_code);
|
||
#endif
|
||
if(close_code == WS_CLOSE_NORMAL) websock_tx_close_err(ts_conn, WS_CLOSE_NORMAL);
|
||
// else websock_tx_frame(ts_conn, WS_OPCODE_CLOSE | WS_FRAGMENT_FIN, NULL, 0);
|
||
}
|
||
else
|
||
{
|
||
websock_tx_close_err(ts_conn, WS_CLOSE_NORMAL);
|
||
// websock_tx_frame(ts_conn, WS_OPCODE_CLOSE | WS_FRAGMENT_FIN, NULL, 0);
|
||
}
|
||
}
|
||
ts_conn->flag.pcb_time_wait_free = 1;
|
||
SetSCB(SCB_DISCONNECT);
|
||
// ts_conn->cntri = ts_conn->sizei;
|
||
/* ws->cur_len += len;
|
||
ts_conn->cntri += len; */
|
||
return false;
|
||
default:
|
||
#if DEBUGSOO > 0
|
||
os_printf("ws:f?! ");
|
||
#endif
|
||
websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR);
|
||
SetSCB(SCB_DISCONNECT);
|
||
// ts_conn->cntri = ts_conn->sizei;
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
if(ws->cur_len >= ws->frame_len) { // прием и разбор нового фрейма
|
||
if((ws->flg & WS_FLG_FIN) != 0) { // обработка
|
||
#if DEBUGSOO > 3
|
||
os_printf("ws_rx:fin=%u ", ws->cur_len);
|
||
#endif
|
||
}
|
||
else {
|
||
uint32 ret = WebsocketHead(ws, pstr, len);
|
||
if(ret >= WS_CLOSE_NORMAL) { // error или close
|
||
|
||
#if DEBUGSOO > 0
|
||
os_printf("ws:txerr=%u ", ret);
|
||
#endif
|
||
websock_tx_close_err(ts_conn, ret);
|
||
// ts_conn->cntri = ts_conn->sizei; // убить буфер ts_conn->pbufi
|
||
return false; // error
|
||
}
|
||
else if(ret == 0) {
|
||
#if DEBUGSOO > 3
|
||
os_printf("ws_rx... ");
|
||
#endif
|
||
return true; // докачивать
|
||
}
|
||
ts_conn->cntri += ws->head_len; // вычесть заголовок
|
||
/*
|
||
switch(ws->status) {
|
||
case sw_frs_binary:
|
||
break;
|
||
case sw_frs_text:
|
||
if(ws->frame_len > MAX_RX_BUF_SIZE) {
|
||
websock_tx_close_err(ts_conn, WS_CLOSE_MESSAGE_TOO_BIG);
|
||
return false;
|
||
}
|
||
break;
|
||
}
|
||
*/
|
||
}
|
||
}
|
||
#if DEBUGSOO > 3
|
||
os_printf("trim%u-%u ", ts_conn->sizei, ts_conn->sizei - ts_conn->cntri );
|
||
#endif
|
||
if(!web_trim_bufi(ts_conn, &ts_conn->pbufi[ts_conn->cntri], ts_conn->sizei - ts_conn->cntri)) {
|
||
#if DEBUGSOO > 0
|
||
os_printf("ws:trim_err! ");
|
||
#endif
|
||
// убить буфер ts_conn->pbufi и ответить ошибкой WS_CLOSE_UNEXPECTED_ERROR
|
||
websock_tx_close_err(ts_conn, WS_CLOSE_UNEXPECTED_ERROR);
|
||
SetSCB(SCB_DISCONNECT);
|
||
// ts_conn->cntri = ts_conn->sizei;
|
||
return false;
|
||
};
|
||
}
|
||
return false; // не докачивать, ошибка или закрытие
|
||
}
|
||
//=============================================================================
|
||
//=============================================================================
|
||
#endif // WEBSOCKET_ENA
|
||
|
||
|