r187 - in /debtorrent/trunk/DebTorrent/BT1: FileSelector.py NatCheck.py
camrdale-guest at users.alioth.debian.org
camrdale-guest at users.alioth.debian.org
Sat Jul 28 02:58:34 UTC 2007
Author: camrdale-guest
Date: Sat Jul 28 02:58:33 2007
New Revision: 187
URL: http://svn.debian.org/wsvn/debtorrent/?sc=1&rev=187
Log:
More documentation.
Modified:
debtorrent/trunk/DebTorrent/BT1/FileSelector.py
debtorrent/trunk/DebTorrent/BT1/NatCheck.py
Modified: debtorrent/trunk/DebTorrent/BT1/FileSelector.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/FileSelector.py?rev=187&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/FileSelector.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/FileSelector.py Sat Jul 28 02:58:33 2007
@@ -18,42 +18,48 @@
class FileSelector:
"""Enable the selective downloading of files within a torrent.
- @type files: unknown
- @ivar files: unknown
- @type storage: unknown
- @ivar storage: unknown
- @type storagewrapper: unknown
- @ivar storagewrapper: unknown
- @type sched: unknown
- @ivar sched: unknown
- @type failfunc: unknown
- @ivar failfunc: unknown
+ @type files: C{list} of (C{list} of C{string}, C{long})
+ @ivar files: the paths and sizes of the files
+ @type storage: L{Storage.Storage}
+ @ivar storage: the Storage instance to use
+ @type storagewrapper: L{StorageWrapper.StorageWrapper}
+ @ivar storagewrapper: the StorageWrapper instance to use
+ @type sched: C{method}
+ @ivar sched: the mehtod to call to schedule a future invocation of a function
+ @type failfunc: C{method}
+ @ivar failfunc: the method to call to indicate an error has occurred
@type downloader: unknown
- @ivar downloader: unknown
- @type picker: unknown
- @ivar picker: unknown
- @type numfiles: unknown
- @ivar numfiles: unknown
- @type priority: unknown
- @ivar priority: unknown
- @type new_priority: unknown
- @ivar new_priority: unknown
- @type new_partials: unknown
- @ivar new_partials: unknown
- @type filepieces: unknown
- @ivar filepieces: unknown
- @type numpieces: unknown
- @ivar numpieces: unknown
- @type piece_priority: unknown
- @ivar piece_priority: unknown
- @type cancelfunc: unknown
- @ivar cancelfunc: unknown
- @type requestmorefunc: unknown
- @ivar requestmorefunc: unknown
- @type rerequestfunc: unknown
- @ivar rerequestfunc: unknown
- @type new_piece_priority: unknown
- @ivar new_piece_priority: unknown
+ @ivar downloader: not used
+ @type picker: L{PiecePicker.PiecePicker}
+ @ivar picker: the PiecePicker instance to use
+ @type numfiles: C{int}
+ @ivar numfiles: the number of files in the download
+ @type priority: C{list} of C{int}
+ @ivar priority: the current priority of each file in the download::
+ -1 -- do not download
+ 0 -- highest priority
+ 1 -- medium priority
+ 2 -- lowest priority
+ @type new_priority: C{list} of C{int}
+ @ivar new_priority: the new priority of each file in the download
+ @type new_partials: C{list} of C{int}
+ @ivar new_partials: the new list of partially completed pieces to process
+ @type filepieces: C{list} of C{tuple}
+ @ivar filepieces: an ordered list, one for each file, containing a tuple of
+ the pieces that belong to that file
+ @type numpieces: C{int}
+ @ivar numpieces: the number of pieces in the download
+ @type piece_priority: C{list} of C{int}
+ @ivar piece_priority: the current priority for each piece in the download
+ @type cancelfunc: C{method}
+ @ivar cancelfunc: method to call to cancel piece downloads
+ @type requestmorefunc: C{method}
+ @ivar requestmorefunc: method to call to request more peers for a
+ newly enabled piece download
+ @type rerequestfunc: C{method}
+ @ivar rerequestfunc: method to call to request more peers
+ @type new_piece_priority: C{list} of C{int}
+ @ivar new_piece_priority: the new priority for each piece in the download
"""
@@ -61,22 +67,22 @@
storage, storagewrapper, sched, picker, failfunc):
"""Initialize the instance.
- @type files: unknown
- @param files: unknown
- @type piece_lengths: unknown
- @param piece_lengths: unknown
- @type bufferdir: unknown
- @param bufferdir: unknown
- @type storage: unknown
- @param storage: unknown
- @type storagewrapper: unknown
- @param storagewrapper: unknown
- @type sched: unknown
- @param sched: unknown
- @type picker: unknown
- @param picker: unknown
- @type failfunc: unknown
- @param failfunc: unknown
+ @type files: C{list} of (C{list} of C{string}, C{long})
+ @param files: the paths and sizes of the files
+ @type piece_lengths: C{list} of C{int}
+ @param piece_lengths: the sizes of the pieces
+ @type bufferdir: C{string}
+ @param bufferdir: the directory to store the buffered pieces in
+ @type storage: L{Storage.Storage}
+ @param storage: the Storage instance to use
+ @type storagewrapper: L{StorageWrapper.StorageWrapper}
+ @param storagewrapper: the StorageWrapper instance to use
+ @type sched: C{method}
+ @param sched: the mehtod to call to schedule a future invocation of a function
+ @type picker: L{PiecePicker.PiecePicker}
+ @param picker: the PiecePicker instance to use
+ @type failfunc: C{method}
+ @param failfunc: the method to call to indicate an error has occurred
"""
@@ -120,10 +126,12 @@
def init_priority(self, new_priority):
- """
-
- @type new_priority: unknown
- @param new_priority: unknown
+ """Initialize the priorities of all the files.
+
+ @type new_priority: C{list} of C{int}
+ @param new_priority: the new file priorities
+ @rtype: C{boolean}
+ @return: whether the initialization was successful
"""
@@ -150,14 +158,25 @@
return False
return True
- '''
- d['priority'] = [file #1 priority [,file #2 priority...] ]
- a list of download priorities for each file.
- Priority may be -1, 0, 1, 2. -1 = download disabled,
- 0 = highest, 1 = normal, 2 = lowest.
- Also see Storage.pickle and StorageWrapper.pickle for additional keys.
- '''
def unpickle(self, d):
+ """Unpickle the previously saved state.
+
+ Data is in the format:
+ d['priority'] = [file #1 priority, file #2 priority, ...]
+
+ It is a list of download priorities for each file. The priority may be::
+ -1 -- download disabled
+ 0 -- highest
+ 1 -- normal
+ 2 -- lowest
+
+ Also see Storage.pickle and StorageWrapper.pickle for additional keys.
+
+ @type d: C{dictionary}
+ @param d: the pickled state data
+
+ """
+
if d.has_key('priority'):
if not self.init_priority(d['priority']):
return
@@ -171,6 +190,18 @@
def tie_in(self, cancelfunc, requestmorefunc, rerequestfunc):
+ """Set some instance variables that weren't available at initialization.
+
+ @type cancelfunc: C{method}
+ @param cancelfunc: method to call to cancel piece downloads
+ @type requestmorefunc: C{method}
+ @param requestmorefunc: method to call to request more peers for a
+ newly enabled piece download
+ @type rerequestfunc: C{method}
+ @param rerequestfunc: method to call to request more peers
+
+ """
+
self.cancelfunc = cancelfunc
self.requestmorefunc = requestmorefunc
self.rerequestfunc = rerequestfunc
@@ -188,6 +219,17 @@
def _initialize_files_disabled(self, old_priority, new_priority):
+ """Initialize the disabled files on startup.
+
+ @type old_priority: C{list} of C{int}
+ @param old_priority: the old file priorities
+ @type new_priority: C{list} of C{int}
+ @param new_priority: the new file priorities
+ @rtype: C{boolean}
+ @return: whether the initialization was successful
+
+ """
+
old_disabled = [p == -1 for p in old_priority]
new_disabled = [p == -1 for p in new_priority]
files_updated = False
@@ -212,6 +254,17 @@
def _set_files_disabled(self, old_priority, new_priority):
+ """Disable files based on a new priority setting.
+
+ @type old_priority: C{list} of C{int}
+ @param old_priority: the old file priorities
+ @type new_priority: C{list} of C{int}
+ @param new_priority: the new file priorities
+ @rtype: C{boolean}
+ @return: whether the disabling was successful
+
+ """
+
old_disabled = [p == -1 for p in old_priority]
new_disabled = [p == -1 for p in new_priority]
data_to_update = []
@@ -258,6 +311,14 @@
def _get_piece_priority_list(self, file_priority_list):
+ """Create a piece priority list from a file priority list.
+
+ @type file_priority_list: C{list} of C{int}
+ @param file_priority_list: the file priorities
+ @rtype: C{list} of C{int}
+ @return: the new piece priorities
+
+ """
l = [-1] * self.numpieces
for f in xrange(self.numfiles):
if file_priority_list[f] == -1:
@@ -271,6 +332,15 @@
def _initialize_piece_priority(self, new_priority):
+ """Initialize the piece priorities on startup.
+
+ @type new_priority: C{list} of C{int}
+ @param new_priority: the new file priorities
+ @rtype: C{list} of C{int}
+ @return: the new piece priorities
+
+ """
+
new_piece_priority = self._get_piece_priority_list(new_priority)
pieces = range(self.numpieces)
shuffle(pieces)
@@ -282,6 +352,15 @@
def _set_piece_priority(self, new_priority):
+ """Disable pieces base on a new file priority setting.
+
+ @type new_priority: C{list} of C{int}
+ @param new_priority: the new file priorities
+ @rtype: C{list} of C{int}
+ @return: the new piece priorities
+
+ """
+
was_complete = self.storagewrapper.am_I_complete()
new_piece_priority = self._get_piece_priority_list(new_priority)
pieces = range(self.numpieces)
@@ -308,6 +387,14 @@
def initialize_priorities_now(self, new_priority = None):
+ """Initialize the priorities on startup.
+
+ @type new_priority: C{list} of C{int}
+ @param new_priority: the new file priorities
+ (optional, defaults to not initializing anything)
+
+ """
+
if not new_priority:
return
old_priority = self.priority
@@ -317,6 +404,14 @@
self.piece_priority = self._initialize_piece_priority(new_priority)
def set_priorities_now(self, new_priority = None):
+ """Set the new priorities.
+
+ @type new_priority: C{list} of C{int}
+ @param new_priority: the new file priorities
+ (optional, defaults to using the saved L{new_priority})
+
+ """
+
if not new_priority:
new_priority = self.new_priority
self.new_priority = None # potential race condition
@@ -329,24 +424,65 @@
self.piece_priority = self._set_piece_priority(new_priority)
def set_priorities(self, new_priority):
+ """Schedule the future setting of the new priorities.
+
+ @type new_priority: C{list} of C{int}
+ @param new_priority: the new file priorities
+
+ """
+
self.new_priority = new_priority
self.sched(self.set_priorities_now)
def set_priority(self, f, p):
+ """Set the priority of a single file.
+
+ @type f: C{int}
+ @param f: the index of the file to set the priority of
+ @type p: C{int}
+ @param p: the new priority for the file
+
+ """
+
new_priority = self.get_priorities()
new_priority[f] = p
self.set_priorities(new_priority)
def get_priorities(self):
+ """Get the current (or soon to be set) file priorities.
+
+ @rtype: C{list} of C{int}
+ @return: the current file priorities
+
+ """
+
priority = self.new_priority
if not priority:
priority = self.priority # potential race condition
return [i for i in priority]
def __setitem__(self, index, val):
+ """Set the priority for the file.
+
+ @type index: C{int}
+ @param index: the index of the file to set the priority of
+ @type val: C{int}
+ @param val: the new priority for the file
+
+ """
+
self.set_priority(index, val)
def __getitem__(self, index):
+ """Get the priority for the file.
+
+ @type index: C{int}
+ @param index: the index of the file to get the priority of
+ @rtype: C{int}
+ @return: the priority for the file
+
+ """
+
try:
return self.new_priority[index]
except:
@@ -354,11 +490,20 @@
def finish(self):
+ """Delete disabled files when the download is being shutdown."""
for f in xrange(self.numfiles):
if self.priority[f] == -1:
self.storage.delete_file(f)
def pickle(self):
+ """Pickle the current state for later.
+
+ @see: L{unpickle}
+ @rtype: C{dictionary}
+ @return: the pickled data
+
+ """
+
d = {'priority': self.priority}
try:
s = self.storage.pickle()
Modified: debtorrent/trunk/DebTorrent/BT1/NatCheck.py
URL: http://svn.debian.org/wsvn/debtorrent/debtorrent/trunk/DebTorrent/BT1/NatCheck.py?rev=187&op=diff
==============================================================================
--- debtorrent/trunk/DebTorrent/BT1/NatCheck.py (original)
+++ debtorrent/trunk/DebTorrent/BT1/NatCheck.py Sat Jul 28 02:58:33 2007
@@ -1,8 +1,15 @@
# Written by Bram Cohen
# Modified by Cameron Dale
# see LICENSE.txt for license information
-
+#
# $Id$
+
+"""Check if a peer is unreachable behind a NAT.
+
+ at type CHECK_PEER_ID_ENCRYPTED: C{boolean}
+ at var CHECK_PEER_ID_ENCRYPTED: whether to check if connecting peers are encrypted
+
+"""
from cStringIO import StringIO
from socket import error as socketerror
@@ -20,8 +27,68 @@
# header, reserved, download id, my id, [length, message]
class NatCheck:
+ """Check if a peer is unreachable behind a NAT.
+
+ @type resultfunc: C{method}
+ @ivar resultfunc: the method to call with the result when complete
+ @type downloadid: C{string}
+ @ivar downloadid: the info hash of the torrent to use
+ @type peerid: C{string}
+ @ivar peerid: the peer ID of the peer being checked
+ @type ip: C{string}
+ @ivar ip: the IP of the peer being checked
+ @type port: C{int}
+ @ivar port: the port to connect to the peer on
+ @type encrypted: C{boolean}
+ @ivar encrypted: whether to use an encrypted connection
+ @type closed: C{boolean}
+ @ivar closed: whether the connection has been closed
+ @type buffer: C{string}
+ @ivar buffer: the buffer of received data from the connection
+ @type read: C{method}
+ @ivar read: the method to use to read from the connection
+ @type write: C{method}
+ @ivar write: the method to use to write to the connection
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @ivar connection: the connection to the peer
+ @type _dc: C{boolean}
+ @ivar _dc: whether encrypted connections have been disabled
+ @type encrypter: L{DebTorrent.BTcrypto.Crypto}
+ @ivar encrypter: the encrypter to use for the connection
+ @type next_len: C{int}
+ @ivar next_len: the next amount of data to read from the connection
+ @type next_func: C{method}
+ @ivar next_func: the next method to use to process incoming data on the
+ connection
+ @type _max_search: C{int}
+ @ivar _max_search: the number of remaining bytes to search for the pattern
+ @type cryptmode: C{int}
+ @ivar cryptmode: the type of encryption being used
+
+ """
+
def __init__(self, resultfunc, downloadid, peerid, ip, port, rawserver,
encrypted = False):
+ """Initialize the instance and start a connection to the peer.
+
+ @type resultfunc: C{method}
+ @param resultfunc: the method to call with the result when complete
+ @type downloadid: C{string}
+ @param downloadid: the info hash of the torrent to use
+ @type peerid: C{string}
+ @param peerid: the peer ID of the peer being checked
+ @type ip: C{string}
+ @param ip: the IP of the peer being checked
+ @type port: C{int}
+ @param port: the port to connect to the peer on
+ @type rawserver: L{DebTorrent.RawServer.RawServer}
+ @param rawserver: the server instance to use
+ @type encrypted: C{boolean}
+ @param encrypted: whether to use an encrypted connection
+ (optional, defaults to False)
+
+ """
+
self.resultfunc = resultfunc
self.downloadid = downloadid
self.peerid = peerid
@@ -49,6 +116,13 @@
self.next_len, self.next_func = 1+len(protocol_name), self.read_header
def answer(self, result):
+ """Close the connection and return the result.
+
+ @type result: C{boolean}
+ @param result: whether the peer is connectable
+
+ """
+
self.closed = True
try:
self.connection.close()
@@ -57,11 +131,31 @@
self.resultfunc(result, self.downloadid, self.peerid, self.ip, self.port)
def _read_header(self, s):
+ """Read the protocol header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if s == chr(len(protocol_name))+protocol_name:
return 8, self.read_options
return None
def read_header(self, s):
+ """Read the possibly encrypted protocol header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if self._read_header(s):
if self.encrypted:
return None
@@ -74,6 +168,7 @@
################## ENCRYPTION SUPPORT ######################
def _start_crypto(self):
+ """Setup the instance for the encrypted connection."""
self.encrypter.setrawaccess(self._read,self._write)
self.write = self.encrypter.write
self.read = self.encrypter.read
@@ -81,6 +176,16 @@
self.buffer = self.encrypter.decrypt(self.buffer)
def read_crypto_header(self, s):
+ """Read the encryption key.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
self.encrypter.received_key(s)
self.encrypter.set_skey(self.downloadid)
cryptmode = '\x00\x00\x00\x02' # full stream encryption
@@ -97,6 +202,17 @@
return 1, self.read_crypto_block4a
def _search_for_pattern(self, s, pat):
+ """Search for a patter in the encrypted protocol header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @type pat: C{string}
+ @param pat: the pattern to find
+ @rtype: C{boolean}
+ @return: whether the pattern was found
+
+ """
+
p = s.find(pat)
if p < 0:
if len(s) >= len(pat):
@@ -112,6 +228,16 @@
### OUTGOING CONNECTION ###
def read_crypto_block4a(self, s):
+ """Read the encrypted protocol header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if not self._search_for_pattern(s,self.encrypter.VC_pattern()):
return -1, self.read_crypto_block4a # wait for more data
if self._dc: # can't or won't go any further
@@ -121,6 +247,16 @@
return 6, self.read_crypto_block4b
def read_crypto_block4b(self, s):
+ """Read the encrypted protocol mode and padding.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
self.cryptmode = toint(s[:4]) % 4
if self.cryptmode != 2:
return None # unknown encryption
@@ -132,10 +268,28 @@
return self.read_crypto_block4done()
def read_crypto_pad4(self, s):
+ """Read the encrypted protocol padding.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
# discard data
return self.read_crypto_block4done()
def read_crypto_block4done(self):
+ """Finish with the encrypted header.
+
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if self.cryptmode == 1: # only handshake encryption
if not self.buffer: # oops; check for exceptions to this
return None
@@ -147,35 +301,104 @@
### START PROTOCOL OVER ENCRYPTED CONNECTION ###
def read_encrypted_header(self, s):
+ """Read the regular protocol name header from the encrypted stream.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
return self._read_header(s)
################################################
def read_options(self, s):
+ """Read the options from the header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
return 20, self.read_download_id
def read_download_id(self, s):
+ """Verify the torrent infohash from the header.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: C{int}, C{method}
+ @return: the next amount of data to read and the method to call with
+ it, or None if there is no next method to call
+
+ """
+
if s != self.downloadid:
return None
return 20, self.read_peer_id
def read_peer_id(self, s):
+ """Verify the peer's ID and return the answer.
+
+ @type s: C{string}
+ @param s: the incoming data from the connection
+ @rtype: None
+ @return: None
+
+ """
+
if s != self.peerid:
return None
self.answer(True)
return None
def _write(self, message):
+ """Write a raw message out on the connection.
+
+ @type message: C{string}
+ @param message: the raw data to write to the connection
+
+ """
+
if not self.closed:
self.connection.write(message)
def data_came_in(self, connection, s):
+ """Process the incoming data on the connection.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection the data came in on (not used)
+ @type s: C{string}
+ @param s: the incoming data from the connection
+
+ """
+
self.read(s)
def _write_buffer(self, s):
+ """Write data back onto the buffer.
+
+ @type s: C{string}
+ @param s: the data to rebuffer
+
+ """
+
self.buffer = s+self.buffer
def _read(self, s):
+ """Process the data that came in.
+
+ @type s: C{string}
+ @param s: the (unencrypted) incoming data from the connection
+
+ """
+
self.buffer += s
while True:
if self.closed:
@@ -210,9 +433,23 @@
return
def connection_lost(self, connection):
+ """Close the connection and return the failure.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection that was lost (not used)
+
+ """
+
if not self.closed:
self.closed = True
self.resultfunc(False, self.downloadid, self.peerid, self.ip, self.port)
def connection_flushed(self, connection):
+ """Do nothing.
+
+ @type connection: L{DebTorrent.SocketHandler.SingleSocket}
+ @param connection: the connection that was flushed (not used)
+
+ """
+
pass
More information about the Debtorrent-commits
mailing list