added support for http seeds (BEP 17)
This commit is contained in:
parent
96a771ef8b
commit
e5d3755afb
|
@ -22,6 +22,7 @@ set(sources
|
|||
peer_connection
|
||||
bt_peer_connection
|
||||
web_peer_connection
|
||||
http_seed_connection
|
||||
instantiate_connection
|
||||
natpmp
|
||||
piece_picker
|
||||
|
|
|
@ -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
|
||||
|
|
1
Jamfile
1
Jamfile
|
@ -321,6 +321,7 @@ SOURCES =
|
|||
peer_connection
|
||||
bt_peer_connection
|
||||
web_peer_connection
|
||||
http_seed_connection
|
||||
instantiate_connection
|
||||
natpmp
|
||||
piece_picker
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -1269,6 +1269,9 @@ The ``torrent_info`` has the following synopsis::
|
|||
bool priv() const;
|
||||
|
||||
std::vector<std::string> const& url_seeds() const;
|
||||
void add_url_seed(std::string const& url);
|
||||
std::vector<std::string> 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<std::string> const& url_seeds() const;
|
||||
void add_url_seed(std::string const& url);
|
||||
std::vector<std::string> 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<std::string> url_seeds() const;
|
||||
|
||||
void add_http_seed(std::string const& url);
|
||||
void remove_http_seed(std::string const& url);
|
||||
std::set<std::string> 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<std::string> 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
|
||||
|
||||
|
|
|
@ -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 <ctime>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include "libtorrent/debug.hpp"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push, 1)
|
||||
#endif
|
||||
|
||||
#include <boost/smart_ptr.hpp>
|
||||
#include <boost/weak_ptr.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/array.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
|
||||
#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<torrent> t
|
||||
, boost::shared_ptr<socket_type> 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<piece_block_progress> downloading_piece_progress() const;
|
||||
|
||||
// this has one entry per bittorrent request
|
||||
std::deque<peer_request> 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
|
||||
|
|
@ -175,7 +175,8 @@ namespace libtorrent
|
|||
enum
|
||||
{
|
||||
standard_bittorrent = 0,
|
||||
web_seed = 1
|
||||
web_seed = 1,
|
||||
http_seed = 2
|
||||
};
|
||||
int connection_type;
|
||||
|
||||
|
|
|
@ -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<std::string> url_seeds() const
|
||||
std::set<web_seed_entry> web_seeds() const
|
||||
{ return m_web_seeds; }
|
||||
|
||||
std::set<std::string> 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<std::string> m_web_seeds;
|
||||
std::set<web_seed_entry> m_web_seeds;
|
||||
|
||||
// a list of web seeds that have failed and are
|
||||
// waiting to be retried
|
||||
std::map<std::string, ptime> m_web_seeds_next_retry;
|
||||
std::map<web_seed_entry, ptime> m_web_seeds_next_retry;
|
||||
|
||||
// urls of the web seeds that we are currently
|
||||
// resolving the address for
|
||||
std::set<std::string> m_resolving_web_seeds;
|
||||
std::set<web_seed_entry> m_resolving_web_seeds;
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
typedef std::list<boost::shared_ptr<torrent_plugin> > extension_list_t;
|
||||
|
|
|
@ -341,6 +341,10 @@ namespace libtorrent
|
|||
void remove_url_seed(std::string const& url) const;
|
||||
std::set<std::string> url_seeds() const;
|
||||
|
||||
void add_http_seed(std::string const& url) const;
|
||||
void remove_http_seed(std::string const& url) const;
|
||||
std::set<std::string> http_seeds() const;
|
||||
|
||||
#ifndef TORRENT_DISABLE_EXTENSIONS
|
||||
void add_extension(boost::function<boost::shared_ptr<torrent_plugin>(torrent*, void*)> const& ext
|
||||
, void* userdata = 0);
|
||||
|
|
|
@ -197,6 +197,11 @@ namespace libtorrent
|
|||
void add_url_seed(std::string const& url)
|
||||
{ m_url_seeds.push_back(url); }
|
||||
|
||||
std::vector<std::string> 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<announce_entry> m_urls;
|
||||
std::vector<std::string> m_url_seeds;
|
||||
std::vector<std::string> m_http_seeds;
|
||||
nodes_t m_nodes;
|
||||
|
||||
// the hash that identifies this torrent
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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 <vector>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <boost/bind.hpp>
|
||||
#include <sstream>
|
||||
|
||||
#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<torrent> t
|
||||
, boost::shared_ptr<socket_type> 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<torrent> 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<piece_block_progress>
|
||||
http_seed_connection::downloading_piece_progress() const
|
||||
{
|
||||
if (m_requests.empty())
|
||||
return boost::optional<piece_block_progress>();
|
||||
|
||||
boost::shared_ptr<torrent> 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<torrent> 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<torrent> 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<std::string>(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<std::string>(r.start);
|
||||
request += "-";
|
||||
// TODO: are ranges inclusive?
|
||||
request += boost::lexical_cast<std::string>(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<torrent> 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<std::string>(m_parser.status_code())
|
||||
+ " " + m_parser.message();
|
||||
if (m_ses.m_alerts.should_post<url_seed_alert>())
|
||||
{
|
||||
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
|
||||
|
||||
}
|
||||
|
145
src/torrent.cpp
145
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<std::string> 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<std::string>::const_iterator i = url_seeds.begin()
|
||||
, end(url_seeds.end()); i != end; ++i)
|
||||
add_web_seed(*i, web_seed_entry::url_seed);
|
||||
|
||||
std::vector<std::string> const& http_seeds = m_torrent_file->http_seeds();
|
||||
for (std::vector<std::string>::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<url_seed_alert>())
|
||||
{
|
||||
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<url_seed_alert>())
|
||||
{
|
||||
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<url_seed_alert>())
|
||||
{
|
||||
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<std::string>(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<url_seed_alert>())
|
||||
{
|
||||
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<std::string>(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<url_seed_alert>())
|
||||
{
|
||||
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<url_seed_alert>())
|
||||
{
|
||||
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<std::string>(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<std::string>::iterator i = m_resolving_web_seeds.find(url);
|
||||
std::set<web_seed_entry>::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<http_stream>()->set_no_connect(true);
|
||||
}
|
||||
|
||||
boost::intrusive_ptr<peer_connection> c(new (std::nothrow) web_peer_connection(
|
||||
m_ses, shared_from_this(), s, a, url, 0));
|
||||
boost::intrusive_ptr<peer_connection> 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<std::string>::const_iterator i = m_web_seeds.begin()
|
||||
for (std::set<web_seed_entry>::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<web_seed_entry>::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<std::string, ptime>::iterator iter_t;
|
||||
typedef std::map<web_seed_entry, ptime>::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<std::string> web_seeds;
|
||||
std::set<web_seed_entry> web_seeds;
|
||||
for (peer_iterator i = m_connections.begin();
|
||||
i != m_connections.end(); ++i)
|
||||
{
|
||||
web_peer_connection* p
|
||||
= dynamic_cast<web_peer_connection*>(*i);
|
||||
if (!p) continue;
|
||||
web_seeds.insert(p->url());
|
||||
web_peer_connection* p = dynamic_cast<web_peer_connection*>(*i);
|
||||
if (p) web_seeds.insert(web_seed_entry(p->url(), web_seed_entry::url_seed));
|
||||
http_seed_connection* s = dynamic_cast<http_seed_connection*>(*i);
|
||||
if (s) web_seeds.insert(web_seed_entry(s->url(), web_seed_entry::http_seed));
|
||||
}
|
||||
|
||||
for (std::set<std::string>::iterator i = m_resolving_web_seeds.begin()
|
||||
for (std::set<web_seed_entry>::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<std::string> not_connected_web_seeds;
|
||||
std::vector<web_seed_entry> 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<std::string> 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<std::string> ret;
|
||||
for (std::set<web_seed_entry>::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()
|
||||
|
|
|
@ -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<std::string> torrent_handle::url_seeds() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
const static std::set<std::string> 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<std::string> torrent_handle::http_seeds() const
|
||||
{
|
||||
INVARIANT_CHECK;
|
||||
const static std::set<std::string> empty;
|
||||
TORRENT_FORWARD_RETURN(web_seeds(web_seed_entry::http_seed), empty);
|
||||
}
|
||||
|
||||
void torrent_handle::replace_trackers(
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -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<std::string>(m_parser.status_code())
|
||||
+ " " + m_parser.message();
|
||||
if (m_ses.m_alerts.should_post<url_seed_alert>())
|
||||
|
@ -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)
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue