merged back the asio development branch

This commit is contained in:
Arvid Norberg 2006-04-25 21:04:48 +00:00
parent 410af930a7
commit 41810b1166
71 changed files with 8271 additions and 4661 deletions

View File

@ -1,8 +1,10 @@
Written by Arvid Norberg. Copyright (c) 2003-2005
Written by Arvid Norberg. Copyright (c) 2003-2006
Contributions by Magnus Jonsson, Daniel Wallin and Cory Nelson
Lots of testing, suggestions and contributions by Massaroddel.
Big thanks to Michael Wojciechowski and Peter Koeleman for making the
autotools scripts.

View File

@ -1,3 +1,16 @@
* improved the piece picker performance and made it possible to download
popular pieces in sequence to improve disk performance.
* added the possibility to control upload and download limits per peer.
* fixed problem with re-requesting skipped pieces when peer was sending pieces
out of fifo-order.
* added support for http seeding (the GetRight protocol)
* renamed identifiers called 'id' in the public interface to support linking
with Objective.C++
* changed the extensions protocol to use the new one, which is also
implemented by uTorrent.
* factorized the peer_connection and added web_peer_connection which is
able to download from http-sources. (currently only for single file torrents).
* converted the network code to use asio
* made libtorrent build in vc7 (patches from Allen Zhao)
* fixed bug caused when binding outgoing connections to a non-local interface.
* add_torrent() will now throw if called while the session object is

16
Jamfile
View File

@ -23,6 +23,7 @@ project torrent
: requirements
<include>./include
<include>./asio/include
<include>./zlib
<include>$(BOOST_ROOT)
<variant>release:<define>NDEBUG
@ -33,8 +34,19 @@ project torrent
<library>/boost/filesystem//boost_filesystem/<link>static
<library>/boost/date_time//boost_date_time/<link>static
<threading>multi
<toolset>msvc:<define>_WIN32_WINNT=0x0500
# WIN32 makes sure the win32 socket api is used
# instead of win16
<toolset>msvc:<define>WIN32
# without WIN32_LEAN_AND_MEAN there will be conflicts between
# winsock.h and winsock2.h
<toolset>msvc:<define>WIN32_LEAN_AND_MEAN
# these compiler settings just makes the compiler standard conforming
<toolset>msvc:<cxxflags>/Zc:wchar_t
<toolset>msvc:<cxxflags>/Zc:forScope
# this should be defined when libtorrent is built as
# a dll. It will make sure the functions and classes
# are exported (GCC 4 and msvc)
<link>shared:<define>TORRENT_BUILDING_SHARED
: usage-requirements
@ -50,17 +62,17 @@ project torrent
SOURCES =
allocate_resources.cpp
alert.cpp
async_gethostbyname.cpp
entry.cpp
escape_string.cpp
file.cpp
identify_client.cpp
ip_filter.cpp
peer_connection.cpp
bt_peer_connection.cpp
web_peer_connection.cpp
piece_picker.cpp
policy.cpp
session.cpp
socket.cpp
stat.cpp
storage.cpp
torrent.cpp

View File

@ -4,13 +4,162 @@ docs/extension_protocol.html docs/udp_tracker_protocol.rst \
docs/udp_tracker_protocol.html docs/client_test.rst docs/client_test.html \
docs/unicode_support.png docs/client_test.png docs/style.css Jamfile project-root.jam \
m4/ac_cxx_namespaces.m4 m4/acx_pthread.m4 m4/ax_boost_date-time.m4 \
m4/ax_boost_filesystem.m4 m4/ax_boost_thread.m4 src/file_win.cpp libtorrent.pc
m4/ax_boost_filesystem.m4 m4/ax_boost_thread.m4 src/file_win.cpp libtorrent.pc \
asio/COPYING \
asio/INSTALL \
asio/LICENSE_1_0.txt \
asio/Makefile.am \
asio/README \
asio/THANKS \
asio/TODO \
asio/autogen.sh \
asio/boostify.pl \
asio/configure.ac \
asio/include/Makefile.am \
asio/include/asio.hpp \
asio/include/asio/basic_datagram_socket.hpp \
asio/include/asio/basic_deadline_timer.hpp \
asio/include/asio/basic_io_object.hpp \
asio/include/asio/basic_io_service.hpp \
asio/include/asio/basic_locking_dispatcher.hpp \
asio/include/asio/basic_socket.hpp \
asio/include/asio/basic_socket_acceptor.hpp \
asio/include/asio/basic_stream_socket.hpp \
asio/include/asio/buffer.hpp \
asio/include/asio/buffered_read_stream.hpp \
asio/include/asio/buffered_read_stream_fwd.hpp \
asio/include/asio/buffered_stream.hpp \
asio/include/asio/buffered_stream_fwd.hpp \
asio/include/asio/buffered_write_stream.hpp \
asio/include/asio/buffered_write_stream_fwd.hpp \
asio/include/asio/completion_condition.hpp \
asio/include/asio/datagram_socket_service.hpp \
asio/include/asio/deadline_timer.hpp \
asio/include/asio/deadline_timer_service.hpp \
asio/include/asio/error.hpp \
asio/include/asio/error_handler.hpp \
asio/include/asio/handler_alloc_hook.hpp \
asio/include/asio/io_service.hpp \
asio/include/asio/is_read_buffered.hpp \
asio/include/asio/is_write_buffered.hpp \
asio/include/asio/locking_dispatcher.hpp \
asio/include/asio/placeholders.hpp \
asio/include/asio/read.hpp \
asio/include/asio/service_factory.hpp \
asio/include/asio/socket_acceptor_service.hpp \
asio/include/asio/socket_base.hpp \
asio/include/asio/ssl.hpp \
asio/include/asio/stream_socket_service.hpp \
asio/include/asio/system_exception.hpp \
asio/include/asio/thread.hpp \
asio/include/asio/time_traits.hpp \
asio/include/asio/write.hpp \
asio/include/asio/detail/bind_handler.hpp \
asio/include/asio/detail/buffer_resize_guard.hpp \
asio/include/asio/detail/buffered_stream_storage.hpp \
asio/include/asio/detail/call_stack.hpp \
asio/include/asio/detail/consuming_buffers.hpp \
asio/include/asio/detail/epoll_reactor.hpp \
asio/include/asio/detail/event.hpp \
asio/include/asio/detail/fd_set_adapter.hpp \
asio/include/asio/detail/handler_alloc_helpers.hpp \
asio/include/asio/detail/hash_map.hpp \
asio/include/asio/detail/io_control.hpp \
asio/include/asio/detail/kqueue_reactor.hpp \
asio/include/asio/detail/locking_dispatcher.hpp \
asio/include/asio/detail/mutex.hpp \
asio/include/asio/detail/noncopyable.hpp \
asio/include/asio/detail/null_event.hpp \
asio/include/asio/detail/null_mutex.hpp \
asio/include/asio/detail/null_signal_blocker.hpp \
asio/include/asio/detail/null_thread.hpp \
asio/include/asio/detail/null_tss_ptr.hpp \
asio/include/asio/detail/pipe_select_interrupter.hpp \
asio/include/asio/detail/pop_options.hpp \
asio/include/asio/detail/posix_event.hpp \
asio/include/asio/detail/posix_mutex.hpp \
asio/include/asio/detail/posix_signal_blocker.hpp \
asio/include/asio/detail/posix_thread.hpp \
asio/include/asio/detail/posix_tss_ptr.hpp \
asio/include/asio/detail/push_options.hpp \
asio/include/asio/detail/reactive_deadline_timer_service.hpp \
asio/include/asio/detail/reactive_socket_service.hpp \
asio/include/asio/detail/reactor_op_queue.hpp \
asio/include/asio/detail/reactor_timer_queue.hpp \
asio/include/asio/detail/scoped_lock.hpp \
asio/include/asio/detail/select_interrupter.hpp \
asio/include/asio/detail/select_reactor.hpp \
asio/include/asio/detail/service_registry.hpp \
asio/include/asio/detail/signal_blocker.hpp \
asio/include/asio/detail/signal_init.hpp \
asio/include/asio/detail/socket_holder.hpp \
asio/include/asio/detail/socket_ops.hpp \
asio/include/asio/detail/socket_option.hpp \
asio/include/asio/detail/socket_select_interrupter.hpp \
asio/include/asio/detail/socket_types.hpp \
asio/include/asio/detail/task_io_service.hpp \
asio/include/asio/detail/thread.hpp \
asio/include/asio/detail/tss_ptr.hpp \
asio/include/asio/detail/win_event.hpp \
asio/include/asio/detail/win_iocp_io_service.hpp \
asio/include/asio/detail/win_iocp_operation.hpp \
asio/include/asio/detail/win_iocp_socket_service.hpp \
asio/include/asio/detail/win_local_free_on_block_exit.hpp \
asio/include/asio/detail/win_mutex.hpp \
asio/include/asio/detail/win_signal_blocker.hpp \
asio/include/asio/detail/win_thread.hpp \
asio/include/asio/detail/win_tss_ptr.hpp \
asio/include/asio/detail/winsock_init.hpp \
asio/include/asio/detail/wrapped_handler.hpp \
asio/include/asio/impl/read.ipp \
asio/include/asio/impl/write.ipp \
asio/include/asio/ip/address.hpp \
asio/include/asio/ip/basic_endpoint.hpp \
asio/include/asio/ip/tcp.hpp \
asio/include/asio/ip/udp.hpp \
asio/include/asio/ipv4/address.hpp \
asio/include/asio/ipv4/basic_endpoint.hpp \
asio/include/asio/ipv4/basic_host_resolver.hpp \
asio/include/asio/ipv4/host.hpp \
asio/include/asio/ipv4/host_resolver.hpp \
asio/include/asio/ipv4/host_resolver_service.hpp \
asio/include/asio/ipv4/multicast.hpp \
asio/include/asio/ipv4/tcp.hpp \
asio/include/asio/ipv4/udp.hpp \
asio/include/asio/ipv4/detail/host_resolver_service.hpp \
asio/include/asio/ipv4/detail/socket_option.hpp \
asio/include/asio/ipv6/address.hpp \
asio/include/asio/ipv6/basic_endpoint.hpp \
asio/include/asio/ipv6/multicast.hpp \
asio/include/asio/ipv6/tcp.hpp \
asio/include/asio/ipv6/udp.hpp \
asio/include/asio/ipv6/detail/socket_option.hpp \
asio/include/asio/ssl/basic_context.hpp \
asio/include/asio/ssl/context.hpp \
asio/include/asio/ssl/context_base.hpp \
asio/include/asio/ssl/context_service.hpp \
asio/include/asio/ssl/stream.hpp \
asio/include/asio/ssl/stream_base.hpp \
asio/include/asio/ssl/stream_service.hpp \
asio/include/asio/ssl/detail/openssl_context_service.hpp \
asio/include/asio/ssl/detail/openssl_init.hpp \
asio/include/asio/ssl/detail/openssl_operation.hpp \
asio/include/asio/ssl/detail/openssl_stream_service.hpp \
asio/include/asio/ssl/detail/openssl_types.hpp
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libtorrent.pc
check: test
test/test_hasher && test/test_bencoding && test/test_ip_filter && echo "tests done, all OK"
test/test_hasher \
&& test/test_bencoding \
&& test/test_ip_filter \
&& test/test_piece_picker \
&& test/test_storage \
&& test/test_metadata_extension \
&& test/test_buffer \
&& echo && echo && echo " **** all tests passed ****" && echo && echo
deb:
dpkg-buildpackage -rfakeroot -us -uc

View File

@ -1,7 +1,7 @@
AC_PREREQ(2.59)
AC_INIT(src/torrent.cpp)
AM_INIT_AUTOMAKE(libtorrent, 0.9.1)
AM_INIT_AUTOMAKE(libtorrent, 0.9.2)
AM_CONFIG_HEADER(config.h)

View File

@ -0,0 +1,247 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.3.9: http://docutils.sourceforge.net/" />
<title></title>
<meta name="author" content="Arvid Norberg, arvid&#64;rasterbar.com Ludvig Strigeus, ludde&#64;utorrent.com" />
<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<div class="document">
<table class="docinfo" frame="void" rules="none">
<col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<tr><th class="docinfo-name">Author:</th>
<td>Arvid Norberg, <a class="reference" href="mailto:arvid&#64;rasterbar.com">arvid&#64;rasterbar.com</a>
Ludvig Strigeus, <a class="last reference" href="mailto:ludde&#64;utorrent.com">ludde&#64;utorrent.com</a></td></tr>
</tbody>
</table>
<div class="section" id="extension-protocol-for-bittorrent">
<h1><a name="extension-protocol-for-bittorrent">extension protocol for bittorrent</a></h1>
<p>The intention of this protocol is to provide a simple and thin transport
for extensions to the bittorrent protocol. Supporting this protocol makes
it easy to add new extensions without interfering with the standard
bittorrent protocol or clients that don't support this extension or the
one you want to add.</p>
<p>To advertise to other clients that you support, one bit from the reserved
bytes is used.</p>
<dl class="docutils">
<dt>Right now, two bits have known usages.</dt>
<dd><ul class="first last simple">
<li>[7] &amp; 1 is used by Mainline for DHT support</li>
<li>[7] &amp; 2 is used by XBT client for peer-exchange support</li>
</ul>
</dd>
</dl>
<p>The bit selected for the extension protocol is bit 20 from the right (counting
starts at 0). So (reserved_byte[5] &amp; 0x10) is the expression to use for checking
if the client supports extended messaging.</p>
<p>Once support for the protocol is established, the client is supposed to
support 1 new message:</p>
<table border="1" class="docutils">
<colgroup>
<col width="86%" />
<col width="14%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">name</th>
<th class="head">id</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><tt class="docutils literal"><span class="pre">extended</span></tt></td>
<td>20</td>
</tr>
</tbody>
</table>
<p>This message is sent as any other bittorrent message, with a 4 byte length
prefix and a single byte identifying the message (the single byte being 20
in this case). At the start of the payload of the message, is a single byte
message identifier. This identifier can refer to different extension messages
and only one ID is specified, 0. If the ID is 0, the message is a handshake
message which is described below. The layout of a general <tt class="docutils literal"><span class="pre">extended</span></tt> message
follows (including the message headers used by the bittorrent protocol):</p>
<table border="1" class="docutils">
<colgroup>
<col width="15%" />
<col width="85%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">size</th>
<th class="head">description</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>uint32_t</td>
<td>length prefix. Specifies the number of bytes for the
entire message. (Big endian)</td>
</tr>
<tr><td>uint8_t</td>
<td>bittorrent message ID, = 20</td>
</tr>
<tr><td>uint8_t</td>
<td>extended message ID. 0 = handshake, &gt;0 = extended
message as specified by the handshake.</td>
</tr>
</tbody>
</table>
<div class="section" id="handshake-message">
<h2><a name="handshake-message">handshake message</a></h2>
<p>The payload of the handshake message is a bencoded dictionary. All items
in the dictionary are optional. Any unknown names should be ignored
by the client. All parts of the dictionary are case sensitive.
This is the defined item in the dictionary:</p>
<table border="1" class="docutils">
<colgroup>
<col width="11%" />
<col width="89%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">name</th>
<th class="head">description</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>m</td>
<td>Dictionary of supported extension messages which maps
names of extensions to identification numbers of each
extension. The only requirement on the identification
numbers is that no extensions share the same. Setting
an extension number to zero means that the extension is
not supported/disabled. The client should ignore any
extension names it doesn't recognize.</td>
</tr>
</tbody>
</table>
<p>Here are two other items that an implementation may choose to support:</p>
<table border="1" class="docutils">
<colgroup>
<col width="11%" />
<col width="89%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head">name</th>
<th class="head">description</th>
</tr>
</thead>
<tbody valign="top">
<tr><td>p</td>
<td>Local TCP listen port. Allows each side to learn about
the TCP port number of the other side. Note that there is
no need for the receiving side of the connection to send
this extension message, since its port number is already
known.</td>
</tr>
<tr><td>v</td>
<td>Client name and version (as an utf-8 string).
This is a much more reliable way of identifying the
client than relying on the peer id encoding.</td>
</tr>
</tbody>
</table>
<p>The handshake dictionary could also include extended handshake
information, such as support for encrypted headers or anything
imaginable.</p>
<p>An example of what the payload of a handshake message could look like:</p>
<table border="1" class="docutils">
<colgroup>
<col width="36%" />
<col width="64%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head" colspan="2">Dictionary</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><tt class="docutils literal"><span class="pre">m</span></tt></td>
<td><table border="1" class="first last docutils">
<colgroup>
<col width="88%" />
<col width="12%" />
</colgroup>
<thead valign="bottom">
<tr><th class="head" colspan="2">Dictionary</th>
</tr>
</thead>
<tbody valign="top">
<tr><td><tt class="docutils literal"><span class="pre">LT_metadata</span></tt></td>
<td>1</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">µT_PEX</span></tt></td>
<td>2</td>
</tr>
</tbody>
</table>
</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">p</span></tt></td>
<td>6881</td>
</tr>
<tr><td><tt class="docutils literal"><span class="pre">v</span></tt></td>
<td>&quot;µTorrent 1.2&quot;</td>
</tr>
</tbody>
</table>
<p>and in the encoded form:</p>
<p><tt class="docutils literal"><span class="pre">d1:md11:LT_metadatai1e6:µT_PEXi2ee1:pi6881e1:v13:\xc2\xb5Torrent</span> <span class="pre">1.2e</span></tt></p>
<p>To make sure the extension names do not collide by mistake, they should be
prefixed with the two (or one) character code that is used to identify the
client that introduced the extension. This applies for both the names of
extension messages, and for any additional information put inside the
top-level dictionary. All one and two byte identifiers are invalid to use
unless defined by this specification.</p>
<p>This message should be sent immediately after the standard bittorrent handshake
to any peer that supports this extension protocol. It is valid to send the
handshake message more than once during the lifetime of a connection,
the sending client should not be disconnected. An implementation may choose
to ignore the subsequent handshake messages (or parts of them).</p>
<p>Subsequent handshake messages can be used to enable/disable extensions
without restarting the connection. If a peer supports changing extensions
at run time, it should note that the <tt class="docutils literal"><span class="pre">m</span></tt> dictionary is additive.
It's enough that it contains the actual <em>CHANGES</em> to the extension list.
To disable the support for <tt class="docutils literal"><span class="pre">LT_metadata</span></tt> at run-time, without affecting
any other extensions, this message should be sent:
<tt class="docutils literal"><span class="pre">d11:LT_metadatai0ee</span></tt>.
As specified above, the value 0 is used to turn off an extension.</p>
<p>The extension IDs must be stored for every peer, becuase every peer may have
different IDs for the same extension.</p>
<p>This specification, deliberately, does not specify any extensions such as
peer-exchange or metadata exchange. This protocol is merely a transport
for the actual extensions to the bittorrent protocol and the extensions
named in the example above (such as <tt class="docutils literal"><span class="pre">p</span></tt>) are just examples of possible
extensions.</p>
</div>
<div class="section" id="rationale">
<h2><a name="rationale">rationale</a></h2>
<p>The reason why the extension messages' IDs would be defined in the handshake
is to avoid having a global registry somewhere, where ID's are assigned
global identifiers. Now the extensions have unique names.</p>
<p>If the client supporting the extensions can decide which numbers the messages
it receives will have, it means they are constants within that client. i.e.
they can be used in <tt class="docutils literal"><span class="pre">switch</span></tt> statements. It's easy for the other end to
store an array with the ID's we expect for each message and use that for
lookups each time it sends an extension message.</p>
<p>The reason for having a dictionary instead of having an array (using
implicitly assigned index numbers to the extensions) is that if a client
want to disable some extensions, the ID numbers would change, and it wouldn't
be able to use constants (and hence, not use them in a <tt class="docutils literal"><span class="pre">switch</span></tt>). If the
messages IDs would map directly to bittorrent message IDs, It would also make
it possible to map extensions in the handshake to existing extensions with
fixed message IDs.</p>
<p>The reasoning behind having a single byte as extended message identifier is
to follow the the bittorrent spec. with its single byte message identifiers.
It is also considered to be enough. It won't limit the total number of
extensions, only the number of extensions used simultaneously.</p>
<p>The reason for using single byte identifiers for the standardized handshake
identifiers is 1) The mainline DHT uses single byte identifiers. 2) Saves
bandwidth. The only advantage of longer messages is that it makes the
protocol more readable for a human, but the BT protocol wasn't designed to
be a human readable protocol, so why bother.</p>
</div>
</div>
</div>
</body>
</html>

179
docs/extension_protocol.rst Normal file
View File

@ -0,0 +1,179 @@
:Author: Arvid Norberg, arvid@rasterbar.com
Ludvig Strigeus, ludde@utorrent.com
extension protocol for bittorrent
=================================
The intention of this protocol is to provide a simple and thin transport
for extensions to the bittorrent protocol. Supporting this protocol makes
it easy to add new extensions without interfering with the standard
bittorrent protocol or clients that don't support this extension or the
one you want to add.
To advertise to other clients that you support, one bit from the reserved
bytes is used.
Right now, two bits have known usages.
* [7] & 1 is used by Mainline for DHT support
* [7] & 2 is used by XBT client for peer-exchange support
The bit selected for the extension protocol is bit 20 from the right (counting
starts at 0). So (reserved_byte[5] & 0x10) is the expression to use for checking
if the client supports extended messaging.
Once support for the protocol is established, the client is supposed to
support 1 new message:
+------------------------+----+
|name | id |
+========================+====+
|``extended`` | 20 |
+------------------------+----+
This message is sent as any other bittorrent message, with a 4 byte length
prefix and a single byte identifying the message (the single byte being 20
in this case). At the start of the payload of the message, is a single byte
message identifier. This identifier can refer to different extension messages
and only one ID is specified, 0. If the ID is 0, the message is a handshake
message which is described below. The layout of a general ``extended`` message
follows (including the message headers used by the bittorrent protocol):
+----------+---------------------------------------------------------+
| size | description |
+==========+=========================================================+
| uint32_t | length prefix. Specifies the number of bytes for the |
| | entire message. (Big endian) |
+----------+---------------------------------------------------------+
| uint8_t | bittorrent message ID, = 20 |
+----------+---------------------------------------------------------+
| uint8_t | extended message ID. 0 = handshake, >0 = extended |
| | message as specified by the handshake. |
+----------+---------------------------------------------------------+
handshake message
-----------------
The payload of the handshake message is a bencoded dictionary. All items
in the dictionary are optional. Any unknown names should be ignored
by the client. All parts of the dictionary are case sensitive.
This is the defined item in the dictionary:
+-------+-----------------------------------------------------------+
| name | description |
+=======+===========================================================+
| m | Dictionary of supported extension messages which maps |
| | names of extensions to identification numbers of each |
| | extension. The only requirement on the identification |
| | numbers is that no extensions share the same. Setting |
| | an extension number to zero means that the extension is |
| | not supported/disabled. The client should ignore any |
| | extension names it doesn't recognize. |
+-------+-----------------------------------------------------------+
Here are two other items that an implementation may choose to support:
+-------+-----------------------------------------------------------+
| name | description |
+=======+===========================================================+
| p | Local TCP listen port. Allows each side to learn about |
| | the TCP port number of the other side. Note that there is |
| | no need for the receiving side of the connection to send |
| | this extension message, since its port number is already |
| | known. |
+-------+-----------------------------------------------------------+
| v | Client name and version (as an utf-8 string). |
| | This is a much more reliable way of identifying the |
| | client than relying on the peer id encoding. |
+-------+-----------------------------------------------------------+
The handshake dictionary could also include extended handshake
information, such as support for encrypted headers or anything
imaginable.
An example of what the payload of a handshake message could look like:
+------------------------------------------------------+
| Dictionary |
+===================+==================================+
| ``m`` | +--------------------------+ |
| | | Dictionary | |
| | +======================+===+ |
| | | ``LT_metadata`` | 1 | |
| | +----------------------+---+ |
| | | ``µT_PEX`` | 2 | |
| | +----------------------+---+ |
| | |
+-------------------+----------------------------------+
| ``p`` | 6881 |
+-------------------+----------------------------------+
| ``v`` | "µTorrent 1.2" |
+-------------------+----------------------------------+
and in the encoded form:
``d1:md11:LT_metadatai1e6:µT_PEXi2ee1:pi6881e1:v13:\xc2\xb5Torrent 1.2e``
To make sure the extension names do not collide by mistake, they should be
prefixed with the two (or one) character code that is used to identify the
client that introduced the extension. This applies for both the names of
extension messages, and for any additional information put inside the
top-level dictionary. All one and two byte identifiers are invalid to use
unless defined by this specification.
This message should be sent immediately after the standard bittorrent handshake
to any peer that supports this extension protocol. It is valid to send the
handshake message more than once during the lifetime of a connection,
the sending client should not be disconnected. An implementation may choose
to ignore the subsequent handshake messages (or parts of them).
Subsequent handshake messages can be used to enable/disable extensions
without restarting the connection. If a peer supports changing extensions
at run time, it should note that the ``m`` dictionary is additive.
It's enough that it contains the actual *CHANGES* to the extension list.
To disable the support for ``LT_metadata`` at run-time, without affecting
any other extensions, this message should be sent:
``d11:LT_metadatai0ee``.
As specified above, the value 0 is used to turn off an extension.
The extension IDs must be stored for every peer, becuase every peer may have
different IDs for the same extension.
This specification, deliberately, does not specify any extensions such as
peer-exchange or metadata exchange. This protocol is merely a transport
for the actual extensions to the bittorrent protocol and the extensions
named in the example above (such as ``p``) are just examples of possible
extensions.
rationale
---------
The reason why the extension messages' IDs would be defined in the handshake
is to avoid having a global registry somewhere, where ID's are assigned
global identifiers. Now the extensions have unique names.
If the client supporting the extensions can decide which numbers the messages
it receives will have, it means they are constants within that client. i.e.
they can be used in ``switch`` statements. It's easy for the other end to
store an array with the ID's we expect for each message and use that for
lookups each time it sends an extension message.
The reason for having a dictionary instead of having an array (using
implicitly assigned index numbers to the extensions) is that if a client
want to disable some extensions, the ID numbers would change, and it wouldn't
be able to use constants (and hence, not use them in a ``switch``). If the
messages IDs would map directly to bittorrent message IDs, It would also make
it possible to map extensions in the handshake to existing extensions with
fixed message IDs.
The reasoning behind having a single byte as extended message identifier is
to follow the the bittorrent spec. with its single byte message identifiers.
It is also considered to be enough. It won't limit the total number of
extensions, only the number of extensions used simultaneously.
The reason for using single byte identifiers for the standardized handshake
identifiers is 1) The mainline DHT uses single byte identifiers. 2) Saves
bandwidth. The only advantage of longer messages is that it makes the
protocol more readable for a human, but the BT protocol wasn't designed to
be a human readable protocol, so why bother.

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,7 @@ following features:
* serves multiple torrents on a single port and a single thread
* supports http proxies and proxy authentication
* gzipped tracker-responses
* `HTTP seeding`_, as `specified by Michael Burford of GetRight`__.
* piece picking on block-level like in Azureus_ (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
@ -64,13 +65,19 @@ following features:
* ip filter
__ http://home.elp.rr.com/tur/multitracker-spec.txt
__ http://www.getright.com/seedtorrent.html
.. _Azureus: http://azureus.sourceforge.net
__ extension_protocol.html
__ udp_tracker_protocol.html
libtorrent is portable at least among Windows, MacOS X and other UNIX-systems. It uses Boost.Thread,
Boost.Filesystem, Boost.Date_time and various other boost libraries as well as zlib.
libtorrent is portable at least among Windows, MacOS X and other UNIX-systems.
It uses Boost.Thread, Boost.Filesystem, Boost.Date_time and various other
boost libraries as well as zlib_ (shipped) and asio_ (shipped). At least version
1.33.1 of boost is required.
.. _zlib: http://www.zlib.org
.. _asio: http://asio.sf.net
libtorrent has been successfully compiled and tested on:
@ -255,6 +262,10 @@ The ``Jamfile`` has the following build variants:
* ``debug_log`` - debug version with standard logging
* ``debug_vlog`` - debug version with verbose logging
When building the example client on windows, you need to build with
``link=static`` otherwise you may get unresolved external symbols for some
boost.program-options symbols.
building with autotools
-----------------------
@ -983,6 +994,7 @@ The ``torrent_info`` has the following synopsis::
void set_hash(int index, sha1_hash const& h);
void add_tracker(std::string const& url, int tier = 0);
void add_file(boost::filesystem::path file, size_type size);
void add_url_seed(std::string const& url);
typedef std::vector<file_entry>::const_iterator file_iterator;
typedef std::vector<file_entry>::const_reverse_iterator
@ -996,8 +1008,15 @@ The ``torrent_info`` has the following synopsis::
int num_files() const;
file_entry const& file_at(int index) const;
std::vector<file_slice> map_block(int piece, size_type offset
, int size) const;
peer_request map_file(int file_index, size_type file_offset
, int size) const;
std::vector<announce_entry> const& trackers() const;
std::vector<std::string> const& url_seeds() const;
size_type total_size() const;
size_type piece_length() const;
int num_pieces() const;
@ -1138,6 +1157,81 @@ If you need index-access to files you can use the ``num_files()`` and ``file_at(
to access files using indices.
map_block()
-----------
::
std::vector<file_slice> map_block(int piece, size_type offset
, int size) const;
This function will map a piece index, a byte offset within that piece and
a size (in bytes) into the corresponding files with offsets where that data
for that piece is supposed to be stored.
The file slice struct looks like this::
struct file_slice
{
int file_index;
size_type offset;
size_type size;
};
The ``file_index`` refers to the index of the file (in the torrent_info).
To get the path and filename, use ``file_at()`` and give the ``file_index``
as argument. The ``offset`` is the byte offset in the file where the range
starts, and ``size`` is the number of bytes this range is. The size + offset
will never be greater than the file size.
map_file()
----------
::
peer_request map_file(int file_index, size_type file_offset
, int size) const;
This function will map a range in a specific file into a range in the torrent.
The ``file_offset`` parameter is the offset in the file, given in bytes, where
0 is the start of the file.
The ``peer_request`` structure looks like this::
struct peer_request
{
int piece;
int start;
int length;
bool operator==(peer_request const& r) const;
};
``piece`` is the index of the piece in which the range starts.
``start`` is the offset within that piece where the range starts.
``length`` is the size of the range, in bytes.
The input range is assumed to be valid within the torrent. ``file_offset``
+ ``size`` is not allowed to be greater than the file size. ``file_index``
must refer to a valid file, i.e. it cannot be >= ``num_files()``.
url_seeds()
-----------
::
std::vector<std::string> const& url_seeds() const;
void add_url_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
supported for the url is http.
See `HTTP seeding`_ for more information.
print()
-------
@ -1202,6 +1296,8 @@ hash_for_piece() info_hash()
``hash_for_piece()`` takes a piece-index and returns the 20-bytes sha1-hash for that
piece and ``info_hash()`` returns the 20-bytes sha1-hash for the info-section of the
torrent file. For more information on the ``sha1_hash``, see the big_number_ class.
``info_hash()`` will only return a valid hash if the torrent_info was read from a
``.torrent`` file or if an ``entry`` was created from it (through ``create_torrent``).
name() comment() creation_date() creator()
@ -1250,7 +1346,7 @@ Its declaration looks like this::
entry write_resume_data() const;
std::vector<char> const& metadata() const;
void force_reannounce() const;
void connect_peer(address const& adr) const;
void connect_peer(asio::ipv4::tcp::endpoint const& adr) const;
void set_tracker_login(std::string const& username
, std::string const& password) const;
@ -1258,11 +1354,17 @@ Its declaration looks like this::
std::vector<announce_entry> const& trackers() const;
void replace_trackers(std::vector<announce_entry> const&);
void add_url_seed(std::string const& url);
void set_ratio(float ratio) const;
void set_max_uploads(int max_uploads) const;
void set_max_connections(int max_connections) const;
void set_upload_limit(int limit) const;
void set_download_limit(int limit) const;
void set_peer_upload_limit(asio::ipv4::tcp::endpoint ip, int limit) const;
void set_peer_download_limit(asio::ipv4::tcp::endpoint ip, int limit) const;
void use_interface(char const* net_interface) const;
void pause() const;
@ -1336,7 +1438,7 @@ connect_peer()
::
void connect_peer(address const& adr) const;
void connect_peer(asio::ipv4::tcp::endpoint const& adr) const;
``connect_peer()`` is a way to manually connect to peers that one believe is a part of the
torrent. If the peer does not respond, or is not a member of this torrent, it will simply
@ -1377,6 +1479,16 @@ Note that setting a higher limit on a torrent then the global limit (``session::
will not override the global rate limit. The torrent can never upload more than the global rate
limit.
set_peer_upload_limit() set_peer_download_limit()
-------------------------------------------------
::
void set_peer_upload_limit(asio::ipv4::tcp::endpoint ip, int limit) const;
void set_peer_download_limit(asio::ipv4::tcp::endpoint ip, int limit) const;
Works like ``set_upload_limit`` and ``set_download_limit`` respectively, but controls individual
peer instead of the whole torrent.
pause() resume() is_paused()
----------------------------
@ -1445,6 +1557,21 @@ replace it. If you want an immediate effect, you have to call
`force_reannounce()`_.
add_url_seed()
--------------
::
void add_url_seed(std::string const& url);
``add_url_seed()`` adds another url to the torrent's list of url seeds. If the
given url already exists in that list, the call has no effect. The torrent
will connect to the server and try to download pieces from it, unless it's
paused, queued, checking or seeding.
See `HTTP seeding`_ for more information.
use_interface()
---------------
@ -1649,6 +1776,7 @@ It contains the following fields::
size_type total_payload_upload;
size_type total_failed_bytes;
size_type total_redundant_bytes;
float download_rate;
float upload_rate;
@ -1662,6 +1790,8 @@ It contains the following fields::
int num_incomplete;
const std::vector<bool>* pieces;
int num_pieces;
size_type total_done;
size_type total_wanted_done;
size_type total_wanted;
@ -1727,10 +1857,24 @@ data), these counters ignore any protocol overhead.
has failed the piece hash test. In other words, this is just how much crap that
has been downloaded.
``total_redundant_bytes`` is the number of bytes that has been downloaded even
though that data already was downloaded. The reason for this is that in some
situations the same data can be downloaded by mistake. When libtorrent sends
requests to a peer, and the peer doesn't send a response within a certain
timeout, libtorrent will re-request that block. Another situation when
libtorrent will re-request blocks is when the requests it sends out are not
replyed in FIFO-order (it will re-request blocks that are skipped by an out of
order block). This is supposed to be as low as possible.
``pieces`` is the bitmask that represents which pieces we have (set to true) and
the pieces we don't have. It's a pointer and may be set to 0 if the torrent isn't
downloading or seeding.
``num_pieces`` is the number of pieces that has been downloaded. It is equivalent
to: ``std::accumulate(pieces->begin(), pieces->end())``. So you don't have to
count yourself. This can be used to see if anything has updated since last time
if you want to keep a graph of the pieces up to date.
``download_rate`` and ``upload_rate`` are the total rates for all peers for this
torrent. These will usually have better precision than summing the rates from
all peers. The rates are given as the number of bytes per second. The
@ -1793,22 +1937,23 @@ It contains the following fields::
remote_choked = 0x8,
supports_extensions = 0x10,
local_connection = 0x20,
connecting = 0x40,
queued = 0x80
handshake = 0x40,
connecting = 0x80,
queued = 0x100
};
unsigned int flags;
address ip;
asio::ipv4::tcp::endpoint ip;
float up_speed;
float down_speed;
float payload_up_speed;
float payload_down_speed;
size_type total_download;
size_type total_upload;
peer_id id;
peer_id pid;
std::vector<bool> pieces;
bool seed;
int upload_limit;
int upload_ceiling;
int download_limit;
size_type load_balancing;
@ -1819,6 +1964,15 @@ It contains the following fields::
int downloading_block_index;
int downloading_progress;
int downloading_total;
std::string client;
enum
{
standard_bittorrent = 0,
web_seed = 1
};
int connection_type;
};
The ``flags`` attribute tells you in which state the peer is. It is set to
@ -1838,10 +1992,14 @@ any combination of the enums above. The following table describes each flag:
+-------------------------+-------------------------------------------------------+
| ``local_connection`` | The connection was initiated by us, the peer has a |
| | listen port open, and that port is the same as in the |
| | address_ of this peer. If this flag is not set, this |
| | address of this peer. If this flag is not set, this |
| | peer connection was opened by this peer connecting to |
| | us. |
+-------------------------+-------------------------------------------------------+
| ``handshake`` | The connection is opened, and waiting for the |
| | handshake. Until the handshake is done, the peer |
| | cannot be identified. |
+-------------------------+-------------------------------------------------------+
| ``connecting`` | The connection is in a half-open state (i.e. it is |
| | being connected). |
+-------------------------+-------------------------------------------------------+
@ -1852,8 +2010,10 @@ any combination of the enums above. The following table describes each flag:
__ extension_protocol.html
The ``ip`` field is the IP-address to this peer. Its type is a wrapper around the
actual address and the port number. See address_ class.
The ``ip`` field is the IP-address to this peer. The type is an asio endpoint. For
more info, see the asio_ documentation.
.. _asio: http://asio.sf.net
``up_speed`` and ``down_speed`` contains the current upload and download speed
we have to and from this peer (including any protocol messages). The transfer rates
@ -1864,7 +2024,7 @@ These figures are updated aproximately once every second.
from and uploaded to this peer. These numbers do not include the protocol chatter, but only
the payload data.
``id`` is the peer's id as used in the bit torrent protocol. This id can be used to
``pid`` is the peer's id as used in the bit torrent protocol. This id can be used to
extract 'fingerprints' from the peer. Sometimes it can tell you which client the peer
is using. See identify_client()_
@ -1878,9 +2038,8 @@ or if the peer miss that piece (set to false).
peer every second. It may be -1 if there's no limit. The upload limits of all peers
should sum up to the upload limit set by ``session::set_upload_limit``.
``upload_ceiling`` is the current maximum allowed upload rate given the cownload
rate and share ratio. If the global upload rate is inlimited, the ``upload_limit``
for every peer will be the same as their ``upload_ceiling``.
``download_limit`` is the number of bytes per second this peer is allowed to
receive. -1 means it's unlimited.
``load_balancing`` is a measurment of the balancing of free download (that we get)
and free upload that we give. Every peer gets a certain amount of free upload, but
@ -1903,44 +2062,14 @@ block (or sub-piece) that is being downloaded. ``downloading_progress`` is the n
of bytes of this block we have received from the peer, and ``downloading_total`` is
the total number of bytes in this block.
``client`` is a string describing the software at the other end of the connection.
In some cases this information is not available, then it will contain a string
that may give away something about which software is running in the other end.
In the case of a web seed, the server type and version will be a part of this
string.
address
=======
The ``address`` class represents a name of a network endpoint (usually referred to as
IP-address) and a port number. This is the same thing as a ``sockaddr_in`` would contain.
Its declaration looks like this::
class address
{
public:
address();
address(unsigned char a
, unsigned char b
, unsigned char c
, unsigned char d
, unsigned short port);
address(unsigned int addr, unsigned short port);
address(const std::string& addr, unsigned short port);
address(const address& a);
~address();
std::string as_string() const;
unsigned int ip() const;
unsigned short port() const;
bool operator<(const address& a) const;
bool operator!=(const address& a) const;
bool operator==(const address& a) const;
};
It is less-than comparable to make it possible to use it as a key in a map. ``as_string()`` may block
while it does the DNS lookup, it returns a string that points to the address represented by the object.
``ip()`` will return the 32-bit ip-address as an integer. ``port()`` returns the port number.
``connection_type`` can currently be one of ``standard_bittorrent`` or
``web_seed``. These are currently the only implemented protocols.
http_settings
=============
@ -1960,7 +2089,8 @@ that will be sent to the tracker. The user-agent is a good way to identify your
std::string proxy_login;
std::string proxy_password;
std::string user_agent;
int tracker_timeout;
int tracker_completion_timeout;
int tracker_receive_timeout;
int tracker_maximum_response_length;
};
@ -1978,10 +2108,18 @@ empty, the http proxy will be tried to be used without authentication.
``user_agent`` this is the client identification to the tracker. It will
be followed by the string "(libtorrent)" to identify that this library
is being used. This should be set to your client's name and version number.
This name will not only be used when making HTTP requests, but also when
sending extended headers to peers that support that extension.
``tracker_timeout`` is the number of seconds the tracker connection will
wait until it considers the tracker to have timed-out. Default value is 10
seconds.
``tracker_completion_timeout`` is the number of seconds the tracker
connection will wait from when it sent the request until it considers the
tracker to have timed-out. Default value is 60 seconds.
``tracker_receive_timeout`` is the number of seconds to wait to receive
any data from the tracker. If no data is received for this number of
seconds, the tracker will be considered as having timed out. If a tracker
is down, this is the kind of timeout that will occur. The default value
is 20 seconds.
``tracker_maximum_response_length`` is the maximum number of bytes in a
tracker response. If a response size passes this number it will be rejected
@ -1998,6 +2136,8 @@ ip_filter
The ``ip_filter`` class is a set of rules that uniquely categorizes all
ip addresses as allowed or disallowed. The default constructor creates
a single rule that allowes all addresses (0.0.0.0 - 255.255.255.255).
The ``address`` type here is ``asio::ipv4::address``. It can also be
accessed as ``libtorrent::address``.
::
@ -2112,8 +2252,9 @@ This class creates sha1-hashes. Its declaration looks like this::
{
public:
hasher();
hasher(char const* data, unsigned int len);
void update(const char* data, unsigned int len);
void update(char const* data, unsigned int len);
sha1_hash final();
void reset();
};
@ -2125,6 +2266,9 @@ create the hash in memory. You can feed the hasher parts of it at a time. When
You have fed the hasher with all the data, you call ``final()`` and it
will return the sha1-hash of the data.
The constructor that takes a ``char const*`` and an integer will construct the
sha1 context and feed it the data passed in.
If you want to reuse the hasher object once you have created a hash, you have to
call ``reset()`` to reinitialize it.
@ -2147,7 +2291,7 @@ This is the class declaration::
std::string to_string() const;
char id[2];
char name[2];
char major_version;
char minor_version;
char revision_version;
@ -2458,6 +2602,25 @@ the tracker. It is generated with severity level ``warning``.
};
url_seed_alert
--------------
This alert is generated when a HTTP seed name lookup fails. This alert is
generated as severity level ``warning``.
It contains ``url`` to the HTTP seed that failed along with an error message.
::
struct url_seed_alert: alert
{
url_seed_alert(std::string const& h, const std::string& msg);
virtual std::auto_ptr<alert> clone() const;
std::string url;
};
hash_failed_alert
-----------------
@ -2493,12 +2656,12 @@ to the torrent that this peer was a member of.
struct peer_ban_alert: alert
{
peer_ban_alert(
address const& pip
asio::ipv4::tcp::endpoint const& pip
, torrent_handle h
, const std::string& msg);
virtual std::auto_ptr<alert> clone() const;
address ip;
asio::ipv4::tcp::endpoint ip;
torrent_handle handle;
};
@ -2515,12 +2678,12 @@ is generated as severity level ``debug``.
struct peer_error_alert: alert
{
peer_error_alert(
address const& pip
asio::ipv4::tcp::endpoint const& pip
, peer_id const& pid
, const std::string& msg);
virtual std::auto_ptr<alert> clone() const;
address ip;
asio::ipv4::tcp::endpoint ip;
peer_id id;
};
@ -2540,13 +2703,13 @@ is a handle to the torrent the peer is a member of. ``
invalid_request_alert(
peer_request const& r
, torrent_handle const& h
, address const& send
, asio::ipv4::tcp::endpoint const& send
, peer_id const& pid
, std::string const& msg);
virtual std::auto_ptr<alert> clone() const;
torrent_handle handle;
address ip;
asio::ipv4::tcp::endpoint ip;
peer_request request;
peer_id id;
};
@ -3292,6 +3455,20 @@ Don't have metadata:
| | | doesn't have any metadata. |
+-----------+---------------+----------------------------------------+
HTTP seeding
------------
The HTTP seed extension implements `this specification`__.
The libtorrent implementation 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
filename checks
===============

View File

@ -14,5 +14,5 @@ make_torrent_LDADD = $(top_builddir)/src/libtorrent.la
simple_client_SOURCES = simple_client.cpp
simple_client_LDADD = $(top_builddir)/src/libtorrent.la
AM_CXXFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ @PTHREAD_CFLAGS@
AM_CXXFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/asio/include @DEBUGFLAGS@ @PTHREAD_CFLAGS@
AM_LDFLAGS= ${LDLAGS} -L./ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@

View File

@ -226,11 +226,11 @@ std::string progress_bar(float progress, int width, char const* code = "33")
return std::string(bar.begin(), bar.end());
}
char const* peer_index(libtorrent::address addr, std::vector<libtorrent::peer_info> const& peers)
char const* peer_index(libtorrent::tcp::endpoint addr, std::vector<libtorrent::peer_info> const& peers)
{
using namespace libtorrent;
std::vector<peer_info>::const_iterator i = std::find_if(peers.begin()
, peers.end(), boost::bind(std::equal_to<address>()
, peers.end(), boost::bind(std::equal_to<libtorrent::tcp::endpoint>()
, bind(&peer_info::ip, _1), addr));
if (i == peers.end()) return "+";
@ -274,7 +274,11 @@ void print_peer_info(std::ostream& out, std::vector<libtorrent::peer_info> const
out << progress_bar(0.f, 15);
}
if (i->flags & peer_info::connecting)
if (i->flags & peer_info::handshake)
{
out << esc("31") << " waiting for handshake" << esc("0") << "\n";
}
else if (i->flags & peer_info::connecting)
{
out << esc("31") << " connecting to peer" << esc("0") << "\n";
}
@ -284,7 +288,7 @@ void print_peer_info(std::ostream& out, std::vector<libtorrent::peer_info> const
}
else
{
out << " " << identify_client(i->id) << "\n";
out << " " << i->client << "\n";
}
}
}
@ -569,9 +573,9 @@ int main(int ac, char* av[])
int a, b, c, d;
char dummy;
in >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy;
address start(a, b, c, d, 0);
address start((a << 24) + (b << 16) + (c << 8) + d);
in >> a >> dummy >> b >> dummy >> c >> dummy >> d >> dummy;
address last(a, b, c, d, 0);
address last((a << 24) + (b << 16) + (c << 8) + d);
int flags;
in >> flags;
if (flags <= 127) flags = ip_filter::blocked;
@ -681,6 +685,7 @@ int main(int ac, char* av[])
// loop through the alert queue to see if anything has happened.
std::auto_ptr<alert> a;
a = ses.pop_alert();
std::string now = to_simple_string(second_clock::universal_time());
while (a.get())
{
if (torrent_finished_alert* p = dynamic_cast<torrent_finished_alert*>(a.get()))
@ -693,20 +698,22 @@ int main(int ac, char* av[])
// all finished downloades are
// moved into this directory
//p->handle.move_storage("finished");
events.push_back(
p->handle.get_torrent_info().name() + ": " + a->msg());
events.push_back(now + ": "
+ p->handle.get_torrent_info().name() + ": " + a->msg());
}
else if (peer_error_alert* p = dynamic_cast<peer_error_alert*>(a.get()))
{
events.push_back(identify_client(p->id) + ": " + a->msg());
events.push_back(now + ": " + identify_client(p->pid)
+ ": " + a->msg());
}
else if (invalid_request_alert* p = dynamic_cast<invalid_request_alert*>(a.get()))
{
events.push_back(identify_client(p->id) + ": " + a->msg());
events.push_back(now + ": " + identify_client(p->pid)
+ ": " + a->msg());
}
else
{
events.push_back(a->msg());
events.push_back(now + ": " + a->msg());
}
if (events.size() >= 10) events.pop_front();

View File

@ -73,9 +73,12 @@ int main(int argc, char* argv[])
using namespace libtorrent;
using namespace boost::filesystem;
path::default_name_check(no_check);
if (argc != 4)
{
std::cerr << "usage: make_torrent <output torrent-file> <announce url> <file or directory to create torrent from>\n";
std::cerr << "usage: make_torrent <output torrent-file> "
"<announce url> <file or directory to create torrent from>\n";
return 1;
}

View File

@ -1,7 +1,6 @@
pkginclude_HEADERS = libtorrent/alert.hpp \
libtorrent/alert_types.hpp \
libtorrent/allocate_resources.hpp \
libtorrent/async_gethostbyname.hpp \
libtorrent/bencode.hpp \
libtorrent/buffer.hpp \
libtorrent/config.hpp \
@ -19,6 +18,8 @@ libtorrent/io.hpp \
libtorrent/ip_filter.hpp \
libtorrent/peer.hpp \
libtorrent/peer_connection.hpp \
libtorrent/bt_peer_connection.hpp \
libtorrent/web_peer_connection.hpp \
libtorrent/peer_id.hpp \
libtorrent/peer_info.hpp \
libtorrent/peer_request.hpp \
@ -27,6 +28,7 @@ libtorrent/piece_picker.hpp \
libtorrent/policy.hpp \
libtorrent/resource_request.hpp \
libtorrent/session.hpp \
libtorrent/session_settings.hpp \
libtorrent/size_type.hpp \
libtorrent/socket.hpp \
libtorrent/stat.hpp \

View File

@ -57,6 +57,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/config.hpp"
#define TORRENT_MAX_ALERT_TYPES 10
namespace libtorrent {
class TORRENT_EXPORT alert
@ -112,13 +114,13 @@ namespace libtorrent {
template<
class Handler
, BOOST_PP_ENUM_PARAMS(5, class T)
, BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, class T)
>
void handle_alert_dispatch(
const std::auto_ptr<alert>& alert_
, const Handler& handler
, const std::type_info& typeid_
, BOOST_PP_ENUM_BINARY_PARAMS(5, T, *p))
, BOOST_PP_ENUM_BINARY_PARAMS(TORRENT_MAX_ALERT_TYPES, T, *p))
{
if (typeid_ == typeid(T0))
handler(*static_cast<T0*>(alert_.get()));
@ -127,7 +129,7 @@ namespace libtorrent {
alert_
, handler
, typeid_
, BOOST_PP_ENUM_SHIFTED_PARAMS(5, p), (void_*)0
, BOOST_PP_ENUM_SHIFTED_PARAMS(TORRENT_MAX_ALERT_TYPES, p), (void_*)0
);
}
@ -136,7 +138,7 @@ namespace libtorrent {
const std::auto_ptr<alert>& alert_
, const Handler& handler
, const std::type_info& typeid_
, BOOST_PP_ENUM_PARAMS(5, void_* BOOST_PP_INTERCEPT))
, BOOST_PP_ENUM_PARAMS(TORRENT_MAX_ALERT_TYPES, void_* BOOST_PP_INTERCEPT))
{
throw unhandled_alert();
}
@ -144,7 +146,7 @@ namespace libtorrent {
} // namespace detail
template<
BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(5, class T, detail::void_)
BOOST_PP_ENUM_PARAMS_WITH_A_DEFAULT(TORRENT_MAX_ALERT_TYPES, class T, detail::void_)
>
struct TORRENT_EXPORT handle_alert
{
@ -159,7 +161,7 @@ namespace libtorrent {
alert_
, handler
, typeid(*alert_)
, BOOST_PP_ENUM(5, ALERT_POINTER_TYPE, _)
, BOOST_PP_ENUM(TORRENT_MAX_ALERT_TYPES, ALERT_POINTER_TYPE, _)
);
#undef ALERT_POINTER_TYPE

View File

@ -124,7 +124,7 @@ namespace libtorrent
struct TORRENT_EXPORT peer_ban_alert: alert
{
peer_ban_alert(address const& pip, torrent_handle h, std::string const& msg)
peer_ban_alert(tcp::endpoint const& pip, torrent_handle h, std::string const& msg)
: alert(alert::info, msg)
, ip(pip)
, handle(h)
@ -133,30 +133,30 @@ namespace libtorrent
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new peer_ban_alert(*this)); }
address ip;
tcp::endpoint ip;
torrent_handle handle;
};
struct TORRENT_EXPORT peer_error_alert: alert
{
peer_error_alert(address const& pip, peer_id const& pid, std::string const& msg)
peer_error_alert(tcp::endpoint const& pip, peer_id const& pid_, std::string const& msg)
: alert(alert::debug, msg)
, ip(pip)
, id(pid)
, pid(pid_)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new peer_error_alert(*this)); }
address ip;
peer_id id;
tcp::endpoint ip;
peer_id pid;
};
struct TORRENT_EXPORT chat_message_alert: alert
{
chat_message_alert(
const torrent_handle& h
, const address& sender
, const tcp::endpoint& sender
, const std::string& msg)
: alert(alert::critical, msg)
, handle(h)
@ -167,7 +167,7 @@ namespace libtorrent
{ return std::auto_ptr<alert>(new chat_message_alert(*this)); }
torrent_handle handle;
address ip;
tcp::endpoint ip;
};
struct TORRENT_EXPORT invalid_request_alert: alert
@ -175,23 +175,23 @@ namespace libtorrent
invalid_request_alert(
peer_request const& r
, torrent_handle const& h
, address const& sender
, peer_id const& pid
, tcp::endpoint const& sender
, peer_id const& pid_
, std::string const& msg)
: alert(alert::debug, msg)
, handle(h)
, ip(sender)
, request(r)
, id(pid)
, pid(pid_)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new invalid_request_alert(*this)); }
torrent_handle handle;
address ip;
tcp::endpoint ip;
peer_request request;
peer_id id;
peer_id pid;
};
struct TORRENT_EXPORT torrent_finished_alert: alert
@ -209,6 +209,21 @@ namespace libtorrent
torrent_handle handle;
};
struct TORRENT_EXPORT url_seed_alert: alert
{
url_seed_alert(
const std::string& url_
, const std::string& msg)
: alert(alert::warning, msg)
, url(url_)
{}
virtual std::auto_ptr<alert> clone() const
{ return std::auto_ptr<alert>(new url_seed_alert(*this)); }
std::string url;
};
struct TORRENT_EXPORT file_error_alert: alert
{
file_error_alert(

View File

@ -44,7 +44,6 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent
{
class socket;
class peer_connection;
class torrent;
@ -59,7 +58,7 @@ namespace libtorrent
/*
void allocate_resources(
int resources
, std::map<boost::shared_ptr<socket>, boost::shared_ptr<peer_connection> >& connections
, std::map<boost::shared_ptr<socket>, boost::intrusive_ptr<peer_connection> >& connections
, resource_request peer_connection::* res);
*/
void allocate_resources(
@ -69,7 +68,7 @@ namespace libtorrent
void allocate_resources(
int resources
, std::map<address, peer_connection*>& connections
, std::map<tcp::endpoint, peer_connection*>& connections
, resource_request peer_connection::* res);
}

View File

@ -0,0 +1,295 @@
/*
Copyright (c) 2003, 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_BT_PEER_CONNECTION_HPP_INCLUDED
#define TORRENT_BT_PEER_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/noncopyable.hpp>
#include <boost/array.hpp>
#include <boost/date_time/posix_time/posix_time.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/allocate_resources.hpp"
#include "libtorrent/peer_request.hpp"
#include "libtorrent/piece_block_progress.hpp"
#include "libtorrent/config.hpp"
namespace libtorrent
{
class torrent;
namespace detail
{
struct session_impl;
}
class TORRENT_EXPORT bt_peer_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
bt_peer_connection(
detail::session_impl& ses
, boost::weak_ptr<torrent> t
, boost::shared_ptr<stream_socket> s
, tcp::endpoint const& remote);
// with this constructor we have been contacted and we still don't
// know which torrent the connection belongs to
bt_peer_connection(
detail::session_impl& ses
, boost::shared_ptr<stream_socket> s);
~bt_peer_connection();
// called from the main loop when this connection has any
// work to do.
void on_sent(asio::error const& error
, std::size_t bytes_transferred);
void on_receive(asio::error const& error
, std::size_t bytes_transferred);
virtual void get_peer_info(peer_info& p) const;
bool support_extensions() const { return m_supports_extensions; }
bool supports_extension(extension_index ex) const
{ return m_extension_messages[ex] > 0; }
bool has_metadata() const;
// the message handlers are called
// each time a recv() returns some new
// data, the last time it will be called
// is when the entire packet has been
// received, then it will no longer
// be called. i.e. most handlers need
// to check how much of the packet they
// have received before any processing
void on_keepalive();
void on_choke(int received);
void on_unchoke(int received);
void on_interested(int received);
void on_not_interested(int received);
void on_have(int received);
void on_bitfield(int received);
void on_request(int received);
void on_piece(int received);
void on_cancel(int received);
void on_dht_port(int received);
void on_extended(int received);
void on_extended_handshake();
void on_chat();
void on_metadata();
void on_peer_exchange();
typedef void (bt_peer_connection::*message_handler)(int received);
// 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);
void write_bitfield(std::vector<bool> const& bitfield);
void write_have(int index);
void write_piece(peer_request const& r);
void write_handshake();
void write_extensions();
void write_chat_message(const std::string& msg);
void write_metadata(std::pair<int, int> req);
void write_metadata_request(std::pair<int, int> req);
void write_keepalive();
void write_dht_port(int listen_port);
void on_connected() {}
void on_tick();
#ifndef NDEBUG
void check_invariant() const;
boost::posix_time::ptime m_last_choke;
#endif
private:
bool dispatch_message(int received);
// 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;
// if we don't have all metadata
// this function will request a part of it
// from this peer
void request_metadata();
enum state
{
read_protocol_length = 0,
read_protocol_string,
read_info_hash,
read_peer_id,
read_packet_size,
read_packet
};
std::string m_client_version;
state m_state;
// the timeout in seconds
int m_timeout;
enum message_type
{
// standard messages
msg_choke = 0,
msg_unchoke,
msg_interested,
msg_not_interested,
msg_have,
msg_bitfield,
msg_request,
msg_piece,
msg_cancel,
msg_dht_port,
// extension protocol message
msg_extended = 20,
num_supported_messages
};
static const message_handler m_message_handler[num_supported_messages];
// this is a queue of ranges that describes
// where in the send buffer actual payload
// data is located. This is currently
// only used to be able to gather statistics
// seperately on payload and protocol data.
struct range
{
range(int s, int l)
: start(s)
, length(l)
{
assert(s >= 0);
assert(l > 0);
}
int start;
int length;
};
static bool range_below_zero(const range& r)
{ return r.start < 0; }
std::deque<range> m_payloads;
// this is set to true if the handshake from
// the peer indicated that it supports the
// extension protocol
bool m_supports_extensions;
bool m_supports_dht_port;
static const char* extension_names[num_supported_extensions];
// contains the indices of the extension messages for each extension
// supported by the other end. A value of <= 0 means that the extension
// is not supported.
int m_extension_messages[num_supported_extensions];
// this is set to the current time each time we get a
// "I don't have metadata" message.
boost::posix_time::ptime m_no_metadata;
// this is set to the time when we last sent
// a request for metadata to this peer
boost::posix_time::ptime m_metadata_request;
// this is set to true when we send a metadata
// request to this peer, and reset to false when
// we receive a reply to our request.
bool m_waiting_metadata_request;
// if we're waiting for a metadata request
// this was the request we sent
std::pair<int, int> m_last_metadata_request;
// the number of bytes of metadata we have received
// so far from this per, only counting the current
// request. Any previously finished requests
// that have been forwarded to the torrent object
// do not count.
int m_metadata_progress;
#ifndef NDEBUG
bool m_in_constructor;
#endif
};
}
#endif // TORRENT_BT_PEER_CONNECTION_HPP_INCLUDED

View File

@ -49,6 +49,14 @@ public:
, end(end)
{}
char operator[](int index) const
{
assert(begin + index < end);
return begin[index];
}
int left() const { assert(end > begin); return end - begin; }
char* begin;
char* end;
};
@ -60,6 +68,14 @@ public:
, end(end)
{}
char operator[](int index) const
{
assert(begin + index < end);
return begin[index];
}
int left() const { assert(end > begin); return end - begin; }
char const* begin;
char const* end;
};
@ -230,10 +246,11 @@ inline void buffer::insert(char const* first, char const* last)
inline void buffer::erase(std::size_t n)
{
assert(!m_empty);
INVARIANT_CHECK;
if (n == 0) return;
assert(!m_empty);
#ifndef NDEBUG
int prev_size = size();
#endif

View File

@ -123,7 +123,7 @@ namespace libtorrent
m_file.open(dir / filename, std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out));
log("\n\n\n*** starting log ***\n");
}
virtual void log(const char* text) { assert(text); m_file << text; }
virtual void log(const char* text) { assert(text); m_file << text; m_file.flush(); }
boost::filesystem::ofstream m_file;
};

View File

@ -40,6 +40,7 @@ namespace libtorrent
{
std::string TORRENT_EXPORT unescape_string(std::string const& s);
std::string TORRENT_EXPORT escape_string(const char* str, int len);
std::string TORRENT_EXPORT escape_path(const char* str, int len);
}
#endif // TORRENT_ESCAPE_STRING_HPP_INCLUDED

View File

@ -114,7 +114,7 @@ namespace libtorrent
size_type write(const char*, size_type num_bytes);
size_type read(char*, size_type num_bytes);
void seek(size_type pos, seek_mode m = begin);
size_type seek(size_type pos, seek_mode m = begin);
size_type tell();
private:

View File

@ -55,14 +55,14 @@ namespace libtorrent
assert(revision >= 0);
assert(tag >= 0);
assert(std::strlen(id_string) == 2);
id[0] = id_string[0];
id[1] = id_string[1];
name[0] = id_string[0];
name[1] = id_string[1];
}
std::string to_string() const
{
std::stringstream s;
s << "-" << id[0] << id[1]
s << "-" << name[0] << name[1]
<< version_to_char(major_version)
<< version_to_char(minor_version)
<< version_to_char(revision_version)
@ -70,7 +70,7 @@ namespace libtorrent
return s.str();
}
char id[2];
char name[2];
char major_version;
char minor_version;
char revision_version;

View File

@ -45,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/shared_ptr.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/cstdint.hpp>
#include <boost/tuple/tuple.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
@ -56,19 +57,62 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/peer_id.hpp"
#include "libtorrent/peer.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/async_gethostbyname.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/buffer.hpp"
namespace libtorrent
{
class TORRENT_EXPORT http_tracker_connection: public tracker_connection
class http_parser
{
public:
http_parser();
template <class T>
T header(char const* key) const;
std::string const& protocol() const { return m_protocol; }
int status_code() const { return m_status_code; }
std::string message() const { return m_server_message; }
buffer::const_interval get_body();
bool header_finished() const { return m_state == read_body; }
bool finished() const { return m_finished; }
boost::tuple<int, int> incoming(buffer::const_interval recv_buffer);
int body_start() const { return m_body_start_pos; }
private:
int m_recv_pos;
int m_status_code;
std::string m_protocol;
std::string m_server_message;
int m_content_length;
enum { plain, gzip } m_content_encoding;
enum { read_status, read_header, read_body } m_state;
std::map<std::string, std::string> m_header;
buffer::const_interval m_recv_buffer;
int m_body_start_pos;
bool m_finished;
};
template <class T>
T http_parser::header(char const* key) const
{
std::map<std::string, std::string>::const_iterator i
= m_header.find(key);
if (i == m_header.end()) return T();
return boost::lexical_cast<T>(i->second);
}
class TORRENT_EXPORT http_tracker_connection
: public tracker_connection
{
friend class tracker_manager;
public:
http_tracker_connection(
tracker_manager& man
demuxer& d
, tracker_manager& man
, tracker_request const& req
, std::string const& hostname
, unsigned short port
@ -76,18 +120,29 @@ namespace libtorrent
, boost::weak_ptr<request_callback> c
, const http_settings& stn
, std::string const& password = "");
virtual bool tick();
virtual bool send_finished() const
{ return m_send_buffer.empty(); }
virtual tracker_request const& tracker_req() const
{ return m_req; }
private:
boost::intrusive_ptr<http_tracker_connection> self()
{ return boost::intrusive_ptr<http_tracker_connection>(this); }
void on_response();
void init_send_buffer(
std::string const& hostname
, std::string const& request);
void name_lookup(asio::error const& error);
void connected(asio::error const& error);
void sent(asio::error const& error);
void receive(asio::error const& error
, std::size_t bytes_transferred);
virtual void on_timeout();
void parse(const entry& e);
peer_entry extract_peer_info(const entry& e);
@ -98,15 +153,14 @@ namespace libtorrent
int m_content_length;
std::string m_location;
dns_lookup m_name_lookup;
boost::shared_ptr<socket> m_socket;
host_resolver m_name_lookup;
host m_host;
int m_port;
boost::shared_ptr<stream_socket> m_socket;
int m_recv_pos;
std::vector<char> m_buffer;
std::string m_send_buffer;
// used for time outs
boost::posix_time::ptime m_request_time;
std::string m_server_message;
std::string m_server_protocol;
@ -117,8 +171,11 @@ namespace libtorrent
// server string in http-reply
std::string m_server;
bool m_timed_out;
};
}
#endif // TORRENT_HTTP_TRACKER_CONNECTION_HPP_INCLUDED

View File

@ -30,17 +30,23 @@ POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef IP_FILTER_HPP
#define IP_FILTER_HPP
#ifndef TORRENT_IP_FILTER_HPP
#define TORRENT_IP_FILTER_HPP
#include "libtorrent/socket.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/socket.hpp"
#include <set>
#include <iostream>
namespace libtorrent
{
inline bool operator<=(address const& lhs
, address const& rhs)
{
return lhs < rhs || lhs == rhs;
}
class TORRENT_EXPORT ip_filter
{
public:

View File

@ -44,16 +44,16 @@ namespace libtorrent
{
std::string ip;
int port;
peer_id id;
peer_id pid;
bool operator==(const peer_entry& p) const
{
return id == p.id;
return pid == p.pid;
}
bool operator<(const peer_entry& p) const
{
return id < p.id;
return pid < p.pid;
}
};

View File

@ -46,6 +46,7 @@ POSSIBILITY OF SUCH DAMAGE.
#endif
#include <boost/smart_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/noncopyable.hpp>
#include <boost/array.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
@ -68,6 +69,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/peer_request.hpp"
#include "libtorrent/piece_block_progress.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/session.hpp"
// TODO: each time a block is 'taken over'
// from another peer. That peer must be given
@ -82,6 +84,9 @@ namespace libtorrent
struct session_impl;
}
TORRENT_EXPORT void intrusive_ptr_add_ref(peer_connection const*);
TORRENT_EXPORT void intrusive_ptr_release(peer_connection const*);
struct TORRENT_EXPORT protocol_error: std::runtime_error
{
protocol_error(const std::string& msg): std::runtime_error(msg) {};
@ -89,27 +94,26 @@ namespace libtorrent
class TORRENT_EXPORT peer_connection
: public boost::noncopyable
, public boost::enable_shared_from_this<peer_connection>
{
friend class invariant_access;
friend void intrusive_ptr_add_ref(peer_connection const*);
friend void intrusive_ptr_release(peer_connection const*);
public:
// this is the constructor where the we are teh active part.
// 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
peer_connection(
detail::session_impl& ses
, selector& sel
, torrent* t
, boost::shared_ptr<libtorrent::socket> s
, address const& remote);
, boost::weak_ptr<torrent> t
, boost::shared_ptr<stream_socket> s
, tcp::endpoint const& remote);
// with this constructor we have been contacted and we still don't
// know which torrent the connection belongs to
peer_connection(
detail::session_impl& ses
, selector& sel
, boost::shared_ptr<libtorrent::socket> s);
, boost::shared_ptr<stream_socket> s);
// this function is called once the torrent associated
// with this peer connection has retrieved the meta-
@ -117,17 +121,15 @@ namespace libtorrent
// this is called from the constructor.
void init();
~peer_connection();
void set_upload_limit(int limit);
void set_download_limit(int limit);
virtual ~peer_connection();
// this adds an announcement in the announcement queue
// it will let the peer know that we have the given piece
void announce_piece(int index);
// called from the main loop when this connection has any
// work to do.
void send_data();
void receive_data();
// tells if this connection has data it want to send
// and has enough upload bandwidth quota left to send it.
bool can_write() const;
@ -140,20 +142,14 @@ namespace libtorrent
// will send a keep-alive message to the peer
void keep_alive();
const peer_id& id() const { return m_peer_id; }
peer_id const& pid() const { return m_peer_id; }
void set_pid(const peer_id& pid) { m_peer_id = pid; }
bool has_piece(int i) const;
const std::deque<piece_block>& download_queue() const;
const std::deque<piece_block>& request_queue() const;
const std::deque<peer_request>& upload_queue() const;
// 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() const;
bool is_interesting() const { return m_interesting; }
bool is_choked() const { return m_choked; }
@ -164,21 +160,19 @@ namespace libtorrent
// may be zero if the connection is an incoming connection
// and it hasn't received enough information to determine
// which torrent it should be associated with
torrent* associated_torrent() const { return m_attached_to_torrent?m_torrent:0; }
bool verify_piece(const peer_request& p) const;
boost::weak_ptr<torrent> associated_torrent() const
{ return m_torrent; }
const stat& statistics() const { return m_statistics; }
void add_stat(size_type downloaded, size_type uploaded);
// is called once every second by the main loop
void second_tick();
void second_tick(float tick_interval);
boost::shared_ptr<libtorrent::socket> get_socket() const { return m_socket; }
address const& remote() const { return m_remote; }
boost::shared_ptr<stream_socket> get_socket() const { return m_socket; }
tcp::endpoint const& remote() const { return m_remote; }
const peer_id& get_peer_id() const { return m_peer_id; }
const std::vector<bool>& get_bitfield() const;
std::vector<bool> const& get_bitfield() const;
// this will cause this peer_connection to be disconnected.
// what it does is that it puts a reference to it in
@ -190,7 +184,7 @@ namespace libtorrent
// this is called when the connection attempt has succeeded
// and the peer_connection is supposed to set m_connecting
// to false, and stop monitor writability
void connection_complete();
void on_connection_complete(asio::error const& e);
// returns true if this connection is still waiting to
// finish the connection attempt
@ -207,19 +201,15 @@ namespace libtorrent
// tcp connections.
void connect();
// This is called for every peer right after the upload
// bandwidth has been distributed among them
// It will reset the used bandwidth to 0 and
// possibly add or remove the peer's socket
// from the socket monitor
// It will reset the used bandwidth to 0.
void reset_upload_quota();
// free upload.
size_type total_free_upload() const;
void add_free_upload(size_type free_upload);
// trust management.
void received_valid_data();
void received_invalid_data();
@ -227,8 +217,6 @@ namespace libtorrent
size_type share_diff() const;
bool support_extensions() const { return m_supports_extensions; }
// a connection is local if it was initiated by us.
// if it was an incoming connection, it is remote
bool is_local() const { return m_active; }
@ -236,24 +224,12 @@ namespace libtorrent
void set_failed() { m_failed = true; }
bool failed() const { return m_failed; }
int desired_queue_size() const { return m_desired_queue_size; }
#ifdef TORRENT_VERBOSE_LOGGING
boost::shared_ptr<logger> m_logger;
#endif
enum extension_index
{
extended_chat_message,
extended_metadata_message,
extended_peer_exchange_message,
extended_listen_port_message,
num_supported_extensions
};
bool supports_extension(extension_index ex) const
{ return m_extension_messages[ex] != -1; }
bool has_metadata() const;
// the message handlers are called
// each time a recv() returns some new
// data, the last time it will be called
@ -262,26 +238,18 @@ namespace libtorrent
// be called. i.e. most handlers need
// to check how much of the packet they
// have received before any processing
void on_choke(int received);
void on_unchoke(int received);
void on_interested(int received);
void on_not_interested(int received);
void on_have(int received);
void on_bitfield(int received);
void on_request(int received);
void on_piece(int received);
void on_cancel(int received);
void on_dht_port(int received);
void on_extension_list(int received);
void on_extended(int received);
void on_chat();
void on_metadata();
void on_peer_exchange();
void on_listen_port();
typedef void (peer_connection::*message_handler)(int received);
void incoming_keepalive();
void incoming_choke();
void incoming_unchoke();
void incoming_interested();
void incoming_not_interested();
void incoming_have(int piece_index);
void incoming_bitfield(std::vector<bool> const& bitfield);
void incoming_request(peer_request const& r);
void incoming_piece(peer_request const& p, char const* data);
void incoming_piece_fragment();
void incoming_cancel(peer_request const& r);
void incoming_dht_port(int listen_port);
// the following functions appends messages
// to the send buffer
@ -289,15 +257,10 @@ namespace libtorrent
void send_unchoke();
void send_interested();
void send_not_interested();
void send_request(piece_block block);
void send_cancel(piece_block block);
void send_bitfield();
void send_have(int index);
void send_handshake();
void send_extensions();
void send_chat_message(const std::string& msg);
void send_metadata(std::pair<int, int> req);
void send_metadata_request(std::pair<int, int> req);
// adds a block to the request queue
void add_request(piece_block const& b);
void cancel_request(piece_block const& b);
// how much bandwidth we're using, how much we want,
// and how much we are allowed to use.
@ -309,59 +272,104 @@ namespace libtorrent
boost::posix_time::ptime m_last_choke;
#endif
virtual void get_peer_info(peer_info& p) const = 0;
// 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.
virtual boost::optional<piece_block_progress>
downloading_piece_progress() const
{
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << "downloading_piece_progress() dispatched to the base class!\n";
#endif
return boost::optional<piece_block_progress>();
}
protected:
virtual void write_choke() = 0;
virtual void write_unchoke() = 0;
virtual void write_interested() = 0;
virtual void write_not_interested() = 0;
virtual void write_request(peer_request const& r) = 0;
virtual void write_cancel(peer_request const& r) = 0;
virtual void write_have(int index) = 0;
virtual void write_keepalive() = 0;
virtual void write_piece(peer_request const& r) = 0;
virtual void on_connected() = 0;
virtual void on_tick() {}
virtual void on_receive(asio::error const& error
, std::size_t bytes_transferred) = 0;
virtual void on_sent(asio::error const& error
, std::size_t bytes_transferred) = 0;
void send_buffer(char const* begin, char const* end);
buffer::interval allocate_send_buffer(int size);
int send_buffer_size() const
{
return (int)m_send_buffer[0].size()
+ (int)m_send_buffer[1].size();
}
buffer::const_interval receive_buffer() const
{
return buffer::const_interval(&m_recv_buffer[0]
, &m_recv_buffer[0] + m_recv_pos);
}
void cut_receive_buffer(int size, int packet_size);
void reset_recv_buffer(int packet_size);
int packet_size() const { return m_packet_size; }
bool packet_finished() const
{
assert(m_recv_pos <= m_packet_size);
return m_packet_size == m_recv_pos;
}
void setup_send();
void setup_receive();
void attach_to_torrent(sha1_hash const& ih);
bool verify_piece(peer_request const& p) const;
// statistics about upload and download speeds
// and total amount of uploads and downloads for
// this peer
stat m_statistics;
// a back reference to the session
// the peer belongs to.
detail::session_impl& m_ses;
boost::intrusive_ptr<peer_connection> self()
{ return boost::intrusive_ptr<peer_connection>(this); }
// called from the main loop when this connection has any
// work to do.
void on_send_data(asio::error const& error
, std::size_t bytes_transferred);
void on_receive_data(asio::error const& error
, std::size_t bytes_transferred);
private:
void fill_send_buffer();
void send_block_requests();
bool dispatch_message(int received);
// if we don't have all metadata
// this function will request a part of it
// from this peer
void request_metadata();
// this is called each time this peer generates some
// data to be sent. It will add this socket to
// the writibility monitor in the selector.
void send_buffer_updated();
// is used during handshake
enum state
{
read_protocol_length = 0,
read_protocol_string,
read_info_hash,
read_peer_id,
read_packet_size,
read_packet
};
state m_state;
// the timeout in seconds
int m_timeout;
enum message_type
{
// standard messages
msg_choke = 0,
msg_unchoke,
msg_interested,
msg_not_interested,
msg_have,
msg_bitfield,
msg_request,
msg_piece,
msg_cancel,
msg_dht_port,
// extension protocol message
msg_extension_list = 20,
msg_extended,
num_supported_messages
};
static const message_handler m_message_handler[num_supported_messages];
// the time when we last got a part of a
// piece packet from this peer
boost::posix_time::ptime m_last_piece;
int m_packet_size;
int m_recv_pos;
@ -369,73 +377,37 @@ namespace libtorrent
// this is the buffer where data that is
// to be sent is stored until it gets
// consumed by send()
// std::vector<char> m_send_buffer;
buffer m_send_buffer;
// this is a queue of ranges that describes
// where in the send buffer actual payload
// data is located. This is currently
// only used to be able to gather statistics
// seperately on payload and protocol data.
struct range
{
range(int s, int l)
: start(s)
, length(l)
{
assert(s >= 0);
assert(l > 0);
}
int start;
int length;
};
static bool range_below_zero(const range& r)
{ return r.start < 0; }
std::deque<range> m_payloads;
// consumed by send(). Since asio requires
// the memory buffer that is given to async.
// operations to remain valid until the operation
// finishes, there has to be two buffers. While
// waiting for a async_write operation on one
// buffer, the other is used to write data to
// be queued up.
buffer m_send_buffer[2];
// the current send buffer is the one to write to.
// (m_current_send_buffer + 1) % 2 is the
// buffer we're currently waiting for.
int m_current_send_buffer;
// timeouts
boost::posix_time::ptime m_last_receive;
boost::posix_time::ptime m_last_sent;
// the selector is used to add and remove this
// peer's socket from the writability monitor list.
selector& m_selector;
boost::shared_ptr<libtorrent::socket> m_socket;
address m_remote;
boost::shared_ptr<stream_socket> m_socket;
tcp::endpoint m_remote;
// this is the torrent this connection is
// associated with. If the connection is an
// incoming conncetion, this is set to zero
// until the info_hash is received. Then it's
// set to the torrent it belongs to.
torrent* m_torrent;
// this is set to false until the peer_id
// is received from the other end. Or it is
// true from the start if the conenction
// was actively opened from our side.
bool m_attached_to_torrent;
// a back reference to the session
// the peer belongs to.
detail::session_impl& m_ses;
boost::weak_ptr<torrent> m_torrent;
// is true if it was we that connected to the peer
// and false if we got an incomming connection
// could be considered: true = local, false = remote
bool m_active;
// this is true as long as this peer's
// socket is added to the selector to
// monitor writability. Each time we do
// something that generates data to be
// sent to this peer, we check this and
// if it's not added to the selector we
// add it. (this is done in send_buffer_updated())
bool m_writability_monitored;
bool m_readability_monitored;
// remote peer's id
peer_id m_peer_id;
@ -459,11 +431,6 @@ namespace libtorrent
// this peer
bool m_failed;
// this is set to true if the handshake from
// the peer indicated that it supports the
// extension protocol
bool m_supports_extensions;
// the pieces the other end have
std::vector<bool> m_have_piece;
@ -477,11 +444,6 @@ namespace libtorrent
// from this peer
std::deque<peer_request> m_requests;
// a list of pieces that have become available
// and should be announced as available to
// the peer
std::vector<int> m_announce_queue;
// the blocks we have reserved in the piece
// picker and will send to this peer.
std::deque<piece_block> m_request_queue;
@ -490,10 +452,9 @@ namespace libtorrent
// from this peer
std::deque<piece_block> m_download_queue;
// statistics about upload and download speeds
// and total amount of uploads and downloads for
// this peer
stat m_statistics;
// the number of request we should queue up
// at the remote end.
int m_desired_queue_size;
// the amount of data this peer has been given
// as free upload. This is distributed from
@ -511,8 +472,12 @@ namespace libtorrent
// considered a bad peer and will be banned.
int m_trust_points;
static const char* extension_names[num_supported_extensions];
int m_extension_messages[num_supported_extensions];
// if this is true, this peer is assumed to handle all piece
// requests in fifo order. All skipped blocks are re-requested
// immediately instead of having a looser requirement
// where blocks can be sent out of order. The default is to
// allow non-fifo order.
bool m_assume_fifo;
// the number of invalid piece-requests
// we have got from this peer. If the request
@ -523,10 +488,6 @@ namespace libtorrent
// by sending choke, unchoke.
int m_num_invalid_requests;
// the time when we last got a part of a
// piece packet from this peer
boost::posix_time::ptime m_last_piece;
// this is true if this connection has been added
// to the list of connections that will be closed.
bool m_disconnecting;
@ -539,23 +500,6 @@ namespace libtorrent
// this peer the last time.
boost::posix_time::ptime m_became_uninteresting;
// this is set to the current time each time we get a
// "I don't have metadata" message.
boost::posix_time::ptime m_no_metadata;
// this is set to the time when we last sent
// a request for metadata to this peer
boost::posix_time::ptime m_metadata_request;
// this is set to true when we send a metadata
// request to this peer, and reset to false when
// we receive a reply to our request.
bool m_waiting_metadata_request;
// if we're waiting for a metadata request
// this was the request we sent
std::pair<int, int> m_last_metadata_request;
// this is true until this socket has become
// writable for the first time (i.e. the
// connection completed). While connecting
@ -571,12 +515,24 @@ namespace libtorrent
// connections.
bool m_queued;
// the number of bytes of metadata we have received
// so far from this per, only counting the current
// request. Any previously finished requests
// that have been forwarded to the torrent object
// do not count.
int m_metadata_progress;
// these are true when there's a asynchronous write
// or read operation running.
bool m_writing;
// this is the number of bytes sent to the socket last
// time it was invoked. This is compared against the
// bytes_transferred in the callback function that tells
// how much actually was sent. Then the quota can be
// corrected according to the actual number of bytes sent
int m_last_write_size;
bool m_reading;
int m_last_read_size;
// reference counter for intrusive_ptr
mutable int m_refs;
#ifndef NDEBUG
public:
bool m_in_constructor;
#endif
};
}

View File

@ -52,22 +52,23 @@ namespace libtorrent
remote_choked = 0x8,
supports_extensions = 0x10,
local_connection = 0x20,
connecting = 0x40,
queued = 0x80
handshake = 0x40,
connecting = 0x80,
queued = 0x100
};
unsigned int flags;
address ip;
tcp::endpoint ip;
float up_speed;
float down_speed;
float payload_up_speed;
float payload_down_speed;
size_type total_download;
size_type total_upload;
peer_id id;
peer_id pid;
std::vector<bool> pieces;
bool seed; // true if this is a seed
int upload_limit; // from peer_connection
int upload_ceiling; // from the global upload limiter
int upload_limit;
int download_limit;
size_type load_balancing;
@ -89,6 +90,15 @@ namespace libtorrent
int downloading_block_index;
int downloading_progress;
int downloading_total;
std::string client;
enum
{
standard_bittorrent = 0,
web_seed = 1
};
int connection_type;
};
}

View File

@ -49,13 +49,13 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/peer_id.hpp"
#include "libtorrent/socket.hpp"
#include "libtorrent/session_settings.hpp"
#include "libtorrent/config.hpp"
namespace libtorrent
{
class torrent;
class address;
class peer_connection;
struct TORRENT_EXPORT piece_block
@ -93,7 +93,7 @@ namespace libtorrent
block_info(): num_downloads(0) {}
// the peer this block was requested or
// downloaded from
address peer;
tcp::endpoint peer;
// the number of times this block has been downloaded
int num_downloads;
};
@ -110,8 +110,11 @@ namespace libtorrent
block_info info[max_blocks_per_piece];
};
piece_picker(int blocks_per_piece,
int total_num_blocks);
piece_picker(int blocks_per_piece
, int total_num_blocks
, int sequenced_download_threshold);
void set_sequenced_download_threshold(int sequenced_download_threshold);
// this is called before any other method is called
// after the local files has been checked.
@ -161,12 +164,12 @@ namespace libtorrent
// decides to download a piece, it must mark it as being downloaded
// itself, by using the mark_as_downloading() member function.
// THIS IS DONE BY THE peer_connection::send_request() MEMBER FUNCTION!
// The last argument is the address of the peer that we'll download
// The last argument is the tcp::endpoint of the peer that we'll download
// from.
void pick_pieces(const std::vector<bool>& pieces
, std::vector<piece_block>& interesting_blocks
, int num_pieces, bool prefer_whole_pieces
, address peer) const;
, tcp::endpoint peer) const;
// returns true if any client is currently downloading this
// piece-block, or if it's queued for downloading by some client
@ -175,8 +178,8 @@ namespace libtorrent
bool is_finished(piece_block block) const;
// marks this piece-block as queued for downloading
void mark_as_downloading(piece_block block, const address& peer);
void mark_as_finished(piece_block block, const address& peer);
void mark_as_downloading(piece_block block, tcp::endpoint const& peer);
void mark_as_finished(piece_block block, tcp::endpoint const& peer);
// if a piece had a hash-failure, it must be restored and
// made available for redownloading
@ -195,12 +198,12 @@ namespace libtorrent
// the hash-check yet
int unverified_blocks() const;
void get_downloaders(std::vector<address>& d, int index) const;
void get_downloaders(std::vector<tcp::endpoint>& d, int index) const;
const std::vector<downloading_piece>& get_download_queue() const
{ return m_downloads; }
boost::optional<address> get_downloader(piece_block block) const;
boost::optional<tcp::endpoint> get_downloader(piece_block block) const;
// the number of filtered pieces we don't have
int num_filtered() const { return m_num_filtered; }
@ -252,10 +255,20 @@ namespace libtorrent
enum { we_have_index = 0x3ffff };
bool operator!=(piece_pos p)
int priority(int limit) const
{
return peer_count >= (unsigned)limit ? limit : peer_count;
}
bool ordered(int limit) const
{
return peer_count >= (unsigned)limit;
}
bool operator!=(piece_pos p) const
{ return index != p.index || peer_count != p.peer_count; }
bool operator==(piece_pos p)
bool operator==(piece_pos p) const
{ return index == p.index && peer_count == p.peer_count; }
};
@ -280,7 +293,7 @@ namespace libtorrent
, std::vector<piece_block>& interesting_blocks
, std::vector<piece_block>& backup_blocks
, int num_blocks, bool prefer_whole_pieces
, address peer) const;
, tcp::endpoint peer) const;
// this vector contains all pieces we don't have.
@ -321,6 +334,10 @@ namespace libtorrent
// the number of pieces we have that also are filtered
int m_num_have_filtered;
// the required popularity of a piece in order to download
// it in sequence instead of random order.
int m_sequenced_download_threshold;
};
inline int piece_picker::blocks_in_piece(int index) const

View File

@ -57,7 +57,6 @@ namespace libtorrent
{
class torrent;
class address;
class peer_connection;
enum
@ -84,7 +83,7 @@ namespace libtorrent
// this is called once for every peer we get from
// the tracker
void peer_from_tracker(const address& remote, const peer_id& id);
void peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid);
// called when an incoming connection is accepted
// return false if the connection closed
@ -131,7 +130,7 @@ namespace libtorrent
{
enum connection_type { not_connectable,connectable };
peer(const address& ip, connection_type t);
peer(const tcp::endpoint& ip, connection_type t);
size_type total_download() const;
size_type total_upload() const;
@ -140,7 +139,7 @@ namespace libtorrent
// if it was a remote (incoming) connection, type is
// set thereafter. If it was a peer we got from the
// tracker, type is set to local_connection.
address id;
tcp::endpoint ip;
connection_type type;
// the time when this peer was optimistically unchoked

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2003, Magnus Jonsson
Copyright (c) 2003, Magnus Jonsson, Arvid Norberg
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -60,7 +60,8 @@ namespace libtorrent
{
assert(given <= max);
assert(given >= min);
assert(given >= used);
// TODO: TEMP!
// assert(given >= used);
return given - used;
}

View File

@ -48,6 +48,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/tuple/tuple.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/thread.hpp>
#include <boost/thread/recursive_mutex.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
@ -69,6 +70,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/piece_block_progress.hpp"
#include "libtorrent/ip_filter.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/session_settings.hpp"
#if !defined(NDEBUG) && defined(_MSC_VER)
# include <float.h>
@ -77,9 +79,17 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent
{
class torrent;
enum extension_index
{
extended_handshake,
extended_chat_message,
extended_metadata_message,
extended_peer_exchange_message,
num_supported_extensions
};
namespace detail
{
// workaround for microsofts
@ -123,7 +133,7 @@ namespace libtorrent
std::vector<int> piece_map;
std::vector<piece_picker::downloading_piece> unfinished_pieces;
std::vector<address> peers;
std::vector<tcp::endpoint> peers;
entry resume_data;
// this is true if this torrent is being processed (checked)
@ -170,12 +180,10 @@ namespace libtorrent
struct session_impl: boost::noncopyable
{
friend class invariant_access;
// TODO: maybe this should be changed to a sorted vector
// using lower_bound?
typedef std::map<boost::shared_ptr<socket>, boost::shared_ptr<peer_connection> >
typedef std::map<boost::shared_ptr<stream_socket>, boost::intrusive_ptr<peer_connection> >
connection_map;
typedef std::map<sha1_hash, boost::shared_ptr<torrent> > torrent_map;
typedef std::deque<boost::shared_ptr<peer_connection> >
typedef std::deque<boost::intrusive_ptr<peer_connection> >
connection_queue;
session_impl(
@ -187,10 +195,16 @@ namespace libtorrent
void open_listen_port();
void async_accept();
void on_incoming_connection(boost::shared_ptr<stream_socket> const& s
, boost::weak_ptr<socket_acceptor> const& as, asio::error const& e);
// must be locked to access the data
// in this struct
mutable boost::mutex m_mutex;
torrent* find_torrent(const sha1_hash& info_hash);
typedef boost::recursive_mutex mutex_t;
mutable mutex_t m_mutex;
boost::weak_ptr<torrent> find_torrent(const sha1_hash& info_hash);
peer_id const& get_peer_id() const { return m_peer_id; }
tracker_manager m_tracker_manager;
@ -201,14 +215,10 @@ namespace libtorrent
// is reached.
void process_connection_queue();
void connection_failed(boost::shared_ptr<socket> const& s
, address const& a, char const* message);
// this is where all active sockets are stored.
// the selector can sleep while there's no activity on
// them
selector m_selector;
void close_connection(boost::intrusive_ptr<peer_connection> const& p);
void connection_completed(boost::intrusive_ptr<peer_connection> const& p);
void connection_failed(boost::shared_ptr<stream_socket> const& s
, tcp::endpoint const& a, char const* message);
// this maps sockets to their peer_connection
// object. It is the complete list of all connected
@ -225,13 +235,7 @@ namespace libtorrent
// waiting for one slot in the half-open queue to open up.
connection_queue m_connection_queue;
// this is a list of iterators into the m_connections map
// that should be disconnected as soon as possible.
// It is used to delay disconnections to avoid troubles
// in loops that iterate over them.
std::vector<boost::shared_ptr<peer_connection> > m_disconnect_peer;
// filters incomming connections
// filters incoming connections
ip_filter m_ip_filter;
// the peer id that is generated at the start of the session
@ -250,18 +254,24 @@ namespace libtorrent
// if the ip is set to zero, it means
// that we should let the os decide which
// interface to listen on
address m_listen_interface;
tcp::endpoint m_listen_interface;
boost::shared_ptr<socket> m_listen_socket;
// this is where all active sockets are stored.
// the selector can sleep while there's no activity on
// them
demuxer m_selector;
boost::shared_ptr<socket_acceptor> m_listen_socket;
// the entries in this array maps the
// extension index (as specified in peer_connection)
bool m_extension_enabled[peer_connection::num_supported_extensions];
bool m_extension_enabled[num_supported_extensions];
bool extensions_enabled() const;
// the settings for the client
http_settings m_settings;
session_settings m_settings;
http_settings m_http_settings;
// set to true when the session object
// is being destructed and the thread
@ -293,8 +303,11 @@ namespace libtorrent
// does the actual disconnections
// that are queued up in m_disconnect_peer
void purge_connections();
void second_tick(asio::error const& e);
boost::posix_time::ptime m_last_tick;
// the timer used to fire the second_tick
deadline_timer m_timer;
#ifndef NDEBUG
void check_invariant(const char *place = 0);
#endif
@ -330,7 +343,7 @@ namespace libtorrent
{
public:
session(fingerprint const& print = fingerprint("LT", 0, 9, 1, 0));
session(fingerprint const& print = fingerprint("LT", 0, 9, 2, 0));
session(
fingerprint const& print
, std::pair<int, int> listen_port_range
@ -348,7 +361,7 @@ namespace libtorrent
, bool compact_mode = true
, int block_size = 16 * 1024);
// TODO: depricated, this is for backwards compatibility only
// TODO: deprecated, this is for backwards compatibility only
torrent_handle add_torrent(
entry const& e
, boost::filesystem::path const& save_path
@ -370,11 +383,11 @@ namespace libtorrent
session_status status() const;
void enable_extension(peer_connection::extension_index i);
void enable_extension(extension_index i);
void disable_extensions();
void set_ip_filter(ip_filter const& f);
void set_peer_id(peer_id const& id);
void set_peer_id(peer_id const& pid);
void set_key(int key);
bool is_listening() const;

View File

@ -0,0 +1,68 @@
/*
Copyright (c) 2003, 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_SESSION_SETTINGS_HPP_INCLUDED
#define TORRENT_SESSION_SETTINGS_HPP_INCLUDED
namespace libtorrent
{
struct TORRENT_EXPORT session_settings
{
session_settings()
: piece_timeout(120)
, request_queue_time(3.f)
, sequenced_download_threshold(7)
{}
// the number of seconds from a request is sent until
// it times out if no piece response is returned.
int piece_timeout;
// the length of the request queue given in the number
// of seconds it should take for the other end to send
// all the pieces. i.e. the actual number of requests
// depends on the download rate and this number.
float request_queue_time;
// this is the limit on how popular a piece has to be
// (popular == inverse of rarity) to be downloaded
// in sequence instead of in random (rarest first) order.
// it can be used to tweak disk performance in settings
// where the random download property is less necessary.
// for example, if the threshold is 7, all pieces which 7
// or more peers have, will be downloaded in index order.
int sequenced_download_threshold;
};
}
#endif

View File

@ -37,268 +37,54 @@ POSSIBILITY OF SUCH DAMAGE.
#pragma warning(push, 1)
#endif
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include <boost/noncopyable.hpp>
#include <boost/cstdint.hpp>
#include <boost/thread/thread.hpp>
// if building as Objective C++, asio's template
// parameters Protocol has to be renamed to avoid
// colliding with keywords
#ifdef __OBJC__
#define Protocol Protocol_
#endif
#include <asio.hpp>
#ifdef __OBJC__
#undef Protocol
#endif
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include <vector>
#include <exception>
#include <string>
#include "libtorrent/config.hpp"
namespace libtorrent
{
/*
namespace asio = boost::asio;
class TORRENT_EXPORT network_error : public std::exception
{
public:
network_error(int error_code): m_error_code(error_code) {}
virtual const char* what() const throw();
int error_code() const
{ return m_error_code; }
private:
int m_error_code;
};
using boost::asio::ipv4::tcp;
using boost::asio::ipv4::address;
using boost::asio::stream_socket;
using boost::asio::datagram_socket;
using boost::asio::socket_acceptor;
using boost::asio::demuxer;
using boost::asio::ipv4::host_resolver;
using boost::asio::async_write;
using boost::asio::ipv4::host;
using boost::asio::deadline_timer;
*/
namespace asio = ::asio;
class socket;
class TORRENT_EXPORT address
{
friend class socket;
public:
address();
address(
unsigned char a
, unsigned char b
, unsigned char c
, unsigned char d
, unsigned short port);
address(unsigned int addr, unsigned short port);
address(const char* addr, unsigned short port);
address(const address& a);
~address();
std::string as_string() const;
unsigned int ip() const { return m_ip; }
bool operator<=(const address& a) const
{ if (ip() == a.ip()) return port <= a.port; else return ip() <= a.ip(); }
bool operator<(const address& a) const
{ if (ip() == a.ip()) return port < a.port; else return ip() < a.ip(); }
bool operator!=(const address& a) const
{ return ip() != a.ip() || port != a.port; }
bool operator==(const address& a) const
{ return ip() == a.ip() && port == a.port; }
unsigned short port;
BOOST_STATIC_CONSTANT(unsigned short, any_port = 0);
BOOST_STATIC_CONSTANT(unsigned int, any_addr = 0);
private:
unsigned int m_ip;
};
class TORRENT_EXPORT socket: public boost::noncopyable
{
friend class address;
friend class selector;
public:
enum type
{
tcp = 0,
udp
};
socket(type t, bool blocking = true, unsigned short receive_port = 0);
virtual ~socket();
void connect(
const address& addr
, address const& bind_to
= address(address::any_addr, address::any_port));
void close();
void set_blocking(bool blocking);
bool is_blocking() { return m_blocking; }
const address& sender() const { assert(m_sender != address()); return m_sender; }
address name() const;
void listen(libtorrent::address const& iface, int queue);
boost::shared_ptr<libtorrent::socket> accept();
template<class T> int send(const T& buffer);
template<class T> int send_to(const address& addr, const T& buffer);
template<class T> int receive(T& buf);
int send(const char* buffer, int size);
int send_to(const address& addr, const char* buffer, int size);
int receive(char* buffer, int size);
void set_receive_bufsize(int size);
void set_send_bufsize(int size);
bool is_readable() const;
bool is_writable() const;
bool has_error() const;
enum error_code
{
netdown,
fault,
access,
address_in_use,
address_not_available,
in_progress,
interrupted,
invalid,
net_reset,
not_connected,
no_buffers,
operation_not_supported,
not_socket,
shutdown,
would_block,
connection_reset,
timed_out,
connection_aborted,
message_size,
not_ready,
no_support,
connection_refused,
is_connected,
net_unreachable,
not_initialized,
host_not_found,
unknown_error
};
error_code last_error() const;
private:
socket(int sock, const address& sender, bool blocking);
int m_socket;
address m_sender;
bool m_blocking;
#ifndef NDEBUG
bool m_connected; // indicates that this socket has been connected
type m_type;
#endif
};
template<class T>
inline int socket::send(const T& buf)
{
return send(reinterpret_cast<const char*>(&buf), sizeof(buf));
}
template<class T>
inline int socket::send_to(const address& addr, const T& buf)
{
return send_to(addr, reinterpret_cast<const unsigned char*>(&buf), sizeof(buf));
}
template<class T>
inline int socket::receive(T& buf)
{
return receive(reinterpret_cast<char*>(&buf), sizeof(T));
}
// timeout is given in microseconds
// modified is cleared and filled with the sockets that is ready for reading or writing
// or have had an error
class TORRENT_EXPORT selector
{
public:
void monitor_readability(boost::shared_ptr<socket> s)
{
boost::mutex::scoped_lock l(m_mutex);
assert(std::find(m_readable.begin(), m_readable.end(), s) == m_readable.end());
m_readable.push_back(s);
}
void monitor_writability(boost::shared_ptr<socket> s)
{
boost::mutex::scoped_lock l(m_mutex);
assert(std::find(m_writable.begin(), m_writable.end(), s) == m_writable.end());
m_writable.push_back(s);
}
void monitor_errors(boost::shared_ptr<socket> s)
{
boost::mutex::scoped_lock l(m_mutex);
assert(std::find(m_error.begin(), m_error.end(), s) == m_error.end());
m_error.push_back(s);
}
void remove(boost::shared_ptr<socket> s);
void remove_writable(boost::shared_ptr<socket> s)
{
boost::mutex::scoped_lock l(m_mutex);
m_writable.erase(std::find(m_writable.begin(), m_writable.end(), s));
}
void remove_readable(boost::shared_ptr<socket> s)
{
boost::mutex::scoped_lock l(m_mutex);
m_readable.erase(std::find(m_readable.begin(), m_readable.end(), s));
}
bool is_writability_monitored(boost::shared_ptr<socket> s)
{
boost::mutex::scoped_lock l(m_mutex);
return std::find(m_writable.begin(), m_writable.end(), s)
!= m_writable.end();
}
bool is_readability_monitored(boost::shared_ptr<socket> s)
{
boost::mutex::scoped_lock l(m_mutex);
return std::find(m_readable.begin(), m_readable.end(), s)
!= m_readable.end();
}
void wait(int timeout
, std::vector<boost::shared_ptr<socket> >& readable
, std::vector<boost::shared_ptr<socket> >& writable
, std::vector<boost::shared_ptr<socket> >& error);
int count_read_monitors() const
{
boost::mutex::scoped_lock l(m_mutex);
return (int)m_readable.size();
}
private:
mutable boost::mutex m_mutex;
std::vector<boost::shared_ptr<socket> > m_readable;
std::vector<boost::shared_ptr<socket> > m_writable;
std::vector<boost::shared_ptr<socket> > m_error;
};
using asio::ipv4::tcp;
using asio::ipv4::udp;
typedef asio::ipv4::tcp::socket stream_socket;
using asio::ipv4::address;
typedef asio::ipv4::udp::socket datagram_socket;
typedef asio::ipv4::tcp::acceptor socket_acceptor;
typedef asio::io_service demuxer;
using asio::ipv4::host_resolver;
using asio::ipv4::host;
using asio::async_write;
using asio::deadline_timer;
}
#endif // TORRENT_SOCKET_HPP_INCLUDED

View File

@ -112,7 +112,7 @@ namespace libtorrent
}
// should be called once every second
void second_tick();
void second_tick(float tick_interval);
float upload_rate() const { return m_mean_upload_rate; }
float download_rate() const { return m_mean_download_rate; }
@ -152,11 +152,11 @@ namespace libtorrent
#endif
// history of download/upload speeds a few seconds back
int m_download_rate_history[history];
int m_upload_rate_history[history];
float m_download_rate_history[history];
float m_upload_rate_history[history];
int m_download_payload_rate_history[history];
int m_upload_payload_rate_history[history];
float m_download_payload_rate_history[history];
float m_upload_payload_rate_history[history];
// the accumulators we are adding the downloads/uploads
// to this second. This only counts the actual payload

View File

@ -130,8 +130,9 @@ namespace libtorrent
~piece_manager();
bool check_fastresume(detail::piece_checker_data& d
, std::vector<bool>& pieces, bool compact_mode);
std::pair<bool, float> check_files(std::vector<bool>& pieces);
, std::vector<bool>& pieces, int& num_pieces, bool compact_mode);
std::pair<bool, float> check_files(std::vector<bool>& pieces
, int& num_pieces);
void release_files();

View File

@ -65,6 +65,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/resource_request.hpp"
#include "libtorrent/piece_picker.hpp"
#include "libtorrent/config.hpp"
#include "libtorrent/escape_string.hpp"
namespace libtorrent
{
@ -74,10 +75,6 @@ namespace libtorrent
class piece_manager;
std::string escape_string(const char* str, int len);
std::string unescape_string(std::string const& s);
namespace detail
{
struct session_impl;
@ -101,9 +98,10 @@ namespace libtorrent
, detail::checker_impl& checker
, torrent_info const& tf
, boost::filesystem::path const& save_path
, address const& net_interface
, tcp::endpoint const& net_interface
, bool compact_mode
, int block_size);
, int block_size
, session_settings const& s);
// used with metadata-less torrents
// (the metadata is downloaded from the peers)
@ -113,9 +111,10 @@ namespace libtorrent
, char const* tracker_url
, sha1_hash const& info_hash
, boost::filesystem::path const& save_path
, address const& net_interface
, tcp::endpoint const& net_interface
, bool compact_mode
, int block_size);
, int block_size
, session_settings const& s);
~torrent();
@ -138,7 +137,7 @@ namespace libtorrent
// caclulate the upload/download and number
// of connections this torrent needs. And prepare
// it for being used by allocate_resources.
void second_tick(stat& accumulator);
void second_tick(stat& accumulator, float tick_interval);
// debug purpose only
void print(std::ostream& os) const;
@ -165,19 +164,15 @@ namespace libtorrent
bool is_piece_filtered(int index) const;
void filtered_pieces(std::vector<bool>& bitmask) const;
// idea from Arvid and MooPolice
// todo refactoring and improving the function body
// marks the file with the given index as filtered
// it will not be downloaded
void filter_file(int index, bool filter);
void filter_files(std::vector<bool> const& files);
torrent_status status() const;
void use_interface(const char* net_interface);
address const& get_interface() const { return m_net_interface; }
peer_connection& connect_to_peer(const address& a);
tcp::endpoint const& get_interface() const { return m_net_interface; }
void connect_to_url_seed(std::string const& url);
peer_connection& connect_to_peer(tcp::endpoint const& a);
void set_ratio(float ratio)
{ assert(ratio >= 0.0f); m_ratio = ratio; }
@ -188,6 +183,14 @@ namespace libtorrent
// --------------------------------------------
// PEER MANAGEMENT
// 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 remove_url_seed(std::string const& url)
{ m_web_seeds.erase(url); }
// used by peer_connection to attach itself to a torrent
// since incoming connections don't know what torrent
// they're a part of until they have received an info_hash.
@ -196,10 +199,9 @@ namespace libtorrent
// this will remove the peer and make sure all
// the pieces it had have their reference counter
// decreased in the piece_picker
// called from the peer_connection destructor
void remove_peer(peer_connection* p);
peer_connection* connection_for(const address& a)
peer_connection* connection_for(tcp::endpoint const& a)
{
peer_iterator i = m_connections.find(a);
if (i == m_connections.end()) return 0;
@ -210,8 +212,8 @@ namespace libtorrent
int num_peers() const { return (int)m_connections.size(); }
int num_seeds() const;
typedef std::map<address, peer_connection*>::iterator peer_iterator;
typedef std::map<address, peer_connection*>::const_iterator const_peer_iterator;
typedef std::map<tcp::endpoint, peer_connection*>::iterator peer_iterator;
typedef std::map<tcp::endpoint, peer_connection*>::const_iterator const_peer_iterator;
const_peer_iterator begin() const { return m_connections.begin(); }
const_peer_iterator end() const { return m_connections.end(); }
@ -264,9 +266,9 @@ namespace libtorrent
// the tracker
void set_tracker_login(std::string const& name, std::string const& pw);
// the address of the tracker that we managed to
// the tcp::endpoint of the tracker that we managed to
// announce ourself at the last time we tried to announce
const address& current_tracker() const;
const tcp::endpoint& current_tracker() const;
// --------------------------------------------
// PIECE MANAGEMENT
@ -300,7 +302,7 @@ namespace libtorrent
m_picker->dec_refcount(index);
}
int block_size() const { return m_block_size; }
int block_size() const { assert(m_block_size > 0); return m_block_size; }
// this will tell all peers that we just got his piece
// and also let the piece picker know that we have this piece
@ -314,6 +316,10 @@ namespace libtorrent
// all seeds and let the tracker know we're finished.
void completed();
// this is the asio callback that is called when a name
// lookup for a web seed is completed.
void on_name_lookup(asio::error const& e, int port, std::string url, host h);
// this is called when the torrent has finished. i.e.
// all the pieces we have not filtered have been downloaded.
// If no pieces are filtered, this is called first and then
@ -326,6 +332,8 @@ namespace libtorrent
// each time a piece has failed the hash
// test
void piece_failed(int index);
void received_redundant_data(int num_bytes)
{ assert(num_bytes > 0); m_total_redundant_bytes += num_bytes; }
float priority() const
{ return m_priority; }
@ -384,6 +392,9 @@ namespace libtorrent
resource_request m_uploads_quota;
resource_request m_connections_quota;
void set_peer_upload_limit(tcp::endpoint ip, int limit);
void set_peer_download_limit(tcp::endpoint ip, int limit);
void set_upload_limit(int limit);
void set_download_limit(int limit);
void set_max_uploads(int limit);
@ -458,11 +469,21 @@ namespace libtorrent
#ifndef NDEBUG
public:
#endif
std::map<address, peer_connection*> m_connections;
std::map<tcp::endpoint, peer_connection*> m_connections;
#ifndef NDEBUG
private:
#endif
// The list of web seeds in this torrent. Seeds
// with fatal errors are removed from the set
std::set<std::string> m_web_seeds;
// The set of url seeds that are currently having
// their hostnames resolved.
std::map<std::string, host> m_resolving_web_seeds;
// used to resolve the names of web seeds
host_resolver m_host_resolver;
// this is the upload and download statistics for the whole torrent.
// it's updated from all its peers once every second.
libtorrent::stat m_stat;
@ -518,13 +539,14 @@ namespace libtorrent
// the number of bytes that has been
// downloaded that failed the hash-test
size_type m_total_failed_bytes;
size_type m_total_redundant_bytes;
std::string m_username;
std::string m_password;
// the network interface all outgoing connections
// are opened through
address m_net_interface;
tcp::endpoint m_net_interface;
// the max number of bytes this torrent
// can upload per second
@ -569,6 +591,15 @@ namespace libtorrent
// them from altering the piece-picker before it
// has been initialized with files_checked().
bool m_connections_initialized;
session_settings const& m_settings;
#ifndef NDEBUG
// this is the amount downloaded when this torrent
// is started. i.e.
// total_done - m_initial_done <= total_payload_download
size_type m_initial_done;
#endif
};
inline boost::posix_time::ptime torrent::next_announce() const

View File

@ -81,6 +81,8 @@ namespace libtorrent
, total_upload(0)
, total_payload_download(0)
, total_payload_upload(0)
, total_failed_bytes(0)
, total_redundant_bytes(0)
, download_rate(0)
, upload_rate(0)
, download_payload_rate(0)
@ -89,6 +91,7 @@ namespace libtorrent
, num_complete(-1)
, num_incomplete(-1)
, pieces(0)
, num_pieces(0)
, total_done(0)
, total_wanted_done(0)
, total_wanted(0)
@ -130,6 +133,10 @@ namespace libtorrent
// has failed their hash test
size_type total_failed_bytes;
// the number of payload bytes that
// has been received redundantly.
size_type total_redundant_bytes;
// current transfer rate
// payload plus protocol
float download_rate;
@ -154,6 +161,11 @@ namespace libtorrent
const std::vector<bool>* pieces;
// this is the number of pieces the client has
// downloaded. it is equal to:
// std::accumulate(pieces->begin(), pieces->end());
int num_pieces;
// the number of bytes of the file we have
// including pieces that may have been filtered
// after we downloaded them
@ -196,7 +208,7 @@ namespace libtorrent
int blocks_in_piece;
std::bitset<max_blocks_per_piece> requested_blocks;
std::bitset<max_blocks_per_piece> finished_blocks;
address peer[max_blocks_per_piece];
tcp::endpoint peer[max_blocks_per_piece];
int num_downloads[max_blocks_per_piece];
};
@ -209,13 +221,15 @@ namespace libtorrent
torrent_handle(): m_ses(0), m_chk(0) {}
void get_peer_info(std::vector<peer_info>& v) const;
bool send_chat_message(address ip, std::string message) const;
bool send_chat_message(tcp::endpoint ip, std::string message) const;
torrent_status status() const;
void get_download_queue(std::vector<partial_piece_info>& queue) const;
std::vector<announce_entry> const& trackers() const;
void replace_trackers(std::vector<announce_entry> const&) const;
void add_url_seed(std::string const& url);
bool has_metadata() const;
const torrent_info& get_torrent_info() const;
bool is_valid() const;
@ -225,7 +239,6 @@ namespace libtorrent
void pause() const;
void resume() const;
// marks the piece with the given index as filtered
// it will not be downloaded
void filter_piece(int index, bool filter) const;
@ -233,8 +246,6 @@ namespace libtorrent
bool is_piece_filtered(int index) const;
std::vector<bool> filtered_pieces() const;
//idea from Arvid and MooPolice
//todo refactoring and improving the function body
// marks the file with the given index as filtered
// it will not be downloaded
void filter_file(int index, bool filter) const;
@ -247,7 +258,7 @@ namespace libtorrent
entry write_resume_data() const;
// kind of similar to get_torrent_info() but this
// is low level, returning the exact info-part of
// is lower level, returning the exact info-part of
// the .torrent file. When hashed, this buffer
// will produce the info hash. The reference is valid
// only as long as the torrent is running.
@ -270,8 +281,11 @@ namespace libtorrent
void set_upload_limit(int limit) const;
void set_download_limit(int limit) const;
void set_peer_upload_limit(tcp::endpoint ip, int limit) const;
void set_peer_download_limit(tcp::endpoint ip, int limit) const;
// manually connect a peer
void connect_peer(address const& adr) const;
void connect_peer(tcp::endpoint const& adr) const;
// valid ratios are 0 (infinite ratio) or [ 1.0 , inf )
// the ratio is uploaded / downloaded. less than 1 is not allowed

View File

@ -54,6 +54,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/socket.hpp"
#include "libtorrent/peer_id.hpp"
#include "libtorrent/size_type.hpp"
#include "libtorrent/peer_request.hpp"
#include "libtorrent/config.hpp"
namespace libtorrent
@ -65,6 +66,13 @@ namespace libtorrent
size_type size;
};
struct TORRENT_EXPORT file_slice
{
int file_index;
size_type offset;
size_type size;
};
struct TORRENT_EXPORT announce_entry
{
announce_entry(std::string const& u): url(u), tier(0) {}
@ -77,11 +85,6 @@ namespace libtorrent
virtual const char* what() const throw() { return "invalid torrent file"; }
};
// TODO: add a check to see if filenames are accepted on the
// current platform.
// also add a filename converter function that will transform
// invalid filenames to valid filenames on the current platform
class TORRENT_EXPORT torrent_info
{
public:
@ -99,6 +102,12 @@ namespace libtorrent
void set_hash(int index, sha1_hash const& h);
void add_tracker(std::string const& url, int tier = 0);
void add_file(boost::filesystem::path file, size_type size);
void add_url_seed(std::string const& url);
std::vector<file_slice> map_block(int piece, size_type offset, int size) const;
peer_request map_file(int file, size_type offset, int size) const;
std::vector<std::string> const& url_seeds() const { return m_url_seeds; }
typedef std::vector<file_entry>::const_iterator file_iterator;
typedef std::vector<file_entry>::const_reverse_iterator reverse_file_iterator;
@ -153,6 +162,8 @@ namespace libtorrent
// the urls to the trackers
std::vector<announce_entry> m_urls;
std::vector<std::string> m_url_seeds;
// the length of one piece
// if this is 0, the torrent_info is
// in an uninitialized state
@ -168,7 +179,7 @@ namespace libtorrent
size_type m_total_size;
// the hash that identifies this torrent
// it is mutable because it's calculated
// is mutable because it's calculated
// lazily
mutable sha1_hash m_info_hash;

View File

@ -46,6 +46,10 @@ POSSIBILITY OF SUCH DAMAGE.
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/cstdint.hpp>
#include <boost/weak_ptr.hpp>
#include <boost/intrusive_ptr.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/recursive_mutex.hpp>
#include <boost/tuple/tuple.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
@ -62,8 +66,8 @@ namespace libtorrent
{
struct request_callback;
class tracker_manager;
// address parse_url(std::string const& url);
struct timeout_handler;
struct tracker_connection;
// encodes a string using the base64 scheme
TORRENT_EXPORT std::string base64encode(const std::string& s);
@ -71,6 +75,9 @@ namespace libtorrent
// returns -1 if gzip header is invalid or the header size in bytes
TORRENT_EXPORT int gzip_header(const char* buf, int size);
TORRENT_EXPORT boost::tuple<std::string, std::string, int, std::string>
parse_url_components(std::string url);
struct TORRENT_EXPORT tracker_request
{
tracker_request()
@ -95,7 +102,7 @@ namespace libtorrent
};
sha1_hash info_hash;
peer_id id;
peer_id pid;
size_type downloaded;
size_type uploaded;
size_type left;
@ -125,7 +132,7 @@ namespace libtorrent
, int response_code
, const std::string& description) = 0;
address m_tracker_address;
tcp::endpoint m_tracker_address;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
virtual void debug_log(const std::string& line) = 0;
@ -140,23 +147,71 @@ namespace libtorrent
, request_callback* requester
, int maximum_tracker_response_length);
struct TORRENT_EXPORT tracker_connection: boost::noncopyable
{
tracker_connection(boost::weak_ptr<request_callback> r)
: m_requester(r)
{}
TORRENT_EXPORT void intrusive_ptr_add_ref(timeout_handler const*);
TORRENT_EXPORT void intrusive_ptr_release(timeout_handler const*);
struct TORRENT_EXPORT timeout_handler
: boost::noncopyable
{
friend void intrusive_ptr_add_ref(timeout_handler const*);
friend void intrusive_ptr_release(timeout_handler const*);
timeout_handler(demuxer& d);
void set_timeout(int completion_timeout, int read_timeout);
void restart_read_timeout();
void cancel();
virtual void on_timeout() = 0;
virtual ~timeout_handler() {}
private:
void timeout_callback(asio::error const&);
boost::intrusive_ptr<timeout_handler> self()
{ return boost::intrusive_ptr<timeout_handler>(this); }
demuxer& m_demuxer;
// used for timeouts
// this is set when the request has been sent
boost::posix_time::ptime m_start_time;
// this is set every time something is received
boost::posix_time::ptime m_read_time;
// the asio async operation
asio::deadline_timer m_timeout;
int m_completion_timeout;
int m_read_timeout;
typedef boost::mutex mutex_t;
mutable mutex_t m_mutex;
mutable int m_refs;
};
struct TORRENT_EXPORT tracker_connection
: timeout_handler
{
tracker_connection(tracker_manager& man
, tracker_request req
, demuxer& d
, boost::weak_ptr<request_callback> r);
virtual bool tick() = 0;
virtual bool send_finished() const = 0;
bool has_requester() const { return !m_requester.expired(); }
request_callback& requester();
virtual ~tracker_connection() {}
virtual tracker_request const& tracker_req() const = 0;
tracker_request const& tracker_req() const { return m_req; }
bool has_requester() const { return !m_requester.expired(); }
void fail(int code, char const* msg);
void fail_timeout();
void close();
protected:
boost::weak_ptr<request_callback> m_requester;
private:
tracker_manager& m_man;
const tracker_request m_req;
};
class TORRENT_EXPORT tracker_manager: boost::noncopyable
@ -166,18 +221,22 @@ namespace libtorrent
tracker_manager(const http_settings& s)
: m_settings(s) {}
void tick();
void queue_request(
tracker_request r
demuxer& d
, tracker_request r
, std::string const& auth
, boost::weak_ptr<request_callback> c
= boost::weak_ptr<request_callback>());
void abort_all_requests();
bool send_finished() const;
void remove_request(tracker_connection const*);
private:
typedef std::list<boost::shared_ptr<tracker_connection> >
typedef boost::recursive_mutex mutex_t;
mutex_t m_mutex;
typedef std::list<boost::intrusive_ptr<tracker_connection> >
tracker_connections_t;
tracker_connections_t m_connections;
const http_settings& m_settings;

View File

@ -56,7 +56,6 @@ POSSIBILITY OF SUCH DAMAGE.
#include "libtorrent/peer_id.hpp"
#include "libtorrent/peer.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/async_gethostbyname.hpp"
#include "libtorrent/config.hpp"
namespace libtorrent
@ -67,45 +66,55 @@ namespace libtorrent
public:
udp_tracker_connection(
tracker_request const& req
demuxer& d
, tracker_manager& man
, tracker_request const& req
, std::string const& hostname
, unsigned short port
, boost::weak_ptr<request_callback> c
, const http_settings& stn);
virtual bool tick();
virtual bool send_finished() const;
virtual tracker_request const& tracker_req() const
{ return m_request; }
private:
enum action_t
{
connect,
announce,
scrape,
error
action_connect,
action_announce,
action_scrape,
action_error
};
boost::intrusive_ptr<udp_tracker_connection> self()
{ return boost::intrusive_ptr<udp_tracker_connection>(this); }
void name_lookup(asio::error const& error);
void timeout(asio::error const& error);
void send_udp_connect();
void connect_response(asio::error const& error, std::size_t bytes_transferred);
void send_udp_announce();
void announce_response(asio::error const& error, std::size_t bytes_transferred);
void send_udp_scrape();
bool parse_connect_response(const char* buf, int ret);
bool parse_announce_response(const char* buf, int ret);
bool parse_scrape_response(const char* buf, int ret);
void scrape_response(asio::error const& error, std::size_t bytes_transferred);
dns_lookup m_name_lookup;
boost::shared_ptr<socket> m_socket;
virtual void on_timeout();
// used for time outs
boost::posix_time::ptime m_request_time;
tracker_manager& m_man;
host_resolver m_name_lookup;
host m_host;
int m_port;
boost::shared_ptr<datagram_socket> m_socket;
udp::endpoint m_target;
udp::endpoint m_sender;
tracker_request m_request;
int m_transaction_id;
boost::int64_t m_connection_id;
const http_settings& m_settings;
int m_attempts;
std::vector<char> m_buffer;
};
}

View File

@ -33,6 +33,6 @@ POSSIBILITY OF SUCH DAMAGE.
#ifndef TORRENT_VERSION_HPP_INCLUDED
#define TORRENT_VERSION_HPP_INCLUDED
#define LIBTORRENT_VERSION "0.9.0.0"
#define LIBTORRENT_VERSION "0.9.2.0"
#endif

View File

@ -0,0 +1,170 @@
/*
Copyright (c) 2003, 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_WEB_PEER_CONNECTION_HPP_INCLUDED
#define TORRENT_WEB_PEER_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/date_time/posix_time/posix_time.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/allocate_resources.hpp"
#include "libtorrent/peer_request.hpp"
#include "libtorrent/piece_block_progress.hpp"
#include "libtorrent/config.hpp"
// parse_url
#include "libtorrent/tracker_manager.hpp"
// http_parser
#include "libtorrent/http_tracker_connection.hpp"
namespace libtorrent
{
class torrent;
namespace detail
{
struct session_impl;
}
class TORRENT_EXPORT web_peer_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
web_peer_connection(
detail::session_impl& ses
, boost::weak_ptr<torrent> t
, boost::shared_ptr<stream_socket> s
, tcp::endpoint const& remote
, std::string const& url);
~web_peer_connection();
// called from the main loop when this connection has any
// work to do.
void on_sent(asio::error const& error
, std::size_t bytes_transferred);
void on_receive(asio::error const& error
, std::size_t bytes_transferred);
std::string const& url() const { return m_url; }
virtual void get_peer_info(peer_info& p) 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) {}
void write_have(int index) {}
void write_piece(peer_request const& r) {}
void write_keepalive() {}
void on_connected();
#ifndef NDEBUG
void check_invariant() const;
boost::posix_time::ptime m_last_choke;
#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;
// this has one entry per http-request
// (might be more than the bt requests)
std::deque<int> m_file_requests;
std::string m_server_string;
http_parser m_parser;
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;
// this is used for intermediate storage of pieces
// that is received in more than on HTTP responses
std::vector<char> m_piece;
// the mapping of the data in the m_piece buffer
peer_request m_intermediate_piece;
};
}
#endif // TORRENT_WEB_PEER_CONNECTION_HPP_INCLUDED

View File

@ -1,9 +1,9 @@
lib_LTLIBRARIES = libtorrent.la
libtorrent_la_SOURCES = allocate_resources.cpp async_gethostbyname.cpp \
libtorrent_la_SOURCES = allocate_resources.cpp \
entry.cpp escape_string.cpp \
peer_connection.cpp piece_picker.cpp policy.cpp \
session.cpp sha1.cpp socket.cpp stat.cpp \
peer_connection.cpp bt_peer_connection.cpp web_peer_connection.cpp \
piece_picker.cpp policy.cpp session.cpp sha1.cpp stat.cpp \
storage.cpp torrent.cpp torrent_handle.cpp \
torrent_info.cpp tracker_manager.cpp \
http_tracker_connection.cpp udp_tracker_connection.cpp \
@ -13,7 +13,6 @@ noinst_HEADERS = \
$(top_srcdir)/include/libtorrent/alert.hpp \
$(top_srcdir)/include/libtorrent/alert_types.hpp \
$(top_srcdir)/include/libtorrent/allocate_resources.hpp \
$(top_srcdir)/include/libtorrent/async_gethostbyname.hpp \
$(top_srcdir)/include/libtorrent/bencode.hpp \
$(top_srcdir)/include/libtorrent/buffer.hpp \
$(top_srcdir)/include/libtorrent/debug.hpp \
@ -30,6 +29,8 @@ $(top_srcdir)/include/libtorrent/io.hpp \
$(top_srcdir)/include/libtorrent/ip_filter.hpp \
$(top_srcdir)/include/libtorrent/peer.hpp \
$(top_srcdir)/include/libtorrent/peer_connection.hpp \
$(top_srcdir)/include/libtorrent/bt_peer_connection.hpp \
$(top_srcdir)/include/libtorrent/web_peer_connection.hpp \
$(top_srcdir)/include/libtorrent/peer_id.hpp \
$(top_srcdir)/include/libtorrent/peer_info.hpp \
$(top_srcdir)/include/libtorrent/peer_request.hpp \
@ -54,5 +55,5 @@ $(top_srcdir)/include/libtorrent/version.hpp
libtorrent_la_LDFLAGS = $(LDFLAGS) -version-info 1:0:1
libtorrent_la_LIBADD = @ZLIB@ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@
AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@
AM_CXXFLAGS= -ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/asio/include @ZLIBINCL@ @DEBUGFLAGS@ @PTHREAD_CFLAGS@
AM_LDFLAGS= $(LDFLAGS) -L./ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@

View File

@ -69,6 +69,8 @@ namespace libtorrent
{
assert(a >= 0);
assert(b >= 0);
assert(a <= resource_request::inf);
assert(b <= resource_request::inf);
assert(resource_request::inf + resource_request::inf < 0);
int sum = a + b;
@ -121,7 +123,6 @@ namespace libtorrent
assert(m_resources >= 0);
for (It i = m_start, end(m_end); i != end; ++i)
{
assert(((*i).*m_res).used >= 0);
assert(((*i).*m_res).max >= 0);
assert(((*i).*m_res).given >= 0);
}
@ -229,10 +230,13 @@ namespace libtorrent
assert(r.given < r.max);
size_type used = (size_type)r.used + 1;
if (used < 1) used = 1;
size_type to_give = used * kNumer / kDenom;
if(to_give > resource_request::inf)
to_give = resource_request::inf;
assert(to_give >= 0);
resources_to_distribute -= give(r, (int)to_give);
assert(resources_to_distribute >= 0);
}
assert(resources_to_distribute >= 0);
@ -240,13 +244,14 @@ namespace libtorrent
}
peer_connection& pick_peer(
std::pair<boost::shared_ptr<socket>, boost::shared_ptr<peer_connection> > const& p)
std::pair<boost::shared_ptr<stream_socket>
, boost::intrusive_ptr<peer_connection> > const& p)
{
return *p.second;
}
peer_connection& pick_peer2(
std::pair<address, peer_connection*> const& p)
std::pair<tcp::endpoint, peer_connection*> const& p)
{
return *p.second;
}
@ -262,11 +267,11 @@ namespace libtorrent
/*
void allocate_resources(
int resources
, std::map<boost::shared_ptr<socket>, boost::shared_ptr<peer_connection> >& c
, std::map<boost::shared_ptr<socket>, boost::intrusive_ptr<peer_connection> >& c
, resource_request peer_connection::* res)
{
typedef std::map<boost::shared_ptr<socket>, boost::shared_ptr<peer_connection> >::iterator orig_iter;
typedef std::pair<boost::shared_ptr<socket>, boost::shared_ptr<peer_connection> > in_param;
typedef std::map<boost::shared_ptr<socket>, boost::intrusive_ptr<peer_connection> >::iterator orig_iter;
typedef std::pair<boost::shared_ptr<socket>, boost::intrusive_ptr<peer_connection> > in_param;
typedef boost::transform_iterator<peer_connection& (*)(in_param const&), orig_iter> new_iter;
allocate_resources_impl(
@ -297,7 +302,7 @@ namespace libtorrent
struct iterator_wrapper2
{
typedef std::map<address, peer_connection*>::iterator orig_iter;
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
orig_iter iter;
@ -326,7 +331,7 @@ namespace libtorrent
void allocate_resources(
int resources
, std::map<address, peer_connection*>& c
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
allocate_resources_impl(
@ -356,11 +361,11 @@ namespace libtorrent
void allocate_resources(
int resources
, std::map<address, peer_connection*>& c
, std::map<tcp::endpoint, peer_connection*>& c
, resource_request peer_connection::* res)
{
typedef std::map<address, peer_connection*>::iterator orig_iter;
typedef std::pair<address, peer_connection*> in_param;
typedef std::map<tcp::endpoint, peer_connection*>::iterator orig_iter;
typedef std::pair<tcp::endpoint, peer_connection*> in_param;
typedef boost::transform_iterator<peer_connection& (*)(in_param const&), orig_iter> new_iter;
allocate_resources_impl(

1537
src/bt_peer_connection.cpp Executable file

File diff suppressed because it is too large Load Diff

View File

@ -62,7 +62,7 @@ namespace
std::pair<std::string
, libtorrent::entry> const& e) const
{
return e.first == m_str;
return m_str && e.first == m_str;
}
char const* m_str;
};

View File

@ -115,4 +115,34 @@ namespace libtorrent
}
return ret.str();
}
std::string escape_path(const char* str, int len)
{
assert(str != 0);
assert(len >= 0);
static const char unreserved_chars[] = "/-_.!~*()"
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
"0123456789";
std::stringstream ret;
ret << std::hex << std::setfill('0');
for (int i = 0; i < len; ++i)
{
if (std::count(
unreserved_chars
, unreserved_chars+sizeof(unreserved_chars)-1
, *str))
{
ret << *str;
}
else
{
ret << '%'
<< std::setw(2)
<< (int)static_cast<unsigned char>(*str);
}
++str;
}
return ret.str();
}
}

View File

@ -167,7 +167,11 @@ namespace libtorrent
m_fd = ::open(
utf8_native(path.native_file_string()).c_str()
, map_open_mode(mode)
#ifdef _WIN32
, S_IREAD | S_IWRITE);
#else
, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
#endif
#endif
if (m_fd == -1)
{
@ -218,7 +222,7 @@ namespace libtorrent
return ret;
}
void seek(size_type offset, int m)
size_type seek(size_type offset, int m)
{
assert(m_open_mode);
assert(m_fd != -1);
@ -242,7 +246,7 @@ namespace libtorrent
<< " seekdir: " << seekdir;
throw file_error(msg.str());
}
return ret;
}
size_type tell()
@ -291,9 +295,9 @@ namespace libtorrent
return m_impl->read(buf, num_bytes);
}
void file::seek(size_type pos, file::seek_mode m)
size_type file::seek(size_type pos, file::seek_mode m)
{
m_impl->seek(pos, m.m_val);
return m_impl->seek(pos, m.m_val);
}
size_type file::tell()

View File

@ -233,7 +233,7 @@ namespace libtorrent
return bytes_read;
}
void seek(size_type pos, seek_mode from_where)
size_type seek(size_type pos, seek_mode from_where)
{
assert(pos >= 0 || from_where != seek_begin);
assert(pos <= 0 || from_where != seek_end);
@ -333,9 +333,9 @@ namespace libtorrent
return m_impl->read(buffer, num_bytes);
}
void file::seek(size_type pos, seek_mode m)
size_type file::seek(size_type pos, seek_mode m)
{
m_impl->seek(pos,impl::seek_mode(m.m_val));
return m_impl->seek(pos,impl::seek_mode(m.m_val));
}
size_type file::tell()

View File

@ -38,16 +38,25 @@ POSSIBILITY OF SUCH DAMAGE.
#include "zlib.h"
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/bind.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/entry.hpp"
#include "libtorrent/bencode.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/io.hpp"
#include "libtorrent/async_gethostbyname.hpp"
using namespace libtorrent;
using boost::bind;
namespace
{
@ -77,9 +86,152 @@ using namespace boost::posix_time;
namespace libtorrent
{
http_parser::http_parser()
: m_recv_pos(0)
, m_status_code(-1)
, m_content_length(-1)
, m_content_encoding(plain)
, m_state(read_status)
, m_recv_buffer(0, 0)
, m_body_start_pos(0)
, m_finished(false)
{}
boost::tuple<int, int> http_parser::incoming(buffer::const_interval recv_buffer)
{
m_recv_buffer = recv_buffer;
boost::tuple<int, int> ret(0, 0);
char const* pos = recv_buffer.begin + m_recv_pos;
if (m_state == read_status)
{
assert(!m_finished);
char const* newline = std::find(pos, recv_buffer.end, '\n');
// if we don't have a full line yet, wait.
if (newline == recv_buffer.end) return ret;
if (newline == pos)
throw std::runtime_error("unexpected newline in HTTP response");
std::istringstream line(std::string(pos, newline - 1));
++newline;
int incoming = (int)std::distance(pos, newline);
m_recv_pos += incoming;
boost::get<1>(ret) += incoming;
pos = newline;
line >> m_protocol;
if (m_protocol.substr(0, 5) != "HTTP/")
{
throw std::runtime_error("unknown protocol in HTTP response: "
+ m_protocol);
}
line >> m_status_code;
std::getline(line, m_server_message);
m_state = read_header;
}
if (m_state == read_header)
{
assert(!m_finished);
char const* newline = std::find(pos, recv_buffer.end, '\n');
std::string line;
while (newline != recv_buffer.end && m_state == read_header)
{
if (newline == pos)
throw std::runtime_error("unexpected newline in HTTP response");
line.assign(pos, newline - 1);
m_recv_pos += newline - pos;
boost::get<1>(ret) += newline - pos;
pos = newline;
std::string::size_type separator = line.find(": ");
if (separator == std::string::npos)
{
++pos;
++m_recv_pos;
boost::get<1>(ret) += 1;
m_state = read_body;
m_body_start_pos = m_recv_pos;
break;
}
std::string name = line.substr(0, separator);
std::string value = line.substr(separator + 2, std::string::npos);
m_header.insert(std::make_pair(name, value));
if (name == "Content-Length")
{
try
{
m_content_length = boost::lexical_cast<int>(value);
}
catch(boost::bad_lexical_cast&) {}
}
else if (name == "Content-Encoding")
{
if (value == "gzip" || value == "x-gzip")
{
m_content_encoding = gzip;
}
else
{
std::string error_str = "unknown content encoding in response: \"";
error_str += value;
error_str += "\"";
throw std::runtime_error(error_str);
}
}
// TODO: make sure we don't step outside of the buffer
++pos;
++m_recv_pos;
assert(m_recv_pos <= (int)recv_buffer.left());
newline = std::find(pos, recv_buffer.end, '\n');
}
}
if (m_state == read_body)
{
int incoming = recv_buffer.end - pos;
if (m_recv_pos - m_body_start_pos + incoming > m_content_length
&& m_content_length >= 0)
incoming = m_content_length - m_recv_pos + m_body_start_pos;
assert(incoming >= 0);
m_recv_pos += incoming;
boost::get<0>(ret) += incoming;
if (m_content_length >= 0
&& m_recv_pos - m_body_start_pos >= m_content_length)
{
m_finished = true;
}
}
return ret;
}
buffer::const_interval http_parser::get_body()
{
char const* body_begin = m_recv_buffer.begin + m_body_start_pos;
char const* body_end = m_recv_buffer.begin + m_recv_pos;
m_recv_pos = 0;
m_body_start_pos = 0;
m_status_code = -1;
m_content_length = -1;
m_finished = false;
m_state = read_status;
m_header.clear();
return buffer::const_interval(body_begin, body_end);
}
http_tracker_connection::http_tracker_connection(
tracker_manager& man
demuxer& d
, tracker_manager& man
, tracker_request const& req
, std::string const& hostname
, unsigned short port
@ -87,20 +239,21 @@ namespace libtorrent
, boost::weak_ptr<request_callback> c
, const http_settings& stn
, std::string const& auth)
: tracker_connection(c)
: tracker_connection(man, req, d, c)
, m_man(man)
, m_state(read_status)
, m_content_encoding(plain)
, m_content_length(0)
, m_name_lookup(d)
, m_port(port)
, m_recv_pos(0)
, m_request_time(second_clock::universal_time())
, m_buffer(http_buffer_size)
, m_settings(stn)
, m_req(req)
, m_password(auth)
, m_code(0)
, m_timed_out(false)
{
const std::string* connect_to_host;
int connect_to_port = port;
bool using_proxy = false;
m_send_buffer.assign("GET ");
@ -117,7 +270,7 @@ namespace libtorrent
m_send_buffer += ":";
m_send_buffer += boost::lexical_cast<std::string>(port);
}
connect_to_port = m_settings.proxy_port != 0
m_port = m_settings.proxy_port != 0
? m_settings.proxy_port : 80 ;
}
else
@ -125,7 +278,7 @@ namespace libtorrent
connect_to_host = &hostname;
}
if (m_req.kind == tracker_request::scrape_request)
if (tracker_req().kind == tracker_request::scrape_request)
{
// find and replace "announce" with "scrape"
// in request
@ -133,7 +286,7 @@ namespace libtorrent
std::size_t pos = request.find("announce");
if (pos == std::string::npos)
throw std::runtime_error("scrape is not available on url: '"
+ m_req.url +"'");
+ tracker_req().url +"'");
request.replace(pos, 8, "scrape");
}
@ -151,11 +304,11 @@ namespace libtorrent
m_send_buffer += escape_string(
reinterpret_cast<const char*>(req.info_hash.begin()), 20);
if (m_req.kind == tracker_request::announce_request)
if (tracker_req().kind == tracker_request::announce_request)
{
m_send_buffer += "&peer_id=";
m_send_buffer += escape_string(
reinterpret_cast<const char*>(req.id.begin()), 20);
reinterpret_cast<const char*>(req.pid.begin()), 20);
m_send_buffer += "&port=";
m_send_buffer += boost::lexical_cast<std::string>(req.listen_port);
@ -191,7 +344,8 @@ namespace libtorrent
m_send_buffer += " HTTP/1.0\r\nAccept-Encoding: gzip\r\n"
"User-Agent: ";
m_send_buffer += m_settings.user_agent;
m_send_buffer += escape_string(m_settings.user_agent.c_str()
, m_settings.user_agent.length());
m_send_buffer += " (libtorrent)\r\n"
"Host: ";
m_send_buffer += hostname;
@ -221,139 +375,147 @@ namespace libtorrent
}
#endif
m_name_lookup = dns_lookup(connect_to_host->c_str(), connect_to_port);
m_socket.reset(new socket(socket::tcp, false));
m_name_lookup.async_by_name(m_host, *connect_to_host
, bind(&http_tracker_connection::name_lookup, self(), _1));
set_timeout(m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout);
}
// returns true if this connection is finished and should be removed from
// the connections list.
bool http_tracker_connection::tick()
void http_tracker_connection::on_timeout()
{
m_timed_out = true;
m_socket.reset();
m_name_lookup.cancel();
fail_timeout();
}
void http_tracker_connection::name_lookup(asio::error const& error) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
try
{
if (has_requester()) requester().debug_log("tracker name lookup successful");
#endif
if (m_name_lookup.running())
{
if (!m_name_lookup.finished()) return false;
if (m_name_lookup.failed())
{
if (has_requester()) requester().tracker_request_error(
m_req, -1, m_name_lookup.error());
return true;
}
address a(m_name_lookup.ip());
restart_read_timeout();
m_socket.reset(new stream_socket(m_name_lookup.io_service()));
tcp::endpoint a(m_port, m_host.address(0));
if (has_requester()) requester().m_tracker_address = a;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("name lookup successful");
#endif
m_socket->connect(a);
// clear the lookup entry so it will not be
// marked as running anymore
m_name_lookup = dns_lookup();
m_socket->async_connect(a, bind(&http_tracker_connection::connected, self(), _1));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
}
using namespace boost::posix_time;
time_duration d = second_clock::universal_time() - m_request_time;
if (d > seconds(m_settings.tracker_timeout) ||
(!has_requester() && d > seconds(m_settings.stop_tracker_timeout)))
void http_tracker_connection::connected(asio::error const& error) try
{
if (has_requester()) requester().tracker_request_timed_out(m_req);
return true;
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
fail(-1, error.what());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker connection tick");
if (has_requester()) requester().debug_log("tracker connection successful");
#endif
// if we have a send buffer and the socket is ready for writing
// send the buffer
if (!m_send_buffer.empty() && m_socket->is_writable())
{
int sent = m_socket->send(m_send_buffer.c_str(),(int)m_send_buffer.size());
if (sent == (int)m_send_buffer.size())
{
m_send_buffer.clear();
restart_read_timeout();
async_write(*m_socket, asio::buffer(m_send_buffer.c_str()
, m_send_buffer.size()), bind(&http_tracker_connection::sent
, self(), _1));
}
else if (sent > 0)
catch (std::exception& e)
{
m_send_buffer.erase(
m_send_buffer.begin()
, m_send_buffer.begin() + sent);
assert(false);
fail(-1, e.what());
}
if (sent != 0)
m_request_time = second_clock::universal_time();
}
if (m_socket->has_error())
void http_tracker_connection::sent(asio::error const& error) try
{
if (has_requester()) requester().tracker_request_error(
m_req, -1, "connection refused");
return true;
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
fail(-1, error.what());
return;
}
// if the socket isn't ready for reading, there's no point in continuing
// trying to read from it
if (!m_socket->is_readable()) return false;
m_request_time = second_clock::universal_time();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker connection socket readable");
if (has_requester()) requester().debug_log("tracker send data completed");
#endif
restart_read_timeout();
assert(m_buffer.size() - m_recv_pos > 0);
m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
, self(), _1, _2));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
}; // msvc 7.1 seems to require this semi-colon
void http_tracker_connection::receive(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (m_timed_out) return;
if (error)
{
if (error == asio::error::eof)
{
on_response();
close();
return;
}
fail(-1, error.what());
return;
}
restart_read_timeout();
assert(bytes_transferred > 0);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("tracker connection reading "
+ boost::lexical_cast<std::string>(bytes_transferred));
#endif
m_recv_pos += bytes_transferred;
// if the receive buffer is full, expand it with http_buffer_size
if ((int)m_buffer.size() == m_recv_pos)
{
if ((int)m_buffer.size() > m_settings.tracker_maximum_response_length)
if ((int)m_buffer.size() >= m_settings.tracker_maximum_response_length)
{
if (has_requester())
{
requester().tracker_request_error(m_req, 200
, "too large tracker response");
}
return true;
fail(200, "too large tracker response");
return;
}
assert(http_buffer_size > 0);
if ((int)m_buffer.size() + http_buffer_size
> m_settings.tracker_maximum_response_length)
m_buffer.resize(m_settings.tracker_maximum_response_length);
else
m_buffer.resize(m_buffer.size() + http_buffer_size);
}
assert(m_recv_pos >= 0);
assert(m_recv_pos < (int)m_buffer.size());
int received = m_socket->receive(&m_buffer[m_recv_pos], (int)m_buffer.size() - m_recv_pos);
assert(received <= (int)m_buffer.size() - m_recv_pos);
if (received > 0) m_recv_pos += received;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("received: " + boost::lexical_cast<std::string>(m_recv_pos));
#endif
if (m_state == read_status)
{
if (received <= 0)
{
if (has_requester())
{
requester().tracker_request_error(m_req, -1
, "invalid tracker response, connection closed");
}
return true;
}
std::vector<char>::iterator end = m_buffer.begin()+m_recv_pos;
std::vector<char>::iterator newline = std::find(m_buffer.begin(), end, '\n');
// if we don't have a full line yet, wait.
if (newline == end) return false;
if (newline == end) return;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log(std::string(m_buffer.begin(), newline));
@ -369,8 +531,8 @@ namespace libtorrent
if (m_server_protocol.substr(0, 5) != "HTTP/")
{
std::string error_msg = "unknown protocol in response: " + m_server_protocol;
if (has_requester()) requester().tracker_request_error(m_req, -1, error_msg.c_str());
return true;
fail(-1, error_msg.c_str());
return;
}
line >> m_code;
std::getline(line, m_server_message);
@ -379,15 +541,6 @@ namespace libtorrent
if (m_state == read_header)
{
if (received <= 0)
{
if (has_requester())
requester().tracker_request_error(m_req, -1
, "invalid tracker response, connection closed "
"while reading header");
return true;
}
std::vector<char>::iterator end = m_buffer.begin() + m_recv_pos;
std::vector<char>::iterator newline
= std::find(m_buffer.begin(), end, '\n');
@ -410,31 +563,19 @@ namespace libtorrent
}
catch(boost::bad_lexical_cast&)
{
if (has_requester())
{
requester().tracker_request_error(m_req, -1,
"invalid content-length in tracker response");
}
return true;
fail(-1, "invalid content-length in tracker response");
return;
}
if (m_content_length > m_settings.tracker_maximum_response_length)
{
if (has_requester())
{
requester().tracker_request_error(m_req, -1
, "content-length is greater than maximum response length");
}
return true;
fail(-1, "content-length is greater than maximum response length");
return;
}
if (m_content_length < minimum_tracker_response_length && m_code == 200)
{
if (has_requester())
{
requester().tracker_request_error(m_req, -1
, "content-length is smaller than minimum response length");
}
return true;
fail(-1, "content-length is smaller than minimum response length");
return;
}
}
else if (line.substr(0, 18) == "Content-Encoding: ")
@ -448,10 +589,8 @@ namespace libtorrent
std::string error_str = "unknown content encoding in response: \"";
error_str += line.substr(18, line.length() - 18 - 2);
error_str += "\"";
if (has_requester())
requester().tracker_request_error(m_req, -1
, error_str.c_str());
return true;
fail(-1, error_str.c_str());
return;
}
}
else if (line.substr(0, 10) == "Location: ")
@ -475,23 +614,24 @@ namespace libtorrent
std::string error_str = "got redirection response (";
error_str += boost::lexical_cast<std::string>(m_code);
error_str += ") without 'Location' header";
if (has_requester())
requester().tracker_request_error(m_req
, m_code, error_str.c_str());
return true;
fail(-1, error_str.c_str());
return;
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("Redirecting to \"" + m_location + "\"");
#endif
tracker_request req = tracker_req();
std::string::size_type i = m_location.find('?');
if (i == std::string::npos)
m_req.url = m_location;
req.url = m_location;
else
m_req.url.assign(m_location.begin(), m_location.begin() + i);
req.url.assign(m_location.begin(), m_location.begin() + i);
m_man.queue_request(m_req, m_password, m_requester);
return true;
m_man.queue_request(m_socket->io_service(), req
, m_password, m_requester);
close();
return;
}
}
@ -508,16 +648,48 @@ namespace libtorrent
if (m_state == read_body)
{
if (m_recv_pos == m_content_length || received <= 0)
if (m_recv_pos == m_content_length)
{
on_response();
close();
return;
}
}
else if (m_recv_pos > m_content_length && m_content_length > 0)
{
fail(-1, "invalid tracker response (body > content_length)");
return;
}
assert(m_buffer.size() - m_recv_pos > 0);
m_socket->async_read_some(asio::buffer(&m_buffer[m_recv_pos]
, m_buffer.size() - m_recv_pos), bind(&http_tracker_connection::receive
, self(), _1, _2));
}
catch (std::exception& e)
{
assert(false);
fail(-1, e.what());
};
void http_tracker_connection::on_response()
{
// GZIP
if (m_content_encoding == gzip)
{
boost::shared_ptr<request_callback> r = m_requester.lock();
if (!r) return true;
if (!r)
{
close();
return;
}
if (inflate_gzip(m_buffer, m_req, r.get(),
m_settings.tracker_maximum_response_length))
return true;
{
close();
return;
}
}
// handle tracker response
@ -526,34 +698,17 @@ namespace libtorrent
entry e = bdecode(m_buffer.begin(), m_buffer.end());
parse(e);
}
catch (std::exception&)
catch (std::exception& e)
{
std::string error_str(m_buffer.begin(), m_buffer.end());
if (has_requester()) requester().tracker_request_error(m_req, m_code, error_str);
std::string error_str(e.what());
error_str += ": ";
error_str.append(m_buffer.begin(), m_buffer.end());
fail(m_code, error_str.c_str());
}
return true;
}
return false;
}
else if (m_recv_pos > m_content_length && m_content_length > 0)
#ifndef NDEBUG
catch (...)
{
if (has_requester())
{
requester().tracker_request_error(m_req, -1
, "invalid tracker response (body > content_length)");
}
return true;
}
return false;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
}
catch (std::exception&)
{
if (has_requester())
requester().debug_log(std::string(m_buffer.begin(), m_buffer.end()));
throw;
assert(false);
}
#endif
}
@ -568,12 +723,12 @@ namespace libtorrent
{
if (i->string().length() != 20)
throw std::runtime_error("invalid response from tracker");
std::copy(i->string().begin(), i->string().end(), ret.id.begin());
std::copy(i->string().begin(), i->string().end(), ret.pid.begin());
}
else
{
// if there's no peer_id, just initialize it to a bunch of zeroes
std::fill_n(ret.id.begin(), 20, 0);
std::fill_n(ret.pid.begin(), 20, 0);
}
// extract ip
@ -600,8 +755,7 @@ namespace libtorrent
{
entry const& failure = e["failure reason"];
if (has_requester()) requester().tracker_request_error(
m_req, m_code, failure.string().c_str());
fail(m_code, failure.string().c_str());
return;
}
catch (type_error const&) {}
@ -616,10 +770,10 @@ namespace libtorrent
std::vector<peer_entry> peer_list;
if (m_req.kind == tracker_request::scrape_request)
if (tracker_req().kind == tracker_request::scrape_request)
{
std::string ih;
std::copy(m_req.info_hash.begin(), m_req.info_hash.end()
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end()
, std::back_inserter(ih));
entry scrape_data = e["files"][ih];
int complete = scrape_data["complete"].integer();
@ -640,7 +794,7 @@ namespace libtorrent
if (std::distance(i, peers.end()) < 6) break;
peer_entry p;
p.id.clear();
p.pid.clear();
std::stringstream ip_str;
ip_str << (int)detail::read_uint8(i) << ".";
ip_str << (int)detail::read_uint8(i) << ".";
@ -661,7 +815,7 @@ namespace libtorrent
}
}
// look for optional crape info
// look for optional scrape info
int complete = -1;
int incomplete = -1;

View File

@ -71,8 +71,8 @@ namespace
|| id[7] != '-')
return boost::optional<fingerprint>();
ret.id[0] = id[1];
ret.id[1] = id[2];
ret.name[0] = id[1];
ret.name[1] = id[2];
ret.major_version = decode_digit(id[3]);
ret.minor_version = decode_digit(id[4]);
ret.revision_version = decode_digit(id[5]);
@ -108,8 +108,8 @@ namespace
ret.revision_version = id[3];
}
ret.id[0] = id[0];
ret.id[1] = 0;
ret.name[0] = id[0];
ret.name[1] = 0;
ret.tag_version = 0;
return boost::optional<fingerprint>(ret);
@ -130,8 +130,8 @@ namespace
fingerprint ret("..", 0, 0, 0, 0);
ret.id[0] = id[0];
ret.id[1] = 0;
ret.name[0] = id[0];
ret.name[1] = 0;
ret.major_version = decode_digit(id[1]);
ret.minor_version = decode_digit(id[3]);
ret.revision_version = decode_digit(id[5]);
@ -166,6 +166,7 @@ namespace
, map_entry("SZ", "Shareaza")
, map_entry("T", "BitTornado")
, map_entry("TN", "Torrent.NET")
, map_entry("TR", "Transmission")
, map_entry("TS", "TorrentStorm")
, map_entry("U", "UPnP")
, map_entry("UT", "MicroTorrent")
@ -188,7 +189,7 @@ namespace
const int size = sizeof(name_map)/sizeof(name_map[0]);
map_entry* i =
std::lower_bound(name_map, name_map + size
, map_entry(f.id, ""), &compare_first_string);
, map_entry(f.name, ""), &compare_first_string);
#ifndef NDEBUG
for (int i = 1; i < size; ++i)
@ -198,19 +199,19 @@ namespace
}
#endif
if (i < name_map + size && std::equal(f.id, f.id + 2, i->first))
if (i < name_map + size && std::equal(f.name, f.name + 2, i->first))
identity << i->second;
else
{
identity << f.id[0];
if (f.id[1] != 0) identity << f.id[1];
identity << f.name[0];
if (f.name[1] != 0) identity << f.name[1];
}
identity << " " << (int)f.major_version
<< "." << (int)f.minor_version
<< "." << (int)f.revision_version;
if (f.id[1] != 0)
if (f.name[1] != 0)
identity << "." << (int)f.tag_version;
return identity.str();

View File

@ -37,10 +37,11 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent
{
ip_filter::ip_filter()
{
// make the entire ip-range non-blocked
m_access_list.insert(range(address(0,0,0,0,0), 0));
m_access_list.insert(range(address(0UL), 0));
}
void ip_filter::add_rule(address first, address last, int flags)
@ -70,7 +71,7 @@ namespace libtorrent
if (i->start != first && first_access != flags)
{
i = m_access_list.insert(i, range(address(first.ip(), 0), flags));
i = m_access_list.insert(i, range(first, flags));
}
else if (i != m_access_list.begin() && prior(i)->access == flags)
{
@ -100,16 +101,16 @@ namespace libtorrent
}
else if (first_access != flags)
{
m_access_list.insert(i, range(address(first.ip(), 0), flags));
m_access_list.insert(i, range(first, flags));
}
if ((j != m_access_list.end() && j->start.ip() - 1 != last.ip())
|| (j == m_access_list.end() && last.ip() != 0xffffffff))
if ((j != m_access_list.end() && j->start.to_ulong() - 1 != last.to_ulong())
|| (j == m_access_list.end() && last.to_ulong() != 0xffffffff))
{
assert(j == m_access_list.end() || last.ip() < j->start.ip() - 1);
assert(j == m_access_list.end() || last.to_ulong() < j->start.to_ulong() - 1);
// std::cout << " -- last_access: " << last_access << "\n";
if (last_access != flags)
j = m_access_list.insert(j, range(address(last.ip() + 1, 0), last_access));
j = m_access_list.insert(j, range(address(last.to_ulong() + 1), last_access));
}
if (j != m_access_list.end() && j->access == flags) m_access_list.erase(j);
@ -138,14 +139,13 @@ namespace libtorrent
{
ip_range r;
r.first = i->start;
assert(r.first.port == 0);
r.flags = i->access;
++i;
if (i == end)
r.last = address(0xffffffff, 0);
r.last = address(0xffffffff);
else
r.last = address(i->start.ip() - 1, 0);
r.last = address(i->start.to_ulong() - 1);
ret.push_back(r);
}

File diff suppressed because it is too large Load Diff

View File

@ -45,12 +45,14 @@ POSSIBILITY OF SUCH DAMAGE.
namespace libtorrent
{
piece_picker::piece_picker(int blocks_per_piece, int total_num_blocks)
piece_picker::piece_picker(int blocks_per_piece, int total_num_blocks
, int sequenced_download_threshold)
: m_piece_info(2)
, m_downloading_piece_info(2)
, m_piece_map((total_num_blocks + blocks_per_piece-1) / blocks_per_piece)
, m_num_filtered(0)
, m_num_have_filtered(0)
, m_sequenced_download_threshold(sequenced_download_threshold)
{
assert(blocks_per_piece > 0);
assert(total_num_blocks >= 0);
@ -104,12 +106,9 @@ namespace libtorrent
}
}
// random shuffle the list
std::random_shuffle(piece_list.begin(), piece_list.end());
// add the pieces to the piece_picker
for (std::vector<int>::iterator i = piece_list.begin();
i != piece_list.end(); ++i)
for (std::vector<int>::reverse_iterator i = piece_list.rbegin();
i != piece_list.rend(); ++i)
{
int index = *i;
assert(index >= 0);
@ -133,7 +132,7 @@ namespace libtorrent
for (std::vector<downloading_piece>::const_iterator i
= unfinished.begin(); i != unfinished.end(); ++i)
{
address peer;
tcp::endpoint peer;
for (int j = 0; j < m_blocks_per_piece; ++j)
{
if (i->finished_blocks[j])
@ -146,6 +145,34 @@ namespace libtorrent
#endif
}
void piece_picker::set_sequenced_download_threshold(
int sequenced_download_threshold)
{
#ifndef NDEBUG
integrity_check();
#endif
if (sequenced_download_threshold == m_sequenced_download_threshold)
return;
int old_limit = m_sequenced_download_threshold;
m_sequenced_download_threshold = sequenced_download_threshold;
for (std::vector<piece_pos>::iterator i = m_piece_map.begin()
, end(m_piece_map.end()); i != end; ++i)
{
if (i->priority(old_limit) != i->priority(m_sequenced_download_threshold))
{
piece_pos& p = m_piece_map[i->index];
int prev_priority = i->priority(old_limit);
move(p.downloading, p.filtered, prev_priority, i->index);
}
}
#ifndef NDEBUG
integrity_check();
#endif
}
#ifndef NDEBUG
void piece_picker::integrity_check(const torrent* t) const
@ -236,8 +263,8 @@ namespace libtorrent
assert(!t->have_piece(index));
const std::vector<std::vector<int> >& c_vec = pick_piece_info_vector(i->downloading, i->filtered);
assert(i->peer_count < c_vec.size());
const std::vector<int>& vec = c_vec[i->peer_count];
assert(i->priority(m_sequenced_download_threshold) < (int)c_vec.size());
const std::vector<int>& vec = c_vec[i->priority(m_sequenced_download_threshold)];
assert(i->index < vec.size());
assert(vec[i->index] == index);
}
@ -302,80 +329,159 @@ namespace libtorrent
std::vector<std::vector<int> >& dst_vec = pick_piece_info_vector(
p.downloading, p.filtered);
if (dst_vec.size() <= p.peer_count)
dst_vec.resize(p.peer_count + 1);
int priority = p.priority(m_sequenced_download_threshold);
if ((int)dst_vec.size() <= priority)
dst_vec.resize(priority + 1);
assert(dst_vec.size() > p.peer_count);
p.index = (int)dst_vec[p.peer_count].size();
dst_vec[p.peer_count].push_back(index);
assert((int)dst_vec.size() > priority);
if (p.ordered(m_sequenced_download_threshold))
{
// the piece should be inserted ordered, not randomly
std::vector<int>& v = dst_vec[priority];
std::vector<int>::iterator i = std::lower_bound(v.begin(), v.end()
, index, std::greater<int>());
p.index = i - v.begin();
v.insert(i, index);
i = v.begin() + p.index + 1;
for (;i != v.end(); ++i)
{
++m_piece_map[*i].index;
assert(v[m_piece_map[*i].index] == *i);
}
}
else if (dst_vec[priority].size() < 2)
{
p.index = dst_vec[priority].size();
dst_vec[priority].push_back(index);
}
else
{
// find a random position in the destination vector where we will place
// this entry.
int dst_index = rand() % dst_vec[priority].size();
// copy the entry at that position to the back
m_piece_map[dst_vec[priority][dst_index]].index
= dst_vec[priority].size();
dst_vec[priority].push_back(dst_vec[priority][dst_index]);
// and then replace the one at dst_index with the one we're moving.
// this procedure is to make sure there's no ordering when pieces
// are moved in sequenced order.
p.index = dst_index;
dst_vec[priority][p.index] = index;
}
}
// will update the piece with the given properties (downloading, filtered, peer_count, elem_index)
// to place it at the correct position in the vectors.
void piece_picker::move(bool downloading, bool filtered, int peer_count, int elem_index)
// will update the piece with the given properties (downloading, filtered,
// priority, elem_index) to place it at the correct position in the
// vectors.
void piece_picker::move(bool downloading, bool filtered, int priority
, int elem_index)
{
assert(!filtered);
assert(peer_count >= 0);
assert(priority >= 0);
assert(elem_index >= 0);
std::vector<std::vector<int> >& src_vec(pick_piece_info_vector(downloading, filtered));
std::vector<std::vector<int> >& src_vec(pick_piece_info_vector(
downloading, filtered));
assert((int)src_vec.size() > peer_count);
assert((int)src_vec[peer_count].size() > elem_index);
assert((int)src_vec.size() > priority);
assert((int)src_vec[priority].size() > elem_index);
int index = src_vec[peer_count][elem_index];
int index = src_vec[priority][elem_index];
// update the piece_map
piece_pos& p = m_piece_map[index];
int new_priority = p.priority(m_sequenced_download_threshold);
assert(p.downloading != downloading
|| p.filtered != filtered
|| (int)p.peer_count != peer_count);
|| (int)new_priority != priority);
std::vector<std::vector<int> >& dst_vec(pick_piece_info_vector(p.downloading, p.filtered));
std::vector<std::vector<int> >& dst_vec(pick_piece_info_vector(
p.downloading, p.filtered));
if (dst_vec.size() <= p.peer_count)
if ((int)dst_vec.size() <= new_priority)
{
dst_vec.resize(p.peer_count+1);
assert(dst_vec.size() > p.peer_count);
dst_vec.resize(new_priority + 1);
assert((int)dst_vec.size() > new_priority);
}
p.index = (int)dst_vec[p.peer_count].size();
dst_vec[p.peer_count].push_back(index);
assert(p.index < dst_vec[p.peer_count].size());
assert(dst_vec[p.peer_count][p.index] == index);
if (p.ordered(m_sequenced_download_threshold))
{
// the piece should be inserted ordered, not randomly
std::vector<int>& v = dst_vec[new_priority];
std::vector<int>::iterator i = std::lower_bound(v.begin(), v.end()
, index, std::greater<int>());
p.index = i - v.begin();
v.insert(i, index);
i = v.begin() + p.index + 1;
for (;i != v.end(); ++i)
{
++m_piece_map[*i].index;
assert(v[m_piece_map[*i].index] == *i);
}
}
else if (dst_vec[new_priority].size() < 2)
{
p.index = dst_vec[new_priority].size();
dst_vec[new_priority].push_back(index);
}
else
{
// find a random position in the destination vector where we will place
// this entry.
int dst_index = rand() % dst_vec[new_priority].size();
// copy the entry at that position to the back
m_piece_map[dst_vec[new_priority][dst_index]].index
= dst_vec[new_priority].size();
dst_vec[new_priority].push_back(dst_vec[new_priority][dst_index]);
// and then replace the one at dst_index with the one we're moving.
// this procedure is to make sure there's no ordering when pieces
// are moved in sequenced order.
p.index = dst_index;
dst_vec[new_priority][p.index] = index;
}
assert(p.index < dst_vec[p.priority(m_sequenced_download_threshold)].size());
assert(dst_vec[p.priority(m_sequenced_download_threshold)][p.index] == index);
// this will remove elem from the source vector without
// preserving order, but the order is random anyway
int replace_index = src_vec[peer_count][elem_index] = src_vec[peer_count].back();
int replace_index = src_vec[priority][elem_index] = src_vec[priority].back();
if (index != replace_index)
{
// update the entry we moved from the back
m_piece_map[replace_index].index = elem_index;
assert((int)src_vec[peer_count].size() > elem_index);
assert((int)m_piece_map[replace_index].peer_count == peer_count);
assert((int)src_vec[priority].size() > elem_index);
assert((int)m_piece_map[replace_index].priority(m_sequenced_download_threshold) == priority);
assert((int)m_piece_map[replace_index].index == elem_index);
assert(src_vec[peer_count][elem_index] == replace_index);
assert(src_vec[priority][elem_index] == replace_index);
}
else
{
assert((int)src_vec[peer_count].size() == elem_index+1);
assert((int)src_vec[priority].size() == elem_index+1);
}
src_vec[peer_count].pop_back();
src_vec[priority].pop_back();
}
void piece_picker::remove(bool downloading, bool filtered, int peer_count, int elem_index)
void piece_picker::remove(bool downloading, bool filtered, int priority
, int elem_index)
{
assert(!filtered);
assert(peer_count >= 0);
assert(priority >= 0);
assert(elem_index >= 0);
std::vector<std::vector<int> >& src_vec(pick_piece_info_vector(downloading, filtered));
assert((int)src_vec.size() > peer_count);
assert((int)src_vec[peer_count].size() > elem_index);
assert((int)src_vec.size() > priority);
assert((int)src_vec[priority].size() > elem_index);
int index = src_vec[peer_count][elem_index];
int index = src_vec[priority][elem_index];
if (downloading)
{
@ -386,16 +492,30 @@ namespace libtorrent
assert(i != m_downloads.end());
m_downloads.erase(i);
}
m_piece_map[index].downloading = 0;
piece_pos& p = m_piece_map[index];
p.downloading = 0;
if (p.ordered(m_sequenced_download_threshold))
{
std::vector<int>& v = src_vec[priority];
std::vector<int>::iterator i = v.begin() + elem_index;
v.erase(i);
i = v.begin() + elem_index;
for (; i != v.end(); ++i)
{
--m_piece_map[*i].index;
assert(v[m_piece_map[*i].index] == *i);
}
}
else
{
// this will remove elem from the vector without
// preserving order
index = src_vec[peer_count][elem_index] = src_vec[peer_count].back();
index = src_vec[priority][elem_index] = src_vec[priority].back();
// update the entry we moved from the back
if ((int)src_vec[peer_count].size() > elem_index+1)
if ((int)src_vec[priority].size() > elem_index+1)
m_piece_map[index].index = elem_index;
src_vec[peer_count].pop_back();
src_vec[priority].pop_back();
}
}
void piece_picker::restore_piece(int index)
@ -415,7 +535,7 @@ namespace libtorrent
m_piece_map[index].downloading = 0;
piece_pos& p = m_piece_map[index];
if (p.filtered) return;
move(true, p.filtered, p.peer_count, p.index);
move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
#ifndef NDEBUG
// integrity_check();
@ -427,10 +547,10 @@ namespace libtorrent
assert(i >= 0);
assert(i < (int)m_piece_map.size());
int peer_count = m_piece_map[i].peer_count;
int index = m_piece_map[i].index;
int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold);
assert(peer_count < 2048);
assert(m_piece_map[i].peer_count < 2048);
m_piece_map[i].peer_count++;
assert(m_piece_map[i].peer_count != 0);
@ -438,9 +558,10 @@ namespace libtorrent
// if we have the piece or if it's filtered
// we don't have to move any entries in the piece_info vector
if (index == piece_pos::we_have_index || p.filtered) return;
if (index == piece_pos::we_have_index || p.filtered
|| p.priority(m_sequenced_download_threshold) == prev_priority) return;
move(p.downloading, p.filtered, peer_count, index);
move(p.downloading, p.filtered, prev_priority, index);
#ifndef NDEBUG
// integrity_check();
@ -457,18 +578,19 @@ namespace libtorrent
assert(i >= 0);
assert(i < (int)m_piece_map.size());
int peer_count = m_piece_map[i].peer_count;
int prev_priority = m_piece_map[i].priority(m_sequenced_download_threshold);
int index = m_piece_map[i].index;
assert(peer_count > 0);
assert(m_piece_map[i].peer_count > 0);
if (m_piece_map[i].peer_count > 0)
m_piece_map[i].peer_count--;
piece_pos& p = m_piece_map[i];
if (index == piece_pos::we_have_index || p.filtered) return;
if (index == piece_pos::we_have_index || p.filtered
|| p.priority(m_sequenced_download_threshold) == prev_priority) return;
move(p.downloading, p.filtered, peer_count, index);
move(p.downloading, p.filtered, prev_priority, index);
}
// this is used to indicate that we succesfully have
@ -481,7 +603,7 @@ namespace libtorrent
assert(index < (int)m_piece_map.size());
int info_index = m_piece_map[index].index;
int peer_count = m_piece_map[index].peer_count;
int priority = m_piece_map[index].priority(m_sequenced_download_threshold);
assert(m_piece_map[index].downloading == 1);
@ -494,7 +616,7 @@ namespace libtorrent
return;
}
if (info_index == piece_pos::we_have_index) return;
remove(p.downloading, p.filtered, peer_count, info_index);
remove(p.downloading, p.filtered, priority, info_index);
p.index = piece_pos::we_have_index;
#ifndef NDEBUG
integrity_check();
@ -517,7 +639,7 @@ namespace libtorrent
if (p.index != piece_pos::we_have_index)
{
++m_num_filtered;
remove(p.downloading, false, p.peer_count, p.index);
remove(p.downloading, false, p.priority(m_sequenced_download_threshold), p.index);
assert(p.filtered == 1);
}
else
@ -582,7 +704,7 @@ namespace libtorrent
void piece_picker::pick_pieces(const std::vector<bool>& pieces
, std::vector<piece_block>& interesting_blocks
, int num_blocks, bool prefer_whole_pieces
, address peer) const
, tcp::endpoint peer) const
{
assert(num_blocks > 0);
assert(pieces.size() == m_piece_map.size());
@ -599,9 +721,13 @@ namespace libtorrent
assert(m_piece_info.begin() != m_piece_info.end());
// +1 is to ignore pieces that no peer has. The bucket with index 0 contains
// pieces that 0 other peers has.
std::vector<std::vector<int> >::const_iterator free = m_piece_info.begin()+1;
assert(m_downloading_piece_info.begin() != m_downloading_piece_info.end());
std::vector<std::vector<int> >::const_iterator partial = m_downloading_piece_info.begin()+1;
std::vector<std::vector<int> >::const_iterator free
= m_piece_info.begin() + 1;
assert(m_downloading_piece_info.begin()
!= m_downloading_piece_info.end());
std::vector<std::vector<int> >::const_iterator partial
= m_downloading_piece_info.begin() + 1;
std::vector<piece_block> backup_blocks;
@ -610,15 +736,18 @@ namespace libtorrent
// has filled the interesting_blocks with num_blocks
// blocks.
// it iterates over two ranges simultaneously. The pieces that are partially downloaded
// or partially requested, and the pieces that hasn't been requested at all.
// The default is to prioritize pieces that are partially requested/downloaded, so the
// loop will first look for blocks among those pieces. And it will also take two steps
// in that range when iterating. This has the effect that partial pieces doesn't have to
// be as rare as non-requested pieces in order to be prefered.
// it iterates over two ranges simultaneously. The pieces that are
// partially downloaded or partially requested, and the pieces that
// hasn't been requested at all. The default is to prioritize pieces
// that are partially requested/downloaded, so the loop will first
// look for blocks among those pieces. And it will also take two steps
// in that range when iterating. This has the effect that partial pieces
// doesn't have to be as rare as non-requested pieces in order to be
// prefered.
// When prefer_whole_pieces is set (usually set when downloading from fast peers) the
// partial pieces will not be prioritized, but actually ignored as long as possible.
// When prefer_whole_pieces is set (usually set when downloading from
// fast peers) the partial pieces will not be prioritized, but actually
// ignored as long as possible.
while((free != m_piece_info.end())
|| (partial != m_downloading_piece_info.end()))
@ -664,14 +793,14 @@ namespace libtorrent
namespace
{
bool exclusively_requested_from(piece_picker::downloading_piece const& p
, int num_blocks_in_piece, address peer)
, int num_blocks_in_piece, tcp::endpoint peer)
{
for (int j = 0; j < num_blocks_in_piece; ++j)
{
if ((p.finished_blocks[j] == 1
|| p.requested_blocks[j] == 1)
&& p.info[j].peer != peer
&& p.info[j].peer != address())
&& p.info[j].peer != tcp::endpoint())
{
return false;
}
@ -715,7 +844,7 @@ namespace libtorrent
, std::vector<piece_block>& interesting_blocks
, std::vector<piece_block>& backup_blocks
, int num_blocks, bool prefer_whole_pieces
, address peer) const
, tcp::endpoint peer) const
{
assert(num_blocks > 0);
@ -842,7 +971,7 @@ namespace libtorrent
}
void piece_picker::mark_as_downloading(piece_block block, const address& peer)
void piece_picker::mark_as_downloading(piece_block block, const tcp::endpoint& peer)
{
#ifndef NDEBUG
// integrity_check();
@ -856,7 +985,7 @@ namespace libtorrent
if (p.downloading == 0)
{
p.downloading = 1;
move(false, p.filtered, p.peer_count, p.index);
move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
downloading_piece dp;
dp.index = block.piece_index;
@ -878,7 +1007,7 @@ namespace libtorrent
#endif
}
void piece_picker::mark_as_finished(piece_block block, const address& peer)
void piece_picker::mark_as_finished(piece_block block, const tcp::endpoint& peer)
{
#ifndef NDEBUG
// integrity_check();
@ -894,7 +1023,7 @@ namespace libtorrent
if (p.downloading == 0)
{
p.downloading = 1;
move(false, p.filtered, p.peer_count, p.index);
move(false, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
downloading_piece dp;
dp.index = block.piece_index;
@ -945,7 +1074,7 @@ namespace libtorrent
#endif
}
*/
void piece_picker::get_downloaders(std::vector<address>& d, int index) const
void piece_picker::get_downloaders(std::vector<tcp::endpoint>& d, int index) const
{
assert(index >= 0 && index <= (int)m_piece_map.size());
std::vector<downloading_piece>::const_iterator i
@ -959,7 +1088,7 @@ namespace libtorrent
}
}
boost::optional<address> piece_picker::get_downloader(piece_block block) const
boost::optional<tcp::endpoint> piece_picker::get_downloader(piece_block block) const
{
std::vector<downloading_piece>::const_iterator i = std::find_if(
m_downloads.begin()
@ -967,16 +1096,16 @@ namespace libtorrent
, has_index(block.piece_index));
if (i == m_downloads.end())
return boost::optional<address>();
return boost::optional<tcp::endpoint>();
assert(block.block_index < max_blocks_per_piece);
assert(block.block_index >= 0);
if (i->requested_blocks[block.block_index] == false
|| i->finished_blocks[block.block_index] == true)
return boost::optional<address>();
return boost::optional<tcp::endpoint>();
return boost::optional<address>(i->info[block.block_index].peer);
return boost::optional<tcp::endpoint>(i->info[block.block_index].peer);
}
void piece_picker::abort_download(piece_block block)
@ -1003,7 +1132,12 @@ namespace libtorrent
if (i->finished_blocks[block.block_index]) return;
assert(block.block_index < blocks_in_piece(block.piece_index));
assert(i->requested_blocks[block.block_index] == 1);
#ifndef NDEBUG
if (i->requested_blocks[block.block_index] != 1)
{
assert(false);
}
#endif
// clear this block as being downloaded
i->requested_blocks[block.block_index] = 0;
@ -1015,7 +1149,7 @@ namespace libtorrent
m_downloads.erase(i);
m_piece_map[block.piece_index].downloading = 0;
piece_pos& p = m_piece_map[block.piece_index];
move(true, p.filtered, p.peer_count, p.index);
move(true, p.filtered, p.priority(m_sequenced_download_threshold), p.index);
}
#ifndef NDEBUG
// integrity_check();

View File

@ -45,6 +45,7 @@ POSSIBILITY OF SUCH DAMAGE.
#pragma warning(pop)
#endif
#include "libtorrent/web_peer_connection.hpp"
#include "libtorrent/policy.hpp"
#include "libtorrent/torrent.hpp"
#include "libtorrent/socket.hpp"
@ -72,26 +73,8 @@ namespace
, peer_connection& c
, std::vector<peer_connection*> ignore = std::vector<peer_connection*>())
{
// this will make the number of requests linearly dependent
// on the rate in which we download from the peer.
// we want the queue to represent:
// TODO: make this constant user-settable
const int queue_time = 3; // seconds
// (if the latency is more than this, the download will stall)
// so, the queue size is 5 * down_rate / 16 kiB (16 kB is the size of each request)
// the minimum request size is 2 and the maximum is 48
// the block size doesn't have to be 16. So we first query the torrent for it
const int block_size = t.block_size();
assert(block_size > 0);
int desired_queue_size = static_cast<int>(queue_time
* c.statistics().download_rate() / block_size);
if (desired_queue_size > max_request_queue) desired_queue_size = max_request_queue;
if (desired_queue_size < min_request_queue) desired_queue_size = min_request_queue;
assert(desired_queue_size >= min_request_queue);
int num_requests = desired_queue_size - (int)c.download_queue().size()
int num_requests = c.desired_queue_size()
- (int)c.download_queue().size()
- (int)c.request_queue().size();
// if our request queue is already full, we
@ -117,7 +100,9 @@ namespace
// the number of blocks we want, but it will try to make the picked
// blocks be from whole pieces, possibly by returning more blocks
// than we requested.
assert(c.remote() == c.get_socket()->sender());
#ifndef NDEBUG
assert(c.remote() == c.get_socket()->remote_endpoint());
#endif
p.pick_pieces(c.get_bitfield(), interesting_pieces
, num_requests, prefer_whole_pieces, c.remote());
@ -141,7 +126,7 @@ namespace
// ok, we found a piece that's not being downloaded
// by somebody else. request it from this peer
// and return
c.send_request(*i);
c.add_request(*i);
num_requests--;
}
@ -217,8 +202,8 @@ namespace
assert(common_block != peer->download_queue().rend());
piece_block block = *common_block;
peer->send_cancel(block);
c.send_request(block);
peer->cancel_request(block);
c.add_request(block);
// the one we interrupted may need to request a new piece
// make sure it doesn't take over a block from the peer
@ -308,14 +293,14 @@ namespace
struct match_peer_ip
{
match_peer_ip(const address& id)
: m_id(id)
match_peer_ip(const tcp::endpoint& ip)
: m_ip(ip)
{}
bool operator()(const policy::peer& p) const
{ return p.id.ip() == m_id.ip(); }
{ return p.ip.address() == m_ip.address(); }
address m_id;
tcp::endpoint m_ip;
};
struct match_peer_connection
@ -837,10 +822,18 @@ namespace libtorrent
, m_peers.end()
, match_peer_connection(c));
assert(i != m_peers.end());
if (i == m_peers.end())
{
// this is probably a http seed
if (web_peer_connection const* p = dynamic_cast<web_peer_connection const*>(&c))
{
m_torrent->remove_url_seed(p->url());
}
return;
}
i->type = peer::not_connectable;
i->id.port = address::any_port;
i->ip.port(0);
i->banned = true;
}
@ -850,7 +843,7 @@ namespace libtorrent
/*
#ifndef NDEBUG
// avoid the invariant check to fail
peer p(address("0.0.0.0", 0), peer::not_connectable);
peer p(tcp::endpoint("0.0.0.0", 0), peer::not_connectable);
p.connection = &c;
m_peers.push_back(p);
#endif
@ -868,15 +861,17 @@ namespace libtorrent
// TODO: only allow _one_ connection to use this
// override at a time
assert(c.remote() == c.get_socket()->sender());
#ifndef NDEBUG
assert(c.remote() == c.get_socket()->remote_endpoint());
#endif
if (m_torrent->num_peers() >= m_torrent->m_connections_quota.given
&& c.remote().ip() != m_torrent->current_tracker().ip())
&& c.remote().address() != m_torrent->current_tracker().address())
{
throw protocol_error("too many connections, refusing incoming connection"); // cause a disconnect
}
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (c.remote().ip() == m_torrent->current_tracker().ip())
if (c.remote().address() == m_torrent->current_tracker().address())
{
m_torrent->debug_log("overriding connection limit for tracker NAT-check");
}
@ -894,8 +889,9 @@ namespace libtorrent
// we don't have ny info about this peer.
// add a new entry
assert(c.remote() == c.get_socket()->sender());
#ifndef NDEBUG
assert(c.remote() == c.get_socket()->remote_endpoint());
#endif
peer p(c.remote(), peer::not_connectable);
m_peers.push_back(p);
i = m_peers.end()-1;
@ -905,7 +901,7 @@ namespace libtorrent
if (i->connection != 0)
throw protocol_error("duplicate connection, closing");
if (i->banned)
throw protocol_error("ip address banned, closing");
throw protocol_error("ip tcp::endpoint banned, closing");
}
assert(i->connection == 0);
@ -918,12 +914,12 @@ namespace libtorrent
m_last_optimistic_disconnect = second_clock::universal_time();
}
void policy::peer_from_tracker(const address& remote, const peer_id& id)
void policy::peer_from_tracker(const tcp::endpoint& remote, const peer_id& pid)
{
INVARIANT_CHECK;
// just ignore the obviously invalid entries from the tracker
if(remote.ip() == 0 || remote.port == 0)
if(remote.address().to_ulong() == 0 || remote.port() == 0)
return;
try
@ -956,7 +952,7 @@ namespace libtorrent
// in case we got the ip from a remote connection, port is
// not known, so save it. Client may also have changed port
// for some reason.
i->id = remote;
i->ip = remote;
if (i->connection)
{
@ -965,11 +961,11 @@ namespace libtorrent
// it again.
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
m_torrent->debug_log("already connected to peer: " + remote.as_string() + ":"
+ boost::lexical_cast<std::string>(remote.port));
m_torrent->debug_log("already connected to peer: " + remote.address().to_string() + ":"
+ boost::lexical_cast<std::string>(remote.port()));
#endif
assert(i->connection->associated_torrent() == m_torrent);
assert(i->connection->associated_torrent().lock().get() == m_torrent);
return;
}
}
@ -990,20 +986,12 @@ namespace libtorrent
}
return;
}
catch(network_error& e)
catch(std::exception& e)
{
if (m_torrent->alerts().should_post(alert::debug))
{
m_torrent->alerts().post_alert(
peer_error_alert(remote, id, e.what()));
}
}
catch(protocol_error& e)
{
if (m_torrent->alerts().should_post(alert::debug))
{
m_torrent->alerts().post_alert(
peer_error_alert(remote, id, e.what()));
peer_error_alert(remote, pid, e.what()));
}
}
}
@ -1174,7 +1162,7 @@ namespace libtorrent
try
{
assert(!p->connection);
p->connection = &m_torrent->connect_to_peer(p->id);
p->connection = &m_torrent->connect_to_peer(p->ip);
assert(p->connection);
p->connection->add_stat(p->prev_amount_download, p->prev_amount_upload);
p->prev_amount_download = 0;
@ -1184,14 +1172,8 @@ namespace libtorrent
second_clock::universal_time();
return true;
}
catch (network_error& e)
{
// TODO: This path needs testing!
#if defined(TORRENT_VERBOSE_LOGGING)
std::string msg = e.what();
(*p->connection->m_logger) << "*** CONNECTION FAILED '" << msg << "'\n";
#endif
}
catch (std::exception& e)
{}
return false;
}
@ -1209,11 +1191,11 @@ namespace libtorrent
}
// this is called whenever a peer connection is closed
void policy::connection_closed(const peer_connection& c)
void policy::connection_closed(const peer_connection& c) try
{
INVARIANT_CHECK;
assert(c.is_disconnecting());
// assert(c.is_disconnecting());
bool unchoked = false;
std::vector<peer>::iterator i = std::find_if(
@ -1234,7 +1216,7 @@ namespace libtorrent
if (c.failed())
{
i->type = peer::not_connectable;
i->id.port = address::any_port;
i->ip.port(0);
}
// if the share ratio is 0 (infinite), the
@ -1242,7 +1224,7 @@ namespace libtorrent
// because it isn't necessary.
if (m_torrent->ratio() != 0.f)
{
assert(i->connection->associated_torrent() == m_torrent);
assert(i->connection->associated_torrent().lock().get() == m_torrent);
assert(i->connection->share_diff() < std::numeric_limits<size_type>::max());
m_available_free_upload += i->connection->share_diff();
}
@ -1260,6 +1242,13 @@ namespace libtorrent
else unchoke_one_peer();
}
}
catch (std::exception& e)
{
#ifndef NDEBUG
std::string err = e.what();
#endif
assert(false);
}
void policy::peer_is_interesting(peer_connection& c)
{
@ -1274,7 +1263,9 @@ namespace libtorrent
bool policy::has_connection(const peer_connection* c)
{
assert(c);
assert(c->remote() == c->get_socket()->sender());
#ifndef NDEBUG
assert(c->remote() == c->get_socket()->remote_endpoint());
#endif
return std::find_if(
m_peers.begin()
, m_peers.end()
@ -1309,6 +1300,9 @@ namespace libtorrent
i != m_torrent->end(); ++i)
{
if (i->second->is_disconnecting()) continue;
// ignore web_peer_connections since they are not managed
// by the policy class
if (dynamic_cast<web_peer_connection*>(i->second)) continue;
++num_torrent_peers;
}
@ -1332,8 +1326,8 @@ namespace libtorrent
}
#endif
policy::peer::peer(const address& pid, peer::connection_type t)
: id(pid)
policy::peer::peer(const tcp::endpoint& ip_, peer::connection_type t)
: ip(ip_)
, type(t)
, last_optimistically_unchoked(
boost::gregorian::date(1970,boost::gregorian::Jan,1))

File diff suppressed because it is too large Load Diff

View File

@ -47,7 +47,7 @@ POSSIBILITY OF SUCH DAMAGE.
using namespace libtorrent;
void libtorrent::stat::second_tick()
void libtorrent::stat::second_tick(float tick_interval)
{
INVARIANT_CHECK;
@ -59,10 +59,12 @@ void libtorrent::stat::second_tick()
m_upload_payload_rate_history[i + 1] = m_upload_payload_rate_history[i];
}
m_download_rate_history[0] = m_downloaded_payload + m_downloaded_protocol;
m_upload_rate_history[0] = m_uploaded_payload + m_uploaded_protocol;
m_download_payload_rate_history[0] = m_downloaded_payload;
m_upload_payload_rate_history[0] = m_uploaded_payload;
m_download_rate_history[0] = (m_downloaded_payload + m_downloaded_protocol)
/ tick_interval;
m_upload_rate_history[0] = (m_uploaded_payload + m_uploaded_protocol)
/ tick_interval;
m_download_payload_rate_history[0] = m_downloaded_payload / tick_interval;
m_upload_payload_rate_history[0] = m_uploaded_payload / tick_interval;
m_downloaded_payload = 0;
m_uploaded_payload = 0;

View File

@ -232,9 +232,9 @@ namespace
}
};
*/
struct file_entry
struct lru_file_entry
{
file_entry(boost::shared_ptr<file> const& f)
lru_file_entry(boost::shared_ptr<file> const& f)
: file_ptr(f)
, last_use(pt::second_clock::universal_time()) {}
mutable boost::shared_ptr<file> file_ptr;
@ -257,7 +257,7 @@ namespace
path_view::iterator i = pt.find(p);
if (i != pt.end())
{
file_entry e = *i;
lru_file_entry e = *i;
e.last_use = pt::second_clock::universal_time();
// if you hit this assert, you probably have more than one
@ -294,7 +294,7 @@ namespace
*/ assert(lt.size() == 1 || (i->last_use <= boost::next(i)->last_use));
lt.erase(i);
}
file_entry e(boost::shared_ptr<file>(new file(p, m)));
lru_file_entry e(boost::shared_ptr<file>(new file(p, m)));
e.mode = m;
e.key = st;
e.file_path = p;
@ -334,13 +334,13 @@ namespace
int m_size;
typedef multi_index_container<
file_entry, indexed_by<
ordered_unique<member<file_entry, path
, &file_entry::file_path> >
, ordered_non_unique<member<file_entry, pt::ptime
, &file_entry::last_use> >
, ordered_non_unique<member<file_entry, void*
, &file_entry::key> >
lru_file_entry, indexed_by<
ordered_unique<member<lru_file_entry, path
, &lru_file_entry::file_path> >
, ordered_non_unique<member<lru_file_entry, pt::ptime
, &lru_file_entry::last_use> >
, ordered_non_unique<member<lru_file_entry, void*
, &lru_file_entry::key> >
>
> file_set;
@ -620,6 +620,12 @@ namespace libtorrent
slot_lock lock(*m_pimpl, slot);
#ifndef NDEBUG
std::vector<file_slice> slices
= m_pimpl->info.map_block(slot, offset, size);
assert(!slices.empty());
#endif
size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset;
// find the file iterator and file offset
@ -642,8 +648,10 @@ namespace libtorrent
assert(file_offset < file_iter->size);
in->seek(file_offset);
if (in->tell() != file_offset)
assert(slices[0].offset == file_offset);
size_type new_pos = in->seek(file_offset);
if (new_pos != file_offset)
{
// the file was not big enough
throw file_error("slot has no storage");
@ -665,12 +673,24 @@ namespace libtorrent
size_type result = left_to_read;
int buf_pos = 0;
#ifndef NDEBUG
int counter = 0;
#endif
while (left_to_read > 0)
{
int read_bytes = left_to_read;
if (file_offset + read_bytes > file_iter->size)
read_bytes = static_cast<int>(file_iter->size - file_offset);
#ifndef NDEBUG
assert(int(slices.size()) > counter);
size_type slice_size = slices[counter].size;
assert(slice_size == read_bytes);
assert(m_pimpl->info.file_at(slices[counter].file_index).path
== file_iter->path);
#endif
size_type actual_read = in->read(buf + buf_pos, read_bytes);
if (read_bytes != actual_read)
@ -687,6 +707,9 @@ namespace libtorrent
if (left_to_read > 0)
{
++file_iter;
#ifndef NDEBUG
++counter;
#endif
path path = m_pimpl->save_path / file_iter->path;
file_offset = 0;
@ -715,6 +738,12 @@ namespace libtorrent
slot_lock lock(*m_pimpl, slot);
#ifndef NDEBUG
std::vector<file_slice> slices
= m_pimpl->info.map_block(slot, offset, size);
assert(!slices.empty());
#endif
size_type start = slot * (size_type)m_pimpl->info.piece_length() + offset;
// find the file iterator and file offset
@ -737,9 +766,9 @@ namespace libtorrent
, p, file::out | file::in);
assert(file_offset < file_iter->size);
assert(slices[0].offset == file_offset);
out->seek(file_offset);
size_type pos = out->tell();
size_type pos = out->seek(file_offset);
if (pos != file_offset)
{
@ -757,7 +786,9 @@ namespace libtorrent
assert(left_to_write >= 0);
int buf_pos = 0;
#ifndef NDEBUG
int counter = 0;
#endif
while (left_to_write > 0)
{
int write_bytes = left_to_write;
@ -767,6 +798,11 @@ namespace libtorrent
write_bytes = static_cast<int>(file_iter->size - file_offset);
}
assert(int(slices.size()) > counter);
assert(slices[counter].size == write_bytes);
assert(m_pimpl->info.file_at(slices[counter].file_index).path
== file_iter->path);
assert(buf_pos >= 0);
assert(write_bytes >= 0);
size_type written = out->write(buf + buf_pos, write_bytes);
@ -786,6 +822,9 @@ namespace libtorrent
if (left_to_write > 0)
{
#ifndef NDEBUG
++counter;
#endif
++file_iter;
assert(file_iter != m_pimpl->info.end_files());
@ -818,10 +857,12 @@ namespace libtorrent
bool check_fastresume(
detail::piece_checker_data& d
, std::vector<bool>& pieces
, int& num_pieces
, bool compact_mode);
std::pair<bool, float> check_files(
std::vector<bool>& pieces);
std::vector<bool>& pieces
, int& num_pieces);
void release_files();
@ -868,6 +909,7 @@ namespace libtorrent
const std::vector<char>& piece_data
, int current_slot
, std::vector<bool>& have_pieces
, int& num_pieces
, const std::multimap<sha1_hash, int>& hash_to_piece);
int allocate_slot_for_piece(int piece_index);
@ -949,6 +991,12 @@ namespace libtorrent
// build the first time it is used (to save time if it
// isn't needed)
std::multimap<sha1_hash, int> m_hash_to_piece;
// used as temporary piece data storage in allocate_slots
// it is a member in order to avoid allocating it on
// the heap every time a new slot is allocated. (This is quite
// frequent with high download speeds)
std::vector<char> m_scratch_buffer;
};
piece_manager::impl::impl(
@ -1156,6 +1204,7 @@ namespace libtorrent
const std::vector<char>& piece_data
, int current_slot
, std::vector<bool>& have_pieces
, int& num_pieces
, const std::multimap<sha1_hash, int>& hash_to_piece)
{
INVARIANT_CHECK;
@ -1245,7 +1294,7 @@ namespace libtorrent
{
// this index is the only piece with this
// hash. The previous slot we found with
// this hash must be tha same piece. Mark
// this hash must be the same piece. Mark
// that piece as unassigned, since this slot
// is the correct place for the piece.
m_slot_to_piece[other_slot] = unassigned;
@ -1254,12 +1303,19 @@ namespace libtorrent
assert(m_piece_to_slot[piece_index] != current_slot);
assert(m_piece_to_slot[piece_index] >= 0);
m_piece_to_slot[piece_index] = has_no_slot;
#ifndef NDEBUG
have_pieces[piece_index] = false;
#endif
}
else
{
++num_pieces;
}
assert(have_pieces[piece_index] == false);
assert(m_piece_to_slot[piece_index] == has_no_slot);
have_pieces[piece_index] = true;
return piece_index;
}
@ -1279,6 +1335,7 @@ namespace libtorrent
assert(have_pieces[free_piece] == false);
assert(m_piece_to_slot[free_piece] == has_no_slot);
have_pieces[free_piece] = true;
++num_pieces;
return free_piece;
}
@ -1296,7 +1353,7 @@ namespace libtorrent
bool piece_manager::impl::check_fastresume(
detail::piece_checker_data& data
, std::vector<bool>& pieces
, bool compact_mode)
, int& num_pieces, bool compact_mode)
{
assert(m_info.piece_length() > 0);
// synchronization ------------------------------------------------------
@ -1320,6 +1377,7 @@ namespace libtorrent
pieces.clear();
pieces.resize(m_info.num_pieces(), false);
num_pieces = 0;
// if we have fast-resume info
// use it instead of doing the actual checking
@ -1342,6 +1400,7 @@ namespace libtorrent
, piece_picker::has_index(found_piece))
== data.unfinished_pieces.end())
{
++num_pieces;
pieces[found_piece] = true;
}
}
@ -1385,8 +1444,10 @@ namespace libtorrent
// file check is at. 0 is nothing done, and 1
// is finished
std::pair<bool, float> piece_manager::impl::check_files(
std::vector<bool>& pieces)
std::vector<bool>& pieces, int& num_pieces)
{
assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
if (m_state == state_allocating)
{
if (m_compact_mode)
@ -1395,16 +1456,17 @@ namespace libtorrent
return std::make_pair(true, 1.f);
}
if (m_unallocated_slots.empty())
{
m_state = state_finished;
return std::make_pair(true, 1.f);
}
// if we're not in compact mode, make sure the
// pieces are spread out and placed at their
// final position.
assert(!m_unallocated_slots.empty());
allocate_slots(1);
if (m_unallocated_slots.empty())
{
m_state = state_finished;
return std::make_pair(true, 1.f);
}
return std::make_pair(false, 1.f - (float)m_unallocated_slots.size()
/ (float)m_slot_to_piece.size());
@ -1462,8 +1524,10 @@ namespace libtorrent
m_piece_data
, m_current_slot
, pieces
, num_pieces
, m_hash_to_piece);
assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
assert(piece_index == unassigned || piece_index >= 0);
const bool this_should_move = piece_index >= 0 && m_slot_to_piece[piece_index] != unallocated;
@ -1712,23 +1776,27 @@ namespace libtorrent
std::vector<char>().swap(m_piece_data);
std::multimap<sha1_hash, int>().swap(m_hash_to_piece);
m_state = state_allocating;
assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
return std::make_pair(false, 1.f);
}
assert(num_pieces == std::count(pieces.begin(), pieces.end(), true));
return std::make_pair(false, (float)m_current_slot / m_info.num_pieces());
}
bool piece_manager::check_fastresume(
detail::piece_checker_data& d, std::vector<bool>& pieces
, bool compact_mode)
, int& num_pieces, bool compact_mode)
{
return m_pimpl->check_fastresume(d, pieces, compact_mode);
return m_pimpl->check_fastresume(d, pieces, num_pieces, compact_mode);
}
std::pair<bool, float> piece_manager::check_files(
std::vector<bool>& pieces)
std::vector<bool>& pieces
, int& num_pieces)
{
return m_pimpl->check_files(pieces);
return m_pimpl->check_files(pieces, num_pieces);
}
int piece_manager::impl::allocate_slot_for_piece(int piece_index)
@ -1902,7 +1970,8 @@ namespace libtorrent
const int piece_size = static_cast<int>(m_info.piece_length());
std::vector<char> buffer(piece_size, 0);
std::vector<char>& buffer = m_scratch_buffer;
buffer.resize(piece_size);
for (int i = 0; i < num_slots && !m_unallocated_slots.empty(); ++i)
{

File diff suppressed because it is too large Load Diff

View File

@ -54,6 +54,7 @@ POSSIBILITY OF SUCH DAMAGE.
#endif
#include "libtorrent/peer_id.hpp"
#include "libtorrent/bt_peer_connection.hpp"
#include "libtorrent/torrent_info.hpp"
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/bencode.hpp"
@ -71,6 +72,8 @@ namespace std
#endif
using boost::bind;
using boost::mutex;
using libtorrent::detail::session_impl;
namespace libtorrent
{
@ -92,15 +95,15 @@ namespace libtorrent
if (chk)
{
boost::mutex::scoped_lock l(chk->m_mutex);
mutex::scoped_lock l(chk->m_mutex);
detail::piece_checker_data* d = chk->find_torrent(hash);
if (d != 0) return f(*d->torrent_ptr);
}
{
boost::mutex::scoped_lock l(ses->m_mutex);
torrent* t = ses->find_torrent(hash);
if (t != 0) return f(*t);
session_impl::mutex_t::scoped_lock l(ses->m_mutex);
boost::shared_ptr<torrent> t = ses->find_torrent(hash).lock();
if (t) return f(*t);
}
throw invalid_handle();
@ -144,6 +147,24 @@ namespace libtorrent
, bind(&torrent::set_max_connections, _1, max_connections));
}
void torrent_handle::set_peer_upload_limit(tcp::endpoint ip, int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_peer_upload_limit, _1, ip, limit));
}
void torrent_handle::set_peer_download_limit(tcp::endpoint ip, int limit) const
{
INVARIANT_CHECK;
assert(limit >= -1);
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::set_peer_download_limit, _1, ip, limit));
}
void torrent_handle::set_upload_limit(int limit) const
{
INVARIANT_CHECK;
@ -231,7 +252,7 @@ namespace libtorrent
if (m_chk)
{
boost::mutex::scoped_lock l(m_chk->m_mutex);
mutex::scoped_lock l(m_chk->m_mutex);
detail::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0)
@ -254,9 +275,9 @@ namespace libtorrent
}
{
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
if (t != 0) return t->status();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (t) return t->status();
}
throw_invalid_handle();
@ -293,13 +314,6 @@ namespace libtorrent
return ret;
}
void torrent_handle::filter_file(int index, bool filter) const
{
INVARIANT_CHECK;
call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::filter_file, _1, index, filter));
}
void torrent_handle::filter_files(std::vector<bool> const& files) const
{
INVARIANT_CHECK;
@ -315,6 +329,14 @@ namespace libtorrent
, m_chk, m_info_hash, bind(&torrent::trackers, _1));
}
void torrent_handle::add_url_seed(std::string const& url)
{
INVARIANT_CHECK;
return call_member<void>(m_ses, m_chk, m_info_hash
, bind(&torrent::add_url_seed, _1, url));
}
void torrent_handle::replace_trackers(
std::vector<announce_entry> const& urls) const
{
@ -341,15 +363,15 @@ namespace libtorrent
if (m_chk)
{
boost::mutex::scoped_lock l(m_chk->m_mutex);
mutex::scoped_lock l(m_chk->m_mutex);
detail::piece_checker_data* d = m_chk->find_torrent(m_info_hash);
if (d != 0) return true;
}
{
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
if (t != 0) return true;
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::weak_ptr<torrent> t = m_ses->find_torrent(m_info_hash);
if (!t.expired()) return true;
}
return false;
@ -362,9 +384,9 @@ namespace libtorrent
std::vector<int> piece_index;
if (m_ses == 0) return entry();
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
if (t == 0) return entry();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return entry();
if (!t->valid_metadata()) return entry();
@ -398,9 +420,7 @@ namespace libtorrent
// info for each unfinished piece
for (std::vector<piece_picker::downloading_piece>::const_iterator i
= q.begin();
i != q.end();
++i)
= q.begin(); i != q.end(); ++i)
{
if (i->finished_blocks.count() == 0) continue;
@ -450,10 +470,10 @@ namespace libtorrent
// but still connectable
if (!i->second->is_local()) continue;
address ip = i->second->remote();
tcp::endpoint ip = i->second->remote();
entry peer(entry::dictionary_t);
peer["ip"] = ip.as_string();
peer["port"] = ip.port;
peer["ip"] = ip.address().to_string();
peer["port"] = ip.port();
peer_list.push_back(peer);
}
@ -491,15 +511,15 @@ namespace libtorrent
, bind(&torrent::metadata, _1));
}
void torrent_handle::connect_peer(address const& adr) const
void torrent_handle::connect_peer(tcp::endpoint const& adr) const
{
INVARIANT_CHECK;
if (m_ses == 0) throw_invalid_handle();
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
if (t == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) throw_invalid_handle();
peer_id id;
std::fill(id.begin(), id.end(), 0);
@ -513,9 +533,9 @@ namespace libtorrent
if (m_ses == 0) throw_invalid_handle();
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
if (t == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) throw_invalid_handle();
using boost::posix_time::second_clock;
t->force_tracker_request(second_clock::universal_time()
@ -528,9 +548,9 @@ namespace libtorrent
if (m_ses == 0) throw_invalid_handle();
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
if (t == 0) throw_invalid_handle();
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) throw_invalid_handle();
t->force_tracker_request();
}
@ -555,10 +575,10 @@ namespace libtorrent
v.clear();
if (m_ses == 0) throw_invalid_handle();
boost::mutex::scoped_lock l(m_ses->m_mutex);
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
const torrent* t = m_ses->find_torrent(m_info_hash);
if (t == 0) return;
boost::shared_ptr<const torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return;
for (torrent::const_peer_iterator i = t->begin();
i != t->end(); ++i)
@ -567,75 +587,22 @@ namespace libtorrent
// peers that haven't finished the handshake should
// not be included in this list
if (peer->associated_torrent() == 0) continue;
if (peer->associated_torrent().expired()) continue;
v.push_back(peer_info());
peer_info& p = v.back();
const stat& statistics = peer->statistics();
p.down_speed = statistics.download_rate();
p.up_speed = statistics.upload_rate();
p.payload_down_speed = statistics.download_payload_rate();
p.payload_up_speed = statistics.upload_payload_rate();
p.id = peer->get_peer_id();
p.ip = peer->remote();
p.total_download = statistics.total_payload_download();
p.total_upload = statistics.total_payload_upload();
if (peer->m_ul_bandwidth_quota.given == std::numeric_limits<int>::max())
p.upload_limit = -1;
else
p.upload_limit = peer->m_ul_bandwidth_quota.given;
if (peer->m_ul_bandwidth_quota.max == std::numeric_limits<int>::max())
p.upload_ceiling = -1;
else
p.upload_ceiling = peer->m_ul_bandwidth_quota.given;
p.load_balancing = peer->total_free_upload();
p.download_queue_length = (int)peer->download_queue().size();
p.upload_queue_length = (int)peer->upload_queue().size();
boost::optional<piece_block_progress> ret = peer->downloading_piece();
if (ret)
{
p.downloading_piece_index = ret->piece_index;
p.downloading_block_index = ret->block_index;
p.downloading_progress = ret->bytes_downloaded;
p.downloading_total = ret->full_block_bytes;
}
else
{
p.downloading_piece_index = -1;
p.downloading_block_index = -1;
p.downloading_progress = 0;
p.downloading_total = 0;
}
p.flags = 0;
if (peer->is_interesting()) p.flags |= peer_info::interesting;
if (peer->is_choked()) p.flags |= peer_info::choked;
if (peer->is_peer_interested()) p.flags |= peer_info::remote_interested;
if (peer->has_peer_choked()) p.flags |= peer_info::remote_choked;
if (peer->support_extensions()) p.flags |= peer_info::supports_extensions;
if (peer->is_local()) p.flags |= peer_info::local_connection;
if (peer->is_connecting() && !peer->is_queued()) p.flags |= peer_info::connecting;
if (peer->is_queued()) p.flags |= peer_info::queued;
p.pieces = peer->get_bitfield();
p.seed = peer->is_seed();
peer->get_peer_info(p);
}
}
bool torrent_handle::send_chat_message(address ip, std::string message) const
bool torrent_handle::send_chat_message(tcp::endpoint ip, std::string message) const
{
if (m_ses == 0) throw_invalid_handle();
boost::mutex::scoped_lock l(m_ses->m_mutex);
const torrent* t = m_ses->find_torrent(m_info_hash);
if (t == 0) return false;
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
if (!t) return false;
for (torrent::const_peer_iterator i = t->begin();
i != t->end(); ++i)
@ -644,22 +611,24 @@ namespace libtorrent
// peers that haven't finished the handshake should
// not be included in this list
if (peer->associated_torrent() == 0) continue;
if (peer->associated_torrent().expired()) continue;
tcp::endpoint sender = peer->get_socket()->remote_endpoint();
// loop until we find the required ip tcp::endpoint
if (ip != sender) continue;
bt_peer_connection* p = dynamic_cast<bt_peer_connection*>(peer);
if (!p) return false;
// peers that don's support chat message extension
// should not be included either
if (!peer->supports_extension(
peer_connection::extended_chat_message))
continue;
if (!p->supports_extension(extended_chat_message))
return false;
// loop until we find the required ip address
if (ip == peer->get_socket()->sender())
{
// send the message
peer->send_chat_message(message);
p->write_chat_message(message);
return true;
}
}
return false;
}
@ -669,11 +638,11 @@ namespace libtorrent
if (m_ses == 0) throw_invalid_handle();
boost::mutex::scoped_lock l(m_ses->m_mutex);
torrent* t = m_ses->find_torrent(m_info_hash);
session_impl::mutex_t::scoped_lock l(m_ses->m_mutex);
boost::shared_ptr<torrent> t = m_ses->find_torrent(m_info_hash).lock();
queue.clear();
if (t == 0) return;
if (!t) return;
if (!t->valid_metadata()) return;
const piece_picker& p = t->picker();

View File

@ -279,8 +279,7 @@ namespace libtorrent
void torrent_info::read_torrent_info(const entry& torrent_file)
{
// extract the url of the tracker
entry const* i = torrent_file.find_key("announce-list");
if (i)
if (entry const* i = torrent_file.find_key("announce-list"))
{
const entry::list_type& l = i->list();
for (entry::list_type::const_iterator j = l.begin(); j != l.end(); ++j)
@ -302,19 +301,19 @@ namespace libtorrent
torrent_file["announce"].string()));
}
// shuffle each tier
std::vector<announce_entry>::iterator i = m_urls.begin();
std::vector<announce_entry>::iterator j;
std::vector<announce_entry>::iterator start = m_urls.begin();
std::vector<announce_entry>::iterator stop;
int current_tier = m_urls.front().tier;
for (j = m_urls.begin(); j != m_urls.end(); ++j)
for (stop = m_urls.begin(); stop != m_urls.end(); ++stop)
{
if (j->tier != current_tier)
if (stop->tier != current_tier)
{
std::random_shuffle(i, j);
i = j;
current_tier = j->tier;
std::random_shuffle(start, stop);
start = stop;
current_tier = stop->tier;
}
}
std::random_shuffle(i, j);
std::random_shuffle(start, stop);
}
else
{
@ -330,6 +329,26 @@ namespace libtorrent
}
catch (type_error) {}
// if there are any url-seeds, extract them
try
{
entry const& url_seeds = torrent_file["url-list"];
if (url_seeds.type() == entry::string_t)
{
m_url_seeds.push_back(url_seeds.string());
}
else if (url_seeds.type() == entry::list_t)
{
entry::list_type const& l = url_seeds.list();
for (entry::list_type::const_iterator i = l.begin();
i != l.end(); ++i)
{
m_url_seeds.push_back(i->string());
}
}
}
catch (type_error&) {}
// extract comment
if (entry const* e = torrent_file.find_key("comment.utf-8"))
{ m_comment = e->string(); }
@ -409,6 +428,11 @@ namespace libtorrent
}
void torrent_info::add_url_seed(std::string const& url)
{
m_url_seeds.push_back(url);
}
void torrent_info::set_comment(char const* str)
{
m_comment = str;
@ -458,7 +482,7 @@ namespace libtorrent
for (fs::path::iterator j = boost::next(file_path.begin());
j != file_path.end(); ++j)
{
path_e.list().push_back(*j);
path_e.list().push_back(entry(*j));
}
}
}
@ -513,7 +537,7 @@ namespace libtorrent
trackers.list().push_back(tier);
tier.list().clear();
}
tier.list().push_back(i->url);
tier.list().push_back(entry(i->url));
}
trackers.list().push_back(tier);
dict["announce-list"] = trackers;
@ -528,6 +552,24 @@ namespace libtorrent
if (!m_created_by.empty())
dict["created by"] = m_created_by;
if (!m_url_seeds.empty())
{
if (m_url_seeds.size() == 1)
{
dict["url-list"] = m_url_seeds.front();
}
else
{
entry& list = dict["url-list"];
list = entry(entry::list_t);
for (std::vector<std::string>::const_iterator i
= m_url_seeds.begin(); i != m_url_seeds.end(); ++i)
{
list.list().push_back(entry(*i));
}
}
}
dict["info"] = create_info_metadata();
entry const& info_section = dict["info"];
@ -584,4 +626,55 @@ namespace libtorrent
else
return piece_length();
}
std::vector<file_slice> torrent_info::map_block(int piece, size_type offset
, int size) const
{
assert(num_files() > 0);
std::vector<file_slice> ret;
size_type start = piece * (size_type)m_piece_length + offset;
// find the file iterator and file offset
// TODO: make a vector that can map piece -> file index in O(1)
size_type file_offset = start;
std::vector<file_entry>::const_iterator file_iter;
int counter = 0;
for (file_iter = begin_files();; ++counter, ++file_iter)
{
assert(file_iter != end_files());
if (file_offset < file_iter->size)
{
file_slice f;
f.file_index = counter;
f.offset = file_offset;
f.size = (std::min)(file_iter->size - file_offset, (size_type)size);
size -= f.size;
file_offset += f.size;
ret.push_back(f);
}
assert(size >= 0);
if (size <= 0) break;
file_offset -= file_iter->size;
}
return ret;
}
peer_request torrent_info::map_file(int file_index, size_type file_offset
, int size) const
{
size_type offset = file_offset;
for (int i = 0; i < file_index; ++i)
offset += file_at(i).size;
peer_request ret;
ret.piece = offset / piece_length();
ret.start = offset - ret.piece * piece_length();
ret.length = size;
return ret;
}
}

View File

@ -35,10 +35,11 @@ POSSIBILITY OF SUCH DAMAGE.
#include <cctype>
#include <iomanip>
#include <sstream>
#include <boost/tuple/tuple.hpp>
#include "zlib.h"
#include <boost/bind.hpp>
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/http_tracker_connection.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
@ -49,6 +50,7 @@ POSSIBILITY OF SUCH DAMAGE.
using namespace libtorrent;
using boost::tuples::make_tuple;
using boost::tuples::tuple;
using boost::bind;
namespace
{
@ -76,54 +78,11 @@ namespace
namespace libtorrent
{
/*
address parse_url(std::string const& url)
{
std::string hostname; // hostname only
int port = 80;
using boost::posix_time::second_clock;
using boost::posix_time::seconds;
using boost::posix_time::ptime;
using boost::posix_time::time_duration;
// PARSE URL
std::string::const_iterator start = url.begin();
std::string::const_iterator end
= std::find(url.begin(), url.end(), ':');
while ((*start == ' ' || *start == '\t') && start != end) ++start;
if (end == url.end()) throw std::runtime_error("invalid url: \"" + url + "\"");
++end;
if (end == url.end()) throw std::runtime_error("invalid url: \"" + url + "\"");
if (*end != '/') throw std::runtime_error("invalid url: \"" + url + "\"");
++end;
if (end == url.end()) throw std::runtime_error("invalid url: \"" + url + "\"");
if (*end != '/') throw std::runtime_error("invalid url: \"" + url + "\"");
++end;
start = end;
end = std::find(start, url.end(), '/');
std::string::const_iterator port_pos
= std::find(start, url.end(), ':');
if (port_pos < end)
{
hostname.assign(start, port_pos);
++port_pos;
try
{
port = boost::lexical_cast<int>(std::string(port_pos, end));
}
catch(boost::bad_lexical_cast&)
{
throw std::runtime_error("invalid url: \"" + url + "\"");
}
}
else
{
hostname.assign(start, end);
}
return address(hostname.c_str(), port);
}
*/
// returns -1 if gzip header is invalid or the header size in bytes
int gzip_header(const char* buf, int size)
{
@ -332,6 +291,101 @@ namespace libtorrent
return ret;
}
void intrusive_ptr_add_ref(timeout_handler const* c)
{
assert(c != 0);
assert(c->m_refs >= 0);
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
++c->m_refs;
}
void intrusive_ptr_release(timeout_handler const* c)
{
assert(c != 0);
assert(c->m_refs > 0);
timeout_handler::mutex_t::scoped_lock l(c->m_mutex);
--c->m_refs;
if (c->m_refs == 0)
{
l.unlock();
delete c;
}
}
timeout_handler::timeout_handler(demuxer& d)
: m_demuxer(d)
, m_start_time(second_clock::universal_time())
, m_read_time(second_clock::universal_time())
, m_timeout(d)
, m_completion_timeout(0)
, m_read_timeout(0)
, m_refs(0)
{}
void timeout_handler::set_timeout(int completion_timeout, int read_timeout)
{
m_completion_timeout = completion_timeout;
m_read_timeout = read_timeout;
m_start_time = second_clock::universal_time();
m_read_time = second_clock::universal_time();
m_timeout.expires_at(std::min(
m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout)));
m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
}
void timeout_handler::restart_read_timeout()
{
m_read_time = second_clock::universal_time();
}
void timeout_handler::cancel()
{
m_timeout.cancel();
m_completion_timeout = 0;
}
void timeout_handler::timeout_callback(asio::error const& error) try
{
if (error) return;
if (m_completion_timeout == 0) return;
ptime now(second_clock::universal_time());
time_duration receive_timeout = now - m_read_time;
time_duration completion_timeout = now - m_start_time;
if (m_read_timeout
< receive_timeout.total_seconds()
|| m_completion_timeout
< completion_timeout.total_seconds())
{
on_timeout();
return;
}
m_timeout.expires_at(std::min(
m_read_time + seconds(m_read_timeout)
, m_start_time + seconds(m_completion_timeout)));
m_timeout.async_wait(bind(&timeout_handler::timeout_callback, self(), _1));
}
catch (std::exception& e)
{
assert(false);
}
tracker_connection::tracker_connection(
tracker_manager& man
, tracker_request req
, demuxer& d
, boost::weak_ptr<request_callback> r)
: timeout_handler(d)
, m_requester(r)
, m_man(man)
, m_req(req)
{}
request_callback& tracker_connection::requester()
{
boost::shared_ptr<request_callback> r = m_requester.lock();
@ -339,34 +393,35 @@ namespace libtorrent
return *r;
}
void tracker_manager::tick()
void tracker_connection::fail(int code, char const* msg)
{
tracker_connections_t::iterator i;
for (i = m_connections.begin(); i != m_connections.end();)
{
boost::shared_ptr<tracker_connection>& c = *i;
try
{
if (!c->tick())
{
++i;
continue;
}
}
catch (const std::exception& e)
{
if (c->has_requester())
c->requester().tracker_request_error(c->tracker_req()
, -1, e.what());
}
if (c->has_requester()) c->requester().m_manager = 0;
i = m_connections.erase(i);
}
if (has_requester()) requester().tracker_request_error(
m_req, code, msg);
close();
}
namespace
void tracker_connection::fail_timeout()
{
if (has_requester()) requester().tracker_request_timed_out(m_req);
close();
}
void tracker_connection::close()
{
m_man.remove_request(this);
cancel();
}
void tracker_manager::remove_request(tracker_connection const* c)
{
mutex_t::scoped_lock l(m_mutex);
tracker_connections_t::iterator i = std::find(m_connections.begin()
, m_connections.end(), boost::intrusive_ptr<const tracker_connection>(c));
if (i == m_connections.end()) return;
m_connections.erase(i);
}
tuple<std::string, std::string, int, std::string>
parse_url_components(std::string url)
@ -377,6 +432,9 @@ namespace libtorrent
// PARSE URL
std::string::iterator start = url.begin();
// remove white spaces in front of the url
while (start != url.end() && (*start == ' ' || *start == '\t'))
++start;
std::string::iterator end
= std::find(url.begin(), url.end(), ':');
protocol = std::string(start, end);
@ -418,13 +476,14 @@ namespace libtorrent
return make_tuple(protocol, hostname, port
, std::string(start, url.end()));
}
}
void tracker_manager::queue_request(
tracker_request req
demuxer& d
, tracker_request req
, std::string const& auth
, boost::weak_ptr<request_callback> c)
{
mutex_t::scoped_lock l(m_mutex);
assert(req.num_want >= 0);
if (req.event == tracker_request::stopped)
req.num_want = 0;
@ -439,28 +498,31 @@ namespace libtorrent
boost::tie(protocol, hostname, port, request_string)
= parse_url_components(req.url);
boost::shared_ptr<tracker_connection> con;
boost::intrusive_ptr<tracker_connection> con;
if (protocol == "http")
{
con.reset(new http_tracker_connection(
*this
con = new http_tracker_connection(
d
, *this
, req
, hostname
, port
, request_string
, c
, m_settings
, auth));
, auth);
}
else if (protocol == "udp")
{
con.reset(new udp_tracker_connection(
req
con = new udp_tracker_connection(
d
, *this
, req
, hostname
, port
, c
, m_settings));
, m_settings);
}
else
{
@ -473,54 +535,28 @@ namespace libtorrent
}
catch (std::exception& e)
{
if (!c.expired())
{
boost::shared_ptr<request_callback> r = c.lock();
if (boost::shared_ptr<request_callback> r = c.lock())
r->tracker_request_error(req, -1, e.what());
}
}
}
/*
void tracker_manager::abort_request(request_callback* c)
{
assert(c != 0);
tracker_connections_t::iterator i;
for (i = m_connections.begin(); i != m_connections.end(); ++i)
{
if ((*i)->requester() == c)
{
m_connections.erase(i);
break;
}
}
}
*/
void tracker_manager::abort_all_requests()
{
// removes all connections from m_connections
// except those with a requester == 0 (since those are
// 'event=stopped'-requests)
mutex_t::scoped_lock l(m_mutex);
tracker_connections_t keep_connections;
for (tracker_connections_t::const_iterator i =
m_connections.begin(); i != m_connections.end(); ++i)
{
if (!(*i)->has_requester()) keep_connections.push_back(*i);
tracker_request const& req = (*i)->tracker_req();
if (req.event == tracker_request::stopped)
keep_connections.push_back(*i);
}
std::swap(m_connections, keep_connections);
}
bool tracker_manager::send_finished() const
{
for (tracker_connections_t::const_iterator i =
m_connections.begin(); i != m_connections.end(); ++i)
{
if (!(*i)->send_finished()) return false;
}
return true;
}
}

View File

@ -38,6 +38,17 @@ POSSIBILITY OF SUCH DAMAGE.
#include "zlib.h"
#ifdef _MSC_VER
#pragma warning(push, 1)
#endif
#include <boost/bind.hpp>
#include <boost/lexical_cast.hpp>
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#include "libtorrent/tracker_manager.hpp"
#include "libtorrent/udp_tracker_connection.hpp"
#include "libtorrent/io.hpp"
@ -55,133 +66,181 @@ namespace
}
using namespace boost::posix_time;
using boost::bind;
using boost::lexical_cast;
namespace libtorrent
{
udp_tracker_connection::udp_tracker_connection(
tracker_request const& req
demuxer& d
, tracker_manager& man
, tracker_request const& req
, std::string const& hostname
, unsigned short port
, boost::weak_ptr<request_callback> c
, const http_settings& stn)
: tracker_connection(c)
, m_request_time(second_clock::universal_time())
, m_request(req)
: tracker_connection(man, req, d, c)
, m_man(man)
, m_name_lookup(d)
, m_port(port)
, m_transaction_id(0)
, m_connection_id(0)
, m_settings(stn)
, m_attempts(0)
{
m_name_lookup = dns_lookup(hostname.c_str(), port);
m_socket.reset(new socket(socket::udp, false));
m_socket.reset(new datagram_socket(d));
m_name_lookup.async_by_name(m_host, hostname.c_str()
, bind(&udp_tracker_connection::name_lookup, self(), _1));
set_timeout(m_settings.tracker_completion_timeout
, m_settings.tracker_receive_timeout);
}
bool udp_tracker_connection::send_finished() const
void udp_tracker_connection::name_lookup(asio::error const& error) try
{
using namespace boost::posix_time;
time_duration d = second_clock::universal_time() - m_request_time;
return (m_transaction_id != 0
&& m_connection_id != 0)
|| d > seconds(m_settings.tracker_timeout);
if (error == asio::error::operation_aborted) return;
if (error)
{
fail(-1, error.what());
return;
}
bool udp_tracker_connection::tick()
{
using namespace boost::posix_time;
if (m_name_lookup.running())
{
if (!m_name_lookup.finished()) return false;
if (m_name_lookup.failed())
{
if (has_requester()) requester().tracker_request_error(
m_request, -1, m_name_lookup.error());
return true;
}
address a(m_name_lookup.ip());
if (has_requester()) requester().m_tracker_address = a;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester()) requester().debug_log("name lookup successful");
if (has_requester()) requester().debug_log("udp tracker name lookup successful");
#endif
restart_read_timeout();
m_target = udp::endpoint(m_port, m_host.address(0));
if (has_requester()) requester().m_tracker_address
= tcp::endpoint(m_port, m_host.address(0));
m_socket->connect(m_target);
send_udp_connect();
}
catch (std::exception& e)
{
fail(-1, e.what());
assert(false);
}
void udp_tracker_connection::on_timeout()
{
m_socket.reset();
m_name_lookup.cancel();
fail_timeout();
}
void udp_tracker_connection::send_udp_connect()
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("==> UDP_TRACKER_CONNECT ["
+ lexical_cast<std::string>(tracker_req().info_hash) + "]");
}
#endif
m_socket->connect(a);
send_udp_connect();
char send_buf[16];
char* ptr = send_buf;
// clear the lookup entry so it will not be
// marked as running anymore
m_name_lookup = dns_lookup();
if (m_transaction_id == 0)
m_transaction_id = rand() ^ (rand() << 16);
// connection_id
detail::write_uint32(0x417, ptr);
detail::write_uint32(0x27101980, ptr);
// action (connect)
detail::write_int32(action_connect, ptr);
// transaction_id
detail::write_int32(m_transaction_id, ptr);
m_socket->send(asio::buffer((void*)send_buf, 16), 0);
++m_attempts;
m_buffer.resize(udp_buffer_size);
m_socket->async_receive_from(asio::buffer(m_buffer), 0, m_sender
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
}
time_duration d = second_clock::universal_time() - m_request_time;
if (m_connection_id == 0
&& d > seconds(udp_connect_timeout))
void udp_tracker_connection::connect_response(asio::error const& error
, std::size_t bytes_transferred) try
{
if (m_attempts >= udp_connection_retries)
if (error == asio::error::operation_aborted) return;
if (error)
{
fail(-1, error.what());
return;
}
if (m_target != m_sender)
{
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), 0, m_sender
, boost::bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
const char* ptr = &m_buffer[0];
int action = detail::read_int32(ptr);
int transaction = detail::read_int32(ptr);
if (action == action_error)
{
fail(-1, std::string(ptr, bytes_transferred - 8).c_str());
return;
}
if (action != action_connect)
{
fail(-1, "invalid action in connect reply");
return;
}
if (m_transaction_id != transaction)
{
fail(-1, "incorrect transaction id");
return;
}
if (bytes_transferred < 16)
{
fail(-1, "udp_tracker_connection: "
"got a message with size < 16");
return;
}
// reset transaction
m_transaction_id = 0;
m_attempts = 0;
m_connection_id = detail::read_int64(ptr);
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
requester().tracker_request_timed_out(m_request);
return true;
}
send_udp_connect();
return false;
}
if (m_connection_id != 0
&& d > seconds(udp_announce_timeout))
{
if (m_attempts >= udp_announce_retries)
{
if (has_requester())
requester().tracker_request_timed_out(m_request);
return true;
requester().debug_log("<== UDP_TRACKER_CONNECT_RESPONSE ["
+ lexical_cast<std::string>(m_connection_id) + "]");
}
#endif
if (m_request.kind == tracker_request::announce_request)
if (tracker_req().kind == tracker_request::announce_request)
send_udp_announce();
else if (m_request.kind == tracker_request::scrape_request)
else if (tracker_req().kind == tracker_request::scrape_request)
send_udp_scrape();
return false;
}
char buf[udp_buffer_size];
int ret = m_socket->receive(buf, udp_buffer_size);
// if there was nothing to receive, return
if (ret == 0) return false;
if (ret < 0)
catch (std::exception& e)
{
socket::error_code err = m_socket->last_error();
if (err == socket::would_block) return false;
throw network_error(m_socket->last_error());
}
if (ret == udp_buffer_size)
{
if (has_requester())
requester().tracker_request_error(
m_request, -1, "tracker reply too big");
return true;
}
if (m_connection_id == 0)
{
return parse_connect_response(buf, ret);
}
else if (m_request.kind == tracker_request::announce_request)
{
return parse_announce_response(buf, ret);
}
else if (m_request.kind == tracker_request::scrape_request)
{
return parse_scrape_response(buf, ret);
}
fail(-1, e.what());
assert(false);
return false;
}
void udp_tracker_connection::send_udp_announce()
@ -192,38 +251,50 @@ namespace libtorrent
std::vector<char> buf;
std::back_insert_iterator<std::vector<char> > out(buf);
tracker_request const& req = tracker_req();
// connection_id
detail::write_int64(m_connection_id, out);
// action (announce)
detail::write_int32(announce, out);
detail::write_int32(action_announce, out);
// transaction_id
detail::write_int32(m_transaction_id, out);
// info_hash
std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), out);
std::copy(req.info_hash.begin(), req.info_hash.end(), out);
// peer_id
std::copy(m_request.id.begin(), m_request.id.end(), out);
std::copy(req.pid.begin(), req.pid.end(), out);
// downloaded
detail::write_int64(m_request.downloaded, out);
detail::write_int64(req.downloaded, out);
// left
detail::write_int64(m_request.left, out);
detail::write_int64(req.left, out);
// uploaded
detail::write_int64(m_request.uploaded, out);
detail::write_int64(req.uploaded, out);
// event
detail::write_int32(m_request.event, out);
detail::write_int32(req.event, out);
// ip address
detail::write_int32(0, out);
// key
detail::write_int32(m_request.key, out);
detail::write_int32(req.key, out);
// num_want
detail::write_int32(m_request.num_want, out);
detail::write_int32(req.num_want, out);
// port
detail::write_uint16(m_request.listen_port, out);
detail::write_uint16(req.listen_port, out);
// extensions
detail::write_uint16(0, out);
m_socket->send(&buf[0], buf.size());
m_request_time = second_clock::universal_time();
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("==> UDP_TRACKER_ANNOUNCE ["
+ lexical_cast<std::string>(req.info_hash) + "]");
}
#endif
m_socket->send(asio::buffer(buf), 0);
++m_attempts;
m_socket->async_receive_from(asio::buffer(m_buffer), 0, m_sender
, bind(&udp_tracker_connection::announce_response, self(), _1, _2));
}
void udp_tracker_connection::send_udp_scrape()
@ -237,91 +308,100 @@ namespace libtorrent
// connection_id
detail::write_int64(m_connection_id, out);
// action (scrape)
detail::write_int32(scrape, out);
detail::write_int32(action_scrape, out);
// transaction_id
detail::write_int32(m_transaction_id, out);
// info_hash
std::copy(m_request.info_hash.begin(), m_request.info_hash.end(), out);
std::copy(tracker_req().info_hash.begin(), tracker_req().info_hash.end(), out);
m_socket->send(&buf[0], buf.size());
m_request_time = second_clock::universal_time();
m_socket->send(asio::buffer(&buf[0], buf.size()), 0);
++m_attempts;
m_socket->async_receive_from(asio::buffer(m_buffer), 0, m_sender
, bind(&udp_tracker_connection::scrape_response, self(), _1, _2));
}
void udp_tracker_connection::send_udp_connect()
void udp_tracker_connection::announce_response(asio::error const& error
, std::size_t bytes_transferred) try
{
char send_buf[16];
char* ptr = send_buf;
if (m_transaction_id == 0)
m_transaction_id = rand() ^ (rand() << 16);
// connection_id
detail::write_uint32(0x417, ptr);
detail::write_uint32(0x27101980, ptr);
// action (connect)
detail::write_int32(connect, ptr);
// transaction_id
detail::write_int32(m_transaction_id, ptr);
m_socket->send(send_buf, 16);
m_request_time = second_clock::universal_time();
++m_attempts;
if (error == asio::error::operation_aborted) return;
if (error)
{
fail(-1, error.what());
return;
}
bool udp_tracker_connection::parse_announce_response(const char* buf, int len)
if (m_target != m_sender)
{
assert(buf != 0);
assert(len > 0);
if (len < 8)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
requester().debug_log("udp_tracker_connection: "
"got a message with size < 8, ignoring");
#endif
return false;
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), 0, m_sender
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
char* buf = &m_buffer[0];
int action = detail::read_int32(buf);
int transaction = detail::read_int32(buf);
if (transaction != m_transaction_id)
{
return false;
fail(-1, "incorrect transaction id");
return;
}
if (action == error)
if (action == action_error)
{
if (has_requester())
requester().tracker_request_error(
m_request, -1, std::string(buf, buf + len - 8));
return true;
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
return;
}
if (action != announce) return false;
if (len < 20)
if (action != action_announce)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
requester().debug_log("udp_tracker_connection: "
"got a message with size < 20, ignoring");
#endif
return false;
fail(-1, "invalid action in announce response");
return;
}
if (bytes_transferred < 20)
{
fail(-1, "got a message with size < 20");
return;
}
int interval = detail::read_int32(buf);
int incomplete = detail::read_int32(buf);
int complete = detail::read_int32(buf);
int num_peers = (len - 20) / 6;
if ((len - 20) % 6 != 0)
int num_peers = (bytes_transferred - 20) / 6;
if ((bytes_transferred - 20) % 6 != 0)
{
if (has_requester())
requester().tracker_request_error(
m_request, -1, "invalid tracker response");
return true;
fail(-1, "invalid udp tracker response length");
return;
}
if (!has_requester()) return true;
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
{
requester().debug_log("<== UDP_TRACKER_ANNOUNCE_RESPONSE");
}
#endif
if (!has_requester())
{
m_man.remove_request(this);
return;
}
std::vector<peer_entry> peer_list;
for (int i = 0; i < num_peers; ++i)
@ -334,126 +414,102 @@ namespace libtorrent
s << (int)detail::read_uint8(buf);
e.ip = s.str();
e.port = detail::read_uint16(buf);
e.id.clear();
e.pid.clear();
peer_list.push_back(e);
}
requester().tracker_response(m_request, peer_list, interval
requester().tracker_response(tracker_req(), peer_list, interval
, complete, incomplete);
return true;
m_man.remove_request(this);
return;
}
catch (std::exception& e)
{
fail(-1, e.what());
assert(false);
}; // msvc 7.1 seems to require this
void udp_tracker_connection::scrape_response(asio::error const& error
, std::size_t bytes_transferred) try
{
if (error == asio::error::operation_aborted) return;
if (error)
{
fail(-1, error.what());
return;
}
bool udp_tracker_connection::parse_scrape_response(const char* buf, int len)
if (m_target != m_sender)
{
assert(buf != 0);
assert(len > 0);
if (len < 8)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
requester().debug_log("udp_tracker_connection: "
"got a message with size < 8, ignoring");
#endif
return false;
// this packet was not received from the tracker
m_socket->async_receive_from(asio::buffer(m_buffer), 0, m_sender
, bind(&udp_tracker_connection::connect_response, self(), _1, _2));
return;
}
if (bytes_transferred >= udp_buffer_size)
{
fail(-1, "udp response too big");
return;
}
if (bytes_transferred < 8)
{
fail(-1, "got a message with size < 8");
return;
}
restart_read_timeout();
char* buf = &m_buffer[0];
int action = detail::read_int32(buf);
int transaction = detail::read_int32(buf);
if (transaction != m_transaction_id)
{
return false;
fail(-1, "incorrect transaction id");
return;
}
if (action == error)
if (action == action_error)
{
if (has_requester())
requester().tracker_request_error(
m_request, -1, std::string(buf, buf + len - 8));
return true;
fail(-1, std::string(buf, bytes_transferred - 8).c_str());
return;
}
if (action != scrape) return false;
if (len < 20)
if (action != action_scrape)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
requester().debug_log("udp_tracker_connection: "
"got a message with size < 20, ignoring");
#endif
return false;
fail(-1, "invalid action in announce response");
return;
}
if (bytes_transferred < 20)
{
fail(-1, "got a message with size < 20");
return;
}
int complete = detail::read_int32(buf);
/*int downloaded = */detail::read_int32(buf);
int incomplete = detail::read_int32(buf);
if (!has_requester()) return true;
if (!has_requester())
{
m_man.remove_request(this);
return;
}
std::vector<peer_entry> peer_list;
requester().tracker_response(m_request, peer_list, 0
requester().tracker_response(tracker_req(), peer_list, 0
, complete, incomplete);
return true;
m_man.remove_request(this);
}
bool udp_tracker_connection::parse_connect_response(const char* buf, int len)
catch (std::exception& e)
{
assert(buf != 0);
assert(len > 0);
if (len < 8)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
requester().debug_log("udp_tracker_connection: "
"got a message with size < 8, ignoring");
#endif
return false;
fail(-1, e.what());
assert(false);
}
const char* ptr = buf;
int action = detail::read_int32(ptr);
int transaction = detail::read_int32(ptr);
if (action == error)
{
if (has_requester())
requester().tracker_request_error(
m_request, -1, std::string(ptr, buf + len));
return true;
}
if (action != connect) return false;
if (m_transaction_id != transaction)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
requester().debug_log("udp_tracker_connection: "
"got a message with incorrect transaction id, ignoring");
#endif
return false;
}
if (len < 16)
{
#if defined(TORRENT_VERBOSE_LOGGING) || defined(TORRENT_LOGGING)
if (has_requester())
requester().debug_log("udp_tracker_connection: "
"got a connection message size < 16, ignoring");
#endif
return false;
}
// reset transaction
m_transaction_id = 0;
m_attempts = 0;
m_connection_id = detail::read_int64(ptr);
if (m_request.kind == tracker_request::announce_request)
send_udp_announce();
else if (m_request.kind == tracker_request::scrape_request)
send_udp_scrape();
return false;
}
}

417
src/web_peer_connection.cpp Executable file
View File

@ -0,0 +1,417 @@
/*
Copyright (c) 2003, 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 <vector>
#include <iostream>
#include <iomanip>
#include <limits>
#include <boost/bind.hpp>
#include <sstream>
#include "libtorrent/web_peer_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"
using namespace boost::posix_time;
using boost::bind;
using boost::shared_ptr;
using libtorrent::detail::session_impl;
namespace libtorrent
{
web_peer_connection::web_peer_connection(
detail::session_impl& ses
, boost::weak_ptr<torrent> t
, boost::shared_ptr<stream_socket> s
, tcp::endpoint const& remote
, std::string const& url)
: peer_connection(ses, t, s, remote)
, m_url(url)
, m_first_request(true)
{
INVARIANT_CHECK;
#ifdef TORRENT_VERBOSE_LOGGING
(*m_logger) << "*** web_peer_connection\n";
#endif
std::string protocol;
boost::tie(protocol, m_host, m_port, m_path)
= parse_url_components(url);
m_server_string = "URL seed @ ";
m_server_string += m_host;
}
web_peer_connection::~web_peer_connection()
{}
boost::optional<piece_block_progress>
web_peer_connection::downloading_piece_progress() const
{
if (!m_parser.header_finished() || m_requests.empty())
return boost::optional<piece_block_progress>();
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
int body_start = m_parser.body_start();
buffer::const_interval recv_buffer = receive_buffer();
assert(body_start <= recv_buffer.left());
piece_block_progress ret;
ret.piece_index = m_requests.front().piece;
ret.block_index = m_requests.front().start / t->block_size();
ret.bytes_downloaded = recv_buffer.left() - body_start;
ret.full_block_bytes = m_requests.front().length;
return ret;
}
void web_peer_connection::on_connected()
{
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
// this is always a seed
incoming_bitfield(std::vector<bool>(
t->torrent_file().num_pieces(), true));
// it is always possible to request pieces
incoming_unchoke();
reset_recv_buffer(512*1024+1024);
}
void web_peer_connection::write_request(peer_request const& r)
{
INVARIANT_CHECK;
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
assert(t->valid_metadata());
bool single_file_request = false;
if (!m_path.empty() && m_path[m_path.size() - 1] != '/')
single_file_request = true;
torrent_info const& info = t->torrent_file();
std::string request;
m_requests.push_back(r);
if (single_file_request)
{
request += "GET ";
request += escape_path(m_path.c_str(), m_path.length());
request += " HTTP/1.1\r\n";
request += "Host: ";
request += m_host;
if (m_first_request)
{
request += "\r\nUser-Agent: ";
request += escape_string(m_ses.m_http_settings.user_agent.c_str()
, m_ses.m_http_settings.user_agent.size());
}
request += "\r\nRange: bytes=";
request += boost::lexical_cast<std::string>(r.piece
* info.piece_length() + r.start);
request += "-";
request += boost::lexical_cast<std::string>(r.piece
* info.piece_length() + r.start + r.length - 1);
if (m_first_request)
request += "\r\nConnection: keep-alive";
request += "\r\n\r\n";
m_first_request = false;
m_file_requests.push_back(0);
}
else
{
std::vector<file_slice> files = info.map_block(r.piece, r.start
, r.length);
for (std::vector<file_slice>::iterator i = files.begin();
i != files.end(); ++i)
{
file_slice const& f = *i;
std::string path = m_path;
path += info.file_at(f.file_index).path.string();
request += "GET ";
request += escape_path(path.c_str(), path.length());
request += " HTTP/1.1\r\n";
request += "Host: ";
request += m_host;
if (m_first_request)
{
request += "\r\nUser-Agent: ";
request += escape_string(m_ses.m_http_settings.user_agent.c_str()
, m_ses.m_http_settings.user_agent.size());
}
request += "\r\nRange: bytes=";
request += boost::lexical_cast<std::string>(f.offset);
request += "-";
request += boost::lexical_cast<std::string>(f.offset + f.size - 1);
if (m_first_request)
request += "\r\nConnection: keep-alive";
request += "\r\n\r\n";
m_first_request = false;
m_file_requests.push_back(f.file_index);
}
}
send_buffer(request.c_str(), request.c_str() + request.size());
}
// --------------------------
// RECEIVE DATA
// --------------------------
// throws exception when the client should be disconnected
void web_peer_connection::on_receive(const asio::error& error
, std::size_t bytes_transferred)
{
INVARIANT_CHECK;
if (error)
{
return;
}
boost::shared_ptr<torrent> t = associated_torrent().lock();
assert(t);
incoming_piece_fragment();
for (;;)
{
buffer::const_interval recv_buffer = receive_buffer();
int payload;
int protocol;
boost::tie(payload, protocol) = m_parser.incoming(recv_buffer);
m_statistics.received_bytes(payload, protocol);
if (m_parser.status_code() != 206 && m_parser.status_code() != -1)
{
// we should not try this server again.
t->remove_url_seed(m_url);
if (m_parser.status_code() == 404)
throw std::runtime_error("File not found on server");
throw std::runtime_error("HTTP server does not support byte range requests");
}
if (!m_parser.finished()) break;
std::string server_version = m_parser.header<std::string>("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 += ")";
}
std::stringstream range_str(m_parser.header<std::string>("Content-Range"));
size_type range_start;
size_type range_end;
char dummy;
std::string bytes;
range_str >> bytes >> range_start >> dummy >> range_end;
if (!range_str)
{
// we should not try this server again.
t->remove_url_seed(m_url);
throw std::runtime_error("invalid range in HTTP response: " + range_str.str());
}
// the http range is inclusive
range_end++;
torrent_info const& info = t->torrent_file();
if (m_requests.empty() || m_file_requests.empty())
throw std::runtime_error("unexpected HTTP response");
int file_index = m_file_requests.front();
m_file_requests.pop_front();
peer_request r = info.map_file(file_index, range_start
, range_end - range_start);
buffer::const_interval http_body = m_parser.get_body();
if (r == m_requests.front())
{
m_requests.pop_front();
incoming_piece(r, http_body.begin);
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
return;
}
if (!m_piece.empty())
{
// this is not the first partial request we get
if (m_intermediate_piece.start + m_intermediate_piece.length != r.start
|| m_intermediate_piece.piece != r.piece)
{
throw std::runtime_error("invalid range in HTTP response");
}
}
else
{
// this is the first part of a partial request
if (r.start != m_requests.front().start
|| r.piece != m_requests.front().piece)
{
throw std::runtime_error("invalid range in HTTP response");
}
m_intermediate_piece.piece = r.piece;
m_intermediate_piece.start = r.start;
m_intermediate_piece.length = 0;
}
m_piece.reserve(info.piece_length());
std::copy(http_body.begin, http_body.end, back_inserter(m_piece));
m_intermediate_piece.length += r.length;
if (m_intermediate_piece.length == m_requests.front().length)
{
assert(m_requests.front() == m_intermediate_piece);
assert(int(m_piece.size()) == m_intermediate_piece.length);
m_requests.pop_front();
incoming_piece(m_intermediate_piece, &m_piece[0]);
m_piece.clear();
}
else if (m_intermediate_piece.length > m_requests.front().length)
{
throw std::runtime_error("too large HTTP response body");
}
cut_receive_buffer(http_body.end - recv_buffer.begin, 512*1024+1024);
}
}
// --------------------------
// SEND DATA
// --------------------------
void web_peer_connection::get_peer_info(peer_info& p) const
{
assert(!associated_torrent().expired());
p.down_speed = statistics().download_rate();
p.up_speed = statistics().upload_rate();
p.payload_down_speed = statistics().download_payload_rate();
p.payload_up_speed = statistics().upload_payload_rate();
p.pid = pid();
p.ip = remote();
p.total_download = statistics().total_payload_download();
p.total_upload = statistics().total_payload_upload();
if (m_ul_bandwidth_quota.given == std::numeric_limits<int>::max())
p.upload_limit = -1;
else
p.upload_limit = m_ul_bandwidth_quota.given;
if (m_dl_bandwidth_quota.given == std::numeric_limits<int>::max())
p.download_limit = -1;
else
p.download_limit = m_dl_bandwidth_quota.given;
p.load_balancing = total_free_upload();
p.download_queue_length = (int)download_queue().size();
p.upload_queue_length = (int)upload_queue().size();
if (boost::optional<piece_block_progress> ret = downloading_piece_progress())
{
p.downloading_piece_index = ret->piece_index;
p.downloading_block_index = ret->block_index;
p.downloading_progress = ret->bytes_downloaded;
p.downloading_total = ret->full_block_bytes;
}
else
{
p.downloading_piece_index = -1;
p.downloading_block_index = -1;
p.downloading_progress = 0;
p.downloading_total = 0;
}
p.flags = 0;
if (is_interesting()) p.flags |= peer_info::interesting;
if (is_choked()) p.flags |= peer_info::choked;
if (is_peer_interested()) p.flags |= peer_info::remote_interested;
if (has_peer_choked()) p.flags |= peer_info::remote_choked;
if (is_local()) 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.pieces = get_bitfield();
p.seed = is_seed();
p.client = m_server_string;
p.connection_type = peer_info::web_seed;
}
// throws exception when the client should be disconnected
void web_peer_connection::on_sent(asio::error const& error
, std::size_t bytes_transferred)
{
INVARIANT_CHECK;
if (error) return;
m_statistics.sent_bytes(0, bytes_transferred);
}
#ifndef NDEBUG
void web_peer_connection::check_invariant() const
{
/*
assert(m_num_pieces == std::count(
m_have_piece.begin()
, m_have_piece.end()
, true));
*/ }
#endif
}

View File

@ -15,5 +15,6 @@ test-suite libtorrent :
[ run test_bencoding.cpp ]
[ run test_ip_filter.cpp ]
[ run test_hasher.cpp ]
[ run test_metadata_extension.cpp ]
;

View File

@ -1,4 +1,4 @@
bin_PROGRAMS = test_hasher test_bencoding test_ip_filter test_piece_picker test_storage
bin_PROGRAMS = test_hasher test_bencoding test_ip_filter test_piece_picker test_storage test_metadata_extension test_buffer
EXTRA_DIST = Jamfile
test_hasher_SOURCES = main.cpp test_hasher.cpp
@ -16,7 +16,13 @@ test_piece_picker_LDADD = $(top_builddir)/src/libtorrent.la
test_storage_SOURCES = main.cpp test_storage.cpp
test_storage_LDADD = $(top_builddir)/src/libtorrent.la
test_buffer_SOURCES = main.cpp test_buffer.cpp
test_buffer_LDADD = $(top_builddir)/src/libtorrent.la
test_metadata_extension_SOURCES = main.cpp test_metadata_extension.cpp
test_metadata_extension_LDADD = $(top_builddir)/src/libtorrent.la
noinst_HEADERS = test.hpp
AM_CXXFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include @DEBUGFLAGS@ @PTHREAD_CFLAGS@
AM_CXXFLAGS=-ftemplate-depth-50 -I$(top_srcdir)/include -I$(top_srcdir)/asio/include @DEBUGFLAGS@ @PTHREAD_CFLAGS@
AM_LDFLAGS= -L./ -l@BOOST_DATE_TIME_LIB@ -l@BOOST_FILESYSTEM_LIB@ -l@BOOST_THREAD_LIB@ @PTHREAD_LIBS@

View File

@ -63,8 +63,8 @@ int test_main()
// ** dictionaries **
{
entry e(entry::dictionary_t);
e["cow"] = entry("moo");
e["spam"] = entry("eggs");
e["cow"] = entry("moo");
TEST_CHECK(encode(e) == "d3:cow3:moo4:spam4:eggse");
TEST_CHECK(decode(encode(e)) == e);
}

View File

@ -19,8 +19,8 @@ void test_rules_invariant(std::vector<ip_filter::ip_range> const& r, ip_filter c
TEST_CHECK(!r.empty());
if (r.empty()) return;
TEST_CHECK(r.front().first == address(0,0,0,0,0));
TEST_CHECK(r.back().last == address(255,255,255,255,0));
TEST_CHECK(r.front().first == address("0.0.0.0"));
TEST_CHECK(r.back().last == address("255.255.255.255"));
iterator i = r.begin();
iterator j = boost::next(i);
@ -29,7 +29,7 @@ void test_rules_invariant(std::vector<ip_filter::ip_range> const& r, ip_filter c
{
TEST_CHECK(f.access(i->last) == i->flags);
TEST_CHECK(f.access(j->first) == j->flags);
TEST_CHECK(i->last.ip() + 1 == j->first.ip());
TEST_CHECK(i->last.to_ulong() + 1 == j->first.to_ulong());
}
}
@ -41,15 +41,15 @@ int test_main()
// **** test joining of ranges at the end ****
ip_filter::ip_range expected1[] =
{
{address(0,0,0,0,0), address(0,255,255,255,0), 0}
, {address(1,0,0,0,0), address(3,0,0,0,0), ip_filter::blocked}
, {address(3,0,0,1,0), address(255,255,255,255,0), 0}
{address("0.0.0.0"), address("0.255.255.255"), 0}
, {address("1.0.0.0"), address("3.0.0.0"), ip_filter::blocked}
, {address("3.0.0.1"), address("255.255.255.255"), 0}
};
{
ip_filter f;
f.add_rule(address(1,0,0,0,0), address(2,0,0,0,0), ip_filter::blocked);
f.add_rule(address(2,0,0,1,0), address(3,0,0,0,0), ip_filter::blocked);
f.add_rule(address("1.0.0.0"), address("2.0.0.0"), ip_filter::blocked);
f.add_rule(address("2.0.0.1"), address("3.0.0.0"), ip_filter::blocked);
range = f.export_filter();
test_rules_invariant(range, f);
@ -62,8 +62,8 @@ int test_main()
{
ip_filter f;
f.add_rule(address(2,0,0,1,0), address(3,0,0,0,0), ip_filter::blocked);
f.add_rule(address(1,0,0,0,0), address(2,0,0,0,0), ip_filter::blocked);
f.add_rule(address("2.0.0.1"), address("3.0.0.0"), ip_filter::blocked);
f.add_rule(address("1.0.0.0"), address("2.0.0.0"), ip_filter::blocked);
range = f.export_filter();
test_rules_invariant(range, f);
@ -77,8 +77,8 @@ int test_main()
{
ip_filter f;
f.add_rule(address(2,0,0,1,0), address(3,0,0,0,0), ip_filter::blocked);
f.add_rule(address(1,0,0,0,0), address(2,4,0,0,0), ip_filter::blocked);
f.add_rule(address("2.0.0.1"), address("3.0.0.0"), ip_filter::blocked);
f.add_rule(address("1.0.0.0"), address("2.4.0.0"), ip_filter::blocked);
range = f.export_filter();
test_rules_invariant(range, f);
@ -92,8 +92,8 @@ int test_main()
{
ip_filter f;
f.add_rule(address(1,0,0,0,0), address(2,4,0,0,0), ip_filter::blocked);
f.add_rule(address(2,0,0,1,0), address(3,0,0,0,0), ip_filter::blocked);
f.add_rule(address("1.0.0.0"), address("2.4.0.0"), ip_filter::blocked);
f.add_rule(address("2.0.0.1"), address("3.0.0.0"), ip_filter::blocked);
range = f.export_filter();
test_rules_invariant(range, f);
@ -107,12 +107,12 @@ int test_main()
{
ip_filter f;
f.add_rule(address(1,0,0,0,0), address(2,0,0,0,0), ip_filter::blocked);
f.add_rule(address(3,0,0,0,0), address(4,0,0,0,0), ip_filter::blocked);
f.add_rule(address(5,0,0,0,0), address(6,0,0,0,0), ip_filter::blocked);
f.add_rule(address(7,0,0,0,0), address(8,0,0,0,0), ip_filter::blocked);
f.add_rule(address("1.0.0.0"), address("2.0.0.0"), ip_filter::blocked);
f.add_rule(address("3.0.0.0"), address("4.0.0.0"), ip_filter::blocked);
f.add_rule(address("5.0.0.0"), address("6.0.0.0"), ip_filter::blocked);
f.add_rule(address("7.0.0.0"), address("8.0.0.0"), ip_filter::blocked);
f.add_rule(address(1,0,1,0,0), address(9,0,0,0,0), ip_filter::blocked);
f.add_rule(address("1.0.1.0"), address("9.0.0.0"), ip_filter::blocked);
range = f.export_filter();
test_rules_invariant(range, f);
@ -120,9 +120,9 @@ int test_main()
TEST_CHECK(range.size() == 3);
ip_filter::ip_range expected[] =
{
{address(0,0,0,0,0), address(0,255,255,255,0), 0}
, {address(1,0,0,0,0), address(9,0,0,0,0), ip_filter::blocked}
, {address(9,0,0,1,0), address(255,255,255,255,0), 0}
{address("0.0.0.0"), address("0.255.255.255"), 0}
, {address("1.0.0.0"), address("9.0.0.0"), ip_filter::blocked}
, {address("9.0.0.1"), address("255.255.255.255"), 0}
};
TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare));
@ -132,12 +132,12 @@ int test_main()
{
ip_filter f;
f.add_rule(address(1,0,0,0,0), address(2,0,0,0,0), ip_filter::blocked);
f.add_rule(address(3,0,0,0,0), address(4,0,0,0,0), ip_filter::blocked);
f.add_rule(address(5,0,0,0,0), address(6,0,0,0,0), ip_filter::blocked);
f.add_rule(address(7,0,0,0,0), address(8,0,0,0,0), ip_filter::blocked);
f.add_rule(address("1.0.0.0"), address("2.0.0.0"), ip_filter::blocked);
f.add_rule(address("3.0.0.0"), address("4.0.0.0"), ip_filter::blocked);
f.add_rule(address("5.0.0.0"), address("6.0.0.0"), ip_filter::blocked);
f.add_rule(address("7.0.0.0"), address("8.0.0.0"), ip_filter::blocked);
f.add_rule(address(0,0,1,0,0), address(7,0,4,0,0), ip_filter::blocked);
f.add_rule(address("0.0.1.0"), address("7.0.4.0"), ip_filter::blocked);
range = f.export_filter();
test_rules_invariant(range, f);
@ -145,9 +145,9 @@ int test_main()
TEST_CHECK(range.size() == 3);
ip_filter::ip_range expected[] =
{
{address(0,0,0,0,0), address(0,0,0,255,0), 0}
, {address(0,0,1,0,0), address(8,0,0,0,0), ip_filter::blocked}
, {address(8,0,0,1,0), address(255,255,255,255,0), 0}
{address("0.0.0.0"), address("0.0.0.255"), 0}
, {address("0.0.1.0"), address("8.0.0.0"), ip_filter::blocked}
, {address("8.0.0.1"), address("255.255.255.255"), 0}
};
TEST_CHECK(std::equal(range.begin(), range.end(), expected, &compare));

View File

@ -0,0 +1,106 @@
#include "libtorrent/session.hpp"
#include "libtorrent/hasher.hpp"
#include <boost/thread.hpp>
#include "test.hpp"
void sleep(int msec)
{
boost::xtime xt;
boost::xtime_get(&xt, boost::TIME_UTC);
xt.nsec += msec * 1000000;
boost::thread::sleep(xt);
}
void test_transfer(char const* tracker_url, libtorrent::torrent_info const& t)
{
using namespace libtorrent;
session ses1;
session ses2(fingerprint("LT", 0, 1, 0, 0), std::make_pair(49000, 50000));
// they should not use the same save dir, because the
// file pool will complain if two torrents are trying to
// use the same files
torrent_handle tor1 = ses1.add_torrent(t, "./tmp1");
torrent_handle tor2 = ses2.add_torrent(tracker_url
, t.info_hash(), "./tmp2");
std::cerr << "waiting for file check to complete\n";
// wait for 5 seconds or until the torrent is in a state
// were it can accept connections
for (int i = 0; i < 50; ++i)
{
torrent_status st = tor1.status();
if (st.state != torrent_status::queued_for_checking
&&st.state != torrent_status::checking_files)
break;
sleep(100);
}
std::cerr << "connecting peer\n";
tor1.connect_peer(tcp::endpoint(ses2.listen_port(), "127.0.0.1"));
for (int i = 0; i < 50; ++i)
{
// make sure this function can be called on
// torrents without metadata
tor2.status();
if (tor2.has_metadata()) break;
sleep(100);
}
std::cerr << "metadata received. waiting for transfer to complete\n";
TEST_CHECK(tor2.has_metadata());
for (int i = 0; i < 50; ++i)
{
tor2.status();
if (tor2.is_seed()) break;
sleep(100);
}
std::cerr << "done\n";
TEST_CHECK(tor2.is_seed());
}
int test_main()
{
using namespace libtorrent;
using namespace boost::filesystem;
char const* tracker_url = "http://non-existant-name.com/announce";
torrent_info t;
t.add_file(path("temporary"), 42);
t.set_piece_size(256 * 1024);
t.add_tracker(tracker_url);
std::vector<char> piece(42);
std::fill(piece.begin(), piece.end(), 0xfe);
// calculate the hash for all pieces
int num = t.num_pieces();
for (int i = 0; i < num; ++i)
{
t.set_hash(i, hasher(&piece[0], piece.size()).final());
}
create_directory("./tmp1");
std::ofstream file("./tmp1/temporary");
file.write(&piece[0], piece.size());
file.close();
remove_all("./tmp2/temporary");
t.create_torrent();
// test where one has data and one doesn't
test_transfer(tracker_url, t);
// test where both have data (to trigger the file check)
test_transfer(tracker_url, t);
return 0;
}

View File

@ -12,7 +12,7 @@ int test_main()
const int num_pieces = 6;
// 4 blocks per piece
piece_picker p(4, num_pieces * 4);
piece_picker p(4, num_pieces * 4, 7);
// we have the first piece
std::vector<bool> have(num_pieces, false);
@ -86,21 +86,21 @@ int test_main()
std::vector<piece_block> picked;
picked.clear();
p.pick_pieces(peer1, picked, 1, false, address());
p.pick_pieces(peer1, picked, 1, false, tcp::endpoint());
TEST_CHECK(picked.size() == 1);
TEST_CHECK(picked.front().piece_index == 2);
// now pick a piece from peer2. The block is supposed to be
// from piece 3, since it is the rarest piece that peer has.
picked.clear();
p.pick_pieces(peer2, picked, 1, false, address());
p.pick_pieces(peer2, picked, 1, false, tcp::endpoint());
TEST_CHECK(picked.size() == 1);
TEST_CHECK(picked.front().piece_index == 3);
// same thing for peer3.
picked.clear();
p.pick_pieces(peer3, picked, 1, false, address());
p.pick_pieces(peer3, picked, 1, false, tcp::endpoint());
TEST_CHECK(picked.size() == 1);
TEST_CHECK(picked.front().piece_index == 5);
@ -114,7 +114,7 @@ int test_main()
p.inc_refcount(1);
picked.clear();
p.pick_pieces(peer3, picked, 1, false, address());
p.pick_pieces(peer3, picked, 1, false, tcp::endpoint());
TEST_CHECK(picked.size() == 1);
TEST_CHECK(picked.front().piece_index == 1);
// and the block picked should not be 0 or 2
@ -138,9 +138,9 @@ int test_main()
// we have block 0 and 2 already, so we can't mark
// them as begin downloaded.
p.mark_as_downloading(piece_block(1, 1), address(1,1,1,1,0));
p.mark_as_downloading(piece_block(1, 3), address(1,1,1,1,0));
p.mark_as_downloading(piece_block(2, 0), address(1,1,1,1,0));
p.mark_as_downloading(piece_block(1, 1), tcp::endpoint(0, address("1.1.1.1")));
p.mark_as_downloading(piece_block(1, 3), tcp::endpoint(0, address("1.1.1.1")));
p.mark_as_downloading(piece_block(2, 0), tcp::endpoint(0, address("1.1.1.1")));
std::vector<piece_picker::downloading_piece> const& downloads = p.get_download_queue();
TEST_CHECK(downloads.size() == 2);
@ -168,7 +168,7 @@ int test_main()
TEST_CHECK(!p.is_downloading(piece_block(2, 1)));
picked.clear();
p.pick_pieces(peer1, picked, 1, false, address());
p.pick_pieces(peer1, picked, 1, false, tcp::endpoint());
TEST_CHECK(picked.size() == 2);
piece_block expected3[] = { piece_block(2, 0), piece_block(2, 1) };
@ -181,7 +181,7 @@ int test_main()
// partially selected)
picked.clear();
p.pick_pieces(peer1, picked, 1, true, address());
p.pick_pieces(peer1, picked, 1, true, tcp::endpoint());
// it will pick 4 blocks, since we said we
// wanted whole pieces.
@ -199,7 +199,7 @@ int test_main()
// to make sure it can still fall back on partial pieces
picked.clear();
p.pick_pieces(peer1, picked, 100, true, address());
p.pick_pieces(peer1, picked, 100, true, tcp::endpoint());
TEST_CHECK(picked.size() == 14);
@ -221,12 +221,7 @@ int test_main()
// to make sure it can still fall back on partial pieces
picked.clear();
p.pick_pieces(peer1, picked, 100, true, address(1,1,1,1,0));
for (std::vector<piece_block>::iterator i = picked.begin(); i != picked.end(); ++i)
{
std::cerr << "(" << i->piece_index << "," << i->block_index << ")\n";
}
p.pick_pieces(peer1, picked, 100, true, tcp::endpoint(0, address("1.1.1.1")));
TEST_CHECK(picked.size() == 11);
@ -245,7 +240,7 @@ int test_main()
// make sure the piece picker allows filtered pieces
// to become available
p.mark_as_finished(piece_block(4, 2), address());
p.mark_as_finished(piece_block(4, 2), tcp::endpoint());
}
return 0;

View File

@ -79,11 +79,16 @@ int test_main()
libtorrent::detail::piece_checker_data d;
std::vector<bool> pieces;
TEST_CHECK(pm.check_fastresume(d, pieces, true) == false);
num_pieces = 0;
TEST_CHECK(pm.check_fastresume(d, pieces, num_pieces, true) == false);
bool finished = false;
float progress;
num_pieces = 0;
while (!finished)
boost::tie(finished, progress) = pm.check_files(pieces);
boost::tie(finished, progress) = pm.check_files(pieces, num_pieces);
TEST_CHECK(num_pieces == std::count(pieces.begin(), pieces.end()
, true));
pm.read(piece, 0, 0, piece_size);
TEST_CHECK(std::equal(piece, piece + piece_size, piece0));