Simple websocket client

Dependencies:   EthernetInterface

Dependents:   Websocket_Test

Fork of WebSocketClient by S5info_H14

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