|
25 | 25 | #include "WebSockets.h"
|
26 | 26 | #include "WebSocketsClient.h"
|
27 | 27 |
|
| 28 | +WebSocketsClient::WebSocketsClient() { |
| 29 | + _cbEvent = NULL; |
| 30 | + _client.num = 0; |
| 31 | + |
| 32 | +} |
| 33 | + |
| 34 | +WebSocketsClient::~WebSocketsClient() { |
| 35 | + disconnect(); |
| 36 | +} |
| 37 | + |
| 38 | +/** |
| 39 | + * calles to init the Websockets server |
| 40 | + */ |
| 41 | +void WebSocketsClient::begin(const char *host, uint16_t port, const char * url) { |
| 42 | + _host = host; |
| 43 | + _port = port; |
| 44 | + |
| 45 | + _client.status = WSC_NOT_CONNECTED; |
| 46 | + _client.cUrl = url; |
| 47 | + _client.cIsUpgrade = false; |
| 48 | + _client.cIsWebsocket = true; |
| 49 | + _client.cKey = ""; |
| 50 | + _client.cProtocol = ""; |
| 51 | + _client.cExtensions = ""; |
| 52 | + _client.cVersion = 0; |
| 53 | +} |
| 54 | + |
| 55 | +void WebSocketsClient::begin(String host, uint16_t port, String url) { |
| 56 | + begin(host.c_str(), port, url.c_str()); |
| 57 | +} |
| 58 | + |
| 59 | +/** |
| 60 | + * called in arduino loop |
| 61 | + */ |
| 62 | +void WebSocketsClient::loop(void) { |
| 63 | + if(!clientIsConnected(&_client)) { |
| 64 | + if(_client.tcp.connect(_host.c_str(), _port)) { |
| 65 | + DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u\n", _host.c_str(), _port); |
| 66 | + |
| 67 | + _client.tcp.setNoDelay(true); |
| 68 | + // set Timeout for readBytesUntil and readStringUntil |
| 69 | + _client.tcp.setTimeout(WEBSOCKETS_TCP_TIMEOUT); |
| 70 | + _client.status = WSC_HEADER; |
| 71 | + } else { |
| 72 | + DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port); |
| 73 | + delay(10); //some litle delay to not flood the server |
| 74 | + } |
| 75 | + } else { |
| 76 | + handleClientData(); |
| 77 | + } |
| 78 | +} |
| 79 | + |
| 80 | +/** |
| 81 | + * set callback function |
| 82 | + * @param cbEvent WebSocketServerEvent |
| 83 | + */ |
| 84 | +void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) { |
| 85 | + _cbEvent = cbEvent; |
| 86 | +} |
| 87 | + |
| 88 | +/** |
| 89 | + * send text data to client |
| 90 | + * @param num uint8_t client id |
| 91 | + * @param payload uint8_t * |
| 92 | + * @param length size_t |
| 93 | + */ |
| 94 | +void WebSocketsClient::sendTXT(uint8_t * payload, size_t length) { |
| 95 | + if(length == 0) { |
| 96 | + length = strlen((const char *) payload); |
| 97 | + } |
| 98 | + if(clientIsConnected(&_client)) { |
| 99 | + sendFrame(&_client, WSop_text, payload, length); |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +void WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) { |
| 104 | + sendTXT((uint8_t *) payload, length); |
| 105 | +} |
| 106 | + |
| 107 | +void WebSocketsClient::sendTXT(char * payload, size_t length) { |
| 108 | + sendTXT((uint8_t *) payload, length); |
| 109 | +} |
| 110 | + |
| 111 | +void WebSocketsClient::sendTXT(const char * payload, size_t length) { |
| 112 | + sendTXT((uint8_t *) payload, length); |
| 113 | +} |
| 114 | + |
| 115 | +void WebSocketsClient::sendTXT(String payload) { |
| 116 | + sendTXT((uint8_t *) payload.c_str(), payload.length()); |
| 117 | +} |
| 118 | + |
| 119 | +/** |
| 120 | + * send binary data to client |
| 121 | + * @param num uint8_t client id |
| 122 | + * @param payload uint8_t * |
| 123 | + * @param length size_t |
| 124 | + */ |
| 125 | +void WebSocketsClient::sendBIN(uint8_t * payload, size_t length) { |
| 126 | + if(clientIsConnected(&_client)) { |
| 127 | + sendFrame(&_client, WSop_binary, payload, length); |
| 128 | + } |
| 129 | +} |
| 130 | + |
| 131 | +void WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) { |
| 132 | + sendBIN((uint8_t *) payload, length); |
| 133 | +} |
| 134 | + |
| 135 | +/** |
| 136 | + * disconnect one client |
| 137 | + * @param num uint8_t client id |
| 138 | + */ |
| 139 | +void WebSocketsClient::disconnect(void) { |
| 140 | + if(clientIsConnected(&_client)) { |
| 141 | + WebSockets::clientDisconnect(&_client, 1000); |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +//################################################################################# |
| 146 | +//################################################################################# |
| 147 | +//################################################################################# |
| 148 | + |
| 149 | +/** |
| 150 | + * |
| 151 | + * @param client WSclient_t * ptr to the client struct |
| 152 | + * @param opcode WSopcode_t |
| 153 | + * @param payload uint8_t * |
| 154 | + * @param lenght size_t |
| 155 | + */ |
| 156 | +void WebSocketsClient::messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) { |
| 157 | + WStype_t type = WStype_ERROR; |
| 158 | + |
| 159 | + switch(opcode) { |
| 160 | + case WSop_text: |
| 161 | + type = WStype_TEXT; |
| 162 | + break; |
| 163 | + case WSop_binary: |
| 164 | + type = WStype_BIN; |
| 165 | + break; |
| 166 | + } |
| 167 | + |
| 168 | + if(_cbEvent) { |
| 169 | + _cbEvent(type, payload, lenght); |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +/** |
| 174 | + * Disconnect an client |
| 175 | + * @param client WSclient_t * ptr to the client struct |
| 176 | + */ |
| 177 | +void WebSocketsClient::clientDisconnect(WSclient_t * client) { |
| 178 | + |
| 179 | + if(client->tcp) { |
| 180 | + client->tcp.flush(); |
| 181 | + client->tcp.stop(); |
| 182 | + } |
| 183 | + |
| 184 | + client->cKey = ""; |
| 185 | + client->cProtocol = ""; |
| 186 | + client->cVersion = 0; |
| 187 | + client->cIsUpgrade = false; |
| 188 | + client->cIsWebsocket = false; |
| 189 | + |
| 190 | + client->status = WSC_NOT_CONNECTED; |
| 191 | + |
| 192 | + DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n"); |
| 193 | + |
| 194 | + if(_cbEvent) { |
| 195 | + _cbEvent(WStype_DISCONNECTED, NULL, 0); |
| 196 | + } |
| 197 | +} |
| 198 | + |
| 199 | +/** |
| 200 | + * get client state |
| 201 | + * @param client WSclient_t * ptr to the client struct |
| 202 | + * @return true = conneted |
| 203 | + */ |
| 204 | +bool WebSocketsClient::clientIsConnected(WSclient_t * client) { |
| 205 | + |
| 206 | + if(client->status != WSC_NOT_CONNECTED && client->tcp.connected()) { |
| 207 | + return true; |
| 208 | + } |
| 209 | + |
| 210 | + if(client->status != WSC_NOT_CONNECTED) { |
| 211 | + // cleanup |
| 212 | + clientDisconnect(&_client); |
| 213 | + } |
| 214 | + return false; |
| 215 | +} |
| 216 | + |
| 217 | +/** |
| 218 | + * Handel incomming data from Client |
| 219 | + */ |
| 220 | +void WebSocketsClient::handleClientData(void) { |
| 221 | + int len = _client.tcp.available(); |
| 222 | + if(len > 0) { |
| 223 | + switch(_client.status) { |
| 224 | + case WSC_HEADER: |
| 225 | + handleHeader(&_client); |
| 226 | + break; |
| 227 | + case WSC_CONNECTED: |
| 228 | + WebSockets::handleWebsocket(&_client); |
| 229 | + break; |
| 230 | + default: |
| 231 | + WebSockets::clientDisconnect(&_client, 1002); |
| 232 | + break; |
| 233 | + } |
| 234 | + } |
| 235 | + delay(0); |
| 236 | +} |
| 237 | + |
| 238 | +/** |
| 239 | + * handle the WebSocket header reading |
| 240 | + * @param client WSclient_t * ptr to the client struct |
| 241 | + */ |
| 242 | +void WebSocketsClient::handleHeader(WSclient_t * client) { |
| 243 | + |
| 244 | + String headerLine = client->tcp.readStringUntil('\n'); |
| 245 | + headerLine.trim(); // remove \r |
| 246 | + |
| 247 | + if(headerLine.length() > 0) { |
| 248 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine.c_str()); |
| 249 | + |
| 250 | + // websocket request starts allways with GET see rfc6455 |
| 251 | + if(headerLine.startsWith("GET ")) { |
| 252 | + // cut URL out |
| 253 | + client->cUrl = headerLine.substring(4, headerLine.indexOf(' ', 4)); |
| 254 | + } else if(headerLine == "Connection: Upgrade") { |
| 255 | + client->cIsUpgrade = true; |
| 256 | + } else if(headerLine == "Upgrade: websocket") { |
| 257 | + client->cIsWebsocket = true; |
| 258 | + } else if(headerLine.startsWith("Sec-WebSocket-Version: ")) { |
| 259 | + // 23 = lenght of "Sec-WebSocket-Version: " |
| 260 | + client->cVersion = headerLine.substring(23).toInt(); |
| 261 | + } else if(headerLine.startsWith("Sec-WebSocket-Key: ")) { |
| 262 | + // 19 = lenght of "Sec-WebSocket-Key: " |
| 263 | + client->cKey = headerLine.substring(19); |
| 264 | + client->cKey.trim(); // see rfc6455 |
| 265 | + } else if(headerLine.startsWith("Sec-WebSocket-Protocol: ")) { |
| 266 | + // 24 = lenght of "Sec-WebSocket-Protocol: " |
| 267 | + client->cProtocol = headerLine.substring(24); |
| 268 | + } else if(headerLine.startsWith("Sec-WebSocket-Extensions: ")) { |
| 269 | + // 26 = lenght of "Sec-WebSocket-Extensions: " |
| 270 | + client->cExtensions = headerLine.substring(26); |
| 271 | + } |
| 272 | + |
| 273 | + } else { |
| 274 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n"); |
| 275 | + |
| 276 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n", client->cUrl.c_str()); |
| 277 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n", client->cIsUpgrade); |
| 278 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n", client->cIsWebsocket); |
| 279 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n", client->cKey.c_str()); |
| 280 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str()); |
| 281 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str()); |
| 282 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion); |
| 283 | + |
| 284 | + bool ok = (client->cIsUpgrade && client->cIsWebsocket); |
| 285 | + |
| 286 | + if(ok) { |
| 287 | + if(client->cUrl.length() == 0) { |
| 288 | + ok = false; |
| 289 | + } |
| 290 | + if(client->cKey.length() == 0) { |
| 291 | + ok = false; |
| 292 | + } |
| 293 | + if(client->cVersion != 13) { |
| 294 | + ok = false; |
| 295 | + } |
| 296 | + } |
| 297 | + |
| 298 | + if(ok) { |
| 299 | + |
| 300 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection incomming.\n"); |
| 301 | + |
| 302 | + // generate Sec-WebSocket-Accept key |
| 303 | + String sKey = acceptKey(client->cKey); |
| 304 | + |
| 305 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - sKey: %s\n", sKey.c_str()); |
| 306 | + |
| 307 | + client->status = WSC_CONNECTED; |
| 308 | + |
| 309 | + client->tcp.write("HTTP/1.1 101 Switching Protocols\r\n" |
| 310 | + "Server: ESP8266-WebSocketsClient\r\n" |
| 311 | + "Upgrade: websocket\r\n" |
| 312 | + "Connection: Upgrade\r\n" |
| 313 | + "Sec-WebSocket-Version: 13\r\n" |
| 314 | + "Sec-WebSocket-Accept: "); |
| 315 | + client->tcp.write(sKey.c_str(), sKey.length()); |
| 316 | + client->tcp.write("\r\n"); |
| 317 | + |
| 318 | + if(client->cProtocol.length() > 0) { |
| 319 | + // todo add api to set Protocol of Server |
| 320 | + client->tcp.write("Sec-WebSocket-Protocol: esp8266\r\n"); |
| 321 | + } |
| 322 | + |
| 323 | + // header end |
| 324 | + client->tcp.write("\r\n"); |
| 325 | + |
| 326 | + // send ping |
| 327 | + WebSockets::sendFrame(client, WSop_ping); |
| 328 | + |
| 329 | + if(_cbEvent) { |
| 330 | + _cbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); |
| 331 | + } |
| 332 | + |
| 333 | + } else { |
| 334 | + DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); |
| 335 | + client->tcp.write("HTTP/1.1 400 Bad Request\r\n" |
| 336 | + "Server: ESP8266-WebSocketsClient\r\n" |
| 337 | + "Content-Type: text/plain\r\n" |
| 338 | + "Content-Length: 32\r\n" |
| 339 | + "Connection: close\r\n" |
| 340 | + "Sec-WebSocket-Version: 13\r\n" |
| 341 | + "\r\n" |
| 342 | + "This is a Websocket server only!"); |
| 343 | + clientDisconnect(client); |
| 344 | + } |
| 345 | + } |
| 346 | +} |
| 347 | + |
0 commit comments