114

I want to store the result of this curl function in a variable, how can I do so?

#include <stdio.h>
#include <curl/curl.h>

int main(void)
{
  CURL *curl;
  CURLcode res;

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    res = curl_easy_perform(curl);

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

thanks, I solved it like this:

#include <stdio.h>
#include <stdlib.h>
#include <curl/curl.h>

function_pt(void *ptr, size_t size, size_t nmemb, void *stream){
    printf("%d", atoi(ptr));
}

int main(void)
{
  CURL *curl;
  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_pt);
    curl_easy_perform(curl);
    curl_easy_cleanup(curl);
  }
  system("pause");
  return 0;
}
frx08
  • 4,222
  • 8
  • 37
  • 45
  • 2
    Just to point out in your solution in function_pt() you are converting the string in ptr to integer in order to convert it back to string in the output. You can output the string directly (and see the full response). – zzz Jun 19 '15 at 03:33
  • 3
    here is a link to the cURL example http://curl.haxx.se/libcurl/c/getinmemory.html – lafferc Jul 30 '15 at 16:19
  • 1
    `CURLcode res;` is unused – fnc12 Sep 17 '16 at 11:14
  • 1
    same question but for C++ instead of c go to here: [Save cURL content result into a string in C++](https://stackoverflow.com/q/9786150/52074) – Trevor Boyd Smith Sep 04 '18 at 14:36

4 Answers4

136

You can set a callback function to receive incoming data chunks using curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, myfunc);

The callback will take a user defined argument that you can set using curl_easy_setopt(curl, CURLOPT_WRITEDATA, p)

Here's a snippet of code that passes a buffer struct string {*ptr; len} to the callback function and grows that buffer on each call using realloc().

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>

struct string {
  char *ptr;
  size_t len;
};

void init_string(struct string *s) {
  s->len = 0;
  s->ptr = malloc(s->len+1);
  if (s->ptr == NULL) {
    fprintf(stderr, "malloc() failed\n");
    exit(EXIT_FAILURE);
  }
  s->ptr[0] = '\0';
}

size_t writefunc(void *ptr, size_t size, size_t nmemb, struct string *s)
{
  size_t new_len = s->len + size*nmemb;
  s->ptr = realloc(s->ptr, new_len+1);
  if (s->ptr == NULL) {
    fprintf(stderr, "realloc() failed\n");
    exit(EXIT_FAILURE);
  }
  memcpy(s->ptr+s->len, ptr, size*nmemb);
  s->ptr[new_len] = '\0';
  s->len = new_len;

  return size*nmemb;
}

int main(void)
{
  CURL *curl;
  CURLcode res;

  curl = curl_easy_init();
  if(curl) {
    struct string s;
    init_string(&s);

    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
    res = curl_easy_perform(curl);

    printf("%s\n", s.ptr);
    free(s.ptr);

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}
Alex Jasmin
  • 39,094
  • 7
  • 77
  • 67
43

The following answer is the C++ way to do it, with std::string, instead of null-terminated string. It still uses a callback function (there's no way around it), but also handles allocation error using try/catch.

#include <iostream>
#include <string>
#include <curl/curl.h>

size_t CurlWrite_CallbackFunc_StdString(void *contents, size_t size, size_t nmemb, std::string *s)
{
    size_t newLength = size*nmemb;
    try
    {
        s->append((char*)contents, newLength);
    }
    catch(std::bad_alloc &e)
    {
        //handle memory problem
        return 0;
    }
    return newLength;
}
int main()
{
    CURL *curl;
    CURLcode res;

    curl_global_init(CURL_GLOBAL_DEFAULT);

    curl = curl_easy_init();
    std::string s;
    if(curl)
    {

        curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");

        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); //only for https
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); //only for https
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlWrite_CallbackFunc_StdString);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);
        curl_easy_setopt (curl, CURLOPT_VERBOSE, 1L); //remove this to disable verbose output


        /* Perform the request, res will get the return code */
        res = curl_easy_perform(curl);
        /* Check for errors */
        if(res != CURLE_OK)
        {
            fprintf(stderr, "curl_easy_perform() failed: %s\n",
                    curl_easy_strerror(res));
        }

        /* always cleanup */
        curl_easy_cleanup(curl);
    }

    std::cout<<s<<std::endl;

    std::cout<< "Program finished!" << std::endl;
}
The Quantum Physicist
  • 24,987
  • 19
  • 103
  • 189
  • I think std::string::append can make that callback function a lot simpler. – Ryan Burn Dec 05 '18 at 18:54
  • @rnickb You're right; `s->append((char*)contents. nmemb);` works flawlessly with me and is more concise. Also, the [official function signature](https://curl.haxx.se/libcurl/c/CURLOPT_WRITEFUNCTION.html) for the callback has a `char*` as first argument, so you could use that and omit the casting. And lastly, `s->resize()` actually initializes the newly allocated space. As you are about to overwrite it anyway, `s->reserve()` would be more efficient. – Jeinzi Jan 31 '19 at 14:53
  • This helped me a lot. Can you also give an example of how to do it with HTTP POST please :-) – Lord Wolfenstein Jan 08 '20 at 15:34
9

From reading the manual here: http://curl.haxx.se/libcurl/c/curl_easy_setopt.html I think you need several calls to CURL_SETOPT, the first being the URL you want to process, the second being something like:

curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, function_ptr);

Where function_ptr matches this signature:

size_t function( void *ptr, size_t size, size_t nmemb, void *stream)

What happens here is you denote a callback function which libcurl will call when it has some output to write from whatever transfer you've invoked. You can get it to automatically write to a file, or pass it a pointer to a function which will handle the output itself. Using this function you should be able to assemble the various output strings into one piece and then use them in your program.

I'm not sure what other options you may have to set / what else affects how you want your app to behave, so have a good look through that page.

6

Here's a C++ flavor of the accepted answer from alex-jasmin

#include <iostream>
#include <string>
#include <curl/curl.h>

size_t writefunc(void *ptr, size_t size, size_t nmemb, std::string *s) 
{
  s->append(static_cast<char *>(ptr), size*nmemb);
  return size*nmemb;
}

int main(void)
{
  CURL *curl = curl_easy_init();
  if (curl)
  {
    std::string s;

    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writefunc);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &s);

    CURLcode res = curl_easy_perform(curl);

    std::cout << s << std::endl;

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

Paul Grinberg
  • 1,184
  • 14
  • 37