summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2017-01-06 23:09:58 +0300
committerGeorge Hazan <ghazan@miranda.im>2017-01-06 23:10:07 +0300
commit3d4f907c1237b1210ae75a9530a82fabda2af484 (patch)
tree6dd0ed3b2f81e28ccf27707539e37b9cd752dd30
parent85e72f5e8a819daeb333d38f4ef312b5f49ead6c (diff)
gateway commands
-rw-r--r--protocols/Discord/src/gateway.cpp94
-rw-r--r--protocols/Discord/src/proto.h23
-rw-r--r--protocols/Discord/src/utils.cpp6
3 files changed, 103 insertions, 20 deletions
diff --git a/protocols/Discord/src/gateway.cpp b/protocols/Discord/src/gateway.cpp
index adf07eff4a..c8ed0121ce 100644
--- a/protocols/Discord/src/gateway.cpp
+++ b/protocols/Discord/src/gateway.cpp
@@ -34,13 +34,15 @@ void CDiscordProto::OnReceiveGateway(NETLIBHTTPREQUEST *pReply, AsyncHttpRequest
ForkThread(&CDiscordProto::GatewayThread, NULL);
}
-void CDiscordProto::GatewaySend(int opCode, const char *szBuf)
+void CDiscordProto::GatewaySend(int opCode, const JSONNode &pRoot)
{
if (m_hGatewayConnection == NULL)
return;
+ json_string szText = pRoot.write();
+
BYTE header[20];
- size_t datalen, strLen = mir_strlen(szBuf);
+ size_t datalen, strLen = szText.length();
header[0] = 0x80 + (opCode & 0x7F);
if (strLen < 126) {
header[1] = (strLen & 0xFF);
@@ -68,7 +70,7 @@ void CDiscordProto::GatewaySend(int opCode, const char *szBuf)
ptrA sendBuf((char*)mir_alloc(strLen + datalen));
memcpy(sendBuf, header, datalen);
if (strLen)
- memcpy(sendBuf.get() + datalen, szBuf, strLen);
+ memcpy(sendBuf.get() + datalen, szText.c_str(), strLen);
Netlib_Send(m_hGatewayConnection, sendBuf, int(strLen + datalen), 0);
}
@@ -100,7 +102,7 @@ void CDiscordProto::GatewayThread(void*)
}
{
CMStringA szBuf;
- szBuf.AppendFormat("GET https://%s/?encoding=etf&v=6 HTTP/1.1\r\n", m_szGateway.c_str());
+ szBuf.AppendFormat("GET https://%s/?v=6 HTTP/1.1\r\n", m_szGateway.c_str());
szBuf.AppendFormat("Host: %s\r\n", m_szGateway.c_str());
szBuf.AppendFormat("Upgrade: websocket\r\n");
szBuf.AppendFormat("Pragma: no-cache\r\n");
@@ -134,8 +136,9 @@ void CDiscordProto::GatewayThread(void*)
bool bExit = false;
int offset = 0;
- unsigned char *dataBuf = NULL;
+ char *dataBuf = NULL;
size_t dataBufSize = 0;
+ bool bDataBufAllocated = false;
while (!bExit) {
if (m_bTerminated)
@@ -145,8 +148,12 @@ void CDiscordProto::GatewayThread(void*)
sel.cbSize = sizeof(sel);
sel.dwTimeout = 1000;
sel.hReadConns[0] = m_hGatewayConnection;
- if (CallService(MS_NETLIB_SELECT, 0, (LPARAM)&sel) == 0) // timeout, send a hartbeat packet
- GatewaySend(0, "{ \"op\":1, \"d\":(null) }");
+ CallService(MS_NETLIB_SELECT, 0, (LPARAM)&sel);
+ {
+ int iInterval = GetTickCount() - m_dwLastHeartbeat;
+ if (m_iHartbeatInterval && iInterval > m_iHartbeatInterval)
+ GatewaySendHeartbeat();
+ }
unsigned char buf[2048];
int bufSize = Netlib_Recv(m_hGatewayConnection, (char*)buf+offset, _countof(buf) - offset, 0);
@@ -201,12 +208,21 @@ void CDiscordProto::GatewayThread(void*)
debugLogA("Got packet: buffer = %d, opcode = %d, headerSize = %d, final = %d, masked = %d", bufSize, opCode, headerSize, bIsFinal, bIsMasked);
+ // we have some additional data, not only opcode
if (bufSize > headerSize) {
- size_t newSize = dataBufSize + bufSize - headerSize;
- dataBuf = (unsigned char*)mir_realloc(dataBuf, newSize);
- memcpy(dataBuf + dataBufSize, buf + headerSize, bufSize - headerSize);
- dataBufSize = newSize;
- debugLogA("data buffer reallocated to %d bytes", dataBufSize);
+ if (bIsFinal && dataBuf == NULL) { // it fits, no need to reallocate a buffer
+ bDataBufAllocated = false;
+ dataBuf = (char*)buf + headerSize;
+ dataBufSize = bufSize - headerSize;
+ }
+ else {
+ size_t newSize = dataBufSize + bufSize - headerSize;
+ dataBuf = (char*)mir_realloc(dataBuf, newSize+1);
+ memcpy(dataBuf + dataBufSize, buf + headerSize, bufSize - headerSize);
+ dataBufSize = newSize;
+ debugLogA("data buffer reallocated to %d bytes", dataBufSize);
+ }
+ dataBuf[dataBufSize] = 0;
}
if (dataBufSize < payloadSize)
@@ -218,12 +234,12 @@ void CDiscordProto::GatewayThread(void*)
case 2: // continuation
if (bIsFinal) {
// process a packet here
- z_stream stream = {};
- stream.next_in = dataBuf + headerSize;
- stream.avail_in = bufSize - headerSize;
- deflate(&stream, true);
-
- mir_free(dataBuf); dataBuf = NULL;
+ JSONNode root = JSONNode::parse(dataBuf);
+ if (root)
+ GatewayProcess(root);
+ if (bDataBufAllocated)
+ mir_free(dataBuf);
+ dataBuf = NULL;
dataBufSize = 0;
}
break;
@@ -244,3 +260,45 @@ void CDiscordProto::GatewayThread(void*)
m_hGatewayConnection = NULL;
ShutdownSession();
}
+
+void CDiscordProto::GatewayProcess(const JSONNode &pRoot)
+{
+ int opCode = pRoot["op"].as_int();
+ switch (opCode) {
+ case 10: // hello
+ m_iHartbeatInterval = pRoot["d"]["heartbeat_interval"].as_int();
+ m_dwLastHeartbeat = GetTickCount();
+ m_iGatewaySeq = 1;
+
+ GatewaySendIdentify();
+ break;
+ }
+}
+
+void CDiscordProto::GatewaySendHeartbeat()
+{
+ JSONNode root;
+ root << INT_PARAM("op", 1) << INT_PARAM("d", m_iGatewaySeq);
+ GatewaySend(1, root);
+}
+
+void CDiscordProto::GatewaySendIdentify()
+{
+ wchar_t wszOs[256];
+ GetOSDisplayString(wszOs, _countof(wszOs));
+
+ char szVersion[256];
+ Miranda_GetVersionText(szVersion, _countof(szVersion));
+
+ JSONNode props; props.set_name("properties");
+ props << WCHAR_PARAM("$os", L"Windows") << CHAR_PARAM("$browser", "Miranda NG") << CHAR_PARAM("$device", "Miranda NG")
+ << CHAR_PARAM("$referrer", "") << CHAR_PARAM("$referring_domain", "");
+
+ JSONNode shards(JSON_ARRAY); shards.set_name("shard");
+ shards << INT_PARAM("", 1) << INT_PARAM("", 10);
+
+ JSONNode root;
+ root << CHAR_PARAM("token", m_szAccessToken) << BOOL_PARAM("compress", false) << INT_PARAM("large_threshold", 250);
+ root << props; // << shards;
+ GatewaySend(1, root);
+}
diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h
index b822bbf323..aa4e36ef1c 100644
--- a/protocols/Discord/src/proto.h
+++ b/protocols/Discord/src/proto.h
@@ -26,6 +26,15 @@ struct PARAM
{}
};
+struct BOOL_PARAM : public PARAM
+{
+ bool bValue;
+ __forceinline BOOL_PARAM(LPCSTR _name, bool _value) :
+ PARAM(_name), bValue(_value)
+ {}
+};
+AsyncHttpRequest* operator<<(AsyncHttpRequest*, const BOOL_PARAM&);
+
struct INT_PARAM : public PARAM
{
int iValue;
@@ -54,6 +63,7 @@ struct WCHAR_PARAM : public PARAM
AsyncHttpRequest* operator<<(AsyncHttpRequest*, const WCHAR_PARAM&);
JSONNode& operator<<(JSONNode &json, const INT_PARAM &param);
+JSONNode& operator<<(JSONNode &json, const BOOL_PARAM &param);
JSONNode& operator<<(JSONNode &json, const CHAR_PARAM &param);
JSONNode& operator<<(JSONNode &json, const WCHAR_PARAM &param);
@@ -123,10 +133,19 @@ class CDiscordProto : public PROTO<CDiscordProto>
HANDLE
m_hGatewayNetlibUser, // the separate netlib user handle for gateways
m_hGatewayConnection; // gateway connection
+
void __cdecl GatewayThread(void*);
- void GatewaySend(int opCode, const char*);
+ void GatewaySend(int opCode, const JSONNode&);
+ void GatewayProcess(const JSONNode&);
+
+ void GatewaySendHeartbeat(void);
+ void GatewaySendIdentify(void);
+
+ void OnReceiveGateway(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
- void OnReceiveGateway(NETLIBHTTPREQUEST*, AsyncHttpRequest*);
+ int m_iHartbeatInterval;
+ int m_iGatewaySeq; // gateway sequence number
+ DWORD m_dwLastHeartbeat;
//////////////////////////////////////////////////////////////////////////////////////
// options
diff --git a/protocols/Discord/src/utils.cpp b/protocols/Discord/src/utils.cpp
index 0513e49aac..ce000c0989 100644
--- a/protocols/Discord/src/utils.cpp
+++ b/protocols/Discord/src/utils.cpp
@@ -32,6 +32,12 @@ JSONNode& operator<<(JSONNode &json, const INT_PARAM &param)
return json;
}
+JSONNode& operator<<(JSONNode &json, const BOOL_PARAM &param)
+{
+ json.push_back(JSONNode(param.szName, param.bValue));
+ return json;
+}
+
JSONNode& operator<<(JSONNode &json, const CHAR_PARAM &param)
{
json.push_back(JSONNode(param.szName, param.szValue));