The library with which to configure a Web Socket Server on a Mbed. This lib was coded by a day at least one year before when this description is written. It will be updated adopting mbed os 5.
Diff: WS_SERVER.cpp
- Revision:
- 0:86a479dd1814
- Child:
- 1:73f2f67d1732
diff -r 000000000000 -r 86a479dd1814 WS_SERVER.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/WS_SERVER.cpp Thu Dec 08 10:11:25 2016 +0000 @@ -0,0 +1,429 @@ +#include "WS_SERVER.h" +#define DEBUG + +//Serial wspc(USBTX, USBRX); +namespace WS_SERVER +{ +void DEBUG_PRINT_LINE(const char* arg_line) +{ +#ifdef DEBUG + printf("(WSS) "); + printf(arg_line); + printf("\r\n"); +#endif +} +template<typename T> +void DEBUG_PRINT_LINE(const char* arg_line, T arg_t) +{ +#ifdef DEBUG + printf("(WSS) "); + printf(arg_line, arg_t); + printf("\r\n"); +#endif +} +template<typename T1, typename T2> +void DEBUG_PRINT_LINE(const char* arg_line, T1 arg_t1, T2 arg_t2) +{ +#ifdef DEBUG + printf("(WSS) "); + printf(arg_line, arg_t1, arg_t2); + printf("\r\n"); +#endif +} +} +using namespace WS_SERVER; + +WSS::WSS() + :m_RxBufferSize(1024) + ,m_PortNumber(8085) +{ + m_ListeningFlag = (false); + m_RxBuffer = new int8_t[1024]; + m_restAppDataLen = 0; + + m_DiscontiuanceFlag = false; + m_EODFlag = false; +} + +WSS::WSS(uint32_t arg_RxBufferSize, uint32_t arg_PortNumber) + :m_RxBufferSize(arg_RxBufferSize) + ,m_PortNumber(arg_PortNumber) +{ + m_ListeningFlag = (false); + m_RxBuffer = new int8_t[arg_RxBufferSize]; + m_restAppDataLen = 0; + + m_DiscontiuanceFlag = false; + m_EODFlag = false; +} + +WSS::~WSS() +{ + delete(m_RxBuffer); +} + +bool WSS::init() +{ + DEBUG_PRINT_LINE("DEBUG MODE"); + + // Ethernet Initialization + if(m_eth.init()) { + printf("(WSS) Error!@EthernetInterface::init()\r\n"); + return false; + } + // Ethernet Connecting setup + if(m_eth.connect()) { + printf("(WSS) Error!@EthernetInterface::connect()\r\n"); + return false; + } else { + printf("(WSS) IP Address is %s\r\n", m_eth.getIPAddress()); + } + + + // TCP Socket setup + // To open Server-side PORT + if(m_tcpsvr.bind(m_PortNumber)< 0) { + printf("(WSS) Error!@TCPSocketServer::bind()\r\n"); + return false; + } else { + printf("(WSS) TCP Server has bounden!\r\n"); + } + // Server start listening Request from a web browser. + if(m_tcpsvr.listen(1) < 0) { + printf("(WSS) TCP server listen failed.\r\n"); + return false; + } else { + m_ListeningFlag = true; + printf("(WSS) TCP server is listening...\r\n"); + } + + return true; +} + +bool WSS::isListening() +{ + return m_ListeningFlag; +} + +void WSS::run() +{ + DigitalOut led3(LED3); + DigitalOut led4(LED4); + + while (m_ListeningFlag) { + led3 = true; + // blocking mode (never timeout) + // waiting client connection + printf("(WSS) Waiting Connection\r\n"); + if(m_tcpsvr.accept(m_tcpcon) < 0) { + printf("(WSS) Failed to accept connection.\r\n"); + } else { + printf("(WSS) Connection Success!\r\n"); + printf("(WSS) IP: %s\r\n",m_tcpcon.get_address()); + + led4 = true; + } + // When conected + // Main WSS Communication---------------------------------- + while(m_tcpcon.is_connected()) { + DEBUG_PRINT_LINE("(WSS) Connected"); + handshake(); + while(!m_DiscontiuanceFlag)rxMessage(); + DEBUG_PRINT_LINE("(WSS) Closing"); + // txClosing(1002, "RecievedNot Masked Data"); + } + // ^^^^^^^^^^^^^^^^^^^^^^---------------------------------- + printf("(WSS) Close connection.\r\ntcp server is listening...\r\n"); + m_tcpcon.close(); + led4 = false; + + // When any critical errors are occured. + //if(/*anyerror*/) m_ListeningFlag = false; + } + m_tcpsvr.close(); + led3 = false; + while(1); +} + +bool WSS::isReadable() +{ + if(m_restAppDataLen > 0) + return true; + else + return false; +} + +int8_t WSS::getRxData() +{ + if(m_restAppDataLen > 0) { + int l_index = m_ApplicationLen - m_restAppDataLen; + m_restAppDataLen--; + return m_PayloadData[l_index] ^ m_Masking_key[l_index % 4]; + } else + return 0x00; +} + +void WSS::discardRxBuffer() +{ + m_RxBuffer = NULL; + m_restAppDataLen = 0; +} + +void WSS::txPing() +{ + DEBUG_PRINT_LINE("Ping Pong Debug"); +} +int8_t WSS::rxMessage() +{ + int64_t l_RxDataLen = 0; + int64_t l_MesuredDataLen = 0; + + if(isReadable()) return -1; + // + // A (part of) data is recieved here. + // + m_DiscontiuanceFlag = false; + switch(l_RxDataLen = m_tcpcon.receive((char*)m_RxBuffer, m_RxBufferSize)) { + case 0: + DEBUG_PRINT_LINE("Recieved buffer is empty."); + m_DiscontiuanceFlag = true; + break; + case -1: + DEBUG_PRINT_LINE("Failed to read data from client."); + m_DiscontiuanceFlag = true; + break; + default: + DEBUG_PRINT_LINE("Massege Has Received"); + break; + } + DEBUG_PRINT_LINE("Data size: %d Bytes", strlen((char*) m_RxBuffer)); + for(int i = 0; i < strlen((char*) m_RxBuffer); i++) + DEBUG_PRINT_LINE("Data%5d: %x", i, m_RxBuffer[i]); + // + // Data Parsing + // + l_MesuredDataLen++; + m_FIN_RSV = m_RxBuffer[0] >> 4; + DEBUG_PRINT_LINE("FIN RSV: %x", m_FIN_RSV); + //If FIN is 1, the data is the last of a sequence. + if(m_FIN_RSV >> 3) + m_EODFlag = true; + //IF RSV1, 2, or 3 is 1, communication should be discontinued, because there is no negotiation. + if(m_FIN_RSV & 0x07) + m_DiscontiuanceFlag = true; + m_Opcode = m_RxBuffer[0] & 0x0F; + switch((OpCode)m_Opcode) { + case cont: + case txt: + case bin: + case close: + case ping: + case pong: { + m_FrameOpcode = (OpCode)m_Opcode; + break; + } + default: { + m_DiscontiuanceFlag = true; + break; + } + } + DEBUG_PRINT_LINE("Data parsing ... 1/3"); + DEBUG_PRINT_LINE("FIN RSV: %x", m_FIN_RSV); + DEBUG_PRINT_LINE("Opcode : %x", m_Opcode); + l_MesuredDataLen++; + // サーバは、マスクされていないフレームを受信した際には,接続を close しなければならない。 + // この場合,サーバは、 7.4.1 節 の定義に従って,ステータスコード 1002 (プロトコルエラー)を伴う Close フレームを送信してもよい。 + m_Mask = m_RxBuffer[1] >> 7; + if(m_Mask == 0) { + m_DiscontiuanceFlag = true; + } + m_PayloadLen = m_RxBuffer[1] & (uint64_t)0x7f; + if(m_PayloadLen < 0x07e) { + l_MesuredDataLen += m_PayloadLen; + // Buffer [2] [3] [4] [5] have Masking_Key + l_MesuredDataLen += 4; + if(m_Mask) for(int i = 0; i < 4; i++) m_Masking_key[i] = (m_RxBuffer[2 + i]); + // The Head of Payload data is at buffer[6] + m_PayloadData = m_RxBuffer + 6; + } else if (m_PayloadLen == 0x07e) { + l_MesuredDataLen += m_PayloadLen; + // Two succeeded bytes buffer [2] and [3] have SubPayload + l_MesuredDataLen += 2; + m_PayloadLen = (uint64_t)((m_RxBuffer[2] << 8)|(m_RxBuffer[3]&0xff)); + // Buffer [4] [5] [6] [7] have Masking_Key + l_MesuredDataLen += 4; + if(m_Mask) for(int i = 0; i < 4; i++) m_Masking_key[i] = (m_RxBuffer[4 + i]); + // The Head of Payload data is at buffer[8] + m_PayloadData = m_RxBuffer + 8; + } else if (m_PayloadLen == 0x07f) { + l_MesuredDataLen += m_PayloadLen; + // Eight succeeded bytes buffer [2], [3], [4], [5], [6], [7], [8], and [9] have SubPayload + l_MesuredDataLen += 8; + m_PayloadLen = (uint64_t)(m_RxBuffer[2] & 0xff); + for(int i = 0; i < 7; i++) + m_PayloadLen = (uint64_t)((m_PayloadLen << 8) | (m_RxBuffer[3 + i] & 0xff)); + l_MesuredDataLen += 4; + // Buffer [10] [11] [12] [13] have Masking_Key + if(m_Mask) for(int i = 0; i < 4; i++) m_Masking_key[i] = (m_RxBuffer[10 + i]); + // The Head of Payload data is at buffer[14] + m_PayloadData = m_RxBuffer + 14; + } + DEBUG_PRINT_LINE("Data parsing ... 2/3"); + DEBUG_PRINT_LINE("Mask : %x", m_Opcode); + DEBUG_PRINT_LINE("PL Len : %x", m_PayloadLen); + + // Because there is no negotiation of Extention, Extention data is regarded as none. + m_ExtensionLen = 0; + m_ApplicationLen = m_PayloadLen - m_ExtensionLen; + m_restAppDataLen = m_ApplicationLen; + DEBUG_PRINT_LINE("Data parsing ... 3/3"); + DEBUG_PRINT_LINE("Application Data Len: %d byte"); + + if(l_MesuredDataLen != l_RxDataLen) { + m_DiscontiuanceFlag = true; + } + + return m_DiscontiuanceFlag ? 1 : 0; +} + +bool WSS::handshake() +{ + char* httpmethod = NULL; + char* filepath = NULL; + char* http_ver = NULL; + char* header_field_name = NULL; + char* header_field_val = NULL; + + // + // Handshake HTTP Request Analysis + // + switch(m_tcpcon.receive((char*)m_RxBuffer, 1023)) { + case 0: + DEBUG_PRINT_LINE("Recieved buffer is empty."); + m_msger.setStatusLine(400, "No Request"); + httpmethod = NULL; + filepath = NULL; + http_ver = NULL; + break; + case -1: + DEBUG_PRINT_LINE("Failed to read data from client."); + m_msger.setStatusLine(500, "Internal Server Error"); + httpmethod = NULL; + filepath = NULL; + http_ver = NULL; + break; + default: + DEBUG_PRINT_LINE("Recieved Data: %d\r\n-->",strlen((char*)m_RxBuffer)); + DEBUG_PRINT_LINE("%.*s[End of Request]", strlen((char*)m_RxBuffer),(char*)m_RxBuffer); + // get HTTP method, File path, HTTP version + httpmethod = strtok((char*)m_RxBuffer," "); + filepath = strtok(NULL, " "); + http_ver = strtok(NULL, "\r\n"); + DEBUG_PRINT_LINE("httpmethod: %s", httpmethod); + DEBUG_PRINT_LINE("file path: %s", filepath); + DEBUG_PRINT_LINE("http ver : %s", http_ver); + break; + } + + // + // Handshake Response Form + // + if (strcmp(httpmethod,"GET") == 0 ) { + DEBUG_PRINT_LINE("GET request incomming."); + // Define behaviour of server according to Request Header lines + // Apply request header field to response header field + char* field_Connection = NULL; + char* field_Upgrade = NULL; + char* field_Sec_WebSocket_Key = NULL; + char* field_Sec_WebSocket_Version = NULL; + char* field_Origin = NULL; + do { + //Analyze the header feilds + header_field_name = strtok(NULL, ":"); + header_field_name++; + header_field_val = strtok(NULL, "\r\n"); + header_field_val++; + + if(header_field_name - 1 != NULL) { + if(!strcmp(header_field_name, "Connection")) { + field_Connection = header_field_val; + } else if(!strcmp(header_field_name, "Upgrade")) { + field_Upgrade = header_field_val; + } else if(!strcmp(header_field_name, "Sec-WebSocket-Key") ) { + field_Sec_WebSocket_Key = header_field_val; + } else if(!strcmp(header_field_name, "Sec-WebSocket-Version") ) { + field_Sec_WebSocket_Version = header_field_val; + } else if(!strcmp(header_field_name, "Origin") ) { + field_Origin = header_field_val; + } + DEBUG_PRINT_LINE("*header_field_name adr: %d %s", header_field_name - 1, header_field_name); + DEBUG_PRINT_LINE(" header_field_val adr: %d %s", header_field_val - 1, header_field_val); + } else { + break; + } + } while(1); + + DEBUG_PRINT_LINE("Parsing has been done"); + // if the request is to switching to the WebSocket Server + if(!strcmp((char*)field_Connection, "Upgrade")) { + if( !strcmp(field_Upgrade, "websocket") && + field_Sec_WebSocket_Key != NULL && + !strcmp(field_Sec_WebSocket_Version, "13") && + field_Origin != NULL) { + DEBUG_PRINT_LINE("Prepering for Update"); + // + // Calcurate the key + // + // concat the input key and the str. + unsigned char SecWebSocketAccept[256]; + sprintf((char*)SecWebSocketAccept, "%s%s", field_Sec_WebSocket_Key, "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); + DEBUG_PRINT_LINE("Appended Key: %s", (char*)SecWebSocketAccept); + // evaluate SHA1 + unsigned char sha1Output[20]; + sha1(SecWebSocketAccept, strlen((char*)SecWebSocketAccept), sha1Output); + DEBUG_PRINT_LINE("SHA1 hashed Key:"); + for(int i = 0; i < 20; i++) DEBUG_PRINT_LINE("%x", sha1Output[i]); + // encode the hash into base 64 + { + size_t sizeofbuffer = strlen((char*)SecWebSocketAccept); + if(base64_encode(SecWebSocketAccept, &sizeofbuffer, sha1Output, 20) != 0)DEBUG_PRINT_LINE("something wrong@base64_encode"); + DEBUG_PRINT_LINE((char*)SecWebSocketAccept); + } + // --- end of culculation of the key --- + // + // Format HTTP response of ACK + // + DEBUG_PRINT_LINE("Communication Protocol will be Upgraded to Websocket!"); + m_msger.resetHeader(); + m_msger.setStatusLine(101, "Switching Protocols"); + if(m_msger.setHeaderField("Connection", "Upgrade")) DEBUG_PRINT_LINE("buffer over flow@ResposeMessenger"); + if(m_msger.setHeaderField("Upgrade", "websocket")) DEBUG_PRINT_LINE("buffer over flow@ResposeMessenger"); + if(m_msger.setHeaderField("Sec-WebSocket-Accept", (char*)SecWebSocketAccept)) DEBUG_PRINT_LINE("buffer over flow@ResposeMessenger"); + if(m_msger.setHeaderField("Access-Control-Allow-Origin", m_tcpcon.get_address())) DEBUG_PRINT_LINE("buffer over flow@ResposeMessenger"); + // --- end of formatting --- + } else { + // + // Format HTTP Response of NACK + // + DEBUG_PRINT_LINE("Communication Protocol won't be Upgraded."); + m_msger.setStatusLine(426, "Upgrade Required"); + if(m_msger.setHeaderField("Connection", "Close"))DEBUG_PRINT_LINE("buffer over flow @ ResponseMessenger"); + // --- end of formatting --- + } + } + // send valid response + m_msger.sendHTTPResponse(m_tcpcon); + m_msger.resetHeader(); + DEBUG_PRINT_LINE("echo back done."); + } + if (httpmethod == NULL) { + // send error response + m_msger.sendHTTPResponse(m_tcpcon); + m_msger.resetHeader(); + DEBUG_PRINT_LINE("echo back done."); + } + DEBUG_PRINT_LINE("Response to Request has done"); + // ----Handshake Response End---- + + return true; +} \ No newline at end of file