fork of WebSocketClient with some fixes
Fork of WebSocketClient by
Websocket.cpp@0:10b6eaafc2da, 2012-08-10 (annotated)
- Committer:
- samux
- Date:
- Fri Aug 10 11:08:44 2012 +0000
- Revision:
- 0:10b6eaafc2da
- Child:
- 1:de85cd4ec77b
websocket client with new official mbed tcp/ip stack
Who changed what in which revision?
User | Revision | Line number | New 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 | 0:10b6eaafc2da | 55 | char cmd[192]; |
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 | 0:10b6eaafc2da | 89 | ret = read(cmd, 512); |
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 | 0:10b6eaafc2da | 106 | ret = read(cmd, 512); |
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 | |
samux | 0:10b6eaafc2da | 295 | if (i == MAX_TRY_READ - 1 || !block) |
samux | 0:10b6eaafc2da | 296 | return (idx == 0) ? -1 : idx; |
samux | 0:10b6eaafc2da | 297 | } |
samux | 0:10b6eaafc2da | 298 | |
samux | 0:10b6eaafc2da | 299 | idx += res; |
samux | 0:10b6eaafc2da | 300 | } |
samux | 0:10b6eaafc2da | 301 | return idx; |
samux | 0:10b6eaafc2da | 302 | } |