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

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;
}