curl-library
Re: Issue with partial file transfer
Date: Wed, 18 Apr 2012 12:59:20 +0200
Hi,
Sorry, I should say I use libcurl. This is log from my C++ application,
though it is really a copy of what libcurl returns as an error. For more
information on how it all works please, analyze below function, which is
called to initialize data uploading (it is also called when we restore the
connection after described network problem). Note I always call
curl_easy_reset(pEasyHandle). When the transfer is finished, I handle it
using curl_multi_remove_handle API.
Greg
///////////////////////////////////// C O D E
/////////////////////////////////////
/**
* @brief Configures easy handle and inserts it to multi handle.
* @param transfer - data related to transfer
* @param easy handle
* @return error code
* @warning This function can be called only when easy handle is not in
multi yet.
*/
tm_error_t TransferManager::SetupTransfer(transfer_data& transfer, CURL*
pEasyHandle)
{
const long MIN_OPERATION_TIME = 2;
const long FTP_RESPONSE_TIMEOUT = 90;
const long CONNECT_TIMEOUT = 20;
const long LOW_SPEED_TIME = 20;
long timeout = difftime(transfer.task.expireTime, std::time(0));
if (timeout < MIN_OPERATION_TIME)
{
LOG_INFO("There is not enough time for the operation: " << timeout
<< " s, minimum is: " << MIN_OPERATION_TIME << " s");
Fclose(transfer.fileHandle);
return TM_OP_TOUT;
}
long connectTimeout = (timeout > CONNECT_TIMEOUT) ? CONNECT_TIMEOUT :
timeout/2;
long ftpResponseTimeout = (FTP_RESPONSE_TIMEOUT < timeout) ?
FTP_RESPONSE_TIMEOUT : (timeout - 1);
try {
curl_easy_reset(pEasyHandle);
transfer.errorBuffer[0] = '\0'; // we clean buffer before every
retry of transfer
Set_Easy_Opt(pEasyHandle, CURLOPT_ERRORBUFFER,
transfer.errorBuffer);
Set_Easy_Opt(pEasyHandle, CURLOPT_URL,
transfer.task.urlToFile.c_str());
if (transfer.task.isUplink)
{ // UPLOAD
if (not transfer.fileHandle) // it can be initial try
{
transfer.fileHandle =
fopen(transfer.task.GetLocalPath().c_str(), "rb");
if (not transfer.fileHandle)
{
LOG_ERROR("CURL: Could not open file handle for upload
to: " << transfer.task.GetLocalPath()
<< ", errno: " << errno << ", " <<
strerror(errno));
return TM_FILE_ERR;
}
struct stat s;
if (stat(transfer.task.GetLocalPath().c_str(), &s))
{
LOG_ERROR("Stat error for file: " <<
transfer.task.GetLocalPath() << ", errno: "
<< errno << ", " << strerror(errno));
return TM_FILE_ERR;
}
else
transfer.totalBytes = s.st_size;
}
LOG_DEBUG("In SetupTransfer for upload of file " <<
transfer.task.GetLocalPath()
<< " its size is: " << transfer.totalBytes );
Set_Easy_Opt(pEasyHandle, CURLOPT_UPLOAD, 1l);
Set_Easy_Opt(pEasyHandle, CURLOPT_APPEND, 1l);
Set_Easy_Opt(pEasyHandle, CURLOPT_RESUME_FROM, -1l); //
continue upload
Set_Easy_Opt(pEasyHandle, CURLOPT_INFILESIZE,
transfer.totalBytes);
Set_Easy_Opt(pEasyHandle, CURLOPT_READDATA,
transfer.fileHandle);
}
else
{ // DOWNLOAD
if (not transfer.fileHandle) // it can be initial try
{
transfer.fileHandle =
fopen(transfer.task.GetLocalPath().c_str(), "ab");
if (not transfer.fileHandle)
{
LOG_ERROR("CURL: Could not open file handle for
download to: " << transfer.task.GetLocalPath()
<< ", errno: " << errno << ", " <<
strerror(errno));
return TM_FILE_ERR;
}
}
struct stat s;
if (stat(transfer.task.GetLocalPath().c_str(), &s))
{
LOG_ERROR("Stat error before download for file: " <<
transfer.task.GetLocalPath() << ", errno: "
<< errno << ", " << strerror(errno));
return TM_FILE_ERR;
}
LOG_DEBUG("Download of file " << transfer.task.urlToFile << "
to file " << transfer.task.GetLocalPath()
<< " will be started from byte: " << s.st_size);
Set_Easy_Opt(pEasyHandle, CURLOPT_RESUME_FROM, s.st_size);
Set_Easy_Opt(pEasyHandle, CURLOPT_WRITEDATA,
transfer.fileHandle);
}
Set_Easy_Opt(pEasyHandle, CURLOPT_SEEKFUNCTION, my_seek); //fseek
Set_Easy_Opt(pEasyHandle, CURLOPT_SEEKDATA, transfer.fileHandle);
std::string userPass = mFtpUser + ":" + mFtpPass;
Set_Easy_Opt(pEasyHandle, CURLOPT_USERPWD, userPass.c_str());
Set_Easy_Opt(pEasyHandle, CURLOPT_NOSIGNAL, 1l);
LOG_DEBUG("Setting timeout for task with " <<
transfer.task.urlToFile
<< ", timeout = " << timeout << ", connect timeout = "
<< connectTimeout
<< ", ftpResponseTimeout = " << ftpResponseTimeout);
Set_Easy_Opt(pEasyHandle, CURLOPT_TIMEOUT, timeout);
Set_Easy_Opt(pEasyHandle, CURLOPT_CONNECTTIMEOUT, connectTimeout);
Set_Easy_Opt(pEasyHandle, CURLOPT_FTP_RESPONSE_TIMEOUT,
ftpResponseTimeout);
#ifdef CURL_DEBUG_CALLBACK
Set_Easy_Opt(pEasyHandle, CURLOPT_VERBOSE, 1l);
Set_Easy_Opt(pEasyHandle, CURLOPT_DEBUGFUNCTION, CurlDebugCallback);
#endif
Set_Easy_Opt(pEasyHandle, CURLOPT_LOW_SPEED_TIME, LOW_SPEED_TIME);
Set_Easy_Opt(pEasyHandle, CURLOPT_LOW_SPEED_LIMIT, 1l);
Set_Easy_Opt(pEasyHandle, CURLOPT_NOPROGRESS, 0l);
Set_Easy_Opt(pEasyHandle, CURLOPT_PROGRESSFUNCTION,
CurlProgressFunc);
Set_Easy_Opt(pEasyHandle, CURLOPT_PROGRESSDATA, pEasyHandle);
Set_Easy_Opt(pEasyHandle, CURLOPT_IPRESOLVE,
CURL_IPRESOLVE_WHATEVER);
} catch (std::runtime_error const& e) {
LOG_ERROR("CURL: easy handle could not be configured for server
task with url: \""
<< transfer.task.urlToFile << "\", local path: \"" <<
transfer.task.GetLocalPath()
<< "\", exception: " << e.what());
if (transfer.errorBuffer[0])
LOG_ERROR("CURL: ConfigureCurlHandle: curl error buffer: " <<
transfer.errorBuffer);
Fclose(transfer.fileHandle);
return TM_SOFTWARE_ERR;
}
CURLMcode curlResult = curl_multi_add_handle(mMultiHandle, pEasyHandle);
if (curlResult != CURLM_OK)
{
LOG_ERROR("CURL: easy handle could not be added to multi!!!! error
code: "
<< (int) curlResult << " " <<
curl_multi_strerror(curlResult));
Fclose(transfer.fileHandle);
return TM_SOFTWARE_ERR;
}
return TM_NO_ERR;
}
///////////////////////////////////// C O D E
/////////////////////////////////////
-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2012-04-18