puma 5.0.2-java → 5.2.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +667 -567
  3. data/README.md +51 -21
  4. data/bin/puma-wild +3 -9
  5. data/docs/compile_options.md +19 -0
  6. data/docs/deployment.md +6 -7
  7. data/docs/fork_worker.md +2 -0
  8. data/docs/jungle/README.md +0 -4
  9. data/docs/jungle/rc.d/puma +2 -2
  10. data/docs/kubernetes.md +66 -0
  11. data/docs/nginx.md +1 -1
  12. data/docs/plugins.md +1 -1
  13. data/docs/restart.md +46 -23
  14. data/docs/stats.md +142 -0
  15. data/docs/systemd.md +25 -3
  16. data/ext/puma_http11/ext_help.h +1 -1
  17. data/ext/puma_http11/extconf.rb +18 -5
  18. data/ext/puma_http11/http11_parser.c +45 -47
  19. data/ext/puma_http11/http11_parser.java.rl +1 -1
  20. data/ext/puma_http11/http11_parser.rl +1 -1
  21. data/ext/puma_http11/mini_ssl.c +199 -119
  22. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
  23. data/ext/puma_http11/puma_http11.c +25 -12
  24. data/lib/puma.rb +2 -2
  25. data/lib/puma/app/status.rb +44 -46
  26. data/lib/puma/binder.rb +69 -25
  27. data/lib/puma/cli.rb +4 -0
  28. data/lib/puma/client.rb +26 -79
  29. data/lib/puma/cluster.rb +37 -202
  30. data/lib/puma/cluster/worker.rb +176 -0
  31. data/lib/puma/cluster/worker_handle.rb +86 -0
  32. data/lib/puma/configuration.rb +21 -8
  33. data/lib/puma/const.rb +11 -3
  34. data/lib/puma/control_cli.rb +73 -70
  35. data/lib/puma/dsl.rb +100 -22
  36. data/lib/puma/error_logger.rb +10 -3
  37. data/lib/puma/events.rb +18 -3
  38. data/lib/puma/json.rb +96 -0
  39. data/lib/puma/launcher.rb +57 -15
  40. data/lib/puma/minissl.rb +47 -16
  41. data/lib/puma/minissl/context_builder.rb +6 -0
  42. data/lib/puma/null_io.rb +4 -0
  43. data/lib/puma/puma_http11.jar +0 -0
  44. data/lib/puma/queue_close.rb +26 -0
  45. data/lib/puma/reactor.rb +85 -363
  46. data/lib/puma/request.rb +451 -0
  47. data/lib/puma/runner.rb +17 -23
  48. data/lib/puma/server.rb +164 -553
  49. data/lib/puma/single.rb +2 -2
  50. data/lib/puma/state_file.rb +5 -3
  51. data/lib/puma/systemd.rb +46 -0
  52. data/lib/puma/util.rb +11 -0
  53. metadata +11 -6
  54. data/docs/jungle/upstart/README.md +0 -61
  55. data/docs/jungle/upstart/puma-manager.conf +0 -31
  56. data/docs/jungle/upstart/puma.conf +0 -69
  57. data/lib/puma/accept_nonblock.rb +0 -29
@@ -182,8 +182,6 @@ static final int puma_parser_start = 1;
182
182
  static final int puma_parser_first_final = 46;
183
183
  static final int puma_parser_error = 0;
184
184
 
185
- static final int puma_parser_en_main = 1;
186
-
187
185
 
188
186
  // line 62 "ext/puma_http11/http11_parser.java.rl"
189
187
 
@@ -212,12 +210,12 @@ static final int puma_parser_en_main = 1;
212
210
  cs = 0;
213
211
 
214
212
 
215
- // line 218 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
213
+ // line 214 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
216
214
  {
217
215
  cs = puma_parser_start;
218
216
  }
219
217
 
220
- // line 90 "ext/puma_http11/http11_parser.java.rl"
218
+ // line 88 "ext/puma_http11/http11_parser.java.rl"
221
219
 
222
220
  body_start = 0;
223
221
  content_len = 0;
@@ -244,7 +242,7 @@ static final int puma_parser_en_main = 1;
244
242
  parser.buffer = buffer;
245
243
 
246
244
 
247
- // line 250 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
245
+ // line 246 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
248
246
  {
249
247
  int _klen;
250
248
  int _trans = 0;
@@ -400,7 +398,7 @@ case 1:
400
398
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
401
399
  }
402
400
  break;
403
- // line 406 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
401
+ // line 402 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
404
402
  }
405
403
  }
406
404
  }
@@ -420,7 +418,7 @@ case 5:
420
418
  break; }
421
419
  }
422
420
 
423
- // line 116 "ext/puma_http11/http11_parser.java.rl"
421
+ // line 114 "ext/puma_http11/http11_parser.java.rl"
424
422
 
425
423
  parser.cs = cs;
426
424
  parser.nread += (p - off);
@@ -40,7 +40,9 @@ static VALUE global_http_version;
40
40
  static VALUE global_request_path;
41
41
 
42
42
  /** Defines common length and error messages for input length validation. */
43
- #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " # length " allowed length (was %d)"
43
+ #define QUOTE(s) #s
44
+ #define EXPLAIN_MAX_LENGTH_VALUE(s) QUOTE(s)
45
+ #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " EXPLAIN_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
44
46
 
45
47
  /** Validates the max length of given input and throws an HttpParserError exception if over. */
46
48
  #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
@@ -50,12 +52,16 @@ static VALUE global_request_path;
50
52
 
51
53
 
52
54
  /* Defines the maximum allowed lengths for various input elements.*/
55
+ #ifndef PUMA_QUERY_STRING_MAX_LENGTH
56
+ #define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
57
+ #endif
58
+
53
59
  DEF_MAX_LENGTH(FIELD_NAME, 256);
54
60
  DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
55
61
  DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
56
62
  DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
57
63
  DEF_MAX_LENGTH(REQUEST_PATH, 8192);
58
- DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
64
+ DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
59
65
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
60
66
 
61
67
  struct common_field {
@@ -253,11 +259,18 @@ void HttpParser_free(void *data) {
253
259
  }
254
260
  }
255
261
 
256
- void HttpParser_mark(puma_parser* hp) {
262
+ void HttpParser_mark(void *ptr) {
263
+ puma_parser *hp = ptr;
257
264
  if(hp->request) rb_gc_mark(hp->request);
258
265
  if(hp->body) rb_gc_mark(hp->body);
259
266
  }
260
267
 
268
+ const rb_data_type_t HttpParser_data_type = {
269
+ "HttpParser",
270
+ { HttpParser_mark, HttpParser_free, 0 },
271
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
272
+ };
273
+
261
274
  VALUE HttpParser_alloc(VALUE klass)
262
275
  {
263
276
  puma_parser *hp = ALLOC_N(puma_parser, 1);
@@ -274,7 +287,7 @@ VALUE HttpParser_alloc(VALUE klass)
274
287
 
275
288
  puma_parser_init(hp);
276
289
 
277
- return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
290
+ return TypedData_Wrap_Struct(klass, &HttpParser_data_type, hp);
278
291
  }
279
292
 
280
293
  /**
@@ -286,7 +299,7 @@ VALUE HttpParser_alloc(VALUE klass)
286
299
  VALUE HttpParser_init(VALUE self)
287
300
  {
288
301
  puma_parser *http = NULL;
289
- DATA_GET(self, puma_parser, http);
302
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
290
303
  puma_parser_init(http);
291
304
 
292
305
  return self;
@@ -303,7 +316,7 @@ VALUE HttpParser_init(VALUE self)
303
316
  VALUE HttpParser_reset(VALUE self)
304
317
  {
305
318
  puma_parser *http = NULL;
306
- DATA_GET(self, puma_parser, http);
319
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
307
320
  puma_parser_init(http);
308
321
 
309
322
  return Qnil;
@@ -320,7 +333,7 @@ VALUE HttpParser_reset(VALUE self)
320
333
  VALUE HttpParser_finish(VALUE self)
321
334
  {
322
335
  puma_parser *http = NULL;
323
- DATA_GET(self, puma_parser, http);
336
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
324
337
  puma_parser_finish(http);
325
338
 
326
339
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
@@ -351,7 +364,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
351
364
  char *dptr = NULL;
352
365
  long dlen = 0;
353
366
 
354
- DATA_GET(self, puma_parser, http);
367
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
355
368
 
356
369
  from = FIX2INT(start);
357
370
  dptr = rb_extract_chars(data, &dlen);
@@ -385,7 +398,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
385
398
  VALUE HttpParser_has_error(VALUE self)
386
399
  {
387
400
  puma_parser *http = NULL;
388
- DATA_GET(self, puma_parser, http);
401
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
389
402
 
390
403
  return puma_parser_has_error(http) ? Qtrue : Qfalse;
391
404
  }
@@ -400,7 +413,7 @@ VALUE HttpParser_has_error(VALUE self)
400
413
  VALUE HttpParser_is_finished(VALUE self)
401
414
  {
402
415
  puma_parser *http = NULL;
403
- DATA_GET(self, puma_parser, http);
416
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
404
417
 
405
418
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
406
419
  }
@@ -416,7 +429,7 @@ VALUE HttpParser_is_finished(VALUE self)
416
429
  VALUE HttpParser_nread(VALUE self)
417
430
  {
418
431
  puma_parser *http = NULL;
419
- DATA_GET(self, puma_parser, http);
432
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
420
433
 
421
434
  return INT2FIX(http->nread);
422
435
  }
@@ -429,7 +442,7 @@ VALUE HttpParser_nread(VALUE self)
429
442
  */
430
443
  VALUE HttpParser_body(VALUE self) {
431
444
  puma_parser *http = NULL;
432
- DATA_GET(self, puma_parser, http);
445
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
433
446
 
434
447
  return http->body;
435
448
  }
@@ -12,6 +12,7 @@ require 'thread'
12
12
 
13
13
  require 'puma/puma_http11'
14
14
  require 'puma/detect'
15
+ require 'puma/json'
15
16
 
16
17
  module Puma
17
18
  autoload :Const, 'puma/const'
@@ -25,8 +26,7 @@ module Puma
25
26
 
26
27
  # @!attribute [rw] stats_object
27
28
  def self.stats
28
- require 'json'
29
- @get_stats.stats.to_json
29
+ Puma::JSON.generate @get_stats.stats
30
30
  end
31
31
 
32
32
  # @!attribute [r] stats_hash
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'puma/json'
2
3
 
3
4
  module Puma
4
5
  module App
@@ -7,71 +8,68 @@ module Puma
7
8
  class Status
8
9
  OK_STATUS = '{ "status": "ok" }'.freeze
9
10
 
10
- def initialize(cli, token = nil)
11
- @cli = cli
11
+ # @param launcher [::Puma::Launcher]
12
+ # @param token [String, nil] the token used for authentication
13
+ #
14
+ def initialize(launcher, token = nil)
15
+ @launcher = launcher
12
16
  @auth_token = token
13
17
  end
14
18
 
19
+ # most commands call methods in `::Puma::Launcher` based on command in
20
+ # `env['PATH_INFO']`
15
21
  def call(env)
16
22
  unless authenticate(env)
17
23
  return rack_response(403, 'Invalid auth token', 'text/plain')
18
24
  end
19
25
 
20
- if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
21
- require 'json'
22
- end
26
+ # resp_type is processed by following case statement, return
27
+ # is a number (status) or a string used as the body of a 200 response
28
+ resp_type =
29
+ case env['PATH_INFO'][/\/([^\/]+)$/, 1]
30
+ when 'stop'
31
+ @launcher.stop ; 200
23
32
 
24
- case env['PATH_INFO']
25
- when /\/stop$/
26
- @cli.stop
27
- rack_response(200, OK_STATUS)
33
+ when 'halt'
34
+ @launcher.halt ; 200
28
35
 
29
- when /\/halt$/
30
- @cli.halt
31
- rack_response(200, OK_STATUS)
36
+ when 'restart'
37
+ @launcher.restart ; 200
32
38
 
33
- when /\/restart$/
34
- @cli.restart
35
- rack_response(200, OK_STATUS)
39
+ when 'phased-restart'
40
+ @launcher.phased_restart ? 200 : 404
36
41
 
37
- when /\/phased-restart$/
38
- if !@cli.phased_restart
39
- rack_response(404, '{ "error": "phased restart not available" }')
40
- else
41
- rack_response(200, OK_STATUS)
42
- end
42
+ when 'reload-worker-directory'
43
+ @launcher.send(:reload_worker_directory) ? 200 : 404
43
44
 
44
- when /\/reload-worker-directory$/
45
- if !@cli.send(:reload_worker_directory)
46
- rack_response(404, '{ "error": "reload_worker_directory not available" }')
47
- else
48
- rack_response(200, OK_STATUS)
49
- end
45
+ when 'gc'
46
+ GC.start ; 200
50
47
 
51
- when /\/gc$/
52
- GC.start
53
- rack_response(200, OK_STATUS)
48
+ when 'gc-stats'
49
+ Puma::JSON.generate GC.stat
54
50
 
55
- when /\/gc-stats$/
56
- rack_response(200, GC.stat.to_json)
51
+ when 'stats'
52
+ Puma::JSON.generate @launcher.stats
57
53
 
58
- when /\/stats$/
59
- rack_response(200, @cli.stats.to_json)
54
+ when 'thread-backtraces'
55
+ backtraces = []
56
+ @launcher.thread_status do |name, backtrace|
57
+ backtraces << { name: name, backtrace: backtrace }
58
+ end
59
+ Puma::JSON.generate backtraces
60
60
 
61
- when /\/thread-backtraces$/
62
- backtraces = []
63
- @cli.thread_status do |name, backtrace|
64
- backtraces << { name: name, backtrace: backtrace }
61
+ else
62
+ return rack_response(404, "Unsupported action", 'text/plain')
65
63
  end
66
64
 
67
- rack_response(200, backtraces.to_json)
68
-
69
- when /\/refork$/
70
- Process.kill "SIGURG", $$
71
- rack_response(200, OK_STATUS)
72
-
73
- else
74
- rack_response 404, "Unsupported action", 'text/plain'
65
+ case resp_type
66
+ when String
67
+ rack_response 200, resp_type
68
+ when 200
69
+ rack_response 200, OK_STATUS
70
+ when 404
71
+ str = env['PATH_INFO'][/\/(\S+)/, 1].tr '-', '_'
72
+ rack_response 404, "{ \"error\": \"#{str} not available\" }"
75
73
  end
76
74
  end
77
75
 
@@ -12,7 +12,15 @@ module Puma
12
12
  if HAS_SSL
13
13
  require 'puma/minissl'
14
14
  require 'puma/minissl/context_builder'
15
- require 'puma/accept_nonblock'
15
+
16
+ # Odd bug in 'pure Ruby' nio4r verion 2.5.2, which installs with Ruby 2.3.
17
+ # NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
18
+ # The bug was that it did not require openssl.
19
+ # @todo remove when Ruby 2.3 support is dropped
20
+ #
21
+ if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
22
+ require 'openssl'
23
+ end
16
24
  end
17
25
 
18
26
  class Binder
@@ -103,6 +111,43 @@ module Puma
103
111
  ["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
104
112
  end
105
113
 
114
+ # Synthesize binds from systemd socket activation
115
+ #
116
+ # When systemd socket activation is enabled, it can be tedious to keep the
117
+ # binds in sync. This method can synthesize any binds based on the received
118
+ # activated sockets. Any existing matching binds will be respected.
119
+ #
120
+ # When only_matching is true in, all binds that do not match an activated
121
+ # socket is removed in place.
122
+ #
123
+ # It's a noop if no activated sockets were received.
124
+ def synthesize_binds_from_activated_fs(binds, only_matching)
125
+ return binds unless activated_sockets.any?
126
+
127
+ activated_binds = []
128
+
129
+ activated_sockets.keys.each do |proto, addr, port|
130
+ if port
131
+ tcp_url = "#{proto}://#{addr}:#{port}"
132
+ ssl_url = "ssl://#{addr}:#{port}"
133
+ ssl_url_prefix = "#{ssl_url}?"
134
+
135
+ existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
136
+
137
+ activated_binds << (existing || tcp_url)
138
+ else
139
+ # TODO: can there be a SSL bind without a port?
140
+ activated_binds << "#{proto}://#{addr}"
141
+ end
142
+ end
143
+
144
+ if only_matching
145
+ activated_binds
146
+ else
147
+ binds | activated_binds
148
+ end
149
+ end
150
+
106
151
  def parse(binds, logger, log_msg = 'Listening')
107
152
  binds.each do |str|
108
153
  uri = URI.parse str
@@ -115,6 +160,7 @@ module Puma
115
160
  io = inherit_tcp_listener uri.host, uri.port, sock
116
161
  logger.log "* Activated #{str}"
117
162
  else
163
+ ios_len = @ios.length
118
164
  params = Util.parse_query uri.query
119
165
 
120
166
  opt = params.key?('low_latency')
@@ -122,14 +168,8 @@ module Puma
122
168
 
123
169
  io = add_tcp_listener uri.host, uri.port, opt, bak
124
170
 
125
- @ios.each do |i|
126
- next unless TCPServer === i
127
- addr = if i.local_address.ipv6?
128
- "[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
129
- else
130
- i.local_address.ip_unpack.join(':')
131
- end
132
-
171
+ @ios[ios_len..-1].each do |i|
172
+ addr = loc_addr_str i
133
173
  logger.log "* #{log_msg} on http://#{addr}"
134
174
  end
135
175
  end
@@ -184,8 +224,13 @@ module Puma
184
224
  io = inherit_ssl_listener sock, ctx
185
225
  logger.log "* Activated #{str}"
186
226
  else
227
+ ios_len = @ios.length
187
228
  io = add_ssl_listener uri.host, uri.port, ctx
188
- logger.log "* Listening on #{str}"
229
+
230
+ @ios[ios_len..-1].each do |i|
231
+ addr = loc_addr_str i
232
+ logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
233
+ end
189
234
  end
190
235
 
191
236
  @listeners << [str, io] if io
@@ -252,11 +297,7 @@ module Puma
252
297
  end
253
298
 
254
299
  def inherit_tcp_listener(host, port, fd)
255
- if fd.kind_of? TCPServer
256
- s = fd
257
- else
258
- s = TCPServer.for_fd(fd)
259
- end
300
+ s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
260
301
 
261
302
  @ios << s
262
303
  s
@@ -294,11 +335,8 @@ module Puma
294
335
  def inherit_ssl_listener(fd, ctx)
295
336
  raise "Puma compiled without SSL support" unless HAS_SSL
296
337
 
297
- if fd.kind_of? TCPServer
298
- s = fd
299
- else
300
- s = TCPServer.for_fd(fd)
301
- end
338
+ s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
339
+
302
340
  ssl = MiniSSL::Server.new(s, ctx)
303
341
 
304
342
  env = @proto_env.dup
@@ -353,11 +391,8 @@ module Puma
353
391
  def inherit_unix_listener(path, fd)
354
392
  @unix_paths << path unless File.exist? path
355
393
 
356
- if fd.kind_of? TCPServer
357
- s = fd
358
- else
359
- s = UNIXServer.for_fd fd
360
- end
394
+ s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
395
+
361
396
  @ios << s
362
397
 
363
398
  env = @proto_env.dup
@@ -399,6 +434,15 @@ module Puma
399
434
  end.map { |addrinfo| addrinfo.ip_address }.uniq
400
435
  end
401
436
 
437
+ def loc_addr_str(io)
438
+ loc_addr = io.to_io.local_address
439
+ if loc_addr.ipv6?
440
+ "[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
441
+ else
442
+ loc_addr.ip_unpack.join(':')
443
+ end
444
+ end
445
+
402
446
  # @version 5.0.0
403
447
  def socket_activation_fd(int)
404
448
  int + 3 # 3 is the magic number you add to follow the SA protocol