1 diff --git a/src/download/choke_manager.cc b/src/download/choke_manager.cc
2 index 4915a96..3c422c0 100644
3 --- a/src/download/choke_manager.cc
4 +++ b/src/download/choke_manager.cc
5 @@ -129,8 +129,14 @@ ChokeManager::cycle(uint32_t quota) {
6 if (m_unchoked.size() > quota)
7 choke_range(m_unchoked.begin(), m_unchoked.end() - unchoked, m_unchoked.size() - quota);
9 + // In case there were friends who are not to be choked, try again with twice
10 + // as many as remain unchoked (friends who will not be choked again plus as
11 + // many non-friends).
12 if (m_unchoked.size() > quota)
13 - throw internal_error("ChokeManager::cycle() m_unchoked.size() > quota.");
14 + choke_range(m_unchoked.begin(), m_unchoked.end() - unchoked, std::min((m_unchoked.size() - quota) * 2, m_unchoked.size()));
16 + // If there were friends in the n+1..2n range, we still have too many unchoked here.
17 + // FIXME: Change choke_range to take the number of peers to unchoke, instead of a fixed range.
19 return m_unchoked.size() - oldSize;
21 @@ -145,10 +151,11 @@ ChokeManager::set_queued(PeerConnectionBase* pc, ChokeManagerNode* base) {
25 - if (!is_full() && (m_flags & flag_unchoke_all_new || m_slotCanUnchoke()) &&
26 - base->time_last_choke() + rak::timer::from_seconds(10) < cachedTime) {
27 + if (pc->peer_info()->is_friend() ||
28 + (!is_full() && (m_flags & flag_unchoke_all_new || m_slotCanUnchoke()) &&
29 + base->time_last_choke() + rak::timer::from_seconds(10) < cachedTime)) {
30 m_unchoked.push_back(value_type(pc, 0));
31 - m_slotConnection(pc, false);
32 + m_slotConnection(pc, 0);
36 @@ -169,7 +176,7 @@ ChokeManager::set_not_queued(PeerConnectionBase* pc, ChokeManagerNode* base) {
38 if (base->unchoked()) {
39 choke_manager_erase(&m_unchoked, pc);
40 - m_slotConnection(pc, true);
41 + m_slotConnection(pc, flag_connection_choke);
45 @@ -186,7 +193,7 @@ ChokeManager::set_snubbed(PeerConnectionBase* pc, ChokeManagerNode* base) {
47 if (base->unchoked()) {
48 choke_manager_erase(&m_unchoked, pc);
49 - m_slotConnection(pc, true);
50 + m_slotConnection(pc, flag_connection_choke);
53 } else if (base->queued()) {
54 @@ -212,7 +219,7 @@ ChokeManager::set_not_snubbed(PeerConnectionBase* pc, ChokeManagerNode* base) {
55 if (!is_full() && (m_flags & flag_unchoke_all_new || m_slotCanUnchoke()) &&
56 base->time_last_choke() + rak::timer::from_seconds(10) < cachedTime) {
57 m_unchoked.push_back(value_type(pc, 0));
58 - m_slotConnection(pc, false);
59 + m_slotConnection(pc, 0);
63 @@ -346,20 +353,20 @@ ChokeManager::choke_range(iterator first, iterator last, uint32_t max) {
64 (itr - 1)->second > m_unchoked.end())
65 throw internal_error("ChokeManager::choke_range(...) bad iterator range.");
67 - count += (itr - 1)->first;
69 // We move the connections that return true, while the ones that
70 - // return false get thrown out. The function called must update
71 - // ChunkManager::m_queued if false is returned.
72 + // return false stay unchoked.
74 // The C++ standard says std::partition will call the predicate
75 // max 'last - first' times, so we can assume it gets called once
77 iterator split = std::partition(itr->second - (itr - 1)->first, itr->second,
78 - rak::on(rak::mem_ref(&value_type::first), std::bind2nd(m_slotConnection, true)));
79 + rak::on(rak::mem_ref(&value_type::first),
80 + std::bind2nd(m_slotConnection, flag_connection_choke | flag_connection_may_ignore)));
82 m_queued.insert(m_queued.end(), itr->second - (itr - 1)->first, split);
83 - m_unchoked.erase(itr->second - (itr - 1)->first, itr->second);
84 + m_unchoked.erase(itr->second - (itr - 1)->first, split);
86 + count += split - (itr->second - (itr - 1)->first);
90 @@ -392,7 +399,7 @@ ChokeManager::unchoke_range(iterator first, iterator last, uint32_t max) {
91 count += (itr - 1)->first;
93 std::for_each(itr->second - (itr - 1)->first, itr->second,
94 - rak::on(rak::mem_ref(&value_type::first), std::bind2nd(m_slotConnection, false)));
95 + rak::on(rak::mem_ref(&value_type::first), std::bind2nd(m_slotConnection, 0)));
97 m_unchoked.insert(m_unchoked.end(), itr->second - (itr - 1)->first, itr->second);
98 m_queued.erase(itr->second - (itr - 1)->first, itr->second);
99 diff --git a/src/download/choke_manager.h b/src/download/choke_manager.h
100 index d9ab886..3a7ec67 100644
101 --- a/src/download/choke_manager.h
102 +++ b/src/download/choke_manager.h
103 @@ -53,7 +53,7 @@ class ChokeManager {
105 typedef rak::mem_fun1<ResourceManager, void, int> slot_unchoke;
106 typedef rak::mem_fun0<ResourceManager, unsigned int> slot_can_unchoke;
107 - typedef std::mem_fun1_t<bool, PeerConnectionBase, bool> slot_connection;
108 + typedef std::mem_fun1_t<bool, PeerConnectionBase, int> slot_connection;
110 typedef std::vector<std::pair<PeerConnectionBase*, uint32_t> > container_type;
111 typedef container_type::value_type value_type;
112 @@ -65,6 +65,9 @@ public:
114 static const int flag_unchoke_all_new = 0x1;
116 + static const int flag_connection_choke = 0x1;
117 + static const int flag_connection_may_ignore = 0x2;
119 static const uint32_t order_base = (1 << 30);
120 static const uint32_t order_max_size = 4;
121 static const uint32_t weight_size_bytes = order_max_size * sizeof(uint32_t);
122 diff --git a/src/protocol/handshake.cc b/src/protocol/handshake.cc
123 index 7fb389b..ea3eee8 100644
124 --- a/src/protocol/handshake.cc
125 +++ b/src/protocol/handshake.cc
126 @@ -840,7 +840,7 @@ Handshake::validate_download() {
127 throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_unknown_download);
128 if (!m_download->info()->is_active())
129 throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_inactive_download);
130 - if (!m_download->info()->is_accepting_new_peers())
131 + if (!m_download->info()->is_accepting_new_peers() && m_peerInfo != NULL && !m_peerInfo->is_friend())
132 throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_not_accepting_connections);
135 @@ -1024,6 +1024,9 @@ Handshake::prepare_peer_info() {
136 if (m_peerInfo == NULL)
137 throw handshake_error(ConnectionManager::handshake_failed, e_handshake_network_error);
139 + if (!m_download->info()->is_accepting_new_peers() && !m_peerInfo->is_friend())
140 + throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_not_accepting_connections);
142 if (m_peerInfo->failed_counter() > m_manager->max_failed)
143 throw handshake_error(ConnectionManager::handshake_dropped, e_handshake_toomanyfailed);
145 diff --git a/src/protocol/handshake_manager.cc b/src/protocol/handshake_manager.cc
146 index e06f594..2dea2e5 100644
147 --- a/src/protocol/handshake_manager.cc
148 +++ b/src/protocol/handshake_manager.cc
149 @@ -108,7 +108,7 @@ HandshakeManager::erase_download(DownloadMain* info) {
151 HandshakeManager::add_incoming(SocketFd fd, const rak::socket_address& sa) {
152 if (!manager->connection_manager()->can_connect() ||
153 - !manager->connection_manager()->filter(sa.c_sockaddr()) ||
154 + manager->connection_manager()->filter(sa.c_sockaddr()) == ConnectionManager::filter_blocked ||
158 @@ -126,7 +126,7 @@ HandshakeManager::add_incoming(SocketFd fd, const rak::socket_address& sa) {
160 HandshakeManager::add_outgoing(const rak::socket_address& sa, DownloadMain* download) {
161 if (!manager->connection_manager()->can_connect() ||
162 - !manager->connection_manager()->filter(sa.c_sockaddr()))
163 + manager->connection_manager()->filter(sa.c_sockaddr()) == ConnectionManager::filter_blocked)
166 create_outgoing(sa, download, manager->connection_manager()->encryption_options());
167 diff --git a/src/protocol/peer_connection_base.cc b/src/protocol/peer_connection_base.cc
168 index 815ea93..9eddbb8 100644
169 --- a/src/protocol/peer_connection_base.cc
170 +++ b/src/protocol/peer_connection_base.cc
171 @@ -203,10 +203,14 @@ PeerConnectionBase::set_upload_snubbed(bool v) {
175 -PeerConnectionBase::receive_upload_choke(bool choke) {
176 +PeerConnectionBase::receive_upload_choke(int flags) {
177 + bool choke = flags & ChokeManager::flag_connection_choke;
178 if (choke == m_upChoke.choked())
179 throw internal_error("PeerConnectionBase::receive_upload_choke(...) already set to the same state.");
181 + if (choke && m_peerInfo->is_friend() && (flags & ChokeManager::flag_connection_may_ignore))
184 write_insert_poll_safe();
187 @@ -217,7 +221,8 @@ PeerConnectionBase::receive_upload_choke(bool choke) {
191 -PeerConnectionBase::receive_download_choke(bool choke) {
192 +PeerConnectionBase::receive_download_choke(int flags) {
193 + bool choke = flags & ChokeManager::flag_connection_choke;
194 if (choke == m_downChoke.choked())
195 throw internal_error("PeerConnectionBase::receive_download_choke(...) already set to the same state.");
197 @@ -427,7 +432,7 @@ PeerConnectionBase::down_chunk() {
198 transfer->adjust_position(bytesTransfered);
200 m_down->throttle()->node_used(m_peerChunks.download_throttle(), bytesTransfered);
201 - m_download->info()->down_rate()->insert(bytesTransfered);
202 + m_download->info()->down_rate()->insert(bytesTransfered, m_peerInfo->is_friend());
204 return transfer->is_finished();
206 @@ -497,7 +502,7 @@ PeerConnectionBase::down_chunk_process(const void* buffer, uint32_t length) {
207 transfer->adjust_position(length);
209 m_down->throttle()->node_used(m_peerChunks.download_throttle(), length);
210 - m_download->info()->down_rate()->insert(length);
211 + m_download->info()->down_rate()->insert(length, m_peerInfo->is_friend());
215 @@ -516,8 +521,8 @@ PeerConnectionBase::down_chunk_skip_process(const void* buffer, uint32_t length)
216 // Hmm, this might result in more bytes than nessesary being
218 m_down->throttle()->node_used(m_peerChunks.download_throttle(), length);
219 - m_download->info()->down_rate()->insert(length);
220 - m_download->info()->skip_rate()->insert(length);
221 + m_download->info()->down_rate()->insert(length, m_peerInfo->is_friend());
222 + m_download->info()->skip_rate()->insert(length, m_peerInfo->is_friend());
224 if (!transfer->is_valid()) {
225 transfer->adjust_position(length);
226 @@ -662,7 +667,7 @@ PeerConnectionBase::up_chunk() {
229 m_up->throttle()->node_used(m_peerChunks.upload_throttle(), bytesTransfered);
230 - m_download->info()->up_rate()->insert(bytesTransfered);
231 + m_download->info()->up_rate()->insert(bytesTransfered, m_peerInfo->is_friend());
233 // Just modifying the piece to cover the remaining data ends up
234 // being much cleaner and we avoid an unnessesary position variable.
235 diff --git a/src/protocol/peer_connection_base.h b/src/protocol/peer_connection_base.h
236 index d131341..dc0dc41 100644
237 --- a/src/protocol/peer_connection_base.h
238 +++ b/src/protocol/peer_connection_base.h
239 @@ -127,8 +127,8 @@ public:
240 virtual void update_interested() = 0;
241 virtual bool receive_keepalive() = 0;
243 - bool receive_upload_choke(bool choke);
244 - bool receive_download_choke(bool choke);
245 + bool receive_upload_choke(int flags);
246 + bool receive_download_choke(int flags);
248 virtual void event_error();
250 diff --git a/src/protocol/peer_connection_leech.cc b/src/protocol/peer_connection_leech.cc
251 index 36c6d7a..7918dff 100644
252 --- a/src/protocol/peer_connection_leech.cc
253 +++ b/src/protocol/peer_connection_leech.cc
254 @@ -516,6 +516,7 @@ PeerConnection<type>::fill_write_buffer() {
255 if (type == Download::CONNECTION_LEECH && m_tryRequest) {
256 if (!(m_tryRequest = !should_request()) &&
257 !(m_tryRequest = try_request_pieces()) &&
258 + !m_peerInfo->is_friend() &&
260 !download_queue()->is_interested_in_active()) {
261 m_sendInterested = true;
262 diff --git a/src/torrent/connection_manager.cc b/src/torrent/connection_manager.cc
263 index 152a0a1..efc919b 100644
264 --- a/src/torrent/connection_manager.cc
265 +++ b/src/torrent/connection_manager.cc
266 @@ -163,7 +163,7 @@ ConnectionManager::set_proxy_address(const sockaddr* sa) {
268 ConnectionManager::filter(const sockaddr* sa) {
269 if (m_slotFilter.empty())
271 + return filter_permitted;
273 return m_slotFilter(sa);
275 diff --git a/src/torrent/connection_manager.h b/src/torrent/connection_manager.h
276 index 07ac736..c5e81c0 100644
277 --- a/src/torrent/connection_manager.h
278 +++ b/src/torrent/connection_manager.h
279 @@ -84,6 +84,10 @@ public:
280 static const uint32_t encryption_enable_retry = (1 << 4);
281 static const uint32_t encryption_prefer_plaintext = (1 << 5);
283 + static const uint32_t filter_blocked = 0;
284 + static const uint32_t filter_permitted = (1 << 0);
285 + static const uint32_t filter_friend = (1 << 1);
287 // Internal to libtorrent.
288 static const uint32_t encryption_use_proxy = (1 << 6);
290 diff --git a/src/torrent/peer/connection_list.cc b/src/torrent/peer/connection_list.cc
291 index e50606e..fa9e912 100644
292 --- a/src/torrent/peer/connection_list.cc
293 +++ b/src/torrent/peer/connection_list.cc
294 @@ -70,7 +70,7 @@ ConnectionList::clear() {
297 ConnectionList::insert(PeerInfo* peerInfo, const SocketFd& fd, Bitfield* bitfield, EncryptionInfo* encryptionInfo, ProtocolExtension* extensions) {
298 - if (size() >= m_maxSize)
299 + if (size() >= m_maxSize && !peerInfo->is_friend())
302 PeerConnectionBase* peerConnection = m_slotNewConnection(encryptionInfo->is_encrypted());
303 diff --git a/src/torrent/peer/peer.h b/src/torrent/peer/peer.h
304 index 70169ad..13e2880 100644
305 --- a/src/torrent/peer/peer.h
306 +++ b/src/torrent/peer/peer.h
307 @@ -71,6 +71,8 @@ public:
308 void set_snubbed(bool v);
311 + bool is_friend() const { return peer_info()->is_friend(); }
313 const HashString& id() const { return peer_info()->id(); }
314 const char* options() const { return peer_info()->options(); }
315 const sockaddr* address() const { return peer_info()->socket_address(); }
316 diff --git a/src/torrent/peer/peer_info.h b/src/torrent/peer/peer_info.h
317 index fe80027..8f56f73 100644
318 --- a/src/torrent/peer/peer_info.h
319 +++ b/src/torrent/peer/peer_info.h
321 #include <torrent/hash_string.h>
322 #include <torrent/peer/client_info.h>
324 +// For conditional compilation depending on whether this patch was applied.
325 +// Remove for release.
326 +#define LIBTORRENT_FRIENDS 1
330 class LIBTORRENT_EXPORT PeerInfo {
331 @@ -56,8 +60,11 @@ public:
332 static const int flag_handshake = (1 << 2);
333 static const int flag_blocked = (1 << 3); // For initial seeding.
334 static const int flag_restart = (1 << 4);
335 + static const int flag_filtered = (1 << 5);
336 + static const int flag_friend = (1 << 6);
338 - PeerInfo(const sockaddr* address);
339 + // Constructor is private. Use PeerList::insert_address or
340 + // PeerList::create_peer_info instead.
343 bool is_connected() const { return m_flags & flag_connected; }
344 @@ -65,6 +72,8 @@ public:
345 bool is_handshake() const { return m_flags & flag_handshake; }
346 bool is_blocked() const { return m_flags & flag_blocked; }
347 bool is_restart() const { return m_flags & flag_restart; }
348 + bool is_filtered() const { return m_flags & flag_filtered; }
349 + bool is_friend() const { return m_flags & flag_friend; }
351 int flags() const { return m_flags; }
353 @@ -108,6 +117,7 @@ protected:
354 void set_connection(PeerConnectionBase* c) { m_connection = c; }
357 + PeerInfo(const sockaddr* address);
358 PeerInfo(const PeerInfo&);
359 void operator = (const PeerInfo&);
361 diff --git a/src/torrent/peer/peer_list.cc b/src/torrent/peer/peer_list.cc
362 index cef840b..7164b91 100644
363 --- a/src/torrent/peer/peer_list.cc
364 +++ b/src/torrent/peer/peer_list.cc
366 #include <rak/socket_address.h>
368 #include "download/available_list.h"
369 +#include "torrent/connection_manager.h"
370 #include "torrent/peer/client_list.h"
371 +#include "torrent.h"
373 #include "exceptions.h"
375 @@ -102,6 +104,20 @@ PeerList::~PeerList() {
379 +PeerList::create_peer_info(const sockaddr* sa) {
380 + PeerInfo* peerInfo = new PeerInfo(sa);
381 + uint32_t filter = connection_manager()->filter(sa);
383 + if (filter == ConnectionManager::filter_blocked)
384 + peerInfo->set_flags(PeerInfo::flag_filtered);
386 + else if (filter & ConnectionManager::filter_friend)
387 + peerInfo->set_flags(PeerInfo::flag_friend);
393 PeerList::insert_address(const sockaddr* sa, int flags) {
394 if (!socket_address_key::is_comparable(sa))
396 @@ -118,7 +134,7 @@ PeerList::insert_address(const sockaddr* sa, int flags) {
398 const rak::socket_address* address = rak::socket_address::cast_from(sa);
400 - PeerInfo* peerInfo = new PeerInfo(sa);
401 + PeerInfo* peerInfo = create_peer_info(sa);
402 peerInfo->set_listen_port(address->port());
404 manager->client_list()->retrieve_unknown(&peerInfo->mutable_client_info());
405 @@ -216,7 +232,7 @@ PeerList::connected(const sockaddr* sa, int flags) {
407 if (range.first == range.second) {
408 // Create a new entry.
409 - peerInfo = new PeerInfo(sa);
410 + peerInfo = create_peer_info(sa);
412 base_type::insert(range.second, value_type(socket_address_key(peerInfo->socket_address()), peerInfo));
414 diff --git a/src/torrent/peer/peer_list.h b/src/torrent/peer/peer_list.h
415 index fece106..2ee5e1e 100644
416 --- a/src/torrent/peer/peer_list.h
417 +++ b/src/torrent/peer/peer_list.h
418 @@ -111,6 +111,8 @@ public:
419 const_reverse_iterator rbegin() const { return base_type::rbegin(); }
420 const_reverse_iterator rend() const { return base_type::rend(); }
422 + static PeerInfo* create_peer_info(const sockaddr* sa);
425 // Insert, or find a PeerInfo with socket address 'sa'. Returns end
426 // if no more connections are allowed from that host.
427 diff --git a/src/torrent/rate.cc b/src/torrent/rate.cc
428 index c28d718..586350d 100644
429 --- a/src/torrent/rate.cc
430 +++ b/src/torrent/rate.cc
431 @@ -58,7 +58,7 @@ Rate::rate() const {
435 -Rate::insert(rate_type bytes) {
436 +Rate::insert(rate_type bytes, bool rate_only) {
439 if (m_current > ((rate_type)1 << 40) || bytes > ((rate_type)1 << 28))
440 @@ -69,7 +69,7 @@ Rate::insert(rate_type bytes) {
442 m_container.front().second += bytes;
445 + m_total += rate_only ? 0 : bytes;
449 diff --git a/src/torrent/rate.h b/src/torrent/rate.h
450 index 239e4b6..4c48aea 100644
451 --- a/src/torrent/rate.h
452 +++ b/src/torrent/rate.h
453 @@ -68,7 +68,7 @@ public:
454 timer_type span() const { return m_span; }
455 void set_span(timer_type s) { m_span = s; }
457 - void insert(rate_type bytes);
458 + void insert(rate_type bytes, bool rate_only = false);
460 void reset_rate() { m_current = 0; m_container.clear(); }