puma 3.12.0 → 5.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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1413 -439
  3. data/LICENSE +23 -20
  4. data/README.md +131 -60
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +24 -19
  7. data/docs/compile_options.md +19 -0
  8. data/docs/deployment.md +38 -13
  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/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  14. data/docs/kubernetes.md +66 -0
  15. data/docs/nginx.md +1 -1
  16. data/docs/plugins.md +20 -10
  17. data/docs/rails_dev_mode.md +29 -0
  18. data/docs/restart.md +47 -22
  19. data/docs/signals.md +7 -6
  20. data/docs/stats.md +142 -0
  21. data/docs/systemd.md +48 -70
  22. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  23. data/ext/puma_http11/ext_help.h +1 -1
  24. data/ext/puma_http11/extconf.rb +27 -0
  25. data/ext/puma_http11/http11_parser.c +84 -109
  26. data/ext/puma_http11/http11_parser.h +1 -1
  27. data/ext/puma_http11/http11_parser.java.rl +22 -38
  28. data/ext/puma_http11/http11_parser.rl +4 -2
  29. data/ext/puma_http11/http11_parser_common.rl +3 -3
  30. data/ext/puma_http11/mini_ssl.c +262 -87
  31. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  32. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  33. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +89 -106
  34. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +92 -22
  35. data/ext/puma_http11/puma_http11.c +34 -50
  36. data/lib/puma/app/status.rb +68 -49
  37. data/lib/puma/binder.rb +197 -144
  38. data/lib/puma/cli.rb +17 -15
  39. data/lib/puma/client.rb +257 -226
  40. data/lib/puma/cluster/worker.rb +176 -0
  41. data/lib/puma/cluster/worker_handle.rb +90 -0
  42. data/lib/puma/cluster.rb +223 -212
  43. data/lib/puma/commonlogger.rb +4 -2
  44. data/lib/puma/configuration.rb +58 -51
  45. data/lib/puma/const.rb +41 -19
  46. data/lib/puma/control_cli.rb +117 -73
  47. data/lib/puma/detect.rb +26 -3
  48. data/lib/puma/dsl.rb +531 -123
  49. data/lib/puma/error_logger.rb +104 -0
  50. data/lib/puma/events.rb +57 -31
  51. data/lib/puma/io_buffer.rb +9 -5
  52. data/lib/puma/jruby_restart.rb +2 -58
  53. data/lib/puma/json.rb +96 -0
  54. data/lib/puma/launcher.rb +182 -70
  55. data/lib/puma/minissl/context_builder.rb +79 -0
  56. data/lib/puma/minissl.rb +149 -48
  57. data/lib/puma/null_io.rb +15 -1
  58. data/lib/puma/plugin/tmp_restart.rb +2 -0
  59. data/lib/puma/plugin.rb +8 -12
  60. data/lib/puma/queue_close.rb +26 -0
  61. data/lib/puma/rack/builder.rb +4 -5
  62. data/lib/puma/rack/urlmap.rb +2 -0
  63. data/lib/puma/rack_default.rb +2 -0
  64. data/lib/puma/reactor.rb +87 -316
  65. data/lib/puma/request.rb +456 -0
  66. data/lib/puma/runner.rb +33 -52
  67. data/lib/puma/server.rb +288 -679
  68. data/lib/puma/single.rb +13 -67
  69. data/lib/puma/state_file.rb +10 -3
  70. data/lib/puma/systemd.rb +46 -0
  71. data/lib/puma/thread_pool.rb +131 -81
  72. data/lib/puma/util.rb +14 -6
  73. data/lib/puma.rb +54 -0
  74. data/lib/rack/handler/puma.rb +8 -6
  75. data/tools/Dockerfile +16 -0
  76. data/tools/trickletest.rb +0 -1
  77. metadata +45 -29
  78. data/ext/puma_http11/io_buffer.c +0 -155
  79. data/lib/puma/accept_nonblock.rb +0 -23
  80. data/lib/puma/compat.rb +0 -14
  81. data/lib/puma/convenient.rb +0 -23
  82. data/lib/puma/daemon_ext.rb +0 -31
  83. data/lib/puma/delegation.rb +0 -11
  84. data/lib/puma/java_io_buffer.rb +0 -45
  85. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  86. data/lib/puma/tcp_logger.rb +0 -39
  87. data/tools/jungle/README.md +0 -19
  88. data/tools/jungle/init.d/README.md +0 -61
  89. data/tools/jungle/init.d/puma +0 -421
  90. data/tools/jungle/init.d/run-puma +0 -18
  91. data/tools/jungle/upstart/README.md +0 -61
  92. data/tools/jungle/upstart/puma-manager.conf +0 -31
  93. 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
@@ -39,7 +40,9 @@ static VALUE global_http_version;
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 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)"
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,16 @@ static VALUE global_request_path;
49
52
 
50
53
 
51
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
+
52
59
  DEF_MAX_LENGTH(FIELD_NAME, 256);
53
60
  DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
54
61
  DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
55
62
  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));
63
+ DEF_MAX_LENGTH(REQUEST_PATH, 8192);
64
+ DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
58
65
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
59
66
 
60
67
  struct common_field {
@@ -111,21 +118,6 @@ static struct common_field common_http_fields[] = {
111
118
  # undef f
112
119
  };
113
120
 
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
121
  static void init_common_fields(void)
130
122
  {
131
123
  unsigned i;
@@ -142,28 +134,10 @@ static void init_common_fields(void)
142
134
  }
143
135
  rb_global_variable(&cf->value);
144
136
  }
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
137
  }
153
138
 
154
139
  static VALUE find_common_field_value(const char *field, size_t flen)
155
140
  {
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
141
  unsigned i;
168
142
  struct common_field *cf = common_http_fields;
169
143
  for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
@@ -171,7 +145,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
171
145
  return cf->value;
172
146
  }
173
147
  return Qnil;
174
- #endif /* !HAVE_QSORT_BSEARCH */
175
148
  }
176
149
 
177
150
  void http_field(puma_parser* hp, const char *field, size_t flen,
@@ -200,6 +173,8 @@ void http_field(puma_parser* hp, const char *field, size_t flen,
200
173
  f = rb_str_new(hp->buf, new_size);
201
174
  }
202
175
 
176
+ while (vlen > 0 && isspace(value[vlen - 1])) vlen--;
177
+
203
178
  /* check for duplicate header */
204
179
  v = rb_hash_aref(hp->request, f);
205
180
 
@@ -284,11 +259,18 @@ void HttpParser_free(void *data) {
284
259
  }
285
260
  }
286
261
 
287
- void HttpParser_mark(puma_parser* hp) {
262
+ void HttpParser_mark(void *ptr) {
263
+ puma_parser *hp = ptr;
288
264
  if(hp->request) rb_gc_mark(hp->request);
289
265
  if(hp->body) rb_gc_mark(hp->body);
290
266
  }
291
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
+
292
274
  VALUE HttpParser_alloc(VALUE klass)
293
275
  {
294
276
  puma_parser *hp = ALLOC_N(puma_parser, 1);
@@ -305,7 +287,7 @@ VALUE HttpParser_alloc(VALUE klass)
305
287
 
306
288
  puma_parser_init(hp);
307
289
 
308
- return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
290
+ return TypedData_Wrap_Struct(klass, &HttpParser_data_type, hp);
309
291
  }
310
292
 
311
293
  /**
@@ -317,7 +299,7 @@ VALUE HttpParser_alloc(VALUE klass)
317
299
  VALUE HttpParser_init(VALUE self)
318
300
  {
319
301
  puma_parser *http = NULL;
320
- DATA_GET(self, puma_parser, http);
302
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
321
303
  puma_parser_init(http);
322
304
 
323
305
  return self;
@@ -334,7 +316,7 @@ VALUE HttpParser_init(VALUE self)
334
316
  VALUE HttpParser_reset(VALUE self)
335
317
  {
336
318
  puma_parser *http = NULL;
337
- DATA_GET(self, puma_parser, http);
319
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
338
320
  puma_parser_init(http);
339
321
 
340
322
  return Qnil;
@@ -351,7 +333,7 @@ VALUE HttpParser_reset(VALUE self)
351
333
  VALUE HttpParser_finish(VALUE self)
352
334
  {
353
335
  puma_parser *http = NULL;
354
- DATA_GET(self, puma_parser, http);
336
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
355
337
  puma_parser_finish(http);
356
338
 
357
339
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
@@ -382,7 +364,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
382
364
  char *dptr = NULL;
383
365
  long dlen = 0;
384
366
 
385
- DATA_GET(self, puma_parser, http);
367
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
386
368
 
387
369
  from = FIX2INT(start);
388
370
  dptr = rb_extract_chars(data, &dlen);
@@ -398,7 +380,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
398
380
  VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
399
381
 
400
382
  if(puma_parser_has_error(http)) {
401
- 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?");
402
384
  } else {
403
385
  return INT2FIX(puma_parser_nread(http));
404
386
  }
@@ -416,7 +398,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
416
398
  VALUE HttpParser_has_error(VALUE self)
417
399
  {
418
400
  puma_parser *http = NULL;
419
- DATA_GET(self, puma_parser, http);
401
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
420
402
 
421
403
  return puma_parser_has_error(http) ? Qtrue : Qfalse;
422
404
  }
@@ -431,7 +413,7 @@ VALUE HttpParser_has_error(VALUE self)
431
413
  VALUE HttpParser_is_finished(VALUE self)
432
414
  {
433
415
  puma_parser *http = NULL;
434
- DATA_GET(self, puma_parser, http);
416
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
435
417
 
436
418
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
437
419
  }
@@ -447,7 +429,7 @@ VALUE HttpParser_is_finished(VALUE self)
447
429
  VALUE HttpParser_nread(VALUE self)
448
430
  {
449
431
  puma_parser *http = NULL;
450
- DATA_GET(self, puma_parser, http);
432
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
451
433
 
452
434
  return INT2FIX(http->nread);
453
435
  }
@@ -460,13 +442,14 @@ VALUE HttpParser_nread(VALUE self)
460
442
  */
461
443
  VALUE HttpParser_body(VALUE self) {
462
444
  puma_parser *http = NULL;
463
- DATA_GET(self, puma_parser, http);
445
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
464
446
 
465
447
  return http->body;
466
448
  }
467
449
 
468
- void Init_io_buffer(VALUE puma);
450
+ #ifdef HAVE_OPENSSL_BIO_H
469
451
  void Init_mini_ssl(VALUE mod);
452
+ #endif
470
453
 
471
454
  void Init_puma_http11()
472
455
  {
@@ -495,6 +478,7 @@ void Init_puma_http11()
495
478
  rb_define_method(cHttpParser, "body", HttpParser_body, 0);
496
479
  init_common_fields();
497
480
 
498
- Init_io_buffer(mPuma);
481
+ #ifdef HAVE_OPENSSL_BIO_H
499
482
  Init_mini_ssl(mPuma);
483
+ #endif
500
484
  }
@@ -1,73 +1,92 @@
1
+ # frozen_string_literal: true
2
+ require 'puma/json'
3
+
1
4
  module Puma
2
5
  module App
6
+ # Check out {#call}'s source code to see what actions this web application
7
+ # can respond to.
3
8
  class Status
4
- def initialize(cli)
5
- @cli = cli
6
- @auth_token = nil
7
- end
8
9
  OK_STATUS = '{ "status": "ok" }'.freeze
9
10
 
10
- attr_accessor :auth_token
11
-
12
- def authenticate(env)
13
- return true unless @auth_token
14
- env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
15
- end
16
-
17
- def rack_response(status, body, content_type='application/json')
18
- headers = {
19
- 'Content-Type' => content_type,
20
- 'Content-Length' => body.bytesize.to_s
21
- }
22
-
23
- [status, headers, [body]]
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
16
+ @auth_token = token
24
17
  end
25
18
 
19
+ # most commands call methods in `::Puma::Launcher` based on command in
20
+ # `env['PATH_INFO']`
26
21
  def call(env)
27
22
  unless authenticate(env)
28
23
  return rack_response(403, 'Invalid auth token', 'text/plain')
29
24
  end
30
25
 
31
- case env['PATH_INFO']
32
- when /\/stop$/
33
- @cli.stop
34
- return 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
35
32
 
36
- when /\/halt$/
37
- @cli.halt
38
- return rack_response(200, OK_STATUS)
33
+ when 'halt'
34
+ @launcher.halt ; 200
39
35
 
40
- when /\/restart$/
41
- @cli.restart
42
- return rack_response(200, OK_STATUS)
36
+ when 'restart'
37
+ @launcher.restart ; 200
43
38
 
44
- when /\/phased-restart$/
45
- if !@cli.phased_restart
46
- return rack_response(404, '{ "error": "phased restart not available" }')
47
- else
48
- return rack_response(200, OK_STATUS)
49
- end
39
+ when 'phased-restart'
40
+ @launcher.phased_restart ? 200 : 404
41
+
42
+ when 'reload-worker-directory'
43
+ @launcher.send(:reload_worker_directory) ? 200 : 404
44
+
45
+ when 'gc'
46
+ GC.start ; 200
47
+
48
+ when 'gc-stats'
49
+ Puma::JSON.generate GC.stat
50
+
51
+ when 'stats'
52
+ Puma::JSON.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::JSON.generate backtraces
50
60
 
51
- when /\/reload-worker-directory$/
52
- if !@cli.send(:reload_worker_directory)
53
- return rack_response(404, '{ "error": "reload_worker_directory not available" }')
54
61
  else
55
- return rack_response(200, OK_STATUS)
62
+ return rack_response(404, "Unsupported action", 'text/plain')
56
63
  end
57
64
 
58
- when /\/gc$/
59
- GC.start
60
- return rack_response(200, OK_STATUS)
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\" }"
73
+ end
74
+ end
61
75
 
62
- when /\/gc-stats$/
63
- json = "{" + GC.stat.map { |k, v| "\"#{k}\": #{v}" }.join(",") + "}"
64
- return rack_response(200, json)
76
+ private
65
77
 
66
- when /\/stats$/
67
- return rack_response(200, @cli.stats)
68
- else
69
- rack_response 404, "Unsupported action", 'text/plain'
70
- end
78
+ def authenticate(env)
79
+ return true unless @auth_token
80
+ env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
81
+ end
82
+
83
+ def rack_response(status, body, content_type='application/json')
84
+ headers = {
85
+ 'Content-Type' => content_type,
86
+ 'Content-Length' => body.bytesize.to_s
87
+ }
88
+
89
+ [status, headers, [body]]
71
90
  end
72
91
  end
73
92
  end