1
# Written by Bram Cohen
2
# see LICENSE.txt for license information
4
from BitTornado.bitfield import Bitfield
5
from BitTornado.clock import clock
6
from binascii import b2a_hex
18
return long(b2a_hex(s), 16)
21
return (chr(i >> 24) + chr((i >> 16) & 0xFF) +
22
chr((i >> 8) & 0xFF) + chr(i & 0xFF))
27
NOT_INTERESTED = chr(3)
32
# index, begin, length
40
def __init__(self, connection, connecter, ccount):
41
self.connection = connection
42
self.connecter = connecter
44
self.got_anything = False
45
self.next_upload = None
47
self.partial_message = None
49
self.send_choke_queued = False
50
self.just_unchoked = None
52
def get_ip(self, real=False):
53
return self.connection.get_ip(real)
56
return self.connection.get_id()
58
def get_readable_id(self):
59
return self.connection.get_readable_id()
63
print (self.ccount,'connection closed')
64
self.connection.close()
66
def is_locally_initiated(self):
67
return self.connection.is_locally_initiated()
69
def is_encrypted(self):
70
return self.connection.is_encrypted()
72
def send_interested(self):
73
self._send_message(INTERESTED)
75
def send_not_interested(self):
76
self._send_message(NOT_INTERESTED)
79
if self.partial_message:
80
self.send_choke_queued = True
82
self._send_message(CHOKE)
83
self.upload.choke_sent()
84
self.just_unchoked = 0
86
def send_unchoke(self):
87
if self.send_choke_queued:
88
self.send_choke_queued = False
90
print (self.ccount,'CHOKE SUPPRESSED')
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
97
self.just_unchoked = clock()
99
def send_request(self, index, begin, length):
100
self._send_message(REQUEST + tobinary(index) +
101
tobinary(begin) + tobinary(length))
103
print (self.ccount,'sent request',index,begin,begin+length)
105
def send_cancel(self, index, begin, length):
106
self._send_message(CANCEL + tobinary(index) +
107
tobinary(begin) + tobinary(length))
109
print (self.ccount,'sent cancel',index,begin,begin+length)
111
def send_bitfield(self, bitfield):
112
self._send_message(BITFIELD + bitfield)
114
def send_have(self, index):
115
self._send_message(HAVE + tobinary(index))
117
def send_keepalive(self):
118
self._send_message('')
120
def _send_message(self, s):
123
print (self.ccount,'SENDING MESSAGE',ord(s[0]),len(s))
125
print (self.ccount,'SENDING MESSAGE',-1,0)
126
s = tobinary(len(s))+s
127
if self.partial_message:
128
self.outqueue.append(s)
130
self.connection.send_message_raw(s)
132
def send_partial(self, bytes):
133
if self.connection.closed:
135
if self.partial_message is None:
136
s = self.upload.get_upload_chunk()
139
index, begin, piece = s
140
self.partial_message = ''.join((
141
tobinary(len(piece) + 9), PIECE,
142
tobinary(index), tobinary(begin), piece.tostring() ))
144
print (self.ccount,'sending chunk',index,begin,begin+len(piece))
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:]
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)
161
self.connection.send_message_raw(q)
164
def get_upload(self):
167
def get_download(self):
170
def set_download(self, download):
171
self.download = download
173
def backlogged(self):
174
return not self.connection.is_flushed()
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
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
191
self.numpieces = numpieces
193
self.ratelimiter = ratelimiter
194
self.rate_capped = False
196
self.totalup = totalup
197
self.rate_capped = False
198
self.connections = {}
199
self.external_connection_made = 0
202
def how_many_connections(self):
203
return len(self.connections)
205
def connection_made(self, connection):
207
c = Connection(connection, self, self.ccount)
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)
216
def connection_lost(self, connection):
217
c = self.connections[connection]
219
print (c.ccount,'connection closed')
220
del self.connections[connection]
222
c.download.disconnected()
223
self.choker.connection_lost(c)
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)
231
def got_piece(self, i):
232
for co in self.connections.values():
235
def got_message(self, connection, message):
236
c = self.connections[connection]
239
print (c.ccount,'message received',ord(t))
240
if t == BITFIELD and c.got_anything:
242
print (c.ccount,'misplaced bitfield')
245
c.got_anything = True
246
if (t in [CHOKE, UNCHOKE, INTERESTED, NOT_INTERESTED] and
249
print (c.ccount,'bad message length')
253
c.download.got_choke()
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()
262
if len(message) != 5:
264
print (c.ccount,'bad message length')
267
i = toint(message[1:])
268
if i >= self.numpieces:
270
print (c.ccount,'bad piece number')
273
if c.download.got_have(i):
274
c.upload.got_not_interested()
277
b = Bitfield(self.numpieces, message[1:])
280
print (c.ccount,'bad bitfield')
283
if c.download.got_have_bitfield(b):
284
c.upload.got_not_interested()
286
if len(message) != 13:
288
print (c.ccount,'bad message length')
291
i = toint(message[1:5])
292
if i >= self.numpieces:
294
print (c.ccount,'bad piece number')
297
c.got_request(i, toint(message[5:9]),
300
if len(message) != 13:
302
print (c.ccount,'bad message length')
305
i = toint(message[1:5])
306
if i >= self.numpieces:
308
print (c.ccount,'bad piece number')
311
c.upload.got_cancel(i, toint(message[5:9]),
314
if len(message) <= 9:
316
print (c.ccount,'bad message length')
319
i = toint(message[1:5])
320
if i >= self.numpieces:
322
print (c.ccount,'bad piece number')
325
if c.download.got_piece(i, toint(message[5:9]), message[9:]):