Websocket_Sample for MurataTypeYD

Dependencies:   mbed picojson

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(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  // KTEA ADD S
00150  // シェイクハンズのコマンドを送ってからレスポンスが返ってくるまで少し待つ必要があるためwaitを追加
00151     wait(3.0);
00152  // KTEA ADD E
00153 
00154     ret = read(cmd, 200, 100);
00155     if (ret < 0) {
00156         close();
00157         ERR("Could not receive answer\r\n");
00158         return false;
00159     }
00160 
00161     cmd[ret] = '\0';
00162     DBG("recv: %s\r\n", cmd);
00163 
00164     if ( strstr(cmd, "DdLWT/1JcX+nQFHebYP+rqEx5xI=") == NULL ) {
00165         ERR("Wrong answer from server, got \"%s\" instead\r\n", cmd);
00166         do {
00167             ret = read(cmd, 200, 100);
00168             if (ret < 0) {
00169                 ERR("Could not receive answer\r\n");
00170                 return false;
00171             }
00172             cmd[ret] = '\0';
00173             printf("%s",cmd);
00174         } while (ret > 0);
00175         close();
00176         return false;
00177     }
00178 
00179     INFO("\r\nhost: %s\r\npath: %s\r\nport: %d\r\n\r\n", host, path, port);
00180     return true;
00181 }
00182 
00183 int Websocket::sendLength(uint32_t len, char * msg) {
00184 
00185     if (len < 126) {
00186         msg[0] = len | (1<<7);
00187         return 1;
00188     } else if (len < 65535) {
00189         msg[0] = 126 | (1<<7);
00190         msg[1] = (len >> 8) & 0xff;
00191         msg[2] = len & 0xff;
00192         return 3;
00193     } else {
00194         msg[0] = 127 | (1<<7);
00195         for (int i = 0; i < 8; i++) {
00196             msg[i+1] = (len >> i*8) & 0xff;
00197         }
00198         return 9;
00199     }
00200 }
00201 
00202 int Websocket::readChar(char * pC, bool block) {
00203     return read(pC, 1, 1);
00204 }
00205 
00206 int Websocket::sendOpcode(uint8_t opcode, char * msg) {
00207     msg[0] = 0x80 | (opcode & 0x0f);
00208     return 1;
00209 }
00210 
00211 int Websocket::sendMask(char * msg) {
00212     for (int i = 0; i < 4; i++) {
00213         msg[i] = 0;
00214     }
00215     return 4;
00216 }
00217 
00218 int Websocket::send(char * str) {
00219     char msg[strlen(str) + 15];
00220     int idx = 0;
00221     idx = sendOpcode(0x01, msg);
00222     idx += sendLength(strlen(str), msg + idx);
00223     idx += sendMask(msg + idx);
00224     memcpy(msg+idx, str, strlen(str));
00225     int res = write(msg, idx + strlen(str));
00226     return res;
00227 }
00228 
00229 
00230 bool Websocket::read(char * message) {
00231     int i = 0;
00232     uint32_t len_msg;
00233     char opcode = 0;
00234     char c;
00235     char mask[4] = {0, 0, 0, 0};
00236     bool is_masked = false;
00237     Timer tmr;
00238 
00239     // read the opcode
00240     tmr.start();
00241     while (true) {
00242         if (tmr.read() > 3) {
00243             DBG("timeout ws\r\n");
00244             return false;
00245         }
00246         
00247         if(!socket.is_connected())
00248         {
00249             WARN("Connection was closed by server");
00250             return false;
00251         }
00252 
00253         socket.set_blocking(false, 1);
00254         if (socket.receive(&opcode, 1) != 1) {
00255             socket.set_blocking(false, 2000);
00256             return false;
00257         }
00258 
00259         socket.set_blocking(false, 2000);
00260 
00261         if (opcode == 0x81)
00262             break;
00263     }
00264     DBG("opcode: 0x%X\r\n", opcode);
00265 
00266     readChar(&c);
00267     len_msg = c & 0x7f;
00268     is_masked = c & 0x80;
00269     if (len_msg == 126) {
00270         readChar(&c);
00271         len_msg = c << 8;
00272         readChar(&c);
00273         len_msg += c;
00274     } else if (len_msg == 127) {
00275         len_msg = 0;
00276         for (int i = 0; i < 8; i++) {
00277             readChar(&c);
00278             len_msg += (c << (7-i)*8);
00279         }
00280     }
00281 
00282     if (len_msg == 0) {
00283         return false;
00284     }
00285     DBG("length: %d\r\n", len_msg);
00286     
00287     if (is_masked) {
00288         for (i = 0; i < 4; i++)
00289             readChar(&c);
00290         mask[i] = c;
00291     }
00292 
00293     int nb = read(message, len_msg, len_msg);
00294     if (nb != len_msg)
00295         return false;
00296 
00297     for (i = 0; i < len_msg; i++) {
00298         message[i] = message[i] ^ mask[i % 4];
00299     }
00300 
00301     message[len_msg] = '\0';
00302 
00303     return true;
00304 }
00305 
00306 bool Websocket::close() {
00307     if (!is_connected())
00308         return false;
00309 
00310     int ret = socket.close();
00311     if (ret < 0) {
00312         ERR("Could not disconnect");
00313         return false;
00314     }
00315     return true;
00316 }
00317 
00318 bool Websocket::is_connected() {
00319     return socket.is_connected();
00320 }
00321 
00322 char* Websocket::getPath() {
00323     return path;
00324 }
00325 
00326 int Websocket::write(char * str, int len) {
00327     int res = 0, idx = 0;
00328     
00329     for (int j = 0; j < MAX_TRY_WRITE; j++) {
00330     
00331         if(!socket.is_connected())
00332         {
00333             WARN("Connection was closed by server");
00334             break;
00335         }
00336 
00337         if ((res = socket.send_all(str + idx, len - idx)) == -1)
00338             continue;
00339 
00340         idx += res;
00341         
00342         if (idx == len)
00343             return len;
00344     }
00345     
00346     return (idx == 0) ? -1 : idx;
00347 }
00348 
00349 int Websocket::read(char * str, int len, int min_len) {
00350     int res = 0, idx = 0;
00351     
00352     for (int j = 0; j < MAX_TRY_WRITE; j++) {
00353 
00354         if ((res = socket.receive_all(str + idx, len - idx)) == -1)
00355             continue;
00356 
00357         idx += res;
00358         
00359         if (idx == len || (min_len != -1 && idx > min_len))
00360             return idx;
00361     }
00362     
00363     return (idx == 0) ? -1 : idx;
00364 }