Add binary frames support and tests, to close feature request #38.
This commit is contained in:
+43
-13
@@ -74,6 +74,9 @@
|
||||
|
||||
#include "easywsclient.hpp"
|
||||
|
||||
using easywsclient::Callback_Imp;
|
||||
using easywsclient::BytesCallback_Imp;
|
||||
|
||||
namespace { // private module-only namespace
|
||||
|
||||
socket_t hostname_connect(const std::string& hostname, int port) {
|
||||
@@ -112,14 +115,16 @@ class _DummyWebSocket : public easywsclient::WebSocket
|
||||
public:
|
||||
void poll(int timeout) { }
|
||||
void send(const std::string& message) { }
|
||||
void sendBinary(const std::string& message) { }
|
||||
void sendBinary(const std::vector<uint8_t>& message) { }
|
||||
void sendPing() { }
|
||||
void close() { }
|
||||
void _dispatch(Callback & callable) { }
|
||||
readyStateValues getReadyState() const { return CLOSED; }
|
||||
void _dispatch(Callback_Imp & callable) { }
|
||||
void _dispatchBinary(BytesCallback_Imp& callable) { }
|
||||
};
|
||||
|
||||
|
||||
|
||||
class _RealWebSocket : public easywsclient::WebSocket
|
||||
{
|
||||
public:
|
||||
@@ -242,7 +247,22 @@ class _RealWebSocket : public easywsclient::WebSocket
|
||||
// lambda:
|
||||
//template<class Callable>
|
||||
//void dispatch(Callable callable)
|
||||
virtual void _dispatch(WebSocket::Callback & callable) {
|
||||
virtual void _dispatch(Callback_Imp & callable) {
|
||||
struct CallbackAdapter : public BytesCallback_Imp
|
||||
// Adapt void(const std::string<uint8_t>&) to void(const std::string&)
|
||||
{
|
||||
Callback_Imp& callable;
|
||||
CallbackAdapter(Callback_Imp& callable) : callable(callable) { }
|
||||
void operator()(const std::vector<uint8_t>& message) {
|
||||
std::string stringMessage(message.begin(), message.end());
|
||||
callable(stringMessage);
|
||||
}
|
||||
};
|
||||
CallbackAdapter bytesCallback(callable);
|
||||
_dispatchBinary(bytesCallback);
|
||||
}
|
||||
|
||||
virtual void _dispatchBinary(BytesCallback_Imp & callable) {
|
||||
// TODO: consider acquiring a lock on rxbuf...
|
||||
while (true) {
|
||||
wsheader_type ws;
|
||||
@@ -293,14 +313,15 @@ class _RealWebSocket : public easywsclient::WebSocket
|
||||
|
||||
// We got a whole message, now do something with it:
|
||||
if (false) { }
|
||||
else if (ws.opcode == wsheader_type::TEXT_FRAME
|
||||
else if (
|
||||
ws.opcode == wsheader_type::TEXT_FRAME
|
||||
|| ws.opcode == wsheader_type::BINARY_FRAME
|
||||
|| ws.opcode == wsheader_type::CONTINUATION
|
||||
) {
|
||||
if (ws.mask) { for (size_t i = 0; i != ws.N; ++i) { rxbuf[i+ws.header_size] ^= ws.masking_key[i&0x3]; } }
|
||||
receivedData.insert(receivedData.end(), rxbuf.begin()+ws.header_size, rxbuf.begin()+ws.header_size+(size_t)ws.N);// just feed
|
||||
if (ws.fin) {
|
||||
std::string data(receivedData.begin(), receivedData.end());
|
||||
callable((const std::string) data);
|
||||
callable((const std::vector<uint8_t>) receivedData);
|
||||
receivedData.erase(receivedData.begin(), receivedData.end());
|
||||
std::vector<uint8_t> ().swap(receivedData);// free memory
|
||||
}
|
||||
@@ -308,7 +329,7 @@ class _RealWebSocket : public easywsclient::WebSocket
|
||||
else if (ws.opcode == wsheader_type::PING) {
|
||||
if (ws.mask) { for (size_t i = 0; i != ws.N; ++i) { rxbuf[i+ws.header_size] ^= ws.masking_key[i&0x3]; } }
|
||||
std::string data(rxbuf.begin()+ws.header_size, rxbuf.begin()+ws.header_size+(size_t)ws.N);
|
||||
sendData(wsheader_type::PONG, data);
|
||||
sendData(wsheader_type::PONG, data.size(), data.begin(), data.end());
|
||||
}
|
||||
else if (ws.opcode == wsheader_type::PONG) { }
|
||||
else if (ws.opcode == wsheader_type::CLOSE) { close(); }
|
||||
@@ -319,14 +340,24 @@ class _RealWebSocket : public easywsclient::WebSocket
|
||||
}
|
||||
|
||||
void sendPing() {
|
||||
sendData(wsheader_type::PING, std::string());
|
||||
std::string empty;
|
||||
sendData(wsheader_type::PING, empty.size(), empty.begin(), empty.end());
|
||||
}
|
||||
|
||||
void send(const std::string& message) {
|
||||
sendData(wsheader_type::TEXT_FRAME, message);
|
||||
sendData(wsheader_type::TEXT_FRAME, message.size(), message.begin(), message.end());
|
||||
}
|
||||
|
||||
void sendData(wsheader_type::opcode_type type, const std::string& message) {
|
||||
void sendBinary(const std::string& message) {
|
||||
sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end());
|
||||
}
|
||||
|
||||
void sendBinary(const std::vector<uint8_t>& message) {
|
||||
sendData(wsheader_type::BINARY_FRAME, message.size(), message.begin(), message.end());
|
||||
}
|
||||
|
||||
template<class Iterator>
|
||||
void sendData(wsheader_type::opcode_type type, uint64_t message_size, Iterator message_begin, Iterator message_end) {
|
||||
// TODO:
|
||||
// Masking key should (must) be derived from a high quality random
|
||||
// number generator, to mitigate attacks on non-WebSocket friendly
|
||||
@@ -335,7 +366,6 @@ class _RealWebSocket : public easywsclient::WebSocket
|
||||
// TODO: consider acquiring a lock on txbuf...
|
||||
if (readyState == CLOSING || readyState == CLOSED) { return; }
|
||||
std::vector<uint8_t> header;
|
||||
uint64_t message_size = message.size();
|
||||
header.assign(2 + (message_size >= 126 ? 2 : 0) + (message_size >= 65536 ? 6 : 0) + (useMask ? 4 : 0), 0);
|
||||
header[0] = 0x80 | type;
|
||||
if (false) { }
|
||||
@@ -378,9 +408,9 @@ class _RealWebSocket : public easywsclient::WebSocket
|
||||
}
|
||||
// N.B. - txbuf will keep growing until it can be transmitted over the socket:
|
||||
txbuf.insert(txbuf.end(), header.begin(), header.end());
|
||||
txbuf.insert(txbuf.end(), message.begin(), message.end());
|
||||
txbuf.insert(txbuf.end(), message_begin, message_end);
|
||||
if (useMask) {
|
||||
for (size_t i = 0; i != message.size(); ++i) { *(txbuf.end() - message.size() + i) ^= masking_key[i&0x3]; }
|
||||
for (size_t i = 0; i != message_size; ++i) { *(txbuf.end() - message_size + i) ^= masking_key[i&0x3]; }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user