Fork of https://os.mbed.com/teams/mbed_example/code/WebSocketClient/ Update to MbedOS6
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Tue Jul 19 2022 14:17:08 by
![doxygen](doxygen.png)