We need a better way to handle http request timeouts
dmikushin opened this issue · comments
Dear TgBot authors, greetings,
Here is a feedback from using TgBot in a production workload. Once in every few hours I get timeout of the http resuest, like this:
Error: curl error: Operation timed out after 25001 milliseconds with 0 bytes received
In the present code design, everything what is not an OK status throws an exception:
if (res != CURLE_OK) {
throw std::runtime_error(std::string("curl error: ") + curl_easy_strerror(res));
}
I'm not sure whether the timeout is caused by network oddities, or by the Telegram server behavior. In any case, timeout should be an expected event. For this reason, throwing an exception is this case seems to be a doubtful design. In order to handle the timeout, one needs to derive another HttpRequest class and silence the exception, for example like this:
// Derive our own http client to handle the exceptions
// produced inside the library.
class MyHttpClient : public TgBot::CurlHttpClient
{
std::string makeRequest(const TgBot::Url& url, const std::vector<TgBot::HttpReqArg>& args) const override
{
// Repeat, until the request is successful.
while (1)
{
try
{
return TgBot::CurlHttpClient::makeRequest(url, args);
}
catch (std::runtime_error& e)
{
fprintf(stderr, "Error: %s\n", e.what());
}
// Wait a moment for the potential network issue to get resolved,
// and try again.
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
};
static auto& getHttpClient()
{
static MyHttpClient instance;
return instance;
}
...
TgBot::Bot bot(token, getHttpClient());
I don't have any alternative suggestion, atm. Could you please share your thoughts and experiences? Thanks!
Just now, 3 variations spontaneously come to my mind. The first would be to make your own derived class and handle the error that way. Like you did it.
Or you could automatically ignore certain errors directly in the library so that they are never thrown. But then it could be that someone would like to handle this error instead of ignoring it.
Or the user can use some function to specify which errors should (not) be thrown or how extensively errors should be thrown.
Now the question is, what makes the most sense or if there are other better options?