distribustream 0.2.0 → 0.2.1
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 +5 -0
- data/README +21 -24
- data/distribustream.gemspec +1 -1
- data/lib/pdtp/client.rb +1 -0
- data/lib/pdtp/client/connection.rb +11 -9
- data/lib/pdtp/client/http_client.rb +114 -0
- data/lib/pdtp/client/transfer.rb +51 -41
- data/lib/pdtp/common.rb +15 -0
- data/lib/pdtp/common/common_init.rb +2 -2
- data/lib/pdtp/common/{packet.rb → length_prefix_protocol.rb} +42 -29
- data/lib/pdtp/common/protocol.rb +9 -9
- data/lib/pdtp/server.rb +1 -0
- data/lib/pdtp/server/file_service_protocol.rb +3 -3
- metadata +9 -7
data/CHANGES
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
Version 0.2.1
|
2
|
+
* Switch HTTP client to use an EventMachine-based version, rather than net/http
|
3
|
+
|
4
|
+
* Added PDTP.version for retrieving version of the library
|
5
|
+
|
1
6
|
Version 0.2.0
|
2
7
|
* PDTP::Server and PDTP::Client now expose APIs intended for public consumption
|
3
8
|
They may change slightly down the road, but will remain largely the same
|
data/README
CHANGED
@@ -6,6 +6,11 @@ or live streaming media to be delivered at a fraction of the normal cost.
|
|
6
6
|
This README covers the initial public release, known issues, and a general
|
7
7
|
development roadmap.
|
8
8
|
|
9
|
+
Contents:
|
10
|
+
1. Usage
|
11
|
+
2. Known Issues
|
12
|
+
3. Development Roadmap
|
13
|
+
|
9
14
|
--
|
10
15
|
|
11
16
|
Usage:
|
@@ -27,7 +32,7 @@ to identify as.
|
|
27
32
|
|
28
33
|
Next, start the DistribuStream server:
|
29
34
|
|
30
|
-
|
35
|
+
dstream --conf myconfig.yml
|
31
36
|
|
32
37
|
The DistribuStream server manages traffic on the peer network. It also handles
|
33
38
|
the checksumming of files.
|
@@ -48,32 +53,19 @@ At this point your server is ready to go.
|
|
48
53
|
|
49
54
|
To test your server, use the DistribuStream client:
|
50
55
|
|
51
|
-
dsclient
|
56
|
+
dsclient pdtp://myserver.url/file.ext
|
52
57
|
|
53
58
|
This will download file.ext from your DistribuStream server.
|
54
59
|
|
55
|
-
|
56
|
-
|
57
|
-
you can:
|
60
|
+
The client also supports non-seekable output such as pipes. To play streaming
|
61
|
+
media as it downloads, you can:
|
58
62
|
|
59
|
-
|
60
|
-
dsclient --url pdtp://myserver.url/file.ext &
|
61
|
-
mediaplayer file.ext
|
63
|
+
dsclient -o pdtp://myserver.url/file.ext | mediaplayer -
|
62
64
|
|
63
65
|
--
|
64
66
|
|
65
67
|
Known Issues:
|
66
68
|
|
67
|
-
The client presently stores incoming data in a memory buffer. This causes
|
68
|
-
the client to consume massive amounts of memory as the file downloads.
|
69
|
-
Subsequent releases will fix this by improving the design of the memory
|
70
|
-
buffer, moving to a disk-backed buffer and/or discarding some of the
|
71
|
-
downloaded data after it's been played back.
|
72
|
-
|
73
|
-
The protocol facilitates allowing clients to have a moving window of data
|
74
|
-
in a stream, so they need not retain data which has already been displayed
|
75
|
-
to the user.
|
76
|
-
|
77
69
|
Seeds are presently not authenticated in any way, thus anyone can attach
|
78
70
|
a seed and populate the server with any files of their choosing. However,
|
79
71
|
since file checksumming is done by the server itself, this means that only
|
@@ -87,10 +79,6 @@ checksumming to the server <-> seed protocol.
|
|
87
79
|
|
88
80
|
Development Roadmap:
|
89
81
|
|
90
|
-
The immediate goal is to improve the performance of the client, which presently
|
91
|
-
consumes far too much RAM for practical use with large media files. Another
|
92
|
-
immediate goal is solving the above problems with seeds.
|
93
|
-
|
94
82
|
DistribuStream uses an assemblage of various tools which do not work together
|
95
83
|
particularly well. These include the EventMachine Ruby gem, which provides
|
96
84
|
the I/O layer for the DistribuStream server, and the Mongrel web server, which
|
@@ -99,8 +87,17 @@ runs independently of EventMachine and uses threads.
|
|
99
87
|
Initial work will focus on converting the existing implementation to a fully
|
100
88
|
EventMachine-based approach which eliminates the use of threads.
|
101
89
|
|
102
|
-
|
103
|
-
|
90
|
+
Mid-term work will focus on improving the efficiency of peer-to-peer traffic
|
91
|
+
routing, by incorporating all of the following constraints:
|
92
|
+
|
93
|
+
1. Does the potential "giver" peer have chunks the "taker" peer is requesting?
|
94
|
+
2. Are the peers in the same prefix? (at least the same /16, if not the same /24)
|
95
|
+
3. Are the peers firewalled? (the server could do a connect test as soon as a
|
96
|
+
client registers their listen port)
|
97
|
+
4. What is the trust between peers? (the "random" connecting of untrusted peers
|
98
|
+
could be replaced by something that operates on the above two bits of data)
|
99
|
+
5. Does the "giver" have enough bandwidth? (modeled over time)
|
100
|
+
6. Are total inbound and outbound links minimized? (to improve TCP congestion)
|
104
101
|
|
105
102
|
Long-term goals include a move to UDP to reduce protocol latency and overhead
|
106
103
|
as well as encrypting all traffic to ensure privacy and security of the
|
data/distribustream.gemspec
CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
|
|
2
2
|
|
3
3
|
GEMSPEC = Gem::Specification.new do |s|
|
4
4
|
s.name = "distribustream"
|
5
|
-
s.version = "0.2.
|
5
|
+
s.version = "0.2.1"
|
6
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"
|
data/lib/pdtp/client.rb
CHANGED
@@ -13,6 +13,7 @@ require 'eventmachine'
|
|
13
13
|
require 'thread'
|
14
14
|
require 'digest/md5'
|
15
15
|
|
16
|
+
require File.dirname(__FILE__) + '/common'
|
16
17
|
require File.dirname(__FILE__) + '/client/connection'
|
17
18
|
require File.dirname(__FILE__) + '/client/callbacks'
|
18
19
|
require File.dirname(__FILE__) + '/client/file_service'
|
@@ -57,18 +57,20 @@ module PDTP
|
|
57
57
|
message,
|
58
58
|
@client.file_service
|
59
59
|
)
|
60
|
+
|
61
|
+
transfer.run
|
60
62
|
|
61
|
-
|
63
|
+
#@@log.debug "TRANSFER STARTING"
|
62
64
|
|
63
65
|
# Run each transfer in its own thread and notify the server upon completion
|
64
|
-
Thread.new(transfer) do |t|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
66
|
+
#Thread.new(transfer) do |t|
|
67
|
+
# begin
|
68
|
+
# t.run
|
69
|
+
# rescue Exception=>e
|
70
|
+
# @@log.info("Exception in dispatch_message: " + e.exception + "\n" + e.backtrace.join("\n"))
|
71
|
+
# end
|
72
|
+
# t.send_completed_message(t.hash)
|
73
|
+
#end
|
72
74
|
when "tell_verify"
|
73
75
|
# We are a listener, and asked for verification of a transfer from a server.
|
74
76
|
# After asking for verification, we stopped running, and must be restarted
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# $Id: httpclient.rb 518 2007-08-30 10:17:02Z blackhedd $
|
2
|
+
#
|
3
|
+
# Author:: Francis Cianfrocca (gmail: blackhedd)
|
4
|
+
# Homepage:: http://rubyeventmachine.com
|
5
|
+
# Date:: 16 July 2006
|
6
|
+
#
|
7
|
+
# See EventMachine and EventMachine::Connection for documentation and
|
8
|
+
# usage examples.
|
9
|
+
#
|
10
|
+
#----------------------------------------------------------------------------
|
11
|
+
#
|
12
|
+
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
13
|
+
# Gmail: blackhedd
|
14
|
+
#
|
15
|
+
# This program is free software; you can redistribute it and/or modify
|
16
|
+
# it under the terms of either: 1) the GNU General Public License
|
17
|
+
# as published by the Free Software Foundation; either version 2 of the
|
18
|
+
# License, or (at your option) any later version; or 2) Ruby's License.
|
19
|
+
#
|
20
|
+
# See the file COPYING for complete licensing information.
|
21
|
+
#
|
22
|
+
#---------------------------------------------------------------------------
|
23
|
+
#
|
24
|
+
#
|
25
|
+
|
26
|
+
# This version of HttpClient has been modified for use in DistribuStream
|
27
|
+
# Notable changes:
|
28
|
+
# - Moved into PDTP::Client namespace
|
29
|
+
# - HTTP/1.1 switched to HTTP/1.0
|
30
|
+
# - Introduced support for HTTP ranges
|
31
|
+
# - Support for X headers
|
32
|
+
|
33
|
+
module PDTP
|
34
|
+
class Client
|
35
|
+
class HttpClient < EventMachine::Protocols::HttpClient
|
36
|
+
# Override send_request to support the additional features we need
|
37
|
+
def send_request args
|
38
|
+
args[:verb] ||= args[:method] # Support :method as an alternative to :verb.
|
39
|
+
args[:verb] ||= :get # IS THIS A GOOD IDEA, to default to GET if nothing was specified?
|
40
|
+
|
41
|
+
verb = args[:verb].to_s.upcase
|
42
|
+
unless ["GET", "POST", "PUT", "DELETE", "HEAD"].include?(verb)
|
43
|
+
set_deferred_status :failed, {:status => 0} # TODO, not signalling the error type
|
44
|
+
return # NOTE THE EARLY RETURN, we're not sending any data.
|
45
|
+
end
|
46
|
+
|
47
|
+
request = args[:request] || "/"
|
48
|
+
unless request[0,1] == "/"
|
49
|
+
request = "/" + request
|
50
|
+
end
|
51
|
+
|
52
|
+
qs = args[:query_string] || ""
|
53
|
+
if qs.length > 0 and qs[0,1] != '?'
|
54
|
+
qs = "?" + qs
|
55
|
+
end
|
56
|
+
|
57
|
+
# Allow an override for the host header if it's not the connect-string.
|
58
|
+
port = args[:port]
|
59
|
+
host = args[:host_header] || "#{args[:host]}#{port}" || "_"
|
60
|
+
|
61
|
+
# POST items.
|
62
|
+
postcontenttype = args[:contenttype] || "application/octet-stream"
|
63
|
+
postcontent = args[:content] || ""
|
64
|
+
raise "oversized content in HTTP POST" if postcontent.length > MaxPostContentLength
|
65
|
+
|
66
|
+
# ESSENTIAL for the request's line-endings to be CRLF, not LF. Some servers misbehave otherwise.
|
67
|
+
# TODO: We ASSUME the caller wants to send a 1.1 request. May not be a good assumption.
|
68
|
+
req = [
|
69
|
+
"#{verb} #{request}#{qs} HTTP/1.1",
|
70
|
+
"Host: #{host}",
|
71
|
+
"User-Agent: #{args[:agent] || 'Ruby EventMachine'}"
|
72
|
+
]
|
73
|
+
|
74
|
+
if verb == "POST" || verb == "PUT"
|
75
|
+
req << "Content-Type: #{postcontenttype}"
|
76
|
+
req << "Content-Length: #{postcontent.length}"
|
77
|
+
end
|
78
|
+
|
79
|
+
if args[:range]
|
80
|
+
# Convert the range to an array if it isn't one already
|
81
|
+
args[:range] = [args[:range]] unless args[:range].is_a?(Array)
|
82
|
+
|
83
|
+
# Transform it to a text string
|
84
|
+
range = args[:range].map { |v| v.is_a?(Range) ? "#{v.min}-#{v.max}" : (v >= 0 ? "#{v}-" : v.to_s) }.join(',')
|
85
|
+
req << "Range: bytes=#{range}"
|
86
|
+
end
|
87
|
+
|
88
|
+
if args[:headers]
|
89
|
+
headers = case args[:headers]
|
90
|
+
when Array then args[:headers]
|
91
|
+
when String then [args[:headers]]
|
92
|
+
else raise ArgumentError, "headers must be an array or string"
|
93
|
+
end
|
94
|
+
|
95
|
+
req += headers
|
96
|
+
end
|
97
|
+
|
98
|
+
# TODO, this cookie handler assumes it's getting a single, semicolon-delimited string.
|
99
|
+
# Eventually we will want to deal intelligently with arrays and hashes.
|
100
|
+
if args[:cookie]
|
101
|
+
req << "Cookie: #{args[:cookie]}"
|
102
|
+
end
|
103
|
+
|
104
|
+
req << ""
|
105
|
+
reqstring = req.map {|l| "#{l}\r\n"}.join
|
106
|
+
send_data reqstring
|
107
|
+
|
108
|
+
if verb == "POST" || verb == "PUT"
|
109
|
+
send_data postcontent
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
data/lib/pdtp/client/transfer.rb
CHANGED
@@ -8,12 +8,15 @@
|
|
8
8
|
# See http://distribustream.rubyforge.org/
|
9
9
|
#++
|
10
10
|
|
11
|
-
require File.dirname(__FILE__) + '/file_service'
|
12
11
|
require "thread"
|
13
|
-
require "net/http"
|
12
|
+
#require "net/http"
|
14
13
|
require "uri"
|
15
14
|
require "digest/sha2"
|
16
15
|
|
16
|
+
require File.dirname(__FILE__) + '/../common'
|
17
|
+
require File.dirname(__FILE__) + '/file_service'
|
18
|
+
require File.dirname(__FILE__) + '/http_client'
|
19
|
+
|
17
20
|
module PDTP
|
18
21
|
class Client
|
19
22
|
module Transfer
|
@@ -33,10 +36,10 @@ module PDTP
|
|
33
36
|
|
34
37
|
# Returns true if a server message matches this transfer
|
35
38
|
def matches_message?(message)
|
36
|
-
@peer
|
37
|
-
@url
|
39
|
+
@peer == message["peer"] and
|
40
|
+
@url == message["url"] and
|
38
41
|
@byte_range == message["range"] and
|
39
|
-
@peer_id
|
42
|
+
@peer_id == message["peer_id"]
|
40
43
|
end
|
41
44
|
|
42
45
|
# Takes an HTTP range and returns a ruby Range object
|
@@ -177,50 +180,57 @@ module PDTP
|
|
177
180
|
# Implements http transfer between two peers from the connector's (client) perspective
|
178
181
|
class Connector < Base
|
179
182
|
def initialize(connection, message, file_service)
|
180
|
-
@
|
181
|
-
@
|
182
|
-
@
|
183
|
-
@
|
184
|
-
@
|
185
|
-
@
|
186
|
-
@
|
183
|
+
@connection = connection
|
184
|
+
@file_service = file_service
|
185
|
+
@peer = message["host"]
|
186
|
+
@port = message["port"]
|
187
|
+
@method = message["method"]
|
188
|
+
@url = message["url"]
|
189
|
+
@byte_range = message["range"]
|
190
|
+
@peer_id = message["peer_id"]
|
187
191
|
end
|
188
192
|
|
189
193
|
# Perform the transfer
|
190
194
|
def run
|
191
|
-
|
192
|
-
|
193
|
-
info=@file_service.get_info(@url)
|
195
|
+
info = @file_service.get_info(@url)
|
194
196
|
|
195
197
|
#compute the vhost and path
|
196
198
|
#FIXME work with ports
|
197
|
-
uri=URI.
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
199
|
+
uri = URI.parse(@url)
|
200
|
+
|
201
|
+
options = {
|
202
|
+
:method => @method,
|
203
|
+
:host => @peer,
|
204
|
+
:host_header => uri.host,
|
205
|
+
:port => @port,
|
206
|
+
:request => uri.path,
|
207
|
+
:range => @byte_range,
|
208
|
+
:agent => "DistribuStream #{PDTP::VERSION}",
|
209
|
+
:headers => "X-PDTP-Peer-Id: #{@connection.client.client_id}"
|
210
|
+
}
|
211
|
+
|
212
|
+
case @method
|
213
|
+
when 'get'
|
214
|
+
when 'put' then options[:content] = info.read(@byte_range)
|
215
|
+
else raise HTTPException.new(405, "Invalid method: #{@method}")
|
216
|
+
end
|
217
|
+
|
218
|
+
http = HttpClient.request options
|
219
|
+
http.callback do |response|
|
220
|
+
hash = nil
|
221
|
+
|
222
|
+
if @method == 'get' and response[:status] == 206
|
223
|
+
@@log.debug("Body Downloaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
|
224
|
+
info.write(@byte_range.first,response[:content])
|
225
|
+
hash = Digest::SHA256.hexdigest(response[:content]) rescue nil
|
226
|
+
elsif @method == 'put' and response[:status] == 200
|
227
|
+
@@log.debug("Body Uploaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
|
228
|
+
else
|
229
|
+
raise RuntimeError, "Invalid method/status combination: #{@method} #{response[:status]}"
|
230
|
+
end
|
231
|
+
|
232
|
+
send_completed_message hash
|
209
233
|
end
|
210
|
-
|
211
|
-
req.add_field("Range", "bytes=#{@byte_range.begin}-#{@byte_range.end}")
|
212
|
-
req.add_field("Host",vhost)
|
213
|
-
req.add_field("X-PDTP-Peer-Id", @connection.client.client_id)
|
214
|
-
res = Net::HTTP.start(@peer,@port) {|http| http.request(req,body) }
|
215
|
-
|
216
|
-
if res.code == '206' and @method == 'get'
|
217
|
-
#we are the taker
|
218
|
-
@@log.debug("Body Downloaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
|
219
|
-
info.write(@byte_range.first,res.body)
|
220
|
-
@hash=Digest::SHA256.hexdigest(res.body) rescue nil
|
221
|
-
else
|
222
|
-
raise "HTTP RESPONSE: code=#{res.code} body=#{res.body}"
|
223
|
-
end
|
224
234
|
end
|
225
235
|
end
|
226
236
|
end
|
data/lib/pdtp/common.rb
ADDED
@@ -0,0 +1,15 @@
|
|
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
|
+
# Namespace for all PDTP components
|
12
|
+
module PDTP
|
13
|
+
PDTP::VERSION = '0.2.1' unless defined? PDTP::VERSION
|
14
|
+
def self.version() VERSION end
|
15
|
+
end
|
@@ -30,7 +30,7 @@ CONFIG_TYPES = {
|
|
30
30
|
:quiet => :bool,
|
31
31
|
:chunk_size => :int,
|
32
32
|
:request_url => :string
|
33
|
-
}
|
33
|
+
} unless defined? CONFIG_TYPES
|
34
34
|
|
35
35
|
#prints banner and loads config file
|
36
36
|
def common_init(program_name, config = nil)
|
@@ -119,4 +119,4 @@ end
|
|
119
119
|
#responds to config options that are used by both client and server
|
120
120
|
def handle_config_options
|
121
121
|
@@log.level=Logger::INFO if @@config[:quiet]
|
122
|
-
end
|
122
|
+
end
|
@@ -12,58 +12,71 @@ require 'rubygems'
|
|
12
12
|
require 'eventmachine'
|
13
13
|
|
14
14
|
module PDTP
|
15
|
-
# EventMachine connection adapter for
|
16
|
-
class
|
17
|
-
# Class for processing
|
15
|
+
# EventMachine connection adapter for length prefix framing
|
16
|
+
class LengthPrefixProtocol < EventMachine::Connection
|
17
|
+
# Class for processing size prefixes in packet frames
|
18
18
|
class Prefix
|
19
|
-
attr_reader :
|
19
|
+
attr_reader :size, :data
|
20
20
|
|
21
|
-
def initialize
|
22
|
-
|
23
|
-
|
21
|
+
def initialize(size = 2)
|
22
|
+
unless size == 2 or size == 4
|
23
|
+
raise ArgumentError, 'only 2 or 4 byte prefixes are supported'
|
24
|
+
end
|
25
|
+
|
26
|
+
@size = size
|
24
27
|
reset!
|
25
28
|
end
|
26
29
|
|
30
|
+
# Has the entire prefix been read yet?
|
27
31
|
def read?
|
28
|
-
@
|
32
|
+
@size == @read
|
29
33
|
end
|
30
34
|
|
35
|
+
# Append data to the prefix and return any extra
|
31
36
|
def append(data)
|
32
|
-
toread = @
|
37
|
+
toread = @size - @read
|
33
38
|
new_data = data[0..(toread - 1)]
|
34
39
|
|
35
40
|
@data << new_data
|
36
|
-
@read += new_data.
|
37
|
-
return nil unless @read == @
|
41
|
+
@read += new_data.length
|
42
|
+
return nil unless @read == @size
|
38
43
|
|
39
|
-
@
|
40
|
-
result = data[toread..data.
|
44
|
+
@length = @data.unpack(@size == 2 ? 'n' : 'N').first
|
45
|
+
result = data[toread..data.length]
|
41
46
|
|
42
47
|
return nil if result.nil? or result.empty?
|
43
48
|
result
|
44
49
|
end
|
45
50
|
|
46
|
-
|
47
|
-
|
48
|
-
|
51
|
+
# Length of the payload extracted from the prefix
|
52
|
+
def length
|
53
|
+
raise RuntimeError, 'length called before prefix extracted' unless read?
|
54
|
+
@length
|
49
55
|
end
|
50
56
|
|
51
57
|
def reset!
|
52
|
-
@
|
58
|
+
@length = nil
|
53
59
|
@read = 0
|
54
60
|
@data = ''
|
55
61
|
end
|
56
62
|
end
|
57
63
|
|
58
|
-
def initialize
|
64
|
+
def initialize *args
|
59
65
|
super
|
66
|
+
|
60
67
|
@prefix = Prefix.new
|
61
68
|
@buffer = ''
|
62
69
|
end
|
63
70
|
|
71
|
+
def prefix_size=(size)
|
72
|
+
data = @prefix.data << @buffer
|
73
|
+
@prefix = Prefix.new size
|
74
|
+
receive_data data
|
75
|
+
end
|
76
|
+
|
64
77
|
# Callback for processing incoming frames
|
65
78
|
def receive_data(data)
|
66
|
-
# Read data and append it to the
|
79
|
+
# Read data and append it to the size prefix unless it's already been read
|
67
80
|
data = @prefix.append(data) unless @prefix.read?
|
68
81
|
return if data.nil?
|
69
82
|
|
@@ -71,14 +84,16 @@ module PDTP
|
|
71
84
|
@buffer << data
|
72
85
|
|
73
86
|
# Don't do anything until we receive the specified amount of data
|
74
|
-
return unless @buffer.
|
87
|
+
return unless @buffer.length >= @prefix.length
|
75
88
|
|
76
89
|
# Extract the specified amount of data and process it
|
77
|
-
data = @buffer[0..(@prefix.
|
78
|
-
receive_packet data
|
90
|
+
data = @buffer[0..(@prefix.length - 1)]
|
79
91
|
|
80
92
|
# Store any remaining data
|
81
|
-
remainder = @buffer[@prefix.
|
93
|
+
remainder = @buffer[@prefix.length..@buffer.length]
|
94
|
+
|
95
|
+
# Invoke receive_packet and allow the user to process the data
|
96
|
+
receive_packet data
|
82
97
|
|
83
98
|
# Reset the prefix and buffer since we've received a whole frame
|
84
99
|
@prefix.reset!
|
@@ -95,12 +110,10 @@ module PDTP
|
|
95
110
|
def receive_packet(packet)
|
96
111
|
end
|
97
112
|
|
98
|
-
# Send a packet with a specified
|
113
|
+
# Send a packet with a specified size prefix
|
99
114
|
def send_packet(data)
|
100
|
-
length
|
101
|
-
|
102
|
-
|
103
|
-
send_data [length].pack('n') << data
|
115
|
+
raise ArgumentError, 'packet too long for prefix length' if data.size >= 256**@prefix.size
|
116
|
+
send_data [data.size].pack(@prefix.size == 2 ? 'n' : 'N') << data
|
104
117
|
end
|
105
118
|
end
|
106
|
-
end
|
119
|
+
end
|
data/lib/pdtp/common/protocol.rb
CHANGED
@@ -19,10 +19,10 @@ rescue LoadError
|
|
19
19
|
require 'json'
|
20
20
|
end
|
21
21
|
|
22
|
-
require File.dirname(__FILE__) + '/
|
22
|
+
require File.dirname(__FILE__) + '/length_prefix_protocol.rb'
|
23
23
|
|
24
24
|
module PDTP
|
25
|
-
PROTOCOL_DEBUG=true
|
25
|
+
PDTP::PROTOCOL_DEBUG = true unless defined? PDTP::PROTOCOL_DEBUG
|
26
26
|
|
27
27
|
class ProtocolError < Exception
|
28
28
|
end
|
@@ -31,7 +31,7 @@ module PDTP
|
|
31
31
|
end
|
32
32
|
|
33
33
|
# EventMachine handler class for the PDTP protocol
|
34
|
-
class Protocol < PDTP::
|
34
|
+
class Protocol < PDTP::LengthPrefixProtocol
|
35
35
|
@@num_connections = 0
|
36
36
|
@@message_params = nil
|
37
37
|
@connection_open = false
|
@@ -40,10 +40,10 @@ module PDTP
|
|
40
40
|
@connection_open
|
41
41
|
end
|
42
42
|
|
43
|
-
def initialize
|
43
|
+
def initialize *args
|
44
44
|
user_data = nil
|
45
45
|
@mutex = Mutex.new
|
46
|
-
super
|
46
|
+
super
|
47
47
|
end
|
48
48
|
|
49
49
|
#called by EventMachine after a connection has been established
|
@@ -67,8 +67,8 @@ module PDTP
|
|
67
67
|
#close a connection, but first send the specified error message
|
68
68
|
def error_close_connection(error)
|
69
69
|
if PROTOCOL_DEBUG
|
70
|
-
send_message :protocol_error, :message =>
|
71
|
-
close_connection
|
70
|
+
send_message :protocol_error, :message => error
|
71
|
+
close_connection true # close after writing
|
72
72
|
else
|
73
73
|
close_connection
|
74
74
|
end
|
@@ -105,7 +105,7 @@ module PDTP
|
|
105
105
|
end
|
106
106
|
end
|
107
107
|
|
108
|
-
RANGENAMES = %w{chunk_range range byte_range}
|
108
|
+
PDTP::RANGENAMES = %w{chunk_range range byte_range} unless defined? PDTP::RANGENAMES
|
109
109
|
|
110
110
|
#converts Ruby Range classes in the message to PDTP protocol hashes with min and max
|
111
111
|
# 0..-1 => nil (entire file)
|
@@ -333,4 +333,4 @@ module PDTP
|
|
333
333
|
mp
|
334
334
|
end
|
335
335
|
end
|
336
|
-
end
|
336
|
+
end
|
data/lib/pdtp/server.rb
CHANGED
@@ -12,6 +12,7 @@ require 'rubygems'
|
|
12
12
|
require 'eventmachine'
|
13
13
|
require 'mongrel'
|
14
14
|
|
15
|
+
require File.dirname(__FILE__) + '/common'
|
15
16
|
require File.dirname(__FILE__) + '/server/dispatcher'
|
16
17
|
require File.dirname(__FILE__) + '/server/file_service'
|
17
18
|
require File.dirname(__FILE__) + '/server/connection'
|
@@ -24,14 +24,14 @@ module PDTP
|
|
24
24
|
class Protocol < PDTP::Client::Connection
|
25
25
|
attr_reader :client, :connection, :file_service, :client_id, :transfers, :lock
|
26
26
|
|
27
|
-
def initialize
|
27
|
+
def initialize *args
|
28
28
|
@transfers = []
|
29
29
|
@lock = Mutex.new
|
30
30
|
@client = self
|
31
31
|
@connection = self
|
32
32
|
@client_id = Digest::MD5.hexdigest "#{Time.now.to_f}#{$$}"
|
33
33
|
|
34
|
-
super
|
34
|
+
super
|
35
35
|
end
|
36
36
|
|
37
37
|
# Called after a connection to the server has been established
|
@@ -113,4 +113,4 @@ module PDTP
|
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
116
|
-
end
|
116
|
+
end
|
metadata
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.
|
2
|
+
rubygems_version: 0.9.4
|
3
3
|
specification_version: 1
|
4
4
|
name: distribustream
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.2.
|
6
|
+
version: 0.2.1
|
7
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:
|
@@ -38,20 +38,21 @@ files:
|
|
38
38
|
- bin/dstream
|
39
39
|
- lib/pdtp
|
40
40
|
- lib/pdtp/client
|
41
|
-
- lib/pdtp/client.rb
|
42
|
-
- lib/pdtp/common
|
43
|
-
- lib/pdtp/server
|
44
|
-
- lib/pdtp/server.rb
|
45
41
|
- lib/pdtp/client/callbacks.rb
|
46
42
|
- lib/pdtp/client/connection.rb
|
47
43
|
- lib/pdtp/client/file_buffer.rb
|
48
44
|
- lib/pdtp/client/file_service.rb
|
45
|
+
- lib/pdtp/client/http_client.rb
|
49
46
|
- lib/pdtp/client/http_handler.rb
|
50
47
|
- lib/pdtp/client/transfer.rb
|
48
|
+
- lib/pdtp/client.rb
|
49
|
+
- lib/pdtp/common
|
51
50
|
- lib/pdtp/common/common_init.rb
|
52
51
|
- lib/pdtp/common/file_service.rb
|
53
|
-
- lib/pdtp/common/
|
52
|
+
- lib/pdtp/common/length_prefix_protocol.rb
|
54
53
|
- lib/pdtp/common/protocol.rb
|
54
|
+
- lib/pdtp/common.rb
|
55
|
+
- lib/pdtp/server
|
55
56
|
- lib/pdtp/server/client_info.rb
|
56
57
|
- lib/pdtp/server/connection.rb
|
57
58
|
- lib/pdtp/server/dispatcher.rb
|
@@ -60,6 +61,7 @@ files:
|
|
60
61
|
- lib/pdtp/server/stats_handler.rb
|
61
62
|
- lib/pdtp/server/transfer.rb
|
62
63
|
- lib/pdtp/server/trust.rb
|
64
|
+
- lib/pdtp/server.rb
|
63
65
|
- conf/bigchunk.yml
|
64
66
|
- conf/debug.yml
|
65
67
|
- conf/example.yml
|