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

352 lines
11 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: 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