puma 4.3.12 → 6.3.1

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 (96) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1729 -521
  3. data/LICENSE +23 -20
  4. data/README.md +169 -45
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +63 -26
  7. data/docs/compile_options.md +55 -0
  8. data/docs/deployment.md +60 -69
  9. data/docs/fork_worker.md +31 -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 +2 -2
  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 +84 -128
  25. data/docs/testing_benchmarks_local_files.md +150 -0
  26. data/docs/testing_test_rackup_ci_files.md +36 -0
  27. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  28. data/ext/puma_http11/ext_help.h +1 -1
  29. data/ext/puma_http11/extconf.rb +49 -12
  30. data/ext/puma_http11/http11_parser.c +46 -48
  31. data/ext/puma_http11/http11_parser.h +2 -2
  32. data/ext/puma_http11/http11_parser.java.rl +3 -3
  33. data/ext/puma_http11/http11_parser.rl +3 -3
  34. data/ext/puma_http11/http11_parser_common.rl +2 -2
  35. data/ext/puma_http11/mini_ssl.c +278 -93
  36. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  37. data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
  38. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +4 -6
  39. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
  40. data/ext/puma_http11/puma_http11.c +46 -57
  41. data/lib/puma/app/status.rb +53 -39
  42. data/lib/puma/binder.rb +237 -121
  43. data/lib/puma/cli.rb +34 -34
  44. data/lib/puma/client.rb +172 -98
  45. data/lib/puma/cluster/worker.rb +180 -0
  46. data/lib/puma/cluster/worker_handle.rb +97 -0
  47. data/lib/puma/cluster.rb +226 -231
  48. data/lib/puma/commonlogger.rb +21 -14
  49. data/lib/puma/configuration.rb +114 -87
  50. data/lib/puma/const.rb +139 -95
  51. data/lib/puma/control_cli.rb +99 -79
  52. data/lib/puma/detect.rb +33 -2
  53. data/lib/puma/dsl.rb +516 -110
  54. data/lib/puma/error_logger.rb +113 -0
  55. data/lib/puma/events.rb +16 -115
  56. data/lib/puma/io_buffer.rb +44 -2
  57. data/lib/puma/jruby_restart.rb +2 -59
  58. data/lib/puma/json_serialization.rb +96 -0
  59. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  60. data/lib/puma/launcher.rb +164 -155
  61. data/lib/puma/log_writer.rb +147 -0
  62. data/lib/puma/minissl/context_builder.rb +36 -19
  63. data/lib/puma/minissl.rb +230 -55
  64. data/lib/puma/null_io.rb +18 -1
  65. data/lib/puma/plugin/systemd.rb +90 -0
  66. data/lib/puma/plugin/tmp_restart.rb +1 -1
  67. data/lib/puma/plugin.rb +3 -12
  68. data/lib/puma/rack/builder.rb +7 -11
  69. data/lib/puma/rack/urlmap.rb +0 -0
  70. data/lib/puma/rack_default.rb +19 -4
  71. data/lib/puma/reactor.rb +93 -368
  72. data/lib/puma/request.rb +671 -0
  73. data/lib/puma/runner.rb +92 -75
  74. data/lib/puma/sd_notify.rb +149 -0
  75. data/lib/puma/server.rb +321 -794
  76. data/lib/puma/single.rb +20 -74
  77. data/lib/puma/state_file.rb +45 -8
  78. data/lib/puma/thread_pool.rb +140 -68
  79. data/lib/puma/util.rb +21 -4
  80. data/lib/puma.rb +54 -7
  81. data/lib/rack/handler/puma.rb +113 -87
  82. data/tools/{docker/Dockerfile → Dockerfile} +1 -1
  83. data/tools/trickletest.rb +0 -0
  84. metadata +33 -24
  85. data/docs/tcp_mode.md +0 -96
  86. data/ext/puma_http11/io_buffer.c +0 -155
  87. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  88. data/lib/puma/accept_nonblock.rb +0 -29
  89. data/lib/puma/tcp_logger.rb +0 -41
  90. data/tools/jungle/README.md +0 -19
  91. data/tools/jungle/init.d/README.md +0 -61
  92. data/tools/jungle/init.d/puma +0 -421
  93. data/tools/jungle/init.d/run-puma +0 -18
  94. data/tools/jungle/upstart/README.md +0 -61
  95. data/tools/jungle/upstart/puma-manager.conf +0 -31
  96. data/tools/jungle/upstart/puma.conf +0 -69
@@ -36,11 +36,13 @@ static VALUE global_request_method;
36
36
  static VALUE global_request_uri;
37
37
  static VALUE global_fragment;
38
38
  static VALUE global_query_string;
39
- static VALUE global_http_version;
39
+ static VALUE global_server_protocol;
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 EXPAND_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 " EXPAND_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,24 @@ static VALUE global_request_path;
50
52
 
51
53
 
52
54
  /* Defines the maximum allowed lengths for various input elements.*/
55
+ #ifndef PUMA_REQUEST_URI_MAX_LENGTH
56
+ #define PUMA_REQUEST_URI_MAX_LENGTH (1024 * 12)
57
+ #endif
58
+
59
+ #ifndef PUMA_REQUEST_PATH_MAX_LENGTH
60
+ #define PUMA_REQUEST_PATH_MAX_LENGTH (8192)
61
+ #endif
62
+
63
+ #ifndef PUMA_QUERY_STRING_MAX_LENGTH
64
+ #define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
65
+ #endif
66
+
53
67
  DEF_MAX_LENGTH(FIELD_NAME, 256);
54
68
  DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
55
- DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
69
+ DEF_MAX_LENGTH(REQUEST_URI, PUMA_REQUEST_URI_MAX_LENGTH);
56
70
  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));
71
+ DEF_MAX_LENGTH(REQUEST_PATH, PUMA_REQUEST_PATH_MAX_LENGTH);
72
+ DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
59
73
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
60
74
 
61
75
  struct common_field {
@@ -112,21 +126,6 @@ static struct common_field common_http_fields[] = {
112
126
  # undef f
113
127
  };
114
128
 
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
129
  static void init_common_fields(void)
131
130
  {
132
131
  unsigned i;
@@ -143,28 +142,10 @@ static void init_common_fields(void)
143
142
  }
144
143
  rb_global_variable(&cf->value);
145
144
  }
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
145
  }
154
146
 
155
147
  static VALUE find_common_field_value(const char *field, size_t flen)
156
148
  {
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
149
  unsigned i;
169
150
  struct common_field *cf = common_http_fields;
170
151
  for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
@@ -172,7 +153,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
172
153
  return cf->value;
173
154
  }
174
155
  return Qnil;
175
- #endif /* !HAVE_QSORT_BSEARCH */
176
156
  }
177
157
 
178
158
  void http_field(puma_parser* hp, const char *field, size_t flen,
@@ -264,10 +244,10 @@ void query_string(puma_parser* hp, const char *at, size_t length)
264
244
  rb_hash_aset(hp->request, global_query_string, val);
265
245
  }
266
246
 
267
- void http_version(puma_parser* hp, const char *at, size_t length)
247
+ void server_protocol(puma_parser* hp, const char *at, size_t length)
268
248
  {
269
249
  VALUE val = rb_str_new(at, length);
270
- rb_hash_aset(hp->request, global_http_version, val);
250
+ rb_hash_aset(hp->request, global_server_protocol, val);
271
251
  }
272
252
 
273
253
  /** Finalizes the request header to have a bunch of stuff that's
@@ -287,11 +267,18 @@ void HttpParser_free(void *data) {
287
267
  }
288
268
  }
289
269
 
290
- void HttpParser_mark(puma_parser* hp) {
270
+ void HttpParser_mark(void *ptr) {
271
+ puma_parser *hp = ptr;
291
272
  if(hp->request) rb_gc_mark(hp->request);
292
273
  if(hp->body) rb_gc_mark(hp->body);
293
274
  }
294
275
 
276
+ const rb_data_type_t HttpParser_data_type = {
277
+ "HttpParser",
278
+ { HttpParser_mark, HttpParser_free, 0 },
279
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
280
+ };
281
+
295
282
  VALUE HttpParser_alloc(VALUE klass)
296
283
  {
297
284
  puma_parser *hp = ALLOC_N(puma_parser, 1);
@@ -302,13 +289,13 @@ VALUE HttpParser_alloc(VALUE klass)
302
289
  hp->fragment = fragment;
303
290
  hp->request_path = request_path;
304
291
  hp->query_string = query_string;
305
- hp->http_version = http_version;
292
+ hp->server_protocol = server_protocol;
306
293
  hp->header_done = header_done;
307
294
  hp->request = Qnil;
308
295
 
309
296
  puma_parser_init(hp);
310
297
 
311
- return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
298
+ return TypedData_Wrap_Struct(klass, &HttpParser_data_type, hp);
312
299
  }
313
300
 
314
301
  /**
@@ -320,7 +307,7 @@ VALUE HttpParser_alloc(VALUE klass)
320
307
  VALUE HttpParser_init(VALUE self)
321
308
  {
322
309
  puma_parser *http = NULL;
323
- DATA_GET(self, puma_parser, http);
310
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
324
311
  puma_parser_init(http);
325
312
 
326
313
  return self;
@@ -337,7 +324,7 @@ VALUE HttpParser_init(VALUE self)
337
324
  VALUE HttpParser_reset(VALUE self)
338
325
  {
339
326
  puma_parser *http = NULL;
340
- DATA_GET(self, puma_parser, http);
327
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
341
328
  puma_parser_init(http);
342
329
 
343
330
  return Qnil;
@@ -354,7 +341,7 @@ VALUE HttpParser_reset(VALUE self)
354
341
  VALUE HttpParser_finish(VALUE self)
355
342
  {
356
343
  puma_parser *http = NULL;
357
- DATA_GET(self, puma_parser, http);
344
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
358
345
  puma_parser_finish(http);
359
346
 
360
347
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
@@ -385,7 +372,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
385
372
  char *dptr = NULL;
386
373
  long dlen = 0;
387
374
 
388
- DATA_GET(self, puma_parser, http);
375
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
389
376
 
390
377
  from = FIX2INT(start);
391
378
  dptr = rb_extract_chars(data, &dlen);
@@ -401,7 +388,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
401
388
  VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
402
389
 
403
390
  if(puma_parser_has_error(http)) {
404
- rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
391
+ rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
405
392
  } else {
406
393
  return INT2FIX(puma_parser_nread(http));
407
394
  }
@@ -419,7 +406,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
419
406
  VALUE HttpParser_has_error(VALUE self)
420
407
  {
421
408
  puma_parser *http = NULL;
422
- DATA_GET(self, puma_parser, http);
409
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
423
410
 
424
411
  return puma_parser_has_error(http) ? Qtrue : Qfalse;
425
412
  }
@@ -434,7 +421,7 @@ VALUE HttpParser_has_error(VALUE self)
434
421
  VALUE HttpParser_is_finished(VALUE self)
435
422
  {
436
423
  puma_parser *http = NULL;
437
- DATA_GET(self, puma_parser, http);
424
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
438
425
 
439
426
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
440
427
  }
@@ -450,7 +437,7 @@ VALUE HttpParser_is_finished(VALUE self)
450
437
  VALUE HttpParser_nread(VALUE self)
451
438
  {
452
439
  puma_parser *http = NULL;
453
- DATA_GET(self, puma_parser, http);
440
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
454
441
 
455
442
  return INT2FIX(http->nread);
456
443
  }
@@ -463,15 +450,16 @@ VALUE HttpParser_nread(VALUE self)
463
450
  */
464
451
  VALUE HttpParser_body(VALUE self) {
465
452
  puma_parser *http = NULL;
466
- DATA_GET(self, puma_parser, http);
453
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
467
454
 
468
455
  return http->body;
469
456
  }
470
457
 
471
- void Init_io_buffer(VALUE puma);
458
+ #ifdef HAVE_OPENSSL_BIO_H
472
459
  void Init_mini_ssl(VALUE mod);
460
+ #endif
473
461
 
474
- void Init_puma_http11()
462
+ void Init_puma_http11(void)
475
463
  {
476
464
 
477
465
  VALUE mPuma = rb_define_module("Puma");
@@ -481,7 +469,7 @@ void Init_puma_http11()
481
469
  DEF_GLOBAL(request_uri, "REQUEST_URI");
482
470
  DEF_GLOBAL(fragment, "FRAGMENT");
483
471
  DEF_GLOBAL(query_string, "QUERY_STRING");
484
- DEF_GLOBAL(http_version, "HTTP_VERSION");
472
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
485
473
  DEF_GLOBAL(request_path, "REQUEST_PATH");
486
474
 
487
475
  eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eIOError);
@@ -498,6 +486,7 @@ void Init_puma_http11()
498
486
  rb_define_method(cHttpParser, "body", HttpParser_body, 0);
499
487
  init_common_fields();
500
488
 
501
- Init_io_buffer(mPuma);
489
+ #ifdef HAVE_OPENSSL_BIO_H
502
490
  Init_mini_ssl(mPuma);
491
+ #endif
503
492
  }
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require_relative '../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
 
@@ -66,13 +80,13 @@ module Puma
66
80
 
67
81
  def authenticate(env)
68
82
  return true unless @auth_token
69
- env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
83
+ env['QUERY_STRING'].to_s.split('&;').include? "token=#{@auth_token}"
70
84
  end
71
85
 
72
86
  def rack_response(status, body, content_type='application/json')
73
87
  headers = {
74
- 'Content-Type' => content_type,
75
- 'Content-Length' => body.bytesize.to_s
88
+ 'content-type' => content_type,
89
+ 'content-length' => body.bytesize.to_s
76
90
  }
77
91
 
78
92
  [status, headers, [body]]