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.
:
MessageQueue(PEGASUS_QUEUENAME_CLIENT),
_timeoutMilliseconds(timeoutMilliseconds),
- _connected(false)
+ _connected(false),
+ _doReconnect(false)
{
//
// Create Monitor and HTTPConnector
_requestEncoder->setDataStorePointer(&perfDataStore);
_responseDecoder->setDataStorePointer(&perfDataStore);
+ _doReconnect = false;
_connected = true;
_httpConnection->setSocketWriteTimeout(_timeoutMilliseconds/1000+1);
}
_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(
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;
// 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);
}
{
Exception* clientException =
- ((ClientExceptionMessage*)response)->clientException;
- delete response;
+ ((ClientExceptionMessage*)response.get())->clientException;
AutoPtr<Exception> d(clientException);
}
else if (response->getType() == expectedResponseMessageType)
{
- CIMResponseMessage* cimResponse = (CIMResponseMessage*)response;
+ CIMResponseMessage* cimResponse =
+ (CIMResponseMessage*)response.get();
if (cimResponse->messageId != messageId)
{
CIMClientResponseException responseException(mlString);
- delete response;
throw responseException;
}
cimResponse->cimException.getCode(),
cimResponse->cimException.getMessage());
cimException.setContentLanguages(responseContentLanguages);
- delete response;
throw cimException;
}
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;
CIMClientResponseException responseException(mlString);
- delete response;
throw responseException;
}
}
//
// Reconnect to reset the connection (disregard late response)
//
- try
- {
- _reconnect();
- }
- catch (...)
- {
- }
+
+ _disconnect();
+ _authenticator.resetChallengeStatus();
+ _doReconnect = true;
//
// Throw timed out exception:
void _disconnect();
- void _reconnect();
-
Message* _doRequest(
AutoPtr<CIMRequestMessage>& request,
const Uint32 expectedResponseMessageType);
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;
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;
}
}
}
- _realm = authRealm;
+ _localAuthFile = authRealm;
return true;
}
return _requestMessage.get();
}
-void ClientAuthenticator::clearReconnect()
+void ClientAuthenticator::resetChallengeStatus()
{
- _requestMessage.reset();
- _realm = String::EMPTY;
_challengeReceived = false;
+ _localAuthFile.clear();
+ _localAuthFileContent.clear();
}
Message* ClientAuthenticator::releaseRequestMessage()
if (!ifs)
{
//ATTN: Log error message
- return (challenge);
+ return String::EMPTY;
}
String line;
//
// 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);
*/
Message* getRequestMessage();
- void clearReconnect();
-
/** Get the request message saved for resending on a challenge and
release memory ownership for the message.
*/
*/
void clear();
+ /**
+ Reset the challenge status for the connection.
+ */
+ void resetChallengeStatus();
+
/** Set the user name
*/
void setUserName(const String& userName);
Boolean _challengeReceived;
String _userName;
-
String _password;
- String _realm;
+ String _localAuthFile;
+ String _localAuthFileContent;
AuthType _authType;
};
#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"
_httpConnection(0),
_timeoutMilliseconds(timeoutMilliseconds),
_connected(false),
+ _doReconnect(false),
_responseDecoder(0),
_requestEncoder(0)
{
_responseDecoder->setEncoderQueue(_requestEncoder);
+ _doReconnect = false;
+
_connected = true;
_httpConnection->setSocketWriteTimeout(_timeoutMilliseconds/1000+1);
_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();
}
}
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;
//
request->setHttpMethod (HTTP_METHOD__POST);
- _requestEncoder->enqueue(request);
+ _requestEncoder->enqueue(request.release());
Uint64 startMilliseconds = TimeValue::getCurrentTime().toMilliseconds();
Uint64 nowMilliseconds = startMilliseconds;
// 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);
{
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
CIMClientResponseException responseException(mlString);
- delete response;
PEG_METHOD_EXIT();
throw responseException;
}
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;
CIMClientResponseException responseException(mlString);
- delete response;
-
PEG_TRACE_STRING(TRC_EXPORT_CLIENT, Tracer::LEVEL4, mlString);
PEG_METHOD_EXIT();
//
// 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();
}
void _disconnect();
- void _reconnect();
-
Message* _doRequest(
CIMRequestMessage * request,
const Uint32 expectedResponseMessageType);
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;
exportclient.exportIndication(
"/", indicationInstance, contentLanguages);
}
- exportclient.disconnect();
}
catch(Exception& e)
{