prefork_engine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 227fa879a618645e19ba65f3abfc259f69f5dc79
4
- data.tar.gz: 416e99bead194dec4626ce28a7bcb0db3a5f9b01
3
+ metadata.gz: 28b3d832f86df0ffd4cc4f50995409fdf6e5fa03
4
+ data.tar.gz: 7cccd2f8bf896a14e5695616cf745b86c0905c10
5
5
  SHA512:
6
- metadata.gz: 9c7b50e3ebe7fed72b1c390c77ec102fb19aa36ad5350361a92f36b9fbeaa242d51c799f0674e93811b500f598fc2e16e04b557f45b78b3af8c7d9b6641f5041
7
- data.tar.gz: adf4233d2a8d8b164d1dafdb160e994855edb56721096bda279dd423cf461b87c97ffe2ba9f597c808724d62f2fb10aecb7b62834a019542c0b3fa83af08ed92
6
+ metadata.gz: 0ca812d9907a61cfe97d6199d3c3f72c56b4e92ef82a34b1748933cb10affecac50081d1cf0e1235e9a173323f3bebae218e6c7150661bfd9cd1b08bfa2ca1a9
7
+ data.tar.gz: 772d6caa397e481338a53018fe948f83172cffbf52bcc057190e4d963acf427a9a3498d040e54daff91c9b7a8b1cb4858bca24cedae3c3e40027f58586e89b4a
data/Changes ADDED
@@ -0,0 +1,4 @@
1
+ 0.0.2 2014-12-11T13:22:49Z
2
+
3
+ - remove unnecessary gems from requires
4
+ - add example rack handler
@@ -0,0 +1,13 @@
1
+ class HelloApp
2
+ def call(env)
3
+ # p env
4
+ [
5
+ 200,
6
+ { 'Content-Type' => 'text/html' },
7
+ ['hello world ']
8
+ ]
9
+ end
10
+ end
11
+
12
+ run HelloApp.new
13
+
@@ -0,0 +1,306 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # sample Rack handler using PreforkEngine
4
+ # This is ruby port of Starlet (https://metacpan.org/pod/Starlet)
5
+ #
6
+ # ## How to use
7
+ # $ gem install pico_http_parser
8
+ # $ gem isntall prefork_engine
9
+ # $ cat config.ru
10
+ # class HelloApp
11
+ # def call(env)
12
+ # [
13
+ # 200,
14
+ # { 'Content-Type' => 'text/html' },
15
+ # ['hello world ']
16
+ # ]
17
+ # end
18
+ # end
19
+ #
20
+ # run HelloApp.new
21
+ #
22
+ # $ rackup -r ./starlet.rb -E production -s Starlet \
23
+ # -O MaxWorkers=5 -O MaxRequestPerChild=1000 -O MinRequestPerChild=800 config.ru
24
+ #
25
+ # ## LICENSE
26
+ # This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
27
+ # See http://www.perl.com/perl/misc/Artistic.html
28
+
29
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
30
+ require 'rubygems'
31
+ require 'rack'
32
+ require 'stringio'
33
+ require 'socket'
34
+ require 'rack/utils'
35
+ require 'io/nonblock'
36
+ require 'prefork_engine'
37
+ require 'pico_http_parser'
38
+
39
+ module Rack
40
+ module Handler
41
+ class Starlet
42
+ DEFAULT_OPTIONS = {
43
+ :Host => '0.0.0.0',
44
+ :Port => 9292,
45
+ :MaxWorkers => 10,
46
+ :Timeout => 300,
47
+ :MaxRequestPerChild => 100,
48
+ :MinRequestPerChild => nil,
49
+ :SpawnInterval => nil,
50
+ :ErrRespawnInterval => nil
51
+ }
52
+ NULLIO = StringIO.new("").set_encoding('BINARY')
53
+
54
+ def self.run(app, options={})
55
+ slf = new(options)
56
+ slf.setup_listener()
57
+ slf.run_worker(app)
58
+ end
59
+
60
+ def self.valid_options
61
+ {
62
+ "Host=HOST" => "Hostname to listen on (default: 0.0.0.0)",
63
+ "Port=PORT" => "Port to listen on (default: 9292)",
64
+ }
65
+ end
66
+
67
+ def initialize(options={})
68
+ @options = DEFAULT_OPTIONS.merge(options)
69
+ @server = nil
70
+ @_is_tcp = false
71
+ @_using_defer_accept = false
72
+
73
+ end
74
+
75
+ def setup_listener()
76
+ if ENV["SERVER_STARTER_PORT"] then
77
+ hostport, fd = ENV["SERVER_STARTER_PORT"].split("=",2)
78
+ if m = hostport.match(/(.*):(\d+)/) then
79
+ @options[:Host] = m[0]
80
+ @options[:Port] = m[1].to_i
81
+ else
82
+ @options[:Port] = hostport
83
+ end
84
+ @server = TCPServer.for_fd(fd.to_i)
85
+ @_is_tcp = true if !@server.local_address.unix?
86
+ end
87
+
88
+ if @server == nil
89
+ @server = TCPServer.new(@options[:Host], @options[:Port])
90
+ @server.setsockopt(:SOCKET, :REUSEADDR, 1)
91
+ @_is_tcp = true
92
+ end
93
+
94
+ if RUBY_PLATFORM.match(/linux/) && @_is_tcp == true then
95
+ begin
96
+ @server.setsockopt(Socket::IPPROTO_TCP, 9, 1)
97
+ @_using_defer_accept = true
98
+ end
99
+ end
100
+ end
101
+
102
+ def run_worker(app)
103
+ pm_args = {
104
+ "max_workers" => @options[:MaxWorkers].to_i,
105
+ "trap_signals" => {
106
+ "TERM" => 'TERM',
107
+ "HUP" => 'TERM',
108
+ },
109
+ }
110
+ if @options[:SpawnInterval] then
111
+ pm_args["trap_signals"]["USR1"] = ["TERM", @options[:SpawnInterval].to_i]
112
+ pm_args["spawn_interval"] = @options[:SpawnInterval].to_i
113
+ end
114
+ if @options[:ErrRespawnInterval] then
115
+ pm_args["err_respawn_interval"] = @options[:ErrRespawnInterval].to_i
116
+ end
117
+ pe = PreforkEngine.new(pm_args)
118
+ while !pe.signal_received.match(/^(TERM|USR1)$/)
119
+ pe.start {
120
+ srand
121
+ self.accept_loop(app)
122
+ }
123
+ end
124
+ pe.wait_all_children
125
+ end
126
+
127
+ def _calc_reqs_per_child
128
+ max = @options[:MaxRequestPerChild].to_i
129
+ if min = @options[:MinRequestPerChild] then
130
+ return (max - (max - min.to_i + 1) * rand).to_i
131
+ end
132
+ return max.to_i
133
+ end
134
+
135
+ def accept_loop(app)
136
+ @can_exit = true
137
+ @term_received = 0
138
+ proc_req_count = 0
139
+
140
+ Signal.trap('TERM') {
141
+ if @can_exit then
142
+ exit!(true)
143
+ end
144
+ @term_received += 1
145
+ if @can_exit || @term_received > 1 then
146
+ exit!(true)
147
+ end
148
+ }
149
+ Signal.trap('PIPE', 'IGNORE')
150
+ max_reqs = self._calc_reqs_per_child()
151
+ while proc_req_count < max_reqs
152
+ @can_exit = true
153
+ connection = @server.accept
154
+ begin
155
+ connection.nonblock(true) {
156
+ peeraddr = nil
157
+ peerport = 0
158
+ if @_is_tcp then
159
+ connection.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
160
+ peer = connection.peeraddr
161
+ peeraddr = peer[2],
162
+ peerport = peer[1].to_s
163
+ end
164
+ proc_req_count += 1
165
+ @_is_deferred_accept = @_using_defer_accept
166
+ env = {
167
+ 'SERVER_NAME' => @options[:Host],
168
+ 'SERVER_PORT' => @options[:Port].to_s,
169
+ 'REMOTE_ADDR' => peeraddr,
170
+ 'REMOTE_PORT' => peerport,
171
+ 'rack.version' => [0,1],
172
+ 'rack.errors' => STDERR,
173
+ 'rack.multithread' => false,
174
+ 'rack.multiprocess' => false,
175
+ 'rack.run_once' => false,
176
+ 'rack.url_scheme' => 'http'
177
+ }
178
+ self.handle_connection(env, connection, app)
179
+ }
180
+ ensure
181
+ connection.close
182
+ end
183
+ end
184
+ end
185
+
186
+ def handle_connection(env, connection, app)
187
+ buf = ""
188
+ while true
189
+ readed = self.read_timeout(connection)
190
+ if readed == nil then
191
+ next
192
+ end
193
+ @can_exit = false
194
+ buf += readed
195
+ reqlen = PicoHTTPParser.parse_http_request(buf,env)
196
+ if reqlen >= 0 then
197
+ # force donwgrade to 1.0
198
+ env["SERVER_PROTOCOL"] = "HTTP/1.0"
199
+ # handle request
200
+ if (cl = env["CONTENT_LENGTH"].to_i) > 0 then
201
+ buf = buf.unpack('C*').slice(reqlen..-1).pack('C*')
202
+ buffer = StringIO.new("").set_encoding('BINARY')
203
+ while cl > 0
204
+ chunk = ""
205
+ if buf.bytesize > 0 then
206
+ chunk = buf
207
+ buf = ""
208
+ else
209
+ readed = self.read_timeout(connection)
210
+ if readed == nil then
211
+ return
212
+ end
213
+ chunk += readed
214
+ end
215
+ buffer << chunk
216
+ cl -= chunk.bytesize
217
+ end
218
+ buffer.rewind
219
+ env["rack.input"] = buffer
220
+ else
221
+ env["rack.input"] = NULLIO
222
+ end
223
+ status, header, body = app.call(env)
224
+ res_header = "HTTP/1.0 "+status.to_s+" "+Rack::Utils::HTTP_STATUS_CODES[status]+"\r\nConnection: close\r\n"
225
+ header.each do |k,vs|
226
+ if k.downcase == "connection" then
227
+ next
228
+ end
229
+ res_header += k + ": " + vs + "\r\n"
230
+ end
231
+ res_header += "\r\n"
232
+ if body.length == 1 && body[0].bytesize < 40960 then
233
+ ret = self.write_all(connection,res_header+body[0])
234
+ else
235
+ ret = self.write_all(connection,res_header)
236
+ body.each do |part|
237
+ self.write_all(connection,part)
238
+ end
239
+ end
240
+ return true
241
+ elsif reqlen == -2 then
242
+ # request is incomplete, do nothing
243
+ else
244
+ # error
245
+ return nil
246
+ end
247
+ end
248
+ end
249
+
250
+ def read_timeout(connection)
251
+ if @_is_deferred_accept then
252
+ @_is_deferred_accept = false
253
+ else
254
+ if !IO.select([connection],nil,nil,300) then
255
+ return nil
256
+ end
257
+ end
258
+ while true
259
+ begin
260
+ buf = connection.sysread(4096)
261
+ return buf
262
+ rescue IO::WaitReadable
263
+ # retry
264
+ break
265
+ rescue EOFError
266
+ # closed
267
+ return nil
268
+ end
269
+ end
270
+ end
271
+
272
+ def write_timeout(connection, buf)
273
+ while true
274
+ begin
275
+ len = connection.syswrite(buf)
276
+ return len
277
+ rescue IO::WaitReadable
278
+ # retry
279
+ break
280
+ rescue EOFError
281
+ # closed
282
+ return nil
283
+ end
284
+ end
285
+ if !IO.select(nil,[connection],nil,300) then
286
+ return nil
287
+ end
288
+ end
289
+
290
+ def write_all(connection, buf)
291
+ off = 0
292
+ while buf.bytesize - off > 0
293
+ ret = self.write_timeout(connection,buf.unpack('C*').slice(off..-1).pack('C*'))
294
+ if ret == nil then
295
+ return nil
296
+ end
297
+ off += ret
298
+ end
299
+ return buf.bytesize
300
+ end
301
+
302
+ end
303
+ end
304
+ end
305
+
306
+
@@ -1,3 +1,3 @@
1
1
  class PreforkEngine
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -22,6 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "rake", "~> 10.0"
23
23
  spec.add_development_dependency "rspec"
24
24
 
25
- spec.add_dependency "pico_http_parser", "~> 0.0.3"
26
25
  spec.add_dependency "proc-wait3", "~> 1.7.2"
27
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: prefork_engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masahiro Nagano
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-10 00:00:00.000000000 Z
11
+ date: 2014-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: pico_http_parser
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: 0.0.3
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: 0.0.3
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: proc-wait3
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -90,10 +76,13 @@ files:
90
76
  - ".gitignore"
91
77
  - ".rspec"
92
78
  - ".travis.yml"
79
+ - Changes
93
80
  - Gemfile
94
81
  - LICENSE.txt
95
82
  - README.md
96
83
  - Rakefile
84
+ - example/config.ru
85
+ - example/starlet.rb
97
86
  - lib/prefork_engine.rb
98
87
  - lib/prefork_engine/version.rb
99
88
  - prefork_engine.gemspec