cURL / Mailing Lists / curl-library / Single Mail

curl-library

Problem: curl multi pipelining mix up easy_handle

From: Cheng Luo (cheluo) <cheluo_at_cisco.com>
Date: Fri, 31 May 2013 05:35:06 +0000

Hi,

This is the problem I encountered:
It seems that multi session mix up the easy_handles (which are been added into the session) when used in multi pipelining.
The phenomenon looks like this:
(1) Enable curlmopt_pipelining and curlopt_followlocation
(2) Add easy_handle "A" with normal url "a" into multi_handle "m"
(3) Add easy_handle "B" with redirect url "b" into multi_handle "m"
(4) Run curl_multi_perform
(5) The result shows that easy_handle "A" is for redirect url "b"; and easy_handle "B" is for normal url "a"
(6) When to disable the multi pipelining, the phenomenon disappear.

I find this problem in one complicated application which use multiple interface in libcurl (version: 7.25 ) to download files from apache server (version: 2.2.12) with multi pipelining enabled. When to disable multi pipelining in this application, the problem disappear. I have extracted the curl download logic from this complicated application into a single file. I have attached this file in the following and you can refer it for the curl download logic in our complicated application.

My question:
Is this a known issue for multi interface with multi pipelining? If it is, is it been resolved at the latest version (a.k.a: 7.30). If it not, is there any workaround with multi pie lining enabled?

=================
#define HANDLER_SIZE 20

int curlDebugCallback (CURL* curl, curl_infotype intoType, char* msg, size_t msgSize)
{
std::string debugString(msg, msgSize);
        fprintf(stdout, "curlDebugCallback: %s\n", debugString.c_str());
return 0;
}

int main(void)
{
        CURLcode res;
        CURLM *mMultiCurl;
        FILE *outputFile;
        std::string tempFilePath;
        char curlErrorBuffer[200];

        mMultiCurl = curl_multi_init();
        curl_multi_setopt(mMultiCurl, CURLMOPT_PIPELINING, 1);

        CURL *handles[HANDLER_SIZE];
        FILE *outputFileHandlers[HANDLER_SIZE];
        for (int i=0; i < HANDLER_SIZE; i++)
        {
                /* init the curl session */
                handles[i] = curl_easy_init();

                curl_easy_setopt(handles[i], CURLOPT_ERRORBUFFER, curlErrorBuffer);
                curl_easy_setopt(handles[i], CURLOPT_HEADER, 0);
                curl_easy_setopt(handles[i], CURLOPT_FOLLOWLOCATION, 1);
                curl_easy_setopt(handles[i], CURLOPT_VERBOSE, 1);
                curl_easy_setopt(handles[i], CURLOPT_FAILONERROR, true);
                curl_easy_setopt(handles[i], CURLOPT_SSL_SESSIONID_CACHE, 0L);
                curl_easy_setopt(handles[i], CURLOPT_SSL_VERIFYHOST, 0L);
                curl_easy_setopt(handles[i], CURLOPT_SSL_VERIFYPEER, 0L);
                curl_easy_setopt(handles[i], CURLOPT_HTTPAUTH, CURLAUTH_BASIC);//CURLAUTH_BASIC
                curl_easy_setopt(handles[i], CURLOPT_COOKIEFILE, "csf2g-pm");
                curl_easy_setopt(handles[i], CURLOPT_HTTPHEADER, NULL);
                curl_easy_setopt(handles[i], CURLOPT_DEBUGFUNCTION, curlDebugCallback);

                //specify URL to get
                if ( i%2 == 0 )
                {
                        //This url is not a redirect url
                        curl_easy_setopt(handles[i], CURLOPT_URL, "http://64.104.172.101/apache_pb.gif");
                        fprintf(stdout, "index is: %d, handler is: %p, url is %s\n",i, handles[i], "http://64.104.172.101/apache_pb.gif");
                }
                else
                {
                        //This url is a redirect url
                        curl_easy_setopt(handles[i], CURLOPT_URL, "http://64.104.172.101/index.html");
                        fprintf(stdout, "index is %d, handler is: %p, url is %s\n",i, handles[i], "http://64.104.172.101/index.html");
                }

                char strtemp[10];
                sprintf(strtemp, "%d", i);
                std::string tempFilePath = "/Users/loc/Downloads/test"+ std::string(strtemp) + ".jpg";

                outputFileHandlers[i] = fopen(tempFilePath.c_str(), "wb");
                if(outputFileHandlers[i] == NULL)
                {
                        fprintf(stderr, "open outputFile failed\n");
                        return 1;
                }
                curl_easy_setopt(handles[i], CURLOPT_WRITEDATA, outputFileHandlers[i]);

                curl_multi_remove_handle(mMultiCurl, handles[i]);
                curl_multi_add_handle(mMultiCurl, handles[i]);

        }

        int still_running;
        CURLMcode mcode = curl_multi_perform(mMultiCurl, &still_running);
        CURLMsg *msg; /* for picking up messages with the transfer status */
        int msgs_left; /* how many messages are left */
        int retries = 0;
        while(still_running )
        {
                struct timeval timeout;
                int rc; /* select() return code */

                fd_set fdread;
                fd_set fdwrite;
                fd_set fdexcep;
                int maxfd = -1;

                long curl_timeo = -1;

                FD_ZERO(&fdread);
                FD_ZERO(&fdwrite);
                FD_ZERO(&fdexcep);

                /* set a suitable timeout to play around with */
                timeout.tv_sec = 1;
                timeout.tv_usec = 0;

                curl_multi_timeout(mMultiCurl, &curl_timeo);
                if(curl_timeo >= 0)
                {
                        timeout.tv_sec = curl_timeo / 1000;
                        if(timeout.tv_sec > 1)
                                timeout.tv_sec = 1;
                        else
                                timeout.tv_usec = (curl_timeo % 1000) * 1000;
                }

                /* get file descriptors from the transfers */
                curl_multi_fdset(mMultiCurl, &fdread, &fdwrite, &fdexcep, &maxfd);

                /* In a real-world program you OF COURSE check the return code of the
                   function calls. On success, the value of maxfd is guaranteed to be
                   greater or equal than -1. We call select(maxfd + 1, ...), specially in
                   case of (maxfd == -1), we call select(0, ...), which is basically equal
                   to sleep. */

                rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);

                switch(rc)
                {
                        case -1:
                                /* select error */
                                fprintf(stderr, "select error\n");
                                break;
                        case 0: /* timeout */
                        default: /* action */
                                mcode = curl_multi_perform(mMultiCurl, &still_running);
                                break;
                }
                retries++;
        }

        /* See how the transfers went */
        while ((msg = curl_multi_info_read(mMultiCurl, &msgs_left)))
        {
                if (msg->msg == CURLMSG_DONE)
                {
                        char* effectivurl;
                        bool found = false;
                        int i = 0;
                        for(; i< HANDLER_SIZE; i++)
                        {
                                if (msg->easy_handle == handles[i])
                                {
                                        found = true;
                                        break;
                                }
                        }

                        if (!found)
                        {
                                printf("curl handle not found\n");
                                return 1;
                        }
                        fseek(outputFileHandlers[i], 0, SEEK_END);
                        long outputFileSize = ftell(outputFileHandlers[i]);

                        int success = fclose(outputFileHandlers[i]);

                        double size = 0.0;
                        double cLength = 0.0;
                        curl_easy_getinfo(msg->easy_handle, CURLINFO_SIZE_DOWNLOAD, &size);
                        curl_easy_getinfo(msg->easy_handle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &cLength);
                        curl_easy_getinfo(msg->easy_handle, CURLINFO_EFFECTIVE_URL, &effectivurl);

                        printf("curl handle is %p,index is %d, url is %s, size is %lf, clength is %lf, msg_result is %d, error is %s\n",msg->easy_handle, i, effectivurl, size, cLength, msg->data.result, curlErrorBuffer);
                        if((cLength > 0.0 && cLength != size) || outputFileSize <= 0)
                        {
                                fprintf(stderr, "download photo lenth error\n");
                        }

                }
        }

        /* cleanup curl stuff */
        for(int i=0; i< HANDLER_SIZE; i++)
        {
                curl_easy_cleanup(handles[i]);
        }

        curl_multi_cleanup(mMultiCurl);
        mMultiCurl = NULL;

        return 0;
}
=================

Thanks.

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette: http://curl.haxx.se/mail/etiquette.html
Received on 2013-05-31