tdesktop/Telegram/SourceFiles/mtproto/session.h

361 lines
8.9 KiB
C++

/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "mtproto/connection.h"
#include "mtproto/dcenter.h"
#include "mtproto/rpc_sender.h"
#include "core/single_timer.h"
namespace MTP {
namespace internal {
class ReceivedMsgIds {
public:
bool registerMsgId(mtpMsgId msgId, bool needAck) {
auto i = _idsNeedAck.constFind(msgId);
if (i == _idsNeedAck.cend()) {
if (_idsNeedAck.size() < MTPIdsBufferSize || msgId > min()) {
_idsNeedAck.insert(msgId, needAck);
return true;
}
MTP_LOG(-1, ("No need to handle - %1 < min = %2").arg(msgId).arg(min()));
} else {
MTP_LOG(-1, ("No need to handle - %1 already is in map").arg(msgId));
}
return false;
}
mtpMsgId min() const {
return _idsNeedAck.isEmpty() ? 0 : _idsNeedAck.cbegin().key();
}
mtpMsgId max() const {
auto end = _idsNeedAck.cend();
return _idsNeedAck.isEmpty() ? 0 : (--end).key();
}
void shrink() {
auto size = _idsNeedAck.size();
while (size-- > MTPIdsBufferSize) {
_idsNeedAck.erase(_idsNeedAck.begin());
}
}
enum class State {
NotFound,
NeedsAck,
NoAckNeeded,
};
State lookup(mtpMsgId msgId) const {
auto i = _idsNeedAck.constFind(msgId);
if (i == _idsNeedAck.cend()) {
return State::NotFound;
}
return i.value() ? State::NeedsAck : State::NoAckNeeded;
}
void clear() {
_idsNeedAck.clear();
}
private:
QMap<mtpMsgId, bool> _idsNeedAck;
};
class Session;
class SessionData {
public:
SessionData(Session *creator) : _owner(creator) {
}
void setSession(uint64 session) {
DEBUG_LOG(("MTP Info: setting server_session: %1").arg(session));
QWriteLocker locker(&lock);
if (_session != session) {
_session = session;
_messagesSent = 0;
}
}
uint64 getSession() const {
QReadLocker locker(&lock);
return _session;
}
bool layerWasInited() const {
QReadLocker locker(&lock);
return _layerInited;
}
void setLayerWasInited(bool was) {
QWriteLocker locker(&lock);
_layerInited = was;
}
void setSalt(uint64 salt) {
QWriteLocker locker(&lock);
_salt = salt;
}
uint64 getSalt() const {
QReadLocker locker(&lock);
return _salt;
}
const AuthKeyPtr &getKey() const {
return _authKey;
}
void setKey(const AuthKeyPtr &key) {
if (_authKey != key) {
uint64 session = rand_value<uint64>();
_authKey = key;
DEBUG_LOG(("MTP Info: new auth key set in SessionData, id %1, setting random server_session %2").arg(key ? key->keyId() : 0).arg(session));
QWriteLocker locker(&lock);
if (_session != session) {
_session = session;
_messagesSent = 0;
}
_layerInited = false;
}
}
bool isCheckedKey() const {
QReadLocker locker(&lock);
return _keyChecked;
}
void setCheckedKey(bool checked) {
QWriteLocker locker(&lock);
_keyChecked = checked;
}
QReadWriteLock *keyMutex() const;
QReadWriteLock *toSendMutex() const {
return &toSendLock;
}
QReadWriteLock *haveSentMutex() const {
return &haveSentLock;
}
QReadWriteLock *toResendMutex() const {
return &toResendLock;
}
QReadWriteLock *wereAckedMutex() const {
return &wereAckedLock;
}
QReadWriteLock *receivedIdsMutex() const {
return &receivedIdsLock;
}
QReadWriteLock *haveReceivedMutex() const {
return &haveReceivedLock;
}
QReadWriteLock *stateRequestMutex() const {
return &stateRequestLock;
}
mtpPreRequestMap &toSendMap() {
return toSend;
}
const mtpPreRequestMap &toSendMap() const {
return toSend;
}
mtpRequestMap &haveSentMap() {
return haveSent;
}
const mtpRequestMap &haveSentMap() const {
return haveSent;
}
mtpRequestIdsMap &toResendMap() { // msgId -> requestId, on which toSend: requestId -> request for resended requests
return toResend;
}
const mtpRequestIdsMap &toResendMap() const {
return toResend;
}
ReceivedMsgIds &receivedIdsSet() {
return receivedIds;
}
const ReceivedMsgIds &receivedIdsSet() const {
return receivedIds;
}
mtpRequestIdsMap &wereAckedMap() {
return wereAcked;
}
const mtpRequestIdsMap &wereAckedMap() const {
return wereAcked;
}
mtpResponseMap &haveReceivedMap() {
return haveReceived;
}
const mtpResponseMap &haveReceivedMap() const {
return haveReceived;
}
mtpMsgIdsSet &stateRequestMap() {
return stateRequest;
}
const mtpMsgIdsSet &stateRequestMap() const {
return stateRequest;
}
mtpRequestId nextFakeRequestId() { // must be locked by haveReceivedMutex()
if (haveReceived.isEmpty() || haveReceived.cbegin().key() > 0) {
_fakeRequestId = -2000000000;
} else {
++_fakeRequestId;
}
return _fakeRequestId;
}
Session *owner() {
return _owner;
}
const Session *owner() const {
return _owner;
}
uint32 nextRequestSeqNumber(bool needAck = true) {
QWriteLocker locker(&lock);
uint32 result(_messagesSent);
_messagesSent += (needAck ? 1 : 0);
return result * 2 + (needAck ? 1 : 0);
}
void clear();
private:
uint64 _session = 0;
uint64 _salt = 0;
uint32 _messagesSent = 0;
mtpRequestId _fakeRequestId = -2000000000;
Session *_owner = nullptr;
AuthKeyPtr _authKey;
bool _keyChecked = false;
bool _layerInited = false;
mtpPreRequestMap toSend; // map of request_id -> request, that is waiting to be sent
mtpRequestMap haveSent; // map of msg_id -> request, that was sent, msDate = 0 for msgs_state_req (no resend / state req), msDate = 0, seqNo = 0 for containers
mtpRequestIdsMap toResend; // map of msg_id -> request_id, that request_id -> request lies in toSend and is waiting to be resent
ReceivedMsgIds receivedIds; // set of received msg_id's, for checking new msg_ids
mtpRequestIdsMap wereAcked; // map of msg_id -> request_id, this msg_ids already were acked or do not need ack
mtpResponseMap haveReceived; // map of request_id -> response, that should be processed in other thread
mtpMsgIdsSet stateRequest; // set of msg_id's, whose state should be requested
// mutexes
mutable QReadWriteLock lock;
mutable QReadWriteLock toSendLock;
mutable QReadWriteLock haveSentLock;
mutable QReadWriteLock toResendLock;
mutable QReadWriteLock receivedIdsLock;
mutable QReadWriteLock wereAckedLock;
mutable QReadWriteLock haveReceivedLock;
mutable QReadWriteLock stateRequestLock;
};
class Session : public QObject {
Q_OBJECT
public:
Session(int32 dcenter);
void restart();
void stop();
void kill();
void unpaused();
int32 getDcWithShift() const;
QReadWriteLock *keyMutex() const;
void notifyKeyCreated(const AuthKeyPtr &key);
void destroyKey();
void notifyLayerInited(bool wasInited);
template <typename TRequest>
mtpRequestId send(const TRequest &request, RPCResponseHandler callbacks = RPCResponseHandler(), TimeMs msCanWait = 0, bool needsLayer = false, bool toMainDC = false, mtpRequestId after = 0); // send mtp request
void ping();
void cancel(mtpRequestId requestId, mtpMsgId msgId);
int32 requestState(mtpRequestId requestId) const;
int32 getState() const;
QString transport() const;
void sendPrepared(const mtpRequest &request, TimeMs msCanWait = 0, bool newRequest = true); // nulls msgId and seqNo in request, if newRequest = true
~Session();
signals:
void authKeyCreated();
void needToSend();
void needToPing();
void needToRestart();
public slots:
void needToResumeAndSend();
mtpRequestId resend(quint64 msgId, qint64 msCanWait = 0, bool forceContainer = false, bool sendMsgStateInfo = false);
void resendMany(QVector<quint64> msgIds, qint64 msCanWait, bool forceContainer, bool sendMsgStateInfo);
void resendAll(); // after connection restart
void authKeyCreatedForDC();
void layerWasInitedForDC(bool wasInited);
void tryToReceive();
void checkRequestsByTimer();
void onConnectionStateChange(qint32 newState);
void onResetDone();
void sendAnything(qint64 msCanWait = 0);
void sendPong(quint64 msgId, quint64 pingId);
void sendMsgsStateInfo(quint64 msgId, QByteArray data);
private:
void createDcData();
Connection *_connection;
bool _killed;
bool _needToReceive;
SessionData data;
int32 dcWithShift;
DcenterPtr dc;
TimeMs msSendCall, msWait;
bool _ping;
QTimer timeouter;
SingleTimer sender;
};
inline QReadWriteLock *SessionData::keyMutex() const {
return _owner->keyMutex();
}
MTPrpcError rpcClientError(const QString &type, const QString &description = QString());
} // namespace internal
} // namespace MTP