Simple websocket client

Dependencies:   EthernetInterface

Dependents:   Websocket_Test

Fork of WebSocketClient by S5info_H14

Committer:
samux
Date:
Mon Aug 13 09:37:35 2012 +0000
Revision:
2:b390f29fb5fa
Parent:
1:de85cd4ec77b
Child:
3:9589afa4712e
less timeout to read the first character received

Who changed what in which revision?

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