RTL00_WEB_VS/RTLGDB/Project/web/web_websocket.c

353 lines
11 KiB
C
Raw Normal View History

2017-11-28 19:31:40 +00:00
/******************************************************************************
* 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