added a setting for max outstanding requests, and a limit for BitComet since it's known to have a very low limit, also added an extension header to specify it. Fixed another unnecessary delay spotted by Tianhao Qiu. Cleaned up the logging class a bit. Fixed a bug that would cause an assert when removing a torrent queued for checking.
This commit is contained in:
@@ -3,10 +3,130 @@
|
|||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||||
<meta name="generator" content="Docutils 0.3.9: http://docutils.sourceforge.net/" />
|
<meta name="generator" content="Docutils 0.4: http://docutils.sourceforge.net/" />
|
||||||
<title></title>
|
<title></title>
|
||||||
<meta name="author" content="Arvid Norberg, arvid@rasterbar.com Ludvig Strigeus, ludde@utorrent.com" />
|
<meta name="author" content="Arvid Norberg, arvid@rasterbar.com Ludvig Strigeus, ludde@utorrent.com" />
|
||||||
<link rel="stylesheet" href="style.css" type="text/css" />
|
<style type="text/css">
|
||||||
|
|
||||||
|
body
|
||||||
|
{
|
||||||
|
background-color: white;
|
||||||
|
color: black;
|
||||||
|
margin: 1em 2em 1em 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 { font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold; text-align: left; font-size: 140%; }
|
||||||
|
h2 { font-family: Verdana, Arial, Helvetica, sans-serif; font-weight: bold; text-align: left; font-size: 110%; }
|
||||||
|
h3 { font-family: "courier new", courier, monospace; font-weight: bold; text-align: left; font-size: 100%; }
|
||||||
|
|
||||||
|
pre
|
||||||
|
{
|
||||||
|
border: gray 1pt solid;
|
||||||
|
padding: 2pt;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
font-family: "courier new", courier, monospace;
|
||||||
|
background-color: #eeeeee;
|
||||||
|
color: black;
|
||||||
|
font-size: small
|
||||||
|
}
|
||||||
|
|
||||||
|
code
|
||||||
|
{
|
||||||
|
white-space: pre;
|
||||||
|
border: gray 1pt solid;
|
||||||
|
padding: 2pt;
|
||||||
|
|
||||||
|
display: block;
|
||||||
|
font-family: "courier new", courier, monospace;
|
||||||
|
color: black;
|
||||||
|
font-size: small
|
||||||
|
}
|
||||||
|
|
||||||
|
tt
|
||||||
|
{
|
||||||
|
display: inline;
|
||||||
|
font-family: "Courier New", Courier, monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
p
|
||||||
|
{
|
||||||
|
text-align: justify;
|
||||||
|
font-family: Georgia, "Times New Roman", Times, serif
|
||||||
|
}
|
||||||
|
|
||||||
|
ul
|
||||||
|
{
|
||||||
|
font-family: Georgia, "Times New Roman", Times, serif
|
||||||
|
}
|
||||||
|
|
||||||
|
ol
|
||||||
|
{
|
||||||
|
font-family: Georgia, "Times New Roman", Times, serif
|
||||||
|
}
|
||||||
|
|
||||||
|
a:link
|
||||||
|
{
|
||||||
|
font-weight: bold;
|
||||||
|
color: #003366;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:visited
|
||||||
|
{
|
||||||
|
font-weight: bold;
|
||||||
|
color: #003366;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
table
|
||||||
|
{
|
||||||
|
border: 1px solid black;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td
|
||||||
|
{
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th
|
||||||
|
{
|
||||||
|
border: 3px solid black;
|
||||||
|
padding: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.menu
|
||||||
|
{
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.menu td
|
||||||
|
{
|
||||||
|
padding-left: 15px;
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-top: 7px;
|
||||||
|
padding-bottom: 7px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
td
|
||||||
|
{
|
||||||
|
border: 1px solid black
|
||||||
|
}
|
||||||
|
|
||||||
|
div.warning, div.note, div.important {
|
||||||
|
width: 80%;
|
||||||
|
margin: 1.5em auto;
|
||||||
|
background: #C1E5F6;
|
||||||
|
background: #F1FFF5;
|
||||||
|
border: solid 1px #D1DFD5;
|
||||||
|
padding: 5px 10px 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="document">
|
<div class="document">
|
||||||
@@ -19,8 +139,8 @@
|
|||||||
Ludvig Strigeus, <a class="last reference" href="mailto:ludde@utorrent.com">ludde@utorrent.com</a></td></tr>
|
Ludvig Strigeus, <a class="last reference" href="mailto:ludde@utorrent.com">ludde@utorrent.com</a></td></tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="section" id="extension-protocol-for-bittorrent">
|
<div class="section">
|
||||||
<h1><a name="extension-protocol-for-bittorrent">extension protocol for bittorrent</a></h1>
|
<h1><a id="extension-protocol-for-bittorrent" 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
|
<p>The intention of this protocol is to provide a simple and thin transport
|
||||||
for extensions to the bittorrent protocol. Supporting this protocol makes
|
for extensions to the bittorrent protocol. Supporting this protocol makes
|
||||||
it easy to add new extensions without interfering with the standard
|
it easy to add new extensions without interfering with the standard
|
||||||
@@ -88,8 +208,8 @@ message as specified by the handshake.</td>
|
|||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="section" id="handshake-message">
|
<div class="section">
|
||||||
<h2><a name="handshake-message">handshake message</a></h2>
|
<h2><a id="handshake-message" name="handshake-message">handshake message</a></h2>
|
||||||
<p>The payload of the handshake message is a bencoded dictionary. All items
|
<p>The payload of the handshake message is a bencoded dictionary. All items
|
||||||
in the dictionary are optional. Any unknown names should be ignored
|
in the dictionary are optional. Any unknown names should be ignored
|
||||||
by the client. All parts of the dictionary are case sensitive.
|
by the client. All parts of the dictionary are case sensitive.
|
||||||
@@ -140,6 +260,11 @@ known.</td>
|
|||||||
This is a much more reliable way of identifying the
|
This is a much more reliable way of identifying the
|
||||||
client than relying on the peer id encoding.</td>
|
client than relying on the peer id encoding.</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr><td>reqq</td>
|
||||||
|
<td>An integer, the number of outstanding request messages
|
||||||
|
this client supports without dropping any. The default in
|
||||||
|
in libtorrent is 250.</td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>The handshake dictionary could also include extended handshake
|
<p>The handshake dictionary could also include extended handshake
|
||||||
@@ -214,8 +339,8 @@ 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
|
named in the example above (such as <tt class="docutils literal"><span class="pre">p</span></tt>) are just examples of possible
|
||||||
extensions.</p>
|
extensions.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section" id="rationale">
|
<div class="section">
|
||||||
<h2><a name="rationale">rationale</a></h2>
|
<h2><a id="rationale" name="rationale">rationale</a></h2>
|
||||||
<p>The reason why the extension messages' IDs would be defined in the handshake
|
<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
|
is to avoid having a global registry somewhere, where ID's are assigned
|
||||||
global identifiers. Now the extensions have unique names.</p>
|
global identifiers. Now the extensions have unique names.</p>
|
||||||
|
@@ -87,6 +87,10 @@ Here are two other items that an implementation may choose to support:
|
|||||||
| | This is a much more reliable way of identifying the |
|
| | This is a much more reliable way of identifying the |
|
||||||
| | client than relying on the peer id encoding. |
|
| | client than relying on the peer id encoding. |
|
||||||
+-------+-----------------------------------------------------------+
|
+-------+-----------------------------------------------------------+
|
||||||
|
| reqq | An integer, the number of outstanding request messages |
|
||||||
|
| | this client supports without dropping any. The default in |
|
||||||
|
| | in libtorrent is 250. |
|
||||||
|
+-------+-----------------------------------------------------------+
|
||||||
|
|
||||||
The handshake dictionary could also include extended handshake
|
The handshake dictionary could also include extended handshake
|
||||||
information, such as support for encrypted headers or anything
|
information, such as support for encrypted headers or anything
|
||||||
|
@@ -229,60 +229,61 @@ div.warning, div.note, div.important {
|
|||||||
<li><a class="reference" href="#fingerprint" id="id96" name="id96">fingerprint</a></li>
|
<li><a class="reference" href="#fingerprint" id="id96" name="id96">fingerprint</a></li>
|
||||||
<li><a class="reference" href="#free-functions" id="id97" name="id97">free functions</a><ul>
|
<li><a class="reference" href="#free-functions" id="id97" name="id97">free functions</a><ul>
|
||||||
<li><a class="reference" href="#identify-client" id="id98" name="id98">identify_client()</a></li>
|
<li><a class="reference" href="#identify-client" id="id98" name="id98">identify_client()</a></li>
|
||||||
<li><a class="reference" href="#bdecode-bencode" id="id99" name="id99">bdecode() bencode()</a></li>
|
<li><a class="reference" href="#client-fingerprint" id="id99" name="id99">client_fingerprint()</a></li>
|
||||||
|
<li><a class="reference" href="#bdecode-bencode" id="id100" name="id100">bdecode() bencode()</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference" href="#alerts" id="id100" name="id100">alerts</a><ul>
|
<li><a class="reference" href="#alerts" id="id101" name="id101">alerts</a><ul>
|
||||||
<li><a class="reference" href="#listen-failed-alert" id="id101" name="id101">listen_failed_alert</a></li>
|
<li><a class="reference" href="#listen-failed-alert" id="id102" name="id102">listen_failed_alert</a></li>
|
||||||
<li><a class="reference" href="#file-error-alert" id="id102" name="id102">file_error_alert</a></li>
|
<li><a class="reference" href="#file-error-alert" id="id103" name="id103">file_error_alert</a></li>
|
||||||
<li><a class="reference" href="#tracker-announce-alert" id="id103" name="id103">tracker_announce_alert</a></li>
|
<li><a class="reference" href="#tracker-announce-alert" id="id104" name="id104">tracker_announce_alert</a></li>
|
||||||
<li><a class="reference" href="#tracker-alert" id="id104" name="id104">tracker_alert</a></li>
|
<li><a class="reference" href="#tracker-alert" id="id105" name="id105">tracker_alert</a></li>
|
||||||
<li><a class="reference" href="#tracker-reply-alert" id="id105" name="id105">tracker_reply_alert</a></li>
|
<li><a class="reference" href="#tracker-reply-alert" id="id106" name="id106">tracker_reply_alert</a></li>
|
||||||
<li><a class="reference" href="#tracker-warning-alert" id="id106" name="id106">tracker_warning_alert</a></li>
|
<li><a class="reference" href="#tracker-warning-alert" id="id107" name="id107">tracker_warning_alert</a></li>
|
||||||
<li><a class="reference" href="#url-seed-alert" id="id107" name="id107">url_seed_alert</a></li>
|
<li><a class="reference" href="#url-seed-alert" id="id108" name="id108">url_seed_alert</a></li>
|
||||||
<li><a class="reference" href="#hash-failed-alert" id="id108" name="id108">hash_failed_alert</a></li>
|
<li><a class="reference" href="#hash-failed-alert" id="id109" name="id109">hash_failed_alert</a></li>
|
||||||
<li><a class="reference" href="#peer-ban-alert" id="id109" name="id109">peer_ban_alert</a></li>
|
<li><a class="reference" href="#peer-ban-alert" id="id110" name="id110">peer_ban_alert</a></li>
|
||||||
<li><a class="reference" href="#peer-error-alert" id="id110" name="id110">peer_error_alert</a></li>
|
<li><a class="reference" href="#peer-error-alert" id="id111" name="id111">peer_error_alert</a></li>
|
||||||
<li><a class="reference" href="#invalid-request-alert" id="id111" name="id111">invalid_request_alert</a></li>
|
<li><a class="reference" href="#invalid-request-alert" id="id112" name="id112">invalid_request_alert</a></li>
|
||||||
<li><a class="reference" href="#torrent-finished-alert" id="id112" name="id112">torrent_finished_alert</a></li>
|
<li><a class="reference" href="#torrent-finished-alert" id="id113" name="id113">torrent_finished_alert</a></li>
|
||||||
<li><a class="reference" href="#metadata-failed-alert" id="id113" name="id113">metadata_failed_alert</a></li>
|
<li><a class="reference" href="#metadata-failed-alert" id="id114" name="id114">metadata_failed_alert</a></li>
|
||||||
<li><a class="reference" href="#metadata-received-alert" id="id114" name="id114">metadata_received_alert</a></li>
|
<li><a class="reference" href="#metadata-received-alert" id="id115" name="id115">metadata_received_alert</a></li>
|
||||||
<li><a class="reference" href="#fastresume-rejected-alert" id="id115" name="id115">fastresume_rejected_alert</a></li>
|
<li><a class="reference" href="#fastresume-rejected-alert" id="id116" name="id116">fastresume_rejected_alert</a></li>
|
||||||
<li><a class="reference" href="#dispatcher" id="id116" name="id116">dispatcher</a></li>
|
<li><a class="reference" href="#dispatcher" id="id117" name="id117">dispatcher</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference" href="#exceptions" id="id117" name="id117">exceptions</a><ul>
|
<li><a class="reference" href="#exceptions" id="id118" name="id118">exceptions</a><ul>
|
||||||
<li><a class="reference" href="#invalid-handle" id="id118" name="id118">invalid_handle</a></li>
|
<li><a class="reference" href="#invalid-handle" id="id119" name="id119">invalid_handle</a></li>
|
||||||
<li><a class="reference" href="#duplicate-torrent" id="id119" name="id119">duplicate_torrent</a></li>
|
<li><a class="reference" href="#duplicate-torrent" id="id120" name="id120">duplicate_torrent</a></li>
|
||||||
<li><a class="reference" href="#invalid-encoding" id="id120" name="id120">invalid_encoding</a></li>
|
<li><a class="reference" href="#invalid-encoding" id="id121" name="id121">invalid_encoding</a></li>
|
||||||
<li><a class="reference" href="#type-error" id="id121" name="id121">type_error</a></li>
|
<li><a class="reference" href="#type-error" id="id122" name="id122">type_error</a></li>
|
||||||
<li><a class="reference" href="#invalid-torrent-file" id="id122" name="id122">invalid_torrent_file</a></li>
|
<li><a class="reference" href="#invalid-torrent-file" id="id123" name="id123">invalid_torrent_file</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference" href="#examples" id="id123" name="id123">examples</a><ul>
|
<li><a class="reference" href="#examples" id="id124" name="id124">examples</a><ul>
|
||||||
<li><a class="reference" href="#dump-torrent" id="id124" name="id124">dump_torrent</a></li>
|
<li><a class="reference" href="#dump-torrent" id="id125" name="id125">dump_torrent</a></li>
|
||||||
<li><a class="reference" href="#simple-client" id="id125" name="id125">simple client</a></li>
|
<li><a class="reference" href="#simple-client" id="id126" name="id126">simple client</a></li>
|
||||||
<li><a class="reference" href="#make-torrent" id="id126" name="id126">make_torrent</a></li>
|
<li><a class="reference" href="#make-torrent" id="id127" name="id127">make_torrent</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference" href="#fast-resume" id="id127" name="id127">fast resume</a><ul>
|
<li><a class="reference" href="#fast-resume" id="id128" name="id128">fast resume</a><ul>
|
||||||
<li><a class="reference" href="#file-format" id="id128" name="id128">file format</a></li>
|
<li><a class="reference" href="#file-format" id="id129" name="id129">file format</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference" href="#threads" id="id129" name="id129">threads</a></li>
|
<li><a class="reference" href="#threads" id="id130" name="id130">threads</a></li>
|
||||||
<li><a class="reference" href="#storage-allocation" id="id130" name="id130">storage allocation</a><ul>
|
<li><a class="reference" href="#storage-allocation" id="id131" name="id131">storage allocation</a><ul>
|
||||||
<li><a class="reference" href="#full-allocation" id="id131" name="id131">full allocation</a></li>
|
<li><a class="reference" href="#full-allocation" id="id132" name="id132">full allocation</a></li>
|
||||||
<li><a class="reference" href="#compact-allocation" id="id132" name="id132">compact allocation</a></li>
|
<li><a class="reference" href="#compact-allocation" id="id133" name="id133">compact allocation</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference" href="#extensions" id="id133" name="id133">extensions</a><ul>
|
<li><a class="reference" href="#extensions" id="id134" name="id134">extensions</a><ul>
|
||||||
<li><a class="reference" href="#chat-messages" id="id134" name="id134">chat messages</a></li>
|
<li><a class="reference" href="#chat-messages" id="id135" name="id135">chat messages</a></li>
|
||||||
<li><a class="reference" href="#metadata-from-peers" id="id135" name="id135">metadata from peers</a></li>
|
<li><a class="reference" href="#metadata-from-peers" id="id136" name="id136">metadata from peers</a></li>
|
||||||
<li><a class="reference" href="#http-seeding" id="id136" name="id136">HTTP seeding</a></li>
|
<li><a class="reference" href="#http-seeding" id="id137" name="id137">HTTP seeding</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
<li><a class="reference" href="#filename-checks" id="id137" name="id137">filename checks</a></li>
|
<li><a class="reference" href="#filename-checks" id="id138" name="id138">filename checks</a></li>
|
||||||
<li><a class="reference" href="#acknowledgements" id="id138" name="id138">acknowledgements</a></li>
|
<li><a class="reference" href="#acknowledgements" id="id139" name="id139">acknowledgements</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="section">
|
<div class="section">
|
||||||
@@ -2459,6 +2460,18 @@ to extract a string describing a client version from its peer-id. It will recogn
|
|||||||
that have this kind of identification in the peer-id.</p>
|
that have this kind of identification in the peer-id.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="section">
|
<div class="section">
|
||||||
|
<h2><a id="client-fingerprint" name="client-fingerprint">client_fingerprint()</a></h2>
|
||||||
|
<blockquote>
|
||||||
|
<pre class="literal-block">
|
||||||
|
boost::optional<fingerprint> client_fingerprint(peer_id const& p);
|
||||||
|
</pre>
|
||||||
|
</blockquote>
|
||||||
|
<p>Returns an optional fingerprint if any can be identified from the peer id. This can be used
|
||||||
|
to automate the identification of clients. It will not be able to identifiy peers with non-
|
||||||
|
standard encodings. Only Azureus style, Shadow's style and Mainline style. This function is
|
||||||
|
declared in the header <tt class="docutils literal"><span class="pre"><libtorrent/identify_client.hpp></span></tt>.</p>
|
||||||
|
</div>
|
||||||
|
<div class="section">
|
||||||
<h2><a id="bdecode-bencode" name="bdecode-bencode">bdecode() bencode()</a></h2>
|
<h2><a id="bdecode-bencode" name="bdecode-bencode">bdecode() bencode()</a></h2>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<pre class="literal-block">
|
<pre class="literal-block">
|
||||||
|
@@ -2346,6 +2346,20 @@ This function is declared in the header ``<libtorrent/identify_client.hpp>``. It
|
|||||||
to extract a string describing a client version from its peer-id. It will recognize most clients
|
to extract a string describing a client version from its peer-id. It will recognize most clients
|
||||||
that have this kind of identification in the peer-id.
|
that have this kind of identification in the peer-id.
|
||||||
|
|
||||||
|
|
||||||
|
client_fingerprint()
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
boost::optional<fingerprint> client_fingerprint(peer_id const& p);
|
||||||
|
|
||||||
|
Returns an optional fingerprint if any can be identified from the peer id. This can be used
|
||||||
|
to automate the identification of clients. It will not be able to identifiy peers with non-
|
||||||
|
standard encodings. Only Azureus style, Shadow's style and Mainline style. This function is
|
||||||
|
declared in the header ``<libtorrent/identify_client.hpp>``.
|
||||||
|
|
||||||
|
|
||||||
bdecode() bencode()
|
bdecode() bencode()
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
|
@@ -67,63 +67,22 @@ namespace libtorrent
|
|||||||
|
|
||||||
struct logger
|
struct logger
|
||||||
{
|
{
|
||||||
logger& operator<<(const char* t)
|
logger(boost::filesystem::path const& filename, bool append = true)
|
||||||
{ assert(t); log(t); return *this; }
|
|
||||||
logger& operator<<(const std::string& t)
|
|
||||||
{ log(t.c_str()); return *this; }
|
|
||||||
logger& operator<<(int i)
|
|
||||||
{
|
|
||||||
log(boost::lexical_cast<std::string>(i).c_str());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
logger& operator<<(unsigned int i)
|
|
||||||
{
|
|
||||||
log(boost::lexical_cast<std::string>(i).c_str());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
logger& operator<<(float i)
|
|
||||||
{
|
|
||||||
log(boost::lexical_cast<std::string>(i).c_str());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
logger& operator<<(char i)
|
|
||||||
{
|
|
||||||
char c[2];
|
|
||||||
c[0] = i;
|
|
||||||
c[1] = 0;
|
|
||||||
log(c);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void log(const char*) = 0;
|
|
||||||
virtual ~logger() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct null_logger: libtorrent::logger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void log(const char*) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct cout_logger: libtorrent::logger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void log(const char* text) { std::cout << text; }
|
|
||||||
};
|
|
||||||
|
|
||||||
struct file_logger: libtorrent::logger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
file_logger(boost::filesystem::path const& filename, bool append = true)
|
|
||||||
{
|
{
|
||||||
using namespace boost::filesystem;
|
using namespace boost::filesystem;
|
||||||
path dir(complete("libtorrent_logs"));
|
path dir(complete("libtorrent_logs"));
|
||||||
if (!exists(dir)) create_directories(dir);
|
if (!exists(dir)) create_directories(dir);
|
||||||
m_file.open(dir / filename, std::ios_base::out | (append ? std::ios_base::app : std::ios_base::out));
|
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");
|
*this << "\n\n\n*** starting log ***\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
logger& operator<<(T const& v)
|
||||||
|
{
|
||||||
|
m_file << v;
|
||||||
|
m_file.flush();
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
virtual void log(const char* text) { assert(text); m_file << text; m_file.flush(); }
|
|
||||||
|
|
||||||
boost::filesystem::ofstream m_file;
|
boost::filesystem::ofstream m_file;
|
||||||
};
|
};
|
||||||
|
@@ -34,12 +34,14 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||||||
#define TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED
|
#define TORRENT_IDENTIFY_CLIENT_HPP_INCLUDED
|
||||||
|
|
||||||
#include "libtorrent/peer_id.hpp"
|
#include "libtorrent/peer_id.hpp"
|
||||||
|
#include "libtorrent/fingerprint.hpp"
|
||||||
#include "libtorrent/config.hpp"
|
#include "libtorrent/config.hpp"
|
||||||
|
|
||||||
namespace libtorrent
|
namespace libtorrent
|
||||||
{
|
{
|
||||||
|
|
||||||
TORRENT_EXPORT std::string identify_client(const peer_id& p);
|
TORRENT_EXPORT std::string identify_client(const peer_id& p);
|
||||||
|
TORRENT_EXPORT boost::optional<fingerprint> client_fingerprint(peer_id const& p);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -261,6 +261,7 @@ namespace libtorrent
|
|||||||
// adds a block to the request queue
|
// adds a block to the request queue
|
||||||
void add_request(piece_block const& b);
|
void add_request(piece_block const& b);
|
||||||
void cancel_request(piece_block const& b);
|
void cancel_request(piece_block const& b);
|
||||||
|
void send_block_requests();
|
||||||
|
|
||||||
// how much bandwidth we're using, how much we want,
|
// how much bandwidth we're using, how much we want,
|
||||||
// and how much we are allowed to use.
|
// and how much we are allowed to use.
|
||||||
@@ -360,10 +361,16 @@ namespace libtorrent
|
|||||||
void on_receive_data(asio::error const& error
|
void on_receive_data(asio::error const& error
|
||||||
, std::size_t bytes_transferred);
|
, std::size_t bytes_transferred);
|
||||||
|
|
||||||
|
// this is the limit on the number of outstanding requests
|
||||||
|
// we have to this peer. This is initialized to the settings
|
||||||
|
// in the session_settings structure. But it may be lowered
|
||||||
|
// if the peer is known to require a smaller limit (like BitComet).
|
||||||
|
// or if the extended handshake sets a limit.
|
||||||
|
int m_max_out_request_queue;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void fill_send_buffer();
|
void fill_send_buffer();
|
||||||
void send_block_requests();
|
|
||||||
|
|
||||||
// the timeout in seconds
|
// the timeout in seconds
|
||||||
int m_timeout;
|
int m_timeout;
|
||||||
@@ -386,7 +393,6 @@ namespace libtorrent
|
|||||||
// buffer, the other is used to write data to
|
// buffer, the other is used to write data to
|
||||||
// be queued up.
|
// be queued up.
|
||||||
std::vector<char> m_send_buffer[2];
|
std::vector<char> m_send_buffer[2];
|
||||||
// buffer m_send_buffer[2];
|
|
||||||
// the current send buffer is the one to write to.
|
// the current send buffer is the one to write to.
|
||||||
// (m_current_send_buffer + 1) % 2 is the
|
// (m_current_send_buffer + 1) % 2 is the
|
||||||
// buffer we're currently waiting for.
|
// buffer we're currently waiting for.
|
||||||
@@ -394,7 +400,7 @@ namespace libtorrent
|
|||||||
|
|
||||||
// if the sending buffer doesn't finish in one send
|
// if the sending buffer doesn't finish in one send
|
||||||
// operation, this is the position within that buffer
|
// operation, this is the position within that buffer
|
||||||
// wher the next operation should continue
|
// where the next operation should continue
|
||||||
int m_write_pos;
|
int m_write_pos;
|
||||||
|
|
||||||
// timeouts
|
// timeouts
|
||||||
|
@@ -51,6 +51,7 @@ namespace libtorrent
|
|||||||
// the number of bytes of the number
|
// the number of bytes of the number
|
||||||
enum { number_size = 20 };
|
enum { number_size = 20 };
|
||||||
public:
|
public:
|
||||||
|
enum { size = number_size };
|
||||||
|
|
||||||
big_number() {}
|
big_number() {}
|
||||||
|
|
||||||
|
@@ -62,7 +62,6 @@ namespace libtorrent
|
|||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
// the limits of the download queue size
|
// the limits of the download queue size
|
||||||
max_request_queue = 48,
|
|
||||||
min_request_queue = 2,
|
min_request_queue = 2,
|
||||||
|
|
||||||
// the amount of free upload allowed before
|
// the amount of free upload allowed before
|
||||||
|
@@ -42,8 +42,10 @@ namespace libtorrent
|
|||||||
: piece_timeout(120)
|
: piece_timeout(120)
|
||||||
, request_queue_time(3.f)
|
, request_queue_time(3.f)
|
||||||
, sequenced_download_threshold(7)
|
, sequenced_download_threshold(7)
|
||||||
, max_allowed_request_queue(200)
|
, max_allowed_in_request_queue(250)
|
||||||
{}
|
, max_out_request_queue(200)
|
||||||
|
, whole_pieces_threshold(20)
|
||||||
|
{}
|
||||||
|
|
||||||
// the number of seconds from a request is sent until
|
// the number of seconds from a request is sent until
|
||||||
// it times out if no piece response is returned.
|
// it times out if no piece response is returned.
|
||||||
@@ -70,7 +72,20 @@ namespace libtorrent
|
|||||||
// been sent) the last request will be dropped.
|
// been sent) the last request will be dropped.
|
||||||
// the higher this is, the faster upload speeds the
|
// the higher this is, the faster upload speeds the
|
||||||
// client can get to a single peer.
|
// client can get to a single peer.
|
||||||
int max_allowed_request_queue;
|
int max_allowed_in_request_queue;
|
||||||
|
|
||||||
|
// the maximum number of outstanding requests to
|
||||||
|
// send to a peer. This limit takes precedence over
|
||||||
|
// request_queue_time.
|
||||||
|
int max_out_request_queue;
|
||||||
|
|
||||||
|
// if a whole piece can be downloaded in this number
|
||||||
|
// of seconds, or less, the peer_connection will prefer
|
||||||
|
// to request whole pieces at a time from this peer.
|
||||||
|
// The benefit of this is to better utilize disk caches by
|
||||||
|
// doing localized accesses and also to make it easier
|
||||||
|
// to identify bad peers if a piece fails the hash check.
|
||||||
|
int whole_pieces_threshold;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -133,6 +133,8 @@ namespace libtorrent
|
|||||||
// by the checker thread.
|
// by the checker thread.
|
||||||
bool is_allocating() const;
|
bool is_allocating() const;
|
||||||
|
|
||||||
|
session_settings const& settings() const;
|
||||||
|
|
||||||
// is called every second by session. This will
|
// is called every second by session. This will
|
||||||
// caclulate the upload/download and number
|
// caclulate the upload/download and number
|
||||||
// of connections this torrent needs. And prepare
|
// of connections this torrent needs. And prepare
|
||||||
|
@@ -724,6 +724,14 @@ namespace libtorrent
|
|||||||
if (client_info->type() == entry::string_t)
|
if (client_info->type() == entry::string_t)
|
||||||
m_client_version = client_info->string();
|
m_client_version = client_info->string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (entry* reqq = root.find_key("reqq"))
|
||||||
|
{
|
||||||
|
if (reqq->type() == entry::int_t)
|
||||||
|
m_max_out_request_queue = reqq->integer();
|
||||||
|
if (m_max_out_request_queue < 1)
|
||||||
|
m_max_out_request_queue = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception& exc)
|
catch (std::exception& exc)
|
||||||
{
|
{
|
||||||
@@ -1113,6 +1121,7 @@ namespace libtorrent
|
|||||||
handshake["m"] = extension_list;
|
handshake["m"] = extension_list;
|
||||||
handshake["p"] = m_ses.m_listen_interface.port();
|
handshake["p"] = m_ses.m_listen_interface.port();
|
||||||
handshake["v"] = m_ses.m_http_settings.user_agent;
|
handshake["v"] = m_ses.m_http_settings.user_agent;
|
||||||
|
handshake["reqq"] = m_ses.m_settings.max_allowed_in_request_queue;
|
||||||
|
|
||||||
std::vector<char> msg;
|
std::vector<char> msg;
|
||||||
bencode(std::back_inserter(msg), handshake);
|
bencode(std::back_inserter(msg), handshake);
|
||||||
@@ -1395,12 +1404,18 @@ namespace libtorrent
|
|||||||
set_pid(pid);
|
set_pid(pid);
|
||||||
|
|
||||||
m_client_version = identify_client(pid);
|
m_client_version = identify_client(pid);
|
||||||
|
boost::optional<fingerprint> f = client_fingerprint(pid);
|
||||||
|
if (f && std::equal(f->name, f->name + 2, "BC"))
|
||||||
|
{
|
||||||
|
// if this is a bitcomet client, lower the request queue size limit
|
||||||
|
if (m_max_out_request_queue > 50) m_max_out_request_queue = 50;
|
||||||
|
}
|
||||||
|
|
||||||
// disconnect if the peer has the same peer-id as ourself
|
// disconnect if the peer has the same peer-id as ourself
|
||||||
// since it most likely is ourself then
|
// since it most likely is ourself then
|
||||||
if (pid == m_ses.get_peer_id())
|
if (pid == m_ses.get_peer_id())
|
||||||
throw std::runtime_error("closing connection to ourself");
|
throw std::runtime_error("closing connection to ourself");
|
||||||
|
|
||||||
if (m_supports_extensions) write_extensions();
|
if (m_supports_extensions) write_extensions();
|
||||||
/*
|
/*
|
||||||
if (!m_active)
|
if (!m_active)
|
||||||
|
@@ -247,6 +247,7 @@ namespace libtorrent
|
|||||||
{
|
{
|
||||||
throw_exception("file::seek");
|
throw_exception("file::seek");
|
||||||
}
|
}
|
||||||
|
return offs.QuadPart;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_type tell()
|
size_type tell()
|
||||||
@@ -264,8 +265,8 @@ namespace libtorrent
|
|||||||
throw_exception("file::tell");
|
throw_exception("file::tell");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_type pos=offs.QuadPart;
|
size_type pos = offs.QuadPart;
|
||||||
assert(pos>=0);
|
assert(pos >= 0);
|
||||||
return pos;
|
return pos;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@@ -226,7 +226,24 @@ namespace
|
|||||||
namespace libtorrent
|
namespace libtorrent
|
||||||
{
|
{
|
||||||
|
|
||||||
std::string identify_client(const peer_id& p)
|
boost::optional<fingerprint> client_fingerprint(peer_id const& p)
|
||||||
|
{
|
||||||
|
// look for azureus style id
|
||||||
|
boost::optional<fingerprint> f;
|
||||||
|
f = parse_az_style(p);
|
||||||
|
if (f) return f;
|
||||||
|
|
||||||
|
// look for shadow style id
|
||||||
|
f = parse_shadow_style(p);
|
||||||
|
if (f) return f;
|
||||||
|
|
||||||
|
// look for mainline style id
|
||||||
|
f = parse_mainline_style(p);
|
||||||
|
if (f) return f;
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string identify_client(peer_id const& p)
|
||||||
{
|
{
|
||||||
peer_id::const_iterator PID = p.begin();
|
peer_id::const_iterator PID = p.begin();
|
||||||
boost::optional<fingerprint> f;
|
boost::optional<fingerprint> f;
|
||||||
|
@@ -82,6 +82,7 @@ namespace libtorrent
|
|||||||
,
|
,
|
||||||
#endif
|
#endif
|
||||||
m_ses(ses)
|
m_ses(ses)
|
||||||
|
, m_max_out_request_queue(m_ses.m_settings.max_out_request_queue)
|
||||||
, m_timeout(120)
|
, m_timeout(120)
|
||||||
, m_last_piece(second_clock::universal_time())
|
, m_last_piece(second_clock::universal_time())
|
||||||
, m_packet_size(0)
|
, m_packet_size(0)
|
||||||
@@ -172,6 +173,7 @@ namespace libtorrent
|
|||||||
,
|
,
|
||||||
#endif
|
#endif
|
||||||
m_ses(ses)
|
m_ses(ses)
|
||||||
|
, m_max_out_request_queue(m_ses.m_settings.max_out_request_queue)
|
||||||
, m_timeout(120)
|
, m_timeout(120)
|
||||||
, m_last_piece(second_clock::universal_time())
|
, m_last_piece(second_clock::universal_time())
|
||||||
, m_packet_size(0)
|
, m_packet_size(0)
|
||||||
@@ -792,7 +794,7 @@ namespace libtorrent
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (int(m_requests.size()) > m_ses.m_settings.max_allowed_request_queue)
|
if (int(m_requests.size()) > m_ses.m_settings.max_allowed_in_request_queue)
|
||||||
{
|
{
|
||||||
// don't allow clients to abuse our
|
// don't allow clients to abuse our
|
||||||
// memory consumption.
|
// memory consumption.
|
||||||
@@ -830,7 +832,7 @@ namespace libtorrent
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
m_requests.push_back(r);
|
m_requests.push_back(r);
|
||||||
setup_send();
|
fill_send_buffer();
|
||||||
#ifdef TORRENT_VERBOSE_LOGGING
|
#ifdef TORRENT_VERBOSE_LOGGING
|
||||||
using namespace boost::posix_time;
|
using namespace boost::posix_time;
|
||||||
(*m_logger) << to_simple_string(second_clock::universal_time())
|
(*m_logger) << to_simple_string(second_clock::universal_time())
|
||||||
@@ -1093,7 +1095,6 @@ namespace libtorrent
|
|||||||
|
|
||||||
t->picker().mark_as_downloading(block, m_remote);
|
t->picker().mark_as_downloading(block, m_remote);
|
||||||
m_request_queue.push_back(block);
|
m_request_queue.push_back(block);
|
||||||
send_block_requests();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void peer_connection::cancel_request(piece_block const& block)
|
void peer_connection::cancel_request(piece_block const& block)
|
||||||
@@ -1410,10 +1411,10 @@ namespace libtorrent
|
|||||||
|
|
||||||
m_desired_queue_size = static_cast<int>(queue_time
|
m_desired_queue_size = static_cast<int>(queue_time
|
||||||
* statistics().download_rate() / block_size);
|
* statistics().download_rate() / block_size);
|
||||||
if (m_desired_queue_size > max_request_queue) m_desired_queue_size
|
if (m_desired_queue_size > m_max_out_request_queue)
|
||||||
= max_request_queue;
|
m_desired_queue_size = m_max_out_request_queue;
|
||||||
if (m_desired_queue_size < min_request_queue) m_desired_queue_size
|
if (m_desired_queue_size < min_request_queue)
|
||||||
= min_request_queue;
|
m_desired_queue_size = min_request_queue;
|
||||||
|
|
||||||
if (!m_download_queue.empty()
|
if (!m_download_queue.empty()
|
||||||
&& now - m_last_piece > seconds(m_ses.m_settings.piece_timeout))
|
&& now - m_last_piece > seconds(m_ses.m_settings.piece_timeout))
|
||||||
@@ -1550,8 +1551,6 @@ namespace libtorrent
|
|||||||
{
|
{
|
||||||
INVARIANT_CHECK;
|
INVARIANT_CHECK;
|
||||||
|
|
||||||
if (!can_write()) return;
|
|
||||||
|
|
||||||
boost::shared_ptr<torrent> t = m_torrent.lock();
|
boost::shared_ptr<torrent> t = m_torrent.lock();
|
||||||
if (!t) return;
|
if (!t) return;
|
||||||
|
|
||||||
@@ -1778,8 +1777,7 @@ namespace libtorrent
|
|||||||
|
|
||||||
// if we have requests or pending data to be sent or announcements to be made
|
// if we have requests or pending data to be sent or announcements to be made
|
||||||
// we want to send data
|
// we want to send data
|
||||||
return ((!m_requests.empty() && !m_choked)
|
return (!m_send_buffer[m_current_send_buffer].empty()
|
||||||
|| !m_send_buffer[m_current_send_buffer].empty()
|
|
||||||
|| !m_send_buffer[(m_current_send_buffer + 1) & 1].empty())
|
|| !m_send_buffer[(m_current_send_buffer + 1) & 1].empty())
|
||||||
&& m_ul_bandwidth_quota.left() > 0
|
&& m_ul_bandwidth_quota.left() > 0
|
||||||
&& !m_connecting;
|
&& !m_connecting;
|
||||||
|
@@ -94,7 +94,8 @@ namespace
|
|||||||
// for this peer. If we're downloading one piece in 20 seconds
|
// for this peer. If we're downloading one piece in 20 seconds
|
||||||
// then use this mode.
|
// then use this mode.
|
||||||
// TODO: 20 seconds has to be customizable
|
// TODO: 20 seconds has to be customizable
|
||||||
bool prefer_whole_pieces = c.statistics().download_payload_rate() * 20.f
|
bool prefer_whole_pieces = c.statistics().download_payload_rate()
|
||||||
|
* t.settings().whole_pieces_threshold
|
||||||
> t.torrent_file().piece_length();
|
> t.torrent_file().piece_length();
|
||||||
|
|
||||||
// if we prefer whole pieces, the piece picker will pick at least
|
// if we prefer whole pieces, the piece picker will pick at least
|
||||||
@@ -131,6 +132,8 @@ namespace
|
|||||||
num_requests--;
|
num_requests--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.send_block_requests();
|
||||||
|
|
||||||
// in this case, we could not find any blocks
|
// in this case, we could not find any blocks
|
||||||
// that was free. If we couldn't find any busy
|
// that was free. If we couldn't find any busy
|
||||||
// blocks as well, we cannot download anything
|
// blocks as well, we cannot download anything
|
||||||
@@ -222,6 +225,7 @@ namespace
|
|||||||
|
|
||||||
if (weight <= min_weight) break;
|
if (weight <= min_weight) break;
|
||||||
}
|
}
|
||||||
|
c.send_block_requests();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@@ -372,6 +372,7 @@ namespace libtorrent { namespace detail
|
|||||||
for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
|
for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
|
||||||
= m_processing.begin(); i != m_processing.end(); ++i)
|
= m_processing.begin(); i != m_processing.end(); ++i)
|
||||||
{
|
{
|
||||||
|
|
||||||
if ((*i)->info_hash == info_hash) return i->get();
|
if ((*i)->info_hash == info_hash) return i->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -390,6 +391,17 @@ namespace libtorrent { namespace detail
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (std::deque<boost::shared_ptr<piece_checker_data> >::iterator i
|
||||||
|
= m_processing.begin(); i != m_processing.end(); ++i)
|
||||||
|
{
|
||||||
|
if ((*i)->info_hash == info_hash)
|
||||||
|
{
|
||||||
|
assert((*i)->processing == false);
|
||||||
|
m_torrents.erase(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,6 +851,9 @@ namespace libtorrent { namespace detail
|
|||||||
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
|
for (std::map<sha1_hash, boost::shared_ptr<torrent> >::iterator i
|
||||||
= m_torrents.begin(); i != m_torrents.end(); ++i)
|
= m_torrents.begin(); i != m_torrents.end(); ++i)
|
||||||
{
|
{
|
||||||
|
#ifndef DEBUG
|
||||||
|
i->second->check_invariant();
|
||||||
|
#endif
|
||||||
i->second->distribute_resources();
|
i->second->distribute_resources();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -955,7 +970,7 @@ namespace libtorrent { namespace detail
|
|||||||
boost::shared_ptr<logger> session_impl::create_log(std::string const& name, bool append)
|
boost::shared_ptr<logger> session_impl::create_log(std::string const& name, bool append)
|
||||||
{
|
{
|
||||||
// current options are file_logger, cout_logger and null_logger
|
// current options are file_logger, cout_logger and null_logger
|
||||||
return boost::shared_ptr<logger>(new file_logger(name + ".log", append));
|
return boost::shared_ptr<logger>(new logger(name + ".log", append));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -1322,7 +1322,10 @@ namespace libtorrent
|
|||||||
return torrent_handle(&m_ses, 0, m_torrent_file.info_hash());
|
return torrent_handle(&m_ses, 0, m_torrent_file.info_hash());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
session_settings const& torrent::settings() const
|
||||||
|
{
|
||||||
|
return m_ses.m_settings;
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
void torrent::check_invariant() const
|
void torrent::check_invariant() const
|
||||||
@@ -1599,6 +1602,7 @@ namespace libtorrent
|
|||||||
{
|
{
|
||||||
bencode(std::back_inserter(m_metadata)
|
bencode(std::back_inserter(m_metadata)
|
||||||
, m_torrent_file.create_info_metadata());
|
, m_torrent_file.create_info_metadata());
|
||||||
|
|
||||||
assert(hasher(&m_metadata[0], m_metadata.size()).final()
|
assert(hasher(&m_metadata[0], m_metadata.size()).final()
|
||||||
== m_torrent_file.info_hash());
|
== m_torrent_file.info_hash());
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user