cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: Issue with partial file transfer

From: Grzegorz Sikorski <engineer.gregor_at_gmail.com>
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