From e5d3755afbdd4f0bae23785335ccb1ee6c2eb811 Mon Sep 17 00:00:00 2001 From: Arvid Norberg Date: Tue, 30 Dec 2008 03:54:07 +0000 Subject: [PATCH] added support for http seeds (BEP 17) --- CMakeLists.txt | 1 + ChangeLog | 1 + Jamfile | 1 + docs/features.rst | 13 +- docs/makefile | 2 +- docs/manual.rst | 49 +- include/libtorrent/http_seed_connection.hpp | 171 +++++++ include/libtorrent/peer_info.hpp | 3 +- include/libtorrent/torrent.hpp | 44 +- include/libtorrent/torrent_handle.hpp | 4 + include/libtorrent/torrent_info.hpp | 6 + src/Makefile.am | 6 +- src/http_seed_connection.cpp | 477 ++++++++++++++++++++ src/torrent.cpp | 145 ++++-- src/torrent_handle.cpp | 25 +- src/torrent_info.cpp | 16 + src/web_peer_connection.cpp | 17 +- 17 files changed, 890 insertions(+), 91 deletions(-) create mode 100644 include/libtorrent/http_seed_connection.hpp create mode 100644 src/http_seed_connection.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 07292e174..2dae0b6b0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,6 +22,7 @@ set(sources peer_connection bt_peer_connection web_peer_connection + http_seed_connection instantiate_connection natpmp piece_picker diff --git a/ChangeLog b/ChangeLog index 165dc5013..a6e1af96c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,4 @@ + * added support for BEP 17 http seeds * added read_piece() to read pieces from torrent storage * added option for udp tracker preference * added super seeding diff --git a/Jamfile b/Jamfile index 69d5d4b9e..6b6449649 100755 --- a/Jamfile +++ b/Jamfile @@ -321,6 +321,7 @@ SOURCES = peer_connection bt_peer_connection web_peer_connection + http_seed_connection instantiate_connection natpmp piece_picker diff --git a/docs/features.rst b/docs/features.rst index 3ab067102..20e95c7ee 100644 --- a/docs/features.rst +++ b/docs/features.rst @@ -41,8 +41,8 @@ following features: * supports local peer discovery (multicasts for peers on the same local network) * adjusts the length of the request queue depending on download rate. * has an adjustable read and write disk cache for improved disk throughput. -* multitracker extension support (supports both the `specification by John Hoffman`__ - and the uTorrent interpretation). +* multitracker extension support (supports both strict `BEP 12`_ and the + uTorrent interpretation). * tracker scrapes * supports both sparse files and compact file allocation (where pieces are kept consolidated on disk) @@ -51,12 +51,12 @@ following features: * fast resume support, a way to get rid of the costly piece check at the start of a resumed torrent. Saves the storage state, piece_picker state as well as all local peers in a separate fast-resume file. -* `HTTP seeding`_, as `specified by Michael Burford of GetRight`__. +* `HTTP seeding`_, as specified in `BEP 17`_ and `BEP 19`_. * piece picking on block-level (as opposed to piece-level). This means it can download parts of the same piece from different peers. It will also prefer to download whole pieces from single peers if the download speed is high enough from that particular peer. -* supports the `udp-tracker protocol`_ by Olaf van der Spek. +* supports `BEP 15`_ the udp-tracker protocol. * queues torrents for file check, instead of checking all of them in parallel. * supports http proxies and basic proxy authentication * gzipped tracker-responses @@ -80,7 +80,10 @@ following features: .. _`DHT extensions`: dht_extensions.html __ http://home.elp.rr.com/tur/multitracker-spec.txt -__ http://www.getright.com/seedtorrent.html +.. _`BEP 12`: http://bittorrent.org/beps/bep_0012.html +.. _`BEP 15`: http://bittorrent.org/beps/bep_0015.html +.. _`BEP 17`: http://bittorrent.org/beps/bep_0017.html +.. _`BEP 19`: http://bittorrent.org/beps/bep_0019.html .. _`extension protocol`: extension_protocol.html .. _`udp-tracker protocol`: udp_tracker_protocol.html diff --git a/docs/makefile b/docs/makefile index 4c97a8de9..879e8c46e 100644 --- a/docs/makefile +++ b/docs/makefile @@ -1,7 +1,7 @@ # this makefile assumes that you have docutils and rst2pdf installed WEB_PATH = ~/Documents/rasterbar/web/products/libtorrent -DOCUTILS = ~/docutils +DOCUTILS = ~/docutils-0.5 TARGETS = index \ udp_tracker_protocol \ diff --git a/docs/manual.rst b/docs/manual.rst index 9e72db7cd..fef0f0e78 100644 --- a/docs/manual.rst +++ b/docs/manual.rst @@ -1269,6 +1269,9 @@ The ``torrent_info`` has the following synopsis:: bool priv() const; std::vector const& url_seeds() const; + void add_url_seed(std::string const& url); + std::vector const& http_seeds() const; + void add_http_seed(std::string const& url); size_type total_size() const; int piece_length() const; @@ -1473,17 +1476,20 @@ The input range is assumed to be valid within the torrent. ``file_offset`` must refer to a valid file, i.e. it cannot be >= ``num_files()``. -url_seeds() add_url_seed() --------------------------- +url_seeds() add_url_seed() http_seeds() add_http_seed() +------------------------------------------------------- :: std::vector const& url_seeds() const; void add_url_seed(std::string const& url); + std::vector const& http_seeds() const; + void add_http_seed(std::string const& url); -If there are any url-seeds in this torrent, ``url_seeds()`` will return a -vector of those urls. If you're creating a torrent file, ``add_url_seed()`` -adds one url to the list of url-seeds. Currently, the only transport protocol +If there are any url-seeds or http seeds in this torrent, ``url_seeds()`` +and ``http_seeds()`` will return a vector of those urls. +``add_url_seed()`` and ``add_http_seed()`` adds one url to the list of +url/http seeds. Currently, the only transport protocol supported for the url is http. See `HTTP seeding`_ for more information. @@ -1687,6 +1693,10 @@ Its declaration looks like this:: void remove_url_seed(std::string const& url); std::set url_seeds() const; + void add_http_seed(std::string const& url); + void remove_http_seed(std::string const& url); + std::set http_seeds() const; + void set_ratio(float ratio) const; void set_max_uploads(int max_uploads) const; void set_max_connections(int max_connections) const; @@ -2225,6 +2235,19 @@ automatically from the list. See `HTTP seeding`_ for more information. +add_http_seed() remove_http_seed() http_seeds() +----------------------------------------------- + + :: + + void add_http_seed(std::string const& url); + void remove_http_seed(std::string const& url); + std::set http_seeds() const; + +These functions are identical as the ``*_url_seed()`` variants, but they +operate on BEP 17 web seeds instead of BEP 19. + +See `HTTP seeding`_ for more information. queue_position() queue_position_up() queue_position_down() queue_position_top() queue_position_bottom() ------------------------------------------------------------------------------------------------------- @@ -5514,17 +5537,23 @@ Don't have metadata: HTTP seeding ------------ -The HTTP seed extension implements `this specification`__. +There are two kinds of HTTP seeding. One with that assumes a smart +(and polite) client and one that assumes a smart server. These +are specified in `BEP 19`_ and `BEP 17`_ respectively. -The libtorrent implementation assumes that, if the URL ends with a slash +libtorrent supports both. In the libtorrent source code and API, +BEP 19 urls are typically referred to as *url seeds* and BEP 17 +urls are typically referred to as *HTTP seeds*. + +The libtorrent implementation of `BEP 19`_ assumes that, if the URL ends with a slash ('/'), the filename should be appended to it in order to request pieces from that file. The way this works is that if the torrent is a single-file torrent, only that filename is appended. If the torrent is a multi-file torrent, the torrent's name '/' the file name is appended. This is the same directory structure that libtorrent will download torrents into. -__ http://www.getright.com/seedtorrent.html - +.. _`BEP 17`: http://bittorrent.org/beps/bep_0017.html +.. _`BEP 19`: http://bittorrent.org/beps/bep_0019.html filename checks =============== @@ -5544,7 +5573,7 @@ __ http://www.boost.org/libs/filesystem/doc/index.htm acknowledgments =============== -Written by Arvid Norberg. Copyright |copy| 2003-2006 +Written by Arvid Norberg. Copyright |copy| 2003-2008 Contributions by Magnus Jonsson, Daniel Wallin and Cory Nelson diff --git a/include/libtorrent/http_seed_connection.hpp b/include/libtorrent/http_seed_connection.hpp new file mode 100644 index 000000000..0e2f80de0 --- /dev/null +++ b/include/libtorrent/http_seed_connection.hpp @@ -0,0 +1,171 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef TORRENT_HTTP_SEED_CONNECTION_HPP_INCLUDED +#define TORRENT_HTTP_SEED_CONNECTION_HPP_INCLUDED + +#include +#include +#include +#include +#include + +#include "libtorrent/debug.hpp" + +#ifdef _MSC_VER +#pragma warning(push, 1) +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _MSC_VER +#pragma warning(pop) +#endif + +#include "libtorrent/buffer.hpp" +#include "libtorrent/peer_connection.hpp" +#include "libtorrent/socket.hpp" +#include "libtorrent/peer_id.hpp" +#include "libtorrent/storage.hpp" +#include "libtorrent/stat.hpp" +#include "libtorrent/alert.hpp" +#include "libtorrent/torrent_handle.hpp" +#include "libtorrent/torrent.hpp" +#include "libtorrent/peer_request.hpp" +#include "libtorrent/piece_block_progress.hpp" +#include "libtorrent/config.hpp" +// parse_url +#include "libtorrent/tracker_manager.hpp" +#include "libtorrent/http_parser.hpp" + +namespace libtorrent +{ + class torrent; + + namespace detail + { + struct session_impl; + } + + class TORRENT_EXPORT http_seed_connection + : public peer_connection + { + friend class invariant_access; + public: + + // this is the constructor where the we are the active part. + // The peer_conenction should handshake and verify that the + // other end has the correct id + http_seed_connection( + aux::session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , std::string const& url + , policy::peer* peerinfo); + + ~http_seed_connection(); + + // called from the main loop when this connection has any + // work to do. + void on_sent(error_code const& error + , std::size_t bytes_transferred); + void on_receive(error_code const& error + , std::size_t bytes_transferred); + + std::string const& url() const { return m_url; } + + virtual void get_specific_peer_info(peer_info& p) const; + virtual bool in_handshake() const; + + // the following functions appends messages + // to the send buffer + void write_choke() {} + void write_unchoke() {} + void write_interested() {} + void write_not_interested() {} + void write_request(peer_request const& r); + void write_cancel(peer_request const& r) + { incoming_reject_request(r); } + void write_have(int index) {} + void write_piece(peer_request const& r, disk_buffer_holder& buffer) { TORRENT_ASSERT(false); } + void write_keepalive() {} + void on_connected(); + void write_reject_request(peer_request const&) {} + void write_allow_fast(int) {} + +#ifdef TORRENT_DEBUG + void check_invariant() const; +#endif + + private: + + // returns the block currently being + // downloaded. And the progress of that + // block. If the peer isn't downloading + // a piece for the moment, the boost::optional + // will be invalid. + boost::optional downloading_piece_progress() const; + + // this has one entry per bittorrent request + std::deque m_requests; + + std::string m_server_string; + http_parser m_parser; + std::string m_auth; + std::string m_host; + int m_port; + std::string m_path; + std::string m_url; + + // the first request will contain a little bit more data + // than subsequent ones, things that aren't critical are left + // out to save bandwidth. + bool m_first_request; + + // the number of bytes left to receive of the response we're + // currently parsing + int m_response_left; + + // the number of bytes in the start of the receive buffer + // that's http header + int m_body_start; + }; +} + +#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED + diff --git a/include/libtorrent/peer_info.hpp b/include/libtorrent/peer_info.hpp index 5cfb096a3..34f73f731 100644 --- a/include/libtorrent/peer_info.hpp +++ b/include/libtorrent/peer_info.hpp @@ -175,7 +175,8 @@ namespace libtorrent enum { standard_bittorrent = 0, - web_seed = 1 + web_seed = 1, + http_seed = 2 }; int connection_type; diff --git a/include/libtorrent/torrent.hpp b/include/libtorrent/torrent.hpp index 2ee583862..b18464870 100644 --- a/include/libtorrent/torrent.hpp +++ b/include/libtorrent/torrent.hpp @@ -90,6 +90,24 @@ namespace libtorrent struct piece_checker_data; } + struct web_seed_entry + { + std::string url; + // http seeds are different from url seeds in the + // protocol they use. http seeds follows the original + // http seed spec. by John Hoffman + enum type_t { url_seed, http_seed} type; + + web_seed_entry(std::string const& url_, type_t type_) + : url(url_), type(type_) {} + + bool operator==(web_seed_entry const& e) const + { return url == e.url && type == e.type; } + + bool operator<(web_seed_entry const& e) const + { return url < e.url && type < e.type; } + }; + namespace fs = boost::filesystem; // a torrent is a class that holds information @@ -281,7 +299,7 @@ namespace libtorrent void use_interface(const char* net_interface); tcp::endpoint const& get_interface() const { return m_net_interface; } - void connect_to_url_seed(std::string const& url); + void connect_to_url_seed(web_seed_entry const& url); bool connect_to_peer(policy::peer* peerinfo); void set_ratio(float ratio) @@ -325,17 +343,19 @@ namespace libtorrent // add or remove a url that will be attempted for // finding the file(s) in this torrent. - void add_url_seed(std::string const& url) - { m_web_seeds.insert(url); } + void add_web_seed(std::string const& url, web_seed_entry::type_t type) + { m_web_seeds.insert(web_seed_entry(url, type)); } - void remove_url_seed(std::string const& url) - { m_web_seeds.erase(url); } + void remove_web_seed(std::string const& url, web_seed_entry::type_t type) + { m_web_seeds.erase(web_seed_entry(url, type)); } - void retry_url_seed(std::string const& url); + void retry_web_seed(std::string const& url, web_seed_entry::type_t type, int retry = 0); - std::set url_seeds() const + std::set web_seeds() const { return m_web_seeds; } + std::set web_seeds(web_seed_entry::type_t type) const; + bool free_upload_slots() const { return m_num_uploads < m_max_uploads; } @@ -531,12 +551,12 @@ namespace libtorrent // this is the asio callback that is called when a name // lookup for a WEB SEED is completed. void on_name_lookup(error_code const& e, tcp::resolver::iterator i - , std::string url, tcp::endpoint proxy); + , web_seed_entry url, tcp::endpoint proxy); // this is the asio callback that is called when a name // lookup for a proxy for a web seed is completed. void on_proxy_name_lookup(error_code const& e, tcp::resolver::iterator i - , std::string url); + , web_seed_entry url); // this is called when the torrent has finished. i.e. // all the pieces we have not filtered have been downloaded. @@ -755,15 +775,15 @@ namespace libtorrent // The list of web seeds in this torrent. Seeds // with fatal errors are removed from the set - std::set m_web_seeds; + std::set m_web_seeds; // a list of web seeds that have failed and are // waiting to be retried - std::map m_web_seeds_next_retry; + std::map m_web_seeds_next_retry; // urls of the web seeds that we are currently // resolving the address for - std::set m_resolving_web_seeds; + std::set m_resolving_web_seeds; #ifndef TORRENT_DISABLE_EXTENSIONS typedef std::list > extension_list_t; diff --git a/include/libtorrent/torrent_handle.hpp b/include/libtorrent/torrent_handle.hpp index da7448659..d2cdd82cc 100644 --- a/include/libtorrent/torrent_handle.hpp +++ b/include/libtorrent/torrent_handle.hpp @@ -341,6 +341,10 @@ namespace libtorrent void remove_url_seed(std::string const& url) const; std::set url_seeds() const; + void add_http_seed(std::string const& url) const; + void remove_http_seed(std::string const& url) const; + std::set http_seeds() const; + #ifndef TORRENT_DISABLE_EXTENSIONS void add_extension(boost::function(torrent*, void*)> const& ext , void* userdata = 0); diff --git a/include/libtorrent/torrent_info.hpp b/include/libtorrent/torrent_info.hpp index dccbf6814..d9df4afcc 100644 --- a/include/libtorrent/torrent_info.hpp +++ b/include/libtorrent/torrent_info.hpp @@ -197,6 +197,11 @@ namespace libtorrent void add_url_seed(std::string const& url) { m_url_seeds.push_back(url); } + std::vector const& http_seeds() const + { return m_http_seeds; } + void add_http_seed(std::string const& url) + { m_http_seeds.push_back(url); } + size_type total_size() const { return m_files.total_size(); } int piece_length() const { return m_files.piece_length(); } int num_pieces() const { return m_files.num_pieces(); } @@ -296,6 +301,7 @@ namespace libtorrent // the urls to the trackers std::vector m_urls; std::vector m_url_seeds; + std::vector m_http_seeds; nodes_t m_nodes; // the hash that identifies this torrent diff --git a/src/Makefile.am b/src/Makefile.am index 63ca76a42..367dbff29 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,8 +15,9 @@ endif libtorrent_rasterbar_la_SOURCES = entry.cpp escape_string.cpp \ lazy_bdecode.cpp assert.cpp enum_net.cpp broadcast_socket.cpp \ peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \ -natpmp.cpp piece_picker.cpp policy.cpp session.cpp session_impl.cpp sha1.cpp \ -stat.cpp storage.cpp torrent.cpp torrent_handle.cpp pe_crypto.cpp \ +http_seed_connection.cpp natpmp.cpp piece_picker.cpp policy.cpp \ +session.cpp session_impl.cpp sha1.cpp stat.cpp storage.cpp torrent.cpp \ +torrent_handle.cpp pe_crypto.cpp \ torrent_info.cpp tracker_manager.cpp http_connection.cpp \ http_tracker_connection.cpp udp_tracker_connection.cpp \ alert.cpp identify_client.cpp ip_filter.cpp file.cpp metadata_transfer.cpp \ @@ -58,6 +59,7 @@ $(top_srcdir)/include/libtorrent/fingerprint.hpp \ $(top_srcdir)/include/libtorrent/gzip.hpp \ $(top_srcdir)/include/libtorrent/hasher.hpp \ $(top_srcdir)/include/libtorrent/http_connection.hpp \ +$(top_srcdir)/include/libtorrent/http_seed_connection.hpp \ $(top_srcdir)/include/libtorrent/http_stream.hpp \ $(top_srcdir)/include/libtorrent/http_parser.hpp \ $(top_srcdir)/include/libtorrent/session_settings.hpp \ diff --git a/src/http_seed_connection.cpp b/src/http_seed_connection.cpp new file mode 100644 index 000000000..e8cc4254d --- /dev/null +++ b/src/http_seed_connection.cpp @@ -0,0 +1,477 @@ +/* + +Copyright (c) 2008, Arvid Norberg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the distribution. + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +*/ + +#include "libtorrent/pch.hpp" + +#include +#include +#include +#include +#include +#include + +#include "libtorrent/http_seed_connection.hpp" +#include "libtorrent/session.hpp" +#include "libtorrent/identify_client.hpp" +#include "libtorrent/entry.hpp" +#include "libtorrent/bencode.hpp" +#include "libtorrent/alert_types.hpp" +#include "libtorrent/invariant_check.hpp" +#include "libtorrent/io.hpp" +#include "libtorrent/version.hpp" +#include "libtorrent/aux_/session_impl.hpp" +#include "libtorrent/parse_url.hpp" + +using boost::bind; +using boost::shared_ptr; +using libtorrent::aux::session_impl; + +namespace libtorrent +{ + http_seed_connection::http_seed_connection( + session_impl& ses + , boost::weak_ptr t + , boost::shared_ptr s + , tcp::endpoint const& remote + , std::string const& url + , policy::peer* peerinfo) + : peer_connection(ses, t, s, remote, peerinfo) + , m_url(url) + , m_first_request(true) + , m_response_left(0) + , m_body_start(0) + { + INVARIANT_CHECK; + + // we want large blocks as well, so + // we can request more bytes at once + request_large_blocks(true); + set_upload_only(true); + prefer_whole_pieces(1); + + // we only want left-over bandwidth + set_priority(0); + shared_ptr tor = t.lock(); + TORRENT_ASSERT(tor); + int blocks_per_piece = tor->torrent_file().piece_length() / tor->block_size(); + + // multiply with the blocks per piece since that many requests are + // merged into one http request + m_max_out_request_queue = ses.settings().urlseed_pipeline_size + * blocks_per_piece; + + // since this is a web seed, change the timeout + // according to the settings. + set_timeout(ses.settings().urlseed_timeout); +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "*** http_seed_connection\n"; +#endif + + std::string protocol; + char const* error; + boost::tie(protocol, m_auth, m_host, m_port, m_path, error) + = parse_url_components(url); + TORRENT_ASSERT(error == 0); + + if (!m_auth.empty()) + m_auth = base64encode(m_auth); + + m_server_string = "HTTP seed @ "; + m_server_string += m_host; + } + + http_seed_connection::~http_seed_connection() + {} + + boost::optional + http_seed_connection::downloading_piece_progress() const + { + if (m_requests.empty()) + return boost::optional(); + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + piece_block_progress ret; + + peer_request const& pr = m_requests.front(); + ret.piece_index = pr.piece; + if (!m_parser.header_finished()) + { + ret.bytes_downloaded = 0; + } + else + { + int receive_buffer_size = receive_buffer().left() - m_parser.body_start(); + TORRENT_ASSERT(receive_buffer_size < t->block_size()); + ret.bytes_downloaded = t->block_size() - receive_buffer_size; + } + ret.block_index = (pr.start + ret.bytes_downloaded) / t->block_size(); + ret.full_block_bytes = t->block_size(); + const int last_piece = t->torrent_file().num_pieces() - 1; + if (ret.piece_index == last_piece && ret.block_index + == t->torrent_file().piece_size(last_piece) / t->block_size()) + ret.full_block_bytes = t->torrent_file().piece_size(last_piece) % t->block_size(); + return ret; + } + + void http_seed_connection::on_connected() + { + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + // this is always a seed + incoming_have_all(); + + // it is always possible to request pieces + incoming_unchoke(); + + reset_recv_buffer(t->block_size() + 1024); + } + + void http_seed_connection::write_request(peer_request const& r) + { + INVARIANT_CHECK; + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + TORRENT_ASSERT(t->valid_metadata()); + // http_seeds don't support requesting more than one piece + // at a time + TORRENT_ASSERT(r.length <= t->torrent_file().piece_size(r.piece)); + + std::string request; + request.reserve(400); + + int size = r.length; + const int block_size = t->block_size(); + const int piece_size = t->torrent_file().piece_length(); + peer_request pr; + while (size > 0) + { + int request_offset = r.start + r.length - size; + pr.start = request_offset % piece_size; + pr.length = (std::min)(block_size, size); + pr.piece = r.piece + request_offset / piece_size; + m_requests.push_back(pr); + size -= pr.length; + } + + proxy_settings const& ps = m_ses.web_seed_proxy(); + bool using_proxy = ps.type == proxy_settings::http + || ps.type == proxy_settings::http_pw; + + request += "GET "; + request += using_proxy ? m_url : m_path; + request += "?info_hash="; + request += escape_string((char const*)&t->torrent_file().info_hash()[0], 20); + request += "&piece="; + request += boost::lexical_cast(r.piece); + + // if we're requesting less than an entire piece we need to + // add ranges + if (r.start > 0 || r.length != t->torrent_file().piece_size(r.piece)) + { + request += "&ranges="; + request += boost::lexical_cast(r.start); + request += "-"; + // TODO: are ranges inclusive? + request += boost::lexical_cast(r.start + r.length); + } + + request += " HTTP/1.1\r\n"; + request += "Host: "; + request += m_host; + if (m_first_request) + { + request += "\r\nUser-Agent: "; + request += m_ses.settings().user_agent; + } + if (!m_auth.empty()) + { + request += "\r\nAuthorization: Basic "; + request += m_auth; + } + if (ps.type == proxy_settings::http_pw) + { + request += "\r\nProxy-Authorization: Basic "; + request += base64encode(ps.username + ":" + ps.password); + } + if (using_proxy) + { + request += "\r\nProxy-Connection: keep-alive"; + } + if (m_first_request || using_proxy) + request += "\r\nConnection: keep-alive"; + request += "\r\n\r\n"; + m_first_request = false; + +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << request << "\n"; +#endif + + send_buffer(request.c_str(), request.size(), message_type_request); + } + + // -------------------------- + // RECEIVE DATA + // -------------------------- + + void http_seed_connection::on_receive(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) + { +#ifdef TORRENT_VERBOSE_LOGGING + (*m_logger) << "*** http_seed_connection error: " + << error.message() << "\n"; +#endif + return; + } + + boost::shared_ptr t = associated_torrent().lock(); + TORRENT_ASSERT(t); + + incoming_piece_fragment(); + + for (;;) + { + buffer::const_interval recv_buffer = receive_buffer(); + + if (bytes_transferred == 0) break; + TORRENT_ASSERT(recv_buffer.left() > 0); + + TORRENT_ASSERT(!m_requests.empty()); + if (m_requests.empty()) + { + disconnect("unexpected HTTP response", 2); + return; + } + + peer_request front_request = m_requests.front(); + + int payload = 0; + int protocol = 0; + bool header_finished = m_parser.header_finished(); + if (!header_finished) + { + bool error = false; + boost::tie(payload, protocol) = m_parser.incoming(recv_buffer, error); + m_statistics.received_bytes(0, protocol); + bytes_transferred -= protocol; + if (payload > front_request.length) payload = front_request.length; + + if (error) + { + disconnect("failed to parse HTTP response", 2); + return; + } + + TORRENT_ASSERT(recv_buffer.left() == 0 || *recv_buffer.begin == 'H'); + + TORRENT_ASSERT(recv_buffer.left() <= packet_size()); + + // this means the entire status line hasn't been received yet + if (m_parser.status_code() == -1) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); + break; + } + + // if the status code is not one of the accepted ones, abort + if (m_parser.status_code() != 200 // OK + && m_parser.status_code() != 503 + && !(m_parser.status_code() >= 300 // redirect + && m_parser.status_code() < 400)) + { + t->remove_web_seed(m_url, web_seed_entry::http_seed); + std::string error_msg = boost::lexical_cast(m_parser.status_code()) + + " " + m_parser.message(); + if (m_ses.m_alerts.should_post()) + { + session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); + m_ses.m_alerts.post_alert(url_seed_alert(t->get_handle(), url() + , error_msg)); + } + disconnect(error_msg.c_str(), 1); + return; + } + if (!m_parser.header_finished()) + { + TORRENT_ASSERT(payload == 0); + TORRENT_ASSERT(bytes_transferred == 0); + break; + } + } + + // we just completed reading the header + if (!header_finished) + { + if (m_parser.status_code() >= 300 && m_parser.status_code() < 400) + { + // this means we got a redirection request + // look for the location header + std::string location = m_parser.header("location"); + + if (location.empty()) + { + // we should not try this server again. + t->remove_web_seed(m_url, web_seed_entry::http_seed); + disconnect("got HTTP redirection status without location header", 2); + return; + } + + // add the redirected url and remove the current one + t->add_web_seed(location, web_seed_entry::http_seed); + t->remove_web_seed(m_url, web_seed_entry::http_seed); + std::stringstream msg; + msg << "redirecting to \"" << location << "\""; + disconnect(msg.str().c_str()); + return; + } + + std::string const& server_version = m_parser.header("server"); + if (!server_version.empty()) + { + m_server_string = "URL seed @ "; + m_server_string += m_host; + m_server_string += " ("; + m_server_string += server_version; + m_server_string += ")"; + } + + m_response_left = atol(m_parser.header("content-length").c_str()); + if (m_response_left == -1) + { + // we should not try this server again. + t->remove_web_seed(m_url, web_seed_entry::http_seed); + disconnect("no content-length in HTTP response", 2); + return; + } + if (payload > m_response_left) payload = m_response_left; + m_body_start = m_parser.body_start(); + m_response_left -= payload; + m_statistics.received_bytes(payload, 0); + } + else + { + payload = bytes_transferred; + if (payload > m_response_left) payload = m_response_left; + if (payload > front_request.length) payload = front_request.length; + m_statistics.received_bytes(payload, 0); + m_response_left -= payload; + } + recv_buffer.begin += m_body_start; + + if (m_parser.status_code() == 503) + { + if (!m_parser.finished()) return; + + int retry_time = atol(std::string(recv_buffer.begin, recv_buffer.end).c_str()); + if (retry_time <= 0) retry_time = 0; +#ifdef TORRENT_VERBOSE_LOGGING + else + { + (*m_logger) << time_now_string() << ": retrying in " << retry_time << " seconds\n"; + } +#endif + + // temporarily unavailable, retry later + t->retry_web_seed(m_url, web_seed_entry::http_seed, retry_time); + t->remove_web_seed(m_url, web_seed_entry::http_seed); + disconnect("503 retrying later", 1); + return; + } + + // we only received the header, no data + if (recv_buffer.left() == 0) break; + + if (recv_buffer.left() < front_request.length) break; + + m_requests.pop_front(); + incoming_piece(front_request, recv_buffer.begin); + if (associated_torrent().expired()) return; + cut_receive_buffer(m_body_start + front_request.length, t->block_size() + 1024); + bytes_transferred -= payload; + m_body_start = 0; + if (m_response_left > 0) continue; + TORRENT_ASSERT(m_response_left == 0); + m_parser.reset(); + } + } + + void http_seed_connection::get_specific_peer_info(peer_info& p) const + { + if (is_interesting()) p.flags |= peer_info::interesting; + if (is_choked()) p.flags |= peer_info::choked; + p.flags |= peer_info::local_connection; + if (!is_connecting() && m_server_string.empty()) + p.flags |= peer_info::handshake; + if (is_connecting() && !is_queued()) p.flags |= peer_info::connecting; + if (is_queued()) p.flags |= peer_info::queued; + + p.client = m_server_string; + p.connection_type = peer_info::http_seed; + } + + bool http_seed_connection::in_handshake() const + { + return m_server_string.empty(); + } + + void http_seed_connection::on_sent(error_code const& error + , std::size_t bytes_transferred) + { + INVARIANT_CHECK; + + if (error) return; + m_statistics.sent_bytes(0, bytes_transferred); + } + + +#ifdef TORRENT_DEBUG + void http_seed_connection::check_invariant() const + { +/* + TORRENT_ASSERT(m_num_pieces == std::count( + m_have_piece.begin() + , m_have_piece.end() + , true)); +*/ } +#endif + +} + diff --git a/src/torrent.cpp b/src/torrent.cpp index 92196fbdb..8256712f5 100644 --- a/src/torrent.cpp +++ b/src/torrent.cpp @@ -69,6 +69,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "libtorrent/peer.hpp" #include "libtorrent/bt_peer_connection.hpp" #include "libtorrent/web_peer_connection.hpp" +#include "libtorrent/http_seed_connection.hpp" #include "libtorrent/peer_id.hpp" #include "libtorrent/alert.hpp" #include "libtorrent/identify_client.hpp" @@ -593,8 +594,14 @@ namespace libtorrent , int((m_torrent_file->total_size()+m_block_size-1)/m_block_size)); std::vector const& url_seeds = m_torrent_file->url_seeds(); - std::copy(url_seeds.begin(), url_seeds.end(), std::inserter(m_web_seeds - , m_web_seeds.begin())); + for (std::vector::const_iterator i = url_seeds.begin() + , end(url_seeds.end()); i != end; ++i) + add_web_seed(*i, web_seed_entry::url_seed); + + std::vector const& http_seeds = m_torrent_file->http_seeds(); + for (std::vector::const_iterator i = http_seeds.begin() + , end(http_seeds.end()); i != end; ++i) + add_web_seed(*i, web_seed_entry::http_seed); set_state(torrent_status::checking_resume_data); @@ -2430,12 +2437,12 @@ namespace libtorrent } } - void torrent::connect_to_url_seed(std::string const& url) + void torrent::connect_to_url_seed(web_seed_entry const& web) { INVARIANT_CHECK; #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " resolving web seed: " << url << "\n"; + (*m_ses.m_logger) << time_now_string() << " resolving web seed: " << web.url << "\n"; #endif std::string protocol; @@ -2445,7 +2452,7 @@ namespace libtorrent std::string path; char const* error; boost::tie(protocol, auth, hostname, port, path, error) - = parse_url_components(url); + = parse_url_components(web.url); if (error) { @@ -2453,7 +2460,7 @@ namespace libtorrent (*m_ses.m_logger) << time_now_string() << " failed to parse web seed url: " << error << "\n"; #endif // never try it again - remove_url_seed(url); + m_web_seeds.erase(web); return; } @@ -2466,10 +2473,10 @@ namespace libtorrent if (m_ses.m_alerts.should_post()) { m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, "unknown protocol")); + url_seed_alert(get_handle(), web.url, "unknown protocol")); } // never try it again - remove_url_seed(url); + m_web_seeds.erase(web); return; } @@ -2478,10 +2485,10 @@ namespace libtorrent if (m_ses.m_alerts.should_post()) { m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, "invalid hostname")); + url_seed_alert(get_handle(), web.url, "invalid hostname")); } // never try it again - remove_url_seed(url); + m_web_seeds.erase(web); return; } @@ -2490,14 +2497,14 @@ namespace libtorrent if (m_ses.m_alerts.should_post()) { m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, "invalid port")); + url_seed_alert(get_handle(), web.url, "invalid port")); } // never try it again - remove_url_seed(url); + m_web_seeds.erase(web); return; } - m_resolving_web_seeds.insert(url); + m_resolving_web_seeds.insert(web); proxy_settings const& ps = m_ses.web_seed_proxy(); if (ps.type == proxy_settings::http || ps.type == proxy_settings::http_pw) @@ -2506,7 +2513,7 @@ namespace libtorrent tcp::resolver::query q(ps.hostname , boost::lexical_cast(ps.port)); m_host_resolver.async_resolve(q, - bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, url)); + bind(&torrent::on_proxy_name_lookup, shared_from_this(), _1, _2, web)); } else { @@ -2515,30 +2522,30 @@ namespace libtorrent if (m_ses.m_alerts.should_post()) { m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, "port blocked by port-filter")); + url_seed_alert(get_handle(), web.url, "port blocked by port-filter")); } // never try it again - remove_url_seed(url); + m_web_seeds.erase(web); return; } tcp::resolver::query q(hostname, boost::lexical_cast(port)); m_host_resolver.async_resolve(q, - bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url + bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, web , tcp::endpoint())); } } void torrent::on_proxy_name_lookup(error_code const& e, tcp::resolver::iterator host - , std::string url) + , web_seed_entry web) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); INVARIANT_CHECK; #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " completed resolve proxy hostname for: " << url << "\n"; + (*m_ses.m_logger) << time_now_string() << " completed resolve proxy hostname for: " << web.url << "\n"; #endif if (m_abort) return; @@ -2548,12 +2555,12 @@ namespace libtorrent if (m_ses.m_alerts.should_post()) { m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, e.message())); + url_seed_alert(get_handle(), web.url, e.message())); } // the name lookup failed for the http host. Don't try // this host again - remove_url_seed(url); + m_web_seeds.erase(web); return; } @@ -2566,16 +2573,16 @@ namespace libtorrent int port; char const* error; boost::tie(ignore, ignore, hostname, port, ignore, error) - = parse_url_components(url); + = parse_url_components(web.url); if (error) { if (m_ses.m_alerts.should_post()) { m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, error)); + url_seed_alert(get_handle(), web.url, error)); } - remove_url_seed(url); + m_web_seeds.erase(web); return; } @@ -2588,23 +2595,23 @@ namespace libtorrent tcp::resolver::query q(hostname, boost::lexical_cast(port)); m_host_resolver.async_resolve(q, - bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, url, a)); + bind(&torrent::on_name_lookup, shared_from_this(), _1, _2, web, a)); } void torrent::on_name_lookup(error_code const& e, tcp::resolver::iterator host - , std::string url, tcp::endpoint proxy) + , web_seed_entry web, tcp::endpoint proxy) { session_impl::mutex_t::scoped_lock l(m_ses.m_mutex); INVARIANT_CHECK; #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING - (*m_ses.m_logger) << time_now_string() << " completed resolve: " << url << "\n"; + (*m_ses.m_logger) << time_now_string() << " completed resolve: " << web.url << "\n"; #endif if (m_abort) return; - std::set::iterator i = m_resolving_web_seeds.find(url); + std::set::iterator i = m_resolving_web_seeds.find(web); if (i != m_resolving_web_seeds.end()) m_resolving_web_seeds.erase(i); if (e || host == tcp::resolver::iterator()) @@ -2614,15 +2621,15 @@ namespace libtorrent std::stringstream msg; msg << "HTTP seed hostname lookup failed: " << e.message(); m_ses.m_alerts.post_alert( - url_seed_alert(get_handle(), url, msg.str())); + url_seed_alert(get_handle(), web.url, msg.str())); } #if defined TORRENT_VERBOSE_LOGGING || defined TORRENT_LOGGING || defined TORRENT_ERROR_LOGGING - (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << url << "\n"; + (*m_ses.m_logger) << " ** HOSTNAME LOOKUP FAILED!**: " << web.url << "\n"; #endif // the name lookup failed for the http host. Don't try // this host again - remove_url_seed(url); + m_web_seeds.erase(web); return; } @@ -2652,8 +2659,17 @@ namespace libtorrent s->get()->set_no_connect(true); } - boost::intrusive_ptr c(new (std::nothrow) web_peer_connection( - m_ses, shared_from_this(), s, a, url, 0)); + boost::intrusive_ptr c; + if (web.type == web_seed_entry::url_seed) + { + c.reset(new (std::nothrow) web_peer_connection( + m_ses, shared_from_this(), s, a, web.url, 0)); + } + else if (web.type == web_seed_entry::http_seed) + { + c.reset(new (std::nothrow) http_seed_connection( + m_ses, shared_from_this(), s, a, web.url, 0)); + } if (!c) return; #ifdef TORRENT_DEBUG @@ -2924,7 +2940,18 @@ namespace libtorrent { std::string url = url_list->list_string_value_at(i); if (url.empty()) continue; - m_web_seeds.insert(url); + add_web_seed(url, web_seed_entry::url_seed); + } + } + + lazy_entry const* httpseeds = rd.dict_find_list("httpseeds"); + if (httpseeds) + { + for (int i = 0; i < httpseeds->list_size(); ++i) + { + std::string url = httpseeds->list_string_value_at(i); + if (url.empty()) continue; + add_web_seed(url, web_seed_entry::http_seed); } } } @@ -3028,10 +3055,19 @@ namespace libtorrent if (!m_web_seeds.empty()) { entry::list_type& url_list = ret["url-list"].list(); - for (std::set::const_iterator i = m_web_seeds.begin() + for (std::set::const_iterator i = m_web_seeds.begin() , end(m_web_seeds.end()); i != end; ++i) { - url_list.push_back(*i); + if (i->type != web_seed_entry::url_seed) continue; + url_list.push_back(i->url); + } + + entry::list_type& httpseed_list = ret["httpseeds"].list(); + for (std::set::const_iterator i = m_web_seeds.begin() + , end(m_web_seeds.end()); i != end; ++i) + { + if (i->type != web_seed_entry::http_seed) continue; + httpseed_list.push_back(i->url); } } @@ -4536,12 +4572,13 @@ namespace libtorrent // ---- WEB SEEDS ---- + ptime now = time_now(); // re-insert urls that are to be retrieds into the m_web_seeds - typedef std::map::iterator iter_t; + typedef std::map::iterator iter_t; for (iter_t i = m_web_seeds_next_retry.begin(); i != m_web_seeds_next_retry.end();) { iter_t erase_element = i++; - if (erase_element->second <= time_now()) + if (erase_element->second <= now) { m_web_seeds.insert(erase_element->first); m_web_seeds_next_retry.erase(erase_element); @@ -4553,23 +4590,23 @@ namespace libtorrent { // keep trying web-seeds if there are any // first find out which web seeds we are connected to - std::set web_seeds; + std::set web_seeds; for (peer_iterator i = m_connections.begin(); i != m_connections.end(); ++i) { - web_peer_connection* p - = dynamic_cast(*i); - if (!p) continue; - web_seeds.insert(p->url()); + web_peer_connection* p = dynamic_cast(*i); + if (p) web_seeds.insert(web_seed_entry(p->url(), web_seed_entry::url_seed)); + http_seed_connection* s = dynamic_cast(*i); + if (s) web_seeds.insert(web_seed_entry(s->url(), web_seed_entry::http_seed)); } - for (std::set::iterator i = m_resolving_web_seeds.begin() + for (std::set::iterator i = m_resolving_web_seeds.begin() , end(m_resolving_web_seeds.end()); i != end; ++i) web_seeds.insert(web_seeds.begin(), *i); // from the list of available web seeds, subtract the ones we are // already connected to. - std::vector not_connected_web_seeds; + std::vector not_connected_web_seeds; std::set_difference(m_web_seeds.begin(), m_web_seeds.end(), web_seeds.begin() , web_seeds.end(), std::back_inserter(not_connected_web_seeds)); @@ -4615,10 +4652,22 @@ namespace libtorrent } } - void torrent::retry_url_seed(std::string const& url) + std::set torrent::web_seeds(web_seed_entry::type_t type) const { - m_web_seeds_next_retry[url] = time_now() - + seconds(m_ses.settings().urlseed_wait_retry); + std::set ret; + for (std::set::const_iterator i = m_web_seeds.begin() + , end(m_web_seeds.end()); i != end; ++i) + { + if (i->type != type) continue; + ret.insert(i->url); + } + return ret; + } + + void torrent::retry_web_seed(std::string const& url, web_seed_entry::type_t type, int retry) + { + if (retry == 0) retry = m_ses.settings().urlseed_wait_retry; + m_web_seeds_next_retry[web_seed_entry(url, type)] = time_now() + seconds(retry); } bool torrent::try_connect_peer() diff --git a/src/torrent_handle.cpp b/src/torrent_handle.cpp index dac900bdf..217a0734b 100644 --- a/src/torrent_handle.cpp +++ b/src/torrent_handle.cpp @@ -506,20 +506,39 @@ namespace libtorrent void torrent_handle::add_url_seed(std::string const& url) const { INVARIANT_CHECK; - TORRENT_FORWARD(add_url_seed(url)); + TORRENT_FORWARD(add_web_seed(url, web_seed_entry::url_seed)); } void torrent_handle::remove_url_seed(std::string const& url) const { INVARIANT_CHECK; - TORRENT_FORWARD(remove_url_seed(url)); + TORRENT_FORWARD(remove_web_seed(url, web_seed_entry::url_seed)); } std::set torrent_handle::url_seeds() const { INVARIANT_CHECK; const static std::set empty; - TORRENT_FORWARD_RETURN(url_seeds(), empty); + TORRENT_FORWARD_RETURN(web_seeds(web_seed_entry::url_seed), empty); + } + + void torrent_handle::add_http_seed(std::string const& url) const + { + INVARIANT_CHECK; + TORRENT_FORWARD(add_web_seed(url, web_seed_entry::http_seed)); + } + + void torrent_handle::remove_http_seed(std::string const& url) const + { + INVARIANT_CHECK; + TORRENT_FORWARD(remove_web_seed(url, web_seed_entry::http_seed)); + } + + std::set torrent_handle::http_seeds() const + { + INVARIANT_CHECK; + const static std::set empty; + TORRENT_FORWARD_RETURN(web_seeds(web_seed_entry::http_seed), empty); } void torrent_handle::replace_trackers( diff --git a/src/torrent_info.cpp b/src/torrent_info.cpp index d03893825..f41be2b7b 100644 --- a/src/torrent_info.cpp +++ b/src/torrent_info.cpp @@ -651,6 +651,22 @@ namespace libtorrent } } + // if there are any http-seeds, extract them + lazy_entry const* http_seeds = torrent_file.dict_find("httpseeds"); + if (http_seeds && http_seeds->type() == lazy_entry::string_t) + { + m_http_seeds.push_back(http_seeds->string_value()); + } + else if (http_seeds && http_seeds->type() == lazy_entry::list_t) + { + for (int i = 0, end(http_seeds->list_size()); i < end; ++i) + { + lazy_entry const* url = http_seeds->list_at(i); + if (url->type() != lazy_entry::string_t) continue; + m_http_seeds.push_back(url->string_value()); + } + } + m_comment = torrent_file.dict_find_string_value("comment.utf-8"); if (m_comment.empty()) m_comment = torrent_file.dict_find_string_value("comment"); diff --git a/src/web_peer_connection.cpp b/src/web_peer_connection.cpp index b856d0ec4..f763bbda6 100644 --- a/src/web_peer_connection.cpp +++ b/src/web_peer_connection.cpp @@ -381,9 +381,9 @@ namespace libtorrent if (m_parser.status_code() == 503) { // temporarily unavailable, retry later - t->retry_url_seed(m_url); + t->retry_web_seed(m_url, web_seed_entry::url_seed); } - t->remove_url_seed(m_url); + t->remove_web_seed(m_url, web_seed_entry::url_seed); std::string error_msg = boost::lexical_cast(m_parser.status_code()) + " " + m_parser.message(); if (m_ses.m_alerts.should_post()) @@ -418,7 +418,7 @@ namespace libtorrent if (location.empty()) { // we should not try this server again. - t->remove_url_seed(m_url); + t->remove_web_seed(m_url, web_seed_entry::url_seed); disconnect("got HTTP redirection status without location header", 2); return; } @@ -439,7 +439,7 @@ namespace libtorrent size_t i = location.rfind(path); if (i == std::string::npos) { - t->remove_url_seed(m_url); + t->remove_web_seed(m_url, web_seed_entry::url_seed); std::stringstream msg; msg << "got invalid HTTP redirection location (\"" << location << "\") " "expected it to end with: " << path; @@ -448,8 +448,8 @@ namespace libtorrent } location.resize(i); } - t->add_url_seed(location); - t->remove_url_seed(m_url); + t->add_web_seed(location, web_seed_entry::url_seed); + t->remove_web_seed(m_url, web_seed_entry::url_seed); std::stringstream msg; msg << "redirecting to \"" << location << "\""; disconnect(msg.str().c_str()); @@ -487,7 +487,7 @@ namespace libtorrent if (!range_str) { // we should not try this server again. - t->remove_url_seed(m_url); + t->remove_web_seed(m_url, web_seed_entry::url_seed); std::stringstream msg; msg << "invalid range in HTTP response: " << range_str.str(); disconnect(msg.str().c_str(), 2); @@ -503,7 +503,7 @@ namespace libtorrent if (range_end == -1) { // we should not try this server again. - t->remove_url_seed(m_url); + t->remove_web_seed(m_url, web_seed_entry::url_seed); disconnect("no content-length in HTTP response", 2); return; } @@ -684,7 +684,6 @@ namespace libtorrent return m_server_string.empty(); } - // throws exception when the client should be disconnected void web_peer_connection::on_sent(error_code const& error , std::size_t bytes_transferred) {