oldmoe-neverblock 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -14,29 +14,28 @@ module NeverBlock
14
14
  end
15
15
  end
16
16
 
17
- # A proxy for the connection's exec method
17
+ # A proxy for the connection's query method
18
18
  # quries the pool to get a connection first
19
- def exec(query)
19
+ def query(query)
20
20
  @pool.hold do |conn|
21
21
  conn.query(query)
22
22
  end
23
23
  end
24
24
 
25
- alias :query :exec
26
25
  # This method must be called for transactions to work correctly.
27
26
  # One cannot just send "begin" as you never know which connection
28
27
  # will be available next. This method ensures you get the same connection
29
28
  # while in a transaction.
30
29
  def begin_db_transaction
31
30
  @pool.hold(true) do |conn|
32
- conn.exec("begin")
31
+ conn.query("begin")
33
32
  end
34
33
  end
35
34
 
36
35
  # see =begin_db_transaction
37
36
  def rollback_db_transaction
38
37
  @pool.hold do |conn|
39
- conn.exec("rollback")
38
+ conn.query("rollback")
40
39
  @pool.release(Fiber.current,conn)
41
40
  end
42
41
  end
@@ -44,7 +43,7 @@ module NeverBlock
44
43
  # see =begin_db_transaction
45
44
  def commit_db_transaction
46
45
  @pool.hold do |conn|
47
- conn.exec("commit")
46
+ conn.query("commit")
48
47
  @pool.release(Fiber.current,conn)
49
48
  end
50
49
  end
@@ -0,0 +1,236 @@
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
+ # NeverBlock support added
8
+
9
+ begin
10
+ load_attempted ||= false
11
+ require 'eventmachine'
12
+ rescue LoadError
13
+ unless load_attempted
14
+ load_attempted = true
15
+ require 'rubygems'
16
+ retry
17
+ end
18
+ end
19
+
20
+ require 'rubygems'
21
+ require 'neverblock'
22
+ require 'mongrel'
23
+
24
+ module Mongrel
25
+ class MongrelProtocol < EventMachine::Connection
26
+ def post_init
27
+ @parser = HttpParser.new
28
+ @params = HttpParams.new
29
+ @nparsed = 0
30
+ @request = nil
31
+ @request_len = nil
32
+ @linebuffer = ''
33
+ end
34
+
35
+ def receive_data data
36
+ @linebuffer << data
37
+ @nparsed = @parser.execute(@params, @linebuffer, @nparsed) unless @parser.finished?
38
+ if @parser.finished?
39
+ if @request_len.nil?
40
+ @request_len = @params[::Mongrel::Const::CONTENT_LENGTH].to_i
41
+ script_name, path_info, handlers = ::Mongrel::HttpServer::Instance.classifier.resolve(@params[::Mongrel::Const::REQUEST_PATH])
42
+ if handlers
43
+ @params[::Mongrel::Const::PATH_INFO] = path_info
44
+ @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]
46
+ @notifiers = handlers.select { |h| h.request_notify }
47
+ end
48
+ if @request_len > ::Mongrel::Const::MAX_BODY
49
+ new_buffer = Tempfile.new(::Mongrel::Const::MONGREL_TMP_BASE)
50
+ new_buffer.binmode
51
+ new_buffer << @linebuffer[@nparsed..-1]
52
+ @linebuffer = new_buffer
53
+ else
54
+ @linebuffer = StringIO.new(@linebuffer[@nparsed..-1])
55
+ @linebuffer.pos = @linebuffer.length
56
+ end
57
+ end
58
+ if @linebuffer.length >= @request_len
59
+ @linebuffer.rewind
60
+ ::Mongrel::HttpServer::Instance.process_http_request(@params,@linebuffer,self)
61
+ @linebuffer.delete if Tempfile === @linebuffer
62
+ end
63
+ elsif @linebuffer.length > ::Mongrel::Const::MAX_HEADER
64
+ close_connection
65
+ raise ::Mongrel::HttpParserError.new("HEADER is longer than allowed, aborting client early.")
66
+ end
67
+ rescue ::Mongrel::HttpParserError
68
+ if $mongrel_debug_client
69
+ STDERR.puts "#{Time.now}: BAD CLIENT (#{params[Const::HTTP_X_FORWARDED_FOR] || client.peeraddr.last}): #$!"
70
+ STDERR.puts "#{Time.now}: REQUEST DATA: #{data.inspect}\n---\nPARAMS: #{params.inspect}\n---\n"
71
+ end
72
+ close_connection
73
+ rescue Exception => e
74
+ close_connection
75
+ raise e
76
+ end
77
+
78
+ def write data
79
+ send_data data
80
+ end
81
+
82
+ def closed?
83
+ false
84
+ end
85
+
86
+ end
87
+
88
+ class HttpServer
89
+ DEFAULT_FIBER_POOL_SIZE = 20
90
+ def initialize(host, port, num_processors=950, x=0, y=nil) # Deal with Mongrel 1.0.1 or earlier, as well as later.
91
+ @socket = nil
92
+ @classifier = URIClassifier.new
93
+ @host = host
94
+ @port = port
95
+ @workers = ThreadGroup.new
96
+ if y
97
+ @throttle = x
98
+ @timeout = y || 60
99
+ else
100
+ @timeout = x
101
+ end
102
+ @num_processors = num_processors #num_processors is pointless for evented....
103
+ @death_time = 60
104
+ self.class.const_set(:Instance,self)
105
+ end
106
+
107
+ def fiber_pool
108
+ @fiber_pool ||= NB::Pool::FiberPool.new(DEFAULT_FIBER_POOL_SIZE)
109
+ end
110
+
111
+ def run
112
+ trap('INT') { raise StopServer }
113
+ trap('TERM') { raise StopServer }
114
+ @acceptor = Thread.new do
115
+ EventMachine.epoll
116
+ EventMachine.set_descriptor_table_size(4096)
117
+ EventMachine.run do
118
+ begin
119
+ EventMachine.start_server(@host,@port,MongrelProtocol)
120
+ rescue StopServer
121
+ EventMachine.stop_event_loop
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ def process_http_request(params,linebuffer,client)
128
+ if not params[Const::REQUEST_PATH]
129
+ uri = URI.parse(params[Const::REQUEST_URI])
130
+ params[Const::REQUEST_PATH] = uri.request_uri
131
+ end
132
+
133
+ raise "No REQUEST PATH" if not params[Const::REQUEST_PATH]
134
+
135
+ script_name, path_info, handlers = @classifier.resolve(params[Const::REQUEST_PATH])
136
+
137
+ if handlers
138
+ notifiers = handlers.select { |h| h.request_notify }
139
+ request = HttpRequest.new(params, linebuffer, notifiers)
140
+
141
+ # request is good so far, continue processing the response
142
+ response = HttpResponse.new(client)
143
+
144
+ # Process each handler in registered order until we run out or one finalizes the response.
145
+ fiber_pool.spawn do
146
+ dispatch_to_handlers(handlers,request,response)
147
+ end
148
+ # And finally, if nobody closed the response off, we finalize it.
149
+ unless response.done
150
+ response.finished
151
+ else
152
+ response.close_connection_after_writing
153
+ end
154
+ else
155
+ # Didn't find it, return a stock 404 response.
156
+ client.send_data(Const::ERROR_404_RESPONSE)
157
+ client.close_connection_after_writing
158
+ end
159
+ end
160
+
161
+ def dispatch_to_handlers(handlers,request,response)
162
+ handlers.each do |handler|
163
+ handler.process(request, response)
164
+ break if response.done
165
+ end
166
+ end
167
+ end
168
+
169
+ class HttpRequest
170
+ def initialize(params, linebuffer, dispatchers)
171
+ @params = params
172
+ @dispatchers = dispatchers
173
+ @body = linebuffer
174
+ end
175
+ end
176
+
177
+ class HttpResponse
178
+ def send_file(path, small_file = false)
179
+ File.open(path, "rb") do |f|
180
+ while chunk = f.read(Const::CHUNK_SIZE) and chunk.length > 0
181
+ begin
182
+ write(chunk)
183
+ rescue Object => exc
184
+ break
185
+ end
186
+ end
187
+ end
188
+ @body_sent = true
189
+ end
190
+
191
+ def write(data)
192
+ @socket.send_data data
193
+ end
194
+
195
+ def close_connection_after_writing
196
+ @socket.close_connection_after_writing
197
+ end
198
+
199
+ def socket_error(details)
200
+ @socket.close_connection
201
+ done = true
202
+ raise details
203
+ end
204
+
205
+ def finished
206
+ send_status
207
+ send_header
208
+ send_body
209
+ @socket.close_connection_after_writing
210
+ end
211
+ end
212
+
213
+ class Configurator
214
+ # This version fixes a bug in the regular Mongrel version by adding
215
+ # initialization of groups.
216
+ def change_privilege(user, group)
217
+ if user and group
218
+ log "Initializing groups for {#user}:{#group}."
219
+ Process.initgroups(user,Etc.getgrnam(group).gid)
220
+ end
221
+
222
+ if group
223
+ log "Changing group to #{group}."
224
+ Process::GID.change_privilege(Etc.getgrnam(group).gid)
225
+ end
226
+
227
+ if user
228
+ log "Changing user to #{user}."
229
+ Process::UID.change_privilege(Etc.getpwnam(user).uid)
230
+ end
231
+ rescue Errno::EPERM
232
+ log "FAILED to change user:group #{user}:#{group}: #$!"
233
+ exit 1
234
+ end
235
+ end
236
+ end
data/neverblock.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "neverblock"
3
- s.version = "0.1.2"
3
+ s.version = "0.1.3"
4
4
  s.date = "2008-09-04"
5
5
  s.summary = "Utilities for non-blocking stack components"
6
6
  s.email = "oldmoe@gmail.com"
@@ -21,6 +21,7 @@ Gem::Specification.new do |s|
21
21
  "lib/never_block/frameworks/rails.rb",
22
22
  "lib/never_block/frameworks/activerecord.rb",
23
23
  "lib/never_block/servers/thin.rb",
24
+ "lib/never_block/servers/mongrel.rb",
24
25
  "lib/never_block/db/fibered_postgres_connection.rb",
25
26
  "lib/never_block/db/pooled_fibered_postgres_connection.rb",
26
27
  "lib/never_block/db/fibered_mysql_connection.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oldmoe-neverblock
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Muhammad A. Ali
@@ -36,6 +36,7 @@ files:
36
36
  - lib/never_block/frameworks/rails.rb
37
37
  - lib/never_block/frameworks/activerecord.rb
38
38
  - lib/never_block/servers/thin.rb
39
+ - lib/never_block/servers/mongrel.rb
39
40
  - lib/never_block/db/fibered_postgres_connection.rb
40
41
  - lib/never_block/db/pooled_fibered_postgres_connection.rb
41
42
  - lib/never_block/db/fibered_mysql_connection.rb