Wednesday, April 27, 2016

Curl command line works, C++ curl library does not

Leave a Comment

I'm trying to do some request via curl library of C++. I can successfully do my request and get the correct response via command line, but I cannot get the correct response via C++ code. My command line command looks like this

curl -X POST -H 'Accept: application/json' -H 'Content-Type: application/json' -H 'Authorization: <some_hash_value>' -k <my_full_url> -data '<my_json_string>' 

That works fine. Now I try to do the same request in C++ code. My code looks like this

void performRequest(const std::string& json, const void* userData, CallbackFunction callback) {     struct curl_slist* headers = NULL;      headers = curl_slist_append(headers, "Accept: application/json");     headers = curl_slist_append(headers, "Content-Type: application/json");     headers = curl_slist_append(headers, (std::string("Authorization: ") + m_authorization).c_str());      CURL* curlHandle = curl_easy_init();     if (!curlHandle)     {         std::cerr << "Curl handler initialization failed";     }      curl_easy_setopt(curlHandle, CURLOPT_NOSIGNAL, 1);     curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, headers);      // specify target URL, and note that this URL should include a file name, not only a directory      curl_easy_setopt(curlHandle, CURLOPT_URL, m_url.c_str());      // enable uploading     curl_easy_setopt(curlHandle, CURLOPT_UPLOAD, 1L);      // set HTTP method to POST     curl_easy_setopt(curlHandle, CURLOPT_CUSTOMREQUEST, "POST");      // set json data; I use EXACTLY the same string as in command line     curl_easy_setopt(curlHandle, CURLOPT_COPYPOSTFIELDS, json.c_str());      // set data size     curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDSIZE_LARGE, json.size());      // set user data for getting it in response     curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, userData);    // pointer to a custom struct      // set callback function for getting response     curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, callback);    // some callback      // send request     curl_easy_perform(curlHandle);      curl_easy_cleanup(curlHandle);     curl_slist_free_all(headers); } 

However, for some reason I get an error in the response from the server, from which I can assume that my code's request is not equivalent to command line's command. It seems that body is not sent. I cannot see my request Json body when I use CURLOPT_DEBUGFUNCTION for dumping debug info.

What is the problem here? What am I doing wrong? Any ideas?

3 Answers

Answers 1

Here is sample code that should work for you.

Notice that I:

  • Removed CURLOPT_UPLOAD as it does not seem as you are actually uploading something but rather just doing a simple POST.

  • Changed CURLOPT_CUSTOMREQUEST to CURLOPT_POST (not that it should matter), but I find it cleaner.

  • Reordered CURLOPT_POSTFIELDSIZE_LARGE and CURLOPT_COPYPOSTFIELDS

  • Removed the CURLOPT_WRITEDATA line for the sake of this sample code.

I have tested the following only by connecting to an instance of nc -l localhost 80

 static size_t callback(char *ptr, size_t size, size_t nmemb, void *userdata)  {    string s(ptr);    cout << s << endl;    return size * nmemb;  }    int main(int argc, char** argv)  {    string m_authorization("PWNED");    string m_url("http://localhost");    string m_json("{}");     curl_global_init(CURL_GLOBAL_ALL);     CURL* curlHandle = curl_easy_init();     struct curl_slist* headers = nullptr;    headers = curl_slist_append(headers, "Accept: application/json");    headers = curl_slist_append(headers, "Content-Type: application/json");    headers = curl_slist_append(headers, (std::string("Authorization: ") + m_authorization).c_str());     curl_easy_setopt(curlHandle, CURLOPT_NOSIGNAL, 1);    curl_easy_setopt(curlHandle, CURLOPT_HTTPHEADER, headers);     // specify target URL, and note that this URL should include a file name, not only a directory    curl_easy_setopt(curlHandle, CURLOPT_URL, m_url.c_str());     // <= You are not uploading anything actually, this is a simple POST with payload    // enable uploading    // curl_easy_setopt(curlHandle, CURLOPT_UPLOAD, 1L);     // set HTTP method to POST    curl_easy_setopt(curlHandle, CURLOPT_POST, 1L);     // set data size before copy    curl_easy_setopt(curlHandle, CURLOPT_POSTFIELDSIZE_LARGE, m_json.size());     // set json data; I use EXACTLY the same string as in command line    curl_easy_setopt(curlHandle, CURLOPT_COPYPOSTFIELDS, m_json.c_str());     // set user data for getting it in response    // curl_easy_setopt(curlHandle, CURLOPT_WRITEDATA, userData);    // pointer to a custom struct     // set callback function for getting response    curl_easy_setopt(curlHandle, CURLOPT_WRITEFUNCTION, callback);    // some callback     // send request    curl_easy_perform(curlHandle);     curl_slist_free_all(headers);     curl_easy_cleanup(curlHandle);    curl_global_cleanup();    return 0;  } 

Answers 2

In windows, you must init the winsock stuff with below function

curl_global_init(CURL_GLOBAL_ALL); 

Answers 3

The problem was solved tanks to Rocki's and drew010's comments.

  1. I have removed CURLOPT_CUSTOMREQUEST, CURLOPT_UPLOAD and CURLOPT_NOSIGNAL setting statements as there is no need of them.
  2. I have also removed the line for setting CURLOPT_POSTFIELDSIZE_LARGE, although it works fine if it is set before setting CURLOPT_COPYPOSTFIELDS. If the size has not been set prior to CURLOPT_COPYPOSTFIELDS, the data is assumed to be a zero terminated string; else the stored size informs the library about the byte count to copy. In any case, the size must not be changed after CURLOPT_COPYPOSTFIELDS, unless another CURLOPT_POSTFIELDS or CURLOPT_COPYPOSTFIELDS option is issued. (See: curl.haxx.se/libcurl/c/CURLOPT_COPYPOSTFIELDS.html)
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment