jun-puma 1.0.0-java

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.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +2897 -0
  3. data/LICENSE +29 -0
  4. data/README.md +475 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +25 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +74 -0
  9. data/docs/compile_options.md +55 -0
  10. data/docs/deployment.md +102 -0
  11. data/docs/fork_worker.md +35 -0
  12. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  13. data/docs/images/puma-connection-flow.png +0 -0
  14. data/docs/images/puma-general-arch.png +0 -0
  15. data/docs/jungle/README.md +9 -0
  16. data/docs/jungle/rc.d/README.md +74 -0
  17. data/docs/jungle/rc.d/puma +61 -0
  18. data/docs/jungle/rc.d/puma.conf +10 -0
  19. data/docs/kubernetes.md +78 -0
  20. data/docs/nginx.md +80 -0
  21. data/docs/plugins.md +38 -0
  22. data/docs/rails_dev_mode.md +28 -0
  23. data/docs/restart.md +65 -0
  24. data/docs/signals.md +98 -0
  25. data/docs/stats.md +142 -0
  26. data/docs/systemd.md +253 -0
  27. data/docs/testing_benchmarks_local_files.md +150 -0
  28. data/docs/testing_test_rackup_ci_files.md +36 -0
  29. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  30. data/ext/puma_http11/ext_help.h +15 -0
  31. data/ext/puma_http11/extconf.rb +80 -0
  32. data/ext/puma_http11/http11_parser.c +1057 -0
  33. data/ext/puma_http11/http11_parser.h +65 -0
  34. data/ext/puma_http11/http11_parser.java.rl +145 -0
  35. data/ext/puma_http11/http11_parser.rl +149 -0
  36. data/ext/puma_http11/http11_parser_common.rl +54 -0
  37. data/ext/puma_http11/mini_ssl.c +842 -0
  38. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  39. data/ext/puma_http11/org/jruby/puma/Http11.java +228 -0
  40. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
  41. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +509 -0
  42. data/ext/puma_http11/puma_http11.c +495 -0
  43. data/lib/puma/app/status.rb +96 -0
  44. data/lib/puma/binder.rb +502 -0
  45. data/lib/puma/cli.rb +247 -0
  46. data/lib/puma/client.rb +682 -0
  47. data/lib/puma/cluster/worker.rb +180 -0
  48. data/lib/puma/cluster/worker_handle.rb +96 -0
  49. data/lib/puma/cluster.rb +616 -0
  50. data/lib/puma/commonlogger.rb +115 -0
  51. data/lib/puma/configuration.rb +390 -0
  52. data/lib/puma/const.rb +307 -0
  53. data/lib/puma/control_cli.rb +316 -0
  54. data/lib/puma/detect.rb +45 -0
  55. data/lib/puma/dsl.rb +1425 -0
  56. data/lib/puma/error_logger.rb +113 -0
  57. data/lib/puma/events.rb +57 -0
  58. data/lib/puma/io_buffer.rb +46 -0
  59. data/lib/puma/jruby_restart.rb +11 -0
  60. data/lib/puma/json_serialization.rb +96 -0
  61. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  62. data/lib/puma/launcher.rb +488 -0
  63. data/lib/puma/log_writer.rb +147 -0
  64. data/lib/puma/minissl/context_builder.rb +96 -0
  65. data/lib/puma/minissl.rb +459 -0
  66. data/lib/puma/null_io.rb +84 -0
  67. data/lib/puma/plugin/systemd.rb +90 -0
  68. data/lib/puma/plugin/tmp_restart.rb +36 -0
  69. data/lib/puma/plugin.rb +111 -0
  70. data/lib/puma/puma_http11.jar +0 -0
  71. data/lib/puma/rack/builder.rb +297 -0
  72. data/lib/puma/rack/urlmap.rb +93 -0
  73. data/lib/puma/rack_default.rb +24 -0
  74. data/lib/puma/reactor.rb +125 -0
  75. data/lib/puma/request.rb +688 -0
  76. data/lib/puma/runner.rb +213 -0
  77. data/lib/puma/sd_notify.rb +149 -0
  78. data/lib/puma/server.rb +680 -0
  79. data/lib/puma/single.rb +69 -0
  80. data/lib/puma/state_file.rb +68 -0
  81. data/lib/puma/thread_pool.rb +434 -0
  82. data/lib/puma/util.rb +141 -0
  83. data/lib/puma.rb +78 -0
  84. data/lib/rack/handler/puma.rb +144 -0
  85. data/tools/Dockerfile +16 -0
  86. data/tools/trickletest.rb +44 -0
  87. metadata +153 -0
@@ -0,0 +1,502 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'socket'
5
+
6
+ require_relative 'const'
7
+ require_relative 'util'
8
+ require_relative 'configuration'
9
+
10
+ module Puma
11
+
12
+ if HAS_SSL
13
+ require_relative 'minissl'
14
+ require_relative 'minissl/context_builder'
15
+ end
16
+
17
+ class Binder
18
+ include Puma::Const
19
+
20
+ RACK_VERSION = [1,6].freeze
21
+
22
+ def initialize(log_writer, conf = Configuration.new, env: ENV)
23
+ @log_writer = log_writer
24
+ @conf = conf
25
+ @listeners = []
26
+ @inherited_fds = {}
27
+ @activated_sockets = {}
28
+ @unix_paths = []
29
+ @env = env
30
+
31
+ @proto_env = {
32
+ "rack.version".freeze => RACK_VERSION,
33
+ "rack.errors".freeze => log_writer.stderr,
34
+ "rack.multithread".freeze => conf.options[:max_threads] > 1,
35
+ "rack.multiprocess".freeze => conf.options[:workers] >= 1,
36
+ "rack.run_once".freeze => false,
37
+ RACK_URL_SCHEME => conf.options[:rack_url_scheme],
38
+ "SCRIPT_NAME".freeze => env['SCRIPT_NAME'] || "",
39
+
40
+ # I'd like to set a default CONTENT_TYPE here but some things
41
+ # depend on their not being a default set and inferring
42
+ # it from the content. And so if i set it here, it won't
43
+ # infer properly.
44
+
45
+ "QUERY_STRING".freeze => "",
46
+ SERVER_SOFTWARE => PUMA_SERVER_STRING,
47
+ GATEWAY_INTERFACE => CGI_VER
48
+ }
49
+
50
+ @envs = {}
51
+ @ios = []
52
+ end
53
+
54
+ attr_reader :ios
55
+
56
+ # @version 5.0.0
57
+ attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
58
+
59
+ # @version 5.0.0
60
+ attr_writer :ios, :listeners
61
+
62
+ def env(sock)
63
+ @envs.fetch(sock, @proto_env)
64
+ end
65
+
66
+ def close
67
+ @ios.each { |i| i.close }
68
+ end
69
+
70
+ # @!attribute [r] connected_ports
71
+ # @version 5.0.0
72
+ def connected_ports
73
+ t = ios.map { |io| io.addr[1] }; t.uniq!; t
74
+ end
75
+
76
+ # @version 5.0.0
77
+ def create_inherited_fds(env_hash)
78
+ env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
79
+ fd, url = v.split(":", 2)
80
+ @inherited_fds[url] = fd.to_i
81
+ end.keys # pass keys back for removal
82
+ end
83
+
84
+ # systemd socket activation.
85
+ # LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
86
+ # LISTEN_PID = PID of the service process, aka us
87
+ # @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
88
+ # @version 5.0.0
89
+ #
90
+ def create_activated_fds(env_hash)
91
+ @log_writer.debug "ENV['LISTEN_FDS'] #{@env['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
92
+ return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
93
+ env_hash['LISTEN_FDS'].to_i.times do |index|
94
+ sock = TCPServer.for_fd(socket_activation_fd(index))
95
+ key = begin # Try to parse as a path
96
+ [:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
97
+ rescue ArgumentError # Try to parse as a port/ip
98
+ port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
99
+ addr = "[#{addr}]" if addr&.include? ':'
100
+ [:tcp, addr, port]
101
+ end
102
+ @activated_sockets[key] = sock
103
+ @log_writer.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
104
+ end
105
+ ["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
106
+ end
107
+
108
+ # Synthesize binds from systemd socket activation
109
+ #
110
+ # When systemd socket activation is enabled, it can be tedious to keep the
111
+ # binds in sync. This method can synthesize any binds based on the received
112
+ # activated sockets. Any existing matching binds will be respected.
113
+ #
114
+ # When only_matching is true in, all binds that do not match an activated
115
+ # socket is removed in place.
116
+ #
117
+ # It's a noop if no activated sockets were received.
118
+ def synthesize_binds_from_activated_fs(binds, only_matching)
119
+ return binds unless activated_sockets.any?
120
+
121
+ activated_binds = []
122
+
123
+ activated_sockets.keys.each do |proto, addr, port|
124
+ if port
125
+ tcp_url = "#{proto}://#{addr}:#{port}"
126
+ ssl_url = "ssl://#{addr}:#{port}"
127
+ ssl_url_prefix = "#{ssl_url}?"
128
+
129
+ existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
130
+
131
+ activated_binds << (existing || tcp_url)
132
+ else
133
+ # TODO: can there be a SSL bind without a port?
134
+ activated_binds << "#{proto}://#{addr}"
135
+ end
136
+ end
137
+
138
+ if only_matching
139
+ activated_binds
140
+ else
141
+ binds | activated_binds
142
+ end
143
+ end
144
+
145
+ def parse(binds, log_writer = nil, log_msg = 'Listening')
146
+ log_writer ||= @log_writer
147
+ binds.each do |str|
148
+ uri = URI.parse str
149
+ case uri.scheme
150
+ when "tcp"
151
+ if fd = @inherited_fds.delete(str)
152
+ io = inherit_tcp_listener uri.host, uri.port, fd
153
+ log_writer.log "* Inherited #{str}"
154
+ elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
155
+ io = inherit_tcp_listener uri.host, uri.port, sock
156
+ log_writer.log "* Activated #{str}"
157
+ else
158
+ ios_len = @ios.length
159
+ params = Util.parse_query uri.query
160
+
161
+ low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
162
+ backlog = params.fetch('backlog', 1024).to_i
163
+
164
+ io = add_tcp_listener uri.host, uri.port, low_latency, backlog
165
+
166
+ @ios[ios_len..-1].each do |i|
167
+ addr = loc_addr_str i
168
+ log_writer.log "* #{log_msg} on http://#{addr}"
169
+ end
170
+ end
171
+
172
+ @listeners << [str, io] if io
173
+ when "unix"
174
+ path = "#{uri.host}#{uri.path}".gsub("%20", " ")
175
+ abstract = false
176
+ if str.start_with? 'unix://@'
177
+ raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
178
+ abstract = true
179
+ path = "@#{path}"
180
+ end
181
+
182
+ if fd = @inherited_fds.delete(str)
183
+ @unix_paths << path unless abstract || File.exist?(path)
184
+ io = inherit_unix_listener path, fd
185
+ log_writer.log "* Inherited #{str}"
186
+ elsif sock = @activated_sockets.delete([ :unix, path ]) ||
187
+ !abstract && @activated_sockets.delete([ :unix, File.realdirpath(path) ])
188
+ @unix_paths << path unless abstract || File.exist?(path)
189
+ io = inherit_unix_listener path, sock
190
+ log_writer.log "* Activated #{str}"
191
+ else
192
+ umask = nil
193
+ mode = nil
194
+ backlog = 1024
195
+
196
+ if uri.query
197
+ params = Util.parse_query uri.query
198
+ if u = params['umask']
199
+ # Use Integer() to respect the 0 prefix as octal
200
+ umask = Integer(u)
201
+ end
202
+
203
+ if u = params['mode']
204
+ mode = Integer('0'+u)
205
+ end
206
+
207
+ if u = params['backlog']
208
+ backlog = Integer(u)
209
+ end
210
+ end
211
+
212
+ @unix_paths << path unless abstract || File.exist?(path)
213
+ io = add_unix_listener path, umask, mode, backlog
214
+ log_writer.log "* #{log_msg} on #{str}"
215
+ end
216
+
217
+ @listeners << [str, io]
218
+ when "ssl"
219
+ cert_key = %w[cert key]
220
+
221
+ raise "Puma compiled without SSL support" unless HAS_SSL
222
+
223
+ params = Util.parse_query uri.query
224
+
225
+ # If key and certs are not defined and localhost gem is required.
226
+ # localhost gem will be used for self signed
227
+ # Load localhost authority if not loaded.
228
+ # Ruby 3 `values_at` accepts an array, earlier do not
229
+ if params.values_at(*cert_key).all? { |v| v.to_s.empty? }
230
+ ctx = localhost_authority && localhost_authority_context
231
+ end
232
+
233
+ ctx ||=
234
+ begin
235
+ # Extract cert_pem and key_pem from options[:store] if present
236
+ cert_key.each do |v|
237
+ if params[v]&.start_with?('store:')
238
+ index = Integer(params.delete(v).split('store:').last)
239
+ params["#{v}_pem"] = @conf.options[:store][index]
240
+ end
241
+ end
242
+ MiniSSL::ContextBuilder.new(params, @log_writer).context
243
+ end
244
+
245
+ if fd = @inherited_fds.delete(str)
246
+ log_writer.log "* Inherited #{str}"
247
+ io = inherit_ssl_listener fd, ctx
248
+ elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
249
+ io = inherit_ssl_listener sock, ctx
250
+ log_writer.log "* Activated #{str}"
251
+ else
252
+ ios_len = @ios.length
253
+ backlog = params.fetch('backlog', 1024).to_i
254
+ low_latency = params['low_latency'] != 'false'
255
+ io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
256
+
257
+ @ios[ios_len..-1].each do |i|
258
+ addr = loc_addr_str i
259
+ log_writer.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
260
+ end
261
+ end
262
+
263
+ @listeners << [str, io] if io
264
+ else
265
+ log_writer.error "Invalid URI: #{str}"
266
+ end
267
+ end
268
+
269
+ # If we inherited fds but didn't use them (because of a
270
+ # configuration change), then be sure to close them.
271
+ @inherited_fds.each do |str, fd|
272
+ log_writer.log "* Closing unused inherited connection: #{str}"
273
+
274
+ begin
275
+ IO.for_fd(fd).close
276
+ rescue SystemCallError
277
+ end
278
+
279
+ # We have to unlink a unix socket path that's not being used
280
+ uri = URI.parse str
281
+ if uri.scheme == "unix"
282
+ path = "#{uri.host}#{uri.path}"
283
+ File.unlink path
284
+ end
285
+ end
286
+
287
+ # Also close any unused activated sockets
288
+ unless @activated_sockets.empty?
289
+ fds = @ios.map(&:to_i)
290
+ @activated_sockets.each do |key, sock|
291
+ next if fds.include? sock.to_i
292
+ log_writer.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
293
+ begin
294
+ sock.close
295
+ rescue SystemCallError
296
+ end
297
+ # We have to unlink a unix socket path that's not being used
298
+ File.unlink key[1] if key.first == :unix
299
+ end
300
+ end
301
+ end
302
+
303
+ def localhost_authority
304
+ @localhost_authority ||= Localhost::Authority.fetch if defined?(Localhost::Authority) && !Puma::IS_JRUBY
305
+ end
306
+
307
+ def localhost_authority_context
308
+ return unless localhost_authority
309
+
310
+ key_path, crt_path = if [:key_path, :certificate_path].all? { |m| localhost_authority.respond_to?(m) }
311
+ [localhost_authority.key_path, localhost_authority.certificate_path]
312
+ else
313
+ local_certificates_path = File.expand_path("~/.localhost")
314
+ [File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
315
+ end
316
+ MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @log_writer).context
317
+ end
318
+
319
+ # Tell the server to listen on host +host+, port +port+.
320
+ # If +optimize_for_latency+ is true (the default) then clients connecting
321
+ # will be optimized for latency over throughput.
322
+ #
323
+ # +backlog+ indicates how many unaccepted connections the kernel should
324
+ # allow to accumulate before returning connection refused.
325
+ #
326
+ def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
327
+ if host == "localhost"
328
+ loopback_addresses.each do |addr|
329
+ add_tcp_listener addr, port, optimize_for_latency, backlog
330
+ end
331
+ return
332
+ end
333
+
334
+ host = host[1..-2] if host&.start_with? '['
335
+ tcp_server = TCPServer.new(host, port)
336
+
337
+ if optimize_for_latency
338
+ tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
339
+ end
340
+ tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
341
+ tcp_server.listen backlog
342
+
343
+ @ios << tcp_server
344
+ tcp_server
345
+ end
346
+
347
+ def inherit_tcp_listener(host, port, fd)
348
+ s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
349
+
350
+ @ios << s
351
+ s
352
+ end
353
+
354
+ def add_ssl_listener(host, port, ctx,
355
+ optimize_for_latency=true, backlog=1024)
356
+
357
+ raise "Puma compiled without SSL support" unless HAS_SSL
358
+ # Puma will try to use local authority context if context is supplied nil
359
+ ctx ||= localhost_authority_context
360
+
361
+ if host == "localhost"
362
+ loopback_addresses.each do |addr|
363
+ add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
364
+ end
365
+ return
366
+ end
367
+
368
+ host = host[1..-2] if host&.start_with? '['
369
+ s = TCPServer.new(host, port)
370
+ if optimize_for_latency
371
+ s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
372
+ end
373
+ s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
374
+ s.listen backlog
375
+
376
+ ssl = MiniSSL::Server.new s, ctx
377
+ env = @proto_env.dup
378
+ env[HTTPS_KEY] = HTTPS
379
+ @envs[ssl] = env
380
+
381
+ @ios << ssl
382
+ s
383
+ end
384
+
385
+ def inherit_ssl_listener(fd, ctx)
386
+ raise "Puma compiled without SSL support" unless HAS_SSL
387
+ # Puma will try to use local authority context if context is supplied nil
388
+ ctx ||= localhost_authority_context
389
+
390
+ s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
391
+
392
+ ssl = MiniSSL::Server.new(s, ctx)
393
+
394
+ env = @proto_env.dup
395
+ env[HTTPS_KEY] = HTTPS
396
+ @envs[ssl] = env
397
+
398
+ @ios << ssl
399
+
400
+ s
401
+ end
402
+
403
+ # Tell the server to listen on +path+ as a UNIX domain socket.
404
+ #
405
+ def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
406
+ # Let anyone connect by default
407
+ umask ||= 0
408
+
409
+ begin
410
+ old_mask = File.umask(umask)
411
+
412
+ if File.exist? path
413
+ begin
414
+ old = UNIXSocket.new path
415
+ rescue SystemCallError, IOError
416
+ File.unlink path
417
+ else
418
+ old.close
419
+ raise "There is already a server bound to: #{path}"
420
+ end
421
+ end
422
+ s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
423
+ s.listen backlog
424
+ @ios << s
425
+ ensure
426
+ File.umask old_mask
427
+ end
428
+
429
+ if mode
430
+ File.chmod mode, path
431
+ end
432
+
433
+ env = @proto_env.dup
434
+ env[REMOTE_ADDR] = "127.0.0.1"
435
+ @envs[s] = env
436
+
437
+ s
438
+ end
439
+
440
+ def inherit_unix_listener(path, fd)
441
+ s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
442
+
443
+ @ios << s
444
+
445
+ env = @proto_env.dup
446
+ env[REMOTE_ADDR] = "127.0.0.1"
447
+ @envs[s] = env
448
+
449
+ s
450
+ end
451
+
452
+ def close_listeners
453
+ @listeners.each do |l, io|
454
+ begin
455
+ io.close unless io.closed?
456
+ uri = URI.parse l
457
+ next unless uri.scheme == 'unix'
458
+ unix_path = "#{uri.host}#{uri.path}"
459
+ File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
460
+ rescue Errno::EBADF
461
+ end
462
+ end
463
+ end
464
+
465
+ def redirects_for_restart
466
+ redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
467
+ redirects[:close_others] = true
468
+ redirects
469
+ end
470
+
471
+ # @version 5.0.0
472
+ def redirects_for_restart_env
473
+ @listeners.each_with_object({}).with_index do |(listen, memo), i|
474
+ memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
475
+ end
476
+ end
477
+
478
+ private
479
+
480
+ # @!attribute [r] loopback_addresses
481
+ def loopback_addresses
482
+ t = Socket.ip_address_list.select do |addrinfo|
483
+ addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
484
+ end
485
+ t.map! { |addrinfo| addrinfo.ip_address }; t.uniq!; t
486
+ end
487
+
488
+ def loc_addr_str(io)
489
+ loc_addr = io.to_io.local_address
490
+ if loc_addr.ipv6?
491
+ "[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
492
+ else
493
+ loc_addr.ip_unpack.join(':')
494
+ end
495
+ end
496
+
497
+ # @version 5.0.0
498
+ def socket_activation_fd(int)
499
+ int + 3 # 3 is the magic number you add to follow the SA protocol
500
+ end
501
+ end
502
+ end