swiftiply 0.6.1.1 → 1.0.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.
- checksums.yaml +7 -0
- data/CONTRIBUTORS +2 -0
- data/README.md +62 -0
- data/bin/{mongrel_rails → evented_mongrel_rails} +6 -14
- data/bin/swiftiplied_mongrel_rails +246 -0
- data/bin/swiftiply +136 -116
- data/bin/swiftiply_mongrel_rails +2 -2
- data/bin/swiftiplyctl +283 -0
- data/cleanup.sh +5 -0
- data/ext/deque/extconf.rb +162 -0
- data/ext/deque/swiftcore/rubymain.cpp +435 -0
- data/ext/fastfilereader/extconf.rb +2 -2
- data/ext/fastfilereader/mapper.cpp +2 -0
- data/ext/map/extconf.rb +161 -0
- data/ext/map/rubymain.cpp +500 -0
- data/ext/splaytree/extconf.rb +161 -0
- data/ext/splaytree/swiftcore/rubymain.cpp +580 -0
- data/ext/splaytree/swiftcore/splay_map.h +635 -0
- data/ext/splaytree/swiftcore/splay_set.h +575 -0
- data/ext/splaytree/swiftcore/splay_tree.h +1127 -0
- data/external/httpclient.rb +231 -0
- data/external/package.rb +13 -13
- data/setup.rb +18 -2
- data/src/swiftcore/Swiftiply.rb +417 -773
- data/src/swiftcore/Swiftiply/backend_protocol.rb +213 -0
- data/src/swiftcore/Swiftiply/cache_base.rb +49 -0
- data/src/swiftcore/Swiftiply/cache_base_mixin.rb +52 -0
- data/src/swiftcore/Swiftiply/cluster_managers/rest_based_cluster_manager.rb +9 -0
- data/src/swiftcore/Swiftiply/cluster_protocol.rb +70 -0
- data/src/swiftcore/Swiftiply/config.rb +370 -0
- data/src/swiftcore/Swiftiply/config/rest_updater.rb +26 -0
- data/src/swiftcore/Swiftiply/constants.rb +101 -0
- data/src/swiftcore/Swiftiply/content_cache_entry.rb +44 -0
- data/src/swiftcore/Swiftiply/content_response.rb +45 -0
- data/src/swiftcore/Swiftiply/control_protocol.rb +49 -0
- data/src/swiftcore/Swiftiply/dynamic_request_cache.rb +41 -0
- data/src/swiftcore/Swiftiply/etag_cache.rb +64 -0
- data/src/swiftcore/Swiftiply/file_cache.rb +46 -0
- data/src/swiftcore/Swiftiply/hash_cache_base.rb +22 -0
- data/src/swiftcore/Swiftiply/http_recognizer.rb +267 -0
- data/src/swiftcore/Swiftiply/loggers/Analogger.rb +21 -0
- data/src/swiftcore/Swiftiply/loggers/stderror.rb +13 -0
- data/src/swiftcore/Swiftiply/mocklog.rb +10 -0
- data/src/swiftcore/Swiftiply/proxy.rb +15 -0
- data/src/swiftcore/Swiftiply/proxy_backends/keepalive.rb +286 -0
- data/src/swiftcore/Swiftiply/proxy_backends/traditional.rb +286 -0
- data/src/swiftcore/Swiftiply/proxy_backends/traditional/redis_directory.rb +87 -0
- data/src/swiftcore/Swiftiply/proxy_backends/traditional/static_directory.rb +69 -0
- data/src/swiftcore/Swiftiply/proxy_bag.rb +716 -0
- data/src/swiftcore/Swiftiply/rest_based_cluster_manager.rb +15 -0
- data/src/swiftcore/Swiftiply/splay_cache_base.rb +21 -0
- data/src/swiftcore/Swiftiply/support_pagecache.rb +6 -3
- data/src/swiftcore/Swiftiply/swiftiply_2_http_proxy.rb +7 -0
- data/src/swiftcore/Swiftiply/swiftiply_client.rb +20 -5
- data/src/swiftcore/Swiftiply/version.rb +5 -0
- data/src/swiftcore/evented_mongrel.rb +26 -8
- data/src/swiftcore/hash.rb +43 -0
- data/src/swiftcore/method_builder.rb +28 -0
- data/src/swiftcore/streamer.rb +46 -0
- data/src/swiftcore/swiftiplied_mongrel.rb +91 -23
- data/src/swiftcore/types.rb +20 -3
- data/swiftiply.gemspec +14 -8
- data/test/TC_Deque.rb +152 -0
- data/test/TC_ProxyBag.rb +147 -166
- data/test/TC_Swiftiply.rb +576 -169
- data/test/TC_Swiftiply/mongrel/evented_hello.rb +1 -1
- data/test/TC_Swiftiply/mongrel/swiftiplied_hello.rb +1 -1
- data/test/TC_Swiftiply/test_serve_static_file_xsendfile/sendfile_client.rb +27 -0
- data/test/TC_Swiftiply/test_ssl/bin/validate_ssl_capability.rb +21 -0
- data/test/TC_Swiftiply/test_ssl/test.cert +16 -0
- data/test/TC_Swiftiply/test_ssl/test.key +15 -0
- data/{bin → test/bin}/echo_client +0 -0
- metadata +136 -94
- data/README +0 -126
- data/ext/swiftiply_parse/parse.rl +0 -90
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'em-http'
|
2
|
+
module Swiftcore
|
3
|
+
module Swiftiply
|
4
|
+
module ManagerProtocols
|
5
|
+
class RestBasedClusterManager
|
6
|
+
def self.call(callsite, params)
|
7
|
+
EventMachine::HttpRequest.new(callsite).get
|
8
|
+
true
|
9
|
+
rescue Exception
|
10
|
+
false
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'swiftcore/Swiftiply/cache_base_mixin'
|
2
|
+
|
3
|
+
module Swiftcore
|
4
|
+
# Use Array instead of Deque, if Deque wasn't available.
|
5
|
+
Deque = Array unless HasDeque or const_defined?(:Deque)
|
6
|
+
|
7
|
+
module Swiftiply
|
8
|
+
class CacheBase < Swiftcore::SplayTreeMap
|
9
|
+
include CacheBaseMixin
|
10
|
+
def initialize(vw = 900, time_limit = 0.05, maxsize = 100)
|
11
|
+
@vw = vw
|
12
|
+
@tl = time_limit
|
13
|
+
@vwtl = vw * time_limit
|
14
|
+
@old_vql = 0
|
15
|
+
@vq = Deque.new
|
16
|
+
super()
|
17
|
+
self.max_size = maxsize
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -33,16 +33,19 @@ module Swiftcore
|
|
33
33
|
|
34
34
|
def find_static_file(dr,path_info,client_name)
|
35
35
|
path = File.join(dr,path_info)
|
36
|
-
|
36
|
+
puts path
|
37
|
+
if FileTest.exist?(path) and FileTest.file?(path) and File.expand_path(path).index(dr) == 0 and !(x = static_mask(client_name) and path =~ x)
|
37
38
|
path
|
38
39
|
elsif @suffix_list.has_key?(client_name)
|
39
40
|
path = File.join(dr,@cache_dir[client_name],path_info)
|
40
|
-
if FileTest.exist?(path) and FileTest.file?(path)
|
41
|
+
if FileTest.exist?(path) and FileTest.file?(path) and File.expand_path(path).index(dr) == 0 and !(x = static_mask(client_name) and path =~ x)
|
41
42
|
path
|
42
43
|
else
|
43
44
|
for suffix in @suffix_list[client_name] do
|
44
45
|
p = "#{path}.#{suffix}"
|
45
|
-
|
46
|
+
if FileTest.exist?(p) and FileTest.file?(p) and File.expand_path(p).index(dr) == 0 and !(x = static_mask(client_name) and p =~ x)
|
47
|
+
return p
|
48
|
+
end
|
46
49
|
end
|
47
50
|
nil
|
48
51
|
end
|
@@ -1,6 +1,20 @@
|
|
1
|
-
#
|
2
|
-
|
3
|
-
|
1
|
+
# Encoding:ascii-8bit
|
2
|
+
|
3
|
+
begin
|
4
|
+
load_attempted ||= false
|
5
|
+
require 'eventmachine'
|
6
|
+
require 'socket'
|
7
|
+
rescue LoadError => e
|
8
|
+
unless load_attempted
|
9
|
+
load_attempted = true
|
10
|
+
# Ugh. Everything gets slower once rubygems are used. So, for the
|
11
|
+
# best speed possible, don't install EventMachine or Swiftiply via
|
12
|
+
# gems.
|
13
|
+
require 'rubygems'
|
14
|
+
retry
|
15
|
+
end
|
16
|
+
raise e
|
17
|
+
end
|
4
18
|
|
5
19
|
# This is a basic Swiftiply client implementation.
|
6
20
|
|
@@ -22,7 +36,8 @@ class SwiftiplyClientProtocol < EventMachine::Connection
|
|
22
36
|
conn.port = port
|
23
37
|
conn.key = key
|
24
38
|
ip = conn.ip = conn.__get_ip(hostname)
|
25
|
-
conn.id = 'swiftclient' << ip.collect {|x| sprintf('%02x',x.to_i)}.join << sprintf('%04x',port.to_i)<< sprintf('%02x',key.length) << key
|
39
|
+
#conn.id = 'swiftclient' << ip.collect {|x| sprintf('%02x',x.to_i)}.join << sprintf('%04x',port.to_i)<< sprintf('%02x',key.length) << key
|
40
|
+
conn.id = 'swiftclient' << ip.collect {|x| sprintf('%02x',x.to_i)}.join << sprintf('%04x',$$)<< sprintf('%02x',key.length) << key
|
26
41
|
conn.set_comm_inactivity_timeout inactivity_timeout
|
27
42
|
end
|
28
43
|
end
|
@@ -54,4 +69,4 @@ class SwiftiplyClientProtocol < EventMachine::Connection
|
|
54
69
|
def __get_ip hostname
|
55
70
|
Socket.gethostbyname(hostname)[3].unpack(CCCCC) rescue ip = C0s
|
56
71
|
end
|
57
|
-
end
|
72
|
+
end
|
@@ -19,6 +19,10 @@ require 'mongrel'
|
|
19
19
|
|
20
20
|
module Mongrel
|
21
21
|
class MongrelProtocol < EventMachine::Connection
|
22
|
+
|
23
|
+
Cblank = ''.freeze
|
24
|
+
C400Header = "HTTP/1.0 400 Bad Request\r\nContent-Type: text/plain\r\nServer: Swiftiplied Mongrel 0.6.5\r\nConnection: close\r\n\r\n"
|
25
|
+
|
22
26
|
def post_init
|
23
27
|
@parser = HttpParser.new
|
24
28
|
@params = HttpParams.new
|
@@ -34,11 +38,16 @@ module Mongrel
|
|
34
38
|
if @parser.finished?
|
35
39
|
if @request_len.nil?
|
36
40
|
@request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
|
37
|
-
script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
|
41
|
+
script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH] || Cblank)
|
38
42
|
if handlers
|
39
43
|
@params[::Mongrel::Const::PATH_INFO] = path_info
|
40
44
|
@params[::Mongrel::Const::SCRIPT_NAME] = script_name
|
41
|
-
|
45
|
+
# The previous behavior of this line set REMOTE_ADDR equal to HTTP_X_FORWARDED_FOR
|
46
|
+
# if it was defined. This behavior seems inconsistent with the intention of
|
47
|
+
# http://www.ietf.org/rfc/rfc3875 so it has been changed. REMOTE_ADDR now always
|
48
|
+
# contains the address of the immediate source of the connection. Check
|
49
|
+
# @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] if you need that information.
|
50
|
+
@params[::Mongrel::Const::REMOTE_ADDR] = ::Socket.unpack_sockaddr_in(get_peername)[1] rescue nil
|
42
51
|
@notifiers = handlers.select { |h| h.request_notify }
|
43
52
|
end
|
44
53
|
if @request_len > ::Mongrel::Const::MAX_BODY
|
@@ -65,10 +74,12 @@ module Mongrel
|
|
65
74
|
STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
|
66
75
|
STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
|
67
76
|
end
|
68
|
-
|
77
|
+
send_data C400Header
|
78
|
+
close_connection_after_writing
|
69
79
|
rescue Exception => e
|
70
|
-
close_connection
|
71
80
|
raise e
|
81
|
+
send_data C400Header
|
82
|
+
close_connection_after_writing
|
72
83
|
end
|
73
84
|
|
74
85
|
def write data
|
@@ -102,23 +113,28 @@ module Mongrel
|
|
102
113
|
def run
|
103
114
|
trap('INT') { raise StopServer }
|
104
115
|
trap('TERM') { raise StopServer }
|
105
|
-
|
116
|
+
#@acceptor = Thread.new do
|
117
|
+
@acceptor = Thread.current
|
118
|
+
# SHOULD NOT DO THIS AUTOMATICALLY.
|
119
|
+
# There either needs to be a way to configure this, or to detect
|
120
|
+
# when it is safe or when kqueue needs to run.
|
106
121
|
EventMachine.epoll
|
107
122
|
EventMachine.set_descriptor_table_size(4096)
|
108
123
|
EventMachine.run do
|
124
|
+
EM.set_timer_quantum(5)
|
109
125
|
begin
|
110
|
-
EventMachine.start_server(@host,@port,MongrelProtocol)
|
126
|
+
EventMachine.start_server(@host,@port.to_i,MongrelProtocol)
|
111
127
|
rescue StopServer
|
112
128
|
EventMachine.stop_event_loop
|
113
129
|
end
|
114
130
|
end
|
115
|
-
end
|
131
|
+
#end
|
116
132
|
end
|
117
133
|
|
118
134
|
def process_http_request(params,linebuffer,client)
|
119
135
|
if not params[Const::REQUEST_PATH]
|
120
136
|
uri = URI.parse(params[Const::REQUEST_URI])
|
121
|
-
params[Const::REQUEST_PATH] = uri.
|
137
|
+
params[Const::REQUEST_PATH] = uri.path
|
122
138
|
end
|
123
139
|
|
124
140
|
raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
|
@@ -129,6 +145,8 @@ module Mongrel
|
|
129
145
|
notifiers = handlers.select { |h| h.request_notify }
|
130
146
|
request = HttpRequest.new(params, linebuffer, notifiers)
|
131
147
|
|
148
|
+
# TODO: Add Keep-Alive support
|
149
|
+
|
132
150
|
# request is good so far, continue processing the response
|
133
151
|
response = HttpResponse.new(client)
|
134
152
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# Hash#merge doesn't work right if any of the values in the hash are themselves
|
4
|
+
# hashes. This creates frowns on my normally smiling face. This method
|
5
|
+
# will walk a hash, merging it, while preserving default_proc's that may
|
6
|
+
# exist on the hashes involved. The code was adopted from IOWA. It's also
|
7
|
+
# quite likely that it sucks. I wrote it years ago, and have left it alone
|
8
|
+
# since then. If you need similar functionality in your code somewhere,
|
9
|
+
# though, feel free to use this. If you make it better, I'd appreciate it
|
10
|
+
# if you would send me your patches, though.
|
11
|
+
|
12
|
+
def rmerge!(h)
|
13
|
+
h.each do |k,v|
|
14
|
+
if v.kind_of?(::Hash)
|
15
|
+
if self[k].kind_of?(::Hash)
|
16
|
+
unless self[k].respond_to?(:rmerge!)
|
17
|
+
if dp = self[k].default_proc
|
18
|
+
self[k] = Hash.new {|h,k| dp.call(h,k)}.rmerge!(self[k])
|
19
|
+
else
|
20
|
+
osk = self[k]
|
21
|
+
self[k] = Hash.new
|
22
|
+
self[k].rmerge!(osk)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
self[k].rmerge!(v)
|
26
|
+
else
|
27
|
+
if self.default_proc
|
28
|
+
self.delete k
|
29
|
+
self[k]
|
30
|
+
end
|
31
|
+
unless self[k].kind_of?(::Hash)
|
32
|
+
self.delete k
|
33
|
+
self[k] = Hash.new
|
34
|
+
end
|
35
|
+
self[k].rmerge!(v)
|
36
|
+
end
|
37
|
+
else
|
38
|
+
self[k] = v
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Mixin to allow dynamic construction of instance methods.
|
2
|
+
|
3
|
+
module MethodBuilder
|
4
|
+
def method_builder(method_name, method_args, chunks, callback_args = [])
|
5
|
+
if Array === method_args
|
6
|
+
pma = [] method_args.each do |ma|
|
7
|
+
if Array === ma
|
8
|
+
pma << "#{ma[0]}=#{ma[1]}"
|
9
|
+
else
|
10
|
+
pma << ma.to_s
|
11
|
+
end
|
12
|
+
end
|
13
|
+
parsed_method_args = pma.join(',')
|
14
|
+
else
|
15
|
+
parsed_method_args = method_args.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
m = "def #{method_name}(#{parsed_method_args})\n" chunks.each do |chunk|
|
19
|
+
if chunk.respond_to?(:call)
|
20
|
+
m << chunk.call(*args)
|
21
|
+
else
|
22
|
+
m << chunk.to_s
|
23
|
+
end
|
24
|
+
end m << "\nend"
|
25
|
+
|
26
|
+
class_eval m
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
begin
|
2
|
+
load_attempted ||= false
|
3
|
+
require 'em/streamer'
|
4
|
+
rescue LoadError
|
5
|
+
unless load_attempted
|
6
|
+
load_attempted = true
|
7
|
+
require 'rubygems'
|
8
|
+
retry
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module EventMachine
|
13
|
+
class FileStreamer
|
14
|
+
|
15
|
+
def stream_one_chunk
|
16
|
+
loop {
|
17
|
+
if @position < @size
|
18
|
+
if @connection.get_outbound_data_size > BackpressureLevel
|
19
|
+
EventMachine::next_tick {stream_one_chunk}
|
20
|
+
break
|
21
|
+
else
|
22
|
+
len = @size - @position
|
23
|
+
len = ChunkSize if (len > ChunkSize)
|
24
|
+
|
25
|
+
#@connection.send_data( "#{format("%x",len)}\r\n" ) if @http_chunks
|
26
|
+
if @http_chunks
|
27
|
+
@connection.send_data( "#{len.to_s(16)}\r\n" ) if @http_chunks
|
28
|
+
@connection.send_data( @mapping.get_chunk( @position, len ))
|
29
|
+
@connection.send_data("\r\n") if @http_chunks
|
30
|
+
else
|
31
|
+
@connection.send_data( @mapping.get_chunk( @position, len ))
|
32
|
+
end
|
33
|
+
|
34
|
+
@position += len
|
35
|
+
end
|
36
|
+
else
|
37
|
+
@connection.send_data "0\r\n\r\n" if @http_chunks
|
38
|
+
@mapping.close
|
39
|
+
succeed
|
40
|
+
break
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
# application running inside an EventMachine event loop. It should
|
4
4
|
# be compatible with the existing Mongrel handlers for Rails,
|
5
5
|
# Camping, Nitro, etc....
|
6
|
-
|
6
|
+
|
7
7
|
begin
|
8
8
|
load_attempted ||= false
|
9
9
|
require 'eventmachine'
|
@@ -20,6 +20,9 @@ end
|
|
20
20
|
module Mongrel
|
21
21
|
C0s = [0,0,0,0].freeze unless const_defined?(:C0s)
|
22
22
|
CCCCC = 'CCCC'.freeze unless const_defined?(:CCCCC)
|
23
|
+
Cblank = ''.freeze
|
24
|
+
|
25
|
+
C400Header = "HTTP/1.0 400 Bad Request\r\nContent-Type: text/plain\r\nServer: Swiftiplied Mongrel 0.6.5\r\nConnection: close\r\n\r\n"
|
23
26
|
|
24
27
|
class MongrelProtocol < SwiftiplyClientProtocol
|
25
28
|
|
@@ -34,15 +37,26 @@ module Mongrel
|
|
34
37
|
|
35
38
|
def receive_data data
|
36
39
|
@linebuffer << data
|
40
|
+
|
37
41
|
@nparsed = @parser.execute(@params, @linebuffer, @nparsed) unless @parser.finished?
|
38
42
|
if @parser.finished?
|
39
|
-
|
43
|
+
|
44
|
+
unless @params[::Mongrel::Const::REQUEST_PATH]
|
45
|
+
params[::Mongrel::Const::REQUEST_PATH] = URI.parse(params[::Mongrel::Const::REQUEST_URI]).path
|
46
|
+
end
|
47
|
+
|
48
|
+
unless @request_len
|
40
49
|
@request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
|
41
50
|
script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
|
42
51
|
if handlers
|
43
|
-
@params[::Mongrel::Const::PATH_INFO] = path_info
|
52
|
+
@params[::Mongrel::Const::PATH_INFO] = path_info || Cblank # path_info shouldn't be nil, but just in case it somehow is, let's make sure we don't crash later because of it.
|
44
53
|
@params[::Mongrel::Const::SCRIPT_NAME] = script_name
|
45
|
-
|
54
|
+
# The previous behavior of this line set REMOTE_ADDR equal to HTTP_X_FORWARDED_FOR
|
55
|
+
# if it was defined. This behavior seems inconsistent with the intention of
|
56
|
+
# http://www.ietf.org/rfc/rfc3875 so it has been changed. REMOTE_ADDR now always
|
57
|
+
# contains the address of the immediate source of the connection. Check
|
58
|
+
# @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] if you need that information.
|
59
|
+
@params[::Mongrel::Const::REMOTE_ADDR] = ::Socket.unpack_sockaddr_in(get_peername)[1] rescue nil
|
46
60
|
@notifiers = handlers.select { |h| h.request_notify }
|
47
61
|
end
|
48
62
|
if @request_len > ::Mongrel::Const::MAX_BODY
|
@@ -58,7 +72,7 @@ module Mongrel
|
|
58
72
|
if @linebuffer.length >= @request_len
|
59
73
|
@linebuffer.rewind
|
60
74
|
::Mongrel::HttpServer::Instance.process_http_request(@params,@linebuffer,self)
|
61
|
-
@linebuffer.delete if Tempfile === @linebuffer
|
75
|
+
@linebuffer.delete if Tempfile === @linebuffer && FileTest.exist?(@linebuffer.path.to_s)
|
62
76
|
post_init
|
63
77
|
end
|
64
78
|
elsif @linebuffer.length > ::Mongrel::Const::MAX_HEADER
|
@@ -70,10 +84,17 @@ module Mongrel
|
|
70
84
|
STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
|
71
85
|
STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
|
72
86
|
end
|
73
|
-
|
87
|
+
send_data C400Header
|
88
|
+
close_connection_after_writing
|
74
89
|
rescue Exception => e
|
75
|
-
|
90
|
+
# This isn't a parse error; if this rescue caught an exception, then something
|
91
|
+
# significant happened.
|
76
92
|
raise e
|
93
|
+
send_data C400Header
|
94
|
+
close_connection_after_writing
|
95
|
+
ensure
|
96
|
+
# Make sure to cleanup the Tempfile.
|
97
|
+
@linebuffer.delete if Tempfile === @linebuffer && FileTest.exist?(@linebuffer.path.to_s)
|
77
98
|
end
|
78
99
|
|
79
100
|
def write data
|
@@ -84,6 +105,7 @@ module Mongrel
|
|
84
105
|
false
|
85
106
|
end
|
86
107
|
|
108
|
+
def flush; end # EM handles flushing, not us, so this is just here for compatibility.
|
87
109
|
end
|
88
110
|
|
89
111
|
class HttpServer
|
@@ -93,7 +115,12 @@ module Mongrel
|
|
93
115
|
# The support is here, for swiftiplied_mongrels which are secured by
|
94
116
|
# a key. If someone want to donate any patches. Otherwise, this won't
|
95
117
|
# really be useful to most people until 0.7.0.
|
96
|
-
|
118
|
+
|
119
|
+
CHTTP_CONNECTION = 'HTTP_CONNECTION'.freeze
|
120
|
+
CHTTP_VERSION = 'HTTP_VERSION'.freeze
|
121
|
+
CKEEP_ALIVE = 'KEEP_ALIVE'.freeze
|
122
|
+
C1_1 = '1.1'.freeze
|
123
|
+
|
97
124
|
def initialize(host, port, num_processors=950, x=0, y=nil,key='') # Deal with Mongrel 1.0.1 or earlier, as well as later.
|
98
125
|
@socket = nil
|
99
126
|
@classifier = URIClassifier.new
|
@@ -115,24 +142,26 @@ module Mongrel
|
|
115
142
|
def run
|
116
143
|
trap('INT') { EventMachine.stop_event_loop }
|
117
144
|
trap('TERM') { EventMachine.stop_event_loop }
|
118
|
-
|
145
|
+
#@acceptor = Thread.new do
|
146
|
+
@acceptor = Thread.current
|
119
147
|
EventMachine.run do
|
148
|
+
EM.set_timer_quantum(5)
|
120
149
|
begin
|
121
|
-
MongrelProtocol.connect(@host,@port,@key)
|
150
|
+
MongrelProtocol.connect(@host,@port.to_i,@key)
|
122
151
|
rescue StopServer
|
123
152
|
EventMachine.stop_event_loop
|
124
153
|
end
|
125
154
|
end
|
126
|
-
end
|
155
|
+
#end
|
127
156
|
end
|
128
157
|
|
129
158
|
def process_http_request(params,linebuffer,client)
|
130
159
|
if not params[Const::REQUEST_PATH]
|
131
160
|
uri = URI.parse(params[Const::REQUEST_URI])
|
132
|
-
params[Const::REQUEST_PATH] = uri.
|
161
|
+
params[Const::REQUEST_PATH] = uri.path
|
133
162
|
end
|
134
163
|
|
135
|
-
|
164
|
+
raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
|
136
165
|
|
137
166
|
script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
|
138
167
|
|
@@ -140,8 +169,15 @@ module Mongrel
|
|
140
169
|
notifiers = handlers.select { |h| h.request_notify }
|
141
170
|
request = HttpRequest.new(params, linebuffer, notifiers)
|
142
171
|
|
172
|
+
http_version = params[CHTTP_VERSION]
|
173
|
+
if http_version == C1_1
|
174
|
+
keep_alive = params[CHTTP_CONNECTION] =~ /close/i ? false : true
|
175
|
+
else
|
176
|
+
keep_alive = params[CHTTP_CONNECTION] =~ /alive/i ? true : false
|
177
|
+
end
|
178
|
+
|
143
179
|
# request is good so far, continue processing the response
|
144
|
-
response = HttpResponse.new(client)
|
180
|
+
response = HttpResponse.new(client,http_version,keep_alive)
|
145
181
|
|
146
182
|
# Process each handler in registered order until we run out or one finalizes the response.
|
147
183
|
dispatch_to_handlers(handlers,request,response)
|
@@ -156,19 +192,20 @@ module Mongrel
|
|
156
192
|
# header MUST accompany all HTTP responses that go into a swiftiply
|
157
193
|
# keepalive connection, so just use the Response object to construct the
|
158
194
|
# 404 response.
|
195
|
+
response = HttpResponse.new(client)
|
159
196
|
response.status = 404
|
160
|
-
response.body
|
197
|
+
response.body << "#{params[Const::REQUEST_PATH]} not found"
|
161
198
|
response.finished
|
162
199
|
end
|
163
200
|
end
|
164
|
-
|
201
|
+
|
165
202
|
def dispatch_to_handlers(handlers,request,response)
|
166
203
|
handlers.each do |handler|
|
167
204
|
handler.process(request, response)
|
168
205
|
break if response.done
|
169
206
|
end
|
170
207
|
end
|
171
|
-
|
208
|
+
|
172
209
|
end
|
173
210
|
|
174
211
|
class HttpRequest
|
@@ -180,6 +217,25 @@ module Mongrel
|
|
180
217
|
end
|
181
218
|
|
182
219
|
class HttpResponse
|
220
|
+
|
221
|
+
CContentLength = 'Content-Length'.freeze
|
222
|
+
C1_1 = '1.1'.freeze
|
223
|
+
|
224
|
+
def initialize(socket,http_version = C1_1, keepalive = false)
|
225
|
+
@socket = socket
|
226
|
+
@body = StringIO.new
|
227
|
+
@status = 404
|
228
|
+
@reason = nil
|
229
|
+
@header = HeaderOut.new(StringIO.new)
|
230
|
+
@header[Const::DATE] = Time.now.httpdate
|
231
|
+
@body_sent = false
|
232
|
+
@header_sent = false
|
233
|
+
@status_sent = false
|
234
|
+
@http_version = http_version
|
235
|
+
#@keepalive = keepalive
|
236
|
+
@keepalive = false ## DISABLE ALL KEEPALIVE
|
237
|
+
end
|
238
|
+
|
183
239
|
def send_file(path, small_file = false)
|
184
240
|
File.open(path, "rb") do |f|
|
185
241
|
while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
|
@@ -193,6 +249,18 @@ module Mongrel
|
|
193
249
|
@body_sent = true
|
194
250
|
end
|
195
251
|
|
252
|
+
def send_status(content_length=@body.length)
|
253
|
+
unless @status_sent
|
254
|
+
@header[CContentLength] = content_length if content_length && @status != 304
|
255
|
+
if @keepalive
|
256
|
+
write("HTTP/1.1 #{@status} #{@reason || HTTP_STATUS_CODES[@status]}\r\nConnection: Keep-Alive\r\n")
|
257
|
+
else
|
258
|
+
write("HTTP/1.1 #{@status} #{@reason || HTTP_STATUS_CODES[@status]}\r\nConnection: Close\r\n")
|
259
|
+
end
|
260
|
+
@status_sent = true
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
196
264
|
def write(data)
|
197
265
|
@socket.send_data data
|
198
266
|
end
|
@@ -204,12 +272,12 @@ module Mongrel
|
|
204
272
|
end
|
205
273
|
|
206
274
|
def finished
|
207
|
-
|
208
|
-
|
209
|
-
|
275
|
+
send_status
|
276
|
+
send_header
|
277
|
+
send_body
|
210
278
|
end
|
211
279
|
end
|
212
|
-
|
280
|
+
|
213
281
|
class Configurator
|
214
282
|
# This version fixes a bug in the regular Mongrel version by adding
|
215
283
|
# initialization of groups.
|
@@ -218,12 +286,12 @@ module Mongrel
|
|
218
286
|
log "Initializing groups for {#user}:{#group}."
|
219
287
|
Process.initgroups(user,Etc.getgrnam(group).gid)
|
220
288
|
end
|
221
|
-
|
289
|
+
|
222
290
|
if group
|
223
291
|
log "Changing group to #{group}."
|
224
292
|
Process::GID.change_privilege(Etc.getgrnam(group).gid)
|
225
293
|
end
|
226
|
-
|
294
|
+
|
227
295
|
if user
|
228
296
|
log "Changing user to #{user}."
|
229
297
|
Process::UID.change_privilege(Etc.getpwnam(user).uid)
|