distribustream 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|