Arianna autonomous DAQ firmware
Dependencies: mbed SDFileSystemFilinfo AriSnProtocol NetServicesMin AriSnComm MODSERIAL PowerControlClkPatch DS1820OW
Websocket.cpp
- Committer:
- uci1
- Date:
- 2015-11-24
- Revision:
- 110:d1da040a0cf2
- Parent:
- 6:6f002d202f59
File content as of revision 110:d1da040a0cf2:
#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