distribustream 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +13 -0
- data/README +3 -7
- data/Rakefile +2 -2
- data/distribustream.gemspec +18 -9
- data/lib/pdtp/client/callbacks.rb +4 -1
- data/lib/pdtp/common.rb +1 -1
- data/lib/pdtp/common/http_server.rb +5 -0
- data/lib/pdtp/common/protocol.rb +1 -8
- data/lib/pdtp/server/bandwidth_estimator.rb +148 -0
- data/lib/pdtp/server/chunk_info.rb +97 -0
- data/lib/pdtp/server/connection.rb +102 -3
- data/lib/pdtp/server/dispatcher.rb +82 -210
- data/lib/pdtp/server/file_service.rb +5 -4
- data/lib/pdtp/server/status_helper.rb +19 -3
- data/lib/pdtp/server/transfer.rb +26 -14
- data/lib/pdtp/server/transfer_manager.rb +146 -0
- data/status/index.erb +31 -17
- data/status/stylesheets/style.css +1 -1
- metadata +33 -30
- data/lib/pdtp/server/client_info.rb +0 -154
data/CHANGES
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
Version 0.5.0
|
2
|
+
|
3
|
+
* Factor traffic routing code into PDTP::Server::TransferManager
|
4
|
+
|
5
|
+
* Cleanup transfer reporting and factor into PDTP::Server::Transfer
|
6
|
+
|
7
|
+
* Add bandwidth estimator class and integrate into transfer reporting
|
8
|
+
|
9
|
+
* Add bandwidth estimates to the status page with associated helpers
|
10
|
+
|
11
|
+
* Eliminate the ClientInfo class and factor all ClientInfo-related code
|
12
|
+
into the PDTP::Server::Connection class
|
13
|
+
|
1
14
|
Version 0.4.1
|
2
15
|
* Add -v / --version flags to dsclient and dstream
|
3
16
|
|
data/README
CHANGED
@@ -1,11 +1,9 @@
|
|
1
|
-
Welcome to DistribuStream!
|
1
|
+
Welcome to DistribuStream! (http://distribustream.org)
|
2
2
|
|
3
3
|
DistribuStream is a fully open peercasting system which allows on-demand
|
4
4
|
or live streaming media to be delivered at a fraction of the normal cost.
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
Usage:
|
6
|
+
USAGE:
|
9
7
|
|
10
8
|
The DistribuStream gem includes three config files that can be located in
|
11
9
|
the conf directory of the gem:
|
@@ -47,9 +45,7 @@ media as it downloads, you can:
|
|
47
45
|
|
48
46
|
dsclient -o pdtp://myserver.url/file.ext | mediaplayer -
|
49
47
|
|
50
|
-
|
51
|
-
|
52
|
-
Development Roadmap:
|
48
|
+
ROADMAP:
|
53
49
|
|
54
50
|
Short-term goals focus on improving the efficiency of peer-to-peer traffic
|
55
51
|
routing, by incorporating all of the following constraints:
|
data/Rakefile
CHANGED
@@ -10,10 +10,10 @@ task :default => :rdoc
|
|
10
10
|
Rake::RDocTask.new(:rdoc) do |task|
|
11
11
|
task.rdoc_dir = 'doc'
|
12
12
|
task.title = 'DistribuStream'
|
13
|
+
task.options = %w(--title PDTP --main README --line-numbers)
|
13
14
|
task.rdoc_files.include('bin/**/*.rb')
|
14
15
|
task.rdoc_files.include('lib/**/*.rb')
|
15
|
-
task.rdoc_files.include('
|
16
|
-
task.rdoc_files.include('test/**/*.rb')
|
16
|
+
task.rdoc_files.include('README')
|
17
17
|
end
|
18
18
|
|
19
19
|
# Gem
|
data/distribustream.gemspec
CHANGED
@@ -2,19 +2,28 @@ require 'rubygems'
|
|
2
2
|
|
3
3
|
GEMSPEC = Gem::Specification.new do |s|
|
4
4
|
s.name = "distribustream"
|
5
|
-
s.version = "0.
|
6
|
-
s.date = "2008-11-15"
|
7
|
-
s.summary = "DistribuStream is a fully open peercasting system allowing on-demand or live streaming media to be delivered at a fraction of the normal cost"
|
8
|
-
s.email = "tony@clickcaster.com"
|
9
|
-
s.homepage = "http://distribustream.org"
|
10
|
-
s.rubyforge_project = "distribustream"
|
11
|
-
s.has_rdoc = true
|
12
|
-
s.rdoc_options = ["--exclude", "definitions", "--exclude", "indexes"]
|
13
|
-
s.extra_rdoc_files = ["COPYING", "README", "CHANGES", "pdtp-specification.xml"]
|
5
|
+
s.version = "0.5.0"
|
14
6
|
s.authors = ["Tony Arcieri", "Ashvin Mysore", "Galen Pahlke", "James Sanders", "Tom Stapleton"]
|
7
|
+
s.email = "tony@clickcaster.com"
|
8
|
+
s.date = "2008-11-27"
|
9
|
+
s.summary = "DistribuStream is a fully open peercasting system allowing on-demand or live streaming media to be delivered at a fraction of the normal cost"
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
|
12
|
+
# Gem contents
|
15
13
|
s.files = Dir.glob("{bin,lib,conf,status}/**/*") + ['Rakefile', 'distribustream.gemspec']
|
16
14
|
s.executables = %w{dstream dsclient}
|
15
|
+
|
16
|
+
# Dependencies
|
17
17
|
s.add_dependency("eventmachine", ">= 0.9.0")
|
18
18
|
s.add_dependency("mongrel", ">= 1.0.2")
|
19
19
|
s.add_dependency("json", ">= 1.1.0")
|
20
|
+
|
21
|
+
# RubyForge info
|
22
|
+
s.homepage = "http://distribustream.org"
|
23
|
+
s.rubyforge_project = "distribustream"
|
24
|
+
|
25
|
+
# RDoc settings
|
26
|
+
s.has_rdoc = true
|
27
|
+
s.rdoc_options = %w(--title PDTP --main README --line-numbers)
|
28
|
+
s.extra_rdoc_files = ["COPYING", "README", "CHANGES", "pdtp-specification.xml"]
|
20
29
|
end
|
@@ -26,12 +26,15 @@ module PDTP
|
|
26
26
|
class Callbacks
|
27
27
|
attr_accessor :client
|
28
28
|
|
29
|
-
def initialize(*args)
|
29
|
+
def initialize(*args) # :nodoc:
|
30
30
|
end
|
31
31
|
|
32
|
+
# Callback fired when a client has successfully connected to a server
|
32
33
|
def connected(client)
|
33
34
|
end
|
34
35
|
|
36
|
+
# Callback fired when a client has been disconnected from a server
|
37
|
+
# Also fired if the connection attempt to a server fails
|
35
38
|
def disconnected(client)
|
36
39
|
client.stop
|
37
40
|
end
|
data/lib/pdtp/common.rb
CHANGED
@@ -22,7 +22,7 @@
|
|
22
22
|
|
23
23
|
# Namespace for all PDTP components
|
24
24
|
module PDTP
|
25
|
-
PDTP::VERSION = '0.
|
25
|
+
PDTP::VERSION = '0.5.0' unless defined? PDTP::VERSION
|
26
26
|
def self.version() VERSION end
|
27
27
|
|
28
28
|
PDTP::DEFAULT_PORT = 6086 unless defined? PDTP::DEFAULT_PORT
|
@@ -1,8 +1,13 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C)2007 Kirk Haines
|
3
|
+
# Licensed under the Ruby License. See http://www.ruby-lang.org/en/LICENSE.txt
|
4
|
+
#
|
1
5
|
# This module rewrites pieces of the very good Mongrel web server in
|
2
6
|
# order to change it from a threaded application to an event based
|
3
7
|
# application running inside an EventMachine event loop. It should
|
4
8
|
# be compatible with the existing Mongrel handlers for Rails,
|
5
9
|
# Camping, Nitro, etc....
|
10
|
+
#++
|
6
11
|
|
7
12
|
require 'rubygems'
|
8
13
|
require 'eventmachine'
|
data/lib/pdtp/common/protocol.rb
CHANGED
@@ -51,11 +51,6 @@ module PDTP
|
|
51
51
|
@connection_open
|
52
52
|
end
|
53
53
|
|
54
|
-
def initialize(*args)
|
55
|
-
user_data = nil
|
56
|
-
super
|
57
|
-
end
|
58
|
-
|
59
54
|
#called by EventMachine after a connection has been established
|
60
55
|
def post_init
|
61
56
|
# a cache of the peer info because eventmachine seems to drop it before we want
|
@@ -72,8 +67,6 @@ module PDTP
|
|
72
67
|
connection_created if respond_to? :connection_created
|
73
68
|
end
|
74
69
|
|
75
|
-
attr_accessor :user_data #users of this class may store arbitrary data here
|
76
|
-
|
77
70
|
#close a connection, but first send the specified error message
|
78
71
|
def error_close_connection(error)
|
79
72
|
if PROTOCOL_DEBUG
|
@@ -86,7 +79,7 @@ module PDTP
|
|
86
79
|
|
87
80
|
#debug routine: returns id of remote peer on this connection
|
88
81
|
def remote_peer_id
|
89
|
-
ret =
|
82
|
+
ret = client_info.client_id rescue nil
|
90
83
|
ret || 'NOID'
|
91
84
|
end
|
92
85
|
|
@@ -0,0 +1,148 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# This source file is distributed as part of the
|
18
|
+
# DistribuStream file transfer system.
|
19
|
+
#
|
20
|
+
# See http://distribustream.org/
|
21
|
+
#++
|
22
|
+
|
23
|
+
module PDTP
|
24
|
+
class Server
|
25
|
+
# BandwidthEstimator logs the durations of chunk transfers over time,
|
26
|
+
# allowing the server to estimate the bandwidth of individual peers
|
27
|
+
class BandwidthEstimator
|
28
|
+
# Maximum number of transfers to store in a given history object
|
29
|
+
MAXSIZE = 100 unless defined? MAXSIZE
|
30
|
+
|
31
|
+
# Inner class for storing a transfer
|
32
|
+
class Transfer
|
33
|
+
attr_reader :start_time, :end_time, :rate
|
34
|
+
|
35
|
+
def initialize(start_time, end_time, bytes_transferred)
|
36
|
+
# Avoid division by zero or negative transfer rates
|
37
|
+
raise ArgumentError, "end_time must exceed start_time" unless end_time > start_time
|
38
|
+
|
39
|
+
@start_time, @end_time = start_time, end_time
|
40
|
+
|
41
|
+
# Calculate transfer rate in bytes per second
|
42
|
+
@rate = (bytes_transferred / (end_time - start_time)).to_i
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
@transfers = []
|
48
|
+
|
49
|
+
# Clear caches of all calculated values
|
50
|
+
clear_caches
|
51
|
+
end
|
52
|
+
|
53
|
+
# Log when a transfer began, ended, and how much data was transferred
|
54
|
+
def log(start_time, end_time, bytes_transferred)
|
55
|
+
# Remove an old transfer if we've exceeded MAXSIZE
|
56
|
+
@transfers.shift if @transfers.size >= MAXSIZE
|
57
|
+
|
58
|
+
# Clear caches of all calculated values because the sample has changed
|
59
|
+
clear_caches
|
60
|
+
|
61
|
+
# Log the transfer
|
62
|
+
@transfers << Transfer.new(start_time, end_time, bytes_transferred)
|
63
|
+
|
64
|
+
nil
|
65
|
+
end
|
66
|
+
|
67
|
+
# Compute an estimate of the bandwidth, culling statistical outliers
|
68
|
+
def estimate
|
69
|
+
return @estimate unless @estimate.nil?
|
70
|
+
|
71
|
+
# Prune outliers beyond 2 standard deviations of the mean
|
72
|
+
# FIXME maybe this should be 3 standard deviations?
|
73
|
+
pruned = estimates.select { |n| (n - estimates_mean).abs < standard_deviation * 2}
|
74
|
+
|
75
|
+
# Avoid dividing by zero
|
76
|
+
return 0 if pruned.size.zero?
|
77
|
+
|
78
|
+
# Calculate the mean of the pruned estimates
|
79
|
+
@estimate = pruned.inject(0) { |a, v| a + v } / pruned.size
|
80
|
+
end
|
81
|
+
|
82
|
+
#########
|
83
|
+
protected
|
84
|
+
#########
|
85
|
+
|
86
|
+
# Retrieve an array of bandwidth estimates over time
|
87
|
+
# FIXME Some O(n^2) nastiness... could definitely be improved
|
88
|
+
def estimates
|
89
|
+
# Generate a list of transfers which overlap at transfer endpoints
|
90
|
+
overlapping_transfers = @transfers.map do |t1|
|
91
|
+
@transfers.inject([]) do |array, t2|
|
92
|
+
array << t2.rate if (t2.start_time..t2.end_time).include? t1.end_time
|
93
|
+
array
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Sum the overlapping transfer rates and store them as estimates
|
98
|
+
@estimates ||= overlapping_transfers.map { |t| t.inject(0) { |a,v| a + v } }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Compute the mean of the bandwidth estimates
|
102
|
+
def estimates_mean
|
103
|
+
@mean ||= estimates.inject(0) { |a, v| a + v } / estimates.size
|
104
|
+
end
|
105
|
+
|
106
|
+
# Compute the variance of the bandwidth estimates
|
107
|
+
def variance
|
108
|
+
# Return cached value if available
|
109
|
+
return @variance unless @variance.nil?
|
110
|
+
|
111
|
+
mean = 0.0
|
112
|
+
s = 0.0
|
113
|
+
|
114
|
+
estimates.each_with_index do |rate, n|
|
115
|
+
delta = rate - mean
|
116
|
+
mean += delta / (n + 1)
|
117
|
+
s += delta * (rate - mean)
|
118
|
+
end
|
119
|
+
|
120
|
+
@mean = mean
|
121
|
+
@variance = s / estimates.size
|
122
|
+
end
|
123
|
+
|
124
|
+
# Compute the standard deviation of the bandwidth estimates
|
125
|
+
def standard_deviation
|
126
|
+
@std ||= Math.sqrt variance
|
127
|
+
end
|
128
|
+
|
129
|
+
# Clear all cached values for transfer statistics
|
130
|
+
def clear_caches
|
131
|
+
# Array of bandwidth estimates sampled at transfer completions
|
132
|
+
@estimates = nil
|
133
|
+
|
134
|
+
# Mean of the bandwidth estimates
|
135
|
+
@mean = nil
|
136
|
+
|
137
|
+
# Variance of @estimates
|
138
|
+
@variance = nil
|
139
|
+
|
140
|
+
# Standard deviation of @estimates
|
141
|
+
@std = nil
|
142
|
+
|
143
|
+
# Final estimate (arithmetic mean) after outliers have been culled from @estimates
|
144
|
+
@estimate = nil
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
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 3 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, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
# This source file is distributed as part of the
|
18
|
+
# DistribuStream file transfer system.
|
19
|
+
#
|
20
|
+
# See http://distribustream.org/
|
21
|
+
#++
|
22
|
+
|
23
|
+
module PDTP
|
24
|
+
class Server
|
25
|
+
# Stores information about the chunks requested or provided by a client
|
26
|
+
class ChunkInfo
|
27
|
+
def initialize
|
28
|
+
@files = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
#each chunk can either be provided, requested, transfer, or none
|
32
|
+
#FIXME some metaprogramming is probably in order here
|
33
|
+
def provide(filename,range); set(filename, range, :provided); end
|
34
|
+
def unprovide(filename,range); set(filename,range, :none); end
|
35
|
+
def request(filename,range); set(filename,range, :requested); end
|
36
|
+
def unrequest(filename,range); set(filename,range, :none); end
|
37
|
+
def transfer(filename,range); set(filename,range, :transfer); end
|
38
|
+
|
39
|
+
def provided?(filename,chunk); get(filename,chunk) == :provided; end
|
40
|
+
def requested?(filename,chunk); get(filename,chunk) == :requested; end
|
41
|
+
|
42
|
+
#returns a high priority requested chunk
|
43
|
+
def high_priority_chunk
|
44
|
+
#right now return any chunk
|
45
|
+
@files.each do |name,file|
|
46
|
+
file.each_index do |i|
|
47
|
+
return [name, i] if file[i] == :requested
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
class FileStats
|
55
|
+
attr_accessor :file_chunks, :chunks_requested, :url
|
56
|
+
attr_accessor :chunks_provided, :chunks_transferring
|
57
|
+
|
58
|
+
def initialize
|
59
|
+
@url = ""
|
60
|
+
@file_chunks = 0
|
61
|
+
@chunks_requested = 0
|
62
|
+
@chunks_provided = 0
|
63
|
+
@chunks_transferring = 0
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns an array of FileStats objects for debug output
|
68
|
+
def get_file_stats
|
69
|
+
@files.map do |name, file|
|
70
|
+
fs = FileStats.new
|
71
|
+
fs.file_chunks = file.size
|
72
|
+
fs.url = name
|
73
|
+
file.each do |chunk|
|
74
|
+
fs.chunks_requested += 1 if chunk == :requested
|
75
|
+
fs.chunks_provided += 1 if chunk == :provided
|
76
|
+
fs.chunks_transferring += 1 if chunk == :transfer
|
77
|
+
end
|
78
|
+
|
79
|
+
fs
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
#########
|
84
|
+
protected
|
85
|
+
#########
|
86
|
+
|
87
|
+
def get(filename,chunk)
|
88
|
+
@files[filename][chunk] rescue :neither
|
89
|
+
end
|
90
|
+
|
91
|
+
def set(filename,range,state)
|
92
|
+
chunks=@files[filename]||=Array.new
|
93
|
+
range.each { |i| chunks[i]=state }
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -21,13 +21,108 @@
|
|
21
21
|
#++
|
22
22
|
|
23
23
|
require File.dirname(__FILE__) + '/../common/protocol'
|
24
|
+
require File.dirname(__FILE__) + '/chunk_info'
|
25
|
+
require File.dirname(__FILE__) + '/trust'
|
26
|
+
require File.dirname(__FILE__) + '/bandwidth_estimator'
|
24
27
|
|
25
28
|
module PDTP
|
26
29
|
class Server
|
30
|
+
# Server's internal representation of a client connection
|
27
31
|
class Connection < PDTP::Protocol
|
28
|
-
|
29
|
-
|
32
|
+
# Handle to the dispatcher which can hopefully be eliminated in future versions
|
33
|
+
# of EventMachine
|
34
|
+
attr_writer :dispatcher
|
30
35
|
|
36
|
+
# Accessors which can hopefully be eliminated in future versions
|
37
|
+
attr_accessor :chunk_info, :trust
|
38
|
+
attr_accessor :listen_port, :client_id
|
39
|
+
attr_accessor :transfers
|
40
|
+
|
41
|
+
def initialize(*args)
|
42
|
+
# Information about what chunks the client is requesting/providing
|
43
|
+
@chunk_info = ChunkInfo.new
|
44
|
+
|
45
|
+
# Chunk transfers the client is actively participating in
|
46
|
+
@transfers = Hash.new
|
47
|
+
|
48
|
+
# Port the client is listening on
|
49
|
+
@listen_port = 6000 #default
|
50
|
+
|
51
|
+
# Trust relatipnships with other peers
|
52
|
+
@trust = Trust.new
|
53
|
+
|
54
|
+
# Bandwidth estimators
|
55
|
+
@upstream = BandwidthEstimator.new
|
56
|
+
@downstream = BandwidthEstimator.new
|
57
|
+
|
58
|
+
super
|
59
|
+
end
|
60
|
+
|
61
|
+
# Log a completed transfer and update internal data
|
62
|
+
def success(transfer)
|
63
|
+
if transfer.taker == self
|
64
|
+
@chunk_info.provide transfer.url, transfer.chunkid..transfer.chunkid
|
65
|
+
@trust.success transfer.giver.trust
|
66
|
+
bandwidth_estimator = @downstream
|
67
|
+
else
|
68
|
+
bandwidth_estimator = @upstream
|
69
|
+
end
|
70
|
+
|
71
|
+
bandwidth_estimator.log(
|
72
|
+
transfer.creation_time,
|
73
|
+
Time.now,
|
74
|
+
transfer.byte_range.end - transfer.byte_range.begin
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Log a failed transfer and update internal data
|
79
|
+
def failure(transfer)
|
80
|
+
raise ArgumentError, "not taker for this transfer" unless transfer.taker == self
|
81
|
+
@chunk_info.request transfer.url, transfer.chunkid..transfer.chunkid
|
82
|
+
@trust.failure transfer.giver.trust
|
83
|
+
end
|
84
|
+
|
85
|
+
# Estimate of a client's upstream bandwidth
|
86
|
+
def upstream_bandwidth
|
87
|
+
@upstream.estimate rescue nil
|
88
|
+
end
|
89
|
+
|
90
|
+
# Estimate of a client's downstream bandwidth
|
91
|
+
def downstream_bandwidth
|
92
|
+
@downstream.estimate rescue nil
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns true if this client wants the server to spawn a transfer for it
|
96
|
+
def wants_download?
|
97
|
+
transfer_state_allowed = 5
|
98
|
+
total_allowed = 10
|
99
|
+
transferring = 0
|
100
|
+
@transfers.each do |key, t|
|
101
|
+
transferring += 1 if t.verification_asked
|
102
|
+
return false if transferring >= transfer_state_allowed
|
103
|
+
end
|
104
|
+
|
105
|
+
@transfers.size < total_allowed
|
106
|
+
end
|
107
|
+
|
108
|
+
# This could have a different definition, but it works fine to use wants_download?
|
109
|
+
alias_method :wants_upload?, :wants_download?
|
110
|
+
|
111
|
+
# Returns a list of all the stalled transfers this client is a part of
|
112
|
+
def stalled_transfers
|
113
|
+
stalled = []
|
114
|
+
timeout = 20.0
|
115
|
+
now = Time.now
|
116
|
+
@transfers.each do |key,t|
|
117
|
+
#only delete if we are the acceptor to prevent race conditions
|
118
|
+
next unless t.acceptor == self
|
119
|
+
if now - t.creation_time > timeout and not t.verification_asked
|
120
|
+
stalled << t
|
121
|
+
end
|
122
|
+
end
|
123
|
+
stalled
|
124
|
+
end
|
125
|
+
|
31
126
|
# Is this connection the file service?
|
32
127
|
def file_service?
|
33
128
|
@file_service
|
@@ -37,7 +132,11 @@ module PDTP
|
|
37
132
|
def mark_as_file_service
|
38
133
|
@file_service = true
|
39
134
|
end
|
40
|
-
|
135
|
+
|
136
|
+
#
|
137
|
+
# EventMachine callbacks to delegate to the dispatcher
|
138
|
+
#
|
139
|
+
|
41
140
|
def connection_completed
|
42
141
|
raise(RuntimeError, 'server was never initialized') unless @dispatcher
|
43
142
|
@dispatcher.connection_created self
|