If accept fails, stop accepting new connections and emit error signal.
authorJonas M. Gastal <jgastal@gmail.com>
Tue, 12 Jun 2012 01:27:03 +0000 (22:27 -0300)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Wed, 26 Sep 2012 02:03:51 +0000 (04:03 +0200)
Task-number: QTBUG-24778
Change-Id: I6c5b685b3f861a0fafc1475c41bb35cede17d712
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
src/network/socket/qabstractsocket.cpp
src/network/socket/qabstractsocket.h
src/network/socket/qnativesocketengine.cpp
src/network/socket/qnativesocketengine_p.h
src/network/socket/qnativesocketengine_unix.cpp
src/network/socket/qnativesocketengine_win.cpp
src/network/socket/qsocks5socketengine.cpp
src/network/socket/qtcpserver.cpp
src/network/socket/qtcpserver.h
tests/auto/network/socket/qtcpserver/tst_qtcpserver.cpp

index 0d3492e..5ff7da9 100644 (file)
            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()
index 79d6e63..9e1e4f8 100644 (file)
@@ -99,6 +99,7 @@ public:
         OperationError,
         SslInternalError,                       /* 20 */
         SslInvalidUserDataError,
+        TemporaryError,
 
         UnknownSocketError = -1
     };
index 5fba64a..8239324 100644 (file)
@@ -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;
index c363413..dc95de9 100644 (file)
@@ -234,6 +234,7 @@ public:
         PortInuseErrorString,
         NotSocketErrorString,
         InvalidProxyTypeString,
+        TemporaryErrorString,
 
         UnknownSocketErrorString = -1
     };
index 3f6b6b5..224bee8 100644 (file)
@@ -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;
 }
index ef93455..0c14e13 100644 (file)
@@ -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
index ba7ca1b..bb0e911 100644 (file)
@@ -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()
index 331db62..9041950 100644 (file)
     \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
index 0f319e6..f83df73 100644 (file)
@@ -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)
index 66c6c97..18401fb 100644 (file)
@@ -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"