diff --git a/include/libtorrent/http_connection.hpp b/include/libtorrent/http_connection.hpp index 92ae5dad3..bdc4b14f2 100644 --- a/include/libtorrent/http_connection.hpp +++ b/include/libtorrent/http_connection.hpp @@ -66,10 +66,19 @@ struct http_connection : boost::enable_shared_from_this, boost: , m_last_receive(boost::posix_time::second_clock::universal_time()) , m_bottled(bottled) , m_called(false) + , m_rate_limit(0) + , m_download_quota(0) + , m_limiter_timer_active(false) + , m_limiter_timer(ios) { assert(!m_handler.empty()); } + void rate_limit(int limit); + + int rate_limit() const + { return m_rate_limit; } + std::string sendbuffer; void get(std::string const& url, boost::posix_time::time_duration timeout @@ -89,6 +98,7 @@ private: void on_read(asio::error_code const& e, std::size_t bytes_transferred); static void on_timeout(boost::weak_ptr p , asio::error_code const& e); + void on_assign_bandwidth(asio::error_code const& e); std::vector m_recvbuffer; tcp::socket m_sock; @@ -108,6 +118,22 @@ private: bool m_called; std::string m_hostname; std::string m_port; + + // the current download limit, in bytes per second + // 0 is unlimited. + int m_rate_limit; + + // the number of bytes we are allowed to receive + int m_download_quota; + + // only hand out new quota 4 times a second if the + // quota is 0. If it isn't 0 wait for it to reach + // 0 and continue to hand out quota at that time. + bool m_limiter_timer_active; + + // the timer fires every 250 millisecond as long + // as all the quota was used. + deadline_timer m_limiter_timer; }; } diff --git a/src/bandwidth_manager.cpp b/src/bandwidth_manager.cpp index 7995fab13..f4b73df68 100644 --- a/src/bandwidth_manager.cpp +++ b/src/bandwidth_manager.cpp @@ -242,3 +242,4 @@ namespace libtorrent { assert(false); }; } + diff --git a/src/http_connection.cpp b/src/http_connection.cpp index 42c09d518..3fad5fbad 100644 --- a/src/http_connection.cpp +++ b/src/http_connection.cpp @@ -31,9 +31,13 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "http_connection.hpp" +#include +#include #include using boost::posix_time::second_clock; +using boost::posix_time::millisec; +using boost::bind; namespace libtorrent { @@ -160,14 +164,33 @@ void http_connection::on_write(asio::error_code const& e) std::string().swap(sendbuffer); m_recvbuffer.resize(4096); + + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (m_rate_limit > 0 && amount_to_read > m_download_quota) + { + amount_to_read = m_download_quota; + if (m_download_quota == 0) + { + if (!m_limiter_timer_active) + on_assign_bandwidth(asio::error_code()); + return; + } + } m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos - , m_recvbuffer.size() - m_read_pos) - , bind(&http_connection::on_read, shared_from_this(), _1, _2)); + , amount_to_read) + , bind(&http_connection::on_read + , shared_from_this(), _1, _2)); } void http_connection::on_read(asio::error_code const& e , std::size_t bytes_transferred) { + if (m_rate_limit) + { + m_download_quota -= bytes_transferred; + assert(m_download_quota >= 0); + } + if (e == asio::error::eof) { close(); @@ -228,11 +251,58 @@ void http_connection::on_read(asio::error_code const& e m_handler(asio::error::eof, m_parser, 0, 0); return; } + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (m_rate_limit > 0 && amount_to_read > m_download_quota) + { + amount_to_read = m_download_quota; + if (m_download_quota == 0) + { + if (!m_limiter_timer_active) + on_assign_bandwidth(asio::error_code()); + return; + } + } m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos - , m_recvbuffer.size() - m_read_pos) + , amount_to_read) , bind(&http_connection::on_read , shared_from_this(), _1, _2)); } +void http_connection::on_assign_bandwidth(asio::error_code const& e) +{ + m_limiter_timer_active = false; + if (e) return; + + if (m_download_quota > 0) return; + + m_download_quota = m_rate_limit / 4; + + int amount_to_read = m_recvbuffer.size() - m_read_pos; + if (amount_to_read > m_download_quota) + amount_to_read = m_download_quota; + + m_sock.async_read_some(asio::buffer(&m_recvbuffer[0] + m_read_pos + , amount_to_read) + , bind(&http_connection::on_read + , shared_from_this(), _1, _2)); + + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(millisec(250)); + m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); +} + +void http_connection::rate_limit(int limit) +{ + if (!m_limiter_timer_active) + { + m_limiter_timer_active = true; + m_limiter_timer.expires_from_now(millisec(250)); + m_limiter_timer.async_wait(bind(&http_connection::on_assign_bandwidth + , shared_from_this(), _1)); + } + m_rate_limit = limit; +} + }