Transport Resend: bug#3427, retry send when transport got network error

When tranposrt failes, if this is recoverable (such as a temporay network error)
it should not throw an exception, instead log a warning message and let the
upper layer (EvolutionSyncClient) to decide to retry or abort.
This commit is contained in:
Chen Congwu 2009-07-28 13:21:27 +08:00
parent 1b629dc341
commit 64cbb364b2
5 changed files with 50 additions and 26 deletions

View file

@ -179,13 +179,16 @@ void CurlTransportAgent::send(const char *data, size_t len)
m_aborting = false;
if ((code = curl_easy_setopt(m_easyHandle, CURLOPT_PROGRESSDATA, static_cast<void *> (this)))||
(code = curl_easy_setopt(m_easyHandle, CURLOPT_HTTPHEADER, m_slist)) ||
(code = curl_easy_setopt(m_easyHandle, CURLOPT_POSTFIELDSIZE, len)) ||
((code = curl_easy_perform(m_easyHandle)) && ((code != CURLE_ABORTED_BY_CALLBACK)||m_aborting))
(code = curl_easy_setopt(m_easyHandle, CURLOPT_POSTFIELDSIZE, len))
){
m_status = CANCELED;
checkCurl(code);
}
if(code != CURLE_ABORTED_BY_CALLBACK) {
if ((code = curl_easy_perform(m_easyHandle))) {
m_status = FAILED;
checkCurl(code, false);
} else {
m_status = GOT_REPLY;
}
}
@ -257,10 +260,14 @@ size_t CurlTransportAgent::readData(void *buffer, size_t size) throw()
return curr;
}
void CurlTransportAgent::checkCurl(CURLcode code)
void CurlTransportAgent::checkCurl(CURLcode code, bool exception)
{
if (code) {
SE_THROW_EXCEPTION(TransportException, m_curlErrorText);
if(exception){
SE_THROW_EXCEPTION(TransportException, m_curlErrorText);
}else {
SE_LOG_INFO(NULL, NULL, "CurlTransport Failure: %s", m_curlErrorText);
}
}
}
@ -281,6 +288,8 @@ int CurlTransportAgent::processCallback()
if (m_cb){
time_t curTime = time(NULL);
if (curTime - m_sendStartTime > m_cbInterval){
//change here to avoid duplicate call back to the upper layer
m_sendStartTime = curTime;
bool cont = m_cb (m_cbData);
if (cont) {
m_status = TIME_OUT;

View file

@ -105,7 +105,7 @@ class CurlTransportAgent : public TransportAgent
static int progressCallback (void *ptr, double dltotal, double dlnow, double uptotal, double upnow);
/** check curl error code and turn into exception */
void checkCurl(CURLcode code);
void checkCurl(CURLcode code, bool exception = true);
/**
* initialize curl if necessary, return new handle

View file

@ -1165,13 +1165,9 @@ bool EvolutionSyncClient::transport_cb (void *udata)
bool EvolutionSyncClient::processTransportCb()
{
if(++m_retries > m_retryCount)
{
SE_LOG_INFO(NULL, NULL, "Transport: give up after %d tries", m_retries-1);
return false;
}
SE_LOG_INFO(NULL, NULL, "Transport: retry send #%d after %d seconds timeout", m_retries, m_timeout);
//Always return true to continue, we will detect the retry count at
//the higher level together with transport error scenarios.
SE_LOG_INFO(NULL, NULL, "Transport timeout after %d seconds timeout", m_timeout);
return true;
}
@ -1689,8 +1685,9 @@ SyncMLStatus EvolutionSyncClient::doSync()
//too slow to the user; therefore, we can delay abort at other
//points to this two points (before sending and before receiving
//the data).
if (checkForAbort() && stepCmd == (sysync::STEPCMD_SENDDATA
|| stepCmd == sysync::STEPCMD_NEEDDATA)) {
if (checkForAbort() && (stepCmd == sysync::STEPCMD_RESENDDATA
|| stepCmd ==sysync::STEPCMD_SENDDATA
|| stepCmd == sysync::STEPCMD_NEEDDATA)) {
stepCmd = sysync::STEPCMD_ABORT;
}
@ -1800,7 +1797,7 @@ SyncMLStatus EvolutionSyncClient::doSync()
if(!resend) {
sendBuffer = m_engine.GetSyncMLBuffer(session, true);
}else {
SE_LOG_INFO (NULL, NULL, "EvolutionSyncClient: resend previous request");
SE_LOG_INFO (NULL, NULL, "EvolutionSyncClient: resend previous request #%d", m_retries);
}
agent->send(sendBuffer.get(), sendBuffer.size());
stepCmd = sysync::STEPCMD_SENTDATA; // we have sent the data
@ -1813,12 +1810,16 @@ SyncMLStatus EvolutionSyncClient::doSync()
stepCmd = sysync::STEPCMD_SENTDATA; // still sending the data?!
break;
case TransportAgent::TIME_OUT:
stepCmd = sysync::STEPCMD_RESENDDATA;
resend = true;
case TransportAgent::FAILED:
if(m_retries++ >= m_retryCount){
SE_LOG_INFO(NULL, NULL, "Transport give up after %d retries",m_retryCount);
stepCmd = sysync::STEPCMD_ABORT;
}else {
stepCmd = sysync::STEPCMD_RESENDDATA;
resend = true;
}
break;
case TransportAgent::GOT_REPLY: {
m_retries = 0;
sendBuffer.reset();
const char *reply;
size_t replylen;
string contentType;
@ -1829,6 +1830,8 @@ SyncMLStatus EvolutionSyncClient::doSync()
contentType.find("application/vnd.syncml+wbxml") != contentType.npos ||
contentType.find("application/vnd.syncml+xml") != contentType.npos) {
// put answer received earlier into SyncML engine's buffer
m_retries = 0;
sendBuffer.reset();
m_engine.WriteSyncMLBuffer(session,
reply,
replylen);
@ -1837,7 +1840,14 @@ SyncMLStatus EvolutionSyncClient::doSync()
SE_LOG_DEBUG(NULL, NULL, "unexpected content type '%s' in reply, %d bytes:\n%.*s",
contentType.c_str(), (int)replylen, (int)replylen, reply);
SE_LOG_ERROR(NULL, NULL, "unexpected reply from server; might be a temporary problem, try again later");
stepCmd = sysync::STEPCMD_TRANSPFAIL;
if(m_retries++ >= m_retryCount){
SE_LOG_INFO(NULL, NULL, "Transport give up after %d retries",m_retryCount);
stepCmd = sysync::STEPCMD_ABORT;
}else {
stepCmd = sysync::STEPCMD_RESENDDATA;
resend = true;
}
}
break;
}

View file

@ -145,7 +145,7 @@ void SoupTransportAgent::send(const char *data, size_t len)
void SoupTransportAgent::cancel()
{
m_status = FAILED;
m_status = CANCELED;
soup_session_abort(m_session.get());
if(g_main_loop_is_running(m_loop.get()))
g_main_loop_quit(m_loop.get());
@ -168,9 +168,13 @@ TransportAgent::Status SoupTransportAgent::wait()
break;
}
/** For a canceled message, does not check the return status */
if(m_status == TIME_OUT){
m_failure.clear();
/** For a canceled message, does not throw exception, just print a warning, the
* upper layer may decide to retry
*/
if(m_status == TIME_OUT || m_status == FAILED){
std::string failure;
std::swap(failure, m_failure);
SE_LOG_INFO(NULL, NULL, "SoupTransport Failure: %s", failure.c_str());
}
if (!m_failure.empty()) {
std::string failure;

View file

@ -115,7 +115,8 @@ class TransportAgent
*/
CANCELED,
/**
* sending message has failed
* sending message has failed, transport should not throw an exception
* if the error is recoverable (such as a temporary network error)
*/
FAILED,
/**