c libcurl ftp upload incomplete - 1 missing byte
<p>I'm trying to use <code>libcurl</code> to test download and upload speeds. I have this weird issue on the upload side: 1 byte is systematically missing from the file I upload through <code>ftp</code>. Note that I'm not reading a file but rather I generate data on the fly in the <code>read_callback</code> of <code>curl</code> as all I'm interested in is the speed and note the data itself. See the code below, which is heavily based on this example: <a href="https://curl.haxx.se/libcurl/c/chkspeed.html" rel="nofollow noreferrer">libcurl chkspeed example</a></p>
<pre><code>#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;string.h&gt;
#include &lt;time.h&gt;

#include &lt;curl/curl.h&gt;

#define UL_FTP_USR      &quot;user&quot;
#define UL_FTP_PWD      &quot;password&quot;
#define UL_TARGET_URL   &quot;ftp://xxx.xxx.xxx.xxx/curl_upload_test&quot;
#define UL_SIZE          1000000

long ul_total_bytes = 0;
static size_t read_callback_full(void *ptr, size_t size, size_t nmemb, void *stream)
{
    if(ul_total_bytes &gt;= UL_SIZE) {
        printf(&quot;ul_total_bytes = %d\n&quot;, ul_total_bytes);
        return 0; // we stop upload at this point
    }

    char *pp = (char *) ptr;
    int i = 0;
    for(; i &lt; size*nmemb; i++)
    {
        pp[i] = i;
        ul_total_bytes += 1;
        if(ul_total_bytes &gt;= UL_SIZE) {
            printf(&quot;ul_total_bytes = %d\n&quot;, ul_total_bytes);
            return i; // if we reach UL_SIZE_LIM we send last payload
        }
    }
    return i;
}

int main(int argc, char *argv[])
{
  const char *url = UL_TARGET_URL;
  CURL *curl_handle;
  CURLcode res;

  // init libcurl
  curl_global_init(CURL_GLOBAL_ALL);

  // init the curl session
  curl_handle = curl_easy_init();

  // specify URL to put
  curl_easy_setopt(curl_handle, CURLOPT_URL, url);
  
  //---------- FTP CREDENTIALS -----------
  curl_easy_setopt(curl_handle, CURLOPT_USERNAME, UL_FTP_USR);
  curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, UL_FTP_PWD);
  //--------------------------------------
  
  // tell it to &quot;upload&quot; to the URL
  curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L);
  
  // bogus pointer. We don't read file, we generate data in read callback on the fly
  int p = 0;
  curl_easy_setopt(curl_handle, CURLOPT_READDATA, &amp;p);

  // register read callback
  curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, read_callback_full);  
  
  // set the file size
  curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, UL_SIZE);

  // some servers don't like requests that are made without a user-agent field, so we provide one
  curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, &quot;libcurl-speedchecker&quot;); // useful here ???

  // execute the request!
  res = curl_easy_perform(curl_handle);

  //if(CURLE_OK == res) {
    double val;

    // check for bytes downloaded
    res = curl_easy_getinfo(curl_handle, CURLINFO_SIZE_UPLOAD, &amp;val);
    if((CURLE_OK == res) &amp;&amp; (val&gt;0))
      printf(&quot;Data uploaded: %0.0f bytes.\n&quot;, val);
      
    // check for average upload speed
    res = curl_easy_getinfo(curl_handle, CURLINFO_SPEED_UPLOAD, &amp;val);
    if((CURLE_OK == res) &amp;&amp; (val&gt;0))
      printf(&quot;Average upload speed: %0.3f kbyte/sec.\n&quot;, val / 1024);

    // check for name resolution time 
    res = curl_easy_getinfo(curl_handle, CURLINFO_NAMELOOKUP_TIME, &amp;val);
    if((CURLE_OK == res) &amp;&amp; (val&gt;0))
      printf(&quot;Name lookup time: %0.3f sec.\n&quot;, val);
      
    // check for total time
    res = curl_easy_getinfo(curl_handle, CURLINFO_TOTAL_TIME, &amp;val);
    if((CURLE_OK == res) &amp;&amp; (val&gt;0))
      printf(&quot;Total time: %0.3f sec.\n&quot;, val);
      
  //}
  //else {
    fprintf(stderr, &quot;Error while fetching '%s' : %s\n&quot;, url, curl_easy_strerror(res));
  //}

  // cleanup curl stuff
  curl_easy_cleanup(curl_handle);

  // we're done with libcurl, so clean it up
  curl_global_cleanup();
  
  /*
  curl_version_info_data *data = curl_version_info(CURLVERSION_NOW); 
  printf(&quot;Version: %s\n&quot;, data-&gt;version);
  printf(&quot;Host: %s\n&quot;, data-&gt;host);
  printf(&quot;SSL-Version: %s\n&quot;, data-&gt;ssl_version);
  printf(&quot;Libz-Version: %s\n&quot;, data-&gt;libz_version);
  */

  return 0;
}
</code></pre>
<p>When I run this I get this output:</p>
<blockquote>
<p>ul_total_bytes = 1000000</p>
<p>ul_total_bytes = 1000000</p>
<p>Error while fetching 'ftp:// xxx.xxx.xxx.xxx/curl_upload_test' : Transferred a partial file</p>
</blockquote>
<p>But if I comment out the <code>if(CURLE_OK == res)</code> then I get this:</p>
<blockquote>
<p>ul_total_bytes = 1000000</p>
<p>ul_total_bytes = 1000000</p>
<p>Data uploaded: 999999 bytes.</p>
<p>Average upload speed: 386.591 kbyte/sec.</p>
<p>Name lookup time: 0.000 sec.</p>
<p>Total time: 2.526 sec.</p>
<p>Error while fetching 'ftp:// xxx.xxx.xxx.xxx/curl_upload_test' : No error</p>
</blockquote>
<p>And indeed, the file I find on my <code>ftp</code> is 999.999 bytes...</p>
<p>Here is version info:</p>
<blockquote>
<p>Version: 7.21.1</p>
<p>Host: i586-common_3_0-linux-gnu</p>
<p>SSL-Version: OpenSSL/1.0.0a</p>
<p>Libz-Version: 1.2.3</p>
</blockquote>
<p>The code has to run on an embedded system so I'm pretty much stuck with that version of <code>libcurl</code> which is supplied by the manufacturer.</p>