forked from airat77786/MQL5Book
183 lines
5.3 KiB
MQL5
183 lines
5.3 KiB
MQL5
//+------------------------------------------------------------------+
| |||
//| wstransport.mqh |
| |||
//| Copyright 2020-2022, MetaQuotes Ltd. |
| |||
//| https://www.mql5.com |
| |||
//+------------------------------------------------------------------+
| |||
#include "wstools.mqh"
| |||
#include "wsinterfaces.mqh"
| |||
| |||
//+------------------------------------------------------------------+
| |||
//| Network processing based on built-in MQL5 Socket-functions |
| |||
//+------------------------------------------------------------------+
| |||
class MqlWebSocketTransport: public IWebSocketTransport
| |||
{
| |||
protected:
| |||
int handle;
| |||
uint timeout;
| |||
bool TLS;
| |||
bool connected;
| |||
| |||
MqlWebSocketTransport(const string scheme, const string host, const uint port, const uint t = 1)
| |||
{
| |||
handle = SocketCreate();
| |||
timeout = t;
| |||
TLS = scheme == "wss";
| |||
if(handle != INVALID_HANDLE)
| |||
{
| |||
Print("Connecting to ", host, ":", port);
| |||
if(SocketConnect(handle, host, port, timeout))
| |||
{
| |||
SocketTimeouts(handle, timeout, timeout);
| |||
| |||
string subject, issuer, serial, thumbprint;
| |||
datetime expiration;
| |||
if(TLS)
| |||
{
| |||
if(!SocketTlsHandshake(handle, host))
| |||
{
| |||
Print("TLS handshake error: ", _LastError);
| |||
TLS = false;
| |||
}
| |||
else
| |||
{
| |||
Print("TLS enabled");
| |||
}
| |||
if(SocketTlsCertificate(handle, subject, issuer, serial, thumbprint, expiration))
| |||
{
| |||
Print("TLS:");
| |||
Print(" Owner: ", subject);
| |||
Print(" Issuer: ", issuer);
| |||
Print(" Number: ", serial);
| |||
Print(" Signature: ", thumbprint);
| |||
Print(" Expiration: ", expiration);
| |||
}
| |||
}
| |||
}
| |||
else
| |||
{
| |||
Print("Can't connect: ", GetLastError());
| |||
}
| |||
}
| |||
else
| |||
{
| |||
Print("Can't create socket: ", GetLastError());
| |||
}
| |||
}
| |||
| |||
~MqlWebSocketTransport()
| |||
{
| |||
SocketClose(handle);
| |||
}
| |||
| |||
public:
| |||
static MqlWebSocketTransport *create(const string scheme, const string host, const uint port, const uint timeout)
| |||
{
| |||
MqlWebSocketTransport *result = new MqlWebSocketTransport(scheme, host, port, timeout);
| |||
if(result.getHandle() == INVALID_HANDLE)
| |||
{
| |||
Print("Socket create failed: ", _LastError);
| |||
delete result;
| |||
result = NULL;
| |||
}
| |||
| |||
return result;
| |||
}
| |||
| |||
bool isConnected(void) const override
| |||
{
| |||
return SocketIsConnected(handle);
| |||
}
| |||
| |||
int getHandle() const override
| |||
{
| |||
return handle;
| |||
}
| |||
| |||
int write(const uchar &buffer[]) override
| |||
{
| |||
if(!SocketIsConnected(handle))
| |||
{
| |||
Print("Can't write to non-connected socket");
| |||
return -1;
| |||
}
| |||
| |||
const uint len = ArraySize(buffer);
| |||
| |||
int written = 0;
| |||
ResetLastError();
| |||
| |||
if(TLS)
| |||
{
| |||
written = SocketTlsSend(handle, buffer, len);
| |||
}
| |||
else
| |||
{
| |||
written = SocketSend(handle, buffer, len);
| |||
}
| |||
return written;
| |||
}
| |||
| |||
int read(uchar &buffer[]) override
| |||
{ // can be disconnected already but still having data in internal buffer
| |||
if(!SocketIsConnected(handle) && SocketIsReadable(handle) == 0)
| |||
{
| |||
Print("Can't read from non-connected socket");
| |||
return -1;
| |||
}
| |||
| |||
ResetLastError();
| |||
ArrayResize(buffer, 0); // ensure buffer is of zero size
| |||
| |||
uint available = SocketIsReadable(handle);
| |||
int received = 0;
| |||
| |||
#ifdef NETWORK_PURGE
| |||
ResetLastError();
| |||
// depending from network conditions it may require to call
| |||
// SocketIsReadable twice in a row!
| |||
// yes, this is ridiculous but may help here
| |||
available = SocketIsReadable(handle);
| |||
#endif
| |||
| |||
if(!available) return 0;
| |||
else if(available == !_LastError && TLS) return 0;
| |||
| |||
if(TLS)
| |||
{
| |||
// SocketIsReadable is not compatible with SocketTlsRead!
| |||
// first returns raw data count, second tries to read clean deciphered bytes count
| |||
received = SocketTlsReadAvailable(handle, buffer, available); // waiting for data (block)
| |||
}
| |||
else
| |||
{
| |||
received = SocketRead(handle, buffer, available, timeout);
| |||
}
| |||
| |||
if(received == -1)
| |||
{
| |||
// NB: even if we got -1 result and _LastError = 5273,
| |||
// buffer may contain valid data received before connection was lost/closed
| |||
Print("SocketRead failed: ", _LastError, " Available: ", available);
| |||
if(_LastError == 5273 && available == 1 && !TLS) return -1;
| |||
}
| |||
| |||
return ArraySize(buffer);
| |||
}
| |||
| |||
bool isReadable(void) const override
| |||
{
| |||
return SocketIsReadable(handle) > 0;
| |||
}
| |||
| |||
bool isWritable(void) const override
| |||
{
| |||
return SocketIsWritable(handle);
| |||
}
| |||
| |||
void close(void) override
| |||
{
| |||
SocketClose(handle);
| |||
handle = INVALID_HANDLE;
| |||
}
| |||
};
| |||
//+------------------------------------------------------------------+
|