/** * get client state * @param client WSclient_t * ptr to the client struct * @return true = connected */ bool WebSocketsServer::clientIsConnected(WSclient_t * client) { if(!client->tcp) { return false; } if(client->tcp->connected()) { if(client->status != WSC_NOT_CONNECTED) { return true; } } else { // client lost if(client->status != WSC_NOT_CONNECTED) { DEBUG_WEBSOCKETS("[WS-Server][%d] client connection lost.\n", client->num); // do cleanup clientDisconnect(client); } } if(client->tcp) { // do cleanup DEBUG_WEBSOCKETS("[WS-Server][%d] client list cleanup.\n", client->num); clientDisconnect(client); } return false; }
/** * send the WebSocket header to Server * @param client WSclient_t * ptr to the client struct */ void WebSocketsClient::sendHeader(WSclient_t * client) { DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n"); uint8_t randomKey[16] = { 0 }; for(uint8_t i = 0; i < sizeof(randomKey); i++) { randomKey[i] = random(0xFF); } client->cKey = base64_encode(&randomKey[0], 16); #ifndef NODEBUG_WEBSOCKETS unsigned long start = micros(); #endif String handshake = "GET " + client->cUrl + " HTTP/1.1\r\n" "Host: " + _host + ":" + _port + "\r\n" "Connection: Upgrade\r\n" "Upgrade: websocket\r\n" "Origin: file://\r\n" "User-Agent: arduino-WebSocket-Client\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Key: " + client->cKey + "\r\n"; if(client->cProtocol.length() > 0) { handshake += "Sec-WebSocket-Protocol: " + client->cProtocol + "\r\n"; } if(client->cExtensions.length() > 0) { handshake += "Sec-WebSocket-Extensions: " + client->cExtensions + "\r\n"; } if(client->base64Authorization.length() > 0) { handshake += "Authorization: Basic " + client->base64Authorization + "\r\n"; } if(client->plainAuthorization.length() > 0) { handshake += "Authorization: " + client->plainAuthorization + "\r\n"; } handshake += "\r\n"; client->tcp->write(handshake.c_str(), handshake.length()); #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); #endif DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%uus).\n", (micros() - start)); }
/** * handle new client connection * @param client */ bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) { WSclient_t * client; // search free list entry for client for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { client = &_clients[i]; // state is not connected or tcp connection is lost if(!clientIsConnected(client)) { client->tcp = TCPclient; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) client->isSSL = false; client->tcp->setNoDelay(true); #endif #if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) // set Timeout for readBytesUntil and readStringUntil client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); #endif client->status = WSC_HEADER; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) IPAddress ip = client->tcp->remoteIP(); DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]); #else DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num); #endif #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->tcp->onDisconnect(std::bind([](WebSocketsServer * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp; if(*sl == obj) { client->status = WSC_NOT_CONNECTED; *sl = NULL; } return true; }, this, std::placeholders::_1, client)); client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); #endif return true; break; } } return false; }
/** * get client state * @param client WSclient_t * ptr to the client struct * @return true = conneted */ bool WebSocketsClient::clientIsConnected(WSclient_t * client) { if(!client->tcp) { return false; } if(client->tcp->connected()) { if(client->status != WSC_NOT_CONNECTED) { return true; } } else { // client lost if(client->status != WSC_NOT_CONNECTED) { DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n"); // do cleanup clientDisconnect(client); } } if(client->tcp) { // do cleanup clientDisconnect(client); } return false; }
void WebSocketsClient::connectedCb() { DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port); #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) _client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool { DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num); client->status = WSC_NOT_CONNECTED; client->tcp = NULL; // reconnect c->asyncConnect(); return true; }, this, std::placeholders::_1, &_client)); #endif _client.status = WSC_HEADER; #if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) // set Timeout for readBytesUntil and readStringUntil _client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); #endif #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) _client.tcp->setNoDelay(true); if(_client.isSSL && _fingerprint.length()) { if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) { DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n"); WebSockets::clientDisconnect(&_client, 1000); return; } } #endif // send Header to Server sendHeader(&_client); }
/** * called in arduino loop */ void WebSocketsClient::loop(void) { if(!clientIsConnected(&_client)) { #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) if(_client.isSSL) { DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n"); if(_client.ssl) { delete _client.ssl; _client.ssl = NULL; _client.tcp = NULL; } _client.ssl = new WiFiClientSecure(); _client.tcp = _client.ssl; } else { DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n"); if(_client.tcp) { delete _client.tcp; _client.tcp = NULL; } _client.tcp = new WiFiClient(); } #else _client.tcp = new WEBSOCKETS_NETWORK_CLASS(); #endif if(!_client.tcp) { DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); return; } if(_client.tcp->connect(_host.c_str(), _port)) { connectedCb(); } else { connectFailedCb(); delay(10); //some little delay to not flood the server } } else { handleClientData(); } }
/** * Handle incomming Connection Request */ void WebSocketsServer::handleNewClients(void) { WSclient_t * client; while(_server->hasClient()) { bool ok = false; // search free list entry for client for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { client = &_clients[i]; // state is not connected or tcp connection is lost if(!clientIsConnected(client)) { // store new connection client->tcp = _server->available(); #ifdef ESP8266 client->tcp.setNoDelay(true); #endif // set Timeout for readBytesUntil and readStringUntil client->tcp.setTimeout(WEBSOCKETS_TCP_TIMEOUT); client->status = WSC_HEADER; IPAddress ip = client->tcp.remoteIP(); DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]); ok = true; break; } } if(!ok) { // no free space to handle client WiFiClient tcpClient = _server->available(); IPAddress ip = tcpClient.remoteIP(); DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); tcpClient.stop(); } #ifdef ESP8266 delay(0); #endif } }
void WebSocketsClient::asyncConnect() { DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n"); AsyncClient * tcpclient = new AsyncClient(); if(!tcpclient) { DEBUG_WEBSOCKETS("[WS-Client] creating AsyncClient class failed!\n"); return; } tcpclient->onDisconnect([](void *obj, AsyncClient* c) { c->free(); delete c; }); tcpclient->onConnect(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { ws->_client.tcp = new AsyncTCPbuffer(tcp); if(!ws->_client.tcp) { DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n"); ws->connectFailedCb(); return; } ws->connectedCb(); }, this, std::placeholders::_2)); tcpclient->onError(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) { ws->connectFailedCb(); // reconnect ws->asyncConnect(); }, this, std::placeholders::_2)); if(!tcpclient->connect(_host.c_str(), _port)) { connectFailedCb(); delete tcpclient; } }
/** * Handle incoming Connection Request */ void WebSocketsServer::handleNewClients(void) { #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) while(_server->hasClient()) { #endif bool ok = false; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) // store new connection WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); #else WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); #endif if(!tcpClient) { DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); return; } ok = newClient(tcpClient); if(!ok) { // no free space to handle client #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) IPAddress ip = tcpClient->remoteIP(); DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]); #else DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n"); #endif tcpClient->stop(); } #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) delay(0); } #endif }
/** * Disconnect an client * @param client WSclient_t * ptr to the client struct */ void WebSocketsServer::clientDisconnect(WSclient_t * client) { #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) if(client->isSSL && client->ssl) { if(client->ssl->connected()) { client->ssl->flush(); client->ssl->stop(); } delete client->ssl; client->ssl = NULL; client->tcp = NULL; } #endif if(client->tcp) { if(client->tcp->connected()) { #if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) client->tcp->flush(); #endif client->tcp->stop(); } #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->status = WSC_NOT_CONNECTED; #else delete client->tcp; #endif client->tcp = NULL; } client->cUrl = ""; client->cKey = ""; client->cProtocol = ""; client->cVersion = 0; client->cIsUpgrade = false; client->cIsWebsocket = false; client->cWsRXsize = 0; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->cHttpLine = ""; #endif client->status = WSC_NOT_CONNECTED; DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num); runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0); }
/** * send the WebSocket header to Server * @param client WSclient_t * ptr to the client struct */ void WebSocketsClient::sendHeader(WSclient_t * client) { DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n"); uint8_t randomKey[16] = { 0 }; for(uint8_t i = 0; i < sizeof(randomKey); i++) { randomKey[i] = random(0xFF); } client->cKey = base64_encode(&randomKey[0], 16); #ifndef NODEBUG_WEBSOCKETS unsigned long start = micros(); #endif String handshake = "GET " + client->cUrl + " HTTP/1.1\r\n" "Host: " + _host + "\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "User-Agent: arduino-WebSocket-Client\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Protocol: arduino\r\n" "Sec-WebSocket-Key: " + client->cKey + "\r\n"; if(client->cExtensions.length() > 0) { handshake += "Sec-WebSocket-Extensions: " + client->cExtensions + "\r\n"; } handshake += "\r\n"; client->tcp->write(handshake.c_str(), handshake.length()); DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%uus).\n", (micros() - start)); }
/** * Disconnect an client * @param client WSclient_t * ptr to the client struct */ void WebSocketsClient::clientDisconnect(WSclient_t * client) { bool event = false; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) if(client->isSSL && client->ssl) { if(client->ssl->connected()) { client->ssl->flush(); client->ssl->stop(); } event = true; delete client->ssl; client->ssl = NULL; client->tcp = NULL; } #endif if(client->tcp) { if(client->tcp->connected()) { #if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) client->tcp->flush(); #endif client->tcp->stop(); } event = true; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->status = WSC_NOT_CONNECTED; #else delete client->tcp; #endif client->tcp = NULL; } client->cCode = 0; client->cKey = ""; client->cAccept = ""; client->cProtocol = ""; client->cVersion = 0; client->cIsUpgrade = false; client->cIsWebsocket = false; client->status = WSC_NOT_CONNECTED; DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n"); if(event) { runCbEvent(WStype_DISCONNECTED, NULL, 0); } }
/** * called to initialize the Websocket server */ void WebSocketsServer::begin(void) { WSclient_t * client; // init client storage for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { client = &_clients[i]; client->num = i; client->status = WSC_NOT_CONNECTED; client->tcp = NULL; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) client->isSSL = false; client->ssl = NULL; #endif client->cUrl = ""; client->cCode = 0; client->cKey = ""; client->cProtocol = ""; client->cVersion = 0; client->cIsUpgrade = false; client->cIsWebsocket = false; client->base64Authorization = ""; client->cWsRXsize = 0; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->cHttpLine = ""; #endif } #ifdef ESP8266 randomSeed(RANDOM_REG32); #else // TODO find better seed randomSeed(millis()); #endif _server->begin(); DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n"); }
/** * Disconnect an client * @param client WSclient_t * ptr to the client struct */ void WebSocketsServer::clientDisconnect(WSclient_t * client) { if(client->tcp) { client->tcp.flush(); client->tcp.stop(); } client->cUrl = ""; client->cKey = ""; client->cProtocol = ""; client->cVersion = 0; client->cIsUpgrade = false; client->cIsWebsocket = false; client->status = WSC_NOT_CONNECTED; DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num); if(_cbEvent) { _cbEvent(client->num, WStype_DISCONNECTED, NULL, 0); } }
/** * handle the WebSocket header reading * @param client WSclient_t * ptr to the client struct */ void WebSocketsServer::handleHeader(WSclient_t * client) { String headerLine = client->tcp.readStringUntil('\n'); headerLine.trim(); // remove \r if(headerLine.length() > 0) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine.c_str()); // websocket request starts allways with GET see rfc6455 if(headerLine.startsWith("GET ")) { // cut URL out client->cUrl = headerLine.substring(4, headerLine.indexOf(' ', 4)); } else if(headerLine.startsWith("Connection: ")) { // 12 = lenght of "Connection: " if(headerLine.indexOf("Upgrade", 12)) { client->cIsUpgrade = true; } } else if(headerLine.startsWith("Upgrade: ")) { // 9 = lenght of "Upgrade: " String low = headerLine.substring(9); low.toLowerCase(); if(low == "websocket") { client->cIsWebsocket = true; } } else if(headerLine.startsWith("Sec-WebSocket-Version: ")) { // 23 = lenght of "Sec-WebSocket-Version: " client->cVersion = headerLine.substring(23).toInt(); } else if(headerLine.startsWith("Sec-WebSocket-Key: ")) { // 19 = lenght of "Sec-WebSocket-Key: " client->cKey = headerLine.substring(19); client->cKey.trim(); // see rfc6455 } else if(headerLine.startsWith("Sec-WebSocket-Protocol: ")) { // 24 = lenght of "Sec-WebSocket-Protocol: " client->cProtocol = headerLine.substring(24); } else if(headerLine.startsWith("Sec-WebSocket-Extensions: ")) { // 26 = lenght of "Sec-WebSocket-Extensions: " client->cExtensions = headerLine.substring(26); } } else { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion); bool ok = (client->cIsUpgrade && client->cIsWebsocket); if(ok) { if(client->cUrl.length() == 0) { ok = false; } if(client->cKey.length() == 0) { ok = false; } if(client->cVersion != 13) { ok = false; } } if(ok) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incomming.\n", client->num); // generate Sec-WebSocket-Accept key String sKey = acceptKey(client->cKey); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - sKey: %s\n", client->num, sKey.c_str()); client->status = WSC_CONNECTED; client->tcp.write("HTTP/1.1 101 Switching Protocols\r\n" "Server: arduino-WebSocketsServer\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Accept: "); client->tcp.write(sKey.c_str(), sKey.length()); client->tcp.write("\r\n"); if(client->cProtocol.length() > 0) { // todo add api to set Protocol of Server client->tcp.write("Sec-WebSocket-Protocol: arduino\r\n"); } // header end client->tcp.write("\r\n"); // send ping WebSockets::sendFrame(client, WSop_ping); if(_cbEvent) { _cbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); } } else { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num); client->tcp.write("HTTP/1.1 400 Bad Request\r\n" "Server: arduino-WebSocket-Server\r\n" "Content-Type: text/plain\r\n" "Content-Length: 32\r\n" "Connection: close\r\n" "Sec-WebSocket-Version: 13\r\n" "\r\n" "This is a Websocket server only!"); clientDisconnect(client); } } }
/** * handles http header reading for WebSocket upgrade * @param client WSclient_t * ///< pointer to the client struct * @param headerLine String ///< the header being read / processed */ void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) { headerLine->trim(); // remove \r if(headerLine->length() > 0) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str()); // websocket requests always start with GET see rfc6455 if(headerLine->startsWith("GET ")) { // cut URL out client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4)); //reset non-websocket http header validation state for this client client->cHttpHeadersValid = true; client->cMandatoryHeadersCount = 0; } else if(headerLine->indexOf(':')) { String headerName = headerLine->substring(0, headerLine->indexOf(':')); String headerValue = headerLine->substring(headerLine->indexOf(':') + 2); if(headerName.equalsIgnoreCase("Connection")) { headerValue.toLowerCase(); if(headerValue.indexOf("upgrade") >= 0) { client->cIsUpgrade = true; } } else if(headerName.equalsIgnoreCase("Upgrade")) { if(headerValue.equalsIgnoreCase("websocket")) { client->cIsWebsocket = true; } } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) { client->cVersion = headerValue.toInt(); } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Key")) { client->cKey = headerValue; client->cKey.trim(); // see rfc6455 } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) { client->cProtocol = headerValue; } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) { client->cExtensions = headerValue; } else if(headerName.equalsIgnoreCase("Authorization")) { client->base64Authorization = headerValue; } else { client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue); if (_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) { client->cMandatoryHeadersCount++; } } } else { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); } (*headerLine) = ""; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine))); #endif } else { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - base64Authorization: %s\n", client->num, client->base64Authorization.c_str()); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cHttpHeadersValid: %d\n", client->num, client->cHttpHeadersValid); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cMandatoryHeadersCount: %d\n", client->num, client->cMandatoryHeadersCount); bool ok = (client->cIsUpgrade && client->cIsWebsocket); if(ok) { if(client->cUrl.length() == 0) { ok = false; } if(client->cKey.length() == 0) { ok = false; } if(client->cVersion != 13) { ok = false; } if(!client->cHttpHeadersValid) { ok = false; } if (client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) { ok = false; } } if(_base64Authorization.length() > 0) { if(client->base64Authorization.length() > 0) { String auth = "Basic "; auth += _base64Authorization; if(auth != client->base64Authorization) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num); handleAuthorizationFailed(client); return; } } else { ok = false; } } if(ok) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num); // generate Sec-WebSocket-Accept key String sKey = acceptKey(client->cKey); DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - sKey: %s\n", client->num, sKey.c_str()); client->status = WSC_CONNECTED; client->tcp->write("HTTP/1.1 101 Switching Protocols\r\n" "Server: arduino-WebSocketsServer\r\n" "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Version: 13\r\n" "Sec-WebSocket-Accept: "); client->tcp->write(sKey.c_str(), sKey.length()); if(_origin.length() > 0) { String origin = "\r\nAccess-Control-Allow-Origin: "; origin += _origin; origin += "\r\n"; client->tcp->write(origin.c_str(), origin.length()); } if(client->cProtocol.length() > 0) { String protocol = "\r\nSec-WebSocket-Protocol: "; protocol += _protocol; protocol += "\r\n"; client->tcp->write(protocol.c_str(), protocol.length()); } else { client->tcp->write("\r\n"); } // header end client->tcp->write("\r\n"); headerDone(client); // send ping WebSockets::sendFrame(client, WSop_ping); runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); } else { handleNonWebsocketConnection(client); } } }
void WebSocketsClient::connectFailedCb() { DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port); }
/** * called in arduino loop */ void WebSocketsClient::loop(void) { if(!clientIsConnected(&_client)) { #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) if(_client.isSSL) { DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n"); if(_client.ssl) { delete _client.ssl; _client.ssl = NULL; _client.tcp = NULL; } _client.ssl = new WiFiClientSecure(); _client.tcp = _client.ssl; } else { DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n"); if(_client.tcp) { delete _client.tcp; _client.tcp = NULL; } _client.tcp = new WiFiClient(); } #else _client.tcp = new WEBSOCKETS_NETWORK_CLASS(); #endif if(!_client.tcp) { DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!"); return; } if(_client.tcp->connect(_host.c_str(), _port)) { DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port); _client.status = WSC_HEADER; // set Timeout for readBytesUntil and readStringUntil _client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT); #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) _client.tcp->setNoDelay(true); if(_client.isSSL && _fingerprint.length()) { if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) { DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n"); WebSockets::clientDisconnect(&_client, 1000); return; } } #endif // send Header to Server sendHeader(&_client); } else { DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\n", _host.c_str(), _port); delay(10); //some litle delay to not flood the server } } else { handleClientData(); } }
/** * handle the WebSocket header reading * @param client WSclient_t * ptr to the client struct */ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) { headerLine->trim(); // remove \r if(headerLine->length() > 0) { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str()); if(headerLine->startsWith("HTTP/1.")) { // "HTTP/1.1 101 Switching Protocols" client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt(); } else if(headerLine->indexOf(':')) { String headerName = headerLine->substring(0, headerLine->indexOf(':')); String headerValue = headerLine->substring(headerLine->indexOf(':') + 2); if(headerName.equalsIgnoreCase("Connection")) { if(headerValue.equalsIgnoreCase("upgrade")) { client->cIsUpgrade = true; } } else if(headerName.equalsIgnoreCase("Upgrade")) { if(headerValue.equalsIgnoreCase("websocket")) { client->cIsWebsocket = true; } } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Accept")) { client->cAccept = headerValue; client->cAccept.trim(); // see rfc6455 } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Protocol")) { client->cProtocol = headerValue; } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Extensions")) { client->cExtensions = headerValue; } else if(headerName.equalsIgnoreCase("Sec-WebSocket-Version")) { client->cVersion = headerValue.toInt(); } } else { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str()); } (*headerLine) = ""; #if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine))); #endif } else { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n"); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n"); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n", client->cUrl.c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n", client->cKey.c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n"); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cCode: %d\n", client->cCode); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n", client->cIsUpgrade); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n", client->cIsWebsocket); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cAccept: %s\n", client->cAccept.c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str()); DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion); bool ok = (client->cIsUpgrade && client->cIsWebsocket); if(ok) { switch(client->cCode) { case 101: ///< Switching Protocols break; case 403: ///< Forbidden // todo handle login default: ///< Server dont unterstand requrst ok = false; DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode); clientDisconnect(client); break; } } if(ok) { if(client->cAccept.length() == 0) { ok = false; } else { // generate Sec-WebSocket-Accept key for check String sKey = acceptKey(client->cKey); if(sKey != client->cAccept) { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n"); ok = false; } } } if(ok) { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n"); headerDone(client); runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length()); } else { DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n"); client->tcp->write("This is a webSocket client!"); clientDisconnect(client); } } }