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.

Dependencies:   mbedTLSLibrary

Dependents:   SIMPLE_WSS

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