870 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
			
		
		
	
	
			870 lines
		
	
	
	
		
			26 KiB
		
	
	
	
		
			MQL5
		
	
	
	
	
	
//+------------------------------------------------------------------+
 | 
						|
//|                                              WebSocketClient.mqh |
 | 
						|
//|                        Copyright 2019, MetaQuotes Software Corp. |
 | 
						|
//|                                             https://www.mql5.com |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
#property copyright "Copyright 2019, MetaQuotes Software Corp."
 | 
						|
#property link      "https://www.mql5.com"
 | 
						|
#include <Socket.mqh>
 | 
						|
#include <Frame.mqh>
 | 
						|
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| defines                                                          |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
#define GUID                 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
 | 
						|
#define HEADER_EOL          "\r\n"
 | 
						|
#define HEADER_GET          "GET /"
 | 
						|
#define HEADER_HOST         "Host: "
 | 
						|
#define HEADER_UPGRADE      "Upgrade: websocket"+HEADER_EOL
 | 
						|
#define HEADER_CONNECTION   "Connection: Upgrade"+HEADER_EOL
 | 
						|
#define HEADER_KEY          "Sec-WebSocket-Key: "
 | 
						|
#define HEADER_WS_VERSION   "Sec-WebSocket-Version: 13"+HEADER_EOL+HEADER_EOL
 | 
						|
#define HEADER_HTTP         " HTTP/1.1"
 | 
						|
 | 
						|
 | 
						|
 | 
						|
enum ENUM_CLOSE_CODE                 // possible reasons for disconnecting sent with a close frame
 | 
						|
  {
 | 
						|
   NORMAL_CLOSE = 1000,            // normal closure initiated by choice
 | 
						|
   GOING_AWAY_CLOSE,               // close code for client navigating away from end point, used in browsers
 | 
						|
   PROTOCOL_ERROR_CLOSE,           // close caused by some violation of a protocol, usually application defined
 | 
						|
   FRAME_TYPE_ERROR_CLOSE,         // close caused by an endpoint receiving frame type that is not supportted or allowed
 | 
						|
   UNDEFINED_CLOSE_1,              // close code is not defined by websocket protocol
 | 
						|
   UNUSED_CLOSE_1,                 // unused
 | 
						|
   UNUSED_CLOSE_2,                 // values
 | 
						|
   ENCODING_TYPE_ERROR_CLOSE,      // close caused data in message is of wrong encoding type, usually referring to strings
 | 
						|
   APP_POLICY_ERROR_CLOSE,         // close caused by violation of user policy
 | 
						|
   MESSAGE_SIZE_ERROR_CLOSE,       // close caused by endpoint receiving message that is too large
 | 
						|
   EXTENSION_ERROR_CLOSE,          // close caused by non compliance to or no support for specified extension of websocket protocol
 | 
						|
   SERVER_SIDE_ERROR_CLOSE,        // close caused by some error that occurred on the server
 | 
						|
   UNUSED_CLOSE_3 = 1015,          // unused
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
enum ENUM_WEBSOCKET_STATE
 | 
						|
  {
 | 
						|
   CLOSED = 0,
 | 
						|
   CLOSING,
 | 
						|
   CONNECTING,
 | 
						|
   CONNECTED
 | 
						|
  };
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| CWebSocketClient class                                           |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
class CWebSocketClient
 | 
						|
  {
 | 
						|
private:
 | 
						|
   ENUM_WEBSOCKET_STATE  m_wsclient;
 | 
						|
 | 
						|
   CSocket           m_socket;
 | 
						|
 | 
						|
   int               m_frames;
 | 
						|
   int               m_maxsendsize;
 | 
						|
   uint              m_total_len;
 | 
						|
   int               m_msgsize;
 | 
						|
 | 
						|
   uint              m_timeout;
 | 
						|
   bool              m_sent;
 | 
						|
   uchar             m_txbuf[];
 | 
						|
   uchar             m_rxbuf[];
 | 
						|
   uchar             m_send[];
 | 
						|
 | 
						|
 | 
						|
   //---
 | 
						|
   bool              upgrade(void);
 | 
						|
   void              reset(void);
 | 
						|
   bool              fillTxBuffer(ENUM_FRAME_TYPE ftype);
 | 
						|
   int               send(ENUM_FRAME_TYPE frame_type);
 | 
						|
   int               fillRxBuffer(void);
 | 
						|
   bool              parse(CFrame& out[]);
 | 
						|
 | 
						|
public:
 | 
						|
                     CWebSocketClient();
 | 
						|
                    ~CWebSocketClient();
 | 
						|
   //---
 | 
						|
   ENUM_WEBSOCKET_STATE ClientState(void);
 | 
						|
   void              SetMaxSendSize(int maxsend)   {if(maxsend>=0) m_maxsendsize=maxsend;  else m_maxsendsize=0; }
 | 
						|
   bool              Connect(const string url,const uint port,const uint timeout,bool use_tls=false,bool enablelog=false);
 | 
						|
   bool              Close(ENUM_CLOSE_CODE close_code=NORMAL_CLOSE,const string msg="");
 | 
						|
   int               SendString(const string message);
 | 
						|
   int               SendData(uchar& message_buffer[]);
 | 
						|
   int               SendPong(const string msg);
 | 
						|
   int               SendPing(const string msg);
 | 
						|
   int               Readable(void)   {  return(fillRxBuffer());}
 | 
						|
   uint              Read(CFrame& out[]);
 | 
						|
  };
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| constructor                                                      |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|                                                                  |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
CWebSocketClient::CWebSocketClient():m_wsclient(0),
 | 
						|
   m_timeout(0),
 | 
						|
   m_frames(0),
 | 
						|
   m_total_len(0),
 | 
						|
   m_msgsize(0),
 | 
						|
   m_maxsendsize(0),
 | 
						|
   m_sent(true)
 | 
						|
  {
 | 
						|
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| destructor                                                       |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
CWebSocketClient::~CWebSocketClient()
 | 
						|
  {
 | 
						|
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| ClientState()                                                    |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
ENUM_WEBSOCKET_STATE CWebSocketClient::ClientState(void)
 | 
						|
  {
 | 
						|
   if(m_socket.IsConnected())
 | 
						|
      return(m_wsclient);
 | 
						|
//---
 | 
						|
   if(m_wsclient!=CLOSED)
 | 
						|
     {
 | 
						|
      m_socket.Close();
 | 
						|
      m_wsclient=CLOSED;
 | 
						|
     }
 | 
						|
//---
 | 
						|
   return(m_wsclient);
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| reset(): Used to reinitialize class properties                   |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
void CWebSocketClient::reset(void)
 | 
						|
  {
 | 
						|
//---
 | 
						|
   m_wsclient=0;
 | 
						|
   m_timeout=0;
 | 
						|
   m_frames=0;
 | 
						|
   m_total_len=0;
 | 
						|
   m_msgsize=0;
 | 
						|
   m_sent=true;
 | 
						|
//---
 | 
						|
   if(ArraySize(m_send)>0)
 | 
						|
      ArrayFree(m_send);
 | 
						|
   if(ArraySize(m_rxbuf)>0)
 | 
						|
      ArrayFree(m_rxbuf);
 | 
						|
   if(ArraySize(m_txbuf)>0)
 | 
						|
      ArrayFree(m_txbuf);
 | 
						|
//---
 | 
						|
   return;
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|Upgrade(): requests server to accept Websocket protocol connection|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
bool CWebSocketClient::upgrade(void)
 | 
						|
  {
 | 
						|
 | 
						|
   const uchar key[] =
 | 
						|
     {
 | 
						|
      'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
 | 
						|
      'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
 | 
						|
      'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
 | 
						|
      'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
 | 
						|
      'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
 | 
						|
      'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
 | 
						|
      'w', 'x', 'y', 'z', '0', '1', '2', '3',
 | 
						|
      '4', '5', '6', '7', '8', '9', '+', '/'
 | 
						|
     };
 | 
						|
 | 
						|
   char   rsp[];
 | 
						|
   string result;
 | 
						|
   uchar src[16]= {0};
 | 
						|
   uchar sh1key[];
 | 
						|
   string keystr="";
 | 
						|
   uchar dst[];
 | 
						|
 | 
						|
   for(int i=0; i<16; i++)
 | 
						|
     {
 | 
						|
      src[i]=(uchar)(255*MathRand()/32767);
 | 
						|
     }
 | 
						|
 | 
						|
   int res=CryptEncode(CRYPT_BASE64,src,key,dst);
 | 
						|
 | 
						|
   if(res<=0)
 | 
						|
     {
 | 
						|
      m_socket.Log("Crypt encode error",__LINE__,__FUNCTION__);
 | 
						|
      return(false);
 | 
						|
     }
 | 
						|
 | 
						|
   keystr=CharArrayToString(dst,0,WHOLE_ARRAY,CP_UTF8);
 | 
						|
   if(keystr=="")
 | 
						|
     {
 | 
						|
      m_socket.Log("Char array to string error",__LINE__,__FUNCTION__);
 | 
						|
      return(false);
 | 
						|
     }
 | 
						|
 | 
						|
   bool upgraded=false;
 | 
						|
   bool upgraderequest=false;
 | 
						|
   string request;
 | 
						|
 | 
						|
   int ss=StringFind(m_socket.Address(),"/");
 | 
						|
//---
 | 
						|
   if(ss>=0)
 | 
						|
      request=HEADER_GET+StringSubstr(m_socket.Address(),ss+1)+HEADER_HTTP+HEADER_EOL;
 | 
						|
   else
 | 
						|
      request=HEADER_GET+HEADER_HTTP+HEADER_EOL;
 | 
						|
//---
 | 
						|
   if(m_socket.Port()!=80 && m_socket.Port()!=443)
 | 
						|
      request+=HEADER_HOST+m_socket.Address()+":"+IntegerToString(m_socket.Port())+HEADER_EOL;
 | 
						|
   else
 | 
						|
      request+=HEADER_HOST+m_socket.Address()+HEADER_EOL;
 | 
						|
//---
 | 
						|
   request+=HEADER_UPGRADE+HEADER_CONNECTION+HEADER_KEY+keystr+HEADER_EOL+HEADER_WS_VERSION;
 | 
						|
//---
 | 
						|
   m_socket.Log(request,__LINE__,__FUNCTION__);
 | 
						|
//---
 | 
						|
   char req[];
 | 
						|
   int  len=StringToCharArray(request,req)-1;
 | 
						|
   if(len<0)
 | 
						|
     {
 | 
						|
      m_socket.Log("string to char array error",__LINE__,__FUNCTION__);
 | 
						|
      return(false);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   upgraderequest=(m_socket.Send(req,(uint)len)==len)?true:false;
 | 
						|
//---
 | 
						|
   if(!upgraderequest)
 | 
						|
     {
 | 
						|
      m_socket.Log("send error",__LINE__,__FUNCTION__);
 | 
						|
      return(false);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   uint leng=0;
 | 
						|
   uint   timeout_check=GetTickCount()+m_timeout;
 | 
						|
//---
 | 
						|
   do
 | 
						|
     {
 | 
						|
      leng=m_socket.Readable();
 | 
						|
      if(leng)
 | 
						|
        {
 | 
						|
         int rsp_len;
 | 
						|
         //--- various reading commands depending on whether the connection is secure or not
 | 
						|
         rsp_len=m_socket.Read(rsp,leng,m_timeout);
 | 
						|
         //--- analyze the response
 | 
						|
         if(rsp_len>0)
 | 
						|
           {
 | 
						|
            result+=CharArrayToString(rsp,0,rsp_len,CP_UTF8);
 | 
						|
            //--- print only the response header
 | 
						|
            if(StringFind(result,(HEADER_EOL+HEADER_EOL),0)>=0)
 | 
						|
               break;
 | 
						|
           }
 | 
						|
        }
 | 
						|
     }
 | 
						|
   while(GetTickCount()<timeout_check);
 | 
						|
//---
 | 
						|
   m_socket.Log(result,__LINE__,__FUNCTION__);
 | 
						|
//---
 | 
						|
   uchar _sh1[];
 | 
						|
   uchar _dest[];
 | 
						|
   uchar resp[];
 | 
						|
   string sh1confirm="";
 | 
						|
 | 
						|
   int y=StringToCharArray(keystr+GUID,_sh1);
 | 
						|
   if(y>0)
 | 
						|
      ArrayRemove(_sh1,y-1,1);
 | 
						|
   else
 | 
						|
     {
 | 
						|
      m_socket.Log("String to char array error",__LINE__,__FUNCTION__);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   res=0;
 | 
						|
   res=CryptEncode(CRYPT_HASH_SHA1,_sh1,sh1key,_dest);
 | 
						|
   if(res>0)
 | 
						|
      if(CryptEncode(CRYPT_BASE64,_dest,key,resp)>0)
 | 
						|
        {
 | 
						|
         sh1confirm=CharArrayToString(resp,0,WHOLE_ARRAY,CP_UTF8);
 | 
						|
         m_socket.Log(sh1confirm,__LINE__,__FUNCTION__);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
         m_socket.Log("Crypt encode error",__LINE__,__FUNCTION__);
 | 
						|
        }
 | 
						|
//---
 | 
						|
   if(StringFind(result,"Sec-WebSocket-Accept: "+sh1confirm)>=0)
 | 
						|
     {
 | 
						|
      m_wsclient=CONNECTED;
 | 
						|
      return(true);
 | 
						|
     }
 | 
						|
   else
 | 
						|
      if(StringFind(result,"Sec-WebSocket-Accept")>=0)
 | 
						|
        {
 | 
						|
         m_wsclient=CONNECTED;
 | 
						|
         return(true);
 | 
						|
        }
 | 
						|
//---
 | 
						|
    return(false);     
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|prepareSendBuffer()prepares array buffer for socket dispatch      |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
bool CWebSocketClient::fillTxBuffer(ENUM_FRAME_TYPE ftype)
 | 
						|
  {
 | 
						|
   uchar header[];
 | 
						|
   static int it;
 | 
						|
   static int start;
 | 
						|
   uchar masking_key[4]= {0};
 | 
						|
   int maxsend=(m_maxsendsize<7)?m_msgsize:((m_maxsendsize<126)?m_maxsendsize-6:((m_maxsendsize<65536)?m_maxsendsize-8:m_maxsendsize-14));
 | 
						|
//---
 | 
						|
   for(int i=0; i<4; i++)
 | 
						|
     {
 | 
						|
      masking_key[i]=(uchar)(255*MathRand()/32767);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   m_socket.Log("[send]max size - "+IntegerToString(maxsend),__LINE__,__FUNCTION__);
 | 
						|
   m_socket.Log("[send]should be max size - "+IntegerToString(m_maxsendsize),__LINE__,__FUNCTION__);
 | 
						|
   int message_size=(((start+maxsend)-1)<=(m_msgsize-1))?maxsend:m_msgsize%maxsend;
 | 
						|
   bool isfinal=((((start+maxsend)-1)==(m_msgsize-1)) || (message_size<maxsend) ||(message_size<=0))?true:false;
 | 
						|
   bool isfirst=(start==0)?true:false;
 | 
						|
//---
 | 
						|
   m_socket.Log("[send]message size - "+IntegerToString(message_size),__LINE__,__FUNCTION__);
 | 
						|
   if(isfirst)
 | 
						|
      m_socket.Log("[send]first frame",__LINE__,__FUNCTION__);
 | 
						|
   if(isfinal)
 | 
						|
      m_socket.Log("[send]final frame",__LINE__,__FUNCTION__);
 | 
						|
//---
 | 
						|
   if(ArrayResize(header,2 + (message_size >= 126 ? 2 : 0) + (message_size >= 65536 ? 6 : 0) + (4))<0)
 | 
						|
     {
 | 
						|
      m_socket.Log("array resize error",__LINE__,__FUNCTION__);
 | 
						|
      return(false);
 | 
						|
     }
 | 
						|
//header[0] = (isfinal)? (0x80 | 0x1) :( );
 | 
						|
   switch(ftype)
 | 
						|
     {
 | 
						|
      case CLOSE_FRAME:
 | 
						|
         header[0] = uchar(0x80|CLOSE_FRAME);
 | 
						|
         m_socket.Log("[building]close frame",__LINE__,__FUNCTION__);
 | 
						|
         break;
 | 
						|
      case PING_FRAME:
 | 
						|
         header[0] = uchar(0x80|PING_FRAME);
 | 
						|
         m_socket.Log("[building]ping frame",__LINE__,__FUNCTION__);
 | 
						|
         break;
 | 
						|
      case PONG_FRAME:
 | 
						|
         header[0] = uchar(0x80|PONG_FRAME);
 | 
						|
         m_socket.Log("[building]pong frame",__LINE__,__FUNCTION__);
 | 
						|
         break;
 | 
						|
      default:
 | 
						|
         header[0] = (isfinal)? 0x80:0x0;
 | 
						|
         m_socket.Log("[building]"+EnumToString(ftype),__LINE__,__FUNCTION__);
 | 
						|
         if(isfirst)
 | 
						|
            header[0] |=uchar(ftype);
 | 
						|
         break;
 | 
						|
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(message_size < 126)
 | 
						|
     {
 | 
						|
      header[1] = (uchar)(message_size & 0xff) |  0x80 ;
 | 
						|
      header[2] = masking_key[0];
 | 
						|
      header[3] = masking_key[1];
 | 
						|
      header[4] = masking_key[2];
 | 
						|
      header[5] = masking_key[3];
 | 
						|
     }
 | 
						|
   else
 | 
						|
      if(message_size < 65536)
 | 
						|
        {
 | 
						|
         header[1] = 126 |  0x80 ;
 | 
						|
         header[2] = (uchar)(message_size >> 8) & 0xff;
 | 
						|
         header[3] = (uchar)(message_size >> 0) & 0xff;
 | 
						|
         header[4] = masking_key[0];
 | 
						|
         header[5] = masking_key[1];
 | 
						|
         header[6] = masking_key[2];
 | 
						|
         header[7] = masking_key[3];
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
         header[1] = 127 | 0x80;
 | 
						|
         header[2] = (uchar)(message_size >> 56) & 0xff;
 | 
						|
         header[3] = (uchar)(message_size >> 48) & 0xff;
 | 
						|
         header[4] = (uchar)(message_size >> 40) & 0xff;
 | 
						|
         header[5] = (uchar)(message_size >> 32) & 0xff;
 | 
						|
         header[6] = (uchar)(message_size >> 24) & 0xff;
 | 
						|
         header[7] = (uchar)(message_size >> 16) & 0xff;
 | 
						|
         header[8] = (uchar)(message_size >>  8) & 0xff;
 | 
						|
         header[9] = (uchar)(message_size >>  0) & 0xff;
 | 
						|
 | 
						|
         header[10] = masking_key[0];
 | 
						|
         header[11] = masking_key[1];
 | 
						|
         header[12] = masking_key[2];
 | 
						|
         header[13] = masking_key[3];
 | 
						|
 | 
						|
        }
 | 
						|
//---
 | 
						|
   if(ArrayResize(m_send,ArraySize(header),message_size)<0)
 | 
						|
     {
 | 
						|
      m_socket.Log("array resize error",__LINE__,__FUNCTION__);
 | 
						|
      return(false);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   ArrayCopy(m_send,header,0,0);
 | 
						|
//---
 | 
						|
   if(message_size)
 | 
						|
     {
 | 
						|
      if(ArrayResize(m_send,ArraySize(header)+message_size)<0)
 | 
						|
        {
 | 
						|
         m_socket.Log("array resize error",__LINE__,__FUNCTION__);
 | 
						|
         return(false);
 | 
						|
        }
 | 
						|
      //---
 | 
						|
      ArrayCopy(m_send,m_txbuf,ArraySize(header),start,message_size);
 | 
						|
      //---
 | 
						|
      int bufsize=ArraySize(m_send);
 | 
						|
      //---
 | 
						|
      int message_offset = bufsize - message_size;
 | 
						|
      //---
 | 
						|
      for(int i = 0; i < message_size; i++)
 | 
						|
        {
 | 
						|
         m_send[message_offset+ i] ^= masking_key[i&0x3];
 | 
						|
        }
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(isfinal)
 | 
						|
     {
 | 
						|
      it=0;
 | 
						|
      start=0;
 | 
						|
      m_sent=true;
 | 
						|
      ArrayFree(m_txbuf);
 | 
						|
     }
 | 
						|
   else
 | 
						|
     {
 | 
						|
      it++;
 | 
						|
      start=it*maxsend;
 | 
						|
     }
 | 
						|
//---
 | 
						|
   return(true);
 | 
						|
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| receiver()fills rxbuf with raw message                           |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
int CWebSocketClient::fillRxBuffer(void)
 | 
						|
  {
 | 
						|
   uint leng=0;
 | 
						|
   int rsp_len=-1;
 | 
						|
 | 
						|
//---
 | 
						|
   uint timeout_check=GetTickCount()+m_timeout;
 | 
						|
//---
 | 
						|
   do
 | 
						|
     {
 | 
						|
      leng=m_socket.Readable();
 | 
						|
      if(leng)
 | 
						|
         rsp_len+=m_socket.Read(m_rxbuf,leng,m_timeout);
 | 
						|
      leng=0;
 | 
						|
     }
 | 
						|
   while(GetTickCount()<timeout_check);
 | 
						|
//---
 | 
						|
   m_socket.Log("receive size "+IntegerToString(rsp_len),__LINE__,__FUNCTION__);
 | 
						|
//---
 | 
						|
   int m_rxsize=ArraySize(m_rxbuf);
 | 
						|
//---
 | 
						|
   if(m_rxsize<3)
 | 
						|
      return(0);
 | 
						|
//---
 | 
						|
   switch((uint)m_rxbuf[1])
 | 
						|
     {
 | 
						|
      case 126:
 | 
						|
         if(m_rxsize<4)
 | 
						|
           {
 | 
						|
            m_rxsize=0;
 | 
						|
           }
 | 
						|
         break;
 | 
						|
      case 127:
 | 
						|
         if(m_rxsize<10)
 | 
						|
           {
 | 
						|
            m_rxsize=0;
 | 
						|
           }
 | 
						|
         break;
 | 
						|
      default:
 | 
						|
         break;
 | 
						|
     }
 | 
						|
//---
 | 
						|
   return(m_rxsize);
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|int  sendMessage() helper                                         |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
int  CWebSocketClient::send(ENUM_FRAME_TYPE frame_type)
 | 
						|
  {
 | 
						|
//---
 | 
						|
   bool done=false;
 | 
						|
   int bytes_sent=0,sum_sent=0;
 | 
						|
 | 
						|
   while(!m_sent)
 | 
						|
     {
 | 
						|
      done=fillTxBuffer(frame_type);
 | 
						|
      if(done && m_socket.Writable())
 | 
						|
        {
 | 
						|
         bytes_sent=m_socket.Send(m_send,(uint)ArraySize(m_send));
 | 
						|
         //---
 | 
						|
         if(bytes_sent<0)
 | 
						|
            break;
 | 
						|
         else
 | 
						|
           {
 | 
						|
            sum_sent+=bytes_sent;
 | 
						|
            ArrayFree(m_send);
 | 
						|
           }
 | 
						|
         //---
 | 
						|
        }
 | 
						|
      else
 | 
						|
         break;
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(ArraySize(m_send)>0)
 | 
						|
      ArrayFree(m_send);
 | 
						|
//---
 | 
						|
   m_socket.Log("",__LINE__,__FUNCTION__);
 | 
						|
//---
 | 
						|
   return(sum_sent);
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| parse() cleans up raw data buffer discarding unnecessary elements|
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
bool CWebSocketClient::parse(CFrame& out[])
 | 
						|
  {
 | 
						|
   uint i,data_len=0,frames=0;
 | 
						|
   uint s=0;
 | 
						|
   m_total_len=0;
 | 
						|
//---
 | 
						|
   int shift=0;
 | 
						|
   for(i=0; i<(uint)ArraySize(m_rxbuf); i+=(data_len+shift))
 | 
						|
     {
 | 
						|
      ++frames;
 | 
						|
      m_socket.Log("value of frame is "+IntegerToString(frames)+" Value of i is "+IntegerToString(i),__LINE__,__FUNCTION__);
 | 
						|
      switch((uint)m_rxbuf[i+1])
 | 
						|
        {
 | 
						|
         case 126:
 | 
						|
            data_len=((uint)m_rxbuf[i+2]<<8)+((uint)m_rxbuf[i+3]);
 | 
						|
            shift=4;
 | 
						|
            break;
 | 
						|
         case 127:
 | 
						|
            data_len=((uint)m_rxbuf[i+2]<<56)+((uint)m_rxbuf[i+3]<<48)+((uint)m_rxbuf[i+4]<<40)+
 | 
						|
                     ((uint)m_rxbuf[i+5]<<32)+((uint)m_rxbuf[i+6]<<24)+((uint)m_rxbuf[i+7]<<16)+
 | 
						|
                     ((uint)m_rxbuf[i+8]<<8)+((uint)m_rxbuf[i+9]);
 | 
						|
            shift=10;
 | 
						|
            break;
 | 
						|
         default:
 | 
						|
            data_len=(uint)m_rxbuf[i+1];
 | 
						|
            shift=2;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
      m_total_len+=data_len;
 | 
						|
      if(data_len>0)
 | 
						|
        {
 | 
						|
         if(ArraySize(out)<(int)frames)
 | 
						|
           {
 | 
						|
            if(ArrayResize(out,frames,1)<=0)
 | 
						|
              {
 | 
						|
               m_socket.Log("array resize error",__LINE__,__FUNCTION__);
 | 
						|
               return(false);
 | 
						|
              }
 | 
						|
           }
 | 
						|
         //---
 | 
						|
         if(!out[frames-1].Fill(m_rxbuf,i+shift,data_len))
 | 
						|
           {
 | 
						|
            m_socket.Log("Error adding new frame",__LINE__,__FUNCTION__);
 | 
						|
            return(false);
 | 
						|
           }
 | 
						|
         //---
 | 
						|
         switch((uchar)m_rxbuf[i])
 | 
						|
           {
 | 
						|
            case 0x1:
 | 
						|
               if(out[frames-1].MessageType()==0)
 | 
						|
                  out[frames-1].SetMessageType(TEXT_FRAME);
 | 
						|
               break;
 | 
						|
            case 0x2:
 | 
						|
               if(out[frames-1].MessageType()==0)
 | 
						|
                  out[frames-1].SetMessageType(BINARY_FRAME);
 | 
						|
               break;
 | 
						|
            case 0x80:
 | 
						|
            case 0x81:
 | 
						|
               if(out[frames-1].MessageType()==0)
 | 
						|
                  out[frames-1].SetMessageType(TEXT_FRAME);
 | 
						|
            case 0x82:
 | 
						|
               if(out[frames-1].MessageType()==0)
 | 
						|
                  out[frames-1].SetMessageType(BINARY_FRAME);
 | 
						|
               m_socket.Log("received last frame",__LINE__,__FUNCTION__);
 | 
						|
               out[frames-1].SetFinal();
 | 
						|
               break;
 | 
						|
            case 0x88:
 | 
						|
               m_socket.Log("received close frame",__LINE__,__FUNCTION__);
 | 
						|
               out[frames-1].SetMessageType(CLOSE_FRAME);
 | 
						|
               if(m_wsclient==CONNECTED)
 | 
						|
                 {
 | 
						|
                  ArrayCopy(m_txbuf,m_rxbuf,0,i+shift,data_len);
 | 
						|
                  m_wsclient=CLOSING;
 | 
						|
                 }
 | 
						|
               break;
 | 
						|
            case 0x89:
 | 
						|
               m_socket.Log("received ping frame",__LINE__,__FUNCTION__);
 | 
						|
               out[frames-1].SetMessageType(PING_FRAME);
 | 
						|
               if(m_wsclient==CONNECTED)
 | 
						|
                  ArrayCopy(m_txbuf,m_rxbuf,0,i+shift,data_len);
 | 
						|
               break;
 | 
						|
            case 0x8a:
 | 
						|
               m_socket.Log("received pong frame",__LINE__,__FUNCTION__);
 | 
						|
               out[frames-1].SetMessageType(PONG_FRAME);
 | 
						|
               break;
 | 
						|
            default:
 | 
						|
               break;
 | 
						|
           }
 | 
						|
        }
 | 
						|
     }
 | 
						|
   //---  
 | 
						|
   return(true);  
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Connect(): Used to establish connection  to websocket server     |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
bool CWebSocketClient::Connect(const string url,const uint port,const uint timeout,bool use_tls=false,bool enablelog=false)
 | 
						|
  {
 | 
						|
   reset();
 | 
						|
//---
 | 
						|
   m_timeout=timeout;
 | 
						|
//---
 | 
						|
   if(!m_socket.Open(url,port,m_timeout,use_tls,enablelog))
 | 
						|
     {
 | 
						|
      m_socket.Log("Connect error",__LINE__,__FUNCTION__);
 | 
						|
      return(false);
 | 
						|
     }
 | 
						|
   else
 | 
						|
      m_wsclient=CONNECTING;
 | 
						|
//---
 | 
						|
   if(!upgrade())
 | 
						|
    return(false);
 | 
						|
//---
 | 
						|
   m_socket.Log("ws client state "+EnumToString(m_wsclient),__LINE__,__FUNCTION__);
 | 
						|
//---
 | 
						|
   if(m_wsclient!=CONNECTED)
 | 
						|
     {
 | 
						|
      m_wsclient=CLOSED;
 | 
						|
      m_socket.Close();
 | 
						|
      reset();
 | 
						|
     }
 | 
						|
//---
 | 
						|
   return(m_wsclient==CONNECTED);
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Close() inform server client is disconnecting                    |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
bool CWebSocketClient::Close(ENUM_CLOSE_CODE close_code = NORMAL_CLOSE,const string close_reason = "")
 | 
						|
  {
 | 
						|
   ClientState();
 | 
						|
//---
 | 
						|
   if(m_wsclient==0)
 | 
						|
     {
 | 
						|
      m_socket.Log("Client Disconnected",__LINE__,__FUNCTION__);
 | 
						|
      //---
 | 
						|
      return(true);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(ArraySize(m_txbuf)<=0)
 | 
						|
     {
 | 
						|
      if(close_reason!="")
 | 
						|
        {
 | 
						|
         int len=StringToCharArray(close_reason,m_txbuf,2,120,CP_UTF8)-1;
 | 
						|
         if(len<=0)
 | 
						|
            return(false);
 | 
						|
         else
 | 
						|
            ArrayRemove(m_txbuf,len,1);
 | 
						|
        }
 | 
						|
      else
 | 
						|
        {
 | 
						|
         if(ArrayResize(m_txbuf,2)<=0)
 | 
						|
           {
 | 
						|
            m_socket.Log("array resize error",__LINE__,__FUNCTION__);
 | 
						|
            return(false);
 | 
						|
           }
 | 
						|
        }
 | 
						|
      m_txbuf[0]=(uchar)(close_code>>8) & 0xff;
 | 
						|
      m_txbuf[1]=(uchar)(close_code>>0) & 0xff;
 | 
						|
      //---
 | 
						|
     }
 | 
						|
//---
 | 
						|
   m_msgsize=ArraySize(m_txbuf);
 | 
						|
   m_sent=false;
 | 
						|
//---
 | 
						|
   send(CLOSE_FRAME);
 | 
						|
//---
 | 
						|
   m_socket.Close();
 | 
						|
//---
 | 
						|
   reset();
 | 
						|
//---
 | 
						|
   return(true);
 | 
						|
//---
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Send() sends text data to websocket server                       |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
int CWebSocketClient::SendString(const string message)
 | 
						|
  {
 | 
						|
   ClientState();
 | 
						|
//---
 | 
						|
   if(m_wsclient==CLOSED || m_wsclient==CLOSING)
 | 
						|
     {
 | 
						|
      m_socket.Log("invalid ws client handle",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(message=="")
 | 
						|
     {
 | 
						|
      m_socket.Log("no message specified",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   int len=StringToCharArray(message,m_txbuf,0,WHOLE_ARRAY,CP_UTF8)-1;
 | 
						|
   if(len<=0)
 | 
						|
     {
 | 
						|
      m_socket.Log("string char array error",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
   else
 | 
						|
      ArrayRemove(m_txbuf,len,1);
 | 
						|
//---
 | 
						|
   m_msgsize=ArraySize(m_txbuf);
 | 
						|
   m_sent=false;
 | 
						|
//---
 | 
						|
   return(send(TEXT_FRAME));
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| Send() sends user supplied array buffer                          |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
int CWebSocketClient::SendData(uchar& message_buffer[])
 | 
						|
  {
 | 
						|
   ClientState();
 | 
						|
//---
 | 
						|
   if(m_wsclient==CLOSED || m_wsclient==CLOSING)
 | 
						|
     {
 | 
						|
      m_socket.Log("invalid ws client handle",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(ArraySize(message_buffer)==0)
 | 
						|
     {
 | 
						|
      m_socket.Log("array is empty",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(ArrayResize(m_txbuf,ArraySize(message_buffer))<0)
 | 
						|
     {
 | 
						|
      m_socket.Log("array resize error",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
   else
 | 
						|
      ArrayCopy(m_txbuf,message_buffer);
 | 
						|
//---
 | 
						|
   m_msgsize=ArraySize(m_txbuf);
 | 
						|
   m_sent=false;
 | 
						|
//---
 | 
						|
   return(send(BINARY_FRAME));
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| SendPong() sends pong response upon receiving ping               |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
int CWebSocketClient::SendPong(const string msg="")
 | 
						|
  {
 | 
						|
   ClientState();
 | 
						|
//---
 | 
						|
   if(m_wsclient==CLOSED || m_wsclient==CLOSING)
 | 
						|
     {
 | 
						|
      m_socket.Log("invalid ws client handle",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(ArraySize(m_txbuf)<=0)
 | 
						|
     {
 | 
						|
      if(msg!="")
 | 
						|
        {
 | 
						|
         int len=StringToCharArray(msg,m_txbuf,0,122,CP_UTF8)-1;
 | 
						|
         if(len<=0)
 | 
						|
           {
 | 
						|
            m_socket.Log("string to char array error",__LINE__,__FUNCTION__);
 | 
						|
            return(0);
 | 
						|
           }
 | 
						|
         else
 | 
						|
            ArrayRemove(m_txbuf,len,1);
 | 
						|
        }
 | 
						|
     }
 | 
						|
//---
 | 
						|
   m_msgsize=ArraySize(m_txbuf);
 | 
						|
   m_sent=false;
 | 
						|
//---
 | 
						|
   return(send(PONG_FRAME));
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//| SendPing() ping  the server                                      |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
int CWebSocketClient::SendPing(const string msg="")
 | 
						|
  {
 | 
						|
   ClientState();
 | 
						|
//---
 | 
						|
   if(m_wsclient==CLOSED || m_wsclient==CLOSING)
 | 
						|
     {
 | 
						|
      m_socket.Log("invalid ws client handle",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   if(ArraySize(m_txbuf)<=0)
 | 
						|
     {
 | 
						|
      if(msg!="")
 | 
						|
        {
 | 
						|
         int len=StringToCharArray(msg,m_txbuf,0,122,CP_UTF8)-1;
 | 
						|
         if(len<=0)
 | 
						|
           {
 | 
						|
            m_socket.Log("string to char array error",__LINE__,__FUNCTION__);
 | 
						|
            return(0);
 | 
						|
           }
 | 
						|
         else
 | 
						|
            ArrayRemove(m_txbuf,len,1);
 | 
						|
        }
 | 
						|
     }
 | 
						|
//---
 | 
						|
   m_msgsize=ArraySize(m_txbuf);
 | 
						|
   m_sent=false;
 | 
						|
//---
 | 
						|
   return(send(PING_FRAME));
 | 
						|
  }
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
//|Getmessage() return message frame(s) as uchar array               |
 | 
						|
//+------------------------------------------------------------------+
 | 
						|
uint CWebSocketClient::Read(CFrame& out[])
 | 
						|
  {
 | 
						|
   ClientState();
 | 
						|
//---
 | 
						|
   if(m_wsclient==0)
 | 
						|
     {
 | 
						|
      m_socket.Log("invalid ws client handle",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
//---
 | 
						|
   int rx_size=ArraySize(m_rxbuf);
 | 
						|
//---
 | 
						|
   if(rx_size<=0)
 | 
						|
     {
 | 
						|
      m_socket.Log("receive buffer is empty, Make sure to call Readable first",__LINE__,__FUNCTION__);
 | 
						|
      return(0);
 | 
						|
     }
 | 
						|
//---clean up rxbuf
 | 
						|
   if(!parse(out))
 | 
						|
    {
 | 
						|
     ArrayFree(m_rxbuf);
 | 
						|
     return(0);
 | 
						|
    } 
 | 
						|
//---
 | 
						|
   ArrayFree(m_rxbuf);
 | 
						|
//---
 | 
						|
   return(m_total_len);
 | 
						|
  }
 | 
						|
 | 
						|
/////////////////////////////////////////////////////////////////////////////////////////////////////////
 |