distribustream 0.1.0 → 0.2.0
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.
- data/CHANGES +11 -0
- data/Rakefile +14 -10
- data/bin/dsclient +86 -22
- data/bin/dsseed +8 -79
- data/bin/dstream +20 -0
- data/conf/bigchunk.yml +1 -1
- data/conf/debug.yml +1 -1
- data/conf/example.yml +1 -1
- data/distribustream.gemspec +3 -3
- data/lib/pdtp/client/callbacks.rb +29 -0
- data/lib/pdtp/client/connection.rb +114 -0
- data/lib/pdtp/client/file_buffer.rb +93 -103
- data/lib/pdtp/client/file_service.rb +6 -3
- data/lib/pdtp/client/http_handler.rb +77 -0
- data/lib/pdtp/client/transfer.rb +13 -14
- data/lib/pdtp/client.rb +73 -156
- data/lib/pdtp/common/packet.rb +106 -0
- data/lib/pdtp/common/protocol.rb +19 -29
- data/lib/pdtp/server/client_info.rb +109 -107
- data/lib/pdtp/server/connection.rb +35 -0
- data/lib/pdtp/server/dispatcher.rb +370 -0
- data/lib/pdtp/server/file_service.rb +1 -1
- data/lib/pdtp/server/file_service_protocol.rb +116 -0
- data/lib/pdtp/server/stats_handler.rb +23 -0
- data/lib/pdtp/server/trust.rb +60 -58
- data/lib/pdtp/server.rb +45 -346
- metadata +12 -9
- data/bin/distribustream +0 -60
- data/lib/pdtp/client/file_buffer_spec.rb +0 -154
- data/lib/pdtp/client/protocol.rb +0 -66
- data/lib/pdtp/common/file_service_spec.rb +0 -91
- data/lib/pdtp/common/protocol_spec.rb +0 -68
- data/lib/pdtp/server/trust_spec.rb +0 -40
data/lib/pdtp/server.rb
CHANGED
@@ -8,361 +8,60 @@
|
|
8
8
|
# See http://distribustream.rubyforge.org/
|
9
9
|
#++
|
10
10
|
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require File.dirname(__FILE__) + '/server/client_info'
|
15
|
-
require File.dirname(__FILE__) + '/server/transfer'
|
11
|
+
require 'rubygems'
|
12
|
+
require 'eventmachine'
|
13
|
+
require 'mongrel'
|
16
14
|
|
17
|
-
require '
|
18
|
-
require '
|
15
|
+
require File.dirname(__FILE__) + '/server/dispatcher'
|
16
|
+
require File.dirname(__FILE__) + '/server/file_service'
|
17
|
+
require File.dirname(__FILE__) + '/server/connection'
|
18
|
+
require File.dirname(__FILE__) + '/server/stats_handler'
|
19
19
|
|
20
20
|
module PDTP
|
21
|
-
# PDTP server
|
21
|
+
# PDTP::Server provides an interface for creating a PDTP server
|
22
22
|
class Server
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
@connections = Array.new
|
27
|
-
@stats_mutex=Mutex.new
|
28
|
-
@used_client_ids=Hash.new #keeps a list of client ids in use, they must be unique
|
29
|
-
@updated_clients=Hash.new #a set of clients that have been modified and need transfers spawned
|
30
|
-
end
|
31
|
-
|
32
|
-
#called by pdtp_protocol when a connection is created
|
33
|
-
def connection_created(connection)
|
34
|
-
@stats_mutex.synchronize do
|
35
|
-
@@log.info "Client connected: #{connection.get_peer_info.inspect}"
|
36
|
-
connection.user_data = ClientInfo.new
|
37
|
-
@connections << connection
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
#called by pdtp_protocol when a connection is destroyed
|
42
|
-
def connection_destroyed(connection)
|
43
|
-
@stats_mutex.synchronize do
|
44
|
-
@@log.info "Client connection closed: #{connection.get_peer_info.inspect}"
|
45
|
-
@connections.delete connection
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# returns the ClientInfo object associated with this connection
|
50
|
-
def client_info(connection)
|
51
|
-
connection.user_data ||= ClientInfo.new
|
52
|
-
end
|
53
|
-
|
54
|
-
# called when a transfer either finishes, successfully or not
|
55
|
-
def transfer_completed(transfer,connection,chunk_hash,send_response=true)
|
56
|
-
# did the transfer complete successfully?
|
57
|
-
local_hash=@file_service.get_chunk_hash(transfer.url,transfer.chunkid)
|
58
|
-
|
59
|
-
c1=client_info(transfer.taker)
|
60
|
-
c2=client_info(transfer.giver)
|
61
|
-
|
62
|
-
if connection==transfer.taker
|
63
|
-
success= (chunk_hash==local_hash)
|
64
|
-
|
65
|
-
if success
|
66
|
-
#the taker now has the file, so he can provide it
|
67
|
-
client_info(transfer.taker).chunk_info.provide(transfer.url,transfer.chunkid..transfer.chunkid)
|
68
|
-
c1.trust.success(c2.trust)
|
69
|
-
else
|
70
|
-
#transfer failed, the client still wants the chunk
|
71
|
-
client_info(transfer.taker).chunk_info.request(transfer.url,transfer.chunkid..transfer.chunkid)
|
72
|
-
c1.trust.failure(c2.trust)
|
73
|
-
end
|
74
|
-
|
75
|
-
transfer.taker.send_message(:hash_verify,
|
76
|
-
:url => transfer.url,
|
77
|
-
:range => transfer.byte_range,
|
78
|
-
:hash_ok => success
|
79
|
-
) if send_response
|
80
|
-
end
|
81
|
-
|
82
|
-
#outstr="#{@ids[transfer.giver]}->#{@ids[transfer.taker]} transfer completed: #{transfer}"
|
83
|
-
#outstr=outstr+" t->g=#{c1.trust.weight(c2.trust)} g->t=#{c2.trust.weight(c1.trust)}"
|
84
|
-
#outstr=outstr+"sent_by: "+ ( connection==transfer.taker ? "taker" : "giver" )
|
85
|
-
#outstr=outstr+" success=#{success} "
|
86
|
-
#@@log.debug(outstr)
|
87
|
-
|
88
|
-
#remove this transfer from whoever sent it
|
89
|
-
client_info(connection).transfers.delete(transfer.transfer_id)
|
90
|
-
@updated_clients[connection]=true #flag this client for transfer creation
|
91
|
-
end
|
92
|
-
|
93
|
-
#Creates a new transfer between two peers
|
94
|
-
#returns true on success, or false if the specified transfer is already in progress
|
95
|
-
def begin_transfer(taker, giver, url, chunkid)
|
96
|
-
byte_range = @file_service.get_info(url).chunk_range(chunkid)
|
97
|
-
t = Transfer.new(taker, giver, url, chunkid, byte_range)
|
98
|
-
|
99
|
-
#make sure this transfer doesnt already exist
|
100
|
-
t1 = client_info(taker).transfers[t.transfer_id]
|
101
|
-
t2 = client_info(giver).transfers[t.transfer_id]
|
102
|
-
return false unless t1.nil? and t2.nil?
|
103
|
-
|
104
|
-
client_info(taker).chunk_info.transfer(url, chunkid..chunkid)
|
105
|
-
client_info(taker).transfers[t.transfer_id] = t
|
106
|
-
client_info(giver).transfers[t.transfer_id] = t
|
107
|
-
|
108
|
-
#send transfer message to the connector
|
109
|
-
addr, port = t.acceptor.get_peer_info
|
23
|
+
# Create a new PDTP::Server which will listen on the given address and port
|
24
|
+
def initialize(addr, port = 6086)
|
25
|
+
@addr, @port = addr, port
|
110
26
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
true
|
120
|
-
end
|
121
|
-
|
122
|
-
#this function removes all stalled transfers from the list
|
123
|
-
#and spawns new transfers as appropriate
|
124
|
-
#it must be called periodically by EventMachine
|
125
|
-
def clear_all_stalled_transfers
|
126
|
-
@connections.each { |connection| clear_stalled_transfers_for_client connection }
|
127
|
-
spawn_all_transfers
|
128
|
-
end
|
129
|
-
|
130
|
-
#removes all stalled transfers that this client is a part of
|
131
|
-
def clear_stalled_transfers_for_client(client_connection)
|
132
|
-
client_info(client_connection).get_stalled_transfers.each do |transfer|
|
133
|
-
transfer_completed transfer, client_connection, nil, false
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
#spawns uploads and downloads for this client.
|
138
|
-
#should be called every time there is a change that would affect
|
139
|
-
#what this client has or wants
|
140
|
-
def spawn_transfers_for_client(client_connection)
|
141
|
-
info = client_info client_connection
|
142
|
-
|
143
|
-
while info.wants_download? do
|
144
|
-
break if spawn_download_for_client(client_connection) == false
|
145
|
-
end
|
146
|
-
|
147
|
-
while info.wants_upload? do
|
148
|
-
break if spawn_upload_for_client(client_connection) == false
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
#creates a single download for the specified client
|
153
|
-
#returns true on success, false on failure
|
154
|
-
def spawn_download_for_client(client_connection)
|
155
|
-
feasible_peers=[]
|
156
|
-
|
157
|
-
c1info=client_info(client_connection)
|
158
|
-
begin
|
159
|
-
url,chunkid=c1info.chunk_info.high_priority_chunk
|
160
|
-
rescue
|
161
|
-
return false
|
162
|
-
end
|
163
|
-
|
164
|
-
@connections.each do |c2|
|
165
|
-
next if client_connection==c2
|
166
|
-
next if client_info(c2).wants_upload? == false
|
167
|
-
if client_info(c2).chunk_info.provided?(url,chunkid)
|
168
|
-
feasible_peers << c2
|
169
|
-
break if feasible_peers.size > 5
|
170
|
-
end
|
171
|
-
end
|
172
|
-
|
173
|
-
# we now have a list of clients that have the requested chunk.
|
174
|
-
# pick one and start the transfer
|
175
|
-
if feasible_peers.size > 0
|
176
|
-
#FIXME base this on the trust model
|
177
|
-
giver=feasible_peers[rand(feasible_peers.size)]
|
178
|
-
return begin_transfer(client_connection,giver,url,chunkid)
|
179
|
-
#FIXME should we try again if begin_transfer fails?
|
180
|
-
end
|
181
|
-
|
182
|
-
false
|
183
|
-
end
|
184
|
-
|
185
|
-
#creates a single upload for the specified client
|
186
|
-
#returns true on success, false on failure
|
187
|
-
def spawn_upload_for_client(client_connection)
|
188
|
-
c1info=client_info(client_connection)
|
189
|
-
|
190
|
-
@connections.each do |c2|
|
191
|
-
next if client_connection==c2
|
192
|
-
next if client_info(c2).wants_download? == false
|
193
|
-
|
194
|
-
begin
|
195
|
-
url,chunkid=client_info(c2).chunk_info.high_priority_chunk
|
196
|
-
rescue
|
197
|
-
next
|
198
|
-
end
|
199
|
-
|
200
|
-
if c1info.chunk_info.provided?(url,chunkid)
|
201
|
-
return begin_transfer(c2,client_connection,url,chunkid)
|
202
|
-
end
|
203
|
-
end
|
204
|
-
|
205
|
-
false
|
206
|
-
end
|
207
|
-
|
208
|
-
#called by pdtp_protocol for each message that comes in from the wire
|
209
|
-
def dispatch_message(command, message, connection)
|
210
|
-
@stats_mutex.synchronize do
|
211
|
-
dispatch_message_needslock command, message, connection
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
#creates new transfers for all clients that have been updated
|
216
|
-
def spawn_all_transfers
|
217
|
-
while @updated_clients.size > 0 do
|
218
|
-
tmp=@updated_clients
|
219
|
-
@updated_clients=Hash.new
|
220
|
-
tmp.each do |client,true_key|
|
221
|
-
spawn_transfers_for_client(client)
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
#handles the request, provide, unrequest, unprovide messages
|
227
|
-
def handle_requestprovide(connection,message)
|
228
|
-
type=message["type"]
|
229
|
-
url=message["url"]
|
230
|
-
info=@file_service.get_info(url) rescue nil
|
231
|
-
raise ProtocolWarn.new("Requested URL: '#{url}' not found") if info.nil?
|
232
|
-
|
233
|
-
exclude_partial= (type=="provide") #only exclude partial chunks from provides
|
234
|
-
range=info.chunk_range_from_byte_range(message["range"],exclude_partial)
|
235
|
-
|
236
|
-
#call request, provide, unrequest, or unprovide
|
237
|
-
client_info(connection).chunk_info.send( type.to_sym, url, range)
|
238
|
-
@updated_clients[connection]=true #add to the list of client that need new transfers
|
239
|
-
end
|
240
|
-
|
241
|
-
#handles all incoming messages from clients
|
242
|
-
def dispatch_message_needslock(command, message, connection)
|
243
|
-
# store the command in the message hash
|
244
|
-
message["type"] = command
|
27
|
+
@dispatcher = PDTP::Server::Dispatcher.new
|
28
|
+
@dispatcher.file_service = PDTP::Server::FileService.new
|
29
|
+
end
|
30
|
+
|
31
|
+
# Run a web server to display statistics on the given address and port
|
32
|
+
def enable_stats_service(addr = nil, port = 6087)
|
33
|
+
# Use the same address as the main server unless a different one was specified
|
34
|
+
addr ||= @addr
|
245
35
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
response[:streaming] = info.streaming
|
269
|
-
end
|
270
|
-
connection.send_message :tell_info, response
|
271
|
-
when "request", "provide", "unrequest", "unprovide"
|
272
|
-
handle_requestprovide connection, message
|
273
|
-
when "ask_verify"
|
274
|
-
#check if the specified transfer is a real one
|
275
|
-
my_id = client_info(connection).client_id
|
276
|
-
transfer_id=Transfer.gen_transfer_id(my_id,message["peer_id"],message["url"],message["range"])
|
277
|
-
ok = !!client_info(connection).transfers[transfer_id]
|
278
|
-
client_info(connection).transfers[transfer_id].verification_asked=true if ok
|
279
|
-
@@log.debug "AskVerify not ok: id=#{transfer_id}" unless ok
|
280
|
-
connection.send_message(:tell_verify,
|
281
|
-
:url => message["url"],
|
282
|
-
:peer_id => message["peer_id"],
|
283
|
-
:range => message["range"],
|
284
|
-
:peer => message["peer"],
|
285
|
-
:is_authorized=>ok
|
286
|
-
)
|
287
|
-
when "completed"
|
288
|
-
my_id = client_info(connection).client_id
|
289
|
-
transfer_id= Transfer::gen_transfer_id(
|
290
|
-
my_id,message["peer_id"],
|
291
|
-
message["url"],
|
292
|
-
message["range"]
|
293
|
-
)
|
294
|
-
transfer=client_info(connection).transfers[transfer_id]
|
295
|
-
@@log.debug("Completed: id=#{transfer_id} ok=#{transfer != nil}" )
|
296
|
-
if transfer
|
297
|
-
transfer_completed(transfer,connection,message["hash"])
|
298
|
-
else
|
299
|
-
raise ProtocolWarn.new("You sent me a transfer completed message for unknown transfer: #{transfer_id}")
|
36
|
+
@stats_server = Mongrel::HttpServer.new addr, port
|
37
|
+
@@log.info "Mongrel server listening on port: #{port}"
|
38
|
+
@stats_server.register '/', PDTP::Server::StatsHandler.new(@dispatcher)
|
39
|
+
@stats_server.run
|
40
|
+
end
|
41
|
+
|
42
|
+
# Serve files from the given directory
|
43
|
+
def enable_file_service(path, options = {})
|
44
|
+
opts = {
|
45
|
+
:chunk_size => 100000
|
46
|
+
}.merge(options)
|
47
|
+
|
48
|
+
@dispatcher.file_service.root = path
|
49
|
+
@dispatcher.file_service.default_chunk_size = opts[:chunk_size]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Run the PDTP server event loop
|
53
|
+
def run
|
54
|
+
EventMachine::run do
|
55
|
+
EventMachine::start_server(@addr, @port, PDTP::Server::Connection) do |connection|
|
56
|
+
connection.server = @dispatcher
|
57
|
+
connection.connection_completed
|
300
58
|
end
|
301
|
-
when 'protocol_error', 'protocol_warn' #ignore
|
302
|
-
else raise ProtocolError.new("Unhandled message type: #{command}")
|
303
|
-
end
|
304
59
|
|
305
|
-
|
306
|
-
|
60
|
+
@@log.info "accepting connections with ev=#{EventMachine::VERSION}"
|
61
|
+
@@log.info "host=#{@host} port=#{@port}"
|
307
62
|
|
308
|
-
|
309
|
-
|
310
|
-
#host,port=c.get_peer_info
|
311
|
-
#return "#{get_id(c)}: #{host}:#{port}"
|
312
|
-
client_info(c).client_id
|
313
|
-
end
|
314
|
-
|
315
|
-
def generate_html_stats
|
316
|
-
@stats_mutex.synchronize { generate_html_stats_needslock }
|
317
|
-
end
|
318
|
-
|
319
|
-
#builds an html page with information about the server's internal workings
|
320
|
-
def generate_html_stats_needslock
|
321
|
-
s = ERB.new <<EOF
|
322
|
-
<html><head><title>DistribuStream Statistics</title></head>
|
323
|
-
<body>
|
324
|
-
<h1>DistribuStream Statistics</h1>
|
325
|
-
Time=<%= Time.new %><br> Connected Clients=<%= @connections.size %>
|
326
|
-
<center><table border=1>
|
327
|
-
<tr><th>Client</th><th>Transfers</th><th>Files</th></tr>
|
328
|
-
<% @connections.each do |c| %>
|
329
|
-
<tr><td>
|
330
|
-
<% host, port = c.get_peer_info %>
|
331
|
-
<%= connection_name(c) %><br><%= host %>:<%= port %>
|
332
|
-
</td>
|
333
|
-
<td>
|
334
|
-
<%
|
335
|
-
client_info(c).transfers.each do |key,t|
|
336
|
-
if c==t.giver
|
337
|
-
type="UP: "
|
338
|
-
peer=t.taker
|
339
|
-
else
|
340
|
-
type="DOWN: "
|
341
|
-
peer=t.giver
|
342
|
-
end
|
343
|
-
%>
|
344
|
-
<%= type %> id=<%= t.transfer_id %><br>
|
345
|
-
<%
|
346
|
-
end
|
347
|
-
%>
|
348
|
-
</td>
|
349
|
-
<td>
|
350
|
-
<%
|
351
|
-
client_info(c).chunk_info.get_file_stats.each do |fs|
|
352
|
-
%>
|
353
|
-
<%= fs.url %> size=<%= fs.file_chunks %> req=<%= fs.chunks_requested %>
|
354
|
-
prov=<%= fs.chunks_provided %> transf=<%= fs.chunks_transferring %><br>
|
355
|
-
<%
|
356
|
-
end
|
357
|
-
%>
|
358
|
-
</td></tr>
|
359
|
-
<%
|
360
|
-
end
|
361
|
-
%>
|
362
|
-
</table>
|
363
|
-
</body></html>
|
364
|
-
EOF
|
365
|
-
s.result binding
|
63
|
+
EventMachine::add_periodic_timer(2) { @dispatcher.clear_all_stalled_transfers }
|
64
|
+
end
|
366
65
|
end
|
367
66
|
end
|
368
67
|
end
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: distribustream
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2008-10-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2008-10-22 00:00:00 -06:00
|
8
8
|
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
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,30 +33,33 @@ authors:
|
|
33
33
|
- James Sanders
|
34
34
|
- Tom Stapleton
|
35
35
|
files:
|
36
|
-
- bin/distribustream
|
37
36
|
- bin/dsclient
|
38
37
|
- bin/dsseed
|
38
|
+
- bin/dstream
|
39
39
|
- lib/pdtp
|
40
40
|
- lib/pdtp/client
|
41
41
|
- lib/pdtp/client.rb
|
42
42
|
- lib/pdtp/common
|
43
43
|
- lib/pdtp/server
|
44
44
|
- lib/pdtp/server.rb
|
45
|
+
- lib/pdtp/client/callbacks.rb
|
46
|
+
- lib/pdtp/client/connection.rb
|
45
47
|
- lib/pdtp/client/file_buffer.rb
|
46
|
-
- lib/pdtp/client/file_buffer_spec.rb
|
47
48
|
- lib/pdtp/client/file_service.rb
|
48
|
-
- lib/pdtp/client/
|
49
|
+
- lib/pdtp/client/http_handler.rb
|
49
50
|
- lib/pdtp/client/transfer.rb
|
50
51
|
- lib/pdtp/common/common_init.rb
|
51
52
|
- lib/pdtp/common/file_service.rb
|
52
|
-
- lib/pdtp/common/
|
53
|
+
- lib/pdtp/common/packet.rb
|
53
54
|
- lib/pdtp/common/protocol.rb
|
54
|
-
- lib/pdtp/common/protocol_spec.rb
|
55
55
|
- lib/pdtp/server/client_info.rb
|
56
|
+
- lib/pdtp/server/connection.rb
|
57
|
+
- lib/pdtp/server/dispatcher.rb
|
56
58
|
- lib/pdtp/server/file_service.rb
|
59
|
+
- lib/pdtp/server/file_service_protocol.rb
|
60
|
+
- lib/pdtp/server/stats_handler.rb
|
57
61
|
- lib/pdtp/server/transfer.rb
|
58
62
|
- lib/pdtp/server/trust.rb
|
59
|
-
- lib/pdtp/server/trust_spec.rb
|
60
63
|
- conf/bigchunk.yml
|
61
64
|
- conf/debug.yml
|
62
65
|
- conf/example.yml
|
@@ -77,7 +80,7 @@ extra_rdoc_files:
|
|
77
80
|
- README
|
78
81
|
- CHANGES
|
79
82
|
executables:
|
80
|
-
-
|
83
|
+
- dstream
|
81
84
|
- dsseed
|
82
85
|
- dsclient
|
83
86
|
extensions: []
|
data/bin/distribustream
DELETED
@@ -1,60 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
#--
|
3
|
-
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
4
|
-
# All rights reserved. See COPYING for permissions.
|
5
|
-
#
|
6
|
-
# This source file is distributed as part of the
|
7
|
-
# DistribuStream file transfer system.
|
8
|
-
#
|
9
|
-
# See http://distribustream.rubyforge.org/
|
10
|
-
#++
|
11
|
-
|
12
|
-
require 'rubygems'
|
13
|
-
require 'eventmachine'
|
14
|
-
require 'optparse'
|
15
|
-
require 'logger'
|
16
|
-
require 'mongrel'
|
17
|
-
|
18
|
-
require File.dirname(__FILE__) + '/../lib/pdtp/server'
|
19
|
-
|
20
|
-
common_init $0
|
21
|
-
|
22
|
-
server = PDTP::Server.new
|
23
|
-
server.file_service = PDTP::Server::FileService.new
|
24
|
-
PDTP::Protocol.listener = server
|
25
|
-
|
26
|
-
#set up the mongrel server for serving the stats page
|
27
|
-
class MongrelServerHandler< Mongrel::HttpHandler
|
28
|
-
def initialize(server)
|
29
|
-
@server = server
|
30
|
-
end
|
31
|
-
|
32
|
-
def process(request,response)
|
33
|
-
response.start(200) do |head, out|
|
34
|
-
out.write begin
|
35
|
-
outstr = @server.generate_html_stats
|
36
|
-
rescue Exception=>e
|
37
|
-
outstr = "Exception: #{e}\n#{e.backtrace.join("\n")}"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
#run the mongrel server
|
44
|
-
mongrel_server = Mongrel::HttpServer.new '0.0.0.0', @@config[:port] + 1
|
45
|
-
@@log.info "Mongrel server listening on port: #{@@config[:port] + 1}"
|
46
|
-
mongrel_server.register '/', MongrelServerHandler.new(server)
|
47
|
-
mongrel_server.run
|
48
|
-
|
49
|
-
#set root directory
|
50
|
-
server.file_service.root = @@config[:file_root]
|
51
|
-
server.file_service.default_chunk_size = @@config[:chunk_size]
|
52
|
-
|
53
|
-
EventMachine::run do
|
54
|
-
host, port = '0.0.0.0', @@config[:port]
|
55
|
-
EventMachine::start_server host, port, PDTP::Protocol
|
56
|
-
@@log.info "accepting connections with ev=#{EventMachine::VERSION}"
|
57
|
-
@@log.info "host=#{host} port=#{port}"
|
58
|
-
|
59
|
-
EventMachine::add_periodic_timer(2) { server.clear_all_stalled_transfers }
|
60
|
-
end
|
@@ -1,154 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
-
# All rights reserved. See COPYING for permissions.
|
4
|
-
#
|
5
|
-
# This source file is distributed as part of the
|
6
|
-
# DistribuStream file transfer system.
|
7
|
-
#
|
8
|
-
# See http://distribustream.rubyforge.org/
|
9
|
-
#++
|
10
|
-
|
11
|
-
require File.dirname(__FILE__) + '/file_buffer'
|
12
|
-
|
13
|
-
describe PDTP::FileBuffer do
|
14
|
-
before(:each) do
|
15
|
-
@b = PDTP::FileBuffer.new
|
16
|
-
end
|
17
|
-
|
18
|
-
it "returns nil if read when empty" do
|
19
|
-
@b.bytes_stored.should == 0
|
20
|
-
@b.read(0..1).should == nil
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe PDTP::FileBuffer, "with one entry" do
|
25
|
-
before(:each) do
|
26
|
-
@b = PDTP::FileBuffer.new
|
27
|
-
@b.write 0, 'hello'
|
28
|
-
end
|
29
|
-
|
30
|
-
it "calculates bytes stored correctly" do
|
31
|
-
@b.bytes_stored.should == 5
|
32
|
-
end
|
33
|
-
|
34
|
-
it "reads stored data correctly" do
|
35
|
-
@b.read(0..4).should == "hello"
|
36
|
-
@b.read(1..1).should == "e"
|
37
|
-
@b.read(-1..2).should == nil
|
38
|
-
@b.read(0..5).should == nil
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe PDTP::FileBuffer, "with two overlapping entries" do
|
43
|
-
before(:each) do
|
44
|
-
@b = PDTP::FileBuffer.new
|
45
|
-
@b.write(3,"hello")
|
46
|
-
@b.write(7,"World")
|
47
|
-
end
|
48
|
-
|
49
|
-
it "calculates bytes stored correctly" do
|
50
|
-
@b.bytes_stored.should == 9
|
51
|
-
end
|
52
|
-
|
53
|
-
it "reads stored data correctly" do
|
54
|
-
@b.read(3..12).should == nil
|
55
|
-
@b.read(3..11).should == "hellWorld"
|
56
|
-
@b.read(3..1).should == nil
|
57
|
-
@b.read(2..4).should == nil
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
describe PDTP::FileBuffer, "with three overlapping entries" do
|
63
|
-
before(:each) do
|
64
|
-
@b = PDTP::FileBuffer.new
|
65
|
-
@b.write(3,"hello")
|
66
|
-
@b.write(7,"World")
|
67
|
-
@b.write(2,"123456789ABCDEF")
|
68
|
-
end
|
69
|
-
|
70
|
-
it "calculates bytes stored correctly" do
|
71
|
-
@b.bytes_stored.should == 15
|
72
|
-
end
|
73
|
-
|
74
|
-
it "reads stored data correctly" do
|
75
|
-
@b.read(2..16).should == "123456789ABCDEF"
|
76
|
-
@b.read(2..17).should == nil
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
describe PDTP::FileBuffer, "with two tangential entries" do
|
81
|
-
before(:each) do
|
82
|
-
@b = PDTP::FileBuffer.new
|
83
|
-
@b.write(3,"hello")
|
84
|
-
@b.write(8,"World")
|
85
|
-
end
|
86
|
-
|
87
|
-
it "calculates bytes stored correctly" do
|
88
|
-
@b.bytes_stored.should == 10
|
89
|
-
end
|
90
|
-
|
91
|
-
it "reads stored data correctly" do
|
92
|
-
@b.read(3..12).should == "helloWorld"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
describe PDTP::FileBuffer, "with a chain of overlapping entries" do
|
97
|
-
before(:each) do
|
98
|
-
@b = PDTP::FileBuffer.new
|
99
|
-
@b.write(3,"a123")
|
100
|
-
@b.write(4,"b4")
|
101
|
-
@b.write(0,"012c")
|
102
|
-
|
103
|
-
#___a123
|
104
|
-
#___ab43
|
105
|
-
#012cb43
|
106
|
-
|
107
|
-
end
|
108
|
-
|
109
|
-
it "calculates bytes stored correctly" do
|
110
|
-
@b.bytes_stored.should == 7
|
111
|
-
end
|
112
|
-
|
113
|
-
it "reads stored data correctly" do
|
114
|
-
@b.read(0..6).should == "012cb43"
|
115
|
-
@b.read(3..6).should == "cb43"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
describe PDTP::FileBuffer, "with an associated IO object" do
|
120
|
-
before(:each) do
|
121
|
-
@io = mock(:io)
|
122
|
-
@b = PDTP::FileBuffer.new @io
|
123
|
-
end
|
124
|
-
|
125
|
-
it "writes received data to the IO object" do
|
126
|
-
@io.should_receive(:write).once.with('foo').and_return(3)
|
127
|
-
@b.write(0, "foo")
|
128
|
-
end
|
129
|
-
|
130
|
-
it "writes successively received data to the IO object" do
|
131
|
-
@io.should_receive(:write).once.with('foo').and_return(3)
|
132
|
-
@b.write(0, "foo")
|
133
|
-
|
134
|
-
@io.should_receive(:write).once.with('bar').and_return(3)
|
135
|
-
@b.write(3, "bar")
|
136
|
-
|
137
|
-
@io.should_receive(:write).once.with('baz').and_return(3)
|
138
|
-
@b.write(6, "baz")
|
139
|
-
end
|
140
|
-
|
141
|
-
it "reassembles single-byte out-of-order data and writes it to the IO object" do
|
142
|
-
@io.should_receive(:write).once.with('bar').and_return(3)
|
143
|
-
@b.write(1, 'a')
|
144
|
-
@b.write(2, 'r')
|
145
|
-
@b.write(0, 'b')
|
146
|
-
end
|
147
|
-
|
148
|
-
it "reassembles multibyte out-of-order data and writes it to the IO object" do
|
149
|
-
@io.should_receive(:write).once.with('foobar').and_return(6)
|
150
|
-
@b.write(2, 'ob')
|
151
|
-
@b.write(4, 'ar')
|
152
|
-
@b.write(0, 'fo')
|
153
|
-
end
|
154
|
-
end
|