~jtaylor/ubuntu/oneiric/bittornado/dh_python2

« back to all changes in this revision

Viewing changes to .pc/09_timtuckerfixes.dpatch/BitTornado/BT1/Connecter.py

  • Committer: Bazaar Package Importer
  • Author(s): Cameron Dale
  • Date: 2010-03-21 14:36:30 UTC
  • Revision ID: james.westby@ubuntu.com-20100321143630-d1zk1zdasaf8125s
Tags: 0.3.18-10
* New patch from upstream's CVS to allow torrents that only have an
  announce list: 30_announce_list_only_torrents.dpatch (Closes: #551766)
* Fix a lot of lintian warnings
  - Update standards version to 3.8.4 (no changes)
* Fix for when compact_reqd is turned off:
  31_fix_for_compact_reqd_off.dpatch (Closes: #574860)
* Switch to the new "3.0 (quilt)" source format

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Written by Bram Cohen
 
2
# see LICENSE.txt for license information
 
3
 
 
4
from BitTornado.bitfield import Bitfield
 
5
from BitTornado.clock import clock
 
6
from binascii import b2a_hex
 
7
 
 
8
try:
 
9
    True
 
10
except:
 
11
    True = 1
 
12
    False = 0
 
13
 
 
14
DEBUG1 = False
 
15
DEBUG2 = False
 
16
 
 
17
def toint(s):
 
18
    return long(b2a_hex(s), 16)
 
19
 
 
20
def tobinary(i):
 
21
    return (chr(i >> 24) + chr((i >> 16) & 0xFF) + 
 
22
        chr((i >> 8) & 0xFF) + chr(i & 0xFF))
 
23
 
 
24
CHOKE = chr(0)
 
25
UNCHOKE = chr(1)
 
26
INTERESTED = chr(2)
 
27
NOT_INTERESTED = chr(3)
 
28
# index
 
29
HAVE = chr(4)
 
30
# index, bitfield
 
31
BITFIELD = chr(5)
 
32
# index, begin, length
 
33
REQUEST = chr(6)
 
34
# index, begin, piece
 
35
PIECE = chr(7)
 
36
# index, begin, piece
 
37
CANCEL = chr(8)
 
38
 
 
39
class Connection:
 
40
    def __init__(self, connection, connecter, ccount):
 
41
        self.connection = connection
 
42
        self.connecter = connecter
 
43
        self.ccount = ccount
 
44
        self.got_anything = False
 
45
        self.next_upload = None
 
46
        self.outqueue = []
 
47
        self.partial_message = None
 
48
        self.download = None
 
49
        self.send_choke_queued = False
 
50
        self.just_unchoked = None
 
51
 
 
52
    def get_ip(self, real=False):
 
53
        return self.connection.get_ip(real)
 
54
 
 
55
    def get_id(self):
 
56
        return self.connection.get_id()
 
57
 
 
58
    def get_readable_id(self):
 
59
        return self.connection.get_readable_id()
 
60
 
 
61
    def close(self):
 
62
        if DEBUG1:
 
63
            print (self.ccount,'connection closed')
 
64
        self.connection.close()
 
65
 
 
66
    def is_locally_initiated(self):
 
67
        return self.connection.is_locally_initiated()
 
68
 
 
69
    def is_encrypted(self):
 
70
        return self.connection.is_encrypted()
 
71
 
 
72
    def send_interested(self):
 
73
        self._send_message(INTERESTED)
 
74
 
 
75
    def send_not_interested(self):
 
76
        self._send_message(NOT_INTERESTED)
 
77
 
 
78
    def send_choke(self):
 
79
        if self.partial_message:
 
80
            self.send_choke_queued = True
 
81
        else:
 
82
            self._send_message(CHOKE)
 
83
            self.upload.choke_sent()
 
84
            self.just_unchoked = 0
 
85
 
 
86
    def send_unchoke(self):
 
87
        if self.send_choke_queued:
 
88
            self.send_choke_queued = False
 
89
            if DEBUG1:
 
90
                print (self.ccount,'CHOKE SUPPRESSED')
 
91
        else:
 
92
            self._send_message(UNCHOKE)
 
93
            if ( self.partial_message or self.just_unchoked is None
 
94
                 or not self.upload.interested or self.download.active_requests ):
 
95
                self.just_unchoked = 0
 
96
            else:
 
97
                self.just_unchoked = clock()
 
98
 
 
99
    def send_request(self, index, begin, length):
 
100
        self._send_message(REQUEST + tobinary(index) + 
 
101
            tobinary(begin) + tobinary(length))
 
102
        if DEBUG1:
 
103
            print (self.ccount,'sent request',index,begin,begin+length)
 
104
 
 
105
    def send_cancel(self, index, begin, length):
 
106
        self._send_message(CANCEL + tobinary(index) + 
 
107
            tobinary(begin) + tobinary(length))
 
108
        if DEBUG1:
 
109
            print (self.ccount,'sent cancel',index,begin,begin+length)
 
110
 
 
111
    def send_bitfield(self, bitfield):
 
112
        self._send_message(BITFIELD + bitfield)
 
113
 
 
114
    def send_have(self, index):
 
115
        self._send_message(HAVE + tobinary(index))
 
116
 
 
117
    def send_keepalive(self):
 
118
        self._send_message('')
 
119
 
 
120
    def _send_message(self, s):
 
121
        if DEBUG2:
 
122
            if s:
 
123
                print (self.ccount,'SENDING MESSAGE',ord(s[0]),len(s))
 
124
            else:
 
125
                print (self.ccount,'SENDING MESSAGE',-1,0)
 
126
        s = tobinary(len(s))+s
 
127
        if self.partial_message:
 
128
            self.outqueue.append(s)
 
129
        else:
 
130
            self.connection.send_message_raw(s)
 
131
 
 
132
    def send_partial(self, bytes):
 
133
        if self.connection.closed:
 
134
            return 0
 
135
        if self.partial_message is None:
 
136
            s = self.upload.get_upload_chunk()
 
137
            if s is None:
 
138
                return 0
 
139
            index, begin, piece = s
 
140
            self.partial_message = ''.join((
 
141
                            tobinary(len(piece) + 9), PIECE,
 
142
                            tobinary(index), tobinary(begin), piece.tostring() ))
 
143
            if DEBUG1:
 
144
                print (self.ccount,'sending chunk',index,begin,begin+len(piece))
 
145
 
 
146
        if bytes < len(self.partial_message):
 
147
            self.connection.send_message_raw(self.partial_message[:bytes])
 
148
            self.partial_message = self.partial_message[bytes:]
 
149
            return bytes
 
150
 
 
151
        q = [self.partial_message]
 
152
        self.partial_message = None
 
153
        if self.send_choke_queued:
 
154
            self.send_choke_queued = False
 
155
            self.outqueue.append(tobinary(1)+CHOKE)
 
156
            self.upload.choke_sent()
 
157
            self.just_unchoked = 0
 
158
        q.extend(self.outqueue)
 
159
        self.outqueue = []
 
160
        q = ''.join(q)
 
161
        self.connection.send_message_raw(q)
 
162
        return len(q)
 
163
 
 
164
    def get_upload(self):
 
165
        return self.upload
 
166
 
 
167
    def get_download(self):
 
168
        return self.download
 
169
 
 
170
    def set_download(self, download):
 
171
        self.download = download
 
172
 
 
173
    def backlogged(self):
 
174
        return not self.connection.is_flushed()
 
175
 
 
176
    def got_request(self, i, p, l):
 
177
        self.upload.got_request(i, p, l)
 
178
        if self.just_unchoked:
 
179
            self.connecter.ratelimiter.ping(clock() - self.just_unchoked)
 
180
            self.just_unchoked = 0
 
181
    
 
182
 
 
183
 
 
184
 
 
185
class Connecter:
 
186
    def __init__(self, make_upload, downloader, choker, numpieces,
 
187
            totalup, config, ratelimiter, sched = None):
 
188
        self.downloader = downloader
 
189
        self.make_upload = make_upload
 
190
        self.choker = choker
 
191
        self.numpieces = numpieces
 
192
        self.config = config
 
193
        self.ratelimiter = ratelimiter
 
194
        self.rate_capped = False
 
195
        self.sched = sched
 
196
        self.totalup = totalup
 
197
        self.rate_capped = False
 
198
        self.connections = {}
 
199
        self.external_connection_made = 0
 
200
        self.ccount = 0
 
201
 
 
202
    def how_many_connections(self):
 
203
        return len(self.connections)
 
204
 
 
205
    def connection_made(self, connection):
 
206
        self.ccount += 1
 
207
        c = Connection(connection, self, self.ccount)
 
208
        if DEBUG2:
 
209
            print (c.ccount,'connection made')
 
210
        self.connections[connection] = c
 
211
        c.upload = self.make_upload(c, self.ratelimiter, self.totalup)
 
212
        c.download = self.downloader.make_download(c)
 
213
        self.choker.connection_made(c)
 
214
        return c
 
215
 
 
216
    def connection_lost(self, connection):
 
217
        c = self.connections[connection]
 
218
        if DEBUG2:
 
219
            print (c.ccount,'connection closed')
 
220
        del self.connections[connection]
 
221
        if c.download:
 
222
            c.download.disconnected()
 
223
        self.choker.connection_lost(c)
 
224
 
 
225
    def connection_flushed(self, connection):
 
226
        conn = self.connections[connection]
 
227
        if conn.next_upload is None and (conn.partial_message is not None
 
228
               or len(conn.upload.buffer) > 0):
 
229
            self.ratelimiter.queue(conn)
 
230
            
 
231
    def got_piece(self, i):
 
232
        for co in self.connections.values():
 
233
            co.send_have(i)
 
234
 
 
235
    def got_message(self, connection, message):
 
236
        c = self.connections[connection]
 
237
        t = message[0]
 
238
        if DEBUG2:
 
239
            print (c.ccount,'message received',ord(t))
 
240
        if t == BITFIELD and c.got_anything:
 
241
            if DEBUG2:
 
242
                print (c.ccount,'misplaced bitfield')
 
243
            connection.close()
 
244
            return
 
245
        c.got_anything = True
 
246
        if (t in [CHOKE, UNCHOKE, INTERESTED, NOT_INTERESTED] and 
 
247
                len(message) != 1):
 
248
            if DEBUG2:
 
249
                print (c.ccount,'bad message length')
 
250
            connection.close()
 
251
            return
 
252
        if t == CHOKE:
 
253
            c.download.got_choke()
 
254
        elif t == UNCHOKE:
 
255
            c.download.got_unchoke()
 
256
        elif t == INTERESTED:
 
257
            if not c.download.have.complete():
 
258
                c.upload.got_interested()
 
259
        elif t == NOT_INTERESTED:
 
260
            c.upload.got_not_interested()
 
261
        elif t == HAVE:
 
262
            if len(message) != 5:
 
263
                if DEBUG2:
 
264
                    print (c.ccount,'bad message length')
 
265
                connection.close()
 
266
                return
 
267
            i = toint(message[1:])
 
268
            if i >= self.numpieces:
 
269
                if DEBUG2:
 
270
                    print (c.ccount,'bad piece number')
 
271
                connection.close()
 
272
                return
 
273
            if c.download.got_have(i):
 
274
                c.upload.got_not_interested()
 
275
        elif t == BITFIELD:
 
276
            try:
 
277
                b = Bitfield(self.numpieces, message[1:])
 
278
            except ValueError:
 
279
                if DEBUG2:
 
280
                    print (c.ccount,'bad bitfield')
 
281
                connection.close()
 
282
                return
 
283
            if c.download.got_have_bitfield(b):
 
284
                c.upload.got_not_interested()
 
285
        elif t == REQUEST:
 
286
            if len(message) != 13:
 
287
                if DEBUG2:
 
288
                    print (c.ccount,'bad message length')
 
289
                connection.close()
 
290
                return
 
291
            i = toint(message[1:5])
 
292
            if i >= self.numpieces:
 
293
                if DEBUG2:
 
294
                    print (c.ccount,'bad piece number')
 
295
                connection.close()
 
296
                return
 
297
            c.got_request(i, toint(message[5:9]), 
 
298
                toint(message[9:]))
 
299
        elif t == CANCEL:
 
300
            if len(message) != 13:
 
301
                if DEBUG2:
 
302
                    print (c.ccount,'bad message length')
 
303
                connection.close()
 
304
                return
 
305
            i = toint(message[1:5])
 
306
            if i >= self.numpieces:
 
307
                if DEBUG2:
 
308
                    print (c.ccount,'bad piece number')
 
309
                connection.close()
 
310
                return
 
311
            c.upload.got_cancel(i, toint(message[5:9]), 
 
312
                toint(message[9:]))
 
313
        elif t == PIECE:
 
314
            if len(message) <= 9:
 
315
                if DEBUG2:
 
316
                    print (c.ccount,'bad message length')
 
317
                connection.close()
 
318
                return
 
319
            i = toint(message[1:5])
 
320
            if i >= self.numpieces:
 
321
                if DEBUG2:
 
322
                    print (c.ccount,'bad piece number')
 
323
                connection.close()
 
324
                return
 
325
            if c.download.got_piece(i, toint(message[5:9]), message[9:]):
 
326
                self.got_piece(i)
 
327
        else:
 
328
            connection.close()