rubytorrent 0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,536 @@
1
+ ## peer.rb -- bitttorrent peer ("wire") protocol.
2
+ ## Copyright 2004 William Morgan.
3
+ ##
4
+ ## This file is part of RubyTorrent. RubyTorrent is free software;
5
+ ## you can redistribute it and/or modify it under the terms of version
6
+ ## 2 of the GNU General Public License as published by the Free
7
+ ## Software Foundation.
8
+ ##
9
+ ## RubyTorrent is distributed in the hope that it will be useful, but
10
+ ## WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12
+ ## General Public License (in the file COPYING) for more details.
13
+
14
+ require 'socket'
15
+ require 'thread'
16
+ require "rubytorrent/message"
17
+
18
+ module RubyTorrent
19
+
20
+ module ArrayToBitstring
21
+ def to_bitstring
22
+ ret = "\0"
23
+ bit = 7
24
+ map do |b|
25
+ if bit == -1
26
+ ret += "\0"
27
+ bit = 7
28
+ end
29
+ ret[ret.length - 1] |= (1 << bit) if b
30
+ bit -= 1
31
+ end
32
+ ret
33
+ end
34
+ end
35
+
36
+ module ArrayDelete2
37
+ ## just like delete but returns the *array* element deleted rather
38
+ ## than the argument. someone should file an rcr.
39
+ def delete2(el)
40
+ i = index el
41
+ unless i.nil?
42
+ ret = self[i]
43
+ delete_at i
44
+ ret
45
+ else
46
+ nil
47
+ end
48
+ end
49
+ end
50
+
51
+ module StringToBarray
52
+ include StringMapBytes
53
+ def to_barray
54
+ self.map_bytes do |b|
55
+ (0 .. 7).map { |i| (b & (1 << (7 - i))) != 0 }
56
+ end.flatten
57
+ end
58
+ end
59
+
60
+ ## estimate a rate. basically copied from bram's code.
61
+ class RateMeter
62
+ attr_reader :amt
63
+
64
+ def initialize(window=20)
65
+ @window = window.to_f
66
+ @amt = 0
67
+ @rate = 0
68
+ @last = @since = Time.now - 1
69
+ @m = Mutex.new
70
+ end
71
+
72
+ def add(new_amt)
73
+ now = Time.now
74
+ @m.synchronize do
75
+ @amt += new_amt
76
+ @rate = ((@rate * (@last - @since)) + new_amt).to_f / (now - @since)
77
+ @last = now
78
+ @since = [@since, now - @window].max
79
+ end
80
+ end
81
+
82
+ def rate
83
+ (@rate * (@last - @since)).to_f / (Time.now - @since)
84
+ end
85
+
86
+ def bytes_until(new_rate)
87
+ [(new_rate.to_f * (Time.now - @since)) - (@rate * (@last - @since)), 0].max
88
+ end
89
+ end
90
+
91
+ class ProtocolError < StandardError; end
92
+
93
+ ## The PeerConnection object deals with all the protocol issues. It
94
+ ## keeps state information as to the connection and the peer. It is
95
+ ## tightly integrated with the Controller object.
96
+ ##
97
+ ## Remember to be "strict in what you send, lenient in what you
98
+ ## accept".
99
+ class PeerConnection
100
+ extend AttrReaderQ
101
+ include EventSource
102
+
103
+ attr_reader :peer_pieces, :name
104
+ attr_reader_q :running, :choking, :interested, :peer_choking,
105
+ :peer_interested, :snubbing
106
+ event :peer_has_piece, :peer_has_pieces, :received_block, :sent_block,
107
+ :requested_block
108
+
109
+ BUFSIZE = 8192
110
+ MAX_PEER_REQUESTS = 5 # how many peer requests to keep queued
111
+ MAX_REQUESTS = 5 # how many requests for blocks to keep current
112
+ MIN_REQUESTS = 1 # get more blocks from controller when this limit is reached
113
+ REQUEST_TIMEOUT = 60 # number of seconds after sending a request before we
114
+ # decide it's been forgotten
115
+
116
+ def initialize(name, controller, socket, package)
117
+ @name = name
118
+ @controller = controller
119
+ @socket = socket
120
+ @package = package
121
+ @running = false
122
+
123
+ ## my state
124
+ @want_blocks = [].extend(ArrayDelete2) # blocks i want
125
+ @want_blocks_m = Mutex.new
126
+ @choking = true
127
+ @interested = false
128
+ @snubbing = false
129
+
130
+ ## peer's state
131
+ @peer_want_blocks = [].extend(ArrayDelete2)
132
+ @peer_choking = true # assumption of initial condition
133
+ @peer_interested = false # ditto
134
+ @peer_pieces = Array.new(@package.num_pieces, false) # ditto
135
+ @peer_virgin = true # does the peer have any pieces at all?
136
+
137
+ ## connection stats
138
+ @dlmeter = RateMeter.new
139
+ @ulmeter = RateMeter.new
140
+
141
+ @send_q = Queue.new # output thread takes messages from here and
142
+ # puts them on the wire
143
+ end
144
+
145
+ def pending_recv; @want_blocks.find_all { |b| b.requested? }.length; end
146
+ def pending_send; @peer_want_blocks.length; end
147
+
148
+ def start
149
+ @running = true
150
+ @time = {:start => Time.now}
151
+
152
+ Thread.new do # start input thread
153
+ begin
154
+ while @running; input_thread_step; end
155
+ rescue SystemCallError, IOError, ProtocolError => e
156
+ rt_debug "#{self} (input): #{e.message}, releasing #{@want_blocks.length} claimed blocks and dying"
157
+ # rt_debug e.backtrace.join("\n")
158
+ @running = false
159
+ @controller.forget_blocks @want_blocks
160
+ end
161
+ end
162
+
163
+ Thread.new do # start output thread
164
+ begin
165
+ while @running; output_thread_step; end
166
+ rescue SystemCallError, IOError, ProtocolError => e
167
+ rt_debug "#{self} (output): #{e.message}, releasing #{@want_blocks.length} claimed blocks and dying"
168
+ # rt_debug e.backtrace.join("\n")
169
+ @running = false
170
+ @controller.forget_blocks @want_blocks
171
+ end
172
+ end
173
+
174
+ ## queue the initial messages
175
+ queue_message(:bitfield, {:bitfield => @package.pieces.map { |p| p.complete? }.extend(ArrayToBitstring).to_bitstring})
176
+
177
+ ## and that's it. if peer sends a bitfield, we'll send an
178
+ ## interested and start requesting blocks at that point. if they
179
+ ## don't, it means they don't have any pieces, so we can just sit
180
+ ## tight.
181
+ self
182
+ end
183
+
184
+ ## the Controller calls this from heartbeat thread to tell us
185
+ ## whether to choke or not.
186
+ def choke=(now_choke)
187
+ queue_message(now_choke ? :choke : :unchoke) unless @choking == now_choke
188
+ @choking = now_choke
189
+ end
190
+
191
+ ## the Controller calls this from heartbeat thread to tell us
192
+ ## whether to snub or not.
193
+ def snub=(now_snub)
194
+ unless @snubbing = now_snub
195
+ @snubbing = now_snub
196
+ choke = true if @snubbing
197
+ end
198
+ end
199
+
200
+ def peer_complete?; @peer_pieces.all?; end
201
+ def last_send_time; @time[:send]; end
202
+ def last_recv_time; @time[:recv]; end
203
+ def last_send_block_time; @time[:send_block]; end
204
+ def last_recv_block_time; @time[:recv_block]; end
205
+ def start_time; @time[:start]; end
206
+ def dlrate; @dlmeter.rate; end
207
+ def ulrate; @ulmeter.rate; end
208
+ def dlamt; @dlmeter.amt; end
209
+ def ulamt; @ulmeter.amt; end
210
+ def piece_available?(index); @peer_pieces[index]; end
211
+ def to_s; "<peer: #@name>"; end
212
+
213
+ ## called by Controller in the event that a request needs to be
214
+ ## rescinded.
215
+ def cancel(block)
216
+ wblock = @want_blocks_m.synchronize { @want_blocks.delete2 block }
217
+ unless wblock.nil? || !wblock.requested?
218
+ rt_debug "#{self}: sending cancel for #{wblock}"
219
+ queue_message(:cancel, {:index => wblock.pindex, :begin => wblock.begin,
220
+ :length => wblock.length})
221
+ end
222
+ get_want_blocks unless wblock.nil?
223
+ end
224
+
225
+ def shutdown
226
+ rt_debug "#{self.to_s}: shutting down"
227
+ @running = false
228
+ @socket.close rescue nil
229
+ end
230
+
231
+ ## Controller calls this to tell us that a complete piece has been
232
+ ## received.
233
+ def have_piece(piece)
234
+ queue_message(:have, {:index => piece.index})
235
+ end
236
+
237
+ ## Controller calls this to tell us to send a keepalive
238
+ def send_keepalive
239
+ # rt_debug "* sending keepalive!"
240
+ queue_message(:keepalive)
241
+ end
242
+
243
+ ## this is called both by input_thread_step and by the controller's
244
+ ## heartbeat thread. it sends as many pending blocks as it can while
245
+ ## keeping the amount below 'ullim', and sends as many requests as
246
+ ## it can while keeping the amount below 'dllim'.
247
+ ##
248
+ ## returns the number of bytes requested and sent
249
+ def send_blocks_and_reqs(dllim=nil, ullim=nil)
250
+ sent_bytes = 0
251
+ reqd_bytes = 0
252
+
253
+ @want_blocks_m.synchronize do
254
+ @want_blocks.each do |b|
255
+ # puts "[][] #{self}: #{b} is #{b.requested? ? 'requested' : 'NOT requested'} and has time_elapsed of #{b.requested? ? b.time_elapsed.round : 'n/a'}s"
256
+ if b.requested? && (b.time_elapsed > REQUEST_TIMEOUT)
257
+ rt_warning "#{self}: for block #{b}, time elapsed since request is #{b.time_elapsed} > #{REQUEST_TIMEOUT}, assuming peer forgot about it"
258
+ @want_blocks.delete b
259
+ @controller.forget_blocks [b]
260
+ end
261
+ end
262
+ end
263
+
264
+ ## send :requests
265
+ unless @peer_choking || !@interested
266
+ @want_blocks_m.synchronize do
267
+ @want_blocks.each do |b|
268
+ break if dllim && (reqd_bytes >= dllim)
269
+ next if b.requested?
270
+
271
+ if @package.pieces[b.pindex].complete?
272
+ # not sure that this will ever happen, but...
273
+ rt_warning "#{self}: deleting scheduled block for already-complete piece #{b}"
274
+ @want_blocks.delete b
275
+ next
276
+ end
277
+
278
+ queue_message(:request, {:index => b.pindex, :begin => b.begin,
279
+ :length => b.length})
280
+ reqd_bytes += b.length
281
+ b.requested = true
282
+ b.mark_time
283
+ send_event(:requested_block, b)
284
+ end
285
+ end
286
+ end
287
+
288
+ ## send blocks
289
+ # rt_debug "sending blocks. choking? #@choking, choked? #@peer_choking, ul rate #{ulrate}b/s, limit #@ulmeterlim" unless @peer_want_blocks.empty?
290
+ unless @choking || !@peer_interested
291
+ while !@peer_want_blocks.empty?
292
+ break if ullim && (sent_bytes >= ullim)
293
+ if (b = @peer_want_blocks.shift)
294
+ sent_bytes += b.length
295
+ @send_q.push b
296
+ @time[:send_block] = Time.now
297
+ send_event(:sent_block, b)
298
+ end
299
+ end
300
+ end
301
+
302
+ get_want_blocks
303
+
304
+ [reqd_bytes, sent_bytes]
305
+ end
306
+
307
+ private
308
+
309
+ ## re-calculate whether we're interested or not. triggered by
310
+ ## received :have and :bitfield messages.
311
+ def recalc_interested
312
+ show_interest = !@peer_virgin || (@package.pieces.detect do |p|
313
+ !p.complete? && @peer_pieces[p.index]
314
+ end) != nil
315
+
316
+ queue_message(show_interest ? :interested : :uninterested) unless show_interest == @interested
317
+ if ((@interested = show_interest) == false)
318
+ @want_blocks_m.synchronize do
319
+ @controller.forget_blocks @want_blocks
320
+ @want_blocks.clear
321
+ end
322
+ end
323
+ end
324
+
325
+ ## take a message/block from the send_q and place it on the wire. blocking.
326
+ def output_thread_step
327
+ obj = @send_q.deq
328
+ case obj
329
+ when Message
330
+ # rt_debug "output: sending message #{obj}" + (obj.id == :request ? " (request queue size #{@want_blocks.length})" : "")
331
+ send_bytes obj.to_wire_form
332
+ @time[:send] = Time.now
333
+ when Block
334
+ # rt_debug "output: sending block #{obj}"
335
+ send_bytes Message.new(:piece, {:length => obj.length, :index => obj.pindex, :begin => obj.begin}).to_wire_form
336
+ obj.each_chunk(BUFSIZE) { |c| send_bytes c }
337
+ @time[:send] = Time.now
338
+ @ulmeter.add obj.length
339
+ # rt_debug "sent block #{obj} ul rate now #{(ulrate / 1024.0).round}kb/s"
340
+ else
341
+ raise "don't know what to do with #{obj}"
342
+ end
343
+ end
344
+
345
+ ## take bits from the wire and respond to them. blocking.
346
+ def input_thread_step
347
+ case (obj = read_from_wire)
348
+ when Block
349
+ handle_block obj
350
+ when Message
351
+ handle_message obj
352
+ else
353
+ raise "don't know what to do with #{obj.inspect}"
354
+ end
355
+
356
+ ## to enable immediate response, if there are no rate limits,
357
+ ## we'll send the blocks and reqs right here. otherwise, the
358
+ ## controller will call this at intervals.
359
+ send_blocks_and_reqs if @controller.dlratelim.nil? && @controller.ulratelim.nil?
360
+ end
361
+
362
+ ## take bits from the wire and make a message/block out of them. blocking.
363
+ def read_from_wire
364
+ len = nil
365
+ while (len = recv_bytes(4).from_fbbe) == 0
366
+ @time[:recv] = Time.now
367
+ # rt_debug "* hey, a keepalive!"
368
+ end
369
+
370
+ id = recv_bytes(1)[0]
371
+
372
+ if Message::WIRE_IDS[id] == :piece # add a block
373
+ len -= 9
374
+ m = Message.from_wire_form(id, recv_bytes(8))
375
+ b = Block.new(m.index, m.begin, len)
376
+ while len > 0
377
+ thislen = [BUFSIZE, len].min
378
+ b.add_chunk recv_bytes(thislen)
379
+ len -= thislen
380
+ end
381
+ @time[:recv] = @time[:recv_block] = Time.now
382
+ b
383
+ else # add a message
384
+ m = Message.from_wire_form(id, recv_bytes(len - 1))
385
+ # rt_debug "input: read message #{m}"
386
+ @time[:recv] = Time.now
387
+ m
388
+ end
389
+ end
390
+
391
+ def handle_block(block)
392
+ wblock = @want_blocks_m.synchronize { @want_blocks.delete2 block }
393
+
394
+ return rt_warning("#{self}: peer sent unrequested (possibly cancelled) block #{block}") if wblock.nil? || !wblock.requested?
395
+
396
+ @dlmeter.add block.have_length
397
+ # rt_debug "received block #{block}, dl rate now #{(dlrate / 1024.0).round}kb/s"
398
+
399
+ piece = @package.pieces[block.pindex] # find corresponding piece
400
+ piece.add_block block
401
+ send_event(:received_block, block)
402
+ get_want_blocks
403
+ end
404
+
405
+ def send_bytes(s)
406
+ if s.nil?
407
+ raise "can't send nil"
408
+ elsif s.length > 0
409
+ @socket.send(s, 0)
410
+ end
411
+ end
412
+
413
+ def recv_bytes(len)
414
+ if len < 0
415
+ raise "can't recv negative bytes"
416
+ elsif len == 0
417
+ ""
418
+ elsif len > 512 * 1024 # 512k
419
+ raise ProtocolError, "read size too big."
420
+ else
421
+ r = ""
422
+ zeros = 0
423
+ while r.length < len
424
+ x = @socket.recv(len - r.length)
425
+ raise IOError, "zero bytes received" if x.length == 0
426
+ r += x
427
+ end
428
+ r
429
+ end
430
+ end
431
+
432
+ def handle_message(m)
433
+ case m.id
434
+ when :choke
435
+ # rt_debug "#{self}: peer choking (was #{@peer_choking})"
436
+ @peer_choking = true
437
+ @want_blocks_m.synchronize do
438
+ @controller.forget_blocks @want_blocks
439
+ @want_blocks.clear
440
+ end
441
+
442
+ when :unchoke
443
+ # rt_debug "#{self}: peer not choking (was #{@peer_choking})"
444
+ @peer_choking = false
445
+
446
+ when :interested
447
+ # rt_debug "peer interested (was #{@peer_interested})"
448
+ @peer_interested = true
449
+
450
+ when :uninterested
451
+ # rt_debug "peer not interested (was #{@peer_interested})"
452
+ @peer_interested = false
453
+
454
+ when :have
455
+ # rt_debug "peer has piece #{m.index}"
456
+ rt_warning "#{self}: peer already has piece #{m.index}" if @peer_pieces[m.index]
457
+ @peer_pieces[m.index] = true
458
+ @peer_virgin = false
459
+ send_event(:peer_has_piece, m)
460
+ recalc_interested
461
+
462
+ when :bitfield
463
+ # rt_debug "peer reports bitfield #{m.bitfield.inspect}"
464
+ barray = m.bitfield.extend(StringToBarray).to_barray
465
+
466
+ expected_pieces = @package.num_pieces - (@package.num_pieces % 8) + ((@package.num_pieces % 8) == 0 ? 0 : 8)
467
+ raise ProtocolError, "invalid length in bitfield message (package has #{@package.num_pieces} pieces; bitfield should be size #{expected_pieces} but is #{barray.length} pieces)" unless barray.length == expected_pieces
468
+
469
+ @peer_pieces.each_index { |i| @peer_pieces[i] = barray[i] }
470
+ @peer_virgin = false
471
+ send_event(:peer_has_pieces, barray)
472
+ recalc_interested
473
+ get_want_blocks
474
+
475
+ when :request
476
+ return rt_warning("#{self}: peer requests invalid piece #{m.index}") unless m.index < @package.num_pieces
477
+ return rt_warning("#{self}: peer requests a block but we're choking") if @choking
478
+ return rt_warning("#{self}: peer requests a block but isn't interested") unless @peer_interested
479
+ return rt_warning("#{self}: peer requested too many blocks, ignoring") if @peer_want_blocks.length > MAX_PEER_REQUESTS
480
+
481
+ piece = @package.pieces[m.index]
482
+ return rt_warning("#{self}: peer requests unavailable block from piece #{piece}") unless piece.complete?
483
+
484
+ @peer_want_blocks.push piece.get_complete_block(m.begin, m.length)
485
+
486
+ when :piece
487
+ raise "can't handle piece here"
488
+
489
+ when :cancel
490
+ b = Block.new(m.index, m.begin, m.length)
491
+ # rt_debug "peer cancels #{b}"
492
+ if @peer_want_blocks.delete2(b) == nil
493
+ rt_warning "#{self}: peer wants to cancel unrequested block #{b}"
494
+ end
495
+
496
+ else
497
+ raise "unknown message #{type}"
498
+ end
499
+ end
500
+
501
+ ## queues a message for delivery. (for :piece messages, this
502
+ ## transmits everything but the piece itself)
503
+ def queue_message(id, args=nil)
504
+ @send_q.push Message.new(id, args)
505
+ end
506
+
507
+ ## talks to Controller and get some new blocks to request. could be
508
+ ## slow. this is presumably called whenever the queue of requests is
509
+ ## too small.
510
+ def get_want_blocks
511
+ return if (@want_blocks.length >= MIN_REQUESTS) || @peer_virgin || @peer_choking || !@interested
512
+
513
+ rej_count = 0
514
+ acc_count = 0
515
+ @controller.claim_blocks do |b|
516
+ break if @want_blocks.length >= MAX_REQUESTS
517
+ if @peer_pieces[b.pindex] && !@want_blocks.member?(b)
518
+ rt_debug "! #{self}: starting new piece #{@package.pieces[b.pindex]}" unless @package.pieces[b.pindex].started?
519
+
520
+ # rt_debug "#{self}: added to queue block #{b}"
521
+ # puts "#{self}: claimed block #{b}"
522
+ @want_blocks.push b
523
+ acc_count += 1
524
+ true
525
+ else
526
+ # puts "#{self}: cont offers block #{b} but peer has? #{@peer_pieces[b.pindex]} i already want? #{@want_blocks.member? b}" if rej_count < 10
527
+ rej_count += 1
528
+ false
529
+ end
530
+ end
531
+ # puts "#{self}: ... and #{rej_count} more (peer has #{@peer_pieces.inject(0) { |s, p| s + (p ? 1 : 0) }} pieces)... " if rej_count >= 10
532
+ # puts "#{self}: accepted #{acc_count} blocks, rejected #{rej_count} blocks"
533
+ end
534
+ end
535
+
536
+ end