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