From 4920090da0fc937d171065694d5274221d2747c9 Mon Sep 17 00:00:00 2001 From: "Jonas M. Gastal" Date: Mon, 11 Jun 2012 22:27:03 -0300 Subject: If accept fails, stop accepting new connections and emit error signal. Task-number: QTBUG-24778 Change-Id: I6c5b685b3f861a0fafc1475c41bb35cede17d712 Reviewed-by: Thiago Macieira --- src/network/socket/qabstractsocket.cpp | 2 ++ src/network/socket/qabstractsocket.h | 1 + src/network/socket/qnativesocketengine.cpp | 3 ++ src/network/socket/qnativesocketengine_p.h | 1 + src/network/socket/qnativesocketengine_unix.cpp | 40 +++++++++++++++++++++ src/network/socket/qnativesocketengine_win.cpp | 35 +++++++++++++++++- src/network/socket/qsocks5socketengine.cpp | 16 ++++++--- src/network/socket/qtcpserver.cpp | 42 +++++++++++++++++++++- src/network/socket/qtcpserver.h | 4 +++ .../network/socket/qtcpserver/tst_qtcpserver.cpp | 21 +++++++++++ 10 files changed, 159 insertions(+), 6 deletions(-) diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp index 0d3492e639..5ff7da9e9d 100644 --- a/src/network/socket/qabstractsocket.cpp +++ b/src/network/socket/qabstractsocket.cpp @@ -321,6 +321,8 @@ probably the result of a bad installation or misconfiguration of the library. \value SslInvalidUserDataError Invalid data(certificate, key, cypher, etc.) was provided and its use resulted in an error in the SSL library. + \value TemporaryError A temporary error occurred(e.g., operation would block and socket + is non-blocking). \value UnknownSocketError An unidentified error occurred. \sa QAbstractSocket::error() diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h index 79d6e63449..9e1e4f81b8 100644 --- a/src/network/socket/qabstractsocket.h +++ b/src/network/socket/qabstractsocket.h @@ -99,6 +99,7 @@ public: OperationError, SslInternalError, /* 20 */ SslInvalidUserDataError, + TemporaryError, UnknownSocketError = -1 }; diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp index 5fba64a981..8239324b1a 100644 --- a/src/network/socket/qnativesocketengine.cpp +++ b/src/network/socket/qnativesocketengine.cpp @@ -274,6 +274,9 @@ void QNativeSocketEnginePrivate::setError(QAbstractSocket::SocketError error, Er case InvalidProxyTypeString: socketErrorString = QNativeSocketEngine::tr("The proxy type is invalid for this operation"); break; + case TemporaryErrorString: + socketErrorString = QNativeSocketEngine::tr("Temporary error"); + break; case UnknownSocketErrorString: socketErrorString = QNativeSocketEngine::tr("Unknown error"); break; diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h index c363413d6b..dc95de9c86 100644 --- a/src/network/socket/qnativesocketengine_p.h +++ b/src/network/socket/qnativesocketengine_p.h @@ -234,6 +234,7 @@ public: PortInuseErrorString, NotSocketErrorString, InvalidProxyTypeString, + TemporaryErrorString, UnknownSocketErrorString = -1 }; diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp index 3f6b6b556c..224bee8fd6 100644 --- a/src/network/socket/qnativesocketengine_unix.cpp +++ b/src/network/socket/qnativesocketengine_unix.cpp @@ -588,6 +588,46 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog) int QNativeSocketEnginePrivate::nativeAccept() { int acceptedDescriptor = qt_safe_accept(socketDescriptor, 0, 0); + if (acceptedDescriptor == -1) { + switch (errno) { + case EBADF: + case EOPNOTSUPP: + setError(QAbstractSocket::UnsupportedSocketOperationError, InvalidSocketErrorString); + break; + case ECONNABORTED: + setError(QAbstractSocket::NetworkError, RemoteHostClosedErrorString); + break; + case EFAULT: + case ENOTSOCK: + setError(QAbstractSocket::SocketResourceError, NotSocketErrorString); + break; + case EPROTONOSUPPORT: + case EPROTO: + case EAFNOSUPPORT: + case EINVAL: + setError(QAbstractSocket::UnsupportedSocketOperationError, ProtocolUnsupportedErrorString); + break; + case ENFILE: + case EMFILE: + case ENOBUFS: + case ENOMEM: + setError(QAbstractSocket::SocketResourceError, ResourceErrorString); + break; + case EACCES: + case EPERM: + setError(QAbstractSocket::SocketAccessError, AccessErrorString); + break; +#if EAGAIN != EWOULDBLOCK + case EWOULDBLOCK: +#endif + case EAGAIN: + setError(QAbstractSocket::TemporaryError, TemporaryErrorString); + break; + default: + setError(QAbstractSocket::UnknownSocketError, UnknownSocketErrorString); + break; + } + } return acceptedDescriptor; } diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp index ef93455134..0c14e1380b 100644 --- a/src/network/socket/qnativesocketengine_win.cpp +++ b/src/network/socket/qnativesocketengine_win.cpp @@ -909,7 +909,40 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog) int QNativeSocketEnginePrivate::nativeAccept() { int acceptedDescriptor = WSAAccept(socketDescriptor, 0,0,0,0); - if (acceptedDescriptor != -1 && QAbstractEventDispatcher::instance()) { + if (acceptedDescriptor == -1) { + int err = WSAGetLastError(); + switch (err) { + case WSAEACCES: + setError(QAbstractSocket::SocketAccessError, AccessErrorString); + break; + case WSAECONNREFUSED: + setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString); + break; + case WSAECONNRESET: + setError(QAbstractSocket::NetworkError, RemoteHostClosedErrorString); + break; + case WSAENETDOWN: + setError(QAbstractSocket::NetworkError, NetworkUnreachableErrorString); + case WSAENOTSOCK: + setError(QAbstractSocket::SocketResourceError, NotSocketErrorString); + break; + case WSAEINVAL: + case WSAEOPNOTSUPP: + setError(QAbstractSocket::UnsupportedSocketOperationError, ProtocolUnsupportedErrorString); + break; + case WSAEFAULT: + case WSAEMFILE: + case WSAENOBUFS: + setError(QAbstractSocket::SocketResourceError, ResourceErrorString); + break; + case WSAEWOULDBLOCK: + setError(QAbstractSocket::TemporaryError, TemporaryErrorString); + break; + default: + setError(QAbstractSocket::UnknownSocketError, UnknownSocketErrorString); + break; + } + } else if (acceptedDescriptor != -1 && QAbstractEventDispatcher::instance()) { // Because of WSAAsyncSelect() WSAAccept returns a non blocking socket // with the same attributes as the listening socket including the current // WSAAsyncSelect(). To be able to change the socket to blocking mode the diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp index ba7ca1b92e..bb0e911aef 100644 --- a/src/network/socket/qsocks5socketengine.cpp +++ b/src/network/socket/qsocks5socketengine.cpp @@ -1453,13 +1453,15 @@ int QSocks5SocketEngine::accept() QSOCKS5_Q_DEBUG << "accept()"; - if (d->socks5State == QSocks5SocketEnginePrivate::BindSuccess) { + qintptr sd = -1; + switch (d->socks5State) { + case QSocks5SocketEnginePrivate::BindSuccess: QSOCKS5_Q_DEBUG << "BindSuccess adding" << d->socketDescriptor << "to the bind store"; d->data->controlSocket->disconnect(); d->data->controlSocket->setParent(0); d->bindData->localAddress = d->localAddress; d->bindData->localPort = d->localPort; - qintptr sd = d->socketDescriptor; + sd = d->socketDescriptor; socks5BindStore()->add(sd, d->bindData); d->data = 0; d->bindData = 0; @@ -1468,9 +1470,15 @@ int QSocks5SocketEngine::accept() // reset state and local port/address d->socks5State = QSocks5SocketEnginePrivate::Uninitialized; // ..?? d->socketState = QAbstractSocket::UnconnectedState; - return sd; + break; + case QSocks5SocketEnginePrivate::ControlSocketError: + setError(QAbstractSocket::ProxyProtocolError, QLatin1String("Control socket error")); + break; + default: + setError(QAbstractSocket::ProxyProtocolError, QLatin1String("SOCKS5 proxy error")); + break; } - return -1; + return sd; } void QSocks5SocketEngine::close() diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp index 331db627dd..90419502ba 100644 --- a/src/network/socket/qtcpserver.cpp +++ b/src/network/socket/qtcpserver.cpp @@ -90,6 +90,15 @@ \sa hasPendingConnections(), nextPendingConnection() */ +/*! \fn void QTcpServer::acceptError(QAbstractSocket::SocketError socketError) + \since 5.0 + + This signal is emitted when accepting a new connection results in an error. + The \a socketError parameter describes the type of error that occurred. + + \sa pauseAccepting(), resumeAccepting() +*/ + #include "qtcpserver.h" #include "private/qobject_p.h" #include "qalgorithms.h" @@ -209,8 +218,15 @@ void QTcpServerPrivate::readNotification() } int descriptor = socketEngine->accept(); - if (descriptor == -1) + if (descriptor == -1) { + if (socketEngine->error() != QAbstractSocket::TemporaryError) { + q->pauseAccepting(); + serverSocketError = socketEngine->error(); + serverSocketErrorString = socketEngine->errorString(); + emit q->acceptError(serverSocketError); + } break; + } #if defined (QTCPSERVER_DEBUG) qDebug("QTcpServerPrivate::_q_processIncomingConnection() accepted socket %i", descriptor); #endif @@ -650,6 +666,30 @@ QString QTcpServer::errorString() const return d_func()->serverSocketErrorString; } +/*! + \since 5.0 + + Pauses accepting new connections. Queued connections will remain in queue. + + \sa resumeAccepting() +*/ +void QTcpServer::pauseAccepting() +{ + d_func()->socketEngine->setReadNotificationEnabled(false); +} + +/*! + \since 5.0 + + Resumes accepting new connections. + + \sa pauseAccepting() +*/ +void QTcpServer::resumeAccepting() +{ + d_func()->socketEngine->setReadNotificationEnabled(true); +} + #ifndef QT_NO_NETWORKPROXY /*! \since 4.1 diff --git a/src/network/socket/qtcpserver.h b/src/network/socket/qtcpserver.h index 0f319e6c7d..f83df734eb 100644 --- a/src/network/socket/qtcpserver.h +++ b/src/network/socket/qtcpserver.h @@ -85,6 +85,9 @@ public: QAbstractSocket::SocketError serverError() const; QString errorString() const; + void pauseAccepting(); + void resumeAccepting(); + #ifndef QT_NO_NETWORKPROXY void setProxy(const QNetworkProxy &networkProxy); QNetworkProxy proxy() const; @@ -96,6 +99,7 @@ protected: Q_SIGNALS: void newConnection(); + void acceptError(QAbstractSocket::SocketError socketError); private: Q_DISABLE_COPY(QTcpServer) diff --git a/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp b/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp index 66c6c97786..18401fb40d 100644 --- a/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp +++ b/tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp @@ -121,6 +121,8 @@ private slots: void linkLocal(); + void eagainBlockingAccept(); + private: #ifndef QT_NO_BEARERMANAGEMENT QNetworkSession *networkSession; @@ -953,5 +955,24 @@ void tst_QTcpServer::linkLocal() qDeleteAll(servers); } +void tst_QTcpServer::eagainBlockingAccept() +{ + QTcpServer server; + server.listen(QHostAddress::LocalHost, 7896); + + // Receiving a new connection causes TemporaryError, but shouldn't pause accepting. + QTcpSocket s; + s.connectToHost(QHostAddress::LocalHost, 7896); + QSignalSpy spy(&server, SIGNAL(newConnection())); + QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 1, 500); + s.close(); + + // To test try again, should connect just fine. + s.connectToHost(QHostAddress::LocalHost, 7896); + QTRY_COMPARE_WITH_TIMEOUT(spy.count(), 2, 500); + s.close(); + server.close(); +} + QTEST_MAIN(tst_QTcpServer) #include "tst_qtcpserver.moc" -- cgit v1.2.3