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

Committer:
JohnnyK
Date:
Mon May 24 19:40:27 2021 +0000
Revision:
12:495c97f41a64
Parent:
11:9ee267dafdd7
repair - write return

Who changed what in which revision?

UserRevisionLine numberNew contents of line
samux 3:9589afa4712e 1 #include "Websocket.h"
samux 3:9589afa4712e 2
JohnnyK 11:9ee267dafdd7 3 #define MAX_TRY_WRITE 50
samux 7:4567996414a5 4 #define MAX_TRY_READ 10
samux 3:9589afa4712e 5
samux 3:9589afa4712e 6 //Debug is disabled by default
samux 3:9589afa4712e 7 #if 0
samux 3:9589afa4712e 8 #define DBG(x, ...) std::printf("[WebSocket : DBG]"x"\r\n", ##__VA_ARGS__);
samux 3:9589afa4712e 9 #define WARN(x, ...) std::printf("[WebSocket : WARN]"x"\r\n", ##__VA_ARGS__);
samux 3:9589afa4712e 10 #define ERR(x, ...) std::printf("[WebSocket : ERR]"x"\r\n", ##__VA_ARGS__);
samux 3:9589afa4712e 11 #else
samux 3:9589afa4712e 12 #define DBG(x, ...)
samux 3:9589afa4712e 13 #define WARN(x, ...)
samux 3:9589afa4712e 14 #define ERR(x, ...)
samux 3:9589afa4712e 15 #endif
samux 3:9589afa4712e 16
samux 3:9589afa4712e 17 #define INFO(x, ...) printf("[WebSocket : INFO]"x"\r\n", ##__VA_ARGS__);
samux 3:9589afa4712e 18
JohnnyK 11:9ee267dafdd7 19 Websocket::Websocket(char * url, NetworkInterface * iface, const char* ca_cert) {
samux 3:9589afa4712e 20 fillFields(url);
JohnnyK 11:9ee267dafdd7 21 if(secured == false){
JohnnyK 11:9ee267dafdd7 22 _socket = new TCPSocket();
JohnnyK 11:9ee267dafdd7 23 ((TCPSocket*)_socket)->open(iface);
JohnnyK 11:9ee267dafdd7 24 ((TCPSocket*)_socket)->set_timeout(400);
JohnnyK 11:9ee267dafdd7 25 }
JohnnyK 11:9ee267dafdd7 26 else if (secured == true && ca_cert != nullptr){
JohnnyK 11:9ee267dafdd7 27 _socket = new TLSSocket();
JohnnyK 11:9ee267dafdd7 28 ((TLSSocket*)_socket)->open(iface);
JohnnyK 11:9ee267dafdd7 29 ((TLSSocket*)_socket)->set_timeout(400);
JohnnyK 11:9ee267dafdd7 30 ((TLSSocket*)_socket)->set_hostname(host);
JohnnyK 11:9ee267dafdd7 31 int result = ((TLSSocket*)_socket)->set_root_ca_cert(ca_cert);
JohnnyK 11:9ee267dafdd7 32 if (result != NSAPI_ERROR_OK) {
JohnnyK 11:9ee267dafdd7 33 DBG("Error: _socket.set_root_ca_cert() returned %d\n", result);
JohnnyK 11:9ee267dafdd7 34 return;
JohnnyK 11:9ee267dafdd7 35 }
JohnnyK 11:9ee267dafdd7 36
JohnnyK 11:9ee267dafdd7 37 DBG("Host: %s\n", host);
JohnnyK 11:9ee267dafdd7 38 }
JohnnyK 11:9ee267dafdd7 39 else{
JohnnyK 11:9ee267dafdd7 40 ERR("Wrong combination of parameters!");
JohnnyK 11:9ee267dafdd7 41 return;
JohnnyK 11:9ee267dafdd7 42 }
samux 3:9589afa4712e 43 }
samux 3:9589afa4712e 44
samux 3:9589afa4712e 45 void Websocket::fillFields(char * url) {
donatien 6:86e89a0369b9 46 int ret = parseURL(url, scheme, sizeof(scheme), host, sizeof(host), &port, path, sizeof(path));
donatien 6:86e89a0369b9 47 if(ret)
donatien 6:86e89a0369b9 48 {
donatien 6:86e89a0369b9 49 ERR("URL parsing failed; please use: \"ws://ip-or-domain[:port]/path\"");
donatien 6:86e89a0369b9 50 return;
donatien 6:86e89a0369b9 51 }
JohnnyK 11:9ee267dafdd7 52
JohnnyK 11:9ee267dafdd7 53 if(strcmp(scheme, "ws" )== 0) secured = false;
JohnnyK 11:9ee267dafdd7 54 else if(strcmp(scheme, "wss")== 0) secured = true;
JohnnyK 11:9ee267dafdd7 55 else ERR("Wrong scheme, please use \"ws\" or \"wss\"");
JohnnyK 11:9ee267dafdd7 56
JohnnyK 11:9ee267dafdd7 57 if(port == 0)
donatien 6:86e89a0369b9 58 {
JohnnyK 11:9ee267dafdd7 59 if(secured) port = 443;
JohnnyK 11:9ee267dafdd7 60 else port = 80;
donatien 6:86e89a0369b9 61 }
donatien 6:86e89a0369b9 62 }
samux 3:9589afa4712e 63
donatien 6:86e89a0369b9 64 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
donatien 6:86e89a0369b9 65 {
donatien 6:86e89a0369b9 66 char* schemePtr = (char*) url;
donatien 6:86e89a0369b9 67 char* hostPtr = (char*) strstr(url, "://");
donatien 6:86e89a0369b9 68 if(hostPtr == NULL)
donatien 6:86e89a0369b9 69 {
donatien 6:86e89a0369b9 70 WARN("Could not find host");
donatien 6:86e89a0369b9 71 return -1; //URL is invalid
donatien 6:86e89a0369b9 72 }
donatien 6:86e89a0369b9 73
donatien 6:86e89a0369b9 74 if( maxSchemeLen < hostPtr - schemePtr + 1 ) //including NULL-terminating char
donatien 6:86e89a0369b9 75 {
donatien 6:86e89a0369b9 76 WARN("Scheme str is too small (%d >= %d)", maxSchemeLen, hostPtr - schemePtr + 1);
donatien 6:86e89a0369b9 77 return -1;
donatien 6:86e89a0369b9 78 }
donatien 6:86e89a0369b9 79 memcpy(scheme, schemePtr, hostPtr - schemePtr);
donatien 6:86e89a0369b9 80 scheme[hostPtr - schemePtr] = '\0';
donatien 6:86e89a0369b9 81
donatien 6:86e89a0369b9 82 hostPtr+=3;
donatien 6:86e89a0369b9 83
donatien 6:86e89a0369b9 84 size_t hostLen = 0;
samux 3:9589afa4712e 85
donatien 6:86e89a0369b9 86 char* portPtr = strchr(hostPtr, ':');
donatien 6:86e89a0369b9 87 if( portPtr != NULL )
donatien 6:86e89a0369b9 88 {
donatien 6:86e89a0369b9 89 hostLen = portPtr - hostPtr;
donatien 6:86e89a0369b9 90 portPtr++;
donatien 6:86e89a0369b9 91 if( sscanf(portPtr, "%hu", port) != 1)
donatien 6:86e89a0369b9 92 {
donatien 6:86e89a0369b9 93 WARN("Could not find port");
donatien 6:86e89a0369b9 94 return -1;
donatien 6:86e89a0369b9 95 }
donatien 6:86e89a0369b9 96 }
donatien 6:86e89a0369b9 97 else
donatien 6:86e89a0369b9 98 {
donatien 6:86e89a0369b9 99 *port=0;
donatien 6:86e89a0369b9 100 }
donatien 6:86e89a0369b9 101 char* pathPtr = strchr(hostPtr, '/');
mbed_official 9:efa2c147bee1 102 if(pathPtr == NULL)
mbed_official 9:efa2c147bee1 103 {
mbed_official 9:efa2c147bee1 104 WARN("Path not specified. Please add /[path] to the end of the websocket address");
mbed_official 9:efa2c147bee1 105 return -1;
mbed_official 9:efa2c147bee1 106 }
donatien 6:86e89a0369b9 107 if( hostLen == 0 )
donatien 6:86e89a0369b9 108 {
donatien 6:86e89a0369b9 109 hostLen = pathPtr - hostPtr;
donatien 6:86e89a0369b9 110 }
samux 3:9589afa4712e 111
donatien 6:86e89a0369b9 112 if( maxHostLen < hostLen + 1 ) //including NULL-terminating char
donatien 6:86e89a0369b9 113 {
donatien 6:86e89a0369b9 114 WARN("Host str is too small (%d >= %d)", maxHostLen, hostLen + 1);
donatien 6:86e89a0369b9 115 return -1;
donatien 6:86e89a0369b9 116 }
donatien 6:86e89a0369b9 117 memcpy(host, hostPtr, hostLen);
donatien 6:86e89a0369b9 118 host[hostLen] = '\0';
samux 3:9589afa4712e 119
donatien 6:86e89a0369b9 120 size_t pathLen;
donatien 6:86e89a0369b9 121 char* fragmentPtr = strchr(hostPtr, '#');
donatien 6:86e89a0369b9 122 if(fragmentPtr != NULL)
donatien 6:86e89a0369b9 123 {
donatien 6:86e89a0369b9 124 pathLen = fragmentPtr - pathPtr;
donatien 6:86e89a0369b9 125 }
donatien 6:86e89a0369b9 126 else
donatien 6:86e89a0369b9 127 {
donatien 6:86e89a0369b9 128 pathLen = strlen(pathPtr);
donatien 6:86e89a0369b9 129 }
donatien 6:86e89a0369b9 130
donatien 6:86e89a0369b9 131 if( maxPathLen < pathLen + 1 ) //including NULL-terminating char
donatien 6:86e89a0369b9 132 {
donatien 6:86e89a0369b9 133 WARN("Path str is too small (%d >= %d)", maxPathLen, pathLen + 1);
donatien 6:86e89a0369b9 134 return -1;
donatien 6:86e89a0369b9 135 }
donatien 6:86e89a0369b9 136 memcpy(path, pathPtr, pathLen);
donatien 6:86e89a0369b9 137 path[pathLen] = '\0';
donatien 6:86e89a0369b9 138
donatien 6:86e89a0369b9 139 return 0;
samux 3:9589afa4712e 140 }
samux 3:9589afa4712e 141
samux 3:9589afa4712e 142
samux 3:9589afa4712e 143 bool Websocket::connect() {
samux 3:9589afa4712e 144 char cmd[200];
samux 3:9589afa4712e 145
JohnnyK 11:9ee267dafdd7 146 while (_socket->connect(SocketAddress(host, port)) < 0) {
donatien 6:86e89a0369b9 147 ERR("Unable to connect to (%s) on port (%d)", host, port);
JohnnyK 10:91e447f28540 148 ThisThread::sleep_for(200ms);
donatien 6:86e89a0369b9 149 return false;
samux 3:9589afa4712e 150 }
samux 3:9589afa4712e 151
samux 3:9589afa4712e 152 // sent http header to upgrade to the ws protocol
donatien 6:86e89a0369b9 153 sprintf(cmd, "GET %s HTTP/1.1\r\n", path);
samux 3:9589afa4712e 154 write(cmd, strlen(cmd));
donatien 6:86e89a0369b9 155
donatien 6:86e89a0369b9 156 sprintf(cmd, "Host: %s:%d\r\n", host, port);
samux 3:9589afa4712e 157 write(cmd, strlen(cmd));
samux 3:9589afa4712e 158
samux 3:9589afa4712e 159 sprintf(cmd, "Upgrade: WebSocket\r\n");
samux 3:9589afa4712e 160 write(cmd, strlen(cmd));
samux 3:9589afa4712e 161
samux 3:9589afa4712e 162 sprintf(cmd, "Connection: Upgrade\r\n");
samux 3:9589afa4712e 163 write(cmd, strlen(cmd));
samux 3:9589afa4712e 164
samux 3:9589afa4712e 165 sprintf(cmd, "Sec-WebSocket-Key: L159VM0TWUzyDxwJEIEzjw==\r\n");
samux 3:9589afa4712e 166 write(cmd, strlen(cmd));
samux 3:9589afa4712e 167
samux 3:9589afa4712e 168 sprintf(cmd, "Sec-WebSocket-Version: 13\r\n\r\n");
samux 3:9589afa4712e 169 int ret = write(cmd, strlen(cmd));
samux 3:9589afa4712e 170 if (ret != strlen(cmd)) {
samux 3:9589afa4712e 171 close();
samux 3:9589afa4712e 172 ERR("Could not send request");
samux 3:9589afa4712e 173 return false;
samux 3:9589afa4712e 174 }
samux 3:9589afa4712e 175
samux 3:9589afa4712e 176 ret = read(cmd, 200, 100);
samux 3:9589afa4712e 177 if (ret < 0) {
samux 3:9589afa4712e 178 close();
samux 3:9589afa4712e 179 ERR("Could not receive answer\r\n");
samux 3:9589afa4712e 180 return false;
samux 3:9589afa4712e 181 }
samux 3:9589afa4712e 182
samux 3:9589afa4712e 183 cmd[ret] = '\0';
samux 3:9589afa4712e 184 DBG("recv: %s\r\n", cmd);
samux 3:9589afa4712e 185
samux 3:9589afa4712e 186 if ( strstr(cmd, "DdLWT/1JcX+nQFHebYP+rqEx5xI=") == NULL ) {
samux 3:9589afa4712e 187 ERR("Wrong answer from server, got \"%s\" instead\r\n", cmd);
samux 3:9589afa4712e 188 do {
samux 3:9589afa4712e 189 ret = read(cmd, 200, 100);
samux 3:9589afa4712e 190 if (ret < 0) {
samux 3:9589afa4712e 191 ERR("Could not receive answer\r\n");
samux 3:9589afa4712e 192 return false;
samux 3:9589afa4712e 193 }
samux 3:9589afa4712e 194 cmd[ret] = '\0';
samux 3:9589afa4712e 195 } while (ret > 0);
samux 3:9589afa4712e 196 close();
samux 3:9589afa4712e 197 return false;
samux 3:9589afa4712e 198 }
samux 3:9589afa4712e 199
donatien 6:86e89a0369b9 200 INFO("\r\nhost: %s\r\npath: %s\r\nport: %d\r\n\r\n", host, path, port);
samux 3:9589afa4712e 201 return true;
samux 3:9589afa4712e 202 }
samux 3:9589afa4712e 203
samux 4:466f90b7849a 204 int Websocket::sendLength(uint32_t len, char * msg) {
samux 3:9589afa4712e 205
samux 3:9589afa4712e 206 if (len < 126) {
samux 4:466f90b7849a 207 msg[0] = len | (1<<7);
samux 3:9589afa4712e 208 return 1;
samux 3:9589afa4712e 209 } else if (len < 65535) {
samux 4:466f90b7849a 210 msg[0] = 126 | (1<<7);
samux 4:466f90b7849a 211 msg[1] = (len >> 8) & 0xff;
samux 4:466f90b7849a 212 msg[2] = len & 0xff;
samux 3:9589afa4712e 213 return 3;
samux 3:9589afa4712e 214 } else {
samux 4:466f90b7849a 215 msg[0] = 127 | (1<<7);
samux 3:9589afa4712e 216 for (int i = 0; i < 8; i++) {
samux 4:466f90b7849a 217 msg[i+1] = (len >> i*8) & 0xff;
samux 3:9589afa4712e 218 }
samux 3:9589afa4712e 219 return 9;
samux 3:9589afa4712e 220 }
samux 3:9589afa4712e 221 }
samux 3:9589afa4712e 222
samux 3:9589afa4712e 223 int Websocket::readChar(char * pC, bool block) {
samux 3:9589afa4712e 224 return read(pC, 1, 1);
samux 3:9589afa4712e 225 }
samux 3:9589afa4712e 226
samux 4:466f90b7849a 227 int Websocket::sendOpcode(uint8_t opcode, char * msg) {
samux 4:466f90b7849a 228 msg[0] = 0x80 | (opcode & 0x0f);
samux 4:466f90b7849a 229 return 1;
samux 3:9589afa4712e 230 }
samux 3:9589afa4712e 231
samux 4:466f90b7849a 232 int Websocket::sendMask(char * msg) {
samux 3:9589afa4712e 233 for (int i = 0; i < 4; i++) {
samux 4:466f90b7849a 234 msg[i] = 0;
samux 3:9589afa4712e 235 }
samux 3:9589afa4712e 236 return 4;
samux 3:9589afa4712e 237 }
samux 3:9589afa4712e 238
samux 3:9589afa4712e 239 int Websocket::send(char * str) {
samux 4:466f90b7849a 240 char msg[strlen(str) + 15];
samux 4:466f90b7849a 241 int idx = 0;
samux 4:466f90b7849a 242 idx = sendOpcode(0x01, msg);
samux 4:466f90b7849a 243 idx += sendLength(strlen(str), msg + idx);
samux 4:466f90b7849a 244 idx += sendMask(msg + idx);
samux 4:466f90b7849a 245 memcpy(msg+idx, str, strlen(str));
samux 4:466f90b7849a 246 int res = write(msg, idx + strlen(str));
samux 3:9589afa4712e 247 return res;
samux 3:9589afa4712e 248 }
samux 3:9589afa4712e 249
samux 3:9589afa4712e 250
samux 3:9589afa4712e 251 bool Websocket::read(char * message) {
samux 3:9589afa4712e 252 int i = 0;
samux 3:9589afa4712e 253 uint32_t len_msg;
samux 3:9589afa4712e 254 char opcode = 0;
samux 3:9589afa4712e 255 char c;
samux 3:9589afa4712e 256 char mask[4] = {0, 0, 0, 0};
samux 3:9589afa4712e 257 bool is_masked = false;
samux 3:9589afa4712e 258 Timer tmr;
samux 3:9589afa4712e 259
samux 3:9589afa4712e 260 // read the opcode
samux 3:9589afa4712e 261 tmr.start();
samux 3:9589afa4712e 262 while (true) {
JohnnyK 10:91e447f28540 263 if (chrono::duration_cast<chrono::milliseconds>(tmr.elapsed_time()).count() > 3) {
samux 3:9589afa4712e 264 DBG("timeout ws\r\n");
samux 3:9589afa4712e 265 return false;
samux 3:9589afa4712e 266 }
mbed_official 9:efa2c147bee1 267
JohnnyK 11:9ee267dafdd7 268 _socket->set_timeout(1);
JohnnyK 11:9ee267dafdd7 269 if (_socket->recv(&opcode, 1) != 1) {
JohnnyK 11:9ee267dafdd7 270 _socket->set_timeout(2000);
donatien 5:bb09d7a6c92f 271 return false;
donatien 5:bb09d7a6c92f 272 }
samux 3:9589afa4712e 273
JohnnyK 11:9ee267dafdd7 274 _socket->set_timeout(2000);
samux 3:9589afa4712e 275
samux 3:9589afa4712e 276 if (opcode == 0x81)
samux 3:9589afa4712e 277 break;
samux 3:9589afa4712e 278 }
samux 3:9589afa4712e 279 DBG("opcode: 0x%X\r\n", opcode);
samux 3:9589afa4712e 280
samux 3:9589afa4712e 281 readChar(&c);
samux 3:9589afa4712e 282 len_msg = c & 0x7f;
samux 3:9589afa4712e 283 is_masked = c & 0x80;
samux 3:9589afa4712e 284 if (len_msg == 126) {
samux 3:9589afa4712e 285 readChar(&c);
samux 3:9589afa4712e 286 len_msg = c << 8;
samux 3:9589afa4712e 287 readChar(&c);
samux 3:9589afa4712e 288 len_msg += c;
samux 3:9589afa4712e 289 } else if (len_msg == 127) {
samux 3:9589afa4712e 290 len_msg = 0;
samux 3:9589afa4712e 291 for (int i = 0; i < 8; i++) {
samux 3:9589afa4712e 292 readChar(&c);
samux 3:9589afa4712e 293 len_msg += (c << (7-i)*8);
samux 3:9589afa4712e 294 }
samux 3:9589afa4712e 295 }
samux 3:9589afa4712e 296
samux 3:9589afa4712e 297 if (len_msg == 0) {
samux 3:9589afa4712e 298 return false;
samux 3:9589afa4712e 299 }
samux 3:9589afa4712e 300 DBG("length: %d\r\n", len_msg);
samux 3:9589afa4712e 301
samux 3:9589afa4712e 302 if (is_masked) {
mbed_official 9:efa2c147bee1 303 for (i = 0; i < 4; i++) {
mbed_official 9:efa2c147bee1 304 readChar(&c);
mbed_official 9:efa2c147bee1 305 mask[i] = c;
mbed_official 9:efa2c147bee1 306 }
samux 3:9589afa4712e 307 }
samux 3:9589afa4712e 308
samux 3:9589afa4712e 309 int nb = read(message, len_msg, len_msg);
samux 3:9589afa4712e 310 if (nb != len_msg)
samux 3:9589afa4712e 311 return false;
samux 3:9589afa4712e 312
samux 3:9589afa4712e 313 for (i = 0; i < len_msg; i++) {
samux 3:9589afa4712e 314 message[i] = message[i] ^ mask[i % 4];
samux 3:9589afa4712e 315 }
samux 3:9589afa4712e 316
samux 3:9589afa4712e 317 message[len_msg] = '\0';
samux 3:9589afa4712e 318
samux 3:9589afa4712e 319 return true;
samux 3:9589afa4712e 320 }
samux 3:9589afa4712e 321
samux 3:9589afa4712e 322 bool Websocket::close() {
samux 3:9589afa4712e 323
JohnnyK 11:9ee267dafdd7 324 int ret = _socket->close();
samux 3:9589afa4712e 325 if (ret < 0) {
samux 3:9589afa4712e 326 ERR("Could not disconnect");
samux 3:9589afa4712e 327 return false;
samux 3:9589afa4712e 328 }
samux 3:9589afa4712e 329 return true;
samux 3:9589afa4712e 330 }
samux 3:9589afa4712e 331
donatien 6:86e89a0369b9 332 char* Websocket::getPath() {
samux 3:9589afa4712e 333 return path;
samux 3:9589afa4712e 334 }
samux 3:9589afa4712e 335
samux 3:9589afa4712e 336 int Websocket::write(char * str, int len) {
samux 3:9589afa4712e 337 int res = 0, idx = 0;
samux 3:9589afa4712e 338 for (int j = 0; j < MAX_TRY_WRITE; j++) {
samux 3:9589afa4712e 339
JohnnyK 11:9ee267dafdd7 340 if ((res = _socket->send(str + idx, len - idx)) < 0)
samux 3:9589afa4712e 341 continue;
samux 3:9589afa4712e 342
samux 3:9589afa4712e 343 idx += res;
samux 3:9589afa4712e 344
samux 3:9589afa4712e 345 if (idx == len)
samux 3:9589afa4712e 346 return len;
samux 3:9589afa4712e 347 }
samux 3:9589afa4712e 348
JohnnyK 12:495c97f41a64 349 return (idx == 0) ? -1 : idx;
samux 3:9589afa4712e 350 }
samux 3:9589afa4712e 351
samux 3:9589afa4712e 352 int Websocket::read(char * str, int len, int min_len) {
samux 3:9589afa4712e 353 int res = 0, idx = 0;
samux 3:9589afa4712e 354
samux 3:9589afa4712e 355 for (int j = 0; j < MAX_TRY_WRITE; j++) {
samux 3:9589afa4712e 356
JohnnyK 11:9ee267dafdd7 357 if ((res = _socket->recv(str + idx, len - idx)) < 0)
mbed_official 9:efa2c147bee1 358 continue;
samux 3:9589afa4712e 359
samux 3:9589afa4712e 360 idx += res;
samux 3:9589afa4712e 361
samux 3:9589afa4712e 362 if (idx == len || (min_len != -1 && idx > min_len))
samux 3:9589afa4712e 363 return idx;
samux 3:9589afa4712e 364 }
samux 3:9589afa4712e 365
samux 3:9589afa4712e 366 return (idx == 0) ? -1 : idx;
JohnnyK 11:9ee267dafdd7 367 }