Simple websocket client

Dependencies:   EthernetInterface

Dependents:   Websocket_Test

Fork of WebSocketClient by S5info_H14

Committer:
samux
Date:
Mon Aug 13 09:21:41 2012 +0000
Revision:
1:de85cd4ec77b
Parent:
0:10b6eaafc2da
Child:
2:b390f29fb5fa
avoid hardfault if accept received > 200 bytes

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 0:10b6eaafc2da 87 while(socket.receive(cmd, 1) != 1);
samux 0:10b6eaafc2da 88
samux 1:de85cd4ec77b 89 ret = read(cmd, 200);
samux 0:10b6eaafc2da 90 if(ret < 0)
samux 0:10b6eaafc2da 91 {
samux 0:10b6eaafc2da 92 close();
samux 0:10b6eaafc2da 93 printf("Could not receive answer\r\n");
samux 0:10b6eaafc2da 94 return false;
samux 0:10b6eaafc2da 95 }
samux 0:10b6eaafc2da 96
samux 0:10b6eaafc2da 97 cmd[ret] = '\0';
samux 0:10b6eaafc2da 98 #ifdef DEBUG
samux 0:10b6eaafc2da 99 printf("recv: %s\r\n", cmd);
samux 0:10b6eaafc2da 100 #endif
samux 0:10b6eaafc2da 101
samux 0:10b6eaafc2da 102 if( strstr(cmd, "DdLWT/1JcX+nQFHebYP+rqEx5xI=") == NULL )
samux 0:10b6eaafc2da 103 {
samux 0:10b6eaafc2da 104 printf("Wrong answer from server, got \"%s\" instead\r\n", cmd);
samux 0:10b6eaafc2da 105 do{
samux 1:de85cd4ec77b 106 ret = read(cmd, 200);
samux 0:10b6eaafc2da 107 if(ret < 0)
samux 0:10b6eaafc2da 108 {
samux 0:10b6eaafc2da 109 printf("Could not receive answer\r\n");
samux 0:10b6eaafc2da 110 return false;
samux 0:10b6eaafc2da 111 }
samux 0:10b6eaafc2da 112 cmd[ret] = '\0';
samux 0:10b6eaafc2da 113 printf("%s",cmd);
samux 0:10b6eaafc2da 114 } while(ret > 0);
samux 0:10b6eaafc2da 115 close();
samux 0:10b6eaafc2da 116 return false;
samux 0:10b6eaafc2da 117 }
samux 0:10b6eaafc2da 118
samux 0:10b6eaafc2da 119 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 120 return true;
samux 0:10b6eaafc2da 121 }
samux 0:10b6eaafc2da 122
samux 0:10b6eaafc2da 123 int Websocket::sendLength(uint32_t len) {
samux 0:10b6eaafc2da 124
samux 0:10b6eaafc2da 125 if (len < 126) {
samux 0:10b6eaafc2da 126 sendChar(len | (1<<7));
samux 0:10b6eaafc2da 127 return 1;
samux 0:10b6eaafc2da 128 } else if (len < 65535) {
samux 0:10b6eaafc2da 129 sendChar(126 | (1<<7));
samux 0:10b6eaafc2da 130 sendChar((len >> 8) & 0xff);
samux 0:10b6eaafc2da 131 sendChar(len & 0xff);
samux 0:10b6eaafc2da 132 return 3;
samux 0:10b6eaafc2da 133 } else {
samux 0:10b6eaafc2da 134 sendChar(127 | (1<<7));
samux 0:10b6eaafc2da 135 for (int i = 0; i < 8; i++) {
samux 0:10b6eaafc2da 136 sendChar((len >> i*8) & 0xff);
samux 0:10b6eaafc2da 137 }
samux 0:10b6eaafc2da 138 return 9;
samux 0:10b6eaafc2da 139 }
samux 0:10b6eaafc2da 140 }
samux 0:10b6eaafc2da 141
samux 0:10b6eaafc2da 142 int Websocket::sendChar(char c) {
samux 0:10b6eaafc2da 143 return write(&c, 1);
samux 0:10b6eaafc2da 144 }
samux 0:10b6eaafc2da 145
samux 0:10b6eaafc2da 146 int Websocket::readChar(char * pC, bool block)
samux 0:10b6eaafc2da 147 {
samux 0:10b6eaafc2da 148 return read(pC, 1, block);
samux 0:10b6eaafc2da 149 }
samux 0:10b6eaafc2da 150
samux 0:10b6eaafc2da 151 int Websocket::sendOpcode(uint8_t opcode) {
samux 0:10b6eaafc2da 152 return sendChar(0x80 | (opcode & 0x0f));
samux 0:10b6eaafc2da 153 }
samux 0:10b6eaafc2da 154
samux 0:10b6eaafc2da 155 int Websocket::sendMask() {
samux 0:10b6eaafc2da 156 for (int i = 0; i < 4; i++) {
samux 0:10b6eaafc2da 157 sendChar(0);
samux 0:10b6eaafc2da 158 }
samux 0:10b6eaafc2da 159 return 4;
samux 0:10b6eaafc2da 160 }
samux 0:10b6eaafc2da 161
samux 0:10b6eaafc2da 162 int Websocket::send(char * str) {
samux 0:10b6eaafc2da 163 sendOpcode(0x01);
samux 0:10b6eaafc2da 164 sendLength(strlen(str));
samux 0:10b6eaafc2da 165 sendMask();
samux 0:10b6eaafc2da 166 int res = write(str, strlen(str));
samux 0:10b6eaafc2da 167 return res;
samux 0:10b6eaafc2da 168 }
samux 0:10b6eaafc2da 169
samux 0:10b6eaafc2da 170
samux 0:10b6eaafc2da 171 bool Websocket::read(char * message) {
samux 0:10b6eaafc2da 172 int i = 0;
samux 0:10b6eaafc2da 173 uint32_t len_msg;
samux 0:10b6eaafc2da 174 char opcode = 0;
samux 0:10b6eaafc2da 175 char c;
samux 0:10b6eaafc2da 176 char mask[4] = {0, 0, 0, 0};
samux 0:10b6eaafc2da 177 bool is_masked = false;
samux 0:10b6eaafc2da 178 Timer tmr;
samux 0:10b6eaafc2da 179
samux 0:10b6eaafc2da 180 // read the opcode
samux 0:10b6eaafc2da 181 tmr.start();
samux 0:10b6eaafc2da 182 while (true) {
samux 0:10b6eaafc2da 183 if (tmr.read() > 3) {
samux 0:10b6eaafc2da 184 printf("timeout ws\r\n");
samux 0:10b6eaafc2da 185 return false;
samux 0:10b6eaafc2da 186 }
samux 0:10b6eaafc2da 187
samux 0:10b6eaafc2da 188 if (socket.receive(&opcode, 1) != 1) {
samux 0:10b6eaafc2da 189 return false;
samux 0:10b6eaafc2da 190 }
samux 0:10b6eaafc2da 191
samux 0:10b6eaafc2da 192 if (opcode == 0x81)
samux 0:10b6eaafc2da 193 break;
samux 0:10b6eaafc2da 194 }
samux 0:10b6eaafc2da 195 #ifdef DEBUG
samux 0:10b6eaafc2da 196 printf("opcode: 0x%X\r\n", opcode);
samux 0:10b6eaafc2da 197 #endif
samux 0:10b6eaafc2da 198
samux 0:10b6eaafc2da 199 readChar(&c);
samux 0:10b6eaafc2da 200 len_msg = c & 0x7f;
samux 0:10b6eaafc2da 201 is_masked = c & 0x80;
samux 0:10b6eaafc2da 202 if (len_msg == 126) {
samux 0:10b6eaafc2da 203 readChar(&c);
samux 0:10b6eaafc2da 204 len_msg = c << 8;
samux 0:10b6eaafc2da 205 readChar(&c);
samux 0:10b6eaafc2da 206 len_msg += c;
samux 0:10b6eaafc2da 207 } else if (len_msg == 127) {
samux 0:10b6eaafc2da 208 len_msg = 0;
samux 0:10b6eaafc2da 209 for (int i = 0; i < 8; i++) {
samux 0:10b6eaafc2da 210 readChar(&c);
samux 0:10b6eaafc2da 211 len_msg += (c << (7-i)*8);
samux 0:10b6eaafc2da 212 }
samux 0:10b6eaafc2da 213 }
samux 0:10b6eaafc2da 214
samux 0:10b6eaafc2da 215 if(len_msg == 0) {
samux 0:10b6eaafc2da 216 return false;
samux 0:10b6eaafc2da 217 }
samux 0:10b6eaafc2da 218 #ifdef DEBUG
samux 0:10b6eaafc2da 219 printf("length: %d\r\n", len_msg);
samux 0:10b6eaafc2da 220 #endif
samux 0:10b6eaafc2da 221 if (is_masked) {
samux 0:10b6eaafc2da 222 for (i = 0; i < 4; i++)
samux 0:10b6eaafc2da 223 readChar(&c);
samux 0:10b6eaafc2da 224 mask[i] = c;
samux 0:10b6eaafc2da 225 }
samux 0:10b6eaafc2da 226
samux 0:10b6eaafc2da 227 int nb = read(message, len_msg, false);
samux 0:10b6eaafc2da 228 if (nb != len_msg)
samux 0:10b6eaafc2da 229 return false;
samux 0:10b6eaafc2da 230
samux 0:10b6eaafc2da 231 for (i = 0; i < len_msg; i++) {
samux 0:10b6eaafc2da 232 message[i] = message[i] ^ mask[i % 4];
samux 0:10b6eaafc2da 233 }
samux 0:10b6eaafc2da 234
samux 0:10b6eaafc2da 235 message[len_msg] = '\0';
samux 0:10b6eaafc2da 236
samux 0:10b6eaafc2da 237 return true;
samux 0:10b6eaafc2da 238 }
samux 0:10b6eaafc2da 239
samux 0:10b6eaafc2da 240 bool Websocket::close() {
samux 0:10b6eaafc2da 241 if(!is_connected())
samux 0:10b6eaafc2da 242 return false;
samux 0:10b6eaafc2da 243
samux 0:10b6eaafc2da 244 int ret = socket.close();
samux 0:10b6eaafc2da 245 if (ret < 0)
samux 0:10b6eaafc2da 246 {
samux 0:10b6eaafc2da 247 printf("Could not disconnect");
samux 0:10b6eaafc2da 248 return false;
samux 0:10b6eaafc2da 249 }
samux 0:10b6eaafc2da 250 return true;
samux 0:10b6eaafc2da 251 }
samux 0:10b6eaafc2da 252
samux 0:10b6eaafc2da 253 bool Websocket::is_connected() {
samux 0:10b6eaafc2da 254 return socket.is_connected();
samux 0:10b6eaafc2da 255 }
samux 0:10b6eaafc2da 256
samux 0:10b6eaafc2da 257 std::string Websocket::getPath() {
samux 0:10b6eaafc2da 258 return path;
samux 0:10b6eaafc2da 259 }
samux 0:10b6eaafc2da 260
samux 0:10b6eaafc2da 261 int Websocket::write(char * str, int len, uint32_t timeout) {
samux 0:10b6eaafc2da 262 int res = 0, idx = 0;
samux 0:10b6eaafc2da 263 for (int j = 0; j < MAX_TRY_WRITE; j++) {
samux 0:10b6eaafc2da 264 if (idx == len)
samux 0:10b6eaafc2da 265 return len;
samux 0:10b6eaafc2da 266 for(int i = 0; i < MAX_TRY_WRITE; i++) {
samux 0:10b6eaafc2da 267 if ((res = socket.send_all(str + idx, len - idx)) != -1)
samux 0:10b6eaafc2da 268 break;
samux 0:10b6eaafc2da 269
samux 0:10b6eaafc2da 270 if (i == MAX_TRY_WRITE - 1)
samux 0:10b6eaafc2da 271 return -1;
samux 0:10b6eaafc2da 272 }
samux 0:10b6eaafc2da 273 idx += res;
samux 0:10b6eaafc2da 274 }
samux 0:10b6eaafc2da 275 return idx;
samux 0:10b6eaafc2da 276 }
samux 0:10b6eaafc2da 277
samux 0:10b6eaafc2da 278 int Websocket::read(char * str, int len, bool block) {
samux 0:10b6eaafc2da 279 int res = 0, idx = 0;
samux 0:10b6eaafc2da 280 for (int j = 0; j < MAX_TRY_READ; j++) {
samux 0:10b6eaafc2da 281
samux 0:10b6eaafc2da 282 if (idx == len)
samux 0:10b6eaafc2da 283 return len;
samux 0:10b6eaafc2da 284
samux 0:10b6eaafc2da 285 for(int i = 0; i < MAX_TRY_READ; i++) {
samux 0:10b6eaafc2da 286
samux 0:10b6eaafc2da 287 if (block) {
samux 0:10b6eaafc2da 288 if ((res = socket.receive_all(str + idx, len - idx)) != -1)
samux 0:10b6eaafc2da 289 break;
samux 0:10b6eaafc2da 290 } else {
samux 0:10b6eaafc2da 291 if ((res = socket.receive(str + idx, len - idx)) != -1)
samux 0:10b6eaafc2da 292 break;
samux 0:10b6eaafc2da 293 }
samux 0:10b6eaafc2da 294 if (i == MAX_TRY_READ - 1 || !block)
samux 0:10b6eaafc2da 295 return (idx == 0) ? -1 : idx;
samux 0:10b6eaafc2da 296 }
samux 0:10b6eaafc2da 297
samux 0:10b6eaafc2da 298 idx += res;
samux 0:10b6eaafc2da 299 }
samux 0:10b6eaafc2da 300 return idx;
samux 0:10b6eaafc2da 301 }