7

I want to retry a curl connection in my C++ program for 5 times. When it fails 5 times in a row, it should stop the execution of the program. However, it stops after the first error at this point. I am able to catch the error, however I don't know how to execute the previous curl connection. E.g., with jQuery I can use something like $.ajax(this);. For LibCurl in C++ I am looking for a similar solution.

My current LibCurl code is shown below, note that I use multiple curl connections which all have other settings, therefore I would like a general approach which I can use for all my LibCurl errors within my LibcurlError function which is also included below.

curl = curl_easy_init();
if (curl) {
    CurlResponse = "";
    host = "http://google.com";
    LibcurlHeaders = curl_slist_append(NULL, "Expect:");
    if (ProxyAddress.length() > 0) {
        curl_easy_setopt(curl, CURLOPT_PROXY, ProxyAddress.c_str());
        }
    curl_easy_setopt(curl, CURLOPT_URL, (host).c_str());
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER , 1);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST , 1);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, LibcurlHeaders);
    res = curl_easy_perform(curl);
    curl_slist_free_all(LibcurlHeaders);
    if (res != CURLE_OK) {


        //AT THIS POINT I WOULD LIKE TO RETRY FOR 5 TIMES WHICH I WOULD LIKE TO CATCH IN MY LibcurlError FUNCTION.


        LibcurlError(curl_easy_strerror(res), host);
        }
    curl_easy_cleanup(curl);
    }
curl_global_cleanup();


void LibcurlError(string error, string host) {
    //IF FAILED FOR LESS THEN 5 TIMES IN A ROW -> RETRY CURL
    //ELSE I WOULD LIKE TO EXECUTE MY ORIGINAL CODE WHICH IS STATED BELOW 

    Message = "LibCurl Error: ";
    if (error == "Couldn't resolve host name") {
        Message.append("Couldn't connect to the server of ");
        if (host.find("google.com") != string::npos) {
            Message.append("Google");
            }
        else {
            Message.append("'" + host + "'");
            }
        }
    else {
        Message.append("'" + error + "'");
        }
    cout << Message << endl;
    system("pause");
    exit(0);
    }
TVA van Hesteren
  • 1,031
  • 3
  • 20
  • 47
  • 3
    Just call `curl_easy_perform` in a loop, as many times as you want. What specifically seems to be the problem? – Igor Tandetnik May 07 '17 at 13:52
  • Will this keep the set headers etc? Since I clean the headers list before catching the error? Perhaps I should do so when the call executed successfully. So the headers etc will be send again like in the failed attempt? – TVA van Hesteren May 07 '17 at 14:19
  • 1
    Don't clean the headers before catching the error. You must, of course, keep `LibcurlHeaders` alive for as long as you plan to use it. – Igor Tandetnik May 07 '17 at 14:28
  • Alright, thanks for your answer – TVA van Hesteren May 07 '17 at 14:41
  • 1
    Igor: me thinks you should turn that into an answer instead... – Daniel Stenberg May 08 '17 at 07:44
  • 1
    Should the setopts and curl_easy_cleanup be inside the loop (e.g. done each iteration of the loop) or can the init be done once and clean up once a successful call has been made? – Richard Sand Oct 18 '17 at 16:50
  • 1
    @RichardSand, once outside the loop is enough – TVA van Hesteren Oct 18 '17 at 17:58
  • Perfect, thank you. I'll upvote if you post this as the solution – Richard Sand Oct 18 '17 at 17:59
  • I'm in the same situation, I need to repeat the curl_easy_perform function call in case of an HTTP error. I will use the same solution but the annoying thing is that the `CURL` has a retry mechanism, the command arguments `--retry`, `--retry-delay`. – gorn Jun 11 '21 at 14:56

1 Answers1

0

There is no CURL method that specifically does this because it can be accomplished by repeated calls to curl_easy_perform.

Here is how you would write the code in your question (the relevant part at least) using loops to retry the CURL request repeatedly:

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

/*
 * This is the maximum number of times CURL will run
 */
const int max_attempts = 5;

curl = curl_easy_init();
if (curl) {
    CurlResponse = "";
    host = "http://google.com";
    LibcurlHeaders = curl_slist_append(NULL, "Expect:");
    if (ProxyAddress.length() > 0) {
        curl_easy_setopt(curl, CURLOPT_PROXY, ProxyAddress.c_str());
        }
    curl_easy_setopt(curl, CURLOPT_URL, (host).c_str());
    curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER , 1);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST , 1);
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, LibcurlHeaders);
    for (int i = 1; i <= max_attempts &&
        (res = curl_easy_perform(curl)) != CURLE_OK; i++) {
         /*
          * At this point, you would sleep
          * for some seconds between requests
          */
          const int sleep_secs = 1;
          sleep(sleep_secs);
     }
    // As others have mentioned, you should delete this line:
    //curl_slist_free_all(LibcurlHeaders);

    if (res != CURLE_OK) {
        // The max retries have all failed
        LibcurlError(curl_easy_strerror(res), host);
    }
    else {
        // The request has succeeded in the first `max_retries` attempts
        // ...
    }
    curl_easy_cleanup(curl);
}
curl_global_cleanup();
Zenul_Abidin
  • 573
  • 8
  • 23