Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0cb4f90

Browse filesBrowse files
committed
Add initial WebSocketClient
1 parent 32895db commit 0cb4f90
Copy full SHA for 0cb4f90

File tree

Expand file treeCollapse file tree

3 files changed

+386
-0
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+386
-0
lines changed

‎ArduinoHttpClient.h

Copy file name to clipboardExpand all lines: ArduinoHttpClient.h
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
#define ArduinoHttpClient_h
77

88
#include "HttpClient.h"
9+
#include "WebSocketClient.h"
910

1011
#endif

‎WebSocketClient.cpp

Copy file name to clipboard
+295Lines changed: 295 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,295 @@
1+
// (c) Copyright Arduino. 2016
2+
// Released under Apache License, version 2.0
3+
4+
#include "b64.h"
5+
6+
#include "WebSocketClient.h"
7+
8+
WebSocketClient::WebSocketClient(Client& aClient, const char* aServerName, uint16_t aServerPort)
9+
: HttpClient(aClient, aServerName, aServerPort)
10+
{
11+
}
12+
13+
WebSocketClient::WebSocketClient(Client& aClient, const String& aServerName, uint16_t aServerPort)
14+
: HttpClient(aClient, aServerName, aServerPort)
15+
{
16+
}
17+
18+
WebSocketClient::WebSocketClient(Client& aClient, const IPAddress& aServerAddress, uint16_t aServerPort)
19+
: HttpClient(aClient, aServerAddress, aServerPort)
20+
{
21+
}
22+
23+
int WebSocketClient::begin(const char* aPath)
24+
{
25+
// start the GET request
26+
beginRequest();
27+
connectionKeepAlive();
28+
int status = get(aPath);
29+
30+
if (status == 0)
31+
{
32+
uint8_t randomKey[13];
33+
char base64RandomKey[21];
34+
35+
// create a random key for the connection upgrade
36+
for (int i = 0; i < (int)sizeof(randomKey); i++)
37+
{
38+
randomKey[i] = random(0x01, 0xff);
39+
}
40+
memset(base64RandomKey, 0x00, sizeof(base64RandomKey));
41+
b64_encode(randomKey, sizeof(randomKey), (unsigned char*)base64RandomKey, sizeof(base64RandomKey));
42+
43+
// start the connection upgrade sequence
44+
sendHeader("Upgrade", "websocket");
45+
sendHeader("Connection", "Upgrade");
46+
sendHeader("Sec-WebSocket-Key", base64RandomKey);
47+
sendHeader("Sec-WebSocket-Version", "13");
48+
endRequest();
49+
50+
status = responseStatusCode();
51+
52+
if (status > 0)
53+
{
54+
skipResponseHeaders();
55+
}
56+
}
57+
58+
// status code of 101 means success
59+
return (status == 101) ? 0 : status;
60+
}
61+
62+
int WebSocketClient::begin(const String& aPath)
63+
{
64+
return begin(aPath.c_str());
65+
}
66+
67+
int WebSocketClient::beginMessage(int aType)
68+
{
69+
iTxMessageType = (aType & 0xf);
70+
iTxSize = 0;
71+
72+
return 0;
73+
}
74+
75+
int WebSocketClient::endMessage()
76+
{
77+
// send FIN + the message type (opcode)
78+
HttpClient::write(0x80 | iTxMessageType);
79+
80+
// the message is masked (0x80)
81+
// send the length
82+
if (iTxSize < 126)
83+
{
84+
HttpClient::write(0x80 | (uint8_t)iTxSize);
85+
}
86+
else if (iTxSize < 0xffff)
87+
{
88+
HttpClient::write(0x80 | 126);
89+
HttpClient::write((iTxSize >> 8) & 0xff);
90+
HttpClient::write((iTxSize >> 0) & 0xff);
91+
}
92+
else
93+
{
94+
HttpClient::write(0x80 | 127);
95+
HttpClient::write((iTxSize >> 56) & 0xff);
96+
HttpClient::write((iTxSize >> 48) & 0xff);
97+
HttpClient::write((iTxSize >> 40) & 0xff);
98+
HttpClient::write((iTxSize >> 32) & 0xff);
99+
HttpClient::write((iTxSize >> 24) & 0xff);
100+
HttpClient::write((iTxSize >> 16) & 0xff);
101+
HttpClient::write((iTxSize >> 8) & 0xff);
102+
HttpClient::write((iTxSize >> 0) & 0xff);
103+
}
104+
105+
uint8_t maskKey[4];
106+
107+
// create a random mask for the data and send
108+
for (int i = 0; i < (int)sizeof(maskKey); i++)
109+
{
110+
maskKey[i] = random(0xff);
111+
}
112+
HttpClient::write(maskKey, sizeof(maskKey));
113+
114+
// mask the data and send
115+
for (int i = 0; i < (int)iTxSize; i++) {
116+
iTxBuffer[i] ^= maskKey[i % sizeof(maskKey)];
117+
}
118+
119+
int txSize = iTxSize;
120+
121+
iTxSize = 0;
122+
123+
return HttpClient::write(iTxBuffer, txSize);
124+
}
125+
126+
size_t WebSocketClient::write(uint8_t aByte)
127+
{
128+
return write(&aByte, sizeof(aByte));
129+
}
130+
131+
size_t WebSocketClient::write(const uint8_t *aBuffer, size_t aSize)
132+
{
133+
if (iState < eReadingBody)
134+
{
135+
// have not upgraded the connection yet
136+
return HttpClient::write(aBuffer, aSize);
137+
}
138+
139+
// check if the write size, fits in the buffer
140+
if ((iTxSize + aSize) > sizeof(iTxBuffer))
141+
{
142+
aSize = sizeof(iTxSize) - iTxSize;
143+
}
144+
145+
// copy data into the buffer
146+
memcpy(iTxBuffer + iTxSize, aBuffer, aSize);
147+
148+
iTxSize += aSize;
149+
150+
return aSize;
151+
}
152+
153+
int WebSocketClient::parseMessage()
154+
{
155+
// make sure 2 bytes (opcode + length)
156+
// are available
157+
if (HttpClient::available() < 2)
158+
{
159+
return 0;
160+
}
161+
162+
// read open code and length
163+
uint8_t opcode = HttpClient::read();
164+
int length = HttpClient::read();
165+
166+
if ((opcode & 0x0f) == 0)
167+
{
168+
// continuation, use previous opcode and update flags
169+
iRxOpCode |= opcode;
170+
}
171+
else
172+
{
173+
iRxOpCode = opcode;
174+
}
175+
176+
iRxMasked = (length & 0x80);
177+
length &= 0x7f;
178+
179+
// read the RX size
180+
if (length < 126)
181+
{
182+
iRxSize = length;
183+
}
184+
else if (length == 126)
185+
{
186+
iRxSize = (HttpClient::read() << 8) | HttpClient::read();
187+
}
188+
else
189+
{
190+
iRxSize = ((uint64_t)HttpClient::read() << 56) |
191+
((uint64_t)HttpClient::read() << 48) |
192+
((uint64_t)HttpClient::read() << 40) |
193+
((uint64_t)HttpClient::read() << 32) |
194+
((uint64_t)HttpClient::read() << 24) |
195+
((uint64_t)HttpClient::read() << 16) |
196+
((uint64_t)HttpClient::read() << 8) |
197+
(uint64_t)HttpClient::read();
198+
}
199+
200+
// read in the mask, if present
201+
if (iRxMasked)
202+
{
203+
for (int i = 0; i < (int)sizeof(iRxMaskKey); i++)
204+
{
205+
iRxMaskKey[i] = HttpClient::read();
206+
}
207+
}
208+
209+
iRxMaskIndex = 0;
210+
211+
return iRxSize;
212+
}
213+
214+
int WebSocketClient::messageType()
215+
{
216+
return (iRxOpCode & 0x0f);
217+
}
218+
219+
bool WebSocketClient::isFinal()
220+
{
221+
return ((iRxOpCode & 0x80) != 0);
222+
}
223+
224+
String WebSocketClient::readString()
225+
{
226+
int avail = available();
227+
String s;
228+
229+
if (avail > 0)
230+
{
231+
s.reserve(avail);
232+
233+
for (int i = 0; i < avail; i++)
234+
{
235+
s += (char)read();
236+
}
237+
}
238+
239+
return s;
240+
}
241+
242+
int WebSocketClient::available()
243+
{
244+
if (iState < eReadingBody)
245+
{
246+
return HttpClient::available();
247+
}
248+
249+
return iRxSize;
250+
}
251+
252+
int WebSocketClient::read()
253+
{
254+
byte b;
255+
256+
if (read(&b, sizeof(b)))
257+
{
258+
return b;
259+
}
260+
261+
return -1;
262+
}
263+
264+
int WebSocketClient::read(uint8_t *aBuffer, size_t aSize)
265+
{
266+
int readCount = HttpClient::read(aBuffer, aSize);
267+
268+
if (readCount > 0)
269+
{
270+
iRxSize -= readCount;
271+
272+
// unmask the RX data if needed
273+
if (iRxMasked)
274+
{
275+
for (int i = 0; i < (int)aSize; i++, iRxMaskIndex++) {
276+
aBuffer[i] ^= iRxMaskKey[iRxMaskIndex % sizeof(iRxMaskKey)];
277+
}
278+
}
279+
}
280+
281+
return readCount;
282+
}
283+
284+
int WebSocketClient::peek()
285+
{
286+
int p = HttpClient::peek();
287+
288+
if (p != -1 && iRxMasked)
289+
{
290+
// unmask the RX data if needed
291+
p = (uint8_t)p ^ iRxMaskKey[iRxMaskIndex % sizeof(iRxMaskKey)];
292+
}
293+
294+
return p;
295+
}

‎WebSocketClient.h

Copy file name to clipboard
+90Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// (c) Copyright Arduino. 2016
2+
// Released under Apache License, version 2.0
3+
4+
#ifndef WebSocketClient_h
5+
#define WebSocketClient_h
6+
7+
#include <Arduino.h>
8+
9+
#include "HttpClient.h"
10+
11+
static const int TYPE_CONTINUATION = 0x0;
12+
static const int TYPE_TEXT = 0x1;
13+
static const int TYPE_BINARY = 0x2;
14+
static const int TYPE_CONNECTION_CLOSE = 0x8;
15+
static const int TYPE_PING = 0x9;
16+
static const int TYPE_PONG = 0xa;
17+
18+
class WebSocketClient : public HttpClient
19+
{
20+
public:
21+
WebSocketClient(Client& aClient, const char* aServerName, uint16_t aServerPort = HttpClient::kHttpPort);
22+
WebSocketClient(Client& aClient, const String& aServerName, uint16_t aServerPort = HttpClient::kHttpPort);
23+
WebSocketClient(Client& aClient, const IPAddress& aServerAddress, uint16_t aServerPort = HttpClient::kHttpPort);
24+
25+
/** Start the Web Socket connection to the specified path
26+
@param aURLPath Path to use in request (optional, "/" is used by default)
27+
@return 0 if successful, else error
28+
*/
29+
int begin(const char* aPath = "/");
30+
int begin(const String& aPath);
31+
32+
/** Begin to send a message of type (TYPE_TEXT or TYPE_BINARY)
33+
Use the write or Stream API's to set message content, followed by endMessage
34+
to complete the message.
35+
@param aURLPath Path to use in request
36+
@return 0 if successful, else error
37+
*/
38+
int beginMessage(int aType);
39+
40+
/** Completes sending of a message started by beginMessage
41+
@return 0 if successful, else error
42+
*/
43+
int endMessage();
44+
45+
/** Try to parse an incoming messages
46+
@return 0 if no message available, else size of parsed message
47+
*/
48+
int parseMessage();
49+
50+
/** Returns type of current parsed message
51+
@return type of current parsedMessage (TYPE_TEXT or TYPE_BINARY)
52+
*/
53+
int messageType();
54+
55+
/** Returns if the current message is the final chunk of a split
56+
message
57+
@return true for final message, false otherwise
58+
*/
59+
bool isFinal();
60+
61+
/** Read the current messages as a string
62+
@return current message as a string
63+
*/
64+
String readString();
65+
66+
// Inherited from Print
67+
virtual size_t write(uint8_t aByte);
68+
virtual size_t write(const uint8_t *aBuffer, size_t aSize);
69+
// Inherited from Stream
70+
virtual int available();
71+
/** Read the next byte from the server.
72+
@return Byte read or -1 if there are no bytes available.
73+
*/
74+
virtual int read();
75+
virtual int read(uint8_t *buf, size_t size);
76+
virtual int peek();
77+
78+
private:
79+
uint8_t iTxMessageType;
80+
uint8_t iTxBuffer[128];
81+
uint64_t iTxSize;
82+
83+
uint8_t iRxOpCode;
84+
uint64_t iRxSize;
85+
bool iRxMasked;
86+
int iRxMaskIndex;
87+
uint8_t iRxMaskKey[4];
88+
};
89+
90+
#endif

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.