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/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
|