puma 4.3.8 → 5.6.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1543 -521
  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/jungle/README.md +9 -0
  11. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  12. data/{tools → docs}/jungle/rc.d/puma +2 -2
  13. data/docs/kubernetes.md +66 -0
  14. data/docs/nginx.md +1 -1
  15. data/docs/plugins.md +15 -15
  16. data/docs/rails_dev_mode.md +28 -0
  17. data/docs/restart.md +46 -23
  18. data/docs/signals.md +13 -11
  19. data/docs/stats.md +142 -0
  20. data/docs/systemd.md +85 -128
  21. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  22. data/ext/puma_http11/ext_help.h +1 -1
  23. data/ext/puma_http11/extconf.rb +51 -9
  24. data/ext/puma_http11/http11_parser.c +68 -57
  25. data/ext/puma_http11/http11_parser.h +1 -1
  26. data/ext/puma_http11/http11_parser.java.rl +1 -1
  27. data/ext/puma_http11/http11_parser.rl +1 -1
  28. data/ext/puma_http11/http11_parser_common.rl +1 -1
  29. data/ext/puma_http11/mini_ssl.c +295 -124
  30. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  31. data/ext/puma_http11/org/jruby/puma/Http11.java +5 -3
  32. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +51 -51
  33. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +109 -67
  34. data/ext/puma_http11/puma_http11.c +32 -51
  35. data/lib/puma/app/status.rb +50 -36
  36. data/lib/puma/binder.rb +225 -106
  37. data/lib/puma/cli.rb +24 -18
  38. data/lib/puma/client.rb +197 -92
  39. data/lib/puma/cluster/worker.rb +173 -0
  40. data/lib/puma/cluster/worker_handle.rb +94 -0
  41. data/lib/puma/cluster.rb +212 -220
  42. data/lib/puma/commonlogger.rb +2 -2
  43. data/lib/puma/configuration.rb +58 -49
  44. data/lib/puma/const.rb +26 -9
  45. data/lib/puma/control_cli.rb +99 -76
  46. data/lib/puma/detect.rb +29 -2
  47. data/lib/puma/dsl.rb +368 -96
  48. data/lib/puma/error_logger.rb +104 -0
  49. data/lib/puma/events.rb +55 -34
  50. data/lib/puma/io_buffer.rb +9 -2
  51. data/lib/puma/jruby_restart.rb +0 -58
  52. data/lib/puma/json_serialization.rb +96 -0
  53. data/lib/puma/launcher.rb +128 -46
  54. data/lib/puma/minissl/context_builder.rb +14 -9
  55. data/lib/puma/minissl.rb +137 -50
  56. data/lib/puma/null_io.rb +18 -1
  57. data/lib/puma/plugin.rb +3 -12
  58. data/lib/puma/queue_close.rb +26 -0
  59. data/lib/puma/rack/builder.rb +1 -5
  60. data/lib/puma/reactor.rb +85 -369
  61. data/lib/puma/request.rb +489 -0
  62. data/lib/puma/runner.rb +46 -61
  63. data/lib/puma/server.rb +292 -751
  64. data/lib/puma/single.rb +9 -65
  65. data/lib/puma/state_file.rb +48 -8
  66. data/lib/puma/systemd.rb +46 -0
  67. data/lib/puma/thread_pool.rb +125 -57
  68. data/lib/puma/util.rb +32 -4
  69. data/lib/puma.rb +48 -0
  70. data/lib/rack/handler/puma.rb +2 -3
  71. data/lib/rack/version_restriction.rb +15 -0
  72. data/tools/{docker/Dockerfile → Dockerfile} +1 -1
  73. metadata +29 -24
  74. data/docs/tcp_mode.md +0 -96
  75. data/ext/puma_http11/io_buffer.c +0 -155
  76. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  77. data/lib/puma/accept_nonblock.rb +0 -29
  78. data/lib/puma/tcp_logger.rb +0 -41
  79. data/tools/jungle/README.md +0 -19
  80. data/tools/jungle/init.d/README.md +0 -61
  81. data/tools/jungle/init.d/puma +0 -421
  82. data/tools/jungle/init.d/run-puma +0 -18
  83. data/tools/jungle/upstart/README.md +0 -61
  84. data/tools/jungle/upstart/puma-manager.conf +0 -31
  85. data/tools/jungle/upstart/puma.conf +0 -69
  86. /data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
@@ -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,71 @@ 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 'refork'
43
+ @launcher.refork ? 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 'reload-worker-directory'
46
+ @launcher.send(:reload_worker_directory) ? 200 : 404
50
47
 
51
- when /\/gc$/
52
- GC.start
53
- rack_response(200, OK_STATUS)
48
+ when 'gc'
49
+ GC.start ; 200
54
50
 
55
- when /\/gc-stats$/
56
- rack_response(200, GC.stat.to_json)
51
+ when 'gc-stats'
52
+ Puma::JSONSerialization.generate GC.stat
53
+
54
+ when 'stats'
55
+ Puma::JSONSerialization.generate @launcher.stats
56
+
57
+ when 'thread-backtraces'
58
+ backtraces = []
59
+ @launcher.thread_status do |name, backtrace|
60
+ backtraces << { name: name, backtrace: backtrace }
61
+ end
62
+ Puma::JSONSerialization.generate backtraces
63
+
64
+ else
65
+ return rack_response(404, "Unsupported action", 'text/plain')
66
+ end
57
67
 
58
- when /\/stats$/
59
- rack_response(200, @cli.stats)
60
- else
61
- rack_response 404, "Unsupported action", 'text/plain'
68
+ case resp_type
69
+ when String
70
+ rack_response 200, resp_type
71
+ when 200
72
+ rack_response 200, OK_STATUS
73
+ when 404
74
+ str = env['PATH_INFO'][/\/(\S+)/, 1].tr '-', '_'
75
+ rack_response 404, "{ \"error\": \"#{str} not available\" }"
62
76
  end
63
77
  end
64
78