puma 4.3.6 → 5.6.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1486 -518
  3. data/LICENSE +23 -20
  4. data/README.md +120 -36
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +63 -26
  7. data/docs/compile_options.md +21 -0
  8. data/docs/deployment.md +60 -69
  9. data/docs/fork_worker.md +33 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +9 -0
  14. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  15. data/{tools → docs}/jungle/rc.d/puma +2 -2
  16. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  17. data/docs/kubernetes.md +66 -0
  18. data/docs/nginx.md +1 -1
  19. data/docs/plugins.md +15 -15
  20. data/docs/rails_dev_mode.md +28 -0
  21. data/docs/restart.md +46 -23
  22. data/docs/signals.md +13 -11
  23. data/docs/stats.md +142 -0
  24. data/docs/systemd.md +85 -128
  25. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  26. data/ext/puma_http11/ext_help.h +1 -1
  27. data/ext/puma_http11/extconf.rb +46 -9
  28. data/ext/puma_http11/http11_parser.c +68 -57
  29. data/ext/puma_http11/http11_parser.h +1 -1
  30. data/ext/puma_http11/http11_parser.java.rl +1 -1
  31. data/ext/puma_http11/http11_parser.rl +1 -1
  32. data/ext/puma_http11/http11_parser_common.rl +1 -1
  33. data/ext/puma_http11/mini_ssl.c +275 -122
  34. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  35. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  36. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +51 -51
  37. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +105 -61
  38. data/ext/puma_http11/puma_http11.c +32 -51
  39. data/lib/puma/app/status.rb +47 -36
  40. data/lib/puma/binder.rb +225 -106
  41. data/lib/puma/cli.rb +24 -18
  42. data/lib/puma/client.rb +174 -91
  43. data/lib/puma/cluster/worker.rb +173 -0
  44. data/lib/puma/cluster/worker_handle.rb +94 -0
  45. data/lib/puma/cluster.rb +212 -220
  46. data/lib/puma/commonlogger.rb +2 -2
  47. data/lib/puma/configuration.rb +58 -49
  48. data/lib/puma/const.rb +18 -9
  49. data/lib/puma/control_cli.rb +93 -76
  50. data/lib/puma/detect.rb +29 -2
  51. data/lib/puma/dsl.rb +364 -96
  52. data/lib/puma/error_logger.rb +104 -0
  53. data/lib/puma/events.rb +55 -34
  54. data/lib/puma/io_buffer.rb +9 -2
  55. data/lib/puma/jruby_restart.rb +0 -58
  56. data/lib/puma/json_serialization.rb +96 -0
  57. data/lib/puma/launcher.rb +117 -46
  58. data/lib/puma/minissl/context_builder.rb +14 -9
  59. data/lib/puma/minissl.rb +128 -46
  60. data/lib/puma/null_io.rb +13 -1
  61. data/lib/puma/plugin/tmp_restart.rb +0 -0
  62. data/lib/puma/plugin.rb +3 -12
  63. data/lib/puma/queue_close.rb +26 -0
  64. data/lib/puma/rack/builder.rb +1 -5
  65. data/lib/puma/rack/urlmap.rb +0 -0
  66. data/lib/puma/rack_default.rb +0 -0
  67. data/lib/puma/reactor.rb +85 -369
  68. data/lib/puma/request.rb +472 -0
  69. data/lib/puma/runner.rb +46 -61
  70. data/lib/puma/server.rb +287 -743
  71. data/lib/puma/single.rb +9 -65
  72. data/lib/puma/state_file.rb +47 -8
  73. data/lib/puma/systemd.rb +46 -0
  74. data/lib/puma/thread_pool.rb +125 -57
  75. data/lib/puma/util.rb +20 -1
  76. data/lib/puma.rb +46 -0
  77. data/lib/rack/handler/puma.rb +2 -3
  78. data/tools/{docker/Dockerfile → Dockerfile} +1 -1
  79. data/tools/trickletest.rb +0 -0
  80. metadata +28 -24
  81. data/docs/tcp_mode.md +0 -96
  82. data/ext/puma_http11/io_buffer.c +0 -155
  83. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  84. data/lib/puma/accept_nonblock.rb +0 -29
  85. data/lib/puma/tcp_logger.rb +0 -41
  86. data/tools/jungle/README.md +0 -19
  87. data/tools/jungle/init.d/README.md +0 -61
  88. data/tools/jungle/init.d/puma +0 -421
  89. data/tools/jungle/init.d/run-puma +0 -18
  90. data/tools/jungle/upstart/README.md +0 -61
  91. data/tools/jungle/upstart/puma-manager.conf +0 -31
  92. data/tools/jungle/upstart/puma.conf +0 -69
@@ -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
- DEF_MAX_LENGTH(REQUEST_PATH, 2048);
58
- DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
63
+ DEF_MAX_LENGTH(REQUEST_PATH, 8192);
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 {
@@ -112,21 +118,6 @@ static struct common_field common_http_fields[] = {
112
118
  # undef f
113
119
  };
114
120
 
115
- /*
116
- * qsort(3) and bsearch(3) improve average performance slightly, but may
117
- * not be worth it for lack of portability to certain platforms...
118
- */
119
- #if defined(HAVE_QSORT_BSEARCH)
120
- /* sort by length, then by name if there's a tie */
121
- static int common_field_cmp(const void *a, const void *b)
122
- {
123
- struct common_field *cfa = (struct common_field *)a;
124
- struct common_field *cfb = (struct common_field *)b;
125
- signed long diff = cfa->len - cfb->len;
126
- return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
127
- }
128
- #endif /* HAVE_QSORT_BSEARCH */
129
-
130
121
  static void init_common_fields(void)
131
122
  {
132
123
  unsigned i;
@@ -143,28 +134,10 @@ static void init_common_fields(void)
143
134
  }
144
135
  rb_global_variable(&cf->value);
145
136
  }
146
-
147
- #if defined(HAVE_QSORT_BSEARCH)
148
- qsort(common_http_fields,
149
- ARRAY_SIZE(common_http_fields),
150
- sizeof(struct common_field),
151
- common_field_cmp);
152
- #endif /* HAVE_QSORT_BSEARCH */
153
137
  }
154
138
 
155
139
  static VALUE find_common_field_value(const char *field, size_t flen)
156
140
  {
157
- #if defined(HAVE_QSORT_BSEARCH)
158
- struct common_field key;
159
- struct common_field *found;
160
- key.name = field;
161
- key.len = (signed long)flen;
162
- found = (struct common_field *)bsearch(&key, common_http_fields,
163
- ARRAY_SIZE(common_http_fields),
164
- sizeof(struct common_field),
165
- common_field_cmp);
166
- return found ? found->value : Qnil;
167
- #else /* !HAVE_QSORT_BSEARCH */
168
141
  unsigned i;
169
142
  struct common_field *cf = common_http_fields;
170
143
  for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
@@ -172,7 +145,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
172
145
  return cf->value;
173
146
  }
174
147
  return Qnil;
175
- #endif /* !HAVE_QSORT_BSEARCH */
176
148
  }
177
149
 
178
150
  void http_field(puma_parser* hp, const char *field, size_t flen,
@@ -287,11 +259,18 @@ void HttpParser_free(void *data) {
287
259
  }
288
260
  }
289
261
 
290
- void HttpParser_mark(puma_parser* hp) {
262
+ void HttpParser_mark(void *ptr) {
263
+ puma_parser *hp = ptr;
291
264
  if(hp->request) rb_gc_mark(hp->request);
292
265
  if(hp->body) rb_gc_mark(hp->body);
293
266
  }
294
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
+
295
274
  VALUE HttpParser_alloc(VALUE klass)
296
275
  {
297
276
  puma_parser *hp = ALLOC_N(puma_parser, 1);
@@ -308,7 +287,7 @@ VALUE HttpParser_alloc(VALUE klass)
308
287
 
309
288
  puma_parser_init(hp);
310
289
 
311
- return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
290
+ return TypedData_Wrap_Struct(klass, &HttpParser_data_type, hp);
312
291
  }
313
292
 
314
293
  /**
@@ -320,7 +299,7 @@ VALUE HttpParser_alloc(VALUE klass)
320
299
  VALUE HttpParser_init(VALUE self)
321
300
  {
322
301
  puma_parser *http = NULL;
323
- DATA_GET(self, puma_parser, http);
302
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
324
303
  puma_parser_init(http);
325
304
 
326
305
  return self;
@@ -337,7 +316,7 @@ VALUE HttpParser_init(VALUE self)
337
316
  VALUE HttpParser_reset(VALUE self)
338
317
  {
339
318
  puma_parser *http = NULL;
340
- DATA_GET(self, puma_parser, http);
319
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
341
320
  puma_parser_init(http);
342
321
 
343
322
  return Qnil;
@@ -354,7 +333,7 @@ VALUE HttpParser_reset(VALUE self)
354
333
  VALUE HttpParser_finish(VALUE self)
355
334
  {
356
335
  puma_parser *http = NULL;
357
- DATA_GET(self, puma_parser, http);
336
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
358
337
  puma_parser_finish(http);
359
338
 
360
339
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
@@ -385,7 +364,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
385
364
  char *dptr = NULL;
386
365
  long dlen = 0;
387
366
 
388
- DATA_GET(self, puma_parser, http);
367
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
389
368
 
390
369
  from = FIX2INT(start);
391
370
  dptr = rb_extract_chars(data, &dlen);
@@ -401,7 +380,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
401
380
  VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
402
381
 
403
382
  if(puma_parser_has_error(http)) {
404
- rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
383
+ rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
405
384
  } else {
406
385
  return INT2FIX(puma_parser_nread(http));
407
386
  }
@@ -419,7 +398,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
419
398
  VALUE HttpParser_has_error(VALUE self)
420
399
  {
421
400
  puma_parser *http = NULL;
422
- DATA_GET(self, puma_parser, http);
401
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
423
402
 
424
403
  return puma_parser_has_error(http) ? Qtrue : Qfalse;
425
404
  }
@@ -434,7 +413,7 @@ VALUE HttpParser_has_error(VALUE self)
434
413
  VALUE HttpParser_is_finished(VALUE self)
435
414
  {
436
415
  puma_parser *http = NULL;
437
- DATA_GET(self, puma_parser, http);
416
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
438
417
 
439
418
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
440
419
  }
@@ -450,7 +429,7 @@ VALUE HttpParser_is_finished(VALUE self)
450
429
  VALUE HttpParser_nread(VALUE self)
451
430
  {
452
431
  puma_parser *http = NULL;
453
- DATA_GET(self, puma_parser, http);
432
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
454
433
 
455
434
  return INT2FIX(http->nread);
456
435
  }
@@ -463,15 +442,16 @@ VALUE HttpParser_nread(VALUE self)
463
442
  */
464
443
  VALUE HttpParser_body(VALUE self) {
465
444
  puma_parser *http = NULL;
466
- DATA_GET(self, puma_parser, http);
445
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
467
446
 
468
447
  return http->body;
469
448
  }
470
449
 
471
- void Init_io_buffer(VALUE puma);
450
+ #ifdef HAVE_OPENSSL_BIO_H
472
451
  void Init_mini_ssl(VALUE mod);
452
+ #endif
473
453
 
474
- void Init_puma_http11()
454
+ void Init_puma_http11(void)
475
455
  {
476
456
 
477
457
  VALUE mPuma = rb_define_module("Puma");
@@ -498,6 +478,7 @@ void Init_puma_http11()
498
478
  rb_define_method(cHttpParser, "body", HttpParser_body, 0);
499
479
  init_common_fields();
500
480
 
501
- Init_io_buffer(mPuma);
481
+ #ifdef HAVE_OPENSSL_BIO_H
502
482
  Init_mini_ssl(mPuma);
483
+ #endif
503
484
  }
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'puma/json_serialization'
2
3
 
3
4
  module Puma
4
5
  module App
@@ -7,58 +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::JSONSerialization.generate GC.stat
54
50
 
55
- when /\/gc-stats$/
56
- rack_response(200, GC.stat.to_json)
51
+ when 'stats'
52
+ Puma::JSONSerialization.generate @launcher.stats
53
+
54
+ when 'thread-backtraces'
55
+ backtraces = []
56
+ @launcher.thread_status do |name, backtrace|
57
+ backtraces << { name: name, backtrace: backtrace }
58
+ end
59
+ Puma::JSONSerialization.generate backtraces
60
+
61
+ else
62
+ return rack_response(404, "Unsupported action", 'text/plain')
63
+ end
57
64
 
58
- when /\/stats$/
59
- rack_response(200, @cli.stats)
60
- else
61
- 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\" }"
62
73
  end
63
74
  end
64
75