[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[vidalia-svn] r1914: Do the control connection asynchronously so we aren't trying (in trunk: . src/control)
Author: edmanm
Date: 2007-09-05 23:51:37 -0400 (Wed, 05 Sep 2007)
New Revision: 1914
Modified:
trunk/
trunk/src/control/controlconnection.cpp
trunk/src/control/controlconnection.h
trunk/src/control/controlsocket.cpp
trunk/src/control/controlsocket.h
Log:
r2131@adrastea: edmanm | 2007-09-05 23:48:45 -0400
Do the control connection asynchronously so we aren't trying to call
waitForConnected() in the control socket thread before it has an event loop
running.
Property changes on: trunk
___________________________________________________________________
svk:merge ticket from /vidalia/local/trunk [r2131] on 54b3572a-7227-0410-958f-53ecd705b71a
Modified: trunk/src/control/controlconnection.cpp
===================================================================
--- trunk/src/control/controlconnection.cpp 2007-09-06 03:51:29 UTC (rev 1913)
+++ trunk/src/control/controlconnection.cpp 2007-09-06 03:51:37 UTC (rev 1914)
@@ -33,16 +33,16 @@
#include "controlconnection.h"
/** Maximum number of times we'll try to connect to Tor before giving up.*/
-#define MAX_CONNECT_ATTEMPTS 6
-/** Time to wait between control connection attempts (in microseconds). */
-#define CONNECT_RETRY_DELAY 500*1000
+#define MAX_CONNECT_ATTEMPTS 5
+/** Time to wait between control connection attempts (in milliseconds). */
+#define CONNECT_RETRY_DELAY 2*1000
/** Default constructor. */
ControlConnection::ControlConnection(TorEvents *events)
{
_events = events;
- _sock = 0;
+ _status = Unset;
}
/** Destructor. */
@@ -58,81 +58,141 @@
void
ControlConnection::connect(QHostAddress addr, quint16 port)
{
+ if (isRunning()) {
+ vError("Bug: Tried to call ControlConnection::connect() when the "
+ "control thread is already running.");
+ return;
+ }
+
/* Save the destination information */
_addr = addr;
_port = port;
+ _sock = 0;
+ _connectAttempt = 0;
+ setStatus(Connecting);
+
/* Kick off the thread in which the control socket will live */
QThread::start();
}
/** Attempt to establish a connection to Tor's control interface. We will try
-* a maximum of MAX_CONNECT_ATTEMPTS, waiting CONNECT_RETRY_DELAY between each
-* attempt, to give slow Tors a chance to finish binding their control port. */
-bool
+ * a maximum of MAX_CONNECT_ATTEMPTS, waiting CONNECT_RETRY_DELAY between each
+ * attempt, to give slow Tors a chance to finish binding their control port. */
+void
ControlConnection::connect()
{
- QString errmsg;
- bool result;
-
- setStatus(Connecting);
- for (int i = 0; i < MAX_CONNECT_ATTEMPTS; i++) {
- /* Check if we're supposed to cancel our attempt to connect */
- if (status() != Connecting) {
- vNotice("Cancelling attempt to connect to Tor's control port.");
- return false;
- }
-
- /* Try to connect */
- _connMutex.lock();
- vDebug("Control connection attempt %1 of %2").arg(i+1)
- .arg(MAX_CONNECT_ATTEMPTS);
- result = _sock->connect(_addr, _port, &errmsg);
- _connMutex.unlock();
- if (result) {
- vInfo("Connected to Tor's control port.");
- setStatus(Connected);
- emit connected();
- return true;
- }
- QThread::usleep(CONNECT_RETRY_DELAY);
- }
- setStatus(Disconnected);
- vWarn("Failed to connect to Tor's control port after %1 attempts: %2")
- .arg(MAX_CONNECT_ATTEMPTS)
- .arg(errmsg);
- emit connectFailed(errmsg);
- return false;
+ _connectAttempt++;
+ vInfo("Connecting to Tor (Attempt %1 of %2)").arg(_connectAttempt)
+ .arg(MAX_CONNECT_ATTEMPTS);
+ _connMutex.lock();
+ _sock->connectToHost(_addr, _port);
+ _connMutex.unlock();
}
/** Disconnect from Tor's control interface. */
void
ControlConnection::disconnect()
{
- /* Stop the event loop */
+ setStatus(Disconnecting);
+ _connMutex.lock();
+ _sock->disconnectFromHost();
+ _connMutex.unlock();
+}
+
+/** Called when the control socket is connected. This method checks that the
+ * control protocol version of the Tor we connected to is at least V1. */
+void
+ControlConnection::onConnected()
+{
+ setStatus(Connected);
+ emit connected();
+}
+
+/** Called when the control socket is disconnected and stops the control
+ * thread's event loop. */
+void
+ControlConnection::onDisconnected()
+{
+ setStatus(Disconnected);
+ emit disconnected();
exit(0);
}
+/** Called when the control socket encounters <b>error</b>. */
+void
+ControlConnection::onError(QAbstractSocket::SocketError error)
+{
+ if (status() == Connecting) {
+ /* If we got a 'connection refused' and we haven't exceeded
+ * MAX_CONNECT_ATTEMPTS, then try to reconnect since Tor is probably
+ * running, but it doesn't have a ControlPort open yet. */
+ if (error == QAbstractSocket::ConnectionRefusedError &&
+ _connectAttempt < MAX_CONNECT_ATTEMPTS) {
+ vInfo("Control connection refused. Retrying in %1ms.")
+ .arg(CONNECT_RETRY_DELAY);
+ _connectTimer->start(CONNECT_RETRY_DELAY);
+ } else {
+ /* Exceeded maximum number of connect attempts. Give up. */
+ QString errstr = ControlSocket::toString(error);
+ vWarn("Vidalia was unable to connect to Tor: %1").arg(errstr);
+ emit connectFailed(tr("Vidalia was unable to connect to Tor. (%1)")
+ .arg(errstr));
+ setStatus(Disconnected);
+ }
+ } else if (error == QAbstractSocket::RemoteHostClosedError) {
+ /* Tor closed the connection. This is common when we send a 'shutdown' or
+ * 'halt' signal to Tor and doesn't need to be logged as loudly. */
+ vNotice("Tor closed the control connection.");
+ } else {
+ /* Some other error. */
+ /*XXX We may want to be emitting these so the GUI thread can learn about
+ * them and display an error message. */
+ vWarn("Control socket error: %1").arg(ControlSocket::toString(error));
+ }
+}
+
/** Cancels a pending control connection to Tor. */
void
ControlConnection::cancelConnect()
{
- QMutexLocker locker(&_connMutex);
- _status = Disconnected;
+ vNotice("Control connection attempt cancelled.");
+ setStatus(Disconnected);
+ exit(0);
}
/** Returns the status of the control connection. */
ControlConnection::Status
ControlConnection::status()
{
- QMutexLocker locker(&_connMutex);
+ QMutexLocker locker(&_statusMutex);
return _status;
}
+/** Returns a string description of the control Status value
+ * <b>status</b>. */
+QString
+ControlConnection::statusString(Status status)
+{
+ QString str;
+ switch (status) {
+ case Unset: str = "Unset"; break;
+ case Disconnected: str = "Disconnected"; break;
+ case Disconnecting: str = "Disconnecting"; break;
+ case Connecting: str = "Connecting"; break;
+ case Connected: str = "Connected"; break;
+ default: str = "unknown";
+ }
+ return str;
+}
+
/** Sets the control connection status. */
void
ControlConnection::setStatus(Status status)
{
- QMutexLocker locker(&_connMutex);
+ QMutexLocker locker(&_statusMutex);
+ vNotice("Control connection status changed from '%1' to '%2'")
+ .arg(statusString(_status))
+ .arg(statusString(status));
_status = status;
}
@@ -178,6 +238,8 @@
/* Check for a valid and connected socket */
_connMutex.lock();
if (!_sock || _status != Connected) {
+ vDebug("Unable to send control command '%1' when socket status is '%2'")
+ .arg(cmd.keyword()).arg(_status);
_connMutex.unlock();
return err(errmsg, tr("Control socket is not connected."));
}
@@ -278,29 +340,36 @@
/* Create a new control socket */
_connMutex.lock();
_sock = new ControlSocket();
+ _connectTimer = new QTimer();
+ _connectTimer->setSingleShot(true);
+
QObject::connect(_sock, SIGNAL(readyRead()), this, SLOT(onReadyRead()),
Qt::DirectConnection);
- QObject::connect(_sock, SIGNAL(disconnected()), this, SLOT(quit()),
+ QObject::connect(_sock, SIGNAL(disconnected()), this, SLOT(onDisconnected()),
Qt::DirectConnection);
+ QObject::connect(_sock, SIGNAL(connected()), this, SLOT(onConnected()),
+ Qt::DirectConnection);
+ QObject::connect(_sock, SIGNAL(error(QAbstractSocket::SocketError)),
+ this, SLOT(onError(QAbstractSocket::SocketError)),
+ Qt::DirectConnection);
+ QObject::connect(_connectTimer, SIGNAL(timeout()), this, SLOT(connect()),
+ Qt::DirectConnection);
+
_sock->installEventFilter(this);
_connMutex.unlock();
/* Attempt to connect to Tor */
- if (connect()) {
- vDebug("Starting control connection event loop.");
- /* Kick off the event loop */
- exec();
- vDebug("Exited control connection event loop.");
- }
-
- /* Update the connection status */
- setStatus(Disconnected);
- emit disconnected();
+ connect();
+ vDebug("Starting control connection event loop.");
+ exec();
+ vDebug("Exited control connection event loop.");
/* Clean up the socket */
_connMutex.lock();
- QObject::disconnect(_sock, 0, 0, 0);
+ _sock->removeEventFilter(this);
+ _sock->disconnect(this);
delete _sock;
+ delete _connectTimer;
_sock = 0;
_connMutex.unlock();
Modified: trunk/src/control/controlconnection.h
===================================================================
--- trunk/src/control/controlconnection.h 2007-09-06 03:51:29 UTC (rev 1913)
+++ trunk/src/control/controlconnection.h 2007-09-06 03:51:37 UTC (rev 1914)
@@ -33,6 +33,8 @@
#include <QMutex>
#include <QQueue>
#include <QWaitCondition>
+#include <QTimer>
+#include <QHostAddress>
#include "eventtype.h"
#include "controlsocket.h"
@@ -46,9 +48,11 @@
public:
/** Control connection status */
enum Status {
- Disconnected, /**< Control connection disconnected. */
- Connecting, /**< Control connection attempt pending. */
- Connected /**< Control connection established. */
+ Unset, /**< Control connection status is not yet set. */
+ Disconnected, /**< Control connection disconnected. */
+ Disconnecting, /**< Control connection is disconnecting. */
+ Connecting, /**< Control connection attempt pending. */
+ Connected /**< Control connection established. */
};
/** Default constructor. */
@@ -82,14 +86,22 @@
bool eventFilter(QObject *obj, QEvent *event);
private slots:
+ /** Connects to Tor's control interface. */
+ void connect();
/** Called when there is data on the control socket. */
void onReadyRead();
+ /** Called when the control socket is connected. */
+ void onConnected();
+ /** Called when the control socket is disconnected. */
+ void onDisconnected();
+ /** Called when the control socket encounters an error. */
+ void onError(QAbstractSocket::SocketError error);
private:
/** Sets the control connection status. */
void setStatus(Status status);
- /** Connects to Tor's control interface. */
- bool connect();
+ /** Returns the string description of <b>status</b>. */
+ QString statusString(Status status);
/** Main thread implementation. */
void run();
@@ -100,6 +112,10 @@
quint16 _port; /**< Port of Tor's control interface. */
QMutex _connMutex; /**< Mutex around the control socket. */
QMutex _recvMutex; /**< Mutex around the queue of ReceiveWaiters. */
+ QMutex _statusMutex; /**< Mutex around the connection status value. */
+ int _connectAttempt; /**< How many times we've tried to connect to Tor while
+ waiting for Tor to start. */
+ QTimer* _connectTimer; /**< Timer used to delay connect attempts. */
/** Private class used to wait for a response to a control command. */
class ReceiveWaiter {
Modified: trunk/src/control/controlsocket.cpp
===================================================================
--- trunk/src/control/controlsocket.cpp 2007-09-06 03:51:29 UTC (rev 1913)
+++ trunk/src/control/controlsocket.cpp 2007-09-06 03:51:37 UTC (rev 1914)
@@ -25,17 +25,11 @@
* \brief Socket used to connect to Tor's control interface
*/
-#include <QHostAddress>
#include <util/string.h>
#include <vidalia.h>
#include "controlsocket.h"
-/** Give up after waiting five seconds for the control socket to connect to
-* Tor. This timeout used to be shorter (three seconds), but some Agnitum
-* OutPost users yelled at us wanting a longer timeout, for some reason. */
-#define CONN_TIMEOUT 5000
-
/** Timeout reads in 250ms. We can set this to a short value because if there
* isn't any data to read, we want to return anyway. */
#define READ_TIMEOUT 250
@@ -46,42 +40,6 @@
{
}
-/** Connects to Tor's control socket on the specified host and port. If the
- * connection is successful, true is returned. If the connection fails, then
- * this function returns false and sets <b>errmsg</b> appropriately, if not
- * null. */
-bool
-ControlSocket::connect(QHostAddress addr, quint16 port, QString *errmsg)
-{
- /* Connect the control socket. */
- vNotice("Connecting to Tor's control port on %1:%2").arg(addr.toString())
- .arg(port);
- connectToHost(addr, port);
- if (!waitForConnected(CONN_TIMEOUT)) {
- vWarn("Failed to connect to Tor's control port: %1").arg(errorString());
- return err(errmsg, tr("Error connecting to %1:%2 [%3]")
- .arg(addr.toString())
- .arg(port)
- .arg(errorString()));
- }
- return true;
-}
-
-/** Disconnects from Tor's control socket */
-bool
-ControlSocket::disconnect(QString *errmsg)
-{
- disconnectFromHost();
- if (isConnected()) {
- if (!waitForDisconnected(CONN_TIMEOUT)) {
- vWarn("Failed to disconnect from Tor: %1").arg(errorString());
- return err(errmsg, tr("Error disconnecting socket. [%1]")
- .arg(errorString()));
- }
- }
- return true;
-}
-
/** Returns true if the control socket is connected and ready to send or
* receive. */
bool
@@ -215,3 +173,37 @@
return true;
}
+/** Returns the string description of <b>error</b>. */
+QString
+ControlSocket::toString(const QAbstractSocket::SocketError error)
+{
+ QString str;
+ switch (error) {
+ case ConnectionRefusedError:
+ str = "Connection refused by peer."; break;
+ case RemoteHostClosedError:
+ str = "Remote host closed the connection."; break;
+ case HostNotFoundError:
+ str = "Host address not found."; break;
+ case SocketAccessError:
+ str = "Insufficient access privileges."; break;
+ case SocketResourceError:
+ str = "Insufficient resources."; break;
+ case SocketTimeoutError:
+ str = "Socket operation timed out."; break;
+ case DatagramTooLargeError:
+ str = "Datagram size exceeded the operating system limit."; break;
+ case NetworkError:
+ str = "Network error occurred."; break;
+ case AddressInUseError:
+ str = "Specified address already in use."; break;
+ case SocketAddressNotAvailableError:
+ str = "Specified address does not belong to the host."; break;
+ case UnsupportedSocketOperationError:
+ str = "The requested operation is not supported."; break;
+ default:
+ str = "An unidentified error occurred."; break;
+ }
+ return str;
+}
+
Modified: trunk/src/control/controlsocket.h
===================================================================
--- trunk/src/control/controlsocket.h 2007-09-06 03:51:29 UTC (rev 1913)
+++ trunk/src/control/controlsocket.h 2007-09-06 03:51:37 UTC (rev 1914)
@@ -29,7 +29,6 @@
#define _CONTROLSOCKET_H
#include <QTcpSocket>
-#include <QHostAddress>
#include "controlcommand.h"
#include "controlreply.h"
@@ -40,14 +39,9 @@
Q_OBJECT
public:
- /** Default constructor and destructor */
+ /** Default constructor. */
ControlSocket();
- /** Connect to Tor on the specified host and port */
- bool connect(QHostAddress addr, quint16 port, QString *errmsg = 0);
- /** Disconnect from Tor */
- bool disconnect(QString *errmsg = 0);
-
/** Send a command to Tor */
bool sendCommand(ControlCommand cmd, QString *errmsg = 0);
/** Read a response from Tor */
@@ -56,7 +50,10 @@
/** Returns true if the control socket is connected and ready to send or
* receive. */
bool isConnected();
-
+
+ /** Returns the string description of <b>error</b>. */
+ static QString toString(const QAbstractSocket::SocketError error);
+
protected:
/** Reads line data off the socket in chunks. */
bool readLineData(QString &line, QString *errmsg = 0);