forked from rosh/MQL5Book
214 lines
5.4 KiB
MQL5
214 lines
5.4 KiB
MQL5
//+------------------------------------------------------------------+
| |
//| wsmessage.mqh |
| |
//| Copyright 2020-2022, MetaQuotes Ltd. |
| |
//| https://www.mql5.com |
| |
//+------------------------------------------------------------------+
| |
#include "wstools.mqh"
| |
#include "wsinterfaces.mqh"
| |
#include "wsframe.mqh"
| |
| |
//+------------------------------------------------------------------+
| |
//| WebSocket messaging according to Hixie RFC (back compatibility) |
| |
//+------------------------------------------------------------------+
| |
class WebSocketMessageHixie: public IWebSocketMessage
| |
{
| |
protected:
| |
IWebSocketFrame *frame;
| |
| |
public:
| |
WebSocketMessageHixie(IWebSocketFrame *f = NULL): frame(f) { }
| |
WebSocketMessageHixie(const string &text): frame(NULL)
| |
{
| |
setString(text);
| |
}
| |
| |
~WebSocketMessageHixie()
| |
{
| |
if(frame) delete frame;
| |
}
| |
| |
void getFrames(IWebSocketFrame *&frames[]) override
| |
{
| |
WsTools::push(frames, frame);
| |
}
| |
| |
bool setString(const string &d) override
| |
{
| |
if(frame != NULL) delete frame;
| |
frame = WebSocketFrameHixie::create(WS_FRAME_OPCODE::WS_TEXT_FRAME, d);
| |
return frame != NULL;
| |
}
| |
| |
string getString() override
| |
{
| |
return isFinalised() ? frame.getData() : NULL;
| |
}
| |
| |
bool setData(const uchar &[]) override
| |
{
| |
return false;
| |
}
| |
| |
bool getData(uchar &[]) override
| |
{
| |
return false;
| |
}
| |
| |
bool isFinalised() override
| |
{
| |
return frame != NULL;
| |
}
| |
| |
virtual bool takeFrame(IWebSocketFrame *f) override
| |
{
| |
if(!frame)
| |
{
| |
frame = f;
| |
return true;
| |
}
| |
return false;
| |
}
| |
};
| |
| |
//+------------------------------------------------------------------+
| |
//| WebSocket messaging according to Hybi RFC (current standard) |
| |
//+------------------------------------------------------------------+
| |
class WebSocketMessage: public IWebSocketMessage
| |
{
| |
protected:
| |
IWebSocketFrame *frames[];
| |
uchar _data[];
| |
bool compressed;
| |
bool binary;
| |
| |
bool compose()
| |
{
| |
if(compressed)
| |
{
| |
uchar key[] = {1, 0, 0, 0};
| |
uchar load[], result[];
| |
const int n = CryptEncode(CRYPT_ARCH_ZIP, _data, key, result);
| |
if(n == 0)
| |
{
| |
Print("Can't compress: ", _LastError);
| |
compressed = false; // fallback (need to clear deflate flag)
| |
}
| |
else
| |
{
| |
ArraySwap(_data, result);
| |
}
| |
}
| |
WsTools::push(frames, WebSocketFrame::create(/*compressed || */binary ?
| |
WS_FRAME_OPCODE::WS_BINARY_FRAME : WS_FRAME_OPCODE::WS_TEXT_FRAME, _data, compressed));
| |
return true;
| |
}
| |
| |
bool extract()
| |
{
| |
if(!isFinalised())
| |
{
| |
Print("Can't get data from NotFinalised message");
| |
return false;
| |
}
| |
| |
ArrayResize(_data, 0);
| |
| |
for(int i = 0; i < ArraySize(frames); i++)
| |
{
| |
uchar temp[];
| |
frames[i].getData(temp);
| |
ArrayCopy(_data, temp, ArraySize(_data));
| |
}
| |
| |
if(compressed)
| |
{
| |
uchar key[] = {1, 0, 0, 0};
| |
uchar result[];
| |
if(CryptDecode(CRYPT_ARCH_ZIP, _data, key, result) == 0)
| |
{
| |
Print("Can't decompress: ", _LastError);
| |
}
| |
else
| |
{
| |
ArraySwap(_data, result);
| |
}
| |
}
| |
return true;
| |
}
| |
| |
public:
| |
WebSocketMessage(const bool deflate = false): compressed(deflate), binary(false) { }
| |
| |
WebSocketMessage(const uchar &bytes[], bool deflate = false): compressed(deflate), binary(true)
| |
{
| |
setData(bytes);
| |
}
| |
| |
WebSocketMessage(const string &text, bool deflate = false): compressed(deflate), binary(false)
| |
{
| |
setString(text);
| |
}
| |
| |
~WebSocketMessage()
| |
{
| |
for(int i = 0; i < ArraySize(frames); i++)
| |
{
| |
if(CheckPointer(frames[i]) == POINTER_DYNAMIC) delete frames[i];
| |
}
| |
ArrayResize(frames, 0);
| |
}
| |
| |
bool setData(const uchar &data[]) override
| |
{
| |
ArrayCopy(_data, data);
| |
return compose();
| |
}
| |
| |
bool setString(const string &d) override
| |
{
| |
StringToCharArray(d, _data, 0, WHOLE_ARRAY, CP_UTF8);
| |
ArrayResize(_data, ArraySize(_data) - 1); // cut trailing '\0'
| |
return compose();
| |
}
| |
| |
bool getData(uchar &data[]) override
| |
{
| |
return extract() && ArrayCopy(data, _data);
| |
}
| |
| |
string getString() override
| |
{
| |
if(extract())
| |
{
| |
// NB. return string even if it's binary data, just for logging,
| |
// calling code should check applicability
| |
return CharArrayToString(_data, 0, WHOLE_ARRAY, CP_UTF8);
| |
}
| |
return NULL;
| |
}
| |
| |
void getFrames(IWebSocketFrame *&_frames[]) override
| |
{
| |
ArrayCopy(_frames, this.frames);
| |
}
| |
| |
bool isFinalised() override
| |
{
| |
if(ArraySize(frames) == 0)
| |
return false;
| |
| |
return frames[ArraySize(frames) - 1].isFinal();
| |
}
| |
| |
// append frame to the message
| |
virtual bool takeFrame(IWebSocketFrame *frame) override
| |
{
| |
if(CheckPointer(frame) != POINTER_INVALID)
| |
{
| |
WsTools::push(frames, frame);
| |
return true;
| |
}
| |
return false;
| |
}
| |
};
| |
//+------------------------------------------------------------------+
|