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.
WS_SERVER.cpp
- Committer:
- aktk
- Date:
- 2018-03-03
- Revision:
- 2:ccaae77f91b8
- Parent:
- 1:73f2f67d1732
File content as of revision 2:ccaae77f91b8:
#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 } template<typename T1, typename T2, typename T3> void DEBUG_PRINT_LINE(const char* arg_line, T1 arg_t1, T2 arg_t2, T3 arg_t3) { #ifdef DEBUG printf("(WSS) "); printf(arg_line, arg_t1, arg_t2, arg_t3); printf("\r\n"); #endif } } using namespace WS_SERVER; WSS::WSS() :m_RxBufferSize(1024) ,m_PortNumber(8085) { m_ListeningFlag = (false); m_RxBuffer = new uint8_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 uint8_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"); if(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: %2x (%3d)", i, m_RxBuffer[i], m_RxBuffer[i]); // // Data Parsing // l_MesuredDataLen++; m_FIN_RSV = m_RxBuffer[0] >> 4; //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; } DEBUG_PRINT_LINE("Mask : %x", m_Opcode); m_PayloadLen = (uint64_t)(m_RxBuffer[1] & 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; DEBUG_PRINT_LINE("PL Len : %x", (uint32_t)m_PayloadLen); } 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; DEBUG_PRINT_LINE("PL Len : %x", (uint32_t)m_PayloadLen); } 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("PL Len : %x%x", (uint32_t)(m_PayloadLen>>32),(uint32_t)(m_PayloadLen&0xffffffff)); } DEBUG_PRINT_LINE("Data parsing ... 2/3"); // 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: %08x%08x Byte", (uint32_t)(m_ApplicationLen>>32), (uint32_t)(m_ApplicationLen &0xffffffff)); 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; }