0

I want to make HTTPS PUT request to put a csv file. Below is the code used to upload data to Xively. Earlier I was getting 411 length required error. I referred to the code available here (Send string in PUT request with libcurl) to resolve this, where CURLOPT_CUSTOMREQUEST is made. Now I am getting HTTP 500 Internal Server error.

void upload()
{
   CURL *curl;
   CURLcode res;
   FILE * hd_src;
   struct stat file_info;
   struct curl_slist* header  = NULL;
   char * csvfile = "123.csv";

   /* get the file size */
   stat(csvfile, &file_info);

   hd_src = fopen("123.csv","rb");

   curl_global_init(CURL_GLOBAL_ALL);

   curl = curl_easy_init();

   if(curl)
   {

        header  = curl_slist_append(header,"X-ApiKey: 123123123"); /* API KEY HERE - sample only*/
        header = curl_slist_append(header,"Accept: text/csv");
        header = curl_slist_append(header, "Host: api.xively.com"); 

        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

        /* Actual Xively feed  is here. For demonstration purpose the feed is listed as 123 */
        curl_easy_setopt(curl, CURLOPT_URL, "https://api.xively.com/v2/feeds/123.csv");

        curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);

        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT" );
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, hd_src);

        res = curl_easy_perform(curl); 

        if(res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
        }
        curl_easy_cleanup(curl);        
   }
   fclose(hd_src);   
   curl_slist_free_all(header);
   curl_global_cleanup();
}

Can anyone make suggestions? If I remove curl_easy_setopt(curl, CURLOPT_POSTFIELDS, hd_src), I'll get 411 length required error. I removed this and added Content-Length to header using file_info.st_size. Again 411 length required error is received. With curl_easy_setopt(curl, CURLOPT_POSTFIELDS, hd_src) 411 is solved but Xively gives 500 internal server error.

Thanks in advance.

Community
  • 1
  • 1

1 Answers1

1

Try using CURLOPT_UPLOAD instead, like the documentation says to:

CURLOPT_PUT - make a HTTP PUT request
...
This option is deprecated since version 7.12.1. Use CURLOPT_UPLOAD!

CURLOPT_UPLOAD - enable data upload
...
If the protocol is HTTP, uploading means using the PUT request unless you tell libcurl otherwise

You have to be very careful with CURLOPT_CUSTOMREQUEST:

CURLOPT_CUSTOMREQUEST - custom string for request
...
When you change the request method by setting CURLOPT_CUSTOMREQUEST to something, you don't actually change how libcurl behaves or acts in regards to the particular request method, it will only change the actual string sent in the request
...
Many people have wrongly used this option to replace the entire request with their own, including multiple headers and POST contents. While that might work in many cases, it will cause libcurl to send invalid requests and it could possibly confuse the remote server badly.

You are also not sending a Content-Type header, you are sending an Accept header instead. Content-Type tells the server the type of data you are sending. Accept tells the server what type(s) of data you are willing to receive in reply.

And lastly, you are not doing error handling on stat() or fopen().

Try this instead:

void upload()
{
    CURL *curl;
    CURLcode res;
    FILE * hd_src;
    struct stat file_info = {0};
    struct curl_slist* header = NULL;
    char * csvfile = "C:\\full path to\\123.csv";

    /* get the file size */
    if (stat(csvfile, &file_info) == -1)
    {
        fprintf(stderr, "stat() failed: %s\n", strerror(errno));
        return;
    }

    hd_src = fopen(csvfile, "rb");
    if (!hd_src)
    {
        fprintf(stderr, "fopen() failed: %s\n", strerror(errno));
        return;
    }  

    curl_global_init(CURL_GLOBAL_ALL);

    curl = curl_easy_init();
    if (!curl)
    {
        fprintf(stderr, "curl_easy_init() failed\n");
    }
    else
    {
        header = curl_slist_append(header, "X-ApiKey: 123123123"); /* API KEY HERE - sample only*/
        header = curl_slist_append(header, "Content-Type: text/csv");

        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);

        /* Actual Xively feed  is here. For demonstration purpose the feed is listed as 123 */
        curl_easy_setopt(curl, CURLOPT_URL, "https://api.xively.com/v2/feeds/123.csv");

        curl_easy_setopt(curl, CURLOPT_READDATA, hd_src);
        curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);

        curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L );

        res = curl_easy_perform(curl); 
        if (res != CURLE_OK)
            fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));

        curl_slist_free_all(header);
        curl_easy_cleanup(curl);        
    }

    fclose(hd_src);   

    curl_global_cleanup();
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • @ Remy, it worked - thank you. However, now I am getting 413 Request Entity Too Large. I am using Linux machine. If you have any suggestions please let me know. Thank you. – Aravinda Oct 14 '14 at 07:03
  • How large is the file that you are trying to upload? – Remy Lebeau Oct 14 '14 at 16:01
  • @ Remy, it has just a single line. I tried 'ls -l' and the system is showing 10 bytes. – Aravinda Oct 15 '14 at 00:49
  • The only thing I can think of is if `stat()` is failing, leaving an invalid value in `file_info.st_size` which you send as-is to the server. You are not checking if either `stat()` or `fopen()` are failing. For instance, you are using a relative file path. Try using an absolute path instead. I updated my answer to show that. – Remy Lebeau Oct 15 '14 at 01:21
  • @ Remy, I've been using absolute path. In the code above it has not been mentioned. There is no error in reading the file. I used your modified code. Same error is repeating. It seems like when I use curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); the content length appears to be arbitrarily big although the file is small. I tried by disabling CURLOPT_INFILESIZE_LARGE and adding content length string to header from the stat() file information and is matching with the file size. Then 411 error is thrown. Any suggestions?? Thank you. – Aravinda Oct 16 '14 at 01:01
  • If you are sending a valid `Content-Length` and the server is still reporting 411 then either the server is buggy, or your request is passing through a proxy that is corrupting the request. Please show the actual HTTP request that libcurl is sending. – Remy Lebeau Oct 16 '14 at 02:53