From 5810de08d3a5b5311ba1b4175fba51072aa0e754 Mon Sep 17 00:00:00 2001 From: Ossi Jormakka Date: Wed, 20 May 2009 10:58:52 +0300 Subject: [PATCH] Multiple simultaneous request support to Communication --- QtMeetings.pro | 2 +- resources/xml/errortable.xml | 3 +- src/IO/Communication/Communication.cpp | 51 ++++++++++----- src/IO/Communication/Communication.h | 13 ++-- src/IO/Communication/CommunicationManager.cpp | 42 ++++++++----- src/IO/Communication/CommunicationManager.h | 8 ++- src/main.cpp | 82 ++++++++++++------------- tests/IO/Communication/TestCommunication.cpp | 41 ++++++++----- tests/IO/Communication/TestCommunication.h | 4 +- 9 files changed, 148 insertions(+), 98 deletions(-) diff --git a/QtMeetings.pro b/QtMeetings.pro index 38adacb..75a7e9a 100644 --- a/QtMeetings.pro +++ b/QtMeetings.pro @@ -86,7 +86,7 @@ RESOURCES += resources/BusinessLogic.qrc \ CONFIG += link_pkgconfig PKGCONFIG += libalarm -DEFINES += QT_NO_DEBUG_OUTPUT +//DEFINES += DEBUG_OUTPUT_TO_FILE executable.files = qtmeetings executable.path = /usr/bin/ diff --git a/resources/xml/errortable.xml b/resources/xml/errortable.xml index d611fda..4afbc58 100644 --- a/resources/xml/errortable.xml +++ b/resources/xml/errortable.xml @@ -9,10 +9,11 @@ Communication: Server closed the connection unexpectedly Communication: Server sent an invalid HTTP header Communication: Server sent an invalid content length - Communication: Invalid username or password + Communication: The request was aborted Communication: SOAP error when fetching meetings Communication: SOAP error when fetching meeting details Communication error + Communication: Invalid username or password Failed to change the operation mode. Error creating internal file storage. Failed to change the operation mode. Error removing old alarm events. Failed to change the operation mode. Error sending alarm events. %1 diff --git a/src/IO/Communication/Communication.cpp b/src/IO/Communication/Communication.cpp index 82b847e..5c3b924 100644 --- a/src/IO/Communication/Communication.cpp +++ b/src/IO/Communication/Communication.cpp @@ -9,7 +9,6 @@ Communication::Communication( const ConnectionSettings &aConnection ) : iConnectionSettings = new ConnectionSettings( aConnection ); iHttp = new QHttp( iConnectionSettings->serverUrl().toString(), QHttp::ConnectionModeHttps ); - iResponse = new QByteArray(); connect( iHttp, SIGNAL( readyRead( const QHttpResponseHeader& ) ), @@ -46,32 +45,42 @@ Communication::~Communication() { delete iConnectionSettings; delete iHttp; - delete iResponse; + QList responses = iResponses.values(); + while(!responses.isEmpty()) + delete responses.takeFirst(); } void Communication::processResponse( const QHttpResponseHeader& aHeader ) { if ( aHeader.statusCode() == 200 ) { - iResponse->append( iHttp->readAll() ); + if( iResponses.contains( iCurrentRequest ) ) + { + QByteArray* response = iResponses.value( iCurrentRequest, NULL ); + response->append( iHttp->readAll() ); + } } } void Communication::handleRequestStarted( int aRequestId ) { - if( aRequestId != 0 && iCurrentRequest != 0 && - aRequestId == iCurrentRequest ) + if( iResponses.contains( aRequestId ) ) { - emit requestStarted( iCurrentRequest ); + iCurrentRequest = aRequestId; + emit requestStarted( aRequestId ); } } void Communication::handleResults( int aId, bool /*aError*/ ) { - if( aId == iCurrentRequest ) + if( iCurrentRequest != 0 && aId == iCurrentRequest ) { + iAuthFailCount = 0; iCurrentRequest = 0; - emit requestFinished( aId, iHttp->error() ); + int err = (int)iHttp->error(); + if( iHttp->error() == QHttp::Aborted && iAuthFailCount > 1 ) + err = 11; + emit requestFinished( aId, err ); } } @@ -88,24 +97,34 @@ void Communication::handleAuthentication( const QString& /*aHost*/, quint16 /*aP int Communication::request( const QString &aCommand, const QByteArray &aContent ) { - //TODO: This is just temporarily here to implement multiple request support - if( iCurrentRequest ) + if( iAuthFailCount > 1 ) return 0; + QHttpRequestHeader header( QString( "POST" ), QString( "/ews/exchange.asmx" ) ); header.setValue( "Host", iConnectionSettings->serverUrl().toString() ); QString command = aCommand; header.setContentType( command ); - iAuthFailCount = 0; - iResponse->clear(); - iCurrentRequest = iHttp->request( header, aContent ); + int id = iHttp->request( header, aContent ); + if( iResponses.contains( id ) ) + { + QByteArray* response = iResponses.value( id, NULL ); + response->clear(); + } + else + { + iResponses.insert( id, new QByteArray() ); + } - return iCurrentRequest; + return id; } -const QByteArray& Communication::response( int /*aRequestId*/ ) +QByteArray* Communication::response( int aRequestId ) { - return *iResponse; + if( !iResponses.contains( aRequestId ) ) + return NULL; + QByteArray* response = iResponses.take( aRequestId ); + return response; } void Communication::handleReadProgress( int aDone, int aTotal ) diff --git a/src/IO/Communication/Communication.h b/src/IO/Communication/Communication.h index 9fd8523..41dedb5 100644 --- a/src/IO/Communication/Communication.h +++ b/src/IO/Communication/Communication.h @@ -3,6 +3,7 @@ #include #include +#include class QByteArray; class ConnectionSettings; @@ -11,8 +12,6 @@ class ConnectionSettings; /*! * This class uses the QHttp class to make HTTP requests. HTTP responses are * returned as QByteArray. - * - * NOTE! Currently this class does NOT support multiple simultaneuos requests. */ class Communication : public QObject { @@ -29,11 +28,9 @@ public: virtual ~Communication(); //! Returns the response of a request identified by aRequestId. /*! - * NOTE! Currently the last request response is returned as the multiple - * request support is not implemented. * \param aRequestId Request id number. */ - const QByteArray& response( int aRequestId ); + QByteArray* response( int aRequestId ); //! Makes a HTTP request to the server defined in the constructor. /*! @@ -61,7 +58,7 @@ signals: * \param aRequestId Request id number. * \param aError Error code of the request finished. */ - void requestFinished( int aRequestId, QHttp::Error aError ); + void requestFinished( int aRequestId, int aError ); protected slots: //! Connected to QHttp::readyRead to read the response buffer. @@ -85,9 +82,9 @@ private: */ QHttp* iHttp; /*! - * Response buffer + * QHash of requests and response buffers */ - QByteArray* iResponse; + QHash iResponses; /*! * ID number of a request currently ongoing */ diff --git a/src/IO/Communication/CommunicationManager.cpp b/src/IO/Communication/CommunicationManager.cpp index 4212f68..0a2ce77 100644 --- a/src/IO/Communication/CommunicationManager.cpp +++ b/src/IO/Communication/CommunicationManager.cpp @@ -23,9 +23,9 @@ CommunicationManager::CommunicationManager( const ConnectionSettings &aConnectio SLOT( readProgress( int, int, int ) ) ); connect( iFetchingCommunication, - SIGNAL( requestFinished( int, QHttp::Error ) ), + SIGNAL( requestFinished( int, int ) ), this, - SLOT( requestFinished( int, QHttp::Error ) ) + SLOT( requestFinished( int, int ) ) ); } } @@ -41,6 +41,10 @@ CommunicationManager::~CommunicationManager() void CommunicationManager::fetchMeetings( const QDateTime &aFrom, const QDateTime &aUntil, const Room &aIn ) { + //prevent making multiple simultaneous user availibility requests + const RequestData* rd = findRequest( GetUserAvailability ); + if( rd ) + return; ReqMsgGetUserAvailability msg; msg.setTimeZone(); msg.setTimeWindow( aFrom, aUntil ); @@ -102,28 +106,27 @@ void CommunicationManager::getMeetingDetails( Meeting &aMeeting ) } } -void CommunicationManager::requestFinished( int aRequestId, QHttp::Error aError ) +void CommunicationManager::requestFinished( int aRequestId, int aError ) { RequestData* rd = takeRequest( aRequestId ); - qDebug() << "CommunicationManager::requestFinished: id: " << aRequestId << " error: " << (int)aError; + QByteArray* response = iFetchingCommunication->response( aRequestId ); + qDebug() << "CommunicationManager::requestFinished: id: " << aRequestId << " error: " << aError; - if( aError != QHttp::NoError || rd == NULL ) + if( aError != (int)(QHttp::NoError) || rd == NULL || response == NULL ) { - int err = (int)aError; - if( rd == NULL ) + int err = aError; + if( rd == NULL || response == NULL ) err = 10; delete rd; emit error( ERROR_BASE+(int)err, CommunicationManager::FetchingCommunication ); return; } - const QByteArray& response = iFetchingCommunication->response( aRequestId ); - switch( rd->type ) { case GetUserAvailability: { - ResMsgGetUserAvailability msg( response ); + ResMsgGetUserAvailability msg( *response ); while ( !iMeetings.isEmpty() ) delete iMeetings.takeFirst(); @@ -137,7 +140,7 @@ void CommunicationManager::requestFinished( int aRequestId, QHttp::Error aError } case ConvertId: { - ResponseMessage msg( response ); + ResponseMessage msg( *response ); QString id = msg.getNodeValue( QString( "Id" ), QDomNode::AttributeNode, QString( "AlternateId" ) ); @@ -148,7 +151,7 @@ void CommunicationManager::requestFinished( int aRequestId, QHttp::Error aError } case GetCalendarItem: { - ResMsgGetCalendarItem msg( response ); + ResMsgGetCalendarItem msg( *response ); int err = msg.getMeetingDetailsFromResponse( *(rd->meeting) ); if( err ) emit error( ERROR_BASE+9, CommunicationManager::FetchingCommunication ); @@ -157,6 +160,7 @@ void CommunicationManager::requestFinished( int aRequestId, QHttp::Error aError break; } } + delete response; delete rd; } @@ -188,5 +192,15 @@ CommunicationManager::RequestData* CommunicationManager::takeRequest( int aReque return NULL; } - - +const CommunicationManager::RequestData* CommunicationManager::findRequest( RequestType aRequestType ) const +{ + for( int i = iRequestInfos.count() - 1; i >= 0 ; i-- ) + { + struct RequestData* rd = iRequestInfos[i]; + if( rd->type == aRequestType ) + { + return rd; + } + } + return NULL; +} diff --git a/src/IO/Communication/CommunicationManager.h b/src/IO/Communication/CommunicationManager.h index d1ded78..a8d8185 100644 --- a/src/IO/Communication/CommunicationManager.h +++ b/src/IO/Communication/CommunicationManager.h @@ -85,7 +85,7 @@ signals: protected slots: //! Connected to Communication::requestFinished. - void requestFinished( int aRequestId, QHttp::Error aError ); + void requestFinished( int aRequestId, int aError ); //! Connected to Communication::readProgress. void readProgress( int aRequestId, int aDone, int aTotal ); @@ -146,6 +146,12 @@ protected: * \param aRequestId Request ID number returned by the Communication::request */ RequestData* takeRequest( int aRequestId ); + //! Finds a RequestData from the iRequestInfos list. + /*! + * Returns a pointer to RequestData, NULL if not found. + * \param aRequestType Request type + */ + const RequestData* findRequest( RequestType aRequestType ) const; private: //! Instance of Connection settings of the fetching communication diff --git a/src/main.cpp b/src/main.cpp index 9ed1c36..a7dc0e2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,41 +1,41 @@ -#include -#include -#include -#include -#include "WindowManager.h" - -using namespace std; - -ofstream logfile; - -void DebugOutputHandler( QtMsgType type, const char *msg ) { - switch( type ) { - case QtDebugMsg: - logfile << QTime::currentTime().toString().toAscii().data() << " Debug: " << msg << "\n"; - logfile.flush(); - break; - case QtCriticalMsg: - logfile << QTime::currentTime().toString().toAscii().data() << " Critical: " << msg << "\n"; - logfile.flush(); - break; - case QtWarningMsg: - logfile << QTime::currentTime().toString().toAscii().data() << " Warning: " << msg << "\n"; - logfile.flush(); - break; - case QtFatalMsg: - logfile << QTime::currentTime().toString().toAscii().data() << " Fatal: " << msg << "\n"; - logfile.flush(); - } -} - -int main( int argc, char *argv[] ) -{ - #ifndef QT_NO_DEBUG_OUTPUT - logfile.open( "/tmp/qtmeetings.log", ios::app ); - qInstallMsgHandler( DebugOutputHandler ); - #endif - - QApplication app( argc, argv ); - WindowManager *windowManager = new WindowManager; - return app.exec(); -} +#include +#include +#include +#include +#include "WindowManager.h" + +using namespace std; + +ofstream logfile; + +void DebugOutputHandler( QtMsgType type, const char *msg ) { + switch( type ) { + case QtDebugMsg: + logfile << QTime::currentTime().toString().toAscii().data() << " Debug: " << msg << "\n"; + logfile.flush(); + break; + case QtCriticalMsg: + logfile << QTime::currentTime().toString().toAscii().data() << " Critical: " << msg << "\n"; + logfile.flush(); + break; + case QtWarningMsg: + logfile << QTime::currentTime().toString().toAscii().data() << " Warning: " << msg << "\n"; + logfile.flush(); + break; + case QtFatalMsg: + logfile << QTime::currentTime().toString().toAscii().data() << " Fatal: " << msg << "\n"; + logfile.flush(); + } +} + +int main( int argc, char *argv[] ) +{ + #ifdef DEBUG_OUTPUT_TO_FILE + logfile.open( "/tmp/qtmeetings.log", ios::app ); + qInstallMsgHandler( DebugOutputHandler ); + #endif + + QApplication app( argc, argv ); + WindowManager *windowManager = new WindowManager; + return app.exec(); +} diff --git a/tests/IO/Communication/TestCommunication.cpp b/tests/IO/Communication/TestCommunication.cpp index df94925..65fc2a7 100644 --- a/tests/IO/Communication/TestCommunication.cpp +++ b/tests/IO/Communication/TestCommunication.cpp @@ -21,9 +21,9 @@ void TestCommunication::initTestCase() delete conn; conn = NULL; connect( iComm, - SIGNAL( requestFinished( int, QHttp::Error ) ), + SIGNAL( requestFinished( int, int ) ), this, - SLOT( requestFinished( int, QHttp::Error ) ) + SLOT( requestFinished( int, int ) ) ); } @@ -42,8 +42,7 @@ void TestCommunication::testPublicSignals() QCOMPARE( rp.isValid(), true ); QSignalSpy rs( iComm, SIGNAL(requestStarted( int )) ); QCOMPARE( rs.isValid(), true ); - qRegisterMetaType("QHttp::Error"); - QSignalSpy rf( iComm, SIGNAL(requestFinished( int, QHttp::Error )) ); + QSignalSpy rf( iComm, SIGNAL(requestFinished( int, int )) ); QCOMPARE( rf.isValid(), true ); iRequestId = iComm->request( "", QString("").toAscii() ); @@ -59,7 +58,7 @@ void TestCommunication::testPublicSignals() QCOMPARE( rf.count(), 1 ); args = rf.takeFirst(); QCOMPARE( args.at(0).type(), QVariant::Int ); - QCOMPARE( args.at(1).typeName(), "QHttp::Error" ); + QCOMPARE( args.at(1).type(), QVariant::Int ); //verify readProgress QCOMPARE( rp.count(), 1 ); @@ -79,22 +78,36 @@ void TestCommunication::testRequestAndResponse() } QByteArray content = file.readAll(); - iRequestId = iComm->request( command, content ); + iError = 0; + int id_ok = iComm->request( command, content ); + iRequestId = id_ok; int id_fail = iComm->request( "", QString("").toAscii() ); QVERIFY( iRequestId != 0 ); - QCOMPARE( id_fail, 0 ); + QVERIFY( id_fail != 0 ); + qDebug() << "id_ok:" << id_ok; + qDebug() << "id_fail: " << id_fail; for(int i=0; iRequestId != 0 && i < 5000/250; i++ ) QTest::qWait( 250 ); - const QByteArray& response = iComm->response( 0 ); - QVERIFY( response.contains("GetUserAvailabilityResponse") ); - iRequestId = iComm->request( "", QString("").toAscii() ); - QVERIFY( iRequestId != 0 ); - QVERIFY( response.isEmpty() ); + qDebug() << "iError:" << iError; + QVERIFY( iError == 0 ); + QByteArray* response = iComm->response( id_ok ); + QVERIFY( response != NULL ); + QVERIFY( !response->isEmpty() ); + if( response->contains("GetUserAvailabilityResponse") == FALSE ) + { + QWARN( *response ); + } + QVERIFY( response->contains("GetUserAvailabilityResponse") ); + delete response; + response = iComm->response( 0 ); + QVERIFY( response == NULL ); } -void TestCommunication::requestFinished( int aRequestId, QHttp::Error /*aError*/ ) +void TestCommunication::requestFinished( int aRequestId, int aError ) { - QCOMPARE( aRequestId, iRequestId ); + qDebug() << "TestCommunication::requestFinished:" << aRequestId << aError; + if( aRequestId == iRequestId ) + iError = aError; iRequestId = 0; } diff --git a/tests/IO/Communication/TestCommunication.h b/tests/IO/Communication/TestCommunication.h index 1fd9e4d..835234d 100644 --- a/tests/IO/Communication/TestCommunication.h +++ b/tests/IO/Communication/TestCommunication.h @@ -13,9 +13,9 @@ private slots: void cleanupTestCase(); void testPublicSignals(); void testRequestAndResponse(); - void requestFinished( int aRequestId, QHttp::Error aError ); + void requestFinished( int aRequestId, int aError ); private: Communication* iComm; - int iRequestId; + int iRequestId, iError; }; -- 1.7.9.5