distribustream 0.2.1 → 0.3.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 +18 -0
- data/README +5 -41
- data/bin/dsclient +23 -10
- data/bin/dstream +70 -7
- data/distribustream.gemspec +6 -6
- data/lib/pdtp/client.rb +35 -19
- data/lib/pdtp/client/callbacks.rb +18 -7
- data/lib/pdtp/client/connection.rb +31 -25
- data/lib/pdtp/client/file_buffer.rb +14 -2
- data/lib/pdtp/client/file_service.rb +17 -11
- data/lib/pdtp/client/http_client.rb +18 -19
- data/lib/pdtp/client/http_handler.rb +21 -11
- data/lib/pdtp/client/transfer.rb +29 -29
- data/lib/pdtp/common.rb +19 -4
- data/lib/pdtp/common/file_service.rb +14 -2
- data/lib/pdtp/common/http_server.rb +217 -0
- data/lib/pdtp/common/length_prefix_protocol.rb +14 -2
- data/lib/pdtp/common/protocol.rb +23 -16
- data/lib/pdtp/server.rb +101 -26
- data/lib/pdtp/server/client_info.rb +14 -2
- data/lib/pdtp/server/connection.rb +31 -9
- data/lib/pdtp/server/dispatcher.rb +37 -87
- data/lib/pdtp/server/file_service.rb +17 -2
- data/lib/pdtp/server/file_service_protocol.rb +23 -38
- data/lib/pdtp/server/status_handler.rb +103 -0
- data/lib/pdtp/server/transfer.rb +21 -9
- data/lib/pdtp/server/trust.rb +14 -2
- data/pdtp-specification.xml +831 -0
- metadata +8 -8
- data/bin/dsseed +0 -32
- data/lib/pdtp/common/common_init.rb +0 -122
- data/lib/pdtp/server/stats_handler.rb +0 -23
@@ -1,11 +1,23 @@
|
|
1
1
|
#--
|
2
2
|
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
-
#
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
4
16
|
#
|
5
17
|
# This source file is distributed as part of the
|
6
18
|
# DistribuStream file transfer system.
|
7
19
|
#
|
8
|
-
# See http://distribustream.
|
20
|
+
# See http://distribustream.org/
|
9
21
|
#++
|
10
22
|
|
11
23
|
require 'rubygems'
|
@@ -1,18 +1,25 @@
|
|
1
1
|
#--
|
2
2
|
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
-
#
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
4
16
|
#
|
5
17
|
# This source file is distributed as part of the
|
6
18
|
# DistribuStream file transfer system.
|
7
19
|
#
|
8
|
-
# See http://distribustream.
|
20
|
+
# See http://distribustream.org/
|
9
21
|
#++
|
10
22
|
|
11
|
-
require 'rubygems'
|
12
|
-
require 'mongrel'
|
13
|
-
require 'uri'
|
14
|
-
require 'pathname'
|
15
|
-
|
16
23
|
require File.dirname(__FILE__) + '/../common/file_service.rb'
|
17
24
|
require File.dirname(__FILE__) + '/file_buffer.rb'
|
18
25
|
|
@@ -23,25 +30,24 @@ module PDTP
|
|
23
30
|
class FileInfo < PDTP::FileInfo
|
24
31
|
def initialize(filename, io = nil)
|
25
32
|
@buffer = FileBuffer.new io || open(filename, 'w')
|
26
|
-
@lock = Mutex.new
|
27
33
|
end
|
28
34
|
|
29
35
|
# Write data into buffer starting at start_pos
|
30
36
|
def write(start_pos,data)
|
31
|
-
@
|
37
|
+
@buffer.write start_pos, data
|
32
38
|
end
|
33
39
|
|
34
40
|
# Read a range of data out of buffer. Takes a ruby Range object
|
35
41
|
def read(range)
|
36
42
|
begin
|
37
|
-
@
|
43
|
+
@buffer.read range
|
38
44
|
rescue nil
|
39
45
|
end
|
40
46
|
end
|
41
47
|
|
42
48
|
# Return the number of bytes currently stored
|
43
49
|
def bytes_downloaded
|
44
|
-
@
|
50
|
+
@buffer.bytes_stored
|
45
51
|
end
|
46
52
|
end
|
47
53
|
|
@@ -1,34 +1,33 @@
|
|
1
|
-
|
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
|
-
#
|
1
|
+
#--
|
12
2
|
# Copyright (C) 2006-07 by Francis Cianfrocca. All Rights Reserved.
|
13
3
|
# Gmail: blackhedd
|
4
|
+
# Modifications (C) 2007 ClickCaster, Inc.
|
5
|
+
#
|
6
|
+
# This program is free software: you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation, either version 3 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# This program is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
14
|
+
# GNU General Public License for more details.
|
14
15
|
#
|
15
|
-
#
|
16
|
-
#
|
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.
|
16
|
+
# You should have received a copy of the GNU General Public License
|
17
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
18
|
#
|
20
|
-
#
|
19
|
+
# This source file is distributed as part of the
|
20
|
+
# DistribuStream file transfer system.
|
21
21
|
#
|
22
|
-
|
22
|
+
# See http://distribustream.org/
|
23
23
|
#
|
24
|
-
#
|
25
|
-
|
26
24
|
# This version of HttpClient has been modified for use in DistribuStream
|
27
25
|
# Notable changes:
|
28
26
|
# - Moved into PDTP::Client namespace
|
29
27
|
# - HTTP/1.1 switched to HTTP/1.0
|
30
28
|
# - Introduced support for HTTP ranges
|
31
29
|
# - Support for X headers
|
30
|
+
#++
|
32
31
|
|
33
32
|
module PDTP
|
34
33
|
class Client
|
@@ -1,11 +1,23 @@
|
|
1
1
|
#--
|
2
2
|
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
-
#
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
4
16
|
#
|
5
17
|
# This source file is distributed as part of the
|
6
18
|
# DistribuStream file transfer system.
|
7
19
|
#
|
8
|
-
# See http://distribustream.
|
20
|
+
# See http://distribustream.org/
|
9
21
|
#++
|
10
22
|
|
11
23
|
require 'rubygems'
|
@@ -27,12 +39,12 @@ module PDTP
|
|
27
39
|
# This method is called after a connection to the server
|
28
40
|
# has been successfully established.
|
29
41
|
def connection_created(connection)
|
30
|
-
|
42
|
+
#@@log.debug("[mongrel] Opened connection...");
|
31
43
|
end
|
32
44
|
|
33
45
|
# This method is called when the server connection is destroyed
|
34
46
|
def connection_destroyed(connection)
|
35
|
-
|
47
|
+
#@@log.debug("[mongrel] Closed connection...")
|
36
48
|
end
|
37
49
|
|
38
50
|
# Returns a transfer object if the given connection is a peer associated with
|
@@ -42,11 +54,10 @@ module PDTP
|
|
42
54
|
nil
|
43
55
|
end
|
44
56
|
|
45
|
-
# This method is called when an HTTP request is received.
|
46
|
-
|
47
|
-
def process(request,response)
|
57
|
+
# This method is called when an HTTP request is received.
|
58
|
+
def process(request,response)
|
48
59
|
begin
|
49
|
-
|
60
|
+
#@@log.debug "Creating Transfer::Listener"
|
50
61
|
transfer = Transfer::Listener.new(
|
51
62
|
@client.connection,
|
52
63
|
request,
|
@@ -54,9 +65,8 @@ module PDTP
|
|
54
65
|
client.file_service
|
55
66
|
)
|
56
67
|
|
57
|
-
|
58
|
-
|
59
|
-
transfer.handle_header
|
68
|
+
@client.transfers << transfer
|
69
|
+
transfer.handle_header
|
60
70
|
rescue Exception => e
|
61
71
|
raise e if transfer.nil?
|
62
72
|
transfer.write_http_exception(e)
|
data/lib/pdtp/client/transfer.rb
CHANGED
@@ -1,15 +1,25 @@
|
|
1
1
|
#--
|
2
2
|
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
-
#
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
4
16
|
#
|
5
17
|
# This source file is distributed as part of the
|
6
18
|
# DistribuStream file transfer system.
|
7
19
|
#
|
8
|
-
# See http://distribustream.
|
20
|
+
# See http://distribustream.org/
|
9
21
|
#++
|
10
22
|
|
11
|
-
require "thread"
|
12
|
-
#require "net/http"
|
13
23
|
require "uri"
|
14
24
|
require "digest/sha2"
|
15
25
|
|
@@ -92,20 +102,16 @@ module PDTP
|
|
92
102
|
out.write(e.to_s + "\n\n" + e.backtrace.join("\n") )
|
93
103
|
end
|
94
104
|
else
|
95
|
-
|
105
|
+
STDERR.write "MONGREL SERVER ERROR: exception: " + e.to_s+"\n\n"+e.backtrace.join("\n")+"\n"
|
96
106
|
@response.start(500) do |head,out|
|
97
|
-
out.write("Server error, unknown exception:"+e.to_s + "\n\n" + e.backtrace.join("\n") )
|
107
|
+
out.write("Server error, unknown exception: "+e.to_s + "\n\n" + e.backtrace.join("\n") )
|
98
108
|
end
|
99
109
|
end
|
100
110
|
end
|
101
111
|
|
102
112
|
# Parse the HTTP header and ask for verification of transfer
|
103
|
-
# Thread is stopped after asking for verification and will
|
104
|
-
# be restarted when verification arrives
|
105
113
|
def handle_header
|
106
|
-
@
|
107
|
-
|
108
|
-
@@log.debug "params=#{@request.params.inspect}"
|
114
|
+
#@@log.debug "params=#{@request.params.inspect}"
|
109
115
|
|
110
116
|
@method=@request.params["REQUEST_METHOD"].downcase
|
111
117
|
@peer=@request.params["REMOTE_ADDR"]
|
@@ -124,33 +130,24 @@ module PDTP
|
|
124
130
|
raise HTTPException.new(400, "Missing Range header") if @byte_range.nil?
|
125
131
|
|
126
132
|
send_ask_verify_message
|
127
|
-
|
128
|
-
after_verification
|
133
|
+
@response.pending = true
|
129
134
|
end
|
130
135
|
|
131
136
|
# Called after receiving verification message from the server
|
132
|
-
# Set the authorized status and restart the thread
|
133
|
-
# This throws us into after_verification
|
134
137
|
def tell_verify(authorized)
|
135
|
-
@authorized=authorized
|
136
|
-
@thread.run
|
137
|
-
end
|
138
|
-
|
139
|
-
# Perform the transfer if verification was successful
|
140
|
-
def after_verification
|
141
138
|
#check if the server authorized us
|
142
|
-
unless
|
139
|
+
unless authorized
|
143
140
|
raise HTTPException.new(403,"Forbidden: the server did not authorize this transfer")
|
144
141
|
end
|
145
142
|
|
146
143
|
info = @file_service.get_info(@url)
|
147
144
|
if @method == "put"
|
148
145
|
#we are the taker
|
149
|
-
|
146
|
+
#@@log.debug("Body Downloaded: url=#{@url} range=#{@byte_range} peer=#{@peer}")
|
150
147
|
|
151
148
|
@file_service.set_info(FileInfo.new) if info.nil?
|
152
|
-
info.write
|
153
|
-
@hash=Digest::SHA256.hexdigest(res.body) rescue nil
|
149
|
+
info.write @byte_range.first, @request.body.read
|
150
|
+
@hash = Digest::SHA256.hexdigest(res.body) rescue nil
|
154
151
|
|
155
152
|
# Stock HTTP OK response
|
156
153
|
@response.start(200) do |head,out|
|
@@ -158,7 +155,7 @@ module PDTP
|
|
158
155
|
elsif @method=="get"
|
159
156
|
#we are the giver
|
160
157
|
raise HTTPException.new(404,"File not found: #{@url}") if info.nil?
|
161
|
-
data=info.read
|
158
|
+
data = info.read @byte_range
|
162
159
|
raise HTTPException.new(416,"Invalid range: #{@byte_range.inspect}") if data.nil?
|
163
160
|
|
164
161
|
#Request was GET, so now we need to send the data
|
@@ -167,8 +164,11 @@ module PDTP
|
|
167
164
|
head['Content-Range'] = "bytes #{@byte_range.first}-#{@byte_range.last}/*"
|
168
165
|
#FIXME must include a DATE header according to http
|
169
166
|
|
170
|
-
out.write
|
167
|
+
out.write data
|
171
168
|
end
|
169
|
+
|
170
|
+
# Call response.finished ourselves since we left the response pending
|
171
|
+
@response.finished
|
172
172
|
else
|
173
173
|
raise HTTPException.new(405,"Invalid method: #{@method}")
|
174
174
|
end
|
@@ -220,11 +220,11 @@ module PDTP
|
|
220
220
|
hash = nil
|
221
221
|
|
222
222
|
if @method == 'get' and response[:status] == 206
|
223
|
-
|
223
|
+
#@@log.debug("Body Downloaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
|
224
224
|
info.write(@byte_range.first,response[:content])
|
225
225
|
hash = Digest::SHA256.hexdigest(response[:content]) rescue nil
|
226
226
|
elsif @method == 'put' and response[:status] == 200
|
227
|
-
|
227
|
+
#@@log.debug("Body Uploaded: url=#{@url} range=#{@byte_range} peer=#{@peer}:#{@port}")
|
228
228
|
else
|
229
229
|
raise RuntimeError, "Invalid method/status combination: #{@method} #{response[:status]}"
|
230
230
|
end
|
data/lib/pdtp/common.rb
CHANGED
@@ -1,15 +1,30 @@
|
|
1
1
|
#--
|
2
2
|
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
-
#
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
4
16
|
#
|
5
17
|
# This source file is distributed as part of the
|
6
18
|
# DistribuStream file transfer system.
|
7
19
|
#
|
8
|
-
# See http://distribustream.
|
20
|
+
# See http://distribustream.org/
|
9
21
|
#++
|
10
22
|
|
11
23
|
# Namespace for all PDTP components
|
12
24
|
module PDTP
|
13
|
-
PDTP::VERSION = '0.
|
25
|
+
PDTP::VERSION = '0.3.0' unless defined? PDTP::VERSION
|
14
26
|
def self.version() VERSION end
|
15
|
-
|
27
|
+
|
28
|
+
PDTP::DEFAULT_PORT = 6086 unless defined? PDTP::DEFAULT_PORT
|
29
|
+
def self.default_port() DEFAULT_PORT end
|
30
|
+
end
|
@@ -1,11 +1,23 @@
|
|
1
1
|
#--
|
2
2
|
# Copyright (C) 2006-07 ClickCaster, Inc. (info@clickcaster.com)
|
3
|
-
#
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU General Public License as published by
|
6
|
+
# the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This program is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU General Public License
|
15
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
4
16
|
#
|
5
17
|
# This source file is distributed as part of the
|
6
18
|
# DistribuStream file transfer system.
|
7
19
|
#
|
8
|
-
# See http://distribustream.
|
20
|
+
# See http://distribustream.org/
|
9
21
|
#++
|
10
22
|
|
11
23
|
module PDTP
|
@@ -0,0 +1,217 @@
|
|
1
|
+
# This module rewrites pieces of the very good Mongrel web server in
|
2
|
+
# order to change it from a threaded application to an event based
|
3
|
+
# application running inside an EventMachine event loop. It should
|
4
|
+
# be compatible with the existing Mongrel handlers for Rails,
|
5
|
+
# Camping, Nitro, etc....
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'eventmachine'
|
9
|
+
require 'mongrel'
|
10
|
+
|
11
|
+
module Mongrel
|
12
|
+
class Protocol < EventMachine::Connection
|
13
|
+
def post_init
|
14
|
+
@parser = HttpParser.new
|
15
|
+
@params = HttpParams.new
|
16
|
+
@nparsed = 0
|
17
|
+
@request = nil
|
18
|
+
@request_len = nil
|
19
|
+
@linebuffer = ''
|
20
|
+
end
|
21
|
+
|
22
|
+
def receive_data data
|
23
|
+
@linebuffer << data
|
24
|
+
@nparsed = @parser.execute(@params, @linebuffer, @nparsed) unless @parser.finished?
|
25
|
+
if @parser.finished?
|
26
|
+
if @request_len.nil?
|
27
|
+
@request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
|
28
|
+
script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
|
29
|
+
if handlers
|
30
|
+
@params[::Mongrel::Const::PATH_INFO] = path_info
|
31
|
+
@params[::Mongrel::Const::SCRIPT_NAME] = script_name
|
32
|
+
@params[::Mongrel::Const::REMOTE_ADDR] = @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] || ::Socket.unpack_sockaddr_in(get_peername)[1]
|
33
|
+
@notifiers = handlers.select { |h| h.request_notify }
|
34
|
+
end
|
35
|
+
if @request_len > ::Mongrel::Const::MAX_BODY
|
36
|
+
new_buffer = Tempfile.new(::Mongrel::Const::MONGREL_TMP_BASE)
|
37
|
+
new_buffer.binmode
|
38
|
+
new_buffer << @linebuffer[@nparsed..-1]
|
39
|
+
@linebuffer = new_buffer
|
40
|
+
else
|
41
|
+
@linebuffer = StringIO.new(@linebuffer[@nparsed..-1])
|
42
|
+
@linebuffer.pos = @linebuffer.length
|
43
|
+
end
|
44
|
+
end
|
45
|
+
if @linebuffer.length >= @request_len
|
46
|
+
@linebuffer.rewind
|
47
|
+
::Mongrel::HttpServer::Instance.process_http_request(@params,@linebuffer,self)
|
48
|
+
@linebuffer.delete if Tempfile === @linebuffer
|
49
|
+
end
|
50
|
+
elsif @linebuffer.length > ::Mongrel::Const::MAX_HEADER
|
51
|
+
close_connection
|
52
|
+
raise ::Mongrel::HttpParserError.new("HEADER is longer than allowed, aborting client early.")
|
53
|
+
end
|
54
|
+
rescue ::Mongrel::HttpParserError
|
55
|
+
if $mongrel_debug_client
|
56
|
+
STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
|
57
|
+
STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
|
58
|
+
end
|
59
|
+
close_connection
|
60
|
+
rescue Exception => e
|
61
|
+
close_connection
|
62
|
+
raise e
|
63
|
+
end
|
64
|
+
|
65
|
+
def write data
|
66
|
+
send_data data
|
67
|
+
end
|
68
|
+
|
69
|
+
def closed?
|
70
|
+
false
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
class HttpServer
|
76
|
+
def initialize(host, port, num_processors = 950, throttle = 0, timeout = 60)
|
77
|
+
@socket = nil
|
78
|
+
@classifier = Mongrel::URIClassifier.new
|
79
|
+
@host = host
|
80
|
+
@port = port
|
81
|
+
@workers = ThreadGroup.new
|
82
|
+
@timeout = timeout
|
83
|
+
@num_processors = num_processors
|
84
|
+
@death_time = 60
|
85
|
+
self.class.const_set(:Instance,self)
|
86
|
+
end
|
87
|
+
|
88
|
+
def run
|
89
|
+
trap('INT') { raise StopServer }
|
90
|
+
trap('TERM') { raise StopServer }
|
91
|
+
@acceptor = Thread.new do
|
92
|
+
EventMachine.run do
|
93
|
+
begin
|
94
|
+
run_evented
|
95
|
+
rescue StopServer
|
96
|
+
EventMachine.stop_event_loop
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def run_evented
|
103
|
+
EventMachine.start_server @host, @port, Mongrel::Protocol
|
104
|
+
end
|
105
|
+
|
106
|
+
def process_http_request(params,linebuffer,client)
|
107
|
+
if not params[Const::REQUEST_PATH]
|
108
|
+
uri = URI.parse(params[Const::REQUEST_URI])
|
109
|
+
params[Const::REQUEST_PATH] = uri.request_uri
|
110
|
+
end
|
111
|
+
|
112
|
+
raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
|
113
|
+
|
114
|
+
script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
|
115
|
+
|
116
|
+
if handlers
|
117
|
+
notifiers = handlers.select { |h| h.request_notify }
|
118
|
+
request = HttpRequest.new(params, linebuffer, notifiers)
|
119
|
+
|
120
|
+
# request is good so far, continue processing the response
|
121
|
+
response = HttpResponse.new(client)
|
122
|
+
|
123
|
+
# Process each handler in registered order until we run out or one finalizes the response.
|
124
|
+
dispatch_to_handlers(handlers,request,response)
|
125
|
+
|
126
|
+
# And finally, if nobody closed the response off, we finalize it.
|
127
|
+
unless response.done
|
128
|
+
response.finished unless response.pending
|
129
|
+
else
|
130
|
+
response.close_connection_after_writing
|
131
|
+
end
|
132
|
+
else
|
133
|
+
# Didn't find it, return a stock 404 response.
|
134
|
+
client.send_data(Const::ERROR_404_RESPONSE)
|
135
|
+
client.close_connection_after_writing
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def dispatch_to_handlers(handlers,request,response)
|
140
|
+
handlers.each do |handler|
|
141
|
+
handler.process(request, response)
|
142
|
+
break if response.done
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
class HttpRequest
|
148
|
+
def initialize(params, linebuffer, dispatchers)
|
149
|
+
@params = params
|
150
|
+
@dispatchers = dispatchers
|
151
|
+
@body = linebuffer
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class HttpResponse
|
156
|
+
# A flag to prevent response.finished from being called automatically
|
157
|
+
attr_accessor :pending
|
158
|
+
|
159
|
+
def send_file(path, small_file = false)
|
160
|
+
File.open(path, "rb") do |f|
|
161
|
+
while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
|
162
|
+
begin
|
163
|
+
write(chunk)
|
164
|
+
rescue Object => exc
|
165
|
+
break
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
@body_sent = true
|
170
|
+
end
|
171
|
+
|
172
|
+
def write(data)
|
173
|
+
@socket.send_data data
|
174
|
+
end
|
175
|
+
|
176
|
+
def close_connection_after_writing
|
177
|
+
@socket.close_connection_after_writing
|
178
|
+
end
|
179
|
+
|
180
|
+
def socket_error(details)
|
181
|
+
@socket.close_connection
|
182
|
+
done = true
|
183
|
+
raise details
|
184
|
+
end
|
185
|
+
|
186
|
+
def finished
|
187
|
+
send_status
|
188
|
+
send_header
|
189
|
+
send_body
|
190
|
+
@socket.close_connection_after_writing
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
class Configurator
|
195
|
+
# This version fixes a bug in the regular Mongrel version by adding
|
196
|
+
# initialization of groups.
|
197
|
+
def change_privilege(user, group)
|
198
|
+
if user and group
|
199
|
+
log "Initializing groups for {#user}:{#group}."
|
200
|
+
Process.initgroups(user,Etc.getgrnam(group).gid)
|
201
|
+
end
|
202
|
+
|
203
|
+
if group
|
204
|
+
log "Changing group to #{group}."
|
205
|
+
Process::GID.change_privilege(Etc.getgrnam(group).gid)
|
206
|
+
end
|
207
|
+
|
208
|
+
if user
|
209
|
+
log "Changing user to #{user}."
|
210
|
+
Process::UID.change_privilege(Etc.getpwnam(user).uid)
|
211
|
+
end
|
212
|
+
rescue Errno::EPERM
|
213
|
+
log "FAILED to change user:group #{user}:#{group}: #$!"
|
214
|
+
exit 1
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|