mbed_example / WebSocketClient

Dependents:   WebsocketClient Web_suck_et APS SO - ALARME CONTROLADO VIA SOCKETS F411-mbed-os-iot-project ... more

Fork of WebSocketClient by mbed official

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers Websocket.cpp Source File

Websocket.cpp

00001 #include "Websocket.h"
00002 
00003 #define MAX_TRY_WRITE 20
00004 #define MAX_TRY_READ 10
00005 
00006 //Debug is disabled by default
00007 #if 0
00008 #define DBG(x, ...) std::printf("[WebSocket : DBG]"x"\r\n", ##__VA_ARGS__); 
00009 #define WARN(x, ...) std::printf("[WebSocket : WARN]"x"\r\n", ##__VA_ARGS__); 
00010 #define ERR(x, ...) std::printf("[WebSocket : ERR]"x"\r\n", ##__VA_ARGS__); 
00011 #else
00012 #define DBG(x, ...) 
00013 #define WARN(x, ...)
00014 #define ERR(x, ...) 
00015 #endif
00016 
00017 #define INFO(x, ...) printf("[WebSocket : INFO]"x"\r\n", ##__VA_ARGS__); 
00018 
00019 Websocket::Websocket(char * url, NetworkInterface * iface) {
00020     fillFields(url);
00021     socket.open(iface);
00022     socket.set_timeout(400);
00023 }
00024 
00025 void Websocket::fillFields(char * url) {
00026   int ret = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
00027   if(ret)
00028   {
00029     ERR("URL parsing failed; please use: \"ws://ip-or-domain[:port]/path\"");
00030     return;
00031   }
00032 
00033   if(port == 0) //TODO do handle WSS->443
00034   {
00035     port = 80;
00036   }
00037   
00038   if(strcmp(scheme, "ws"))
00039   {
00040     ERR("Wrong scheme, please use \"ws\" instead");
00041   }
00042 }
00043 
00044 int Websocket::parseURL(const char* url, char* scheme, size_t maxSchemeLen, char* host, size_t maxHostLen, uint16_t* port, char* path, size_t maxPathLen) //Parse URL
00045 {
00046   char* schemePtr = (char*) url;
00047   char* hostPtr = (char*) strstr(url, "://");
00048   if(hostPtr == NULL)
00049   {
00050     WARN("Could not find host");
00051     return -1; //URL is invalid
00052   }
00053 
00054   if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
00055   {
00056     WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
00057     return -1;
00058   }
00059   memcpy(scheme, schemePtr, hostPtr - schemePtr);
00060   scheme[hostPtr - schemePtr] = '\0';
00061 
00062   hostPtr+=3;
00063 
00064   size_t hostLen = 0;
00065 
00066   char* portPtr = strchr(hostPtr, ':');
00067   if( portPtr != NULL )
00068   {
00069     hostLen = portPtr - hostPtr;
00070     portPtr++;
00071     if( sscanf(portPtr, "%hu", port) != 1)
00072     {
00073       WARN("Could not find port");
00074       return -1;
00075     }
00076   }
00077   else
00078   {
00079     *port=0;
00080   }
00081   char* pathPtr = strchr(hostPtr, '/');
00082   if(pathPtr == NULL)
00083   {
00084     WARN("Path not specified. Please add /[path] to the end of the websocket address");
00085     return -1;
00086   }
00087   if( hostLen == 0 )
00088   {
00089     hostLen = pathPtr - hostPtr;
00090   }
00091 
00092   if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
00093   {
00094     WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
00095     return -1;
00096   }
00097   memcpy(host, hostPtr, hostLen);
00098   host[hostLen] = '\0';
00099 
00100   size_t pathLen;
00101   char* fragmentPtr = strchr(hostPtr, '#');
00102   if(fragmentPtr != NULL)
00103   {
00104     pathLen = fragmentPtr - pathPtr;
00105   }
00106   else
00107   {
00108     pathLen = strlen(pathPtr);
00109   }
00110 
00111   if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
00112   {
00113     WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
00114     return -1;
00115   }
00116   memcpy(path, pathPtr, pathLen);
00117   path[pathLen] = '\0';
00118 
00119   return 0;
00120 }
00121 
00122 
00123 bool Websocket::connect() {
00124     char cmd[200];
00125 
00126     while (socket.connect(host, port) < 0) {
00127         ERR("Unable to connect to (%s) on port (%d)", host, port);
00128         wait(0.2);
00129         return false;
00130     }
00131 
00132     // sent http header to upgrade to the ws protocol
00133     sprintf(cmd, "GET %s HTTP/1.1\r\n", path);
00134     write(cmd, strlen(cmd));
00135     
00136     sprintf(cmd, "Host: %s:%d\r\n", host, port);
00137     write(cmd, strlen(cmd));
00138 
00139     sprintf(cmd, "Upgrade: WebSocket\r\n");
00140     write(cmd, strlen(cmd));
00141 
00142     sprintf(cmd, "Connection: Upgrade\r\n");
00143     write(cmd, strlen(cmd));
00144 
00145     sprintf(cmd, "Sec-WebSocket-Key: L159VM0TWUzyDxwJEIEzjw==\r\n");
00146     write(cmd, strlen(cmd));
00147 
00148     sprintf(cmd, "Sec-WebSocket-Version: 13\r\n\r\n");
00149     int ret = write(cmd, strlen(cmd));
00150     if (ret != strlen(cmd)) {
00151         close();
00152         ERR("Could not send request");
00153         return false;
00154     }
00155 
00156     ret = read(cmd, 200, 100);
00157     if (ret < 0) {
00158         close();
00159         ERR("Could not receive answer\r\n");
00160         return false;
00161     }
00162 
00163     cmd[ret] = '\0';
00164     DBG("recv: %s\r\n", cmd);
00165 
00166     if ( strstr(cmd, "DdLWT/1JcX+nQFHebYP+rqEx5xI=") == NULL ) {
00167         ERR("Wrong answer from server, got \"%s\" instead\r\n", cmd);
00168         do {
00169             ret = read(cmd, 200, 100);
00170             if (ret < 0) {
00171                 ERR("Could not receive answer\r\n");
00172                 return false;
00173             }
00174             cmd[ret] = '\0';
00175         } while (ret > 0);
00176         close();
00177         return false;
00178     }
00179 
00180     INFO("\r\nhost: %s\r\npath: %s\r\nport: %d\r\n\r\n", host, path, port);
00181     return true;
00182 }
00183 
00184 int Websocket::sendLength(uint32_t len, char * msg) {
00185 
00186     if (len < 126) {
00187         msg[0] = len | (1<<7);
00188         return 1;
00189     } else if (len < 65535) {
00190         msg[0] = 126 | (1<<7);
00191         msg[1] = (len >> 8) & 0xff;
00192         msg[2] = len & 0xff;
00193         return 3;
00194     } else {
00195         msg[0] = 127 | (1<<7);
00196         for (int i = 0; i < 8; i++) {
00197             msg[i+1] = (len >> i*8) & 0xff;
00198         }
00199         return 9;
00200     }
00201 }
00202 
00203 int Websocket::readChar(char * pC, bool block) {
00204     return read(pC, 1, 1);
00205 }
00206 
00207 int Websocket::sendOpcode(uint8_t opcode, char * msg) {
00208     msg[0] = 0x80 | (opcode & 0x0f);
00209     return 1;
00210 }
00211 
00212 int Websocket::sendMask(char * msg) {
00213     for (int i = 0; i < 4; i++) {
00214         msg[i] = 0;
00215     }
00216     return 4;
00217 }
00218 
00219 int Websocket::send(char * str) {
00220     char msg[strlen(str) + 15];
00221     int idx = 0;
00222     idx = sendOpcode(0x01, msg);
00223     idx += sendLength(strlen(str), msg + idx);
00224     idx += sendMask(msg + idx);
00225     memcpy(msg+idx, str, strlen(str));
00226     int res = write(msg, idx + strlen(str));
00227     return res;
00228 }
00229 
00230 
00231 bool Websocket::read(char * message) {
00232     int i = 0;
00233     uint32_t len_msg;
00234     char opcode = 0;
00235     char c;
00236     char mask[4] = {0, 0, 0, 0};
00237     bool is_masked = false;
00238     Timer tmr;
00239 
00240     // read the opcode
00241     tmr.start();
00242     while (true) {
00243         if (tmr.read() > 3) {
00244             DBG("timeout ws\r\n");
00245             return false;
00246         }
00247 
00248         socket.set_timeout(1);
00249         if (socket.recv(&opcode, 1) != 1) {
00250             socket.set_timeout(2000);
00251             return false;
00252         }
00253 
00254         socket.set_timeout(2000);
00255 
00256         if (opcode == 0x81)
00257             break;
00258     }
00259     DBG("opcode: 0x%X\r\n", opcode);
00260 
00261     readChar(&c);
00262     len_msg = c & 0x7f;
00263     is_masked = c & 0x80;
00264     if (len_msg == 126) {
00265         readChar(&c);
00266         len_msg = c << 8;
00267         readChar(&c);
00268         len_msg += c;
00269     } else if (len_msg == 127) {
00270         len_msg = 0;
00271         for (int i = 0; i < 8; i++) {
00272             readChar(&c);
00273             len_msg += (c << (7-i)*8);
00274         }
00275     }
00276 
00277     if (len_msg == 0) {
00278         return false;
00279     }
00280     DBG("length: %d\r\n", len_msg);
00281     
00282     if (is_masked) {
00283         for (i = 0; i < 4; i++) {
00284           readChar(&c);
00285           mask[i] = c;
00286         }
00287     }
00288 
00289     int nb = read(message, len_msg, len_msg);
00290     if (nb != len_msg)
00291         return false;
00292 
00293     for (i = 0; i < len_msg; i++) {
00294         message[i] = message[i] ^ mask[i % 4];
00295     }
00296 
00297     message[len_msg] = '\0';
00298 
00299     return true;
00300 }
00301 
00302 bool Websocket::close() {
00303 
00304     int ret = socket.close();
00305     if (ret < 0) {
00306         ERR("Could not disconnect");
00307         return false;
00308     }
00309     return true;
00310 }
00311 
00312 char* Websocket::getPath() {
00313     return path;
00314 }
00315 
00316 int Websocket::write(char * str, int len) {
00317     int res = 0, idx = 0;
00318     
00319     for (int j = 0; j < MAX_TRY_WRITE; j++) {
00320 
00321         if ((res = socket.send(str + idx, len - idx)) < 0)
00322             continue;
00323 
00324         idx += res;
00325         
00326         if (idx == len)
00327             return len;
00328     }
00329     
00330     return (idx == 0) ? -1 : idx;
00331 }
00332 
00333 int Websocket::read(char * str, int len, int min_len) {
00334     int res = 0, idx = 0;
00335     
00336     for (int j = 0; j < MAX_TRY_WRITE; j++) {
00337 
00338         if ((res = socket.recv(str + idx, len - idx)) < 0)
00339           continue;
00340 
00341         idx += res;
00342         
00343         if (idx == len || (min_len != -1 && idx > min_len))
00344             return idx;
00345     }
00346     
00347     return (idx == 0) ? -1 : idx;
00348 }