Simple websocket client

Dependents:   Websocket_Ethernet_HelloWorld Websocket_Wifly_HelloWorld RPC_Wifly_HelloWorld RPC_Ethernet_HelloWorld ... more

Committer:
donatien
Date:
Wed Aug 29 13:49:48 2012 +0000
Revision:
5:bb09d7a6c92f
Parent:
4:466f90b7849a
Child:
6:86e89a0369b9
Added warning if disconnected by remote server

Who changed what in which revision?

UserRevisionLine numberNew contents of line
samux 3:9589afa4712e 1 #include "Websocket.h"
samux 3:9589afa4712e 2 #include <string>
samux 3:9589afa4712e 3
samux 3:9589afa4712e 4 #define MAX_TRY_WRITE 50
samux 3:9589afa4712e 5 #define MAX_TRY_READ 30
samux 3:9589afa4712e 6
samux 3:9589afa4712e 7 //Debug is disabled by default
samux 3:9589afa4712e 8 #if 0
samux 3:9589afa4712e 9 #define DBG(x, ...) std::printf("[WebSocket : DBG]"x"\r\n", ##__VA_ARGS__);
samux 3:9589afa4712e 10 #define WARN(x, ...) std::printf("[WebSocket : WARN]"x"\r\n", ##__VA_ARGS__);
samux 3:9589afa4712e 11 #define ERR(x, ...) std::printf("[WebSocket : ERR]"x"\r\n", ##__VA_ARGS__);
samux 3:9589afa4712e 12 #else
samux 3:9589afa4712e 13 #define DBG(x, ...)
samux 3:9589afa4712e 14 #define WARN(x, ...)
samux 3:9589afa4712e 15 #define ERR(x, ...)
samux 3:9589afa4712e 16 #endif
samux 3:9589afa4712e 17
samux 3:9589afa4712e 18 #define INFO(x, ...) printf("[WebSocket : INFO]"x"\r\n", ##__VA_ARGS__);
samux 3:9589afa4712e 19
samux 3:9589afa4712e 20 Websocket::Websocket(char * url) {
samux 3:9589afa4712e 21 fillFields(url);
samux 3:9589afa4712e 22 socket.set_blocking(false, 400);
samux 3:9589afa4712e 23 }
samux 3:9589afa4712e 24
samux 3:9589afa4712e 25 void Websocket::fillFields(char * url) {
samux 3:9589afa4712e 26 char *res = NULL;
samux 3:9589afa4712e 27 char *res1 = NULL;
samux 3:9589afa4712e 28
samux 3:9589afa4712e 29 char buf[50];
samux 3:9589afa4712e 30 strcpy(buf, url);
samux 3:9589afa4712e 31
samux 3:9589afa4712e 32 res = strtok(buf, ":");
samux 3:9589afa4712e 33 if (strcmp(res, "ws")) {
samux 3:9589afa4712e 34 DBG("\r\nFormat printfor: please use: \"ws://ip-or-domain[:port]/path\"\r\n\r\n");
samux 3:9589afa4712e 35 } else {
samux 3:9589afa4712e 36 //ip_domain and port
samux 3:9589afa4712e 37 res = strtok(NULL, "/");
samux 3:9589afa4712e 38
samux 3:9589afa4712e 39 //path
samux 3:9589afa4712e 40 res1 = strtok(NULL, " ");
samux 3:9589afa4712e 41 if (res1 != NULL) {
samux 3:9589afa4712e 42 path = res1;
samux 3:9589afa4712e 43 }
samux 3:9589afa4712e 44
samux 3:9589afa4712e 45 //ip_domain
samux 3:9589afa4712e 46 res = strtok(res, ":");
samux 3:9589afa4712e 47
samux 3:9589afa4712e 48 //port
samux 3:9589afa4712e 49 res1 = strtok(NULL, " ");
samux 3:9589afa4712e 50 if (res1 != NULL) {
samux 3:9589afa4712e 51 port = res1;
samux 3:9589afa4712e 52 } else {
samux 3:9589afa4712e 53 port = "80";
samux 3:9589afa4712e 54 }
samux 3:9589afa4712e 55
samux 3:9589afa4712e 56 if (res != NULL) {
samux 3:9589afa4712e 57 ip_domain = res;
samux 3:9589afa4712e 58 }
samux 3:9589afa4712e 59 }
samux 3:9589afa4712e 60 }
samux 3:9589afa4712e 61
samux 3:9589afa4712e 62
samux 3:9589afa4712e 63 bool Websocket::connect() {
samux 3:9589afa4712e 64 char cmd[200];
samux 3:9589afa4712e 65
samux 3:9589afa4712e 66 while (socket.connect(ip_domain.c_str(), atoi(port.c_str())) < 0) {
samux 3:9589afa4712e 67 ERR("Unable to connect to (%s) on port (%d)\r\n", ip_domain.c_str(), atoi(port.c_str()));
samux 3:9589afa4712e 68 wait(0.2);
samux 3:9589afa4712e 69 }
samux 3:9589afa4712e 70
samux 3:9589afa4712e 71 // sent http header to upgrade to the ws protocol
samux 3:9589afa4712e 72 sprintf(cmd, "GET /%s HTTP/1.1\r\n", path.c_str());
samux 3:9589afa4712e 73 write(cmd, strlen(cmd));
samux 3:9589afa4712e 74
samux 3:9589afa4712e 75 sprintf(cmd, "Host: %s:%s\r\n", ip_domain.c_str(), port.c_str());
samux 3:9589afa4712e 76 write(cmd, strlen(cmd));
samux 3:9589afa4712e 77
samux 3:9589afa4712e 78 sprintf(cmd, "Upgrade: WebSocket\r\n");
samux 3:9589afa4712e 79 write(cmd, strlen(cmd));
samux 3:9589afa4712e 80
samux 3:9589afa4712e 81 sprintf(cmd, "Connection: Upgrade\r\n");
samux 3:9589afa4712e 82 write(cmd, strlen(cmd));
samux 3:9589afa4712e 83
samux 3:9589afa4712e 84 sprintf(cmd, "Sec-WebSocket-Key: L159VM0TWUzyDxwJEIEzjw==\r\n");
samux 3:9589afa4712e 85 write(cmd, strlen(cmd));
samux 3:9589afa4712e 86
samux 3:9589afa4712e 87 sprintf(cmd, "Sec-WebSocket-Version: 13\r\n\r\n");
samux 3:9589afa4712e 88 int ret = write(cmd, strlen(cmd));
samux 3:9589afa4712e 89 if (ret != strlen(cmd)) {
samux 3:9589afa4712e 90 close();
samux 3:9589afa4712e 91 ERR("Could not send request");
samux 3:9589afa4712e 92 return false;
samux 3:9589afa4712e 93 }
samux 3:9589afa4712e 94
samux 3:9589afa4712e 95 ret = read(cmd, 200, 100);
samux 3:9589afa4712e 96 if (ret < 0) {
samux 3:9589afa4712e 97 close();
samux 3:9589afa4712e 98 ERR("Could not receive answer\r\n");
samux 3:9589afa4712e 99 return false;
samux 3:9589afa4712e 100 }
samux 3:9589afa4712e 101
samux 3:9589afa4712e 102 cmd[ret] = '\0';
samux 3:9589afa4712e 103 DBG("recv: %s\r\n", cmd);
samux 3:9589afa4712e 104
samux 3:9589afa4712e 105 if ( strstr(cmd, "DdLWT/1JcX+nQFHebYP+rqEx5xI=") == NULL ) {
samux 3:9589afa4712e 106 ERR("Wrong answer from server, got \"%s\" instead\r\n", cmd);
samux 3:9589afa4712e 107 do {
samux 3:9589afa4712e 108 ret = read(cmd, 200, 100);
samux 3:9589afa4712e 109 if (ret < 0) {
samux 3:9589afa4712e 110 ERR("Could not receive answer\r\n");
samux 3:9589afa4712e 111 return false;
samux 3:9589afa4712e 112 }
samux 3:9589afa4712e 113 cmd[ret] = '\0';
samux 3:9589afa4712e 114 printf("%s",cmd);
samux 3:9589afa4712e 115 } while (ret > 0);
samux 3:9589afa4712e 116 close();
samux 3:9589afa4712e 117 return false;
samux 3:9589afa4712e 118 }
samux 3:9589afa4712e 119
samux 3:9589afa4712e 120 INFO("\r\nip_domain: %s\r\npath: /%s\r\nport: %s\r\n\r\n", ip_domain.c_str(), path.c_str(), port.c_str());
samux 3:9589afa4712e 121 return true;
samux 3:9589afa4712e 122 }
samux 3:9589afa4712e 123
samux 4:466f90b7849a 124 int Websocket::sendLength(uint32_t len, char * msg) {
samux 3:9589afa4712e 125
samux 3:9589afa4712e 126 if (len < 126) {
samux 4:466f90b7849a 127 msg[0] = len | (1<<7);
samux 3:9589afa4712e 128 return 1;
samux 3:9589afa4712e 129 } else if (len < 65535) {
samux 4:466f90b7849a 130 msg[0] = 126 | (1<<7);
samux 4:466f90b7849a 131 msg[1] = (len >> 8) & 0xff;
samux 4:466f90b7849a 132 msg[2] = len & 0xff;
samux 3:9589afa4712e 133 return 3;
samux 3:9589afa4712e 134 } else {
samux 4:466f90b7849a 135 msg[0] = 127 | (1<<7);
samux 3:9589afa4712e 136 for (int i = 0; i < 8; i++) {
samux 4:466f90b7849a 137 msg[i+1] = (len >> i*8) & 0xff;
samux 3:9589afa4712e 138 }
samux 3:9589afa4712e 139 return 9;
samux 3:9589afa4712e 140 }
samux 3:9589afa4712e 141 }
samux 3:9589afa4712e 142
samux 3:9589afa4712e 143 int Websocket::readChar(char * pC, bool block) {
samux 3:9589afa4712e 144 return read(pC, 1, 1);
samux 3:9589afa4712e 145 }
samux 3:9589afa4712e 146
samux 4:466f90b7849a 147 int Websocket::sendOpcode(uint8_t opcode, char * msg) {
samux 4:466f90b7849a 148 msg[0] = 0x80 | (opcode & 0x0f);
samux 4:466f90b7849a 149 return 1;
samux 3:9589afa4712e 150 }
samux 3:9589afa4712e 151
samux 4:466f90b7849a 152 int Websocket::sendMask(char * msg) {
samux 3:9589afa4712e 153 for (int i = 0; i < 4; i++) {
samux 4:466f90b7849a 154 msg[i] = 0;
samux 3:9589afa4712e 155 }
samux 3:9589afa4712e 156 return 4;
samux 3:9589afa4712e 157 }
samux 3:9589afa4712e 158
samux 3:9589afa4712e 159 int Websocket::send(char * str) {
samux 4:466f90b7849a 160 char msg[strlen(str) + 15];
samux 4:466f90b7849a 161 int idx = 0;
samux 4:466f90b7849a 162 idx = sendOpcode(0x01, msg);
samux 4:466f90b7849a 163 idx += sendLength(strlen(str), msg + idx);
samux 4:466f90b7849a 164 idx += sendMask(msg + idx);
samux 4:466f90b7849a 165 memcpy(msg+idx, str, strlen(str));
samux 4:466f90b7849a 166 int res = write(msg, idx + strlen(str));
samux 3:9589afa4712e 167 return res;
samux 3:9589afa4712e 168 }
samux 3:9589afa4712e 169
samux 3:9589afa4712e 170
samux 3:9589afa4712e 171 bool Websocket::read(char * message) {
samux 3:9589afa4712e 172 int i = 0;
samux 3:9589afa4712e 173 uint32_t len_msg;
samux 3:9589afa4712e 174 char opcode = 0;
samux 3:9589afa4712e 175 char c;
samux 3:9589afa4712e 176 char mask[4] = {0, 0, 0, 0};
samux 3:9589afa4712e 177 bool is_masked = false;
samux 3:9589afa4712e 178 Timer tmr;
samux 3:9589afa4712e 179
samux 3:9589afa4712e 180 // read the opcode
samux 3:9589afa4712e 181 tmr.start();
samux 3:9589afa4712e 182 while (true) {
samux 3:9589afa4712e 183 if (tmr.read() > 3) {
samux 3:9589afa4712e 184 DBG("timeout ws\r\n");
samux 3:9589afa4712e 185 return false;
samux 3:9589afa4712e 186 }
donatien 5:bb09d7a6c92f 187
donatien 5:bb09d7a6c92f 188 if(!socket.is_connected())
donatien 5:bb09d7a6c92f 189 {
donatien 5:bb09d7a6c92f 190 WARN("Connection was closed by server");
donatien 5:bb09d7a6c92f 191 return false;
donatien 5:bb09d7a6c92f 192 }
samux 3:9589afa4712e 193
samux 3:9589afa4712e 194 socket.set_blocking(false, 1);
samux 3:9589afa4712e 195 if (socket.receive(&opcode, 1) != 1) {
samux 3:9589afa4712e 196 socket.set_blocking(false, 2000);
samux 3:9589afa4712e 197 return false;
samux 3:9589afa4712e 198 }
samux 3:9589afa4712e 199
samux 3:9589afa4712e 200 socket.set_blocking(false, 2000);
samux 3:9589afa4712e 201
samux 3:9589afa4712e 202 if (opcode == 0x81)
samux 3:9589afa4712e 203 break;
samux 3:9589afa4712e 204 }
samux 3:9589afa4712e 205 DBG("opcode: 0x%X\r\n", opcode);
samux 3:9589afa4712e 206
samux 3:9589afa4712e 207 readChar(&c);
samux 3:9589afa4712e 208 len_msg = c & 0x7f;
samux 3:9589afa4712e 209 is_masked = c & 0x80;
samux 3:9589afa4712e 210 if (len_msg == 126) {
samux 3:9589afa4712e 211 readChar(&c);
samux 3:9589afa4712e 212 len_msg = c << 8;
samux 3:9589afa4712e 213 readChar(&c);
samux 3:9589afa4712e 214 len_msg += c;
samux 3:9589afa4712e 215 } else if (len_msg == 127) {
samux 3:9589afa4712e 216 len_msg = 0;
samux 3:9589afa4712e 217 for (int i = 0; i < 8; i++) {
samux 3:9589afa4712e 218 readChar(&c);
samux 3:9589afa4712e 219 len_msg += (c << (7-i)*8);
samux 3:9589afa4712e 220 }
samux 3:9589afa4712e 221 }
samux 3:9589afa4712e 222
samux 3:9589afa4712e 223 if (len_msg == 0) {
samux 3:9589afa4712e 224 return false;
samux 3:9589afa4712e 225 }
samux 3:9589afa4712e 226 DBG("length: %d\r\n", len_msg);
samux 3:9589afa4712e 227
samux 3:9589afa4712e 228 if (is_masked) {
samux 3:9589afa4712e 229 for (i = 0; i < 4; i++)
samux 3:9589afa4712e 230 readChar(&c);
samux 3:9589afa4712e 231 mask[i] = c;
samux 3:9589afa4712e 232 }
samux 3:9589afa4712e 233
samux 3:9589afa4712e 234 int nb = read(message, len_msg, len_msg);
samux 3:9589afa4712e 235 if (nb != len_msg)
samux 3:9589afa4712e 236 return false;
samux 3:9589afa4712e 237
samux 3:9589afa4712e 238 for (i = 0; i < len_msg; i++) {
samux 3:9589afa4712e 239 message[i] = message[i] ^ mask[i % 4];
samux 3:9589afa4712e 240 }
samux 3:9589afa4712e 241
samux 3:9589afa4712e 242 message[len_msg] = '\0';
samux 3:9589afa4712e 243
samux 3:9589afa4712e 244 return true;
samux 3:9589afa4712e 245 }
samux 3:9589afa4712e 246
samux 3:9589afa4712e 247 bool Websocket::close() {
samux 3:9589afa4712e 248 if (!is_connected())
samux 3:9589afa4712e 249 return false;
samux 3:9589afa4712e 250
samux 3:9589afa4712e 251 int ret = socket.close();
samux 3:9589afa4712e 252 if (ret < 0) {
samux 3:9589afa4712e 253 ERR("Could not disconnect");
samux 3:9589afa4712e 254 return false;
samux 3:9589afa4712e 255 }
samux 3:9589afa4712e 256 return true;
samux 3:9589afa4712e 257 }
samux 3:9589afa4712e 258
samux 3:9589afa4712e 259 bool Websocket::is_connected() {
samux 3:9589afa4712e 260 return socket.is_connected();
samux 3:9589afa4712e 261 }
samux 3:9589afa4712e 262
samux 3:9589afa4712e 263 std::string Websocket::getPath() {
samux 3:9589afa4712e 264 return path;
samux 3:9589afa4712e 265 }
samux 3:9589afa4712e 266
samux 3:9589afa4712e 267 int Websocket::write(char * str, int len) {
samux 3:9589afa4712e 268 int res = 0, idx = 0;
samux 3:9589afa4712e 269
samux 3:9589afa4712e 270 for (int j = 0; j < MAX_TRY_WRITE; j++) {
donatien 5:bb09d7a6c92f 271
donatien 5:bb09d7a6c92f 272 if(!socket.is_connected())
donatien 5:bb09d7a6c92f 273 {
donatien 5:bb09d7a6c92f 274 WARN("Connection was closed by server");
donatien 5:bb09d7a6c92f 275 break;
donatien 5:bb09d7a6c92f 276 }
samux 3:9589afa4712e 277
samux 3:9589afa4712e 278 if ((res = socket.send_all(str + idx, len - idx)) == -1)
samux 3:9589afa4712e 279 continue;
samux 3:9589afa4712e 280
samux 3:9589afa4712e 281 idx += res;
samux 3:9589afa4712e 282
samux 3:9589afa4712e 283 if (idx == len)
samux 3:9589afa4712e 284 return len;
samux 3:9589afa4712e 285 }
samux 3:9589afa4712e 286
samux 3:9589afa4712e 287 return (idx == 0) ? -1 : idx;
samux 3:9589afa4712e 288 }
samux 3:9589afa4712e 289
samux 3:9589afa4712e 290 int Websocket::read(char * str, int len, int min_len) {
samux 3:9589afa4712e 291 int res = 0, idx = 0;
samux 3:9589afa4712e 292
samux 3:9589afa4712e 293 for (int j = 0; j < MAX_TRY_WRITE; j++) {
samux 3:9589afa4712e 294
samux 3:9589afa4712e 295 if ((res = socket.receive_all(str + idx, len - idx)) == -1)
samux 3:9589afa4712e 296 continue;
samux 3:9589afa4712e 297
samux 3:9589afa4712e 298 idx += res;
samux 3:9589afa4712e 299
samux 3:9589afa4712e 300 if (idx == len || (min_len != -1 && idx > min_len))
samux 3:9589afa4712e 301 return idx;
samux 3:9589afa4712e 302 }
samux 3:9589afa4712e 303
samux 3:9589afa4712e 304 return (idx == 0) ? -1 : idx;
samux 3:9589afa4712e 305 }