title: c libcurl ftp upload incomplete - 1 missing byte
body: <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>