doubao_chat/lib/net/uC-HTTPc/Client/Source/http-c_websock.c

1622 lines
60 KiB
C

/*
*********************************************************************************************************
* uC/HTTP
* Hypertext Transfer Protocol
*
* Copyright 2004-2020 Silicon Laboratories Inc. www.silabs.com
*
* SPDX-License-Identifier: APACHE-2.0
*
* This software is subject to an open source license and is distributed by
* Silicon Laboratories Inc. pursuant to the terms of the Apache License,
* Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
*
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*
* HTTP CLIENT WEBSOCKET MODULE
*
* Filename : http-c_websock.c
* Version : V3.01.00
*********************************************************************************************************
*/
/*
*********************************************************************************************************
*********************************************************************************************************
* INCLUDE FILES
*********************************************************************************************************
*********************************************************************************************************
*/
#define MICRIUM_SOURCE
#define HTTPc_WEBSOCK_MODULE
#include <net_ascii.h>
#include <net_util.h>
#include <lib_def.h>
#include <lib_math.h>
#include <net.h>
#include <net_cfg_net.h>
#include <net_app.h>
#include <net_sock.h>
#include <net_util.h>
#include "http-c_websock.h"
#include "http-c_conn.h"
#include "http-c_sock.h"
#include "http-c_req.h"
#include "http-c_resp.h"
#ifdef HTTPc_TASK_MODULE_EN
#include "http-c_task.h"
#endif
//添加宏定义
#undef HTTPc_WEBSOCK_MODULE_EN
/*
*********************************************************************************************************
*********************************************************************************************************
* MODULE
*********************************************************************************************************
*********************************************************************************************************
*/
#ifdef HTTPc_WEBSOCK_MODULE_EN
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL DEFINES
*********************************************************************************************************
*********************************************************************************************************
*/
#define HTTPc_WEBSOCK_TX_REQ_Q_NAME "WebSocket Tx Req Q name"
#define HTTPc_WEBSOCK_PROTOCOL_ERR_MSG "\x03\xEAProtocolError"
#define HTTPc_WEBSOCK_MIN_HEADER_LEN 2u
#define HTTPc_WEBSOCK_MAX_HEADER_LEN 14u
#define HTTPc_WEBSOCK_FIN_BIT DEF_BIT_07
#define HTTPc_WEBSOCK_MASK_BIT DEF_BIT_07
#define HTTPc_WEBSOCK_OPCODE_MASK DEF_NIBBLE_MASK
#define HTTPc_WEBSOCK_PAYLOAD_MASK 0x7F
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL MACRO'S
*********************************************************************************************************
*********************************************************************************************************
*/
#define HTTPc_WEBSOCK_GET_TX_BUF_SIZE(p_conn) (p_conn->BufLen - p_conn->TxDataLen - (p_conn->TxBufPtr - p_conn->BufPtr))
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTION PROTOTYPES
*********************************************************************************************************
*********************************************************************************************************
*/
static void HTTPcWebSock_TX(HTTPc_CONN *p_conn);
static void HTTPcWebSock_RX(HTTPc_CONN *p_conn);
static CPU_INT32U HTTPcWebSock_GetMsgHdr(HTTPc_CONN *p_conn,
HTTPc_ERR *p_err);
static void HTTPcWebSock_GetPayload(HTTPc_CONN *p_conn);
static void HTTPcWebSock_SetTxBuf(HTTPc_WEBSOCK_MSG *p_msg,
CPU_CHAR *p_dst_buf,
CPU_CHAR *p_src_buf,
CPU_INT32U len);
static void HTTPcWebSock_SetTxBufHdr(HTTPc_CONN *p_conn,
HTTPc_WEBSOCK_MSG *p_tx_msg,
CPU_CHAR *p_tx_buf);
static void HTTPcWebSock_SetCtrlResponse(HTTPc_CONN *p_conn,
HTTPc_WEBSOCK_OPCODE opcode,
CPU_CHAR *p_data,
CPU_INT32U msg_len);
static void HTTPcWebSock_TxMsgRemove(HTTPc_CONN *p_conn);
static void HTTPcWebSock_TxMsgCleanList(HTTPc_CONN *p_conn);
static CPU_INT16U HTTPcWebSock_ComputeHdrLen(HTTPc_WEBSOCK_MSG *p_msg);
static CPU_BOOLEAN HTTPcWebSock_IsProtocolError(HTTPc_ERR err);
static CPU_BOOLEAN HTTPcWebSock_IsStandardCloseCode(HTTPc_WEBSOCK_CLOSE_CODE close_code);
/*
*********************************************************************************************************
* HTTPcWebSock_Process()
*
* Description : Process all the states for a HTTP connection that has been upgraded to a WebSocket.
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* Return(s) : none.
*
* Caller(s) : HTTPcTask_Handler().
*
* Note(s) : none.
*********************************************************************************************************
*/
void HTTPcWebSock_Process(HTTPc_CONN *p_conn)
{
HTTPc_CONN_OBJ *p_conn_const;
HTTPc_WEBSOCK *p_ws;
CPU_BOOLEAN is_protocol_err;
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_ws = p_conn->WebSockPtr;
switch (p_conn->State) {
case HTTPc_CONN_STATE_WEBSOCK_INIT:
/* WebSocket OnOpen Hook. */
if (p_ws->OnOpen != DEF_NULL) {
p_ws->OnOpen(p_conn_const);
}
/* Set the WebSocket initial state. */
p_ws->TxState = HTTPc_WEBSOCK_TX_STATE_MSG_INIT;
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_WAIT;
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_RXTX;
break;
case HTTPc_CONN_STATE_WEBSOCK_RXTX:
HTTPcWebSock_TX(p_conn); /* Process Tx states of the WebSocket. */
HTTPcWebSock_RX(p_conn); /* Process Rx states of the WebSocket. */
break;
case HTTPc_CONN_STATE_WEBSOCK_ERR:
/* WebSocket OnErr hook. */
if (p_ws->OnErr != DEF_NULL) {
p_ws->OnErr(p_conn_const, p_conn->ErrCode);
}
is_protocol_err = HTTPcWebSock_IsProtocolError(p_conn->ErrCode);
if (is_protocol_err == DEF_YES) { /* Send a Close Frame if the Err Code a Protocol Error. */
p_ws->CloseCode = HTTPc_WEBSOCK_CLOSE_CODE_PROTOCOL_ERR;
if (p_ws->Flags.IsTxMsgCtrlUsed == DEF_NO) {
HTTPcWebSock_SetCtrlResponse(p_conn,
HTTPc_WEBSOCK_OPCODE_CLOSE,
HTTPc_WEBSOCK_PROTOCOL_ERR_MSG,
sizeof(HTTPc_WEBSOCK_PROTOCOL_ERR_MSG));
p_ws->Flags.IsCloseStarted = DEF_YES;
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_WAIT;
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_RXTX;
} else { /* If the ctrl msg is already used, close the conn. */
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_CLOSE;
}
} else {
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_CLOSE;
}
break;
case HTTPc_CONN_STATE_WEBSOCK_CLOSE:
/* WebSocket OnClose hook. */
if (p_ws->OnClose != DEF_NULL) {
p_ws->OnClose(p_conn_const, p_ws->CloseCode, &p_ws->CloseReason);
}
HTTPcWebSock_TxMsgCleanList(p_conn); /* Empty the Tx Msg request pending list. */
/* Change state to remove and close the conn. */
p_conn->State = HTTPc_CONN_STATE_CLOSE;
break;
default:
CPU_SW_EXCEPTION(;);
break;
}
}
/*
*********************************************************************************************************
* HTTPcWebSock_InitReqObj()
*
* Description : Initialize the WebSocket Request object.
*
* Argument(s) : p_err Pointer to variable that will receive the return error code from this function :
*
* HTTPc_ERR_NONE
* HTTPc_ERR_WEBSOCK_BASE64_ENCODE_FAIL
* HTTPc_ERR_WEBSOCK_SHA1_ENCODE_FAIL
* HTTPc_ERR_WEBSOCK_REQ_MEM_ERR
*
* Return(s) : If the operation is successful, pointer to the WebSocket Request object to initialize.
* If the operation has failed, DEF_NULL;
*
* Caller(s) : HTTPcWebSock_Upgrade().
*
* Note(s) : none.
*********************************************************************************************************
*/
HTTPc_WEBSOCK_REQ *HTTPcWebSock_InitReqObj(HTTPc_ERR *p_err)
{
CPU_INT32U val;
CPU_CHAR tmp_buf[HTTPc_WEBSOCK_KEY_PRE_HASH_LEN];
CPU_INT08U i;
NET_ERR net_err;
NET_SHA1_ERR sha1_err;
NET_SHA1_CTX sha1_ctx;
HTTPc_WEBSOCK_REQ *p_ws_req;
/* ------------- CREATE RAMDOM 16 BYTES --------------- */
/* Reset the WebSocket handshake obj. */
p_ws_req = HTTPc_Mem_WebSockReqGet();
if (p_ws_req == DEF_NULL) {
*p_err = HTTPc_ERR_WEBSOCK_REQ_MEM_ERR;
goto exit;
}
/* Generate a Random Seed. */
val = NetUtil_RandomRangeGet(1u, DEF_INT_32U_MAX_VAL);
/* Create the 16-byte random-key. */
for (i = 0; i < HTTPc_WEBSOCK_KEY_LEN; i += sizeof(CPU_INT32U)) {
val = Math_RandSeed(val);
Mem_Copy(&tmp_buf[i], &val, sizeof(CPU_INT32U));
}
/* --------- PROCESS SEC-WEBSOCKET-KEY VALUE ---------- */
NetBase64_Encode(tmp_buf,
HTTPc_WEBSOCK_KEY_LEN,
p_ws_req->Key,
HTTPc_WEBSOCK_KEY_ENCODED_LEN,
&net_err);
if (net_err != NET_ERR_NONE) {
*p_err = HTTPc_ERR_WEBSOCK_BASE64_ENCODE_FAIL;
goto exit_release;
}
/* -------- PROCESS SEC-WEBSOCKET-ACCEPT VALUE -------- */
Str_Copy(tmp_buf, p_ws_req->Key);
Str_Cat(tmp_buf, HTTPc_WEBSOCK_GUID_STRING);
NetSHA1_Reset(&sha1_ctx,
&sha1_err);
if (sha1_err != NET_SHA1_ERR_NONE) {
*p_err = HTTPc_ERR_WEBSOCK_SHA1_ENCODE_FAIL;
goto exit_release;
}
NetSHA1_Input(&sha1_ctx,
tmp_buf,
HTTPc_WEBSOCK_KEY_PRE_HASH_LEN,
&sha1_err);
if (sha1_err != NET_SHA1_ERR_NONE) {
*p_err = HTTPc_ERR_WEBSOCK_SHA1_ENCODE_FAIL;
goto exit_release;
}
NetSHA1_Result(&sha1_ctx,
tmp_buf,
&sha1_err);
if (sha1_err != NET_SHA1_ERR_NONE) {
*p_err = HTTPc_ERR_WEBSOCK_SHA1_ENCODE_FAIL;
goto exit_release;
}
NetBase64_Encode(tmp_buf,
HTTPc_WEBSOCK_KEY_HASH_LEN,
p_ws_req->Accept,
HTTPc_WEBSOCK_KEY_HASH_ENCODED_LEN,
&net_err);
if (net_err != NET_ERR_NONE) {
*p_err = HTTPc_ERR_WEBSOCK_BASE64_ENCODE_FAIL;
goto exit_release;
}
exit:
return p_ws_req;
exit_release:
HTTPc_Mem_WebSockReqRelease(p_ws_req);
return DEF_NULL;
}
/*
*********************************************************************************************************
* HTTPcWebSock_TxMsgAdd()
*
* Description : Add a message in the TX message queue.
*
* Argument(s) : p_msg Pointer to the message to queue
*
* p_err Pointer to variable that will receive the return error code from this function.
*
* Return(s) : none.
*
* Caller(s) : HTTPcTask().
*
* Note(s) : none.
*********************************************************************************************************
*/
void HTTPcWebSock_TxMsgAdd(HTTPc_WEBSOCK_MSG *p_msg,
HTTPc_ERR *p_err)
{
HTTPc_CONN *p_conn;
HTTPc_WEBSOCK *p_ws;
HTTPc_WEBSOCK_MSG *p_msg_item;
CPU_BOOLEAN in_use;
p_conn = p_msg->ConnPtr;
p_ws = p_conn->WebSockPtr;
in_use = DEF_BIT_IS_SET(p_conn->Flags, HTTPc_FLAG_CONN_IN_USE);
if (in_use == DEF_YES) {
if (p_ws->TxMsgListHeadPtr == DEF_NULL) {
p_ws->TxMsgListHeadPtr = p_msg;
p_ws->TxMsgListEndPtr = p_msg;
p_msg->NextPtr = 0;
} else {
p_msg_item = p_ws->TxMsgListEndPtr;
p_msg_item->NextPtr = p_msg;
p_ws->TxMsgListEndPtr = p_msg;
p_msg->NextPtr = 0;
}
p_msg->Flags.IsUsed = DEF_YES;
*p_err = HTTPc_ERR_NONE;
} else {
*p_err = HTTPc_ERR_CONN_IS_RELEASED;
}
}
/*
*********************************************************************************************************
*********************************************************************************************************
* LOCAL FUNCTIONS
*********************************************************************************************************
*********************************************************************************************************
*/
/*
*********************************************************************************************************
* HTTPcWebSock_TX()
*
* Description : Process the WebSocket states TX.
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* Return(s) : none.
*
* Caller(s) : HTTPcConn_WebSocketProcess().
*
* Note(s) : none.
*********************************************************************************************************
*/
static void HTTPcWebSock_TX(HTTPc_CONN *p_conn)
{
HTTPc_ERR err;
HTTPc_WEBSOCK *p_ws;
HTTPc_WEBSOCK_MSG *p_msg;
CPU_BOOLEAN done;
HTTPc_CONN_OBJ *p_conn_const;
HTTPc_WEBSOCK_MSG_OBJ *p_msg_const;
CPU_INT16U hdr_len;
CPU_INT16U buf_size;
CPU_INT32U len;
CPU_INT32U len_rem;
CPU_CHAR *p_src_buf;
CPU_BOOLEAN sock_rdy_wr;
CPU_BOOLEAN sock_rdy_err;
CPU_CHAR *p_tx_buf;
HTTPc_WEBSOCK_CLOSE_CODE *p_close_code;
/* ------------------ INITIALIZAITON ------------------ */
p_ws = p_conn->WebSockPtr;
p_msg = p_ws->TxMsgListHeadPtr;
if (p_conn->State != HTTPc_CONN_STATE_WEBSOCK_RXTX) {
return;
}
if (p_msg != DEF_NULL) {
do {
done = DEF_YES; /* Always set to avoid unhandled state. */
switch (p_ws->TxState) {
case HTTPc_WEBSOCK_TX_STATE_MSG_INIT:
/* On Msg Tx Initialization Hook. */
if (p_msg->OnMsgTxInit != DEF_NULL) {
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_msg_const = (HTTPc_WEBSOCK_MSG_OBJ *)p_msg;
p_msg->Len = p_msg->OnMsgTxInit(p_conn_const, p_msg_const);
}
done = DEF_NO;
p_ws->TxState = HTTPc_WEBSOCK_TX_STATE_SET_BUF;
break;
case HTTPc_WEBSOCK_TX_STATE_SET_BUF:
/* Get hdr space needed. */
if (p_msg->Flags.IsHdrSet == DEF_NO) {
hdr_len = HTTPcWebSock_ComputeHdrLen(p_msg);
p_msg->MskKey = NetUtil_RandomRangeGet(1u, DEF_INT_32U_MAX_VAL);
} else {
hdr_len = 0;
}
/* Compute buffer space available. */
buf_size = HTTPc_WEBSOCK_GET_TX_BUF_SIZE(p_conn);
if (buf_size > hdr_len) {
buf_size -= hdr_len;
} else {
done = DEF_YES;
break;
}
/* Check if sock rdy to wr to avoid using the conn... */
/* ...buf unnecessarily. */
sock_rdy_wr = DEF_BIT_IS_SET(p_conn->SockFlags, HTTPc_FLAG_SOCK_RDY_WR);
if (sock_rdy_wr == DEF_NO) {
done = DEF_YES;
break;
}
/* Check if On Msg Tx Data Hook is initialized. */
if (p_msg->OnMsgTxData != DEF_NULL) {
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_msg_const = (HTTPc_WEBSOCK_MSG_OBJ *)p_msg;
p_msg->DataLen = p_msg->OnMsgTxData(p_conn_const,
p_msg_const,
p_conn->TxBufPtr,
buf_size);
if (p_msg->Len == HTTPc_WEBSOCK_TX_MSG_LEN_NOT_DEFINED) {
if (p_msg->DataLen == 0) {
p_msg->Flags.IsFin = DEF_YES;
} else {
p_msg->Flags.IsFin = DEF_NO;
}
p_msg->Len = p_msg->DataLen;
}
p_src_buf = p_conn->TxBufPtr;
} else {
/* Otherwise, use DataPtr. */
p_src_buf = (CPU_CHAR *)p_msg->DataPtr + p_msg->LenSent;
}
/* Compute chunk len and TxDataPtr. */
len_rem = p_msg->Len - p_msg->LenSent;
len = DEF_MIN(len_rem, buf_size);
p_conn->TxDataPtr = p_conn->BufPtr + p_conn->BufLen - len;
/* Mask data and set Tx Buffer */
HTTPcWebSock_SetTxBuf(p_msg, p_conn->TxDataPtr, p_src_buf, len);
/* Set the Hdr if needed. */
if (p_msg->Flags.IsHdrSet == DEF_NO) {
hdr_len = HTTPcWebSock_ComputeHdrLen(p_msg);
p_tx_buf = (CPU_CHAR *)p_conn->TxDataPtr;
p_tx_buf -= hdr_len;
HTTPcWebSock_SetTxBufHdr(p_conn,
p_msg,
p_tx_buf);
p_conn->TxDataPtr = (void *)p_tx_buf;
p_msg->Flags.IsHdrSet = DEF_YES;
}
p_msg->LenSent += len;
p_conn->TxDataLen = len + hdr_len;
p_ws->TxState = HTTPc_WEBSOCK_TX_STATE_SEND_BUF;
done = DEF_NO;
break;
case HTTPc_WEBSOCK_TX_STATE_SEND_BUF:
/* Send Data available in the buffer. */
sock_rdy_wr = DEF_BIT_IS_SET(p_conn->SockFlags, HTTPc_FLAG_SOCK_RDY_WR);
sock_rdy_err = DEF_BIT_IS_SET(p_conn->SockFlags, HTTPc_FLAG_SOCK_RDY_ERR);
if ((sock_rdy_wr == DEF_YES) ||
(sock_rdy_err == DEF_YES)) {
(void)HTTPcSock_ConnDataTx(p_conn, &err);
switch (err) {
case HTTPc_ERR_NONE:
case HTTPc_ERR_CONN_SOCK_TX:
break;
default:
goto exit_err;
}
}
if (p_conn->TxDataLen == 0u) { /* If all the data available in the buf has been sent. */
if (p_msg->LenSent < p_msg->Len) { /* Rtn to prev state if the frame is not completed. */
p_ws->TxState = HTTPc_WEBSOCK_TX_STATE_SET_BUF;
} else if (p_msg->LenSent == p_msg->Len) {
/* Validate if it was the last frame of the message. */
if (p_msg->Flags.IsFin == DEF_YES) {
p_ws->TxState = HTTPc_WEBSOCK_TX_STATE_COMPLETE;
done = DEF_NO;
} else {
p_msg->OpCode = HTTPc_WEBSOCK_OPCODE_CONTINUATION_FRAME;
p_msg->Flags.IsHdrSet = DEF_NO;
p_msg->Len = HTTPc_WEBSOCK_TX_MSG_LEN_NOT_DEFINED;
p_msg->LenSent = 0;
p_ws->TxState = HTTPc_WEBSOCK_TX_STATE_SET_BUF;
}
} else {
err = HTTPc_ERR_WEBSOCK_FAULT;
goto exit_err;
}
}
break;
case HTTPc_WEBSOCK_TX_STATE_COMPLETE:
p_ws->TxState = HTTPc_WEBSOCK_TX_STATE_MSG_INIT;
p_msg->Flags.IsCompleted = DEF_YES;
switch (p_msg->OpCode) { /* Refresh WebSock states according to the msg types. */
case HTTPc_WEBSOCK_OPCODE_CONTINUATION_FRAME:
case HTTPc_WEBSOCK_OPCODE_TXT_FRAME:
case HTTPc_WEBSOCK_OPCODE_BIN_FRAME:
case HTTPc_WEBSOCK_OPCODE_PING:
break;
case HTTPc_WEBSOCK_OPCODE_CLOSE:
/* Check if it was initiated by the server. */
if (p_ws->Flags.IsCloseStarted == DEF_YES) {
/* Set Close information. */
if (p_msg->Len >= sizeof(CPU_INT16U)) {
p_close_code = (CPU_INT16U *)p_msg->DataPtr;
p_ws->CloseCode = MEM_VAL_GET_INT16U_BIG(p_close_code);
p_ws->CloseReason.DataPtr = ((CPU_CHAR *)p_msg->DataPtr) + sizeof(CPU_INT16U);
p_ws->CloseReason.Len = p_msg->Len - HTTPc_WEBSOCK_CLOSE_CODE_LEN;
} else {
p_ws->CloseCode = HTTPc_WEBSOCK_CLOSE_CODE_NONE;
p_ws->CloseReason.DataPtr = DEF_NULL;
p_ws->CloseReason.Len = 0u;
}
/* Restore the Rx msg cached space. */
if (p_ws->Flags.IsRxDataCached == DEF_YES) {
p_conn->TxBufPtr = p_conn->BufPtr;
p_ws->Flags.IsRxDataCached = DEF_NO;
}
/* Closing Handshake is completed. */
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_CLOSE;
p_ws->Flags.IsCloseStarted = DEF_NO;
} else { /* Otherwise, wait for the close response from server. */
p_ws->Flags.IsCloseStarted = DEF_YES;
}
break;
case HTTPc_WEBSOCK_OPCODE_PONG:
/* Check if the Pong msg was initiated by the server. */
if (p_ws->Flags.IsPongStarted == DEF_YES) {
/* Restore the Rx msg cached space. */
if (p_ws->Flags.IsRxDataCached == DEF_YES) {
p_conn->TxBufPtr = p_conn->BufPtr;
p_ws->Flags.IsRxDataCached = DEF_NO;
}
p_ws->Flags.IsPongStarted = DEF_NO;
}
break;
default:
err = HTTPc_ERR_WEBSOCK_INVALID_OPCODE;
goto exit_err;
}
if (p_msg == &p_ws->TxMsgCtrl) { /* Check If Tx Msg object is the internal obj for ctrl. */
p_ws->Flags.IsTxMsgCtrlUsed = DEF_NO;
HTTPcWebSock_TxMsgRemove(p_conn); /* Remove the Tx Msg obj in the list. */
} else {
/* On Msg Tx Complete Hook. */
if (p_msg->OnMsgTxComplete != DEF_NULL) {
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_msg_const = (HTTPc_WEBSOCK_MSG_OBJ *)p_msg;
p_msg->OnMsgTxComplete(p_conn_const, p_msg_const, DEF_OK);
}
HTTPcWebSock_TxMsgRemove(p_conn); /* Remove the Tx Msg obj in the list. */
#ifdef HTTPc_SIGNAL_TASK_MODULE_EN
/* Signal that TX transaction is done. */
if (p_conn->TransDoneSignal.SemObjPtr != DEF_NULL) {
HTTPcTask_TransDoneSignal(p_conn, &err);
if (err != HTTPc_ERR_NONE) {
CPU_SW_EXCEPTION(;);
}
}
#endif
}
break;
default:
err = HTTPc_ERR_WEBSOCK_INVALID_TX_STATE;
goto exit_err;
}
} while (done == DEF_NO);
}
return;
exit_err:
p_conn->ErrCode = err;
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_ERR;
}
/*
*********************************************************************************************************
* HTTPcWebSock_RX()
*
* Description : Process the WebSocket states for RX.
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* Return(s) : none.
*
* Caller(s) : HTTPcConn_WebSocketProcess().
*
* Note(s) : none.
*********************************************************************************************************
*/
static void HTTPcWebSock_RX(HTTPc_CONN *p_conn)
{
HTTPc_ERR err;
HTTPc_WEBSOCK *p_ws;
CPU_BOOLEAN sock_rdy_rd;
CPU_BOOLEAN sock_rdy_err;
CPU_BOOLEAN done;
CPU_BOOLEAN result;
CPU_INT32U rem_rx_len;
CPU_INT32U rx_len;
HTTPc_CONN_OBJ *p_conn_const;
CPU_INT32U msg_len;
CPU_BOOLEAN is_std;
CPU_INT16U *p_close_code;
/* ------------------ INITIALIZAITON ------------------ */
p_ws = p_conn->WebSockPtr;
if (p_conn->State != HTTPc_CONN_STATE_WEBSOCK_RXTX) {
return;
}
do {
done = DEF_YES; /* Always set to avoid unhandled state. */
rem_rx_len = 0u;
/* --------------- RX STATES PROCESSING --------------- */
switch (p_ws->RxState) {
case HTTPc_WEBSOCK_RX_STATE_WAIT:
if (p_conn->RxDataLenRem != 0) { /* If there's data to process... */
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_HDR;
p_ws->RxMsgLenRead = 0u;
p_conn->RxDataLen = 0u;
done = DEF_NO;
DEF_BIT_CLR(p_conn->RespFlags, HTTPc_FLAG_RESP_RX_MORE_DATA);
} else { /* Otherwise, try to rx WebSocket header. */
DEF_BIT_SET(p_conn->RespFlags, HTTPc_FLAG_RESP_RX_MORE_DATA);
rem_rx_len = HTTPc_WEBSOCK_MAX_HEADER_LEN;
}
break;
case HTTPc_WEBSOCK_RX_STATE_HDR: /* Process the msg header. */
rem_rx_len = HTTPcWebSock_GetMsgHdr(p_conn, &err);
if (err != HTTPc_ERR_NONE) {
goto exit_err;
}
if (rem_rx_len == 0u) { /* Proceed to the next step if successful. */
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_PREPARE;
done = DEF_NO;
DEF_BIT_CLR(p_conn->RespFlags, HTTPc_FLAG_RESP_RX_MORE_DATA);
/* On Msg Rx Initialization Hook. */
if (p_ws->OnMsgRxInit != DEF_NULL) {
if ((p_ws->RxMsgOpCode == HTTPc_WEBSOCK_OPCODE_TXT_FRAME) ||
(p_ws->RxMsgOpCode == HTTPc_WEBSOCK_OPCODE_BIN_FRAME)) {
/* Check if the msg is fragmented. */
if (p_ws->RxMsgFlags.IsFin == DEF_NO) {
msg_len = HTTPc_WEBSOCK_TX_MSG_LEN_NOT_DEFINED;
} else {
msg_len = p_ws->RxMsgLen;
}
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_ws->RxMsgDataPtr = DEF_NULL;
p_ws->OnMsgRxInit(p_conn_const, (HTTPc_WEBSOCK_MSG_TYPE)p_ws->RxMsgOpCode, msg_len, (void **) & (p_ws->RxMsgDataPtr));
}
}
} else { /* If there is no enough data rx to process the hdr. */
DEF_BIT_SET(p_conn->RespFlags, HTTPc_FLAG_RESP_RX_MORE_DATA);
}
break;
case HTTPc_WEBSOCK_RX_STATE_PREPARE:
rx_len = p_ws->RxMsgLenRead + p_conn->RxDataLenRem;
if (rx_len < p_ws->RxMsgLen) { /* Compute to nb of bytes to rx if needed. */
rem_rx_len = p_ws->RxMsgLen - rx_len;
DEF_BIT_SET(p_conn->RespFlags, HTTPc_FLAG_RESP_RX_MORE_DATA);
} else { /* If the msg is already all rxd, process the payload. */
done = DEF_NO;
DEF_BIT_CLR(p_conn->RespFlags, HTTPc_FLAG_RESP_RX_MORE_DATA);
}
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_PAYLOAD;
break;
case HTTPc_WEBSOCK_RX_STATE_PAYLOAD: /* Process the rxd payload data. */
HTTPcWebSock_GetPayload(p_conn);
DEF_BIT_CLR(p_conn->RespFlags, HTTPc_FLAG_RESP_RX_MORE_DATA);
if (p_ws->RxMsgLenRead < p_ws->RxMsgLen) { /* If the msg is not rxed completely. */
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_PREPARE;
/* Continue to process if Ctrl msg. */
if ((p_ws->RxMsgOpCode == HTTPc_WEBSOCK_OPCODE_CLOSE) ||
(p_ws->RxMsgOpCode == HTTPc_WEBSOCK_OPCODE_PING) ||
(p_ws->RxMsgOpCode == HTTPc_WEBSOCK_OPCODE_PONG)) {
done = DEF_NO;
} else { /* Otherwise, save the remaining rx data and exit. */
p_conn->TxBufPtr = p_conn->RxBufPtr + p_conn->RxDataLenRem;
done = DEF_YES;
}
/* If the msg is rxed completely. */
} else if (p_ws->RxMsgLenRead == p_ws->RxMsgLen) {
if (p_ws->Flags.IsRxDataCached == DEF_NO) {
p_conn->TxBufPtr = p_conn->BufPtr; /* Reset TxBufPtr. */
}
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_COMPLETE;
done = DEF_NO;
} else {
err = HTTPc_ERR_WEBSOCK_FAULT;
goto exit_err;
}
break;
case HTTPc_WEBSOCK_RX_STATE_COMPLETE:
switch (p_ws->RxMsgOpCode) {
case HTTPc_WEBSOCK_OPCODE_CONTINUATION_FRAME:
case HTTPc_WEBSOCK_OPCODE_TXT_FRAME:
case HTTPc_WEBSOCK_OPCODE_BIN_FRAME:
if (p_ws->RxMsgFlags.IsFin == DEF_YES) {
p_ws->OrigOpCode = HTTPc_WEBSOCK_OPCODE_NONE;
p_ws->TotalMsgLen = 0u;
/* On Msg Rx Complete Hook. */
if (p_ws->OnMsgRxComplete != DEF_NULL) {
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_ws->OnMsgRxComplete(p_conn_const);
}
}
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_WAIT;
break;
case HTTPc_WEBSOCK_OPCODE_PING: /* Send a Pong Msg. */
if (p_ws->Flags.IsTxMsgCtrlUsed == DEF_NO) {
HTTPcWebSock_SetCtrlResponse(p_conn, HTTPc_WEBSOCK_OPCODE_PONG, p_conn->RxBufPtr, p_ws->RxMsgLen);
p_conn->RxBufPtr += p_ws->RxMsgLen;
p_ws->Flags.IsPongStarted = DEF_YES;
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_WAIT;
}
break;
case HTTPc_WEBSOCK_OPCODE_CLOSE: /* Process the closing handshake */
if (p_ws->RxMsgLen >= sizeof(CPU_INT16U)) {
p_close_code = (CPU_INT16U *)p_conn->RxBufPtr;
p_ws->CloseCode = MEM_VAL_GET_INT16U_BIG(p_close_code);
p_ws->CloseReason.DataPtr = p_conn->RxBufPtr + HTTPc_WEBSOCK_CLOSE_CODE_LEN;
p_ws->CloseReason.Len = p_ws->RxMsgLen - HTTPc_WEBSOCK_CLOSE_CODE_LEN;
} else {
p_ws->CloseCode = HTTPc_WEBSOCK_CLOSE_CODE_NORMAL;
p_ws->CloseReason.DataPtr = DEF_NULL;
p_ws->CloseReason.Len = 0u;
}
/* Check if initiated by the application. */
if (p_ws->Flags.IsCloseStarted == DEF_YES) {
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_CLOSE;
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_WAIT;
p_ws->Flags.IsCloseStarted = DEF_NO;
} else {
/* Otherwise, initiated by the server. */
if (p_ws->Flags.IsTxMsgCtrlUsed == DEF_NO) {
/* Send a close response. */
is_std = HTTPcWebSock_IsStandardCloseCode(p_ws->CloseCode);
if (is_std == DEF_YES) {
HTTPcWebSock_SetCtrlResponse(p_conn, HTTPc_WEBSOCK_OPCODE_CLOSE, p_conn->RxBufPtr, p_ws->RxMsgLen);
p_ws->Flags.IsCloseStarted = DEF_YES;
} else {
err = HTTPc_ERR_WEBSOCK_RX_CLOSE_CODE_INVALID;
goto exit_err;
}
}
p_conn->RxBufPtr += p_ws->RxMsgLen;
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_WAIT;
}
break;
case HTTPc_WEBSOCK_OPCODE_PONG:
/* On Pong Hook. */
if (p_ws->OnPong != DEF_NULL) {
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_ws->OnPong(p_conn_const, p_conn->RxBufPtr, p_ws->RxMsgLen);
}
p_conn->RxBufPtr += p_ws->RxMsgLen;
p_ws->RxState = HTTPc_WEBSOCK_RX_STATE_WAIT;
break;
default:
err = HTTPc_ERR_WEBSOCK_INVALID_OPCODE;
goto exit_err;
}
if ((p_conn->RxDataLenRem == 0u) ||
(p_ws->Flags.IsTxMsgCtrlUsed == DEF_YES)) {
done = DEF_YES;
} else {
done = DEF_NO;
}
break;
default:
err = HTTPc_ERR_WEBSOCK_INVALID_RX_STATE;
goto exit_err;
}
/* --------------------- RX DATA ---------------------- */
result = DEF_BIT_IS_SET(p_conn->RespFlags, HTTPc_FLAG_RESP_RX_MORE_DATA);
if (result == DEF_YES) { /* If more data is needed... */
if (p_ws->Flags.IsRxDataCached == DEF_NO) { /* ...and If there is no rx data cached for ctrl msg... */
sock_rdy_rd = DEF_BIT_IS_SET(p_conn->SockFlags, HTTPc_FLAG_SOCK_RDY_RD);
sock_rdy_err = DEF_BIT_IS_SET(p_conn->SockFlags, HTTPc_FLAG_SOCK_RDY_ERR);
if ((sock_rdy_rd == DEF_YES) || /* ...and if the sock is ready.... */
(sock_rdy_err == DEF_YES)) {
/* ...then Rx data. */
result = HTTPcSock_ConnDataRx(p_conn, rem_rx_len, &err);
switch (err) {
case HTTPc_ERR_NONE:
case HTTPc_ERR_CONN_SOCK_RX:
break;
case HTTPc_ERR_CONN_SOCK_CLOSED:
p_conn->ErrCode = HTTPc_ERR_NONE;
p_conn->CloseStatus = HTTPc_CONN_CLOSE_STATUS_SERVER;
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_CLOSE;
break;
default:
goto exit_err;
}
if (result == DEF_OK) {
if (p_conn->RxDataLenRem == 0u) {
done = DEF_YES;
} else {
done = DEF_NO;
}
} else {
if (p_conn->RxDataLenRem == 0u) {
done = DEF_YES;
}
}
} else {
done = DEF_YES;
}
}
}
} while (done == DEF_NO);
return;
exit_err:
p_conn->ErrCode = err;
p_conn->State = HTTPc_CONN_STATE_WEBSOCK_ERR;
}
/*
*********************************************************************************************************
* HTTPcWebSock_GetMsgHdr()
*
* Description : Process the header of a WebSocket message.
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* p_err Pointer to variable that will receive the return error code from this function :
*
* HTTPc_ERR_NONE
* HTTPc_ERR_WEBSOCK_INVALID_RX_LEN
*
* Return(s) : Remaining number of bytes to parse the hdr.
*
* Caller(s) : HTTPcWebSock_ProcessRXTX().
*
* Note(s) : none.
*********************************************************************************************************
*/
static CPU_INT32U HTTPcWebSock_GetMsgHdr(HTTPc_CONN *p_conn,
HTTPc_ERR *p_err)
{
CPU_SIZE_T len;
CPU_INT08U hdr_1;
CPU_INT08U hdr_2;
CPU_CHAR *p_data;
CPU_INT64U payload;
CPU_INT64U rem_hdr;
HTTPc_WEBSOCK *p_ws;
CPU_BOOLEAN rsvd_bit;
/* ------------------ INITIALIZATION ------------------ */
*p_err = HTTPc_ERR_NONE;
rem_hdr = 0u;
len = p_conn->RxDataLenRem;
p_data = p_conn->RxBufPtr;
p_ws = p_conn->WebSockPtr;
/* ----------------- PROCESS HDR BYTES ---------------- */
if (len < HTTPc_WEBSOCK_MIN_HEADER_LEN) { /* Check if enough data to compute the first parts of...*/
rem_hdr = HTTPc_WEBSOCK_MIN_HEADER_LEN; /* ...the message hdr. */
goto exit;
}
/* Get the 2 first bytes. */
hdr_1 = p_data[0];
hdr_2 = p_data[1];
p_data += 2;
len -= 2;
/* Process msg OPCODE field. */
p_ws->RxMsgOpCode = hdr_1 & HTTPc_WEBSOCK_OPCODE_MASK;
/* Process FIN bit. */
p_ws->RxMsgFlags.IsFin = DEF_BIT_IS_SET(hdr_1, HTTPc_WEBSOCK_FIN_BIT);
/* Process RSVD bits. */
rsvd_bit = DEF_BIT_IS_SET(hdr_1, DEF_BIT_06);
if (rsvd_bit == DEF_YES) {
*p_err = HTTPc_ERR_WEBSOCK_RX_RSVD_BIT_1_SET;
goto exit;
}
rsvd_bit = DEF_BIT_IS_SET(hdr_1, DEF_BIT_05);
if (rsvd_bit == DEF_YES) {
*p_err = HTTPc_ERR_WEBSOCK_RX_RSVD_BIT_2_SET;
goto exit;
}
rsvd_bit = DEF_BIT_IS_SET(hdr_1, DEF_BIT_04);
if (rsvd_bit == DEF_YES) {
*p_err = HTTPc_ERR_WEBSOCK_RX_RSVD_BIT_3_SET;
goto exit;
}
/* Process MASK bit. */
p_ws->RxMsgFlags.IsMasked = DEF_BIT_IS_SET(hdr_2, HTTPc_WEBSOCK_MASK_BIT);
/* ----------------- PROCESS PAYLOAD ------------------ */
payload = hdr_2 & HTTPc_WEBSOCK_PAYLOAD_MASK;
rem_hdr = 0u;
/* According for the 2 first bytes, check the rem... */
/* ...size of the hdr. */
if (p_ws->RxMsgFlags.IsMasked == DEF_YES) {
rem_hdr += sizeof(CPU_INT32U);
}
if (payload == HTTPc_WEBSOCK_NORMAL_FRAME_CODE) {
rem_hdr += sizeof(CPU_INT16U);
} else if (payload == HTTPc_WEBSOCK_LONG_FRAME_CODE) {
rem_hdr += sizeof(CPU_INT64U);
}
/* Check if enough data rxd data. */
if (len < rem_hdr) {
goto exit;
}
/* Get the msg PAYLOAD length. */
if (payload <= HTTPc_WEBSOCK_SMALL_FRAME_MAX_LEN) {
/* Payload length is <= 125 . */
p_ws->RxMsgLen = payload;
} else if (payload == HTTPc_WEBSOCK_NORMAL_FRAME_CODE) {
/* Payload length is > 125 and <= 65535. */
p_ws->RxMsgLen = MEM_VAL_GET_INT16U_BIG(p_data);
p_data += sizeof(CPU_INT16U);
} else if (payload == HTTPc_WEBSOCK_LONG_FRAME_CODE) {
/* Payload length is > 65535. */
payload = MEM_VAL_GET_INT32U_BIG(p_data);
if (payload != 0u) {
*p_err = HTTPc_ERR_WEBSOCK_INVALID_RX_LEN;
goto exit;
}
p_data += sizeof(CPU_INT32U);
payload = MEM_VAL_GET_INT32U_BIG(p_data);
Mem_Copy(&p_ws->RxMsgLen, &payload, sizeof(CPU_INT64U));
p_data += sizeof(CPU_INT32U);
}
/* ------------------ MSG VALIDATION ------------------ */
switch (p_ws->RxMsgOpCode) {
case HTTPc_WEBSOCK_OPCODE_TXT_FRAME:
case HTTPc_WEBSOCK_OPCODE_BIN_FRAME:
/* If not the first frame of a frag msg, must be cont. */
if (p_ws->TotalMsgLen != 0u) {
*p_err = HTTPc_ERR_WEBSOCK_RX_FRAG_FRAME_SEQUENCE_INVALID;
goto exit;
}
p_ws->OrigOpCode = p_ws->RxMsgOpCode;
p_ws->TotalMsgLen = p_ws->RxMsgLen;
break;
case HTTPc_WEBSOCK_OPCODE_CONTINUATION_FRAME:
if (p_ws->TotalMsgLen == 0u) {
*p_err = HTTPc_ERR_WEBSOCK_RX_FRAG_FRAME_SEQUENCE_INVALID;
goto exit;
}
p_ws->TotalMsgLen += p_ws->RxMsgLen;
break;
case HTTPc_WEBSOCK_OPCODE_CLOSE:
case HTTPc_WEBSOCK_OPCODE_PING:
case HTTPc_WEBSOCK_OPCODE_PONG:
/* Control message cannot be longer than 125 bytes. */
if (p_ws->RxMsgLen > HTTPc_WEBSOCK_MAX_CTRL_FRAME_LEN) {
*p_err = HTTPc_ERR_WEBSOCK_RX_CTRL_FRAME_TOO_LONG;
}
/* Control message cannot be fragmented. */
if (p_ws->RxMsgFlags.IsFin == DEF_NO) {
*p_err = HTTPc_ERR_WEBSOCK_RX_CTRL_FRAME_FRAGMENTED;;
}
break;
default:
*p_err = HTTPc_ERR_WEBSOCK_RX_INVALID_OPCODE;
}
/* --------------- PROCESS MASKING KEY ---------------- */
if (p_ws->RxMsgFlags.IsMasked == DEF_YES) {
Mem_Copy(&p_ws->RxMsgMskKey, p_data, sizeof(CPU_INT32U));
p_data += sizeof(CPU_INT32U);
}
/* -------------- UPDATE CONN RX PARAMS --------------- */
p_conn->RxDataLenRem -= (p_data - p_conn->RxBufPtr);
p_conn->RxBufPtr = p_data;
rem_hdr = 0u;
exit:
return rem_hdr;
}
/*
*********************************************************************************************************
* HTTPcWebSock_GetPayload()
*
* Description : Get the payload of a WebSocket message.
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* Return(s) : none.
*
* Caller(s) : HTTPcWebSock_ProcessRX().
*
* Note(s) : none.
*********************************************************************************************************
*/
static void HTTPcWebSock_GetPayload(HTTPc_CONN *p_conn)
{
CPU_CHAR *p_buf;
HTTPc_CONN_OBJ *p_conn_const;
HTTPc_WEBSOCK *p_ws;
CPU_INT32U content_len;
CPU_INT32U msg_len_rem;
CPU_INT32U chunk_len;
HTTPc_WEBSOCK_MSG_TYPE msg_type;
CPU_INT32U data_len_read;
/* ------------------ INTIALIZATION ------------------- */
p_ws = p_conn->WebSockPtr;
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_buf = p_conn->RxBufPtr;
content_len = p_ws->RxMsgLen;
msg_len_rem = p_ws->RxMsgLen - p_conn->RxDataLen;
switch (p_ws->RxMsgOpCode) {
/* -------------------- DATA FRAME -------------------- */
case HTTPc_WEBSOCK_OPCODE_CONTINUATION_FRAME:
case HTTPc_WEBSOCK_OPCODE_TXT_FRAME:
case HTTPc_WEBSOCK_OPCODE_BIN_FRAME:
if (msg_len_rem > p_conn->RxDataLenRem) {
chunk_len = p_conn->RxDataLenRem;
} else {
chunk_len = msg_len_rem;
}
if (p_conn->RxDataLen > content_len) {
return;
}
if (p_ws->RxMsgOpCode == HTTPc_WEBSOCK_OPCODE_CONTINUATION_FRAME) {
msg_type = (HTTPc_WEBSOCK_MSG_TYPE)p_ws->OrigOpCode;
} else {
msg_type = (HTTPc_WEBSOCK_MSG_TYPE)p_ws->RxMsgOpCode;
}
if (p_ws->RxMsgDataPtr != DEF_NULL) {
Mem_Copy(&((p_ws->RxMsgDataPtr)[p_ws->RxMsgLenRead]), p_buf, chunk_len);
data_len_read = chunk_len;
} else if (p_ws->OnMsgRxData != DEF_NULL) {
data_len_read = p_ws->OnMsgRxData(p_conn_const,
msg_type,
p_buf,
chunk_len);
} else {
data_len_read = chunk_len;
}
p_conn->RxDataLen += data_len_read;
p_ws->RxMsgLenRead += data_len_read;
p_conn->RxBufPtr += data_len_read;
p_conn->RxDataLenRem -= data_len_read;
break;
/* -------------------- CTRL FRAME -------------------- */
case HTTPc_WEBSOCK_OPCODE_PONG:
case HTTPc_WEBSOCK_OPCODE_CLOSE:
case HTTPc_WEBSOCK_OPCODE_PING:
if (p_ws->RxMsgLen <= p_conn->RxDataLenRem) {
p_conn->TxBufPtr = p_conn->RxBufPtr + p_conn->RxDataLenRem;
p_conn->RxDataLenRem -= p_ws->RxMsgLen;
p_ws->Flags.IsRxDataCached = DEF_YES;
p_ws->RxMsgLenRead += p_ws->RxMsgLen;
}
break;
default:
break;
}
}
/*
*********************************************************************************************************
* HTTPcWebSock_SetTxBuf()
*
* Description : Mask and copy a message into a buffer.
*
* Argument(s) : p_msg Pointer to the WebSocket message object.
*
* p_dst_buf Pointer to the destination buffer.
*
* p_src_buf Pointer to the source buffer.
*
* len Length of the data to copy.
*
* Return(s) : none.
*
* Caller(s) : HTTPcWebSock_ProcessTX().
*
* Note(s) : none.
*********************************************************************************************************
*/
static void HTTPcWebSock_SetTxBuf(HTTPc_WEBSOCK_MSG *p_msg,
CPU_CHAR *p_dst_buf,
CPU_CHAR *p_src_buf,
CPU_INT32U len)
{
CPU_INT32U msk_idx;
CPU_INT32U msg_idx;
CPU_CHAR msk_byte[4];
if (len == 0u) {
return;
}
msk_byte[3] = (CPU_CHAR)(p_msg->MskKey & 0x000000FF);
msk_byte[2] = (CPU_CHAR)((p_msg->MskKey & 0x0000FF00) >> 8);
msk_byte[1] = (CPU_CHAR)((p_msg->MskKey & 0x00FF0000) >> 16);
msk_byte[0] = (CPU_CHAR)((p_msg->MskKey & 0xFF000000) >> 24);
p_dst_buf += len;
p_src_buf += len;
msg_idx = p_msg->LenSent + len ;
while (p_msg->LenSent < msg_idx) {
msg_idx--;
p_src_buf--;
p_dst_buf--;
msk_idx = msg_idx % sizeof(CPU_INT32U);
*p_dst_buf = *p_src_buf ^ msk_byte[msk_idx];
}
}
/*
*********************************************************************************************************
* HTTPc_WebSock_PrepareTxBufHdr()
*
* Description : Set the message header into the TX buffer..
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* p_tx_msg Pointer to the WebSocket message object to send.
*
* Return(s) : None.
*
* Caller(s) : HTTPcWebSock_ProcessRXTX().
*
* Note(s) : none.
*********************************************************************************************************
*/
static void HTTPcWebSock_SetTxBufHdr(HTTPc_CONN *p_conn,
HTTPc_WEBSOCK_MSG *p_tx_msg,
CPU_CHAR *p_tx_buf)
{
CPU_CHAR *p_hdr;
CPU_INT32U tmp_len;
CPU_INT32U tmp_mask;
CPU_BOOLEAN fin;
/* ------------------ INITIALIZATION ------------------ */
p_hdr = p_tx_buf;
/* Set the first to Byte of the WebSocket hdr. */
fin = p_tx_msg->Flags.IsFin;
*p_hdr = p_tx_msg->OpCode + ((fin == DEF_YES) ? HTTPc_WEBSOCK_FIN_BIT : 0u);
p_hdr++;
/* Set the Masking Key. */
*p_hdr = HTTPc_WEBSOCK_MASK_BIT ;
/* ----------------- SET PAYLOAD LEN ------------------ */
if (p_tx_msg->Len >= HTTPc_WEBSOCK_LONG_FRAME_MIN_LEN) { /* Use the extended 64-bits payload len. */
*p_hdr += HTTPc_WEBSOCK_LONG_FRAME_CODE;
p_hdr++;
Mem_Clr(p_hdr, sizeof(CPU_INT32U)); /* Maximum msg lenght is 32-bit. */
p_hdr += sizeof(CPU_INT32U);
tmp_len = MEM_VAL_GET_INT32U_BIG(&p_tx_msg->Len);
Mem_Copy(p_hdr, &tmp_len, sizeof(CPU_INT32U));
p_hdr += sizeof(CPU_INT32U);
/* Use the extended 16-bits payload len. */
} else if (p_tx_msg->Len >= HTTPc_WEBSOCK_NORMAL_FRAME_MIN_LEN) {
*p_hdr += HTTPc_WEBSOCK_NORMAL_FRAME_CODE;
p_hdr++;
tmp_len = MEM_VAL_GET_INT16U_BIG(&p_tx_msg->Len);
Mem_Copy(p_hdr, &tmp_len, sizeof(CPU_INT16U));
p_hdr += sizeof(CPU_INT16U);
} else { /* Use the 7-bits payload len. */
*p_hdr += p_tx_msg->Len;
p_hdr++;
}
/* ----------------- SET MASKING KEY ------------------ */
tmp_mask = MEM_VAL_GET_INT32U_BIG(&p_tx_msg->MskKey);
Mem_Copy(p_hdr, &tmp_mask, sizeof(CPU_INT32U));
}
/*
*************************************************************************************************
* HTTPcWebSock_SetCtrlResponse()
*
* Description : Set an internal Control message response using cached Rx Data.
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* opcode Type of control frame to send.
*
* Return(s) : none.
*
* Caller(s) : HTTPcWebSock_ProcessRX().
*
* Note(s) : none.
*********************************************************************************************************
*/
static void HTTPcWebSock_SetCtrlResponse(HTTPc_CONN *p_conn,
HTTPc_WEBSOCK_OPCODE opcode,
CPU_CHAR *p_data,
CPU_INT32U msg_len)
{
HTTPc_WEBSOCK *p_ws;
HTTPc_WEBSOCK_MSG *p_msg_tx;
HTTPc_ERR err;
/* ------------------- GET MSG PTR -------------------- */
p_ws = p_conn->WebSockPtr;
p_msg_tx = &p_ws->TxMsgCtrl;
/* --------------- SET TX CTRL MSG OBJ ---------------- */
p_msg_tx->Len = msg_len;
p_msg_tx->LenSent = 0u;
p_msg_tx->DataPtr = p_data;
p_msg_tx->OpCode = opcode;
p_msg_tx->ConnPtr = p_conn;
p_msg_tx->Flags.IsFin = DEF_YES;
p_msg_tx->Flags.IsMasked = DEF_YES;
p_msg_tx->Flags.IsHdrSet = DEF_NO;
p_ws->Flags.IsTxMsgCtrlUsed = DEF_YES;
HTTPcWebSock_TxMsgAdd(p_msg_tx, &err);
(void)err;
}
/*
*********************************************************************************************************
* HTTPcWebSock_TxMsgRemove()
*
* Description : Remove the first message in the TX message queue.
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* Return(s) : none.
*
* Caller(s) : HTTPcWebSock_TX(),
* HTTPcWebSock_TxMsgCleanList().
*
* Note(s) : none.
*********************************************************************************************************
*/
static void HTTPcWebSock_TxMsgRemove(HTTPc_CONN *p_conn)
{
HTTPc_WEBSOCK *p_ws;
HTTPc_WEBSOCK_MSG *p_msg_item;
p_ws = p_conn->WebSockPtr;
p_msg_item = p_ws->TxMsgListHeadPtr;
if (p_msg_item == DEF_NULL) {
p_ws->TxMsgListEndPtr = DEF_NULL;
} else if (p_msg_item->NextPtr == DEF_NULL) {
p_ws->TxMsgListHeadPtr = DEF_NULL;
p_ws->TxMsgListEndPtr = DEF_NULL;
p_msg_item->NextPtr = 0;
} else {
p_ws->TxMsgListHeadPtr = p_msg_item->NextPtr;
}
if (p_msg_item != DEF_NULL) {
p_msg_item->Flags.IsUsed = DEF_NO;
}
}
/*
*********************************************************************************************************
* HTTPcWebSock_TxMsgCleanList()
*
* Description : Remove all the pending WebSocket message.
*
* Argument(s) : p_conn Pointer to current HTTPc Connection (WebSocket Upgraded).
*
* Return(s) : none.
*
* Caller(s) : HTTPcConn_WebSocketProcess().
*
* Note(s) : none.
*********************************************************************************************************
*/
static void HTTPcWebSock_TxMsgCleanList(HTTPc_CONN *p_conn)
{
HTTPc_WEBSOCK *p_ws;
HTTPc_CONN_OBJ *p_conn_const;
HTTPc_WEBSOCK_MSG *p_msg;
HTTPc_WEBSOCK_MSG_OBJ *p_msg_const;
p_ws = p_conn->WebSockPtr;
while (p_ws->TxMsgListHeadPtr != DEF_NULL) {
p_msg = (HTTPc_WEBSOCK_MSG *)p_ws->TxMsgListHeadPtr;
/* Call On Msg Tx Complete Callback if set. */
if (p_msg->OnMsgTxComplete != DEF_NULL) {
p_conn_const = (HTTPc_CONN_OBJ *)p_conn;
p_msg_const = (HTTPc_WEBSOCK_MSG_OBJ *)p_ws->TxMsgListHeadPtr;
p_msg->OnMsgTxComplete(p_conn_const, p_msg_const, DEF_FAIL);
}
HTTPcWebSock_TxMsgRemove(p_conn);
}
}
/*
*********************************************************************************************************
* HTTPcWebSock_ComputeHdrLen()
*
* Description : Compute the header length of a WebSocket Msg.
*
* Argument(s) : p_msg Pointer to a WebSocket Msg obj.
*
* Return(s) : Length of the header.
*
* Caller(s) : HTTPcWebSock_TX().
*
* Note(s) : none.
*********************************************************************************************************
*/
static CPU_INT16U HTTPcWebSock_ComputeHdrLen(HTTPc_WEBSOCK_MSG *p_msg)
{
CPU_INT16U hdr_len ;
hdr_len = 2; /* Minimum Header Len. */
if (p_msg->Len >= HTTPc_WEBSOCK_LONG_FRAME_MIN_LEN) {
/* Use the extended 64-bits payload len. */
hdr_len += sizeof(CPU_INT64U);
} else if (p_msg->Len >= HTTPc_WEBSOCK_NORMAL_FRAME_MIN_LEN) {
/* Use the extended 16-bits payload len. */
hdr_len += sizeof(CPU_INT16U);
}
if (p_msg->Flags.IsMasked == DEF_YES) {
hdr_len += sizeof(CPU_INT32U);
}
return (hdr_len);
}
/*
*********************************************************************************************************
* HTTPcWebSock_IsProtocolError()
*
* Description : Check if the error code is related to a WebSocket protocol error.
*
* Argument(s) : err Error code to check.
*
* Return(s) : DEF_YES, if the error code is a protocol error.
* DEF_NO, if the error code is NOT a protocol error.
*
* Caller(s) : HTTPcWebSock_Process().
*
* Note(s) : none.
*********************************************************************************************************
*/
static CPU_BOOLEAN HTTPcWebSock_IsProtocolError(HTTPc_ERR err)
{
switch (err) {
case HTTPc_ERR_WEBSOCK_RX_RSVD_BIT_1_SET:
case HTTPc_ERR_WEBSOCK_RX_RSVD_BIT_2_SET:
case HTTPc_ERR_WEBSOCK_RX_RSVD_BIT_3_SET:
case HTTPc_ERR_WEBSOCK_RX_CTRL_FRAME_TOO_LONG:
case HTTPc_ERR_WEBSOCK_RX_CTRL_FRAME_FRAGMENTED:
case HTTPc_ERR_WEBSOCK_RX_INVALID_OPCODE:
case HTTPc_ERR_WEBSOCK_RX_FRAG_FRAME_SEQUENCE_INVALID:
case HTTPc_ERR_WEBSOCK_RX_CLOSE_CODE_INVALID:
return DEF_YES;
default:
return DEF_NO;
}
}
/*
*********************************************************************************************************
* HTTPcWebSock_IsStandardCloseCode()
*
* Description : Check if the Close code value is known and standarized.
*
* Argument(s) : close_code Close code value to check.
*
* Return(s) : DEF_YES, if the close code is standard.
* DEF_NO, if the close code is NOT standard.
*
* Caller(s) : HTTPcWebSock_RX().
*
* Note(s) : none.
*********************************************************************************************************
*/
static CPU_BOOLEAN HTTPcWebSock_IsStandardCloseCode(HTTPc_WEBSOCK_CLOSE_CODE close_code)
{
switch (close_code) {
case HTTPc_WEBSOCK_CLOSE_CODE_NORMAL:
case HTTPc_WEBSOCK_CLOSE_CODE_GOING_AWAY:
case HTTPc_WEBSOCK_CLOSE_CODE_PROTOCOL_ERR:
case HTTPc_WEBSOCK_CLOSE_CODE_DATA_TYPE_NOT_ALLOWED:
case HTTPc_WEBSOCK_CLOSE_CODE_DATA_TYPE_ERR:
case HTTPc_WEBSOCK_CLOSE_CODE_POLICY_VIOLATION:
case HTTPc_WEBSOCK_CLOSE_CODE_MSG_TOO_BIG:
case HTTPc_WEBSOCK_CLOSE_CODE_INVALID_EXT:
case HTTPc_WEBSOCK_CLOSE_CODE_UNEXPECTED_CONDITION:
return DEF_YES;
default:
break;
}
if ((close_code >= 3000) && (close_code <= 4999)) {
return DEF_YES;
}
return DEF_NO;
}
/*
*********************************************************************************************************
*********************************************************************************************************
* MODULE END
*********************************************************************************************************
*********************************************************************************************************
*/
#endif /* HTTPc_WEBSOCK_MODULE_EN */