From 54aeea54ca1705d75cf6895edc2fb6aea146fa38 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sun, 30 Jun 2024 19:33:12 +0200 Subject: [PATCH] Alternative implementation of solution for issue #1756 (allow specific HTTP timeout) --- src/tl/tl/tlHttpStream.cc | 19 +++++++++++++ src/tl/tl/tlHttpStream.h | 5 ++++ src/tl/tl/tlHttpStreamCurl.cc | 11 ++++++-- src/tl/tl/tlHttpStreamQt.cc | 5 ++-- src/tl/unit_tests/tlHttpStreamTests.cc | 38 ++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 4 deletions(-) diff --git a/src/tl/tl/tlHttpStream.cc b/src/tl/tl/tlHttpStream.cc index d02b437e3..cf9c90c55 100644 --- a/src/tl/tl/tlHttpStream.cc +++ b/src/tl/tl/tlHttpStream.cc @@ -22,6 +22,7 @@ #include "tlHttpStream.h" +#include "tlEnv.h" namespace tl { @@ -52,4 +53,22 @@ HttpErrorException::format_error (const std::string &em, int ec, const std::stri return msg; } +// --------------------------------------------------------------- +// Common implementation + +double +InputHttpStream::get_default_timeout () +{ + // default timeout + double timeout_value = 10.0; + + std::string timeout = tl::get_env ("KLAYOUT_HTTP_TIMEOUT"); + if (! timeout.empty ()) { + tl::Extractor ex (timeout.c_str ()); + ex.try_read (timeout_value); + } + + return timeout_value; +} + } diff --git a/src/tl/tl/tlHttpStream.h b/src/tl/tl/tlHttpStream.h index 529e81d85..e3f58ce29 100644 --- a/src/tl/tl/tlHttpStream.h +++ b/src/tl/tl/tlHttpStream.h @@ -98,6 +98,11 @@ public: */ virtual ~InputHttpStream (); + /** + * @brief Gets the timeout value (in seconds) + */ + static double get_default_timeout (); + /** * @brief Sets the credential provider */ diff --git a/src/tl/tl/tlHttpStreamCurl.cc b/src/tl/tl/tlHttpStreamCurl.cc index b797487f4..dc1751e9a 100644 --- a/src/tl/tl/tlHttpStreamCurl.cc +++ b/src/tl/tl/tlHttpStreamCurl.cc @@ -33,6 +33,7 @@ #include "tlProgress.h" #include "tlFileUtils.h" #include "tlUri.h" +#include "tlString.h" #if !defined(_MSC_VER) # include @@ -1282,7 +1283,7 @@ void CurlNetworkManager::tick () // InputHttpStreamPrivateData implementation InputHttpStreamPrivateData::InputHttpStreamPrivateData (InputHttpStream *stream, const std::string &url) - : m_timeout (10.0), mp_stream (stream) + : m_timeout (InputHttpStream::get_default_timeout ()), mp_stream (stream) { m_sent = false; m_ready = false; @@ -1391,7 +1392,13 @@ InputHttpStreamPrivateData::read (char *b, size_t n) } tl::Clock start_time = tl::Clock::current (); - while (n > m_connection->read_available () && ! m_connection->finished () && (m_timeout <= 0.0 || (tl::Clock::current() - start_time).seconds () < m_timeout) && ! tl::CurlNetworkManager::instance ()->has_reply ()) { + while (n > m_connection->read_available () && ! m_connection->finished () && ! tl::CurlNetworkManager::instance ()->has_reply ()) { + + // Check for timeout + if (m_timeout > 0.0 && (tl::Clock::current() - start_time).seconds () >= m_timeout) { + throw tl::HttpErrorException (tl::sprintf (tl::to_string (tr ("Connection timed out (timeout is %.1fs)")), m_timeout), 0, m_connection->url ()); + } + mp_stream->tick (); if (m_progress.get ()) { // might have been reset by tick() ++*m_progress; diff --git a/src/tl/tl/tlHttpStreamQt.cc b/src/tl/tl/tlHttpStreamQt.cc index c8c03a572..696f38992 100644 --- a/src/tl/tl/tlHttpStreamQt.cc +++ b/src/tl/tl/tlHttpStreamQt.cc @@ -28,6 +28,7 @@ #include "tlObject.h" #include "tlTimer.h" #include "tlSleep.h" +#include "tlString.h" #include #include @@ -256,7 +257,7 @@ static QNetworkAccessManager *s_network_manager (0); static AuthenticationHandler *s_auth_handler (0); InputHttpStreamPrivateData::InputHttpStreamPrivateData (InputHttpStream *stream, const std::string &url) - : m_url (url), mp_reply (0), m_request ("GET"), mp_buffer (0), mp_resend_timer (new QTimer (this)), m_timeout (10.0), mp_stream (stream) + : m_url (url), mp_reply (0), m_request ("GET"), mp_buffer (0), mp_resend_timer (new QTimer (this)), m_timeout (InputHttpStream::get_default_timeout ()), mp_stream (stream) { if (! s_network_manager) { @@ -466,7 +467,7 @@ InputHttpStreamPrivateData::read (char *b, size_t n) // Reason for this may be HTTPS initialization failure (OpenSSL) - std::string em = tl::to_string (QObject::tr ("Request creation failed")); + std::string em = tl::sprintf (tl::to_string (QObject::tr ("Request creation failed (timeout is %.1fs)")), m_timeout); if (tl::verbosity() >= 30) { tl::info << "HTTP request creation failed"; } diff --git a/src/tl/unit_tests/tlHttpStreamTests.cc b/src/tl/unit_tests/tlHttpStreamTests.cc index 203ff8e1e..ba57d51f4 100644 --- a/src/tl/unit_tests/tlHttpStreamTests.cc +++ b/src/tl/unit_tests/tlHttpStreamTests.cc @@ -25,6 +25,7 @@ #include "tlUnitTest.h" #include "tlTimer.h" #include "tlStream.h" +#include "tlEnv.h" static std::string test_url1 ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata/text"); static std::string test_url1_gz ("http://www.klayout.org/svn-public/klayout-resources/trunk/testdata2/text.gz"); @@ -153,3 +154,40 @@ TEST(5) EXPECT_EQ (res, "hello, world.\n"); } +// tl::InputHttpStream timeout +TEST(6) +{ + if (! tl::InputHttpStream::is_available ()) { + throw tl::CancelException (); + } + + { + tl::set_env ("KLAYOUT_HTTP_TIMEOUT", ""); + tl::InputHttpStream stream (test_url1); + stream.set_timeout (0.001); // probably too fast :) + + try { + char b[100]; + stream.read (b, sizeof (b)); + EXPECT_EQ (true, false); + } catch (tl::HttpErrorException &ex) { + tl::info << "Got exception (expected): " << ex.msg (); + } + } + + { + tl::set_env ("KLAYOUT_HTTP_TIMEOUT", "0.001"); + tl::InputHttpStream stream (test_url1); + + try { + char b[100]; + stream.read (b, sizeof (b)); + EXPECT_EQ (true, false); + } catch (tl::HttpErrorException &ex) { + tl::info << "Got exception (expected): " << ex.msg (); + } + } + + tl::unset_env ("KLAYOUT_HTTP_TIMEOUT"); +} +