puma 4.3.5 → 6.0.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 (95) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1639 -519
  3. data/LICENSE +23 -20
  4. data/README.md +130 -42
  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 +85 -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 +56 -11
  30. data/ext/puma_http11/http11_parser.c +69 -58
  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 +3 -3
  35. data/ext/puma_http11/mini_ssl.c +322 -130
  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 +52 -52
  39. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
  40. data/ext/puma_http11/puma_http11.c +47 -57
  41. data/lib/puma/app/status.rb +53 -37
  42. data/lib/puma/binder.rb +232 -119
  43. data/lib/puma/cli.rb +33 -33
  44. data/lib/puma/client.rb +197 -101
  45. data/lib/puma/cluster/worker.rb +175 -0
  46. data/lib/puma/cluster/worker_handle.rb +97 -0
  47. data/lib/puma/cluster.rb +224 -229
  48. data/lib/puma/commonlogger.rb +2 -2
  49. data/lib/puma/configuration.rb +112 -87
  50. data/lib/puma/const.rb +30 -25
  51. data/lib/puma/control_cli.rb +99 -79
  52. data/lib/puma/detect.rb +31 -2
  53. data/lib/puma/dsl.rb +426 -110
  54. data/lib/puma/error_logger.rb +112 -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 +170 -148
  61. data/lib/puma/log_writer.rb +137 -0
  62. data/lib/puma/minissl/context_builder.rb +35 -19
  63. data/lib/puma/minissl.rb +213 -55
  64. data/lib/puma/null_io.rb +18 -1
  65. data/lib/puma/plugin/tmp_restart.rb +1 -1
  66. data/lib/puma/plugin.rb +3 -12
  67. data/lib/puma/rack/builder.rb +5 -9
  68. data/lib/puma/rack/urlmap.rb +0 -0
  69. data/lib/puma/rack_default.rb +1 -1
  70. data/lib/puma/reactor.rb +85 -369
  71. data/lib/puma/request.rb +644 -0
  72. data/lib/puma/runner.rb +83 -77
  73. data/lib/puma/server.rb +303 -773
  74. data/lib/puma/single.rb +18 -74
  75. data/lib/puma/state_file.rb +45 -8
  76. data/lib/puma/systemd.rb +47 -0
  77. data/lib/puma/thread_pool.rb +136 -68
  78. data/lib/puma/util.rb +21 -4
  79. data/lib/puma.rb +54 -5
  80. data/lib/rack/handler/puma.rb +11 -12
  81. data/tools/{docker/Dockerfile → Dockerfile} +1 -1
  82. data/tools/trickletest.rb +0 -0
  83. metadata +36 -28
  84. data/docs/tcp_mode.md +0 -96
  85. data/ext/puma_http11/io_buffer.c +0 -155
  86. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  87. data/lib/puma/accept_nonblock.rb +0 -29
  88. data/lib/puma/tcp_logger.rb +0 -41
  89. data/tools/jungle/README.md +0 -19
  90. data/tools/jungle/init.d/README.md +0 -61
  91. data/tools/jungle/init.d/puma +0 -421
  92. data/tools/jungle/init.d/run-puma +0 -18
  93. data/tools/jungle/upstart/README.md +0 -61
  94. data/tools/jungle/upstart/puma-manager.conf +0 -31
  95. data/tools/jungle/upstart/puma.conf +0 -69
@@ -10,6 +10,7 @@
10
10
  #include "ext_help.h"
11
11
  #include <assert.h>
12
12
  #include <string.h>
13
+ #include <ctype.h>
13
14
  #include "http11_parser.h"
14
15
 
15
16
  #ifndef MANAGED_STRINGS
@@ -35,11 +36,13 @@ static VALUE global_request_method;
35
36
  static VALUE global_request_uri;
36
37
  static VALUE global_fragment;
37
38
  static VALUE global_query_string;
38
- static VALUE global_http_version;
39
+ static VALUE global_server_protocol;
39
40
  static VALUE global_request_path;
40
41
 
41
42
  /** Defines common length and error messages for input length validation. */
42
- #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)"
43
46
 
44
47
  /** Validates the max length of given input and throws an HttpParserError exception if over. */
45
48
  #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
@@ -49,12 +52,24 @@ static VALUE global_request_path;
49
52
 
50
53
 
51
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
+
52
67
  DEF_MAX_LENGTH(FIELD_NAME, 256);
53
68
  DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
54
- DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
69
+ DEF_MAX_LENGTH(REQUEST_URI, PUMA_REQUEST_URI_MAX_LENGTH);
55
70
  DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
56
- DEF_MAX_LENGTH(REQUEST_PATH, 2048);
57
- 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);
58
73
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
59
74
 
60
75
  struct common_field {
@@ -111,21 +126,6 @@ static struct common_field common_http_fields[] = {
111
126
  # undef f
112
127
  };
113
128
 
114
- /*
115
- * qsort(3) and bsearch(3) improve average performance slightly, but may
116
- * not be worth it for lack of portability to certain platforms...
117
- */
118
- #if defined(HAVE_QSORT_BSEARCH)
119
- /* sort by length, then by name if there's a tie */
120
- static int common_field_cmp(const void *a, const void *b)
121
- {
122
- struct common_field *cfa = (struct common_field *)a;
123
- struct common_field *cfb = (struct common_field *)b;
124
- signed long diff = cfa->len - cfb->len;
125
- return diff ? diff : memcmp(cfa->name, cfb->name, cfa->len);
126
- }
127
- #endif /* HAVE_QSORT_BSEARCH */
128
-
129
129
  static void init_common_fields(void)
130
130
  {
131
131
  unsigned i;
@@ -142,28 +142,10 @@ static void init_common_fields(void)
142
142
  }
143
143
  rb_global_variable(&cf->value);
144
144
  }
145
-
146
- #if defined(HAVE_QSORT_BSEARCH)
147
- qsort(common_http_fields,
148
- ARRAY_SIZE(common_http_fields),
149
- sizeof(struct common_field),
150
- common_field_cmp);
151
- #endif /* HAVE_QSORT_BSEARCH */
152
145
  }
153
146
 
154
147
  static VALUE find_common_field_value(const char *field, size_t flen)
155
148
  {
156
- #if defined(HAVE_QSORT_BSEARCH)
157
- struct common_field key;
158
- struct common_field *found;
159
- key.name = field;
160
- key.len = (signed long)flen;
161
- found = (struct common_field *)bsearch(&key, common_http_fields,
162
- ARRAY_SIZE(common_http_fields),
163
- sizeof(struct common_field),
164
- common_field_cmp);
165
- return found ? found->value : Qnil;
166
- #else /* !HAVE_QSORT_BSEARCH */
167
149
  unsigned i;
168
150
  struct common_field *cf = common_http_fields;
169
151
  for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
@@ -171,7 +153,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
171
153
  return cf->value;
172
154
  }
173
155
  return Qnil;
174
- #endif /* !HAVE_QSORT_BSEARCH */
175
156
  }
176
157
 
177
158
  void http_field(puma_parser* hp, const char *field, size_t flen,
@@ -263,10 +244,10 @@ void query_string(puma_parser* hp, const char *at, size_t length)
263
244
  rb_hash_aset(hp->request, global_query_string, val);
264
245
  }
265
246
 
266
- 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)
267
248
  {
268
249
  VALUE val = rb_str_new(at, length);
269
- rb_hash_aset(hp->request, global_http_version, val);
250
+ rb_hash_aset(hp->request, global_server_protocol, val);
270
251
  }
271
252
 
272
253
  /** Finalizes the request header to have a bunch of stuff that's
@@ -286,11 +267,18 @@ void HttpParser_free(void *data) {
286
267
  }
287
268
  }
288
269
 
289
- void HttpParser_mark(puma_parser* hp) {
270
+ void HttpParser_mark(void *ptr) {
271
+ puma_parser *hp = ptr;
290
272
  if(hp->request) rb_gc_mark(hp->request);
291
273
  if(hp->body) rb_gc_mark(hp->body);
292
274
  }
293
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
+
294
282
  VALUE HttpParser_alloc(VALUE klass)
295
283
  {
296
284
  puma_parser *hp = ALLOC_N(puma_parser, 1);
@@ -301,13 +289,13 @@ VALUE HttpParser_alloc(VALUE klass)
301
289
  hp->fragment = fragment;
302
290
  hp->request_path = request_path;
303
291
  hp->query_string = query_string;
304
- hp->http_version = http_version;
292
+ hp->server_protocol = server_protocol;
305
293
  hp->header_done = header_done;
306
294
  hp->request = Qnil;
307
295
 
308
296
  puma_parser_init(hp);
309
297
 
310
- return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
298
+ return TypedData_Wrap_Struct(klass, &HttpParser_data_type, hp);
311
299
  }
312
300
 
313
301
  /**
@@ -319,7 +307,7 @@ VALUE HttpParser_alloc(VALUE klass)
319
307
  VALUE HttpParser_init(VALUE self)
320
308
  {
321
309
  puma_parser *http = NULL;
322
- DATA_GET(self, puma_parser, http);
310
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
323
311
  puma_parser_init(http);
324
312
 
325
313
  return self;
@@ -336,7 +324,7 @@ VALUE HttpParser_init(VALUE self)
336
324
  VALUE HttpParser_reset(VALUE self)
337
325
  {
338
326
  puma_parser *http = NULL;
339
- DATA_GET(self, puma_parser, http);
327
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
340
328
  puma_parser_init(http);
341
329
 
342
330
  return Qnil;
@@ -353,7 +341,7 @@ VALUE HttpParser_reset(VALUE self)
353
341
  VALUE HttpParser_finish(VALUE self)
354
342
  {
355
343
  puma_parser *http = NULL;
356
- DATA_GET(self, puma_parser, http);
344
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
357
345
  puma_parser_finish(http);
358
346
 
359
347
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
@@ -384,7 +372,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
384
372
  char *dptr = NULL;
385
373
  long dlen = 0;
386
374
 
387
- DATA_GET(self, puma_parser, http);
375
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
388
376
 
389
377
  from = FIX2INT(start);
390
378
  dptr = rb_extract_chars(data, &dlen);
@@ -400,7 +388,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
400
388
  VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
401
389
 
402
390
  if(puma_parser_has_error(http)) {
403
- 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?");
404
392
  } else {
405
393
  return INT2FIX(puma_parser_nread(http));
406
394
  }
@@ -418,7 +406,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
418
406
  VALUE HttpParser_has_error(VALUE self)
419
407
  {
420
408
  puma_parser *http = NULL;
421
- DATA_GET(self, puma_parser, http);
409
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
422
410
 
423
411
  return puma_parser_has_error(http) ? Qtrue : Qfalse;
424
412
  }
@@ -433,7 +421,7 @@ VALUE HttpParser_has_error(VALUE self)
433
421
  VALUE HttpParser_is_finished(VALUE self)
434
422
  {
435
423
  puma_parser *http = NULL;
436
- DATA_GET(self, puma_parser, http);
424
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
437
425
 
438
426
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
439
427
  }
@@ -449,7 +437,7 @@ VALUE HttpParser_is_finished(VALUE self)
449
437
  VALUE HttpParser_nread(VALUE self)
450
438
  {
451
439
  puma_parser *http = NULL;
452
- DATA_GET(self, puma_parser, http);
440
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
453
441
 
454
442
  return INT2FIX(http->nread);
455
443
  }
@@ -462,15 +450,16 @@ VALUE HttpParser_nread(VALUE self)
462
450
  */
463
451
  VALUE HttpParser_body(VALUE self) {
464
452
  puma_parser *http = NULL;
465
- DATA_GET(self, puma_parser, http);
453
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
466
454
 
467
455
  return http->body;
468
456
  }
469
457
 
470
- void Init_io_buffer(VALUE puma);
458
+ #ifdef HAVE_OPENSSL_BIO_H
471
459
  void Init_mini_ssl(VALUE mod);
460
+ #endif
472
461
 
473
- void Init_puma_http11()
462
+ void Init_puma_http11(void)
474
463
  {
475
464
 
476
465
  VALUE mPuma = rb_define_module("Puma");
@@ -480,7 +469,7 @@ void Init_puma_http11()
480
469
  DEF_GLOBAL(request_uri, "REQUEST_URI");
481
470
  DEF_GLOBAL(fragment, "FRAGMENT");
482
471
  DEF_GLOBAL(query_string, "QUERY_STRING");
483
- DEF_GLOBAL(http_version, "HTTP_VERSION");
472
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
484
473
  DEF_GLOBAL(request_path, "REQUEST_PATH");
485
474
 
486
475
  eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eIOError);
@@ -497,6 +486,7 @@ void Init_puma_http11()
497
486
  rb_define_method(cHttpParser, "body", HttpParser_body, 0);
498
487
  init_common_fields();
499
488
 
500
- Init_io_buffer(mPuma);
489
+ #ifdef HAVE_OPENSSL_BIO_H
501
490
  Init_mini_ssl(mPuma);
491
+ #endif
502
492
  }
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
-
3
- require 'json'
2
+ require_relative '../json_serialization'
4
3
 
5
4
  module Puma
6
5
  module App
@@ -9,54 +8,71 @@ module Puma
9
8
  class Status
10
9
  OK_STATUS = '{ "status": "ok" }'.freeze
11
10
 
12
- def initialize(cli, token = nil)
13
- @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
14
16
  @auth_token = token
15
17
  end
16
18
 
19
+ # most commands call methods in `::Puma::Launcher` based on command in
20
+ # `env['PATH_INFO']`
17
21
  def call(env)
18
22
  unless authenticate(env)
19
23
  return rack_response(403, 'Invalid auth token', 'text/plain')
20
24
  end
21
25
 
22
- case env['PATH_INFO']
23
- when /\/stop$/
24
- @cli.stop
25
- rack_response(200, OK_STATUS)
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
26
32
 
27
- when /\/halt$/
28
- @cli.halt
29
- rack_response(200, OK_STATUS)
33
+ when 'halt'
34
+ @launcher.halt ; 200
30
35
 
31
- when /\/restart$/
32
- @cli.restart
33
- rack_response(200, OK_STATUS)
36
+ when 'restart'
37
+ @launcher.restart ; 200
34
38
 
35
- when /\/phased-restart$/
36
- if !@cli.phased_restart
37
- rack_response(404, '{ "error": "phased restart not available" }')
38
- else
39
- rack_response(200, OK_STATUS)
40
- end
39
+ when 'phased-restart'
40
+ @launcher.phased_restart ? 200 : 404
41
41
 
42
- when /\/reload-worker-directory$/
43
- if !@cli.send(:reload_worker_directory)
44
- rack_response(404, '{ "error": "reload_worker_directory not available" }')
45
- else
46
- rack_response(200, OK_STATUS)
47
- end
42
+ when 'refork'
43
+ @launcher.refork ? 200 : 404
44
+
45
+ when 'reload-worker-directory'
46
+ @launcher.send(:reload_worker_directory) ? 200 : 404
48
47
 
49
- when /\/gc$/
50
- GC.start
51
- rack_response(200, OK_STATUS)
48
+ when 'gc'
49
+ GC.start ; 200
52
50
 
53
- when /\/gc-stats$/
54
- 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
55
67
 
56
- when /\/stats$/
57
- rack_response(200, @cli.stats)
58
- else
59
- 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\" }"
60
76
  end
61
77
  end
62
78
 
@@ -69,8 +85,8 @@ module Puma
69
85
 
70
86
  def rack_response(status, body, content_type='application/json')
71
87
  headers = {
72
- 'Content-Type' => content_type,
73
- 'Content-Length' => body.bytesize.to_s
88
+ 'content-type' => content_type,
89
+ 'content-length' => body.bytesize.to_s
74
90
  }
75
91
 
76
92
  [status, headers, [body]]