cURL / Mailing Lists / curl-library / Single Mail

curl-library

simple easy HTTP post file upload test of progress callback question

From: Max L. Eidswick <max_at_eidswick.com>
Date: Fri, 25 Jan 2008 15:24:08 -0700

I am doing a test of the CURLOPT_PROGRESSFUNCTION callback for a simple
libcurl easy http file upload. The test code (included below) uploads a
test file that is ~1.3MB big. The test callback gets called about 90
times, each time all arguments are zero except the last which is about
1MB.

I have not used the CURLOPT_PROGRESSDATA because I was unclear what it
is used for and what type of pointer information gets returned, if any.

I am obviously missing something obvious, however I could not find any
Available clarification online.

So, my questions are twofold: (1) Why doesn't the callback get any
incremental upload stats and (2) Why is the final report less than the
actual data uploaded? The uploaded file is correct and complete.

Thanks for looking into this.

Max

// ---------------------------------------------------------------------
// module to test progress callback during http file upload -- 25.01.08
//
// the symptom I am experiencing in this test that I don't understand
// is that the SendfileProgressCallback proc is called about 90 times
// in the 15 seconds it takes to upload the ~1.3mb test file. It then
// reports 1,075,576,832 bytes uploaded for a file that is
// 1,338,460 bytes big (the upload is successful, I can re-download it
// and test the RAR file's integrity).
//
// obviously, I am doing something wrong, either with the callback
// data or I need to use the CURLOPT_PROGRESSDATA pointer, but I
// am unclear as to what type of structure it is supposed to point to
// and the format of the data that gets pointed to by it. I have gone
// through the current examples and several others I have found on the
// web, but most seem to do with the multi-interface.
//
// I am confident this libcurl version works fine with this feature,
// so this must be a simple mis-implementation of this test code.
//
// Thanks for any direction. The last run output follows together
// with a directory listing of the source sendfile test file ...
//
/* ----- snip: output from command line stdout:

Uploading now:
Call count: 90, dltotal: 0, dlnow: 0, ultotal: 0,ulnow: 1075576832
curl sendfile time: 15281 msecs

 ----- End of test of libcurl sendfile ----- Press any key to exit.

----- end snip */

/* ----- snip: dir size of test file:

">"dir c:\sendfiletest.rar

01/24/2008 11:08 AM 1,338,460 SendfileTest.rar
               1 File(s) 1,338,460 bytes

----- end snip */

// see SendfileProgressCallback for this test module.
// ---------------------------------------------------------------------
// standard c and windows includes
#include <conio.h>

// ---------------------------------------------------------------------
// cURL inclusions (winsock2.h is before windows.h)
// ---------------------------------------------------------------------
#define _WIN32_WINNT 0x0500 // for icon notification size
#pragma warning( disable : 4996 ) // eliminate vs2005 security bs

// ---------------------------------------------------------------------
// libcurl includes in ..\RedPawComms\libcURL\curl-7.17.1\include
#include <curl/curl.h>

// ---------------------------------------------------------------------
// test module storage allocations and structures
struct curl_httppost *formPostSendfile = NULL ; // for formadd
struct curl_httppost *lastPtr = NULL ; // for libcurl curl_formadd

// ---------------------------------------------------------------------
UCHAR ucCURLErrorBuffer[CURL_ERROR_SIZE] ; // used by libcurl errors
ULONG ulStartClick = 0 ; // millisecond counter before easy perform
ULONG ulStopClick = 0 ; // millisecond counter after easy perform
UCHAR szTmpBuf[2048] ; // plenty of space for string output
BOOL bDone = FALSE ; // exit flag, when done

// ---------------------------------------------------------------------
CURL *curlhSendfile ; // libcurl sendfile test handle
CURLcode res ; // result code from libcurl

// =====================================================================
// libcurl file transfer callback -
//
// the docs say to look at the prototype in curl.h, but it doesn't say
// much about what the clientp pointer is for ... maybe testing will
// help clear this up ...
// =====================================================================
int SendfileProgressCallback (
          void *clientp, // CURLOPT_PROGRESSDATA pointer
          double dltotal, // download total bytes
          double dlnow, // downloaded bytes so far
          double ultotal, // uploaded total bytes
          double ulnow // uploaded bytes so far
                                                         )
{
        static BOOL bFirstTime = TRUE ;
        static ULONG ulCallCount = 0 ;

        // write a command line window header for the test, then display
        // the callback data as called ..
        if ( bFirstTime )
        {
                printf ( "\nUploading now:\n" ) ;
                bFirstTime = FALSE ;
        }

        printf (
                "\rCall count: %d, dltotal: %ld, dlnow: %ld, ultotal: %ld,"
                "ulnow: %ld",
                ++ulCallCount, dltotal, dlnow, ultotal, ulnow ) ;

        return 0 ;
}
// end of progress callback test
// =====================================================================

// =====================================================================
// start of main test win32 procedure
//
// Testing: libcurl call to send a test file using a callback
//
// =====================================================================
int main ( int argc, char *argv[] ) // test for comms dvlmt
{
        // ----- local data definitions for main proc ---------------------

        // -----------------------------------------------------------------
        // verify the file exists on client workstation, exit with error msg
        // if not

        // -----------------------------------------------------------------
        // setup libcurl global info for win32, global cleanup on exit
        if ( curl_global_init ( CURL_GLOBAL_ALL | CURL_GLOBAL_WIN32 ) )
        {
                // report and ABEND!
                MessageBox ( NULL, "libcurl global init error",
                        "libcurl global init error", MB_OK ) ;
        } // end of call to curl global init failed
        

        // -----------------------------------------------------------------
        // initialize the easy interface
        curlhSendfile = curl_easy_init ( ) ; // get curl handle
        if ( !curlhSendfile ) // zero means no handle, failed
        {
                // failed to get easy handle, so global cleanup and ABEND
                curl_global_cleanup ( ) ; // clean up environment
                MessageBox ( NULL, "libcurl easy init failure",
                        "libcurl easy init failure", MB_OK ) ;
                ExitProcess ( 101 ) ; // ABEND ABEND ABEND ABEND ABEND ABEND
        } // end of if !curl call to curl init failed

        // -------------------------------------------------------------
        // curl fails on 400+ error codes from the server - it's unclear
        // if this does anything useful for our ping example, but it is
        // in the planned production design, so it is here too ...
        // -------------------------------------------------------------
        curl_easy_setopt ( curlhSendfile, CURLOPT_FAILONERROR, TRUE ) ;

        // ------------------------------------------------------------
        // if there are any errors, make sure we know what libcurl says
        // is going on
        // ------------------------------------------------------------
        curl_easy_setopt (
                                curlhSendfile, // for our sendfile test handle
                                CURLOPT_ERRORBUFFER, // ask libcurl to keep us
                                ucCURLErrorBuffer // informed about errors
                                                ) ;

        // -------------------------------------------------------------
        // setup our test URL
        // -------------------------------------------------------------
        curl_easy_setopt ( // setup URL to touch
                curlhSendfile,
                CURLOPT_URL,
                "http://redpawsys.com/RPV7CommServices/"
                "RPV7SendfileViaHTTPPost.php"
                                                ) ;

        // =================================================================
        // -----------------------------------------------------------------
        // THIS IS WHAT WE ARE TESTING IN THIS MODULE
        // -----------------------------------------------------------------
        // turn on progress callback
        curl_easy_setopt(curlhSendfile, CURLOPT_NOPROGRESS, FALSE ) ;

        // set progress callback
        curl_easy_setopt (
                curlhSendfile,
                CURLOPT_PROGRESSFUNCTION,
                SendfileProgressCallback
                                          ) ;

        // =================================================================

        // -----------------------------------------------------------------
        // setup a simple sendfile form, the php script simply copies the
        // uploaded file and creates an upload logfile entry for testing
        // -----------------------------------------------------------------
        curl_formadd ( // add field/value for sendfile/callback test
                &formPostSendfile, // libcurl form for sendfile
                &lastPtr, // managed by formadd
                CURLFORM_COPYNAME, "sendfile", // field name
                CURLFORM_FILE, "C:\\SendfileTest.rar", // HTTP upload
                CURLFORM_END
                                  ) ; // per libcurl spec

        // -------------------------------------------------------------
        // Tells libcurl you want a multipart/formdata HTTP POST to be
        // made and you instruct what data to pass on to the server.
        // -------------------------------------------------------------
        curl_easy_setopt (
                curlhSendfile,
                CURLOPT_HTTPPOST,
                formPostSendfile
                                         ) ;

        // -------------------------------------------------------------
        // DO IT. This should trigger the progress callback under test
        // check the msecs before and after the call to easy perform
        // -------------------------------------------------------------
        ulStartClick = GetTickCount ( ) ;
        res = curl_easy_perform ( curlhSendfile ) ; // << --- send it!
        ulStopClick = GetTickCount ( ) ;
        if ( res ) // libcurl call failed for some reason, so let's see
        {

                sprintf ( szTmpBuf, "cURL error: %d, %s",
                                                                curlhSendfile, ucCURLErrorBuffer ) ;
                printf ( "\n ----- %s ----- \n", szTmpBuf ) ;

        } // end of if libcurl perform call failed
        else // the libcurl call was successful, so let's just see the
        { // click count and some of the PHP script response ...
                printf ( "\nlibcurl sendfile time: %d msecs",
                                                                        ulStopClick - ulStartClick ) ;
        
        } // end of else libcurl perform call was successful

        // -----------------------------------------------------------------
        // clean up allocations for the sendfile form
        curl_formfree ( formPostSendfile ) ; // free the form

        // -----------------------------------------------------------------
        // standard libcurl cleanup
        // -----------------------------------------------------------------
        curl_easy_cleanup ( curlhSendfile ) ; // end of easy session
        curl_global_cleanup ( ) ; // clean up environment
        
        // -----------------------------------------------------------------
        printf ( "\n\n ----- End of test of libcurl sendfile ----- "
                                "Press any key to exit." ) ;
        res = _getch ( ) ;

        // -----------------------------------------------------------------
        // end of this test program
        // -----------------------------------------------------------------

        return TRUE ;

} // ~~~~~ end of main procedure ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Received on 2008-01-25