Debug Version

Fork of WebSocketClient by Samuel Mokrani

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