~ubuntu-branches/ubuntu/oneiric/libtorrent/oneiric

« back to all changes in this revision

Viewing changes to src/download/download_main.cc

  • Committer: Bazaar Package Importer
  • Author(s): Rogério Brito
  • Date: 2011-03-20 01:06:18 UTC
  • mfrom: (1.1.13 upstream) (4.1.9 sid)
  • Revision ID: james.westby@ubuntu.com-20110320010618-g3wyylccqzqko73c
Tags: 0.12.7-5
* Use Steinar's "real" patch for IPv6. Addresses #490277, #618275,
  and Closes: #617791.
* Adapt libtorrent-0.12.6-ipv6-07.patch. It FTBFS otherwise.
* Add proper attibution to the IPv6 patch.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// libTorrent - BitTorrent library
 
2
// Copyright (C) 2005-2007, Jari Sundell
 
3
//
 
4
// This program is free software; you can redistribute it and/or modify
 
5
// it under the terms of the GNU General Public License as published by
 
6
// the Free Software Foundation; either version 2 of the License, or
 
7
// (at your option) any later version.
 
8
// 
 
9
// This program is distributed in the hope that it will be useful,
 
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
// GNU General Public License for more details.
 
13
// 
 
14
// You should have received a copy of the GNU General Public License
 
15
// along with this program; if not, write to the Free Software
 
16
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
17
//
 
18
// In addition, as a special exception, the copyright holders give
 
19
// permission to link the code of portions of this program with the
 
20
// OpenSSL library under certain conditions as described in each
 
21
// individual source file, and distribute linked combinations
 
22
// including the two.
 
23
//
 
24
// You must obey the GNU General Public License in all respects for
 
25
// all of the code used other than OpenSSL.  If you modify file(s)
 
26
// with this exception, you may extend this exception to your version
 
27
// of the file(s), but you are not obligated to do so.  If you do not
 
28
// wish to do so, delete this exception statement from your version.
 
29
// If you delete this exception statement from all source files in the
 
30
// program, then also delete it here.
 
31
//
 
32
// Contact:  Jari Sundell <jaris@ifi.uio.no>
 
33
//
 
34
//           Skomakerveien 33
 
35
//           3185 Skoppum, NORWAY
 
36
 
 
37
#include "config.h"
 
38
 
 
39
#include <cstring>
 
40
#include <limits>
 
41
 
 
42
#include "data/chunk_list.h"
 
43
#include "protocol/extensions.h"
 
44
#include "protocol/handshake_manager.h"
 
45
#include "protocol/initial_seed.h"
 
46
#include "protocol/peer_connection_base.h"
 
47
#include "protocol/peer_factory.h"
 
48
#include "tracker/tracker_manager.h"
 
49
#include "torrent/download.h"
 
50
#include "torrent/exceptions.h"
 
51
#include "torrent/throttle.h"
 
52
#include "torrent/data/file_list.h"
 
53
#include "torrent/peer/connection_list.h"
 
54
#include "torrent/peer/peer.h"
 
55
#include "torrent/peer/peer_info.h"
 
56
 
 
57
#include "available_list.h"
 
58
#include "choke_manager.h"
 
59
#include "chunk_selector.h"
 
60
#include "chunk_statistics.h"
 
61
#include "download_main.h"
 
62
#include "download_manager.h"
 
63
#include "download_wrapper.h"
 
64
 
 
65
namespace torrent {
 
66
 
 
67
DownloadInfo::DownloadInfo() :
 
68
  m_flags(flag_compact | flag_accepting_new_peers | flag_pex_enabled | flag_pex_active),
 
69
 
 
70
  m_upRate(60),
 
71
  m_downRate(60),
 
72
  m_skipRate(60),
 
73
  
 
74
  m_uploadedBaseline(0),
 
75
  m_completedBaseline(0),
 
76
  m_sizePex(0),
 
77
  m_maxSizePex(8),
 
78
  m_metadataSize(0),
 
79
  
 
80
  m_creationDate(0),
 
81
  m_loadDate(rak::timer::current_seconds()) {
 
82
}
 
83
 
 
84
DownloadMain::DownloadMain() :
 
85
  m_info(new DownloadInfo),
 
86
 
 
87
  m_trackerManager(new TrackerManager()),
 
88
  m_chunkList(new ChunkList),
 
89
  m_chunkSelector(new ChunkSelector),
 
90
  m_chunkStatistics(new ChunkStatistics),
 
91
 
 
92
  m_initialSeeding(NULL),
 
93
  m_uploadThrottle(NULL),
 
94
  m_downloadThrottle(NULL) {
 
95
 
 
96
  m_connectionList       = new ConnectionList(this);
 
97
  m_uploadChokeManager   = new ChokeManager(m_connectionList);
 
98
  m_downloadChokeManager = new ChokeManager(m_connectionList, ChokeManager::flag_unchoke_all_new);
 
99
 
 
100
  m_uploadChokeManager->set_slot_choke_weight(&calculate_upload_choke);
 
101
  m_uploadChokeManager->set_slot_unchoke_weight(&calculate_upload_unchoke);
 
102
  m_uploadChokeManager->set_slot_connection(std::mem_fun(&PeerConnectionBase::receive_upload_choke));
 
103
 
 
104
  std::memcpy(m_uploadChokeManager->choke_weight(),   weights_upload_choke,   ChokeManager::weight_size_bytes);
 
105
  std::memcpy(m_uploadChokeManager->unchoke_weight(), weights_upload_unchoke, ChokeManager::weight_size_bytes);
 
106
 
 
107
  m_downloadChokeManager->set_slot_choke_weight(&calculate_download_choke);
 
108
  m_downloadChokeManager->set_slot_unchoke_weight(&calculate_download_unchoke);
 
109
  m_downloadChokeManager->set_slot_connection(std::mem_fun(&PeerConnectionBase::receive_download_choke));
 
110
 
 
111
  std::memcpy(m_downloadChokeManager->choke_weight(),   weights_download_choke,   ChokeManager::weight_size_bytes);
 
112
  std::memcpy(m_downloadChokeManager->unchoke_weight(), weights_download_unchoke, ChokeManager::weight_size_bytes);
 
113
 
 
114
  m_delegator.slot_chunk_find(rak::make_mem_fun(m_chunkSelector, &ChunkSelector::find));
 
115
  m_delegator.slot_chunk_size(rak::make_mem_fun(file_list(), &FileList::chunk_index_size));
 
116
 
 
117
  m_delegator.transfer_list()->slot_canceled(std::bind1st(std::mem_fun(&ChunkSelector::not_using_index), m_chunkSelector));
 
118
  m_delegator.transfer_list()->slot_queued(std::bind1st(std::mem_fun(&ChunkSelector::using_index), m_chunkSelector));
 
119
  m_delegator.transfer_list()->slot_completed(std::bind1st(std::mem_fun(&DownloadMain::receive_chunk_done), this));
 
120
  m_delegator.transfer_list()->slot_corrupt(std::bind1st(std::mem_fun(&DownloadMain::receive_corrupt_chunk), this));
 
121
 
 
122
  m_delayDisconnectPeers.set_slot(rak::mem_fn(m_connectionList, &ConnectionList::disconnect_queued));
 
123
  m_taskTrackerRequest.set_slot(rak::mem_fn(this, &DownloadMain::receive_tracker_request));
 
124
 
 
125
  m_chunkList->slot_create_chunk(rak::make_mem_fun(file_list(), &FileList::create_chunk_index));
 
126
  m_chunkList->slot_free_diskspace(rak::make_mem_fun(file_list(), &FileList::free_diskspace));
 
127
}
 
128
 
 
129
DownloadMain::~DownloadMain() {
 
130
  if (m_taskTrackerRequest.is_queued())
 
131
    throw internal_error("DownloadMain::~DownloadMain(): m_taskTrackerRequest is queued.");
 
132
 
 
133
  // Check if needed.
 
134
  m_connectionList->clear();
 
135
 
 
136
  if (m_info->size_pex() != 0)
 
137
    throw internal_error("DownloadMain::~DownloadMain(): m_info->size_pex() != 0.");
 
138
 
 
139
  delete m_trackerManager;
 
140
  delete m_uploadChokeManager;
 
141
  delete m_downloadChokeManager;
 
142
  delete m_connectionList;
 
143
 
 
144
  delete m_chunkStatistics;
 
145
  delete m_chunkList;
 
146
  delete m_chunkSelector;
 
147
  delete m_info;
 
148
 
 
149
  m_ut_pex_delta.clear();
 
150
  m_ut_pex_initial.clear();
 
151
}
 
152
 
 
153
std::pair<ThrottleList*, ThrottleList*>
 
154
DownloadMain::throttles(const sockaddr* sa) {
 
155
  ThrottlePair pair = ThrottlePair(NULL, NULL);
 
156
 
 
157
  if (!manager->connection_manager()->address_throttle().empty())
 
158
    pair = manager->connection_manager()->address_throttle()(sa);
 
159
 
 
160
  return std::make_pair(pair.first == NULL ? upload_throttle() : pair.first->throttle_list(),
 
161
                        pair.second == NULL ? download_throttle() : pair.second->throttle_list());
 
162
}
 
163
 
 
164
void
 
165
DownloadMain::open(int flags) {
 
166
  if (info()->is_open())
 
167
    throw internal_error("Tried to open a download that is already open");
 
168
 
 
169
  file_list()->open(flags & FileList::open_no_create);
 
170
 
 
171
  m_chunkList->resize(file_list()->size_chunks());
 
172
  m_chunkStatistics->initialize(file_list()->size_chunks());
 
173
 
 
174
  info()->set_flags(DownloadInfo::flag_open);
 
175
}
 
176
 
 
177
void
 
178
DownloadMain::close() {
 
179
  if (info()->is_active())
 
180
    throw internal_error("Tried to close an active download");
 
181
 
 
182
  if (!info()->is_open())
 
183
    return;
 
184
 
 
185
  info()->unset_flags(DownloadInfo::flag_open);
 
186
 
 
187
  // Don't close the tracker manager here else it will cause STOPPED
 
188
  // requests to be lost. TODO: Check that this is valid.
 
189
//   m_trackerManager->close();
 
190
 
 
191
  m_delegator.transfer_list()->clear();
 
192
 
 
193
  file_list()->mutable_bitfield()->unallocate();
 
194
  file_list()->close();
 
195
 
 
196
  // Clear the chunklist last as it requires all referenced chunks to
 
197
  // be released.
 
198
  m_chunkStatistics->clear();
 
199
  m_chunkList->clear();
 
200
  m_chunkSelector->cleanup();
 
201
}
 
202
 
 
203
void DownloadMain::start() {
 
204
  if (!info()->is_open())
 
205
    throw internal_error("Tried to start a closed download");
 
206
 
 
207
  if (info()->is_active())
 
208
    throw internal_error("Tried to start an active download");
 
209
 
 
210
  info()->set_flags(DownloadInfo::flag_active);
 
211
  m_lastConnectedSize = 0;
 
212
 
 
213
  m_delegator.set_aggressive(false);
 
214
  update_endgame();  
 
215
 
 
216
  receive_connect_peers();
 
217
}  
 
218
 
 
219
void
 
220
DownloadMain::stop() {
 
221
  if (!info()->is_active())
 
222
    return;
 
223
 
 
224
  // Set this early so functions like receive_connect_peers() knows
 
225
  // not to eat available peers.
 
226
  info()->unset_flags(DownloadInfo::flag_active);
 
227
 
 
228
  m_slotStopHandshakes(this);
 
229
  connection_list()->erase_remaining(connection_list()->begin(), ConnectionList::disconnect_available);
 
230
 
 
231
  delete m_initialSeeding;
 
232
  m_initialSeeding = NULL;
 
233
 
 
234
  priority_queue_erase(&taskScheduler, &m_delayDisconnectPeers);
 
235
  priority_queue_erase(&taskScheduler, &m_taskTrackerRequest);
 
236
}
 
237
 
 
238
bool
 
239
DownloadMain::start_initial_seeding() {
 
240
  if (!file_list()->is_done())
 
241
    return false;
 
242
 
 
243
  m_initialSeeding = new InitialSeeding(this);
 
244
  return true;
 
245
}
 
246
 
 
247
void
 
248
DownloadMain::initial_seeding_done(PeerConnectionBase* pcb) {
 
249
  if (m_initialSeeding == NULL)
 
250
    throw internal_error("DownloadMain::initial_seeding_done called when not initial seeding.");
 
251
 
 
252
  // Close all connections but the currently active one (pcb).
 
253
  // That one will be closed by throw close_connection() later.
 
254
  if (m_connectionList->size() > 1) {
 
255
    ConnectionList::iterator itr = std::find(m_connectionList->begin(), m_connectionList->end(), pcb);
 
256
    if (itr == m_connectionList->end())
 
257
      throw internal_error("DownloadMain::initial_seeding_done could not find current connection.");
 
258
 
 
259
    std::iter_swap(m_connectionList->begin(), itr);
 
260
    m_connectionList->erase_remaining(m_connectionList->begin() + 1, ConnectionList::disconnect_available);
 
261
  }
 
262
 
 
263
  // Switch to normal seeding.
 
264
  DownloadManager::iterator itr = manager->download_manager()->find(m_info);
 
265
  (*itr)->set_connection_type(Download::CONNECTION_SEED);
 
266
  m_connectionList->slot_new_connection(&createPeerConnectionSeed);
 
267
  delete m_initialSeeding;
 
268
  m_initialSeeding = NULL;
 
269
 
 
270
  // And close the current connection.
 
271
  throw close_connection();
 
272
}
 
273
 
 
274
void
 
275
DownloadMain::update_endgame() {
 
276
  if (!m_delegator.get_aggressive() &&
 
277
      file_list()->completed_chunks() + m_delegator.transfer_list()->size() + 5 >= file_list()->size_chunks())
 
278
    m_delegator.set_aggressive(true);
 
279
}
 
280
 
 
281
void
 
282
DownloadMain::receive_chunk_done(unsigned int index) {
 
283
  ChunkHandle handle = m_chunkList->get(index, false);
 
284
 
 
285
  if (!handle.is_valid())
 
286
    throw storage_error("DownloadState::chunk_done(...) called with an index we couldn't retrieve from storage");
 
287
 
 
288
  m_slotHashCheckAdd(handle);
 
289
}
 
290
 
 
291
void
 
292
DownloadMain::receive_corrupt_chunk(PeerInfo* peerInfo) {
 
293
  peerInfo->set_failed_counter(peerInfo->failed_counter() + 1);
 
294
 
 
295
  // Just use some very primitive heuristics here to decide if we're
 
296
  // going to disconnect the peer. Also, consider adding a flag so we
 
297
  // don't recalculate these things whenever the peer reconnects.
 
298
 
 
299
  // That is... non at all ;)
 
300
 
 
301
  if (peerInfo->failed_counter() > HandshakeManager::max_failed)
 
302
    connection_list()->erase(peerInfo, ConnectionList::disconnect_unwanted);
 
303
}
 
304
 
 
305
void
 
306
DownloadMain::add_peer(const rak::socket_address& sa) {
 
307
  m_slotStartHandshake(sa, this);
 
308
}
 
309
 
 
310
void
 
311
DownloadMain::receive_connect_peers() {
 
312
  if (!info()->is_active())
 
313
    return;
 
314
 
 
315
  // TODO: Is this actually going to be used?
 
316
  AddressList* alist = peer_list()->available_list()->buffer();
 
317
 
 
318
  if (!alist->empty()) {
 
319
    alist->sort();
 
320
    peer_list()->insert_available(alist);
 
321
    alist->clear();
 
322
  }
 
323
 
 
324
  while (!peer_list()->available_list()->empty() &&
 
325
         manager->connection_manager()->can_connect() &&
 
326
         connection_list()->size() < connection_list()->min_size() &&
 
327
         connection_list()->size() + m_slotCountHandshakes(this) < connection_list()->max_size()) {
 
328
    rak::socket_address sa = peer_list()->available_list()->pop_random();
 
329
 
 
330
    if (connection_list()->find(sa.c_sockaddr()) == connection_list()->end())
 
331
      m_slotStartHandshake(sa, this);
 
332
  }
 
333
}
 
334
 
 
335
void
 
336
DownloadMain::receive_tracker_success() {
 
337
  if (!info()->is_active())
 
338
    return;
 
339
 
 
340
  priority_queue_erase(&taskScheduler, &m_taskTrackerRequest);
 
341
  priority_queue_insert(&taskScheduler, &m_taskTrackerRequest, (cachedTime + rak::timer::from_seconds(30)).round_seconds());
 
342
}
 
343
 
 
344
void
 
345
DownloadMain::receive_tracker_request() {
 
346
  if (connection_list()->size() >= connection_list()->min_size())
 
347
    return;
 
348
 
 
349
  if (m_info->is_pex_enabled() || connection_list()->size() < m_lastConnectedSize + 10)
 
350
    m_trackerManager->request_next();
 
351
  else if (!m_trackerManager->request_current())
 
352
    m_trackerManager->request_next();
 
353
 
 
354
  m_lastConnectedSize = connection_list()->size();
 
355
}
 
356
 
 
357
struct SocketAddressCompact_less {
 
358
  bool operator () (const SocketAddressCompact& a, const SocketAddressCompact& b) const {
 
359
    return (a.addr < b.addr) || ((a.addr == b.addr) && (a.port < b.port));
 
360
  }
 
361
};
 
362
 
 
363
void
 
364
DownloadMain::do_peer_exchange() {
 
365
  if (!info()->is_active())
 
366
    throw internal_error("DownloadMain::do_peer_exchange called on inactive download.");
 
367
 
 
368
  // Check whether we should tell the peers to stop/start sending PEX
 
369
  // messages.
 
370
  int togglePex = 0;
 
371
 
 
372
  if (!m_info->is_pex_active() &&
 
373
      m_connectionList->size() < m_connectionList->min_size() / 2 &&
 
374
      m_peerList.available_list()->size() < m_peerList.available_list()->max_size() / 4) {
 
375
    m_info->set_flags(DownloadInfo::flag_pex_active);
 
376
 
 
377
    // Only set PEX_ENABLE if we don't have max_size_pex set to zero.
 
378
    if (m_info->size_pex() < m_info->max_size_pex())
 
379
      togglePex = PeerConnectionBase::PEX_ENABLE;
 
380
 
 
381
  } else if (m_info->is_pex_active() &&
 
382
             m_connectionList->size() >= m_connectionList->min_size()) {
 
383
//              m_peerList.available_list()->size() >= m_peerList.available_list()->max_size() / 2) {
 
384
    togglePex = PeerConnectionBase::PEX_DISABLE;
 
385
    m_info->unset_flags(DownloadInfo::flag_pex_active);
 
386
  }
 
387
 
 
388
  // Return if we don't really want to do anything?
 
389
 
 
390
  ProtocolExtension::PEXList current;
 
391
 
 
392
  for (ConnectionList::iterator itr = m_connectionList->begin(); itr != m_connectionList->end(); ++itr) {
 
393
    PeerConnectionBase* pcb = (*itr)->m_ptr();
 
394
    const rak::socket_address* sa = rak::socket_address::cast_from(pcb->peer_info()->socket_address());
 
395
 
 
396
    if (pcb->peer_info()->listen_port() != 0 && sa->family() == rak::socket_address::af_inet)
 
397
      current.push_back(SocketAddressCompact(sa->sa_inet()->address_n(), pcb->peer_info()->listen_port()));
 
398
 
 
399
    if (!pcb->extensions()->is_remote_supported(ProtocolExtension::UT_PEX))
 
400
      continue;
 
401
 
 
402
    if (togglePex == PeerConnectionBase::PEX_ENABLE) {
 
403
      pcb->set_peer_exchange(true);
 
404
 
 
405
      if (m_info->size_pex() >= m_info->max_size_pex())
 
406
        togglePex = 0;
 
407
 
 
408
    } else if (!pcb->extensions()->is_local_enabled(ProtocolExtension::UT_PEX)) {
 
409
      continue;
 
410
 
 
411
    } else if (togglePex == PeerConnectionBase::PEX_DISABLE) {
 
412
      pcb->set_peer_exchange(false);
 
413
 
 
414
      continue;
 
415
    }
 
416
 
 
417
    // Still using the old buffer? Make a copy in this rare case.
 
418
    DataBuffer* message = pcb->extension_message();
 
419
 
 
420
    if (!message->empty() && (message->data() == m_ut_pex_initial.data() || message->data() == m_ut_pex_delta.data())) {
 
421
      char* buffer = new char[message->length()];
 
422
      memcpy(buffer, message->data(), message->length());
 
423
      message->set(buffer, buffer + message->length(), true);
 
424
    }
 
425
 
 
426
    pcb->do_peer_exchange();
 
427
  }
 
428
 
 
429
  std::sort(current.begin(), current.end(), SocketAddressCompact_less());
 
430
 
 
431
  ProtocolExtension::PEXList added;
 
432
  added.reserve(current.size());
 
433
  std::set_difference(current.begin(), current.end(), m_ut_pex_list.begin(), m_ut_pex_list.end(), 
 
434
                      std::back_inserter(added), SocketAddressCompact_less());
 
435
 
 
436
  ProtocolExtension::PEXList removed;
 
437
  removed.reserve(m_ut_pex_list.size());
 
438
  std::set_difference(m_ut_pex_list.begin(), m_ut_pex_list.end(), current.begin(), current.end(), 
 
439
                      std::back_inserter(removed), SocketAddressCompact_less());
 
440
    
 
441
  if (current.size() > m_info->max_size_pex_list()) {
 
442
    // This test is only correct as long as we have a constant max
 
443
    // size.
 
444
    if (added.size() < current.size() - m_info->max_size_pex_list())
 
445
      throw internal_error("DownloadMain::do_peer_exchange() added.size() < current.size() - m_info->max_size_pex_list().");
 
446
 
 
447
    // Randomize this:
 
448
    added.erase(added.end() - (current.size() - m_info->max_size_pex_list()), added.end());
 
449
 
 
450
    // Create the new m_ut_pex_list by removing any 'removed'
 
451
    // addresses from the original list and then adding the new
 
452
    // addresses.
 
453
    m_ut_pex_list.erase(std::set_difference(m_ut_pex_list.begin(), m_ut_pex_list.end(), removed.begin(), removed.end(),
 
454
                                            m_ut_pex_list.begin(), SocketAddressCompact_less()), m_ut_pex_list.end());
 
455
    m_ut_pex_list.insert(m_ut_pex_list.end(), added.begin(), added.end());
 
456
 
 
457
    std::sort(m_ut_pex_list.begin(), m_ut_pex_list.end(), SocketAddressCompact_less());
 
458
 
 
459
  } else {
 
460
    m_ut_pex_list.swap(current);
 
461
  }
 
462
 
 
463
  current.clear();
 
464
  m_ut_pex_delta.clear();
 
465
 
 
466
  // If no peers were added or removed, the initial message is still correct and
 
467
  // the delta message stays emptied. Otherwise generate the appropriate messages.
 
468
  if (!added.empty() || !m_ut_pex_list.empty()) {
 
469
    m_ut_pex_delta = ProtocolExtension::generate_ut_pex_message(added, removed);
 
470
 
 
471
    m_ut_pex_initial.clear();
 
472
    m_ut_pex_initial = ProtocolExtension::generate_ut_pex_message(m_ut_pex_list, current);
 
473
  }
 
474
}
 
475
 
 
476
void
 
477
DownloadMain::set_metadata_size(size_t size) {
 
478
  if (m_info->is_meta_download()) {
 
479
    if (m_fileList.size_bytes() < 2)
 
480
      file_list()->reset_filesize(size);
 
481
    else if (size != m_fileList.size_bytes())
 
482
      throw communication_error("Peer-supplied metadata size mismatch.");
 
483
 
 
484
  } else if (m_info->metadata_size() && m_info->metadata_size() != size) {
 
485
      throw communication_error("Peer-supplied metadata size mismatch.");
 
486
  }
 
487
 
 
488
  m_info->set_metadata_size(size);
 
489
}
 
490
 
 
491
}