Skip to content

Commit b5dbecc

Browse files
committed
first steps to Client
1 parent cf54518 commit b5dbecc

File tree

6 files changed

+394
-10
lines changed

6 files changed

+394
-10
lines changed

src/WebSockets.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ extern "C" {
3838
* @param reasonLen
3939
*/
4040
void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) {
41+
DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code);
4142
if(client->status == WSC_CONNECTED && code) {
4243
if(reason) {
4344
sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen);
@@ -60,6 +61,10 @@ void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * rea
6061
*/
6162
void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length) {
6263

64+
if(!client->tcp.connected()) {
65+
return;
66+
}
67+
6368
uint8_t buffer[16] = { 0 };
6469
uint8_t i = 0;
6570

src/WebSockets.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ typedef enum {
5252
WSC_CONNECTED
5353
} WSclientsStatus_t;
5454

55+
typedef enum {
56+
WStype_ERROR,
57+
WStype_DISCONNECTED,
58+
WStype_CONNECTED,
59+
WStype_TEXT,
60+
WStype_BIN
61+
} WStype_t;
62+
5563
typedef enum {
5664
WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
5765
WSop_text = 0x01, ///< %x1 denotes a text frame

src/WebSocketsClient.cpp

Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,323 @@
2525
#include "WebSockets.h"
2626
#include "WebSocketsClient.h"
2727

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

Comments
 (0)