puma-simon 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +7 -0
  2. data/.github/issue_template.md +20 -0
  3. data/.gitignore +18 -0
  4. data/.hoeignore +12 -0
  5. data/.travis.yml +29 -0
  6. data/DEPLOYMENT.md +91 -0
  7. data/Gemfile +12 -0
  8. data/History.md +1254 -0
  9. data/LICENSE +26 -0
  10. data/Manifest.txt +78 -0
  11. data/README.md +353 -0
  12. data/Rakefile +158 -0
  13. data/Release.md +9 -0
  14. data/bin/puma +10 -0
  15. data/bin/puma-wild +31 -0
  16. data/bin/pumactl +12 -0
  17. data/docs/nginx.md +80 -0
  18. data/docs/signals.md +43 -0
  19. data/docs/systemd.md +197 -0
  20. data/examples/CA/cacert.pem +23 -0
  21. data/examples/CA/newcerts/cert_1.pem +19 -0
  22. data/examples/CA/newcerts/cert_2.pem +19 -0
  23. data/examples/CA/private/cakeypair.pem +30 -0
  24. data/examples/CA/serial +1 -0
  25. data/examples/config.rb +200 -0
  26. data/examples/plugins/redis_stop_puma.rb +46 -0
  27. data/examples/puma/cert_puma.pem +19 -0
  28. data/examples/puma/client-certs/ca.crt +19 -0
  29. data/examples/puma/client-certs/ca.key +27 -0
  30. data/examples/puma/client-certs/client.crt +19 -0
  31. data/examples/puma/client-certs/client.key +27 -0
  32. data/examples/puma/client-certs/client_expired.crt +19 -0
  33. data/examples/puma/client-certs/client_expired.key +27 -0
  34. data/examples/puma/client-certs/client_unknown.crt +19 -0
  35. data/examples/puma/client-certs/client_unknown.key +27 -0
  36. data/examples/puma/client-certs/generate.rb +78 -0
  37. data/examples/puma/client-certs/keystore.jks +0 -0
  38. data/examples/puma/client-certs/server.crt +19 -0
  39. data/examples/puma/client-certs/server.key +27 -0
  40. data/examples/puma/client-certs/server.p12 +0 -0
  41. data/examples/puma/client-certs/unknown_ca.crt +19 -0
  42. data/examples/puma/client-certs/unknown_ca.key +27 -0
  43. data/examples/puma/csr_puma.pem +11 -0
  44. data/examples/puma/keystore.jks +0 -0
  45. data/examples/puma/puma_keypair.pem +15 -0
  46. data/examples/qc_config.rb +13 -0
  47. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  48. data/ext/puma_http11/ext_help.h +15 -0
  49. data/ext/puma_http11/extconf.rb +15 -0
  50. data/ext/puma_http11/http11_parser.c +1069 -0
  51. data/ext/puma_http11/http11_parser.h +65 -0
  52. data/ext/puma_http11/http11_parser.java.rl +161 -0
  53. data/ext/puma_http11/http11_parser.rl +147 -0
  54. data/ext/puma_http11/http11_parser_common.rl +54 -0
  55. data/ext/puma_http11/io_buffer.c +155 -0
  56. data/ext/puma_http11/mini_ssl.c +457 -0
  57. data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
  58. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +473 -0
  59. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +339 -0
  60. data/ext/puma_http11/puma_http11.c +500 -0
  61. data/gemfiles/2.1-Gemfile +12 -0
  62. data/lib/puma.rb +15 -0
  63. data/lib/puma/accept_nonblock.rb +23 -0
  64. data/lib/puma/app/status.rb +66 -0
  65. data/lib/puma/binder.rb +402 -0
  66. data/lib/puma/cli.rb +220 -0
  67. data/lib/puma/client.rb +434 -0
  68. data/lib/puma/cluster.rb +510 -0
  69. data/lib/puma/commonlogger.rb +106 -0
  70. data/lib/puma/compat.rb +14 -0
  71. data/lib/puma/configuration.rb +364 -0
  72. data/lib/puma/const.rb +224 -0
  73. data/lib/puma/control_cli.rb +259 -0
  74. data/lib/puma/convenient.rb +23 -0
  75. data/lib/puma/daemon_ext.rb +31 -0
  76. data/lib/puma/delegation.rb +11 -0
  77. data/lib/puma/detect.rb +13 -0
  78. data/lib/puma/dsl.rb +486 -0
  79. data/lib/puma/events.rb +152 -0
  80. data/lib/puma/io_buffer.rb +7 -0
  81. data/lib/puma/java_io_buffer.rb +45 -0
  82. data/lib/puma/jruby_restart.rb +83 -0
  83. data/lib/puma/launcher.rb +410 -0
  84. data/lib/puma/minissl.rb +221 -0
  85. data/lib/puma/null_io.rb +42 -0
  86. data/lib/puma/plugin.rb +115 -0
  87. data/lib/puma/plugin/tmp_restart.rb +35 -0
  88. data/lib/puma/rack/backports/uri/common_193.rb +33 -0
  89. data/lib/puma/rack/builder.rb +298 -0
  90. data/lib/puma/rack/urlmap.rb +91 -0
  91. data/lib/puma/rack_default.rb +7 -0
  92. data/lib/puma/reactor.rb +210 -0
  93. data/lib/puma/runner.rb +171 -0
  94. data/lib/puma/server.rb +949 -0
  95. data/lib/puma/single.rb +112 -0
  96. data/lib/puma/state_file.rb +29 -0
  97. data/lib/puma/tcp_logger.rb +39 -0
  98. data/lib/puma/thread_pool.rb +297 -0
  99. data/lib/puma/util.rb +128 -0
  100. data/lib/rack/handler/puma.rb +78 -0
  101. data/puma.gemspec +52 -0
  102. data/test/ab_rs.rb +22 -0
  103. data/test/config.rb +2 -0
  104. data/test/config/app.rb +9 -0
  105. data/test/config/plugin.rb +1 -0
  106. data/test/config/settings.rb +2 -0
  107. data/test/config/state_file_testing_config.rb +14 -0
  108. data/test/hello-bind.ru +2 -0
  109. data/test/hello-delay.ru +3 -0
  110. data/test/hello-map.ru +3 -0
  111. data/test/hello-post.ru +4 -0
  112. data/test/hello-stuck.ru +1 -0
  113. data/test/hello-tcp.ru +5 -0
  114. data/test/hello.ru +1 -0
  115. data/test/hijack.ru +6 -0
  116. data/test/hijack2.ru +5 -0
  117. data/test/lobster.ru +4 -0
  118. data/test/shell/run.sh +24 -0
  119. data/test/shell/t1.rb +19 -0
  120. data/test/shell/t1_conf.rb +3 -0
  121. data/test/shell/t2.rb +17 -0
  122. data/test/shell/t2_conf.rb +6 -0
  123. data/test/shell/t3.rb +25 -0
  124. data/test/shell/t3_conf.rb +5 -0
  125. data/test/slow.ru +4 -0
  126. data/test/ssl_config.rb +4 -0
  127. data/test/test_app_status.rb +93 -0
  128. data/test/test_binder.rb +31 -0
  129. data/test/test_cli.rb +209 -0
  130. data/test/test_config.rb +95 -0
  131. data/test/test_events.rb +161 -0
  132. data/test/test_helper.rb +50 -0
  133. data/test/test_http10.rb +27 -0
  134. data/test/test_http11.rb +186 -0
  135. data/test/test_integration.rb +247 -0
  136. data/test/test_iobuffer.rb +39 -0
  137. data/test/test_minissl.rb +29 -0
  138. data/test/test_null_io.rb +49 -0
  139. data/test/test_persistent.rb +245 -0
  140. data/test/test_puma_server.rb +626 -0
  141. data/test/test_puma_server_ssl.rb +222 -0
  142. data/test/test_rack_handler.rb +57 -0
  143. data/test/test_rack_server.rb +138 -0
  144. data/test/test_tcp_logger.rb +39 -0
  145. data/test/test_tcp_rack.rb +36 -0
  146. data/test/test_thread_pool.rb +250 -0
  147. data/test/test_unix_socket.rb +35 -0
  148. data/test/test_web_server.rb +88 -0
  149. data/tools/jungle/README.md +9 -0
  150. data/tools/jungle/init.d/README.md +59 -0
  151. data/tools/jungle/init.d/puma +421 -0
  152. data/tools/jungle/init.d/run-puma +18 -0
  153. data/tools/jungle/upstart/README.md +61 -0
  154. data/tools/jungle/upstart/puma-manager.conf +31 -0
  155. data/tools/jungle/upstart/puma.conf +69 -0
  156. data/tools/trickletest.rb +45 -0
  157. metadata +297 -0
@@ -0,0 +1,171 @@
1
+ require 'puma/server'
2
+ require 'puma/const'
3
+
4
+ module Puma
5
+ class Runner
6
+ def initialize(cli, events)
7
+ @launcher = cli
8
+ @events = events
9
+ @options = cli.options
10
+ @app = nil
11
+ @control = nil
12
+ end
13
+
14
+ def daemon?
15
+ @options[:daemon]
16
+ end
17
+
18
+ def development?
19
+ @options[:environment] == "development"
20
+ end
21
+
22
+ def log(str)
23
+ @events.log str
24
+ end
25
+
26
+ def before_restart
27
+ @control.stop(true) if @control
28
+ end
29
+
30
+ def error(str)
31
+ @events.error str
32
+ end
33
+
34
+ def debug(str)
35
+ @events.log "- #{str}" if @options[:debug]
36
+ end
37
+
38
+ def start_control
39
+ str = @options[:control_url]
40
+ return unless str
41
+
42
+ require 'puma/app/status'
43
+
44
+ uri = URI.parse str
45
+
46
+ app = Puma::App::Status.new @launcher
47
+
48
+ if token = @options[:control_auth_token]
49
+ app.auth_token = token unless token.empty? or token == :none
50
+ end
51
+
52
+ control = Puma::Server.new app, @launcher.events
53
+ control.min_threads = 0
54
+ control.max_threads = 1
55
+
56
+ case uri.scheme
57
+ when "tcp"
58
+ log "* Starting control server on #{str}"
59
+ control.add_tcp_listener uri.host, uri.port
60
+ when "unix"
61
+ log "* Starting control server on #{str}"
62
+ path = "#{uri.host}#{uri.path}"
63
+ mask = @options[:control_url_umask]
64
+
65
+ control.add_unix_listener path, mask
66
+ else
67
+ error "Invalid control URI: #{str}"
68
+ end
69
+
70
+ control.run
71
+ @control = control
72
+ end
73
+
74
+ def ruby_engine
75
+ if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
76
+ "ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
77
+ else
78
+ if defined?(RUBY_ENGINE_VERSION)
79
+ "#{RUBY_ENGINE} #{RUBY_ENGINE_VERSION} - ruby #{RUBY_VERSION}"
80
+ else
81
+ "#{RUBY_ENGINE} #{RUBY_VERSION}"
82
+ end
83
+ end
84
+ end
85
+
86
+ def output_header(mode)
87
+ min_t = @options[:min_threads]
88
+ max_t = @options[:max_threads]
89
+
90
+ log "Puma starting in #{mode} mode..."
91
+ log "* Version #{Puma::Const::PUMA_VERSION} (#{ruby_engine}), codename: #{Puma::Const::CODE_NAME}"
92
+ log "* Min threads: #{min_t}, max threads: #{max_t}"
93
+ log "* Environment: #{ENV['RACK_ENV']}"
94
+
95
+ if @options[:mode] == :tcp
96
+ log "* Mode: Lopez Express (tcp)"
97
+ end
98
+ end
99
+
100
+ def redirected_io?
101
+ @options[:redirect_stdout] || @options[:redirect_stderr]
102
+ end
103
+
104
+ def redirect_io
105
+ stdout = @options[:redirect_stdout]
106
+ stderr = @options[:redirect_stderr]
107
+ append = @options[:redirect_append]
108
+
109
+ if stdout
110
+ unless Dir.exists?(File.dirname(stdout))
111
+ raise "Cannot redirect STDOUT to #{stdout}"
112
+ end
113
+
114
+ STDOUT.reopen stdout, (append ? "a" : "w")
115
+ STDOUT.sync = true
116
+ STDOUT.puts "=== puma startup: #{Time.now} ==="
117
+ end
118
+
119
+ if stderr
120
+ unless Dir.exists?(File.dirname(stderr))
121
+ raise "Cannot redirect STDERR to #{stderr}"
122
+ end
123
+
124
+ STDERR.reopen stderr, (append ? "a" : "w")
125
+ STDERR.sync = true
126
+ STDERR.puts "=== puma startup: #{Time.now} ==="
127
+ end
128
+ end
129
+
130
+ def load_and_bind
131
+ unless @launcher.config.app_configured?
132
+ error "No application configured, nothing to run"
133
+ exit 1
134
+ end
135
+
136
+ # Load the app before we daemonize.
137
+ begin
138
+ @app = @launcher.config.app
139
+ rescue Exception => e
140
+ log "! Unable to load application: #{e.class}: #{e.message}"
141
+ raise e
142
+ end
143
+
144
+ @launcher.binder.parse @options[:binds], self
145
+ end
146
+
147
+ def app
148
+ @app ||= @launcher.config.app
149
+ end
150
+
151
+ def start_server
152
+ min_t = @options[:min_threads]
153
+ max_t = @options[:max_threads]
154
+
155
+ server = Puma::Server.new app, @launcher.events, @options
156
+ server.min_threads = min_t
157
+ server.max_threads = max_t
158
+ server.inherit_binder @launcher.binder
159
+
160
+ if @options[:mode] == :tcp
161
+ server.tcp_mode!
162
+ end
163
+
164
+ unless development?
165
+ server.leak_stack_on_error = false
166
+ end
167
+
168
+ server
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,949 @@
1
+ require 'stringio'
2
+
3
+ require 'puma/thread_pool'
4
+ require 'puma/const'
5
+ require 'puma/events'
6
+ require 'puma/null_io'
7
+ require 'puma/compat'
8
+ require 'puma/reactor'
9
+ require 'puma/client'
10
+ require 'puma/binder'
11
+ require 'puma/delegation'
12
+ require 'puma/accept_nonblock'
13
+ require 'puma/util'
14
+
15
+ require 'puma/puma_http11'
16
+
17
+ unless Puma.const_defined? "IOBuffer"
18
+ require 'puma/io_buffer'
19
+ end
20
+
21
+ require 'socket'
22
+
23
+ module Puma
24
+
25
+ # The HTTP Server itself. Serves out a single Rack app.
26
+ class Server
27
+
28
+ include Puma::Const
29
+ extend Puma::Delegation
30
+
31
+ # TCP_TIME_WAIT: 6, TCP_CLOSE: 7, TCP_CLOSE_WAIT: 8, TCP_LAST_ACK: 9
32
+ IGNORED_CHECK_CLIENT_SOCKET_STATES = (6..9)
33
+
34
+ attr_reader :thread
35
+ attr_reader :events
36
+ attr_accessor :app
37
+
38
+ attr_accessor :min_threads
39
+ attr_accessor :max_threads
40
+ attr_accessor :persistent_timeout
41
+ attr_accessor :auto_trim_time
42
+ attr_accessor :reaping_time
43
+ attr_accessor :first_data_timeout
44
+
45
+ # Create a server for the rack app +app+.
46
+ #
47
+ # +events+ is an object which will be called when certain error events occur
48
+ # to be handled. See Puma::Events for the list of current methods to implement.
49
+ #
50
+ # Server#run returns a thread that you can join on to wait for the server
51
+ # to do its work.
52
+ #
53
+ def initialize(app, events=Events.stdio, options={})
54
+ @app = app
55
+ @events = events
56
+
57
+ @check, @notify = Puma::Util.pipe
58
+
59
+ @status = :stop
60
+
61
+ @min_threads = 0
62
+ @max_threads = 16
63
+ @auto_trim_time = 30
64
+ @reaping_time = 1
65
+
66
+ @thread = nil
67
+ @thread_pool = nil
68
+
69
+ @persistent_timeout = options.fetch(:persistent_timeout, PERSISTENT_TIMEOUT)
70
+
71
+ @binder = Binder.new(events)
72
+ @own_binder = true
73
+
74
+ @first_data_timeout = FIRST_DATA_TIMEOUT
75
+
76
+ @leak_stack_on_error = true
77
+
78
+ @options = options
79
+ @queue_requests = options[:queue_requests].nil? ? true : options[:queue_requests]
80
+
81
+ ENV['RACK_ENV'] ||= "development"
82
+
83
+ @mode = :http
84
+ end
85
+
86
+ attr_accessor :binder, :leak_stack_on_error
87
+
88
+ forward :add_tcp_listener, :@binder
89
+ forward :add_ssl_listener, :@binder
90
+ forward :add_unix_listener, :@binder
91
+ forward :connected_port, :@binder
92
+
93
+ def inherit_binder(bind)
94
+ @binder = bind
95
+ @own_binder = false
96
+ end
97
+
98
+ def tcp_mode!
99
+ @mode = :tcp
100
+ end
101
+
102
+ # On Linux, use TCP_CORK to better control how the TCP stack
103
+ # packetizes our stream. This improves both latency and throughput.
104
+ #
105
+ if RUBY_PLATFORM =~ /linux/
106
+ # 6 == Socket::IPPROTO_TCP
107
+ # 3 == TCP_CORK
108
+ # 1/0 == turn on/off
109
+ def cork_socket(socket)
110
+ begin
111
+ socket.setsockopt(6, 3, 1) if socket.kind_of? TCPSocket
112
+ rescue IOError, SystemCallError
113
+ end
114
+ end
115
+
116
+ def uncork_socket(socket)
117
+ begin
118
+ socket.setsockopt(6, 3, 0) if socket.kind_of? TCPSocket
119
+ rescue IOError, SystemCallError
120
+ end
121
+ end
122
+
123
+ def closed_socket?(socket)
124
+ tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO)
125
+ state = tcp_info.unpack("C".freeze)[0]
126
+ IGNORED_CHECK_CLIENT_SOCKET_STATES.cover?(state)
127
+ end
128
+ else
129
+ def cork_socket(socket)
130
+ end
131
+
132
+ def uncork_socket(socket)
133
+ end
134
+
135
+ def closed_socket?(socket)
136
+ false
137
+ end
138
+ end
139
+
140
+ def backlog
141
+ @thread_pool and @thread_pool.backlog
142
+ end
143
+
144
+ def running
145
+ @thread_pool and @thread_pool.spawned
146
+ end
147
+
148
+ # Lopez Mode == raw tcp apps
149
+
150
+ def run_lopez_mode(background=true)
151
+ @thread_pool = ThreadPool.new(@min_threads,
152
+ @max_threads,
153
+ Hash) do |client, tl|
154
+
155
+ io = client.to_io
156
+ addr = io.peeraddr.last
157
+
158
+ if addr.empty?
159
+ # Set unix socket addrs to localhost
160
+ addr = "127.0.0.1:0"
161
+ else
162
+ addr = "#{addr}:#{io.peeraddr[1]}"
163
+ end
164
+
165
+ env = { 'thread' => tl, REMOTE_ADDR => addr }
166
+
167
+ begin
168
+ @app.call env, client.to_io
169
+ rescue Object => e
170
+ STDERR.puts "! Detected exception at toplevel: #{e.message} (#{e.class})"
171
+ STDERR.puts e.backtrace
172
+ end
173
+
174
+ client.close unless env['detach']
175
+ end
176
+
177
+ @events.fire :state, :running
178
+
179
+ if background
180
+ @thread = Thread.new { handle_servers_lopez_mode }
181
+ return @thread
182
+ else
183
+ handle_servers_lopez_mode
184
+ end
185
+ end
186
+
187
+ def handle_servers_lopez_mode
188
+ begin
189
+ check = @check
190
+ sockets = [check] + @binder.ios
191
+ pool = @thread_pool
192
+
193
+ while @status == :run
194
+ begin
195
+ ios = IO.select sockets
196
+ ios.first.each do |sock|
197
+ if sock == check
198
+ break if handle_check
199
+ else
200
+ begin
201
+ if io = sock.accept_nonblock
202
+ client = Client.new io, nil
203
+ pool << client
204
+ end
205
+ rescue SystemCallError
206
+ # nothing
207
+ rescue Errno::ECONNABORTED
208
+ # client closed the socket even before accept
209
+ io.close rescue nil
210
+ end
211
+ end
212
+ end
213
+ rescue Object => e
214
+ @events.unknown_error self, e, "Listen loop"
215
+ end
216
+ end
217
+
218
+ @events.fire :state, @status
219
+
220
+ graceful_shutdown if @status == :stop || @status == :restart
221
+
222
+ rescue Exception => e
223
+ STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
224
+ STDERR.puts e.backtrace
225
+ ensure
226
+ @check.close
227
+ @notify.close
228
+
229
+ if @status != :restart and @own_binder
230
+ @binder.close
231
+ end
232
+ end
233
+
234
+ @events.fire :state, :done
235
+ end
236
+ # Runs the server.
237
+ #
238
+ # If +background+ is true (the default) then a thread is spun
239
+ # up in the background to handle requests. Otherwise requests
240
+ # are handled synchronously.
241
+ #
242
+ def run(background=true)
243
+ BasicSocket.do_not_reverse_lookup = true
244
+
245
+ @events.fire :state, :booting
246
+
247
+ @status = :run
248
+
249
+ if @mode == :tcp
250
+ return run_lopez_mode(background)
251
+ end
252
+
253
+ queue_requests = @queue_requests
254
+
255
+ @thread_pool = ThreadPool.new(@min_threads,
256
+ @max_threads,
257
+ IOBuffer) do |client, buffer|
258
+
259
+ # Advertise this server into the thread
260
+ Thread.current[ThreadLocalKey] = self
261
+
262
+ process_now = false
263
+
264
+ begin
265
+ if queue_requests
266
+ process_now = client.eagerly_finish
267
+ else
268
+ client.finish
269
+ process_now = true
270
+ end
271
+ rescue MiniSSL::SSLError => e
272
+ ssl_socket = client.io
273
+ addr = ssl_socket.peeraddr.last
274
+ cert = ssl_socket.peercert
275
+
276
+ client.close
277
+
278
+ @events.ssl_error self, addr, cert, e
279
+ rescue HttpParserError => e
280
+ client.write_400
281
+ client.close
282
+
283
+ @events.parse_error self, client.env, e
284
+ rescue ConnectionError
285
+ client.close
286
+ else
287
+ if process_now
288
+ process_client client, buffer
289
+ else
290
+ client.set_timeout @first_data_timeout
291
+ @reactor.add client
292
+ end
293
+ end
294
+ end
295
+
296
+ @thread_pool.clean_thread_locals = @options[:clean_thread_locals]
297
+
298
+ if queue_requests
299
+ @reactor = Reactor.new self, @thread_pool
300
+ @reactor.run_in_thread
301
+ end
302
+
303
+ if @reaping_time
304
+ @thread_pool.auto_reap!(@reaping_time)
305
+ end
306
+
307
+ if @auto_trim_time
308
+ @thread_pool.auto_trim!(@auto_trim_time)
309
+ end
310
+
311
+ @events.fire :state, :running
312
+
313
+ if background
314
+ @thread = Thread.new { handle_servers }
315
+ return @thread
316
+ else
317
+ handle_servers
318
+ end
319
+ end
320
+
321
+ def handle_servers
322
+ begin
323
+ check = @check
324
+ sockets = [check] + @binder.ios
325
+ pool = @thread_pool
326
+ queue_requests = @queue_requests
327
+
328
+ remote_addr_value = nil
329
+ remote_addr_header = nil
330
+
331
+ case @options[:remote_address]
332
+ when :value
333
+ remote_addr_value = @options[:remote_address_value]
334
+ when :header
335
+ remote_addr_header = @options[:remote_address_header]
336
+ end
337
+
338
+ while @status == :run
339
+ begin
340
+ ios = IO.select sockets
341
+ ios.first.each do |sock|
342
+ if sock == check
343
+ break if handle_check
344
+ else
345
+ begin
346
+ if io = sock.accept_nonblock
347
+ client = Client.new io, @binder.env(sock)
348
+ if remote_addr_value
349
+ client.peerip = remote_addr_value
350
+ elsif remote_addr_header
351
+ client.remote_addr_header = remote_addr_header
352
+ end
353
+
354
+ pool << client
355
+ pool.wait_until_not_full unless queue_requests
356
+ end
357
+ rescue SystemCallError
358
+ # nothing
359
+ rescue Errno::ECONNABORTED
360
+ # client closed the socket even before accept
361
+ io.close rescue nil
362
+ end
363
+ end
364
+ end
365
+ rescue Object => e
366
+ @events.unknown_error self, e, "Listen loop"
367
+ end
368
+ end
369
+
370
+ @events.fire :state, @status
371
+
372
+ graceful_shutdown if @status == :stop || @status == :restart
373
+ if queue_requests
374
+ @reactor.clear!
375
+ @reactor.shutdown
376
+ end
377
+ rescue Exception => e
378
+ STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
379
+ STDERR.puts e.backtrace
380
+ ensure
381
+ @check.close
382
+ @notify.close
383
+
384
+ if @status != :restart and @own_binder
385
+ @binder.close
386
+ end
387
+ end
388
+
389
+ @events.fire :state, :done
390
+ end
391
+
392
+ # :nodoc:
393
+ def handle_check
394
+ cmd = @check.read(1)
395
+
396
+ case cmd
397
+ when STOP_COMMAND
398
+ @status = :stop
399
+ return true
400
+ when HALT_COMMAND
401
+ @status = :halt
402
+ return true
403
+ when RESTART_COMMAND
404
+ @status = :restart
405
+ return true
406
+ end
407
+
408
+ return false
409
+ end
410
+
411
+ # Given a connection on +client+, handle the incoming requests.
412
+ #
413
+ # This method support HTTP Keep-Alive so it may, depending on if the client
414
+ # indicates that it supports keep alive, wait for another request before
415
+ # returning.
416
+ #
417
+ def process_client(client, buffer)
418
+ begin
419
+
420
+ if client.env[HTTP_EXPECT] == CONTINUE
421
+ client.io << HTTP_11_100
422
+ end
423
+
424
+ clean_thread_locals = @options[:clean_thread_locals]
425
+ close_socket = true
426
+
427
+ while true
428
+ case handle_request(client, buffer)
429
+ when false
430
+ return
431
+ when :async
432
+ close_socket = false
433
+ return
434
+ when true
435
+ return unless @queue_requests
436
+ buffer.reset
437
+
438
+ ThreadPool.clean_thread_locals if clean_thread_locals
439
+
440
+ unless client.reset(@status == :run)
441
+ close_socket = false
442
+ client.set_timeout @persistent_timeout
443
+ @reactor.add client
444
+ return
445
+ end
446
+ end
447
+ end
448
+
449
+ # The client disconnected while we were reading data
450
+ rescue ConnectionError
451
+ # Swallow them. The ensure tries to close +client+ down
452
+
453
+ # SSL handshake error
454
+ rescue MiniSSL::SSLError => e
455
+ lowlevel_error(e, client.env)
456
+
457
+ ssl_socket = client.io
458
+ addr = ssl_socket.peeraddr.last
459
+ cert = ssl_socket.peercert
460
+
461
+ close_socket = true
462
+
463
+ @events.ssl_error self, addr, cert, e
464
+
465
+ # The client doesn't know HTTP well
466
+ rescue HttpParserError => e
467
+ lowlevel_error(e, client.env)
468
+
469
+ client.write_400
470
+
471
+ @events.parse_error self, client.env, e
472
+
473
+ # Server error
474
+ rescue StandardError => e
475
+ lowlevel_error(e, client.env)
476
+
477
+ client.write_500
478
+
479
+ @events.unknown_error self, e, "Read"
480
+
481
+ ensure
482
+ buffer.reset
483
+
484
+ begin
485
+ client.close if close_socket
486
+ rescue IOError, SystemCallError
487
+ # Already closed
488
+ rescue StandardError => e
489
+ @events.unknown_error self, e, "Client"
490
+ end
491
+ end
492
+ end
493
+
494
+ # Given a Hash +env+ for the request read from +client+, add
495
+ # and fixup keys to comply with Rack's env guidelines.
496
+ #
497
+ def normalize_env(env, client)
498
+ if host = env[HTTP_HOST]
499
+ if colon = host.index(":")
500
+ env[SERVER_NAME] = host[0, colon]
501
+ env[SERVER_PORT] = host[colon+1, host.bytesize]
502
+ else
503
+ env[SERVER_NAME] = host
504
+ env[SERVER_PORT] = default_server_port(env)
505
+ end
506
+ else
507
+ env[SERVER_NAME] = LOCALHOST
508
+ env[SERVER_PORT] = default_server_port(env)
509
+ end
510
+
511
+ unless env[REQUEST_PATH]
512
+ # it might be a dumbass full host request header
513
+ uri = URI.parse(env[REQUEST_URI])
514
+ env[REQUEST_PATH] = uri.path
515
+
516
+ raise "No REQUEST PATH" unless env[REQUEST_PATH]
517
+
518
+ env[QUERY_STRING] = uri.query
519
+ end
520
+
521
+ env[PATH_INFO] = env[REQUEST_PATH]
522
+
523
+ # From http://www.ietf.org/rfc/rfc3875 :
524
+ # "Script authors should be aware that the REMOTE_ADDR and
525
+ # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
526
+ # may not identify the ultimate source of the request.
527
+ # They identify the client for the immediate request to the
528
+ # server; that client may be a proxy, gateway, or other
529
+ # intermediary acting on behalf of the actual source client."
530
+ #
531
+
532
+ unless env.key?(REMOTE_ADDR)
533
+ begin
534
+ addr = client.peerip
535
+ rescue Errno::ENOTCONN
536
+ # Client disconnects can result in an inability to get the
537
+ # peeraddr from the socket; default to localhost.
538
+ addr = LOCALHOST_IP
539
+ end
540
+
541
+ # Set unix socket addrs to localhost
542
+ addr = LOCALHOST_IP if addr.empty?
543
+
544
+ env[REMOTE_ADDR] = addr
545
+ end
546
+ end
547
+
548
+ def default_server_port(env)
549
+ return PORT_443 if env[HTTPS_KEY] == 'on' || env[HTTPS_KEY] == 'https'
550
+ env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
551
+ end
552
+
553
+ # Given the request +env+ from +client+ and a partial request body
554
+ # in +body+, finish reading the body if there is one and invoke
555
+ # the rack app. Then construct the response and write it back to
556
+ # +client+
557
+ #
558
+ # +cl+ is the previously fetched Content-Length header if there
559
+ # was one. This is an optimization to keep from having to look
560
+ # it up again.
561
+ #
562
+ def handle_request(req, lines)
563
+ env = req.env
564
+ client = req.io
565
+
566
+ return false if closed_socket?(client)
567
+
568
+ normalize_env env, req
569
+
570
+ env[PUMA_SOCKET] = client
571
+
572
+ if env[HTTPS_KEY] && client.peercert
573
+ env[PUMA_PEERCERT] = client.peercert
574
+ end
575
+
576
+ env[HIJACK_P] = true
577
+ env[HIJACK] = req
578
+
579
+ body = req.body
580
+
581
+ head = env[REQUEST_METHOD] == HEAD
582
+
583
+ env[RACK_INPUT] = body
584
+ env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
585
+
586
+ # A rack extension. If the app writes #call'ables to this
587
+ # array, we will invoke them when the request is done.
588
+ #
589
+ after_reply = env[RACK_AFTER_REPLY] = []
590
+
591
+ begin
592
+ begin
593
+ status, headers, res_body = @app.call(env)
594
+
595
+ return :async if req.hijacked
596
+
597
+ status = status.to_i
598
+
599
+ if status == -1
600
+ unless headers.empty? and res_body == []
601
+ raise "async response must have empty headers and body"
602
+ end
603
+
604
+ return :async
605
+ end
606
+ rescue ThreadPool::ForceShutdown => e
607
+ @events.log "Detected force shutdown of a thread, returning 503"
608
+ @events.unknown_error self, e, "Rack app"
609
+
610
+ status = 503
611
+ headers = {}
612
+ res_body = ["Request was internally terminated early\n"]
613
+
614
+ rescue Exception => e
615
+ @events.unknown_error self, e, "Rack app", env
616
+
617
+ status, headers, res_body = lowlevel_error(e, env)
618
+ end
619
+
620
+ content_length = nil
621
+ no_body = head
622
+
623
+ if res_body.kind_of? Array and res_body.size == 1
624
+ content_length = res_body[0].bytesize
625
+ end
626
+
627
+ cork_socket client
628
+
629
+ line_ending = LINE_END
630
+ colon = COLON
631
+
632
+ http_11 = if env[HTTP_VERSION] == HTTP_11
633
+ allow_chunked = true
634
+ keep_alive = env.fetch(HTTP_CONNECTION, "").downcase != CLOSE
635
+ include_keepalive_header = false
636
+
637
+ # An optimization. The most common response is 200, so we can
638
+ # reply with the proper 200 status without having to compute
639
+ # the response header.
640
+ #
641
+ if status == 200
642
+ lines << HTTP_11_200
643
+ else
644
+ lines.append "HTTP/1.1 ", status.to_s, " ",
645
+ fetch_status_code(status), line_ending
646
+
647
+ no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
648
+ end
649
+ true
650
+ else
651
+ allow_chunked = false
652
+ keep_alive = env.fetch(HTTP_CONNECTION, "").downcase == KEEP_ALIVE
653
+ include_keepalive_header = keep_alive
654
+
655
+ # Same optimization as above for HTTP/1.1
656
+ #
657
+ if status == 200
658
+ lines << HTTP_10_200
659
+ else
660
+ lines.append "HTTP/1.0 ", status.to_s, " ",
661
+ fetch_status_code(status), line_ending
662
+
663
+ no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
664
+ end
665
+ false
666
+ end
667
+
668
+ response_hijack = nil
669
+
670
+ headers.each do |k, vs|
671
+ case k.downcase
672
+ when CONTENT_LENGTH2
673
+ content_length = vs
674
+ next
675
+ when TRANSFER_ENCODING
676
+ allow_chunked = false
677
+ content_length = nil
678
+ when HIJACK
679
+ response_hijack = vs
680
+ next
681
+ end
682
+
683
+ if vs.respond_to?(:to_s)
684
+ vs.to_s.split(NEWLINE).each do |v|
685
+ lines.append k, colon, v, line_ending
686
+ end
687
+ else
688
+ lines.append k, colon, line_ending
689
+ end
690
+ end
691
+
692
+ if include_keepalive_header
693
+ lines << CONNECTION_KEEP_ALIVE
694
+ elsif http_11 && !keep_alive
695
+ lines << CONNECTION_CLOSE
696
+ end
697
+
698
+ if no_body
699
+ if content_length and status != 204
700
+ lines.append CONTENT_LENGTH_S, content_length.to_s, line_ending
701
+ end
702
+
703
+ lines << line_ending
704
+ fast_write client, lines.to_s
705
+ return keep_alive
706
+ end
707
+
708
+ if content_length
709
+ lines.append CONTENT_LENGTH_S, content_length.to_s, line_ending
710
+ chunked = false
711
+ elsif !response_hijack and allow_chunked
712
+ lines << TRANSFER_ENCODING_CHUNKED
713
+ chunked = true
714
+ end
715
+
716
+ lines << line_ending
717
+
718
+ fast_write client, lines.to_s
719
+
720
+ if response_hijack
721
+ response_hijack.call client
722
+ return :async
723
+ end
724
+
725
+ begin
726
+ res_body.each do |part|
727
+ if chunked
728
+ next if part.bytesize.zero?
729
+ fast_write client, part.bytesize.to_s(16)
730
+ fast_write client, line_ending
731
+ fast_write client, part
732
+ fast_write client, line_ending
733
+ else
734
+ fast_write client, part
735
+ end
736
+
737
+ client.flush
738
+ end
739
+
740
+ if chunked
741
+ fast_write client, CLOSE_CHUNKED
742
+ client.flush
743
+ end
744
+ rescue SystemCallError, IOError
745
+ raise ConnectionError, "Connection error detected during write"
746
+ end
747
+
748
+ ensure
749
+ uncork_socket client
750
+
751
+ body.close
752
+ req.tempfile.unlink if req.tempfile
753
+ res_body.close if res_body.respond_to? :close
754
+
755
+ after_reply.each { |o| o.call }
756
+ end
757
+
758
+ return keep_alive
759
+ end
760
+
761
+ def fetch_status_code(status)
762
+ HTTP_STATUS_CODES.fetch(status) { 'CUSTOM' }
763
+ end
764
+ private :fetch_status_code
765
+
766
+ # Given the request +env+ from +client+ and the partial body +body+
767
+ # plus a potential Content-Length value +cl+, finish reading
768
+ # the body and return it.
769
+ #
770
+ # If the body is larger than MAX_BODY, a Tempfile object is used
771
+ # for the body, otherwise a StringIO is used.
772
+ #
773
+ def read_body(env, client, body, cl)
774
+ content_length = cl.to_i
775
+
776
+ remain = content_length - body.bytesize
777
+
778
+ return StringIO.new(body) if remain <= 0
779
+
780
+ # Use a Tempfile if there is a lot of data left
781
+ if remain > MAX_BODY
782
+ stream = Tempfile.new(Const::PUMA_TMP_BASE)
783
+ stream.binmode
784
+ else
785
+ # The body[0,0] trick is to get an empty string in the same
786
+ # encoding as body.
787
+ stream = StringIO.new body[0,0]
788
+ end
789
+
790
+ stream.write body
791
+
792
+ # Read an odd sized chunk so we can read even sized ones
793
+ # after this
794
+ chunk = client.readpartial(remain % CHUNK_SIZE)
795
+
796
+ # No chunk means a closed socket
797
+ unless chunk
798
+ stream.close
799
+ return nil
800
+ end
801
+
802
+ remain -= stream.write(chunk)
803
+
804
+ # Raed the rest of the chunks
805
+ while remain > 0
806
+ chunk = client.readpartial(CHUNK_SIZE)
807
+ unless chunk
808
+ stream.close
809
+ return nil
810
+ end
811
+
812
+ remain -= stream.write(chunk)
813
+ end
814
+
815
+ stream.rewind
816
+
817
+ return stream
818
+ end
819
+
820
+ # A fallback rack response if +@app+ raises as exception.
821
+ #
822
+ def lowlevel_error(e, env)
823
+ if handler = @options[:lowlevel_error_handler]
824
+ if handler.arity == 1
825
+ return handler.call(e)
826
+ else
827
+ return handler.call(e, env)
828
+ end
829
+ end
830
+
831
+ if @leak_stack_on_error
832
+ [500, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
833
+ else
834
+ [500, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
835
+ end
836
+ end
837
+
838
+ # Wait for all outstanding requests to finish.
839
+ #
840
+ def graceful_shutdown
841
+ if @options[:shutdown_debug]
842
+ threads = Thread.list
843
+ total = threads.size
844
+
845
+ pid = Process.pid
846
+
847
+ $stdout.syswrite "#{pid}: === Begin thread backtrace dump ===\n"
848
+
849
+ threads.each_with_index do |t,i|
850
+ $stdout.syswrite "#{pid}: Thread #{i+1}/#{total}: #{t.inspect}\n"
851
+ $stdout.syswrite "#{pid}: #{t.backtrace.join("\n#{pid}: ")}\n\n"
852
+ end
853
+ $stdout.syswrite "#{pid}: === End thread backtrace dump ===\n"
854
+ end
855
+
856
+ if @options[:drain_on_shutdown]
857
+ count = 0
858
+
859
+ while true
860
+ ios = IO.select @binder.ios, nil, nil, 0
861
+ break unless ios
862
+
863
+ ios.first.each do |sock|
864
+ begin
865
+ if io = sock.accept_nonblock
866
+ count += 1
867
+ client = Client.new io, @binder.env(sock)
868
+ @thread_pool << client
869
+ end
870
+ rescue SystemCallError
871
+ end
872
+ end
873
+ end
874
+
875
+ @events.debug "Drained #{count} additional connections."
876
+ end
877
+
878
+ if @thread_pool
879
+ if timeout = @options[:force_shutdown_after]
880
+ @thread_pool.shutdown timeout.to_i
881
+ else
882
+ @thread_pool.shutdown
883
+ end
884
+ end
885
+ end
886
+
887
+ # Stops the acceptor thread and then causes the worker threads to finish
888
+ # off the request queue before finally exiting.
889
+ #
890
+ def stop(sync=false)
891
+ begin
892
+ @notify << STOP_COMMAND
893
+ rescue IOError
894
+ # The server, in another thread, is shutting down
895
+ end
896
+
897
+ @thread.join if @thread && sync
898
+ end
899
+
900
+ def halt(sync=false)
901
+ begin
902
+ @notify << HALT_COMMAND
903
+ rescue IOError
904
+ # The server, in another thread, is shutting down
905
+ end
906
+
907
+ @thread.join if @thread && sync
908
+ end
909
+
910
+ def begin_restart
911
+ begin
912
+ @notify << RESTART_COMMAND
913
+ rescue IOError
914
+ # The server, in another thread, is shutting down
915
+ end
916
+ end
917
+
918
+ def fast_write(io, str)
919
+ n = 0
920
+ while true
921
+ begin
922
+ n = io.syswrite str
923
+ rescue Errno::EAGAIN, Errno::EWOULDBLOCK
924
+ if !IO.select(nil, [io], nil, WRITE_TIMEOUT)
925
+ raise ConnectionError, "Socket timeout writing data"
926
+ end
927
+
928
+ retry
929
+ rescue Errno::EPIPE, SystemCallError, IOError
930
+ raise ConnectionError, "Socket timeout writing data"
931
+ end
932
+
933
+ return if n == str.bytesize
934
+ str = str.byteslice(n..-1)
935
+ end
936
+ end
937
+ private :fast_write
938
+
939
+ ThreadLocalKey = :puma_server
940
+
941
+ def self.current
942
+ Thread.current[ThreadLocalKey]
943
+ end
944
+
945
+ def shutting_down?
946
+ @status == :stop || @status == :restart
947
+ end
948
+ end
949
+ end