BUG#: 6218
authorkumpf <kumpf>
Wed, 7 Mar 2007 20:54:32 +0000 (20:54 +0000)
committerkumpf <kumpf>
Wed, 7 Mar 2007 20:54:32 +0000 (20:54 +0000)
TITLE: Use a lazy reconnect algorithm when a 'Connection: Close' header is received
DESCRIPTION: Do not reconnect immediately when the server sends a Connection: Close header or when the connection times out.  Wait until it is determined that further communication is needed, to avoid unnecessary reconnects.

src/Pegasus/Client/CIMClientRep.cpp
src/Pegasus/Client/CIMClientRep.h
src/Pegasus/Client/ClientAuthenticator.cpp
src/Pegasus/Client/ClientAuthenticator.h
src/Pegasus/ExportClient/CIMExportClient.cpp
src/Pegasus/ExportClient/CIMExportClient.h
src/Pegasus/Handler/CIMxmlIndicationHandler/CIMxmlIndicationHandler.cpp

index a2ac03409df83cd5afed9ce3bdf2fd4975f9f90a..1eaa195fa7e9fb0afc4c6149988005877533daa7 100644 (file)
@@ -76,7 +76,8 @@ CIMClientRep::CIMClientRep(Uint32 timeoutMilliseconds)
     :
     MessageQueue(PEGASUS_QUEUENAME_CLIENT),
     _timeoutMilliseconds(timeoutMilliseconds),
-    _connected(false)
+    _connected(false),
+    _doReconnect(false)
 {
     //
     // Create Monitor and HTTPConnector
@@ -188,6 +189,7 @@ void CIMClientRep::_connect()
     _requestEncoder->setDataStorePointer(&perfDataStore);
     _responseDecoder->setDataStorePointer(&perfDataStore);
 
+    _doReconnect = false;
     _connected = true;
     _httpConnection->setSocketWriteTimeout(_timeoutMilliseconds/1000+1);
 }
@@ -219,13 +221,12 @@ void CIMClientRep::_disconnect()
 
         _connected = false;
     }
-}
 
-void CIMClientRep::_reconnect()
-{
-    _disconnect();
-    _authenticator.clearReconnect();
-    _connect();
+    // Reconnect no longer applies
+    _doReconnect = false;
+
+    // Let go of the cached request message if we have one
+    _authenticator.setRequestMessage(0);
 }
 
 void CIMClientRep::connect(
@@ -1065,12 +1066,17 @@ Message* CIMClientRep::_doRequest(
     const Uint32 expectedResponseMessageType
 )
 {
-    if (!_connected)
+    if (!_connected && !_doReconnect)
     {
-        request.reset();
         throw NotConnectedException();
     }
 
+    if (_doReconnect)
+    {
+        _connect();
+        _doReconnect = false;
+    }
+
     String messageId = XmlWriter::getNextMessageId();
     const_cast<String &>(request->messageId) = messageId;
 
@@ -1119,19 +1125,21 @@ Message* CIMClientRep::_doRequest(
         // Check to see if incoming queue has a message
         //
 
-        Message* response = dequeue();
+        AutoPtr<Message> response(dequeue());
 
-        if (response)
+        if (response.get())
         {
             // Shouldn't be any more messages in our queue
             PEGASUS_ASSERT(getCount() == 0);
 
             //
-            // Reconnect to reset the connection
-            // if Server response contained a Connection: Close Header
+            // Close the connection if response contained a "Connection: Close"
+            // header (e.g. at authentication challenge)
             //
-            if (response->getCloseConnect() == true){
-                _reconnect();
+            if (response->getCloseConnect() == true)
+            {
+                _disconnect();
+                _doReconnect = true;
                 response->setCloseConnect(false);
             }
 
@@ -1144,8 +1152,7 @@ Message* CIMClientRep::_doRequest(
             {
 
                 Exception* clientException =
-                    ((ClientExceptionMessage*)response)->clientException;
-                delete response;
+                    ((ClientExceptionMessage*)response.get())->clientException;
 
                 AutoPtr<Exception> d(clientException);
 
@@ -1198,7 +1205,8 @@ Message* CIMClientRep::_doRequest(
             }
             else if (response->getType() == expectedResponseMessageType)
             {
-                CIMResponseMessage* cimResponse = (CIMResponseMessage*)response;
+                CIMResponseMessage* cimResponse =
+                    (CIMResponseMessage*)response.get();
 
                 if (cimResponse->messageId != messageId)
                 {
@@ -1218,7 +1226,6 @@ Message* CIMClientRep::_doRequest(
 
                     CIMClientResponseException responseException(mlString);
 
-                    delete response;
                     throw responseException;
                 }
 
@@ -1235,7 +1242,6 @@ Message* CIMClientRep::_doRequest(
                         cimResponse->cimException.getCode(),
                         cimResponse->cimException.getMessage());
                     cimException.setContentLanguages(responseContentLanguages);
-                    delete response;
                     throw cimException;
                 }
 
@@ -1256,12 +1262,20 @@ Message* CIMClientRep::_doRequest(
                    perfDataStore.handler_prt->handleClientOpPerformanceData(item);
 
                 }//end of if statmet that call the callback method
-                return response;
+                return response.release();
             }
-            else if (dynamic_cast<CIMRequestMessage*>(response) != 0)
+            else if (dynamic_cast<CIMRequestMessage*>(response.get()) != 0)
             {
-                // Respond to an authentication challenge
-                _requestEncoder->enqueue(response);
+                //
+                // Respond to an authentication challenge.
+                // Reconnect if the connection was closed.
+                //
+                if (_doReconnect)
+                {
+                    _connect();
+                }
+
+                _requestEncoder->enqueue(response.release());
                 nowMilliseconds = TimeValue::getCurrentTime().toMilliseconds();
                 stopMilliseconds = nowMilliseconds + _timeoutMilliseconds;
                 continue;
@@ -1279,7 +1293,6 @@ Message* CIMClientRep::_doRequest(
 
                 CIMClientResponseException responseException(mlString);
 
-                delete response;
                 throw responseException;
             }
         }
@@ -1291,13 +1304,10 @@ Message* CIMClientRep::_doRequest(
     //
     // Reconnect to reset the connection (disregard late response)
     //
-    try
-    {
-        _reconnect();
-    }
-    catch (...)
-    {
-    }
+
+    _disconnect();
+    _authenticator.resetChallengeStatus();
+    _doReconnect = true;
 
     //
     // Throw timed out exception:
index 4ac11d44f6bc50a663ccd6e9adc2602fef2ce846..4ed8abb41e49d858c43f96eff1cdf3f79aed258c 100644 (file)
@@ -313,8 +313,6 @@ private:
 
     void _disconnect();
 
-    void _reconnect();
-
     Message* _doRequest(
         AutoPtr<CIMRequestMessage>& request,
         const Uint32 expectedResponseMessageType);
@@ -327,6 +325,19 @@ private:
 
     Uint32 _timeoutMilliseconds;
     Boolean _connected;
+    /**
+        The CIMClient uses a lazy reconnect algorithm.  A reconnection
+        is necessary when the server (listener) sends a Connection: Close
+        header in the HTTP response or when a connection timeout occurs
+        while waiting for a response.  In these cases, a disconnect is
+        performed immediately and the _doReconnect flag is set.  The
+        connection is re-established only when required to perform another
+        operation.  Note that in the case of a connection timeout, the
+        challenge status must be reset in the ClientAuthenticator to allow
+        authentication to be performed properly on the new connection.
+    */
+    Boolean _doReconnect;
+
     AutoPtr<CIMOperationResponseDecoder> _responseDecoder;
     AutoPtr<CIMOperationRequestEncoder> _requestEncoder;
     ClientAuthenticator _authenticator;
index 2bd278f3f21e401ccc5aee20a46b54eb5f6edceb..a02ed63a795036192ef597147fd1ba99973424e2 100644 (file)
@@ -101,9 +101,10 @@ ClientAuthenticator::~ClientAuthenticator()
 void ClientAuthenticator::clear()
 {
     _requestMessage.reset();
-    _userName = String::EMPTY;
-    _password = String::EMPTY;
-    _realm = String::EMPTY;
+    _userName.clear();
+    _password.clear();
+    _localAuthFile.clear();
+    _localAuthFileContent.clear();
     _challengeReceived = false;
     _authType = ClientAuthenticator::NONE;
 }
@@ -185,7 +186,7 @@ Boolean ClientAuthenticator::checkResponseHeaderForChallenge(
            }
        }
 
-       _realm = authRealm;
+       _localAuthFile = authRealm;
 
        return true;
    }
@@ -319,11 +320,11 @@ Message* ClientAuthenticator::getRequestMessage()
     return _requestMessage.get();
 }
 
-void ClientAuthenticator::clearReconnect()
+void ClientAuthenticator::resetChallengeStatus()
 {
-    _requestMessage.reset();
-    _realm = String::EMPTY;
     _challengeReceived = false;
+    _localAuthFile.clear();
+    _localAuthFileContent.clear();
 }
 
 Message* ClientAuthenticator::releaseRequestMessage()
@@ -387,7 +388,7 @@ String ClientAuthenticator::_getFileContent(String filePath)
     if (!ifs)
     {
        //ATTN: Log error message
-        return (challenge);
+        return String::EMPTY;
     }
 
     String line;
@@ -413,24 +414,28 @@ String ClientAuthenticator::_buildLocalAuthResponse()
         //
         // Append the file path that is in the realm sent by the server
         //
-        authResponse.append(_realm);
+        authResponse.append(_localAuthFile);
 
         authResponse.append(":");
 
-        //
-        // Read and append the challenge file content
-        //
-        String fileContent = String::EMPTY;
-        try
+        if (_localAuthFileContent.size() == 0) 
         {
-            fileContent = _getFileContent(_realm);
-        }
-        catch(NoSuchFile& e)
-        {
-            //ATTN-NB-04-20000305: Log error message to log file
+            //
+            // Read the challenge file content
+            //
+            try
+            {
+                _localAuthFileContent = _getFileContent(_localAuthFile);
+            }
+            catch(NoSuchFile& e)
+            {
+                //ATTN-NB-04-20000305: Log error message to log file
+            }
         }
-        authResponse.append(fileContent);
+
+        authResponse.append(_localAuthFileContent);
     }
+
     authResponse.append("\"");
 
     return (authResponse);
index 5e43dd14e1c62cb5b2ecad6616f33240908d1b22..b04d2f5fe8f53fddf4a70db89435cc387882622c 100644 (file)
@@ -79,8 +79,6 @@ public:
     */
     Message* getRequestMessage();
 
-    void clearReconnect();
-
     /** Get the request message saved for resending on a challenge and
         release memory ownership for the message.
     */
@@ -91,6 +89,11 @@ public:
     */
     void clear();
 
+    /**
+        Reset the challenge status for the connection.
+    */
+    void resetChallengeStatus();
+
     /** Set the user name
     */
     void setUserName(const String& userName);
@@ -131,10 +134,10 @@ private:
     Boolean  _challengeReceived;
 
     String   _userName;
-
     String   _password;
 
-    String   _realm;
+    String   _localAuthFile;
+    String   _localAuthFileContent;
 
     AuthType _authType;
 };
index 353007e32787f9ed2afa4f00d64f1b97dae87e6a..ed2e11ff9c820e56e4db99bcb12c1a7803b87a62 100644 (file)
@@ -51,6 +51,7 @@
 #include <Pegasus/Common/TimeValue.h>
 #include <Pegasus/Common/Exception.h>
 #include <Pegasus/Common/PegasusVersion.h>
+#include <Pegasus/Common/AutoPtr.h>
 
 #include "CIMExportRequestEncoder.h"
 #include "CIMExportResponseDecoder.h"
@@ -76,6 +77,7 @@ CIMExportClient::CIMExportClient(
    _httpConnection(0),
    _timeoutMilliseconds(timeoutMilliseconds),
    _connected(false),
+   _doReconnect(false),
    _responseDecoder(0),
    _requestEncoder(0)
 {
@@ -134,6 +136,8 @@ void CIMExportClient::_connect()
 
     _responseDecoder->setEncoderQueue(_requestEncoder);    
 
+    _doReconnect = false;
+
     _connected = true;
 
     _httpConnection->setSocketWriteTimeout(_timeoutMilliseconds/1000+1);
@@ -177,15 +181,13 @@ void CIMExportClient::_disconnect()
 
         _connected = false;
     }
-    PEG_METHOD_EXIT();
-}
 
-void CIMExportClient::_reconnect()
-{
-    PEG_METHOD_ENTER (TRC_EXPORT_CLIENT, "CIMExportClient::_reconnect()");
-    _disconnect();
+    // Reconnect no longer applies
+    _doReconnect = false;
+
+    // Let go of the cached request message if we have one
     _authenticator.setRequestMessage(0);
-    _connect();
+
     PEG_METHOD_EXIT();
 }
 
@@ -326,19 +328,43 @@ void CIMExportClient::exportIndication(
 }
 
 Message* CIMExportClient::_doRequest(
-    CIMRequestMessage * request,
+    CIMRequestMessage* pRequest,
     const Uint32 expectedResponseMessageType
 )
 {
     PEG_METHOD_ENTER (TRC_EXPORT_CLIENT, "CIMExportClient::_doRequest()");
 
-    if (!_connected)
+    AutoPtr<CIMRequestMessage> request(pRequest);
+
+    if (!_connected && !_doReconnect)
     {
-       delete request;
        PEG_METHOD_EXIT();
        throw NotConnectedException();
     }
-    
+
+    if (_doReconnect)
+    {
+        try
+        {
+            _connect();
+            _doReconnect = false;
+        }
+        catch (const Exception& e)
+        {
+            PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL2,
+                "Failed to connect to indication listener: " + e.getMessage());
+            PEG_METHOD_EXIT();
+            throw;
+        }
+        catch (...)
+        {
+            PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL2,
+                "Failed to connect to indication listener.");
+            PEG_METHOD_EXIT();
+            throw;
+        }
+    }
+
     String messageId = XmlWriter::getNextMessageId();
     const_cast<String &>(request->messageId) = messageId;
 
@@ -352,7 +378,7 @@ Message* CIMExportClient::_doRequest(
     //
     request->setHttpMethod (HTTP_METHOD__POST);
 
-    _requestEncoder->enqueue(request);
+    _requestEncoder->enqueue(request.release());
 
     Uint64 startMilliseconds = TimeValue::getCurrentTime().toMilliseconds();
     Uint64 nowMilliseconds = startMilliseconds;
@@ -369,33 +395,37 @@ Message* CIMExportClient::_doRequest(
        // Check to see if incoming queue has a message
        //
 
-       Message* response = dequeue();
+       AutoPtr<Message> response(dequeue());
 
-       if (response)
+       if (response.get())
        {
             // Shouldn't be any more messages in our queue
             PEGASUS_ASSERT(getCount() == 0);
 
             //
-            //  Future:  If M-POST is used and HTTP response is 501 Not
-            //  Implemented or 510 Not Extended, retry with POST method
-            //
+            // Close the connection if response contained a "Connection: Close"
+            // header (e.g. at authentication challenge)
             //
 
             // Reconnect to reset the connection
             // if Server response contained a Connection: Close Header
             //
-            if (response->getCloseConnect() == true){
-                _reconnect();
+            if (response->getCloseConnect() == true)
+            {
+                _disconnect();
+                _doReconnect = true;
                 response->setCloseConnect(false);
             }
 
+            //
+            //  Future:  If M-POST is used and HTTP response is 501 Not
+            //  Implemented or 510 Not Extended, retry with POST method
+            //
 
             if (response->getType() == CLIENT_EXCEPTION_MESSAGE)
             {
                 Exception* clientException =
-                    ((ClientExceptionMessage*)response)->clientException;
-                delete response;
+                    ((ClientExceptionMessage*)response.get())->clientException;
                 PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL4, "Client Exception Message received.");
 
                 AutoPtr<Exception> d(clientException);
@@ -445,7 +475,8 @@ Message* CIMExportClient::_doRequest(
             {
                 PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL4, 
                     "Received expected indication response message.");
-                CIMResponseMessage* cimResponse = (CIMResponseMessage*)response;
+                CIMResponseMessage* cimResponse =
+                    (CIMResponseMessage*)response.get();
                 if (cimResponse->messageId != messageId)
                 {
                  // l10n
@@ -462,7 +493,6 @@ Message* CIMExportClient::_doRequest(
 
                  CIMClientResponseException responseException(mlString);
 
-                 delete response;
                   PEG_METHOD_EXIT();
                  throw responseException;
                 }
@@ -473,17 +503,25 @@ Message* CIMExportClient::_doRequest(
                     CIMException cimException(
                         cimResponse->cimException.getCode(),
                         cimResponse->cimException.getMessage());
-                    delete response;
                     PEG_METHOD_EXIT();
                    throw cimException;
                 }
                 PEG_METHOD_EXIT();
-                return response;
+                return response.release();
             }
-            else if (dynamic_cast<CIMRequestMessage*>(response) != 0)
+            else if (dynamic_cast<CIMRequestMessage*>(response.get()) != 0)
             {
-                // Respond to an authentication challenge
-                _requestEncoder->enqueue(response);
+                //
+                // Respond to an authentication challenge.
+                // Reconnect if the connection was closed.
+                //
+                if (_doReconnect)
+                {
+                    _connect();
+                }
+
+                _requestEncoder->enqueue(response.release());
+
                 nowMilliseconds = TimeValue::getCurrentTime().toMilliseconds();
                 stopMilliseconds = nowMilliseconds + _timeoutMilliseconds;
                 continue;
@@ -501,8 +539,6 @@ Message* CIMExportClient::_doRequest(
              
              CIMClientResponseException responseException(mlString);
 
-             delete response;
-
               PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL4, mlString);
 
               PEG_METHOD_EXIT();
@@ -517,20 +553,18 @@ Message* CIMExportClient::_doRequest(
     //
     // Reconnect to reset the connection (disregard late response)
     //
-    try
-    {
-        PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL4, "Doing a _reconnect()...");
-        _reconnect();
-    }
-    catch (...)
-    {
-    }
 
-    PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL4, "Connection to the listener timed out.");
-    PEG_METHOD_EXIT();
+    PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL4,
+        "Connection to the listener timed out.");
+
+    _disconnect();
+    _authenticator.resetChallengeStatus();
+    _doReconnect = true;
+
     //
     // Throw timed out exception:
     //
+    PEG_METHOD_EXIT();
     throw ConnectionTimeoutException();
 }
 
index b07277d1be4759c3c2822d361065ff345cc92cb9..dc809549f9e7d875c914d2876b0db5a3ac3ca76e 100644 (file)
@@ -173,8 +173,6 @@ class PEGASUS_EXPORT_CLIENT_LINKAGE CIMExportClient : public MessageQueue
 
       void _disconnect();
 
-      void _reconnect();
-
       Message* _doRequest(
         CIMRequestMessage * request,
         const Uint32 expectedResponseMessageType);
@@ -187,6 +185,19 @@ class PEGASUS_EXPORT_CLIENT_LINKAGE CIMExportClient : public MessageQueue
 
       Uint32 _timeoutMilliseconds;
       Boolean _connected;
+      /**
+          The CIMExportClient uses a lazy reconnect algorithm.  A reconnection
+          is necessary when the server (listener) sends a Connection: Close
+          header in the HTTP response or when a connection timeout occurs
+          while waiting for a response.  In these cases, a disconnect is
+          performed immediately and the _doReconnect flag is set.  The
+          connection is re-established only when required to perform another
+          operation.  Note that in the case of a connection timeout, the
+          challenge status must be reset in the ClientAuthenticator to allow
+          authentication to be performed properly on the new connection.
+      */
+      Boolean _doReconnect;
+
       CIMExportResponseDecoder* _responseDecoder;
       CIMExportRequestEncoder* _requestEncoder;
       ClientAuthenticator _authenticator;
index b7eacf727224466812ca6b2da139d9d29dd29eaa..9598e4d2159da31c50fe0c8caa849a7605296bb7 100644 (file)
@@ -383,7 +383,6 @@ public:
                 exportclient.exportIndication(
                     "/", indicationInstance, contentLanguages);
            }
-            exportclient.disconnect();
         }
         catch(Exception& e)
         {