#include "WebsocketHandler.hpp" namespace httpsserver { /** * @brief Dump the content of the WebSocket frame for debugging. * @param [in] frame The frame to dump. */ static void dumpFrame(WebsocketFrame frame) { std::string opcode = std::string("Unknown"); switch(frame.opCode) { case WebsocketHandler::OPCODE_BINARY: opcode = std::string("BINARY"); break; case WebsocketHandler::OPCODE_CONTINUE: opcode = std::string("CONTINUE"); break; case WebsocketHandler::OPCODE_CLOSE: opcode = std::string("CLOSE"); break; case WebsocketHandler::OPCODE_PING: opcode = std::string("PING"); break; case WebsocketHandler::OPCODE_PONG: opcode = std::string("PONG"); break; case WebsocketHandler::OPCODE_TEXT: opcode = std::string("TEXT"); break; } ESP_LOGI( TAG, "Fin: %d, OpCode: %d (%s), Mask: %d, Len: %d", (int)frame.fin, (int)frame.opCode, opcode.c_str(), (int)frame.mask, (int)frame.len ); } WebsocketHandler::WebsocketHandler() { _con = nullptr; _receivedClose = false; _sentClose = false; } WebsocketHandler::~WebsocketHandler() { } // ~WebSocketHandler() /** * @brief The default onClose handler. * If no over-riding handler is provided for the "close" event, this method is called. */ void WebsocketHandler::onClose() { HTTPS_LOGD("WebsocketHandler close()"); } /** * @brief The default onData handler. * If no over-riding handler is provided for the "message" event, this method is called. * A particularly useful pattern for using onMessage is: * ``` * std::stringstream buffer; * buffer << pWebSocketInputRecordStreambuf; * ``` * This will read the whole message into the string stream. */ void WebsocketHandler::onMessage(WebsocketInputStreambuf* pWebsocketInputStreambuf) { //, Websocket *pWebSocket) { HTTPS_LOGD("WebsocketHandler onMessage()"); } /** * @brief The default onError handler. * If no over-riding handler is provided for the "error" event, this method is called. */ void WebsocketHandler::onError(std::string error) { HTTPS_LOGD("WebsocketHandler onError()"); } void WebsocketHandler::initialize(ConnectionContext * con) { _con = con; } void WebsocketHandler::loop() { if(read() < 0) { close(); } } int WebsocketHandler::read() { WebsocketFrame frame; int length = _con->readBuffer((uint8_t*)&frame, sizeof(frame)); HTTPS_LOGD("Websocket: Read %d bytes", length); if(length == 0) return 0; else if (length != sizeof(frame)) { HTTPS_LOGE("Websocket read error"); //_con->closeConnection(); return -1; } dumpFrame(frame); // The following section parses the WebSocket frame. uint32_t payloadLen = 0; uint8_t mask[4]; if (frame.len < 126) { payloadLen = frame.len; } else if (frame.len == 126) { uint16_t tempLen; _con->readBuffer((uint8_t*)&tempLen, sizeof(tempLen)); payloadLen = ntohs(tempLen); } else if (frame.len == 127) { uint64_t tempLen; _con->readBuffer((uint8_t*)&tempLen, sizeof(tempLen)); payloadLen = ntohl((uint32_t)tempLen); } if (frame.mask == 1) { _con->readBuffer(mask, sizeof(mask)); } if (payloadLen == 0) { HTTPS_LOGW("WS payload not present"); } else { HTTPS_LOGI("WS payload: length=%d", payloadLen); } switch(frame.opCode) { case OPCODE_TEXT: case OPCODE_BINARY: { HTTPS_LOGD("Creating Streambuf"); WebsocketInputStreambuf streambuf(_con, payloadLen, frame.mask==1?mask:nullptr); HTTPS_LOGD("Calling onMessage"); onMessage(&streambuf); HTTPS_LOGD("Discarding Streambuf"); streambuf.discard(); break; } case OPCODE_CLOSE: { // If the WebSocket operation code is close then we are closing the connection. _receivedClose = true; onClose(); //close(); // Close the websocket. return -1; break; } case OPCODE_CONTINUE: { break; } case OPCODE_PING: { break; } case OPCODE_PONG: { break; } default: { HTTPS_LOGW("WebSocketReader: Unknown opcode: %d", frame.opCode); break; } } // Switch opCode return 0; } // Websocket::read /** * @brief Close the Web socket * @param [in] status The code passed in the close request. * @param [in] message A clarification message on the close request. */ void WebsocketHandler::close(uint16_t status, std::string message) { HTTPS_LOGD("Websocket close()"); _sentClose = true; // Flag that we have sent a close request. WebsocketFrame frame; // Build the web socket frame indicating a close request. frame.fin = 1; frame.rsv1 = 0; frame.rsv2 = 0; frame.rsv3 = 0; frame.opCode = OPCODE_CLOSE; frame.mask = 0; frame.len = message.length() + 2; int rc = _con->writeBuffer((uint8_t *)&frame, sizeof(frame)); if (rc > 0) { rc = _con->writeBuffer((byte *) &status, 2); } if (rc > 0) { _con->writeBuffer((byte *) message.data(), message.length()); } } // Websocket::close /** * @brief Send data down the web socket * See the WebSocket spec (RFC6455) section "6.1 Sending Data". * We build a WebSocket frame, send the frame followed by the data. * @param [in] data The data to send down the WebSocket. * @param [in] sendType The type of payload. Either SEND_TYPE_TEXT or SEND_TYPE_BINARY. */ void WebsocketHandler::send(std::string data, uint8_t sendType) { HTTPS_LOGD(">> Websocket.send(): length=%d", data.length()); WebsocketFrame frame; frame.fin = 1; frame.rsv1 = 0; frame.rsv2 = 0; frame.rsv3 = 0; frame.opCode = sendType==SEND_TYPE_TEXT?OPCODE_TEXT:OPCODE_BINARY; frame.mask = 0; if (data.length() < 126) { frame.len = data.length(); _con->writeBuffer((uint8_t *)&frame, sizeof(frame)); } else { frame.len = 126; _con->writeBuffer((uint8_t *)&frame, sizeof(frame)); uint16_t net_len = htons((uint16_t)data.length()); _con->writeBuffer((uint8_t *)&net_len, sizeof(uint16_t)); // Convert to network byte order from host byte order } _con->writeBuffer((uint8_t*)data.data(), data.length()); HTTPS_LOGD("<< Websocket.send()"); } // Websocket::send /** * @brief Send data down the web socket * See the WebSocket spec (RFC6455) section "6.1 Sending Data". * We build a WebSocket frame, send the frame followed by the data. * @param [in] data The data to send down the WebSocket. * @param [in] sendType The type of payload. Either SEND_TYPE_TEXT or SEND_TYPE_BINARY. */ void WebsocketHandler::send(uint8_t* data, uint16_t length, uint8_t sendType) { HTTPS_LOGD(">> Websocket.send(): length=%d", length); WebsocketFrame frame; frame.fin = 1; frame.rsv1 = 0; frame.rsv2 = 0; frame.rsv3 = 0; frame.opCode = sendType==SEND_TYPE_TEXT?OPCODE_TEXT:OPCODE_BINARY; frame.mask = 0; if (length < 126) { frame.len = length; _con->writeBuffer((uint8_t *)&frame, sizeof(frame)); } else { frame.len = 126; _con->writeBuffer((uint8_t *)&frame, sizeof(frame)); uint16_t net_len = htons(length); _con->writeBuffer((uint8_t *)&net_len, sizeof(uint16_t)); // Convert to network byte order from host byte order } _con->writeBuffer(data, length); HTTPS_LOGD("<< Websocket.send()"); } // Websocket::send /** * Returns true if the connection has been closed, either by client or server */ bool WebsocketHandler::closed() { return _receivedClose || _sentClose; } }