X-Git-Url: https://git.quassel-irc.org/?a=blobdiff_plain;f=src%2Fcore%2Fnetworkconnection.cpp;h=97c573a8844e7b3c497c88dfb33ee58359061ba6;hb=21d45dff3595e630e2c4ebce453fe53271fa609d;hp=be2787aa3009e5bd04c8e5ca54a243f962b3c1aa;hpb=2bcb925e42921b124efd19d1978ff19f44a09113;p=quassel.git diff --git a/src/core/networkconnection.cpp b/src/core/networkconnection.cpp index be2787aa..97c573a8 100644 --- a/src/core/networkconnection.cpp +++ b/src/core/networkconnection.cpp @@ -36,30 +36,39 @@ #include "userinputhandler.h" #include "ctcphandler.h" -NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : QObject(network), +NetworkConnection::NetworkConnection(Network *network, CoreSession *session) + : QObject(network), _connectionState(Network::Disconnected), _network(network), _coreSession(session), _ircServerHandler(new IrcServerHandler(this)), _userInputHandler(new UserInputHandler(this)), _ctcpHandler(new CtcpHandler(this)), - _autoReconnectCount(0) + _autoReconnectCount(0), + _quitRequested(false), + + _previousConnectionAttemptFailed(false), + _lastUsedServerlistIndex(0), + + // TODO make autowho configurable (possibly per-network) + _autoWhoEnabled(true), + _autoWhoInterval(90), + _autoWhoNickLimit(0), // unlimited + _autoWhoDelay(3), + + // TokenBucket to avaid sending too much at once + _messagesPerSecond(1), + _burstSize(5), + _tokenBucket(5) // init with a full bucket { _autoReconnectTimer.setSingleShot(true); - - _previousConnectionAttemptFailed = false; - _lastUsedServerlistIndex = 0; - - // TODO make autowho configurable (possibly per-network) - _autoWhoEnabled = true; - _autoWhoInterval = 90; - _autoWhoNickLimit = 0; // unlimited - _autoWhoDelay = 3; - + _socketCloseTimer.setSingleShot(true); + connect(&_socketCloseTimer, SIGNAL(timeout()), this, SLOT(socketCloseTimeout())); + _autoWhoTimer.setInterval(_autoWhoDelay * 1000); - _autoWhoTimer.setSingleShot(false); _autoWhoCycleTimer.setInterval(_autoWhoInterval * 1000); - _autoWhoCycleTimer.setSingleShot(false); + + _tokenBucketTimer.start(_messagesPerSecond * 1000); QHash channels = coreSession()->persistentChannels(networkId()); foreach(QString chan, channels.keys()) { @@ -69,6 +78,7 @@ NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : Q connect(&_autoReconnectTimer, SIGNAL(timeout()), this, SLOT(doAutoReconnect())); connect(&_autoWhoTimer, SIGNAL(timeout()), this, SLOT(sendAutoWho())); connect(&_autoWhoCycleTimer, SIGNAL(timeout()), this, SLOT(startAutoWhoCycle())); + connect(&_tokenBucketTimer, SIGNAL(timeout()), this, SLOT(fillBucketAndProcessQueue())); connect(network, SIGNAL(currentServerSet(const QString &)), this, SLOT(networkInitialized(const QString &))); connect(network, SIGNAL(useAutoReconnectSet(bool)), this, SLOT(autoReconnectSettingsChanged())); @@ -95,6 +105,7 @@ NetworkConnection::NetworkConnection(Network *network, CoreSession *session) : Q NetworkConnection::~NetworkConnection() { if(connectionState() != Network::Disconnected && connectionState() != Network::Reconnecting) disconnectFromIrc(false); // clean up, but this does not count as requested disconnect! + disconnect(&socket, 0, this, 0); // this keeps the socket from triggering events during clean up delete _ircServerHandler; delete _userInputHandler; delete _ctcpHandler; @@ -243,11 +254,14 @@ void NetworkConnection::disconnectFromIrc(bool requested) { if(socket.state() < QAbstractSocket::ConnectedState) { setConnectionState(Network::Disconnected); socketDisconnected(); - } else socket.disconnectFromHost(); - - if(requested) { - emit quitRequested(networkId()); + } else { + _socketCloseTimer.start(10000); // the irc server has 10 seconds to close the socket } + + // this flag triggers quitRequested() once the socket is closed + // it is needed to determine whether or not the connection needs to be + // in the automatic session restore. + _quitRequested = requested; } void NetworkConnection::socketHasData() { @@ -276,6 +290,7 @@ void NetworkConnection::socketError(QAbstractSocket::SocketError) { #ifndef QT_NO_OPENSSL void NetworkConnection::sslErrors(const QList &sslErrors) { + Q_UNUSED(sslErrors) socket.ignoreSslErrors(); /* TODO errorhandling QVariantMap errmsg; @@ -350,18 +365,26 @@ void NetworkConnection::socketStateChanged(QAbstractSocket::SocketState socketSt setConnectionState(state); } +void NetworkConnection::socketCloseTimeout() { + socket.disconnectFromHost(); +} + void NetworkConnection::socketDisconnected() { _autoWhoCycleTimer.stop(); _autoWhoTimer.stop(); _autoWhoQueue.clear(); _autoWhoInProgress.clear(); + _socketCloseTimer.stop(); + network()->setConnected(false); emit disconnected(networkId()); if(_autoReconnectCount != 0) { setConnectionState(Network::Reconnecting); if(_autoReconnectCount == network()->autoReconnectRetries()) doAutoReconnect(); // first try is immediate else _autoReconnectTimer.start(); + } else if(_quitRequested) { + emit quitRequested(networkId()); } } @@ -380,8 +403,27 @@ void NetworkConnection::userInput(BufferInfo buf, QString msg) { } void NetworkConnection::putRawLine(QByteArray s) { + if(_tokenBucket > 0) { + writeToSocket(s); + } else { + _msgQueue.append(s); + } +} + +void NetworkConnection::writeToSocket(QByteArray s) { s += "\r\n"; socket.write(s); + _tokenBucket--; +} + +void NetworkConnection::fillBucketAndProcessQueue() { + if(_tokenBucket < _burstSize) { + _tokenBucket++; + } + + while(_msgQueue.size() > 0 && _tokenBucket > 0) { + writeToSocket(_msgQueue.takeFirst()); + } } void NetworkConnection::putCmd(const QString &cmd, const QVariantList ¶ms, const QByteArray &prefix) { @@ -405,7 +447,7 @@ void NetworkConnection::sendAutoWho() { IrcChannel *ircchan = network()->ircChannel(chan); if(!ircchan) continue; if(_autoWhoNickLimit > 0 && ircchan->ircUsers().count() > _autoWhoNickLimit) continue; - _autoWhoInProgress.insert(chan); + _autoWhoInProgress[chan]++; putRawLine("WHO " + serverEncode(chan)); if(_autoWhoQueue.isEmpty() && _autoWhoEnabled && !_autoWhoCycleTimer.isActive()) { // Timer was stopped, means a new cycle is due immediately @@ -425,18 +467,20 @@ void NetworkConnection::startAutoWhoCycle() { } bool NetworkConnection::setAutoWhoDone(const QString &channel) { - return _autoWhoInProgress.remove(channel); + if(_autoWhoInProgress.value(channel.toLower(), 0) <= 0) return false; + _autoWhoInProgress[channel.toLower()]--; + return true; } void NetworkConnection::setChannelJoined(const QString &channel) { emit channelJoined(networkId(), channel, _channelKeys[channel.toLower()]); - _autoWhoQueue.prepend(channel); // prepend so this new chan is the first to be checked + _autoWhoQueue.prepend(channel.toLower()); // prepend so this new chan is the first to be checked } void NetworkConnection::setChannelParted(const QString &channel) { removeChannelKey(channel); - _autoWhoQueue.removeAll(channel); - _autoWhoInProgress.remove(channel); + _autoWhoQueue.removeAll(channel.toLower()); + _autoWhoInProgress.remove(channel.toLower()); emit channelParted(networkId(), channel); }