distribustream 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/CHANGES
CHANGED
@@ -1,2 +1,13 @@
|
|
1
|
+
Version 0.2.0
|
2
|
+
* PDTP::Server and PDTP::Client now expose APIs intended for public consumption
|
3
|
+
They may change slightly down the road, but will remain largely the same
|
4
|
+
|
5
|
+
* PDTP::Client now uses a Tempfile for its internal buffer, drastically
|
6
|
+
improving its memory usage
|
7
|
+
|
8
|
+
* The protocol now uses length prefix framing, rather than using CRLF encoding.
|
9
|
+
This breaks protocol compatibility with the 0.1.0 release but should represent
|
10
|
+
the last major change to the protocol format.
|
11
|
+
|
1
12
|
Version 0.1.0
|
2
13
|
* Initial release
|
data/Rakefile
CHANGED
@@ -25,20 +25,24 @@ end
|
|
25
25
|
begin
|
26
26
|
require 'spec/rake/spectask'
|
27
27
|
|
28
|
+
SPECS = FileList['spec/**/*_spec.rb']
|
29
|
+
|
28
30
|
Spec::Rake::SpecTask.new(:spec) do |task|
|
29
|
-
task.spec_files =
|
31
|
+
task.spec_files = SPECS
|
30
32
|
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
namespace :spec do
|
35
|
+
Spec::Rake::SpecTask.new(:print) do |task|
|
36
|
+
task.spec_files = SPECS
|
37
|
+
task.spec_opts="-f s".split
|
38
|
+
end
|
39
|
+
|
40
|
+
Spec::Rake::SpecTask.new(:rcov) do |task|
|
41
|
+
task.spec_files = SPECS
|
42
|
+
task.rcov = true
|
43
|
+
task.rcov_opts = ['--exclude', 'spec']
|
44
|
+
end
|
35
45
|
end
|
36
46
|
|
37
|
-
Spec::Rake::SpecTask.new(:spec_coverage) do |task|
|
38
|
-
task.spec_files = FileList['**/*_spec.rb']
|
39
|
-
task.rcov = true
|
40
|
-
end
|
41
47
|
rescue LoadError
|
42
48
|
end
|
43
|
-
|
44
|
-
|
data/bin/dsclient
CHANGED
@@ -1,4 +1,24 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# == Synopsis
|
4
|
+
#
|
5
|
+
# dsclient: DistribuStream client application
|
6
|
+
#
|
7
|
+
# == Usage
|
8
|
+
#
|
9
|
+
# dsclient [OPTION] ... URI
|
10
|
+
#
|
11
|
+
# -h, --help:
|
12
|
+
# Show help
|
13
|
+
#
|
14
|
+
# --output file, -o file:
|
15
|
+
# File to save to. Use '-' for stdout
|
16
|
+
#
|
17
|
+
# --listen_port port, -p port:
|
18
|
+
# Port for the local HTTP server to listen on
|
19
|
+
#
|
20
|
+
# URI: An address in the pdtp:// scheme to retrieve from the server
|
21
|
+
|
2
22
|
#--
|
3
23
|
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
4
24
|
# All rights reserved. See COPYING for permissions.
|
@@ -9,35 +29,79 @@
|
|
9
29
|
# See http://distribustream.rubyforge.org/
|
10
30
|
#++
|
11
31
|
|
12
|
-
require 'rubygems'
|
13
|
-
require 'eventmachine'
|
14
|
-
require 'mongrel'
|
15
|
-
require 'optparse'
|
16
32
|
require 'uri'
|
33
|
+
require 'getoptlong'
|
34
|
+
require 'rdoc/ri/ri_paths'
|
35
|
+
require 'rdoc/usage'
|
36
|
+
require 'logger'
|
17
37
|
|
18
38
|
require File.dirname(__FILE__) + '/../lib/pdtp/client'
|
19
39
|
|
20
|
-
|
21
|
-
|
40
|
+
opts = GetoptLong.new(
|
41
|
+
[ '--help', '-h', GetoptLong::NO_ARGUMENT ],
|
42
|
+
[ '--output', '-o', GetoptLong::REQUIRED_ARGUMENT ],
|
43
|
+
[ '--listen', '-l', GetoptLong::OPTIONAL_ARGUMENT ]
|
44
|
+
)
|
22
45
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
46
|
+
filename = nil
|
47
|
+
listen_port = 60860
|
48
|
+
|
49
|
+
# Display usage information extraced from this file via RDoc
|
50
|
+
def display_usage
|
51
|
+
File.open(__FILE__) { |file| STDERR.write RDoc.find_comment(file).gsub(/^# ?/, '') }
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.each do |opt, arg|
|
56
|
+
case opt
|
57
|
+
when '--help' then display_usage
|
58
|
+
when '--output' then filename = arg
|
59
|
+
when '--listen' then listen_port = arg.to_i
|
34
60
|
end
|
35
|
-
end
|
61
|
+
end
|
62
|
+
|
63
|
+
if ARGV.length != 1
|
64
|
+
puts "Missing URI argument (try --help)"
|
65
|
+
exit 0
|
66
|
+
end
|
36
67
|
|
37
|
-
|
68
|
+
uri = URI.parse ARGV.shift
|
69
|
+
|
70
|
+
# Validate URI scheme
|
38
71
|
raise "Only pdtp:// URLs are supported" unless uri.scheme == 'pdtp'
|
39
72
|
|
40
|
-
|
41
|
-
|
73
|
+
STDERR.write "--#{Time.now.strftime("%H:%M:%S")}-- #{uri}\n"
|
74
|
+
|
75
|
+
filename ||= File.basename(uri.path)
|
76
|
+
|
77
|
+
STDERR.write " => '#{filename}'\n"
|
78
|
+
|
79
|
+
# Open the output file
|
80
|
+
output = case filename
|
81
|
+
when '-' then STDOUT
|
82
|
+
else open(filename, 'w')
|
83
|
+
end
|
84
|
+
|
85
|
+
class Callbacks < PDTP::Client::Callbacks
|
86
|
+
attr_accessor :uri
|
87
|
+
attr_accessor :output
|
88
|
+
|
89
|
+
def connected
|
90
|
+
STDERR.write "Connected to #{uri.host}\n"
|
91
|
+
STDERR.write "Beginning transfer...\n"
|
92
|
+
|
93
|
+
# Begin fetching the file once we've connected
|
94
|
+
client.get uri.path, output
|
95
|
+
end
|
96
|
+
|
97
|
+
def disconnected
|
98
|
+
STDERR.write "Disconnected from #{uri.host}\n"
|
99
|
+
client.stop
|
100
|
+
end
|
101
|
+
end
|
42
102
|
|
43
|
-
|
103
|
+
@@log = Logger.new(STDERR)
|
104
|
+
@@log.level=Logger::INFO
|
105
|
+
|
106
|
+
@client = PDTP::Client.new uri.host, uri.port || 6086, :listen_port => listen_port
|
107
|
+
@client.connect(Callbacks) { |c| c.uri, c.output = uri, output }
|
data/bin/dsseed
CHANGED
@@ -14,90 +14,19 @@ require 'rubygems'
|
|
14
14
|
require 'eventmachine'
|
15
15
|
require 'mongrel'
|
16
16
|
|
17
|
-
require File.dirname(__FILE__) + '/../lib/pdtp/
|
17
|
+
require File.dirname(__FILE__) + '/../lib/pdtp/common/common_init'
|
18
|
+
require File.dirname(__FILE__) + '/../lib/pdtp/server/file_service_protocol'
|
18
19
|
|
19
|
-
common_init $0
|
20
|
-
|
21
|
-
# Fine all suitable files in the give path
|
22
|
-
def find_files(base_path)
|
23
|
-
require 'find'
|
24
|
-
|
25
|
-
found = []
|
26
|
-
excludes = %w{.svn CVS}
|
27
|
-
base_full = File.expand_path(base_path)
|
28
|
-
|
29
|
-
Find.find(base_full) do |path|
|
30
|
-
if FileTest.directory?(path)
|
31
|
-
next unless excludes.include?(File.basename(path))
|
32
|
-
Find.prune
|
33
|
-
else
|
34
|
-
filename = path[(base_path.size - path.size + 1)..-1] #the entire file path after the base_path
|
35
|
-
found << filename
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
found
|
40
|
-
end
|
41
|
-
|
42
|
-
# Implements the file service for the pdtp protocol
|
43
|
-
class FileServiceProtocol < PDTP::Protocol
|
44
|
-
def initialize(*args)
|
45
|
-
super
|
46
|
-
end
|
47
|
-
|
48
|
-
# Called after a connection to the server has been established
|
49
|
-
def connection_completed
|
50
|
-
begin
|
51
|
-
listen_port = @@config[:listen_port]
|
52
|
-
|
53
|
-
#create the client
|
54
|
-
client = PDTP::Client.new
|
55
|
-
PDTP::Protocol.listener = client
|
56
|
-
client.server_connection = self
|
57
|
-
client.generate_client_id listen_port
|
58
|
-
|
59
|
-
# Start a mongrel server on the specified port. If it isnt available, keep trying higher ports
|
60
|
-
begin
|
61
|
-
mongrel_server=Mongrel::HttpServer.new '0.0.0.0', listen_port
|
62
|
-
rescue Exception=>e
|
63
|
-
listen_port+=1
|
64
|
-
retry
|
65
|
-
end
|
66
|
-
|
67
|
-
@@log.info "listening on port #{listen_port}"
|
68
|
-
mongrel_server.register "/", client
|
69
|
-
mongrel_server.run
|
70
|
-
|
71
|
-
# Tell the server a little bit about ourself
|
72
|
-
send_message :client_info, :listen_port => listen_port, :client_id => client.my_id
|
73
|
-
|
74
|
-
@@log.info 'This client is providing'
|
75
|
-
sfs = PDTP::Server::FileService.new
|
76
|
-
sfs.root = @@config[:file_root]
|
77
|
-
client.file_service = sfs #give this client access to all data
|
78
|
-
|
79
|
-
hostname = @@config[:vhost]
|
80
|
-
|
81
|
-
# Provide all the files in the root directory
|
82
|
-
files = find_files @@config[:file_root]
|
83
|
-
files.each { |file| send_message :provide, :url => "http://#{hostname}/#{file}" }
|
84
|
-
rescue Exception => e
|
85
|
-
puts "Exception in connection_completed: #{e}"
|
86
|
-
puts e.backtrace.join("\n")
|
87
|
-
exit
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def unbind
|
92
|
-
super
|
93
|
-
puts "Disconnected from PDTP server."
|
94
|
-
end
|
95
|
-
end
|
20
|
+
common_init File.basename($0)
|
96
21
|
|
97
22
|
# Run the EventMachine reactor loop
|
98
23
|
EventMachine::run do
|
99
24
|
host, port, listen_port = @@config[:host], @@config[:port], @@config[:listen_port]
|
100
|
-
|
25
|
+
|
26
|
+
connection = EventMachine::connect(host, port, PDTP::Server::FileService::Protocol) do |c|
|
27
|
+
c.instance_eval { @base_path = @@config[:file_root] }
|
28
|
+
end
|
29
|
+
|
101
30
|
@@log.info "connecting with ev=#{EventMachine::VERSION}"
|
102
31
|
@@log.info "host= #{host} port=#{port}"
|
103
32
|
end
|
data/bin/dstream
ADDED
@@ -0,0 +1,20 @@
|
|
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 File.dirname(__FILE__) + '/../lib/pdtp/common/common_init'
|
13
|
+
require File.dirname(__FILE__) + '/../lib/pdtp/server'
|
14
|
+
|
15
|
+
common_init File.basename($0)
|
16
|
+
|
17
|
+
server = PDTP::Server.new @@config[:host], @@config[:port]
|
18
|
+
server.enable_stats_service
|
19
|
+
server.enable_file_service @@config[:file_root], :chunk_size => @@config[:chunk_size]
|
20
|
+
server.run
|
data/conf/bigchunk.yml
CHANGED
data/conf/debug.yml
CHANGED
data/conf/example.yml
CHANGED
data/distribustream.gemspec
CHANGED
@@ -2,8 +2,8 @@ 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-10-
|
5
|
+
s.version = "0.2.0"
|
6
|
+
s.date = "2008-10-22"
|
7
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
8
|
s.email = "tony@clickcaster.com"
|
9
9
|
s.homepage = "http://distribustream.rubyforge.org"
|
@@ -13,7 +13,7 @@ GEMSPEC = Gem::Specification.new do |s|
|
|
13
13
|
s.extra_rdoc_files = ["COPYING", "README", "CHANGES"]
|
14
14
|
s.authors = ["Tony Arcieri", "Ashvin Mysore", "Galen Pahlke", "James Sanders", "Tom Stapleton"]
|
15
15
|
s.files = Dir.glob("{bin,lib,conf}/**/*") + ['Rakefile', 'distribustream.gemspec']
|
16
|
-
s.executables = %w{
|
16
|
+
s.executables = %w{dstream dsseed dsclient}
|
17
17
|
s.add_dependency("eventmachine", ">= 0.9.0")
|
18
18
|
s.add_dependency("mongrel", ">= 1.0.1")
|
19
19
|
s.add_dependency("json", ">= 1.1.0")
|
@@ -0,0 +1,29 @@
|
|
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
|
+
module PDTP
|
12
|
+
class Client
|
13
|
+
# Callbacks
|
14
|
+
class Callbacks
|
15
|
+
attr_accessor :client
|
16
|
+
|
17
|
+
def initialize(connection)
|
18
|
+
@connection = connection
|
19
|
+
end
|
20
|
+
|
21
|
+
def connected
|
22
|
+
end
|
23
|
+
|
24
|
+
def disconnected
|
25
|
+
client.stop
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,114 @@
|
|
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 'rubygems'
|
12
|
+
require 'mongrel'
|
13
|
+
|
14
|
+
require File.dirname(__FILE__) + '/../common/protocol.rb'
|
15
|
+
|
16
|
+
module PDTP
|
17
|
+
class Client
|
18
|
+
# Implementation of the PDTP client protocol
|
19
|
+
class Connection < PDTP::Protocol
|
20
|
+
attr_accessor :client
|
21
|
+
attr_accessor :callbacks
|
22
|
+
|
23
|
+
# Called after a connection to the server has been established
|
24
|
+
def connection_completed
|
25
|
+
begin
|
26
|
+
#create the client
|
27
|
+
#PDTP::Protocol.listener = self
|
28
|
+
|
29
|
+
# Tell the server about ourself
|
30
|
+
send_message(:register,
|
31
|
+
:listen_port => @client.listen_port,
|
32
|
+
:client_id => @client.client_id
|
33
|
+
)
|
34
|
+
|
35
|
+
callbacks.connected
|
36
|
+
rescue Exception => e
|
37
|
+
puts "Exception in connection_completed: #{e}"
|
38
|
+
puts e.backtrace.join("\n")
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# Called when any server message is received. This is the brains of
|
44
|
+
# the client's protocol handling.
|
45
|
+
def receive_message(command, message)
|
46
|
+
case command
|
47
|
+
when "tell_info" # Receive and store information for this url
|
48
|
+
info = @client.file_service.get_info(message["url"])
|
49
|
+
#info = FileInfo.new message["url"].split('/').last
|
50
|
+
info.file_size = message["size"]
|
51
|
+
info.base_chunk_size = message["chunk_size"]
|
52
|
+
info.streaming = message["streaming"]
|
53
|
+
#@client.file_service.set_info(message["url"], info)
|
54
|
+
when "transfer" # Begin a transfer as a connector
|
55
|
+
transfer = Transfer::Connector.new(
|
56
|
+
self,
|
57
|
+
message,
|
58
|
+
@client.file_service
|
59
|
+
)
|
60
|
+
|
61
|
+
@@log.debug "TRANSFER STARTING"
|
62
|
+
|
63
|
+
# Run each transfer in its own thread and notify the server upon completion
|
64
|
+
Thread.new(transfer) do |t|
|
65
|
+
begin
|
66
|
+
t.run
|
67
|
+
rescue Exception=>e
|
68
|
+
@@log.info("Exception in dispatch_message: " + e.exception + "\n" + e.backtrace.join("\n"))
|
69
|
+
end
|
70
|
+
t.send_completed_message(t.hash)
|
71
|
+
end
|
72
|
+
when "tell_verify"
|
73
|
+
# We are a listener, and asked for verification of a transfer from a server.
|
74
|
+
# After asking for verification, we stopped running, and must be restarted
|
75
|
+
# if verification is successful
|
76
|
+
|
77
|
+
found=false
|
78
|
+
@client.transfers.each do |t|
|
79
|
+
if t.matches_message?(message)
|
80
|
+
finished(t)
|
81
|
+
t.tell_verify(message["authorized"])
|
82
|
+
found=true
|
83
|
+
break
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
unless found
|
88
|
+
puts "BUG: Tell verify sent for an unknown transfer"
|
89
|
+
exit!
|
90
|
+
end
|
91
|
+
when "hash_verify"
|
92
|
+
@@log.debug "Hash verified for url=#{message["url"]} range=#{message["range"]} hash_ok=#{message["hash_ok"]}"
|
93
|
+
when "protocol_error", "protocol_warn" #ignore
|
94
|
+
else raise "Server sent an unknown message type: #{command} "
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def unbind
|
99
|
+
super
|
100
|
+
puts 'Disconnected from PDTP server.'
|
101
|
+
end
|
102
|
+
|
103
|
+
#Prints the number of transfers associated with this client
|
104
|
+
def print_stats
|
105
|
+
@@log.debug "client: num_transfers=#{@client.transfers.size}"
|
106
|
+
end
|
107
|
+
|
108
|
+
#Provides a threadsafe mechanism for transfers to report themselves finished
|
109
|
+
def finished(transfer)
|
110
|
+
@client.lock.synchronize { @client.transfers.delete(transfer) }
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|