Arianna autonomous DAQ firmware

Dependencies:   mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW

Websocket.cpp

Committer:
uci1
Date:
2019-06-05
Revision:
125:ce4045184366
Parent:
6:6f002d202f59

File content as of revision 125:ce4045184366:

#ifdef IGNORE_THIS_FILE
#include "Websocket.h"
#include <string>

#include "SnBase64.h"

//#define DEBUG

const bool Websocket::kUseB64 = true;

#ifdef TARGET_LPC1768
Websocket::Websocket(char * url) {
    server_ip = NULL;
    netif = ETH;
    eth_writeable = false;
    eth_readable = false;
    eth_connected = false;
    response_server_eth = false;
    new_msg = false;
    fillFields(url);
    
    memset(eth_rx, 0, RX_BUF_SIZE);
    
    eth = new EthernetNetIf(
        IpAddr(128,195,204,148), //IP Address
        IpAddr(255,255,255,0), //Network Mask
        IpAddr(128,195,204,1), //Gateway
        IpAddr(128,200,1,201)  //DNS   
    );
    sock = new TCPSocket();

    EthernetErr ethErr = eth->setup();
#ifdef DEBUG
    if (ethErr) {
        printf("\r\nERROR %d in setup.\r\n", ethErr);
    }
#endif

    //we must use dnsresolver to find the ip address
    if (server_ip == NULL) {
        DNSResolver dr;
        server_ip = new IpAddr();
        *server_ip = dr.resolveName(ip_domain.c_str());
#ifdef DEBUG
        printf("\r\nserver with dns=%d.%d.%d.%d\r\n", (*server_ip)[0], (*server_ip)[1], (*server_ip)[2], (*server_ip)[3]);
#endif

    }

    IpAddr ipt = eth->getIp();
#ifdef DEBUG
    printf("\r\nmbed IP Address is %d.%d.%d.%d\r\n", ipt[0], ipt[1], ipt[2], ipt[3]);
#endif

    sock->setOnEvent(this, &Websocket::onTCPSocketEvent);
}
#endif //target


void Websocket::fillFields(char * url) 
{
#ifdef DEBUG
    printf("FILLFIELDS\r\n");
#endif
    char *res = NULL;
    char *res1 = NULL;

    char buf[50];
    strcpy(buf, url);
    printf("\r\nBuf is:");
    for(int i=0;i<50;i++)
    {
        printf("%d", buf[i]);
    }
    printf("\r\n");
    res = strtok(buf, ":");
    if (strcmp(res, "ws")) 
    {
#ifdef DEBUG
        printf("\r\nFormat error: please use: \"ws://ip-or-domain[:port]/path\"\r\n\r\n");
#endif
    } 
    else 
    {
        //ip_domain and port
        res = strtok(NULL, "/");

        //path
        res1 = strtok(NULL, " ");
        if (res1 != NULL) 
        {
            path = res1;
        }

        //ip_domain
        res = strtok(res, ":");

        //port
        res1 = strtok(NULL, " ");
        //port
        if (res1 != NULL) 
        {
            port = res1;
        } else 
        {
            port = "80";
        }

        if (res != NULL) 
        {
            ip_domain = res;

            //if we use ethernet, we must decode ip address or use dnsresolver
#ifdef TARGET_LPC1768
            if (netif == ETH) {
                strcpy(buf, res);
                //we try to decode the ip address
                if (buf[0] >= '0' && buf[0] <= '9') {
                    res = strtok(buf, ".");
                    int i = 0;
                    int ip[4];
                    while (res != NULL) {
                        ip[i] = atoi(res);
                        res = strtok(NULL, ".");
                        i++;
                    }
                    server_ip = new IpAddr(ip[0], ip[1], ip[2], ip[3]);
#ifdef DEBUG
                    printf("server without dns=%i.%i.%i.%i\n",(*server_ip)[0],(*server_ip)[1],(*server_ip)[2],(*server_ip)[3]);
#endif
                }
            }
#endif //target
        }
    }
}


bool Websocket::connect(const uint32_t timeout) 
{
#ifdef DEBUG
    printf("CONNECT() Function\r\n");
#endif
    char cmd[50];
#ifdef TARGET_LPC1768
   //M: else if (netif == ETH) 
    if (netif == ETH) 
    {
        Host server (*server_ip, atoi(port.c_str()));
        sock->close();
        TCPSocketErr bindErr = sock->connect(server);
        if (bindErr) 
        {
#ifdef DEBUG
            printf("\r\nERROR binderr: %d\r\n", bindErr);
#endif
            return false;
        }
        Timer tmr;
        tmr.start();
        int i = 0;
        while (true) {
            Net::poll();
            if (time(0) > timeout) {
                
                printf("connect timeout %u > %u\r\n",
                    time(0), timeout);
                
                return false;
            }
            if (tmr.read() > 0.05) {
                tmr.reset();
                if (eth_connected) {
                    //printf("connect msg %d\r\n",i);
                    switch (i) {
                        case 0:
                            sprintf(cmd, "GET /%s HTTP/1.1\r\n", path.c_str());
                            sock->send(cmd, strlen(cmd));
                            i++;
                            break;
                        case 1:
                            sprintf(cmd, "Host: %s:%s\r\n", ip_domain.c_str(), port.c_str());
                            sock->send(cmd, strlen(cmd));
                            i++;
                            break;
                        case 2:
                            sprintf(cmd, "Upgrade: WebSocket\r\n");
                            sock->send(cmd, strlen(cmd));
                            i++;
                            break;
                        case 3:
                            sprintf(cmd, "Origin: null\r\n");
                            sock->send(cmd, strlen(cmd));
                            i++;
                            break;
                        case 4:
                            sprintf(cmd, "Connection: Upgrade\r\n");
                            sock->send(cmd, strlen(cmd));
                            i++;
                            break;
                        case 5:
                            sprintf(cmd, "Sec-WebSocket-Key: L159VM0TWUzyDxwJEIEzjw==\r\n");
                            sock->send(cmd, strlen(cmd));
                            i++;
                            break;
                        case 6:
                            sprintf(cmd, "Sec-WebSocket-Version: 13\r\n\r\n");
                            sock->send(cmd, strlen(cmd));
                            i++;
                            break;
                        case 7:
                            if (response_server_eth)
                                i++;
                            else
                                break;

                        default:
                            break;
                    }
                }
                if (i==8) {
#ifdef DEBUG
                    printf("\r\nip_domain: %s\r\npath: /%s\r\nport: %s\r\n\r\n",this->ip_domain.c_str(), this->path.c_str(), this->port.c_str());
#endif
                    return true;
                }
            }
        }
    }
#endif //target
    //the program shouldn't be here
    return false;
}    

void Websocket::sendLength(uint32_t len) 
{
    //printf("SENDLENGTH(), Send Length: %d\r\n", len);
    if (len < 126) 
    {
        sendChar(len | (1<<7));
    } 
    else if (len < 65535) 
    { // use 2 bytes 
        sendChar(126 | (1<<7));
       //M: reverce these two lines 
        sendChar((len >> 8) & 0xff); //2       
        sendChar(len & 0xff); //1
  //      sendChar((len >> 8) & 0xff); //2         
    } 
    else
    {
        sendChar(127 | (1<<7));
        for (int i = 0; i < 8; i++) 
        {
            sendChar((len >> i*8) & 0xff);
        }
    }
}

int Websocket::sendChar(uint8_t c) 
{
#ifdef TARGET_LPC1768
    if (netif == ETH) {
        Net::poll();
        return sock->send((const char *)&c, 1);
  //      printf("SENDCHAR(), Sendchar %d \r\n", c);
    }
#endif
    return 0;
}

void Websocket::sendOpcode(uint8_t opcode) 
{
//    printf("SENDOPCODE()\r\n");
    sendChar(0x80 | (opcode & 0x0f));
//    printf("SendOpcode: 0x%X\r\n", opcode);
}

/*Masking-key:  0 or 4 bytes
      All frames sent from the client to the server are masked by a 32-
      bit value that is contained within the frame.  This field is
      present if the mask bit is set to 1, and is absent if the mask bit
      is set to 0.
*/
void Websocket::sendMask() 
{
//    printf("SENDMASK()\r\n");
    for (int i = 0; i < 4; i++) {
        sendChar(0); //no frame masking 
    }
}

void Websocket::send(const char * str) 
{
#ifdef DEBUG
    printf("SEND(%s)\r\n",str);
#endif
/* Opcode:  4 bits
      The opcode denotes the frame type of the WebSocket frame
      Defines the interpretation of the payload data.  If an unknown
      opcode is received, the receiving endpoint MUST _Fail the
      WebSocket Connection_.  The following values are defined.
      *  %x0 denotes a continuation frame
      *  %x1 denotes a text frame
      *  %x2 denotes a binary frame
      *  %x3-7 are reserved for further non-control frames
      *  %x8 denotes a connection close
      *  %x9 denotes a pingg
      *  %xA denotes a pong
      *  %xB-F are reserved for further control frames
      
      */
    sendOpcode(0x01);
    sendLength(strlen(str));
    sendMask();

#ifdef TARGET_LPC1768
   //M: else if (netif == ETH) {
    if (netif == ETH) 
    {
        Net::poll();
        sock->send(str, strlen(str));
    }
#endif //target
}

bool Websocket::sendBinary(const char* hbuf, const uint32_t hlen, 
                           FILE* f, const uint32_t nbytes,
                           char* const bbuf) {
    if (kUseB64) {
        return sendBinaryB64txt(hbuf, hlen, f, nbytes, bbuf);
    } else {
        return sendBinaryDirect(hbuf, hlen, f, nbytes);
    }
}

bool Websocket::sendBinaryB64txt(const char* hbuf, const uint32_t hlen,
                                 FILE* f, const uint32_t nbytes,
                                 char* const bbuf) {
#ifdef DEBUG
    printf("SENDbinaryB64txt(%s)\r\n",str);
#endif
/* Opcode:  4 bits
      The opcode denotes the frame type of the WebSocket frame
      Defines the interpretation of the payload data.  If an unknown
      opcode is received, the receiving endpoint MUST _Fail the
      WebSocket Connection_.  The following values are defined.
      *  %x0 denotes a continuation frame
      *  %x1 denotes a text frame
      *  %x2 denotes a binary frame
      *  %x3-7 are reserved for further non-control frames
      *  %x8 denotes a connection close
      *  %x9 denotes a pingg
      *  %xA denotes a pong
      *  %xB-F are reserved for further control frames
      
      */
    sendOpcode(0x01);
    sendLength(BASE64ENC_LEN(nbytes));
    sendMask();

#ifdef TARGET_LPC1768
   //M: else if (netif == ETH) {
    if (netif == ETH) 
    {
        Net::poll();
        //sock->send(str, strlen(str));
        // read 3 bytes at a time
        static const uint8_t cbytes = 3; // must be multiple of 3
        static const uint32_t clen = BASE64ENC_LEN(cbytes)+1;
        char chunk[clen];
        char fbuf[cbytes];
        const uint32_t nchunks = nbytes / cbytes;
        for (uint32_t i=0; i<nchunks; i++) {
            fread(fbuf, cbytes, 1, f);
            B64::modp_b64_encode(fbuf, cbytes, chunk);
            sock->send(chunk, clen-1);
        }
        // now the remainder
        const uint32_t rem = nbytes-(nchunks*cbytes);
        if (rem>0) {
            fread(fbuf, rem, 1, f);
            B64::modp_b64_encode(fbuf, rem, chunk);
            sock->send(chunk, strlen(chunk));
        }
    }
#endif //target
    return true;
}

bool Websocket::sendBinary(const char* str, const uint32_t len,
                           char* const bbuf) {
    if (kUseB64) {
        return sendBinaryB64txt(str, len, bbuf);
    } else {
        return sendBinaryDirect(str, len);
    }
}

bool Websocket::sendBinaryB64txt(const char* str, const uint32_t len,
                                 char* const bbuf) {
    B64::modp_b64_encode(str, len, bbuf);
    send(bbuf);
    return true;
}

bool Websocket::sendBinaryDirect(const char* str, const uint32_t len) 
{
    // CJR: add function for sending binary
    // force header info to be sent on each call
#ifdef DEBUG
    printf("SENDBINARY()\r\n");
#endif
/* Opcode:  4 bits
      The opcode denotes the frame type of the WebSocket frame
      Defines the interpretation of the payload data.  If an unknown
      opcode is received, the receiving endpoint MUST _Fail the
      WebSocket Connection_.  The following values are defined.
      *  %x0 denotes a continuation frame
      *  %x1 denotes a text frame
      *  %x2 denotes a binary frame
      *  %x3-7 are reserved for further non-control frames
      *  %x8 denotes a connection close
      *  %x9 denotes a pingg
      *  %xA denotes a pong
      *  %xB-F are reserved for further control frames
      
      */
    printf("send binary: 0x02 %u 0\r\n", len);
    sendOpcode(0x02);
    sendLength(len);
    sendMask();

#ifdef TARGET_LPC1768
   //M: else if (netif == ETH) {
    if (netif == ETH) 
    {
        Net::poll();
        /*
        // first send the header
        const char* hh = hbuf;
        for (uint32_t j=0; j<hlen; j++, hh++) {
            sendChar(*hh);
            printf("%02x ",*hh);
        }
        */
        // now send the message
        uint32_t nbytes=0;
        const char* ss = str;
        for (uint32_t i=0; i<len; i++, ss++) {
            nbytes += sendChar(*ss);
            printf("%02x ",*ss);
        }
        printf("\r\n");
        //const int32_t nbytes = sock->send(str, len);
        return nbytes==len;
    }
#endif //target
    return false;
}

bool Websocket::sendBinaryDirect(const char* hbuf, const uint32_t hlen,
                                 FILE* f, const uint32_t nbytes) 
{
#ifdef DEBUG
    printf("SENDBINARY(FILE*)\r\n");
#endif
    // CJR: add function for sending binary
    // force header info to be sent on each call
    // return false if EOF or file error before nbytes sent
    
    //printf("SEND()\r\n");
/* Opcode:  4 bits
      The opcode denotes the frame type of the WebSocket frame
      Defines the interpretation of the payload data.  If an unknown
      opcode is received, the receiving endpoint MUST _Fail the
      WebSocket Connection_.  The following values are defined.
      *  %x0 denotes a continuation frame
      *  %x1 denotes a text frame
      *  %x2 denotes a binary frame
      *  %x3-7 are reserved for further non-control frames
      *  %x8 denotes a connection close
      *  %x9 denotes a pingg
      *  %xA denotes a pong
      *  %xB-F are reserved for further control frames
      
      */
    sendOpcode(0x02);
    sendLength(nbytes);
    sendMask();

#ifdef TARGET_LPC1768
   //M: else if (netif == ETH) {
    if (netif == ETH) 
    {
        Net::poll();
        // first send the header
        const char* hh = hbuf;
        for (uint32_t j=0; j<hlen; j++, hh++) {
            sendChar(*hh);
        }
        // conserve mbed memory by reading byte-by-byte
        uint8_t c;
        for (uint32_t i=0; i<nbytes; i++) {
            fread(&c, 1, 1, f);
            if ((feof(f)==0) && (ferror(f)==0)) {
                sendChar(c);
            } else {
                return false;
            }
        }
            
    }
#endif //target
    return true;
}

bool Websocket::read(char * message, uint32_t& len_msg, const uint32_t maxlen,
                     const uint32_t timeout,
                     char* const bbuf, const uint32_t bbsize) 
{
#ifdef DEBUG
    printf("READ()\r\n");
#endif
    //uint32_t len_msg; //32 bit size
    char opcode = 0; //continuation frame
    char mask[4] = {0, 0, 0, 0}; //no mask
#ifdef TARGET_LPC1768
    if (netif == ETH) 
    {
#ifdef DEBUG
        printf("Current opcode in read() 0x%X\r\n", opcode);
#endif
        uint32_t index = 0;
        Net::poll();
        if (new_msg) //from webserver
        {
#ifdef DEBUG
            printf("There is new message from webserver\r\n");
#endif
             // read the opcode
            while (true) 
            {
                if (time(0) > timeout) 
                {
                    printf("read: timing out %d > %u \r\n",
                        time(0), timeout);
                    wait(2);
                    return false;
                } else if (index>=RX_BUF_SIZE) {
                    index=0;
                    continue;
                }
                opcode = eth_rx[index++];
#ifdef DEBUG
                printf("new opcode in read() func: 0x%X\r\n", opcode); //0x81
#endif
                if (opcode == 0x81 || opcode==0x82) 
                {
                    break;
                }
            }// end of while(true)
            
            // redo search in case opcode is actually inside a byte message
            // and a race-condition occurred
            int32_t ix=0;
            const char* erx = eth_rx;
            for (ix=0; ix<RX_BUF_SIZE; ix++, erx++) { // 1024 or 512? (since sock->read is called with 512)
                if ( (*erx==0x81) || (*erx==0x82) ) {
                    index = ix+1;
                    opcode = *erx;
                    break;
                }
            }
            if (ix==RX_BUF_SIZE) {
                // some new non-websocket message came in during the redoing of the search?
                // anyway opcode not found
                printf("opcode re-search failed\r\n");
                return false;
            }
//#ifdef DEBUG
            printf("opcode: 0x%X @ index=%u\r\n", opcode, index-1);
            for (int i=0; i<RX_BUF_SIZE; i++) {
                printf("%02x ",eth_rx[i]);
            }
            printf("\r\n");
//#endif                 
            printf("eth_rx[i-1]=%x, eth_rx[i]=%x , eth_rx[i+1]=%x\r\n",
                index>0?eth_rx[index-1]:eth_rx[index],eth_rx[index],eth_rx[index+1]);
            len_msg = eth_rx[index++] & 0x7f;
            printf("length_message2 : %d\r\n", len_msg);            //
            if (len_msg == 126)
            {
                len_msg += eth_rx[index++] << 8;                        
                len_msg = eth_rx[index++];                                  
                printf("len message is greater than 126 %d\r\n", len_msg);     
            } 
            else if (len_msg == 127) 
            {              
                len_msg = 0;
                for (int i = 0; i < 8; i++) 
                {
                    len_msg += eth_rx[index++] << i*8;
                }            
            } 
            if(len_msg == 0) 
            {  
                return false;
            } else if (len_msg>=maxlen) {
                return false;
            }
//#ifdef DEBUG
            printf("length: %d\r\n", len_msg);
//#endif
            if ((len_msg & 0x80)) 
            {
                printf("print mask bit %d\r\n", len_msg & 0x80);
                for (int j = 0; j < 4; j++){
                    mask[j] = eth_rx[index++];
                    printf(" mask index %i is %i, ", j, mask[j]); 
                }                   
            }
            printf("\r\n");
            
            // read the actual message
            for (int i = 0; i < len_msg; i++) 
            {             
                if (len_msg < 126) {
                    message[i] = eth_rx[i+2];                      
                } else {
                    message[i] = eth_rx[i+4];
                }
            }            
            message[len_msg] = 0;
            
            if ( kUseB64 && ((opcode & 0x01)!=0) ) {
                // decode the message
                printf("need to b64decode\r\n");
                const uint8_t pads = len_msg%4;
                if ((len_msg+pads+1)<maxlen) {
                    char* mm = message+len_msg;
                    for (uint8_t k=0; k<pads; k++, mm++) {
                        *mm = CHARPAD;
                    }
                    *mm=0;
                    if (BASE64ENC_LEN(len_msg+pads)<bbsize) {
                        len_msg = B64::modp_b64_decode(message, len_msg+pads, bbuf);
                        if (len_msg<maxlen) {
                            memcpy(message, bbuf, len_msg);
                        } else {
                            return false;
                        }
                    } else {
                        return false;
                    }
                } else {
                    return false;
                }
            }
            
            for (int i=0; i<len_msg; i++) {
                printf("%02x ",message[i]);
            }
            printf("\r\n");
            
            new_msg = false;                
            return true;
        }//if new message
        //printf("no new message!\r\n");
        return false;
    }//end of if(eth)
#endif //target
//the program shouldn't be here
    return false;
} // end of read func

bool Websocket::close() 
{
#ifdef DEBUG
   printf("CLOSE()\r\n");
#endif
#ifdef TARGET_LPC1768
    if (netif == ETH) 
    {
        if (sock->close())
            return false;
        return true;
    }
#endif //target
    //the program shouldn't be here
    return false;
}

bool Websocket::connected() {
 //   printf("CONNECTED()\r\n");

#ifdef TARGET_LPC1768

    if (netif == ETH)
    {    
        return eth_connected;
    }
#endif //target
    //the program shouldn't be here
    return false;
}

std::string Websocket::getPath()
{
    return path;
}

#ifdef TARGET_LPC1768
void Websocket::onTCPSocketEvent(TCPSocketEvent e) 
{
#ifdef DEBUG
    printf("TCPSocketEvent is ");
#endif
    if (e == TCPSOCKET_CONNECTED) 
    {
        eth_connected = true;
#ifdef DEBUG
        printf("TCP Socket Connected\r\n");
#endif
    } 
    else if (e == TCPSOCKET_WRITEABLE) {
#ifdef DEBUG
        printf("TCPSOCKET_WRITEABLE\r\n");
#endif
    } 
    else if (e == TCPSOCKET_READABLE) 
    {
#ifdef DEBUG
        printf("TCPSOCKET_READABLE\r\n");
#endif
        const int len = sock->recv(eth_rx, RX_RECV_BYTES);
        eth_rx[len] = 0;
        new_msg = true;
        printf("sock recv len %d\r\n",len);
        printf("eth_rx:");
        for (int i=0; i<len; i++) {
            printf("%02x ",eth_rx[i]);
        }
        printf("\r\n");
        if (!response_server_eth) 
        {
            string checking;
            size_t found = string::npos;
            checking = eth_rx;
            found = checking.find("DdLWT/1JcX+nQFHebYP+rqEx5xI=");
            if (found != string::npos) {
                printf("response_server_eth true. found=%u\r\n",found);
                response_server_eth = true;
            }
        }
    } 
    else 
    {
#ifdef DEBUG
        printf("TCP Socket Fail (%d)\r\n",(int)e);
#endif
        eth_connected = false;
    }
}
#endif //target

#endif