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:
parent
1b629dc341
commit
64cbb364b2
|
@ -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) {
|
||||
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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,7 +1685,8 @@ 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
|
||||
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:
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
/**
|
||||
|
|
Loading…
Reference in a new issue