swiftiply 0.6.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTORS +2 -0
  3. data/README.md +62 -0
  4. data/bin/{mongrel_rails → evented_mongrel_rails} +6 -14
  5. data/bin/swiftiplied_mongrel_rails +246 -0
  6. data/bin/swiftiply +136 -116
  7. data/bin/swiftiply_mongrel_rails +2 -2
  8. data/bin/swiftiplyctl +283 -0
  9. data/cleanup.sh +5 -0
  10. data/ext/deque/extconf.rb +162 -0
  11. data/ext/deque/swiftcore/rubymain.cpp +435 -0
  12. data/ext/fastfilereader/extconf.rb +2 -2
  13. data/ext/fastfilereader/mapper.cpp +2 -0
  14. data/ext/map/extconf.rb +161 -0
  15. data/ext/map/rubymain.cpp +500 -0
  16. data/ext/splaytree/extconf.rb +161 -0
  17. data/ext/splaytree/swiftcore/rubymain.cpp +580 -0
  18. data/ext/splaytree/swiftcore/splay_map.h +635 -0
  19. data/ext/splaytree/swiftcore/splay_set.h +575 -0
  20. data/ext/splaytree/swiftcore/splay_tree.h +1127 -0
  21. data/external/httpclient.rb +231 -0
  22. data/external/package.rb +13 -13
  23. data/setup.rb +18 -2
  24. data/src/swiftcore/Swiftiply.rb +417 -773
  25. data/src/swiftcore/Swiftiply/backend_protocol.rb +213 -0
  26. data/src/swiftcore/Swiftiply/cache_base.rb +49 -0
  27. data/src/swiftcore/Swiftiply/cache_base_mixin.rb +52 -0
  28. data/src/swiftcore/Swiftiply/cluster_managers/rest_based_cluster_manager.rb +9 -0
  29. data/src/swiftcore/Swiftiply/cluster_protocol.rb +70 -0
  30. data/src/swiftcore/Swiftiply/config.rb +370 -0
  31. data/src/swiftcore/Swiftiply/config/rest_updater.rb +26 -0
  32. data/src/swiftcore/Swiftiply/constants.rb +101 -0
  33. data/src/swiftcore/Swiftiply/content_cache_entry.rb +44 -0
  34. data/src/swiftcore/Swiftiply/content_response.rb +45 -0
  35. data/src/swiftcore/Swiftiply/control_protocol.rb +49 -0
  36. data/src/swiftcore/Swiftiply/dynamic_request_cache.rb +41 -0
  37. data/src/swiftcore/Swiftiply/etag_cache.rb +64 -0
  38. data/src/swiftcore/Swiftiply/file_cache.rb +46 -0
  39. data/src/swiftcore/Swiftiply/hash_cache_base.rb +22 -0
  40. data/src/swiftcore/Swiftiply/http_recognizer.rb +267 -0
  41. data/src/swiftcore/Swiftiply/loggers/Analogger.rb +21 -0
  42. data/src/swiftcore/Swiftiply/loggers/stderror.rb +13 -0
  43. data/src/swiftcore/Swiftiply/mocklog.rb +10 -0
  44. data/src/swiftcore/Swiftiply/proxy.rb +15 -0
  45. data/src/swiftcore/Swiftiply/proxy_backends/keepalive.rb +286 -0
  46. data/src/swiftcore/Swiftiply/proxy_backends/traditional.rb +286 -0
  47. data/src/swiftcore/Swiftiply/proxy_backends/traditional/redis_directory.rb +87 -0
  48. data/src/swiftcore/Swiftiply/proxy_backends/traditional/static_directory.rb +69 -0
  49. data/src/swiftcore/Swiftiply/proxy_bag.rb +716 -0
  50. data/src/swiftcore/Swiftiply/rest_based_cluster_manager.rb +15 -0
  51. data/src/swiftcore/Swiftiply/splay_cache_base.rb +21 -0
  52. data/src/swiftcore/Swiftiply/support_pagecache.rb +6 -3
  53. data/src/swiftcore/Swiftiply/swiftiply_2_http_proxy.rb +7 -0
  54. data/src/swiftcore/Swiftiply/swiftiply_client.rb +20 -5
  55. data/src/swiftcore/Swiftiply/version.rb +5 -0
  56. data/src/swiftcore/evented_mongrel.rb +26 -8
  57. data/src/swiftcore/hash.rb +43 -0
  58. data/src/swiftcore/method_builder.rb +28 -0
  59. data/src/swiftcore/streamer.rb +46 -0
  60. data/src/swiftcore/swiftiplied_mongrel.rb +91 -23
  61. data/src/swiftcore/types.rb +20 -3
  62. data/swiftiply.gemspec +14 -8
  63. data/test/TC_Deque.rb +152 -0
  64. data/test/TC_ProxyBag.rb +147 -166
  65. data/test/TC_Swiftiply.rb +576 -169
  66. data/test/TC_Swiftiply/mongrel/evented_hello.rb +1 -1
  67. data/test/TC_Swiftiply/mongrel/swiftiplied_hello.rb +1 -1
  68. data/test/TC_Swiftiply/test_serve_static_file_xsendfile/sendfile_client.rb +27 -0
  69. data/test/TC_Swiftiply/test_ssl/bin/validate_ssl_capability.rb +21 -0
  70. data/test/TC_Swiftiply/test_ssl/test.cert +16 -0
  71. data/test/TC_Swiftiply/test_ssl/test.key +15 -0
  72. data/{bin → test/bin}/echo_client +0 -0
  73. metadata +136 -94
  74. data/README +0 -126
  75. 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
- if FileTest.exist?(path) and FileTest.file?(path)
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
- return p if FileTest.exist?(p) and FileTest.file?(p)
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
@@ -0,0 +1,7 @@
1
+ module Swiftiply
2
+ class Swiftiply2HttpProxy
3
+ def self.run(config)
4
+ end
5
+
6
+ end
7
+ end
@@ -1,6 +1,20 @@
1
- # -*- coding: ISO-8859-1 -*-
2
- require 'eventmachine'
3
- require 'socket'
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
@@ -0,0 +1,5 @@
1
+ module Swiftcore
2
+ module Swiftiply
3
+ VERSION = '1.0.0'
4
+ end
5
+ 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
- @params[::Mongrel::Const::REMOTE_ADDR] = @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] || ::Socket.unpack_sockaddr_in(get_peername)[1]
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
- close_connection
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
- @acceptor = Thread.new do
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.request_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
- if @request_len.nil?
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
- @params[::Mongrel::Const::REMOTE_ADDR] = @params[::Mongrel::Const::HTTP_X_FORWARDED_FOR] || ::Socket.unpack_sockaddr_in(get_peername)[1]
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
- close_connection
87
+ send_data C400Header
88
+ close_connection_after_writing
74
89
  rescue Exception => e
75
- close_connection
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
- @acceptor = Thread.new do
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.request_uri
161
+ params[Const::REQUEST_PATH] = uri.path
133
162
  end
134
163
 
135
- raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
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 = "#{params[Const::REQUEST_PATH]} not found"
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
- send_status
208
- send_header
209
- send_body
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)