I want to create an SSL server, so I subclass QTcpServer and I override incomingConnection()
, where I create a QSslSocket
, set its descriptor, and call QSslSocket::startServerEncryption
. At this point I need to wait for QSslSocket::encrypted()
signal to be emitted, and only after that should my server emit the newConnection()
signal. The client code would then think it's using a QTcpSocket, but will in fact be using a secure socket.
But QTcpServer always emits newConnection()
after calling incomingConnection()
(I looked in the source of QTcpServer):
void QTcpServerPrivate::readNotification()
{
// .........
q->incomingConnection(descriptor);
QPointer<QTcpServer> that = q;
emit q->newConnection();
// .........
}
So my question is, is there a way I can prevent QTcpServer
from emitting newConnection()
, until I'm ready to emit it myself?
The reason I want this is that I want my class to be able to be used as a drop-in replacement of QTcpServer, by code that is unaware it's using it, so it must behave exactly as a QTcpServer:
QTcpServer* getServer(bool ssl)
{
return ssl ? new SslServer : new QTcpServer;
}
My code for the SslServer class is currently this:
void SslServer::ready()
{
QSslSocket *socket = (QSslSocket *) sender();
addPendingConnection(socket);
emit newConnection();
}
void SslServer::incomingConnection(int socketDescriptor)
{
QSslSocket *serverSocket = new QSslSocket;
if (serverSocket->setSocketDescriptor(socketDescriptor)) {
connect(serverSocket, SIGNAL(encrypted()), this, SLOT(ready()));
serverSocket->startServerEncryption();
} else {
delete serverSocket;
}
}
To be a true drop in replacement, you probably will need to edit the actual source of Qt, because you normally can't reimplement any
Private
class calls.If you are the only one using the replacement one, and you control the classes that connect to the
newConnection
signal...Just connect
newConnection
to your own slothandleNewConnection
. When the secure connection is ready emitmyNewConnection
and connect that to the elements that would have been connected tonewConnection
.EDIT: After a bit of digging, I found an option of to reconnect a signal:
http://qt-project.org/forums/viewthread/6820
Basically, you reimplement
QObject::connect
, and then you keep track of the connections and process them the way you need to. So in this case, you would keep a list of all the connections of the signalnewConnection
and keep it in a list so when you disconnect it you could reconnect it. Be sure to callQObject::connect
at the end of the reimplementation.Another option when going this route would be to go and just reroute the connections there. When a connection is requested from
newConnection
, move it there tomyNewConnection
.Hope that helps.
Here's an idea that could work in this case: redefine the
newConnection
signal in yourQTcpServer
subclass.If you do that, objects that connected with an instance of your server won't receive
QTcpServer
's "version" of the signal, only the one you emit directly from your sub-class.Here's a proof of concept: class
A
is theQTcpServer
,foo
is the signal you're trying to "hijack",bar
is just another (hypothetical) ofQTcpServer
's signals you don't need to touch.Class
B
is your subclass. Notice that it redefines signalfoo
, but doesn't do anything tobar
.Class
C
is a potential client, connects the signals/slots from aB
instance exactly like it would for anA
instance.Here's the output from constructing a
C
:... and nothing else.
A
'semit foo(1)
isn't connected toC
'sfoo
slot, it will never arrive toC
.A
'semit bar(1)
worked as expected, that signal is untouched.With that setup, you can emit
newConnection
when your class is ready,QTcpServer
's version of the signal will not be received by your user's objects.A dirty hack would be to very briefly block the signals from QTcpServer. Since you know that
newConnection()
will be emitted right after you return fromSslServer::incomingConnection()
, callthis->blockSignals(true);
just before you return. That will preventnewConnection()
from invoking any slots it is connected to.To make sure you receive subsequent signals, unblock signals as soon as you can. I suppose the earliest time available would be right when control goes back to the event loop, so a QTimer::singleShot could do it.
The downside of that is that you will lose every signal that could legitimately be emited between
incomingConnection()
andunblockSignals()
. Like I said, it's a dirty hack.