C++ LibCurl retry on error
Asked Answered
B

1

8

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);
    }
Baynebridge answered 7/5, 2017 at 10:15 Comment(9)
Just call curl_easy_perform in a loop, as many times as you want. What specifically seems to be the problem?Niigata
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?Baynebridge
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.Niigata
Alright, thanks for your answerBaynebridge
Igor: me thinks you should turn that into an answer instead...Presidium
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?Moreville
@RichardSand, once outside the loop is enoughBaynebridge
Perfect, thank you. I'll upvote if you post this as the solutionMoreville
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.Dichogamy
G
1

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();
Gabby answered 27/1, 2022 at 5:50 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.