Fork of https://os.mbed.com/teams/mbed_example/code/WebSocketClient/ Update to MbedOS6

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