puma 3.9.0 → 3.12.0

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 (53) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +98 -0
  3. data/README.md +140 -230
  4. data/docs/architecture.md +36 -0
  5. data/{DEPLOYMENT.md → docs/deployment.md} +0 -0
  6. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  7. data/docs/images/puma-connection-flow.png +0 -0
  8. data/docs/images/puma-general-arch.png +0 -0
  9. data/docs/plugins.md +28 -0
  10. data/docs/restart.md +39 -0
  11. data/docs/signals.md +56 -3
  12. data/docs/systemd.md +112 -37
  13. data/ext/puma_http11/http11_parser.c +84 -84
  14. data/ext/puma_http11/http11_parser.rl +9 -9
  15. data/ext/puma_http11/mini_ssl.c +18 -4
  16. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +13 -16
  17. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +6 -0
  18. data/lib/puma.rb +8 -0
  19. data/lib/puma/app/status.rb +8 -0
  20. data/lib/puma/binder.rb +12 -8
  21. data/lib/puma/cli.rb +20 -7
  22. data/lib/puma/client.rb +28 -0
  23. data/lib/puma/cluster.rb +26 -7
  24. data/lib/puma/configuration.rb +19 -14
  25. data/lib/puma/const.rb +7 -2
  26. data/lib/puma/control_cli.rb +5 -5
  27. data/lib/puma/dsl.rb +34 -7
  28. data/lib/puma/jruby_restart.rb +0 -1
  29. data/lib/puma/launcher.rb +36 -19
  30. data/lib/puma/minissl.rb +49 -27
  31. data/lib/puma/plugin/tmp_restart.rb +0 -1
  32. data/lib/puma/reactor.rb +135 -0
  33. data/lib/puma/runner.rb +12 -1
  34. data/lib/puma/server.rb +84 -25
  35. data/lib/puma/single.rb +12 -3
  36. data/lib/puma/thread_pool.rb +47 -8
  37. data/lib/rack/handler/puma.rb +4 -1
  38. data/tools/jungle/README.md +12 -2
  39. data/tools/jungle/init.d/README.md +2 -0
  40. data/tools/jungle/init.d/puma +2 -2
  41. data/tools/jungle/init.d/run-puma +1 -1
  42. data/tools/jungle/rc.d/README.md +74 -0
  43. data/tools/jungle/rc.d/puma +61 -0
  44. data/tools/jungle/rc.d/puma.conf +10 -0
  45. data/tools/trickletest.rb +1 -1
  46. metadata +21 -94
  47. data/.github/issue_template.md +0 -20
  48. data/Gemfile +0 -12
  49. data/Manifest.txt +0 -78
  50. data/Rakefile +0 -158
  51. data/Release.md +0 -9
  52. data/gemfiles/2.1-Gemfile +0 -12
  53. data/puma.gemspec +0 -52
@@ -29,7 +29,7 @@ static void snake_upcase_char(char *c)
29
29
  /** Machine **/
30
30
 
31
31
  %%{
32
-
32
+
33
33
  machine puma_parser;
34
34
 
35
35
  action mark { MARK(mark, fpc); }
@@ -37,7 +37,7 @@ static void snake_upcase_char(char *c)
37
37
 
38
38
  action start_field { MARK(field_start, fpc); }
39
39
  action snake_upcase_field { snake_upcase_char((char *)fpc); }
40
- action write_field {
40
+ action write_field {
41
41
  parser->field_len = LEN(field_start, fpc);
42
42
  }
43
43
 
@@ -45,10 +45,10 @@ static void snake_upcase_char(char *c)
45
45
  action write_value {
46
46
  parser->http_field(parser, PTR_TO(field_start), parser->field_len, PTR_TO(mark), LEN(mark, fpc));
47
47
  }
48
- action request_method {
48
+ action request_method {
49
49
  parser->request_method(parser, PTR_TO(mark), LEN(mark, fpc));
50
50
  }
51
- action request_uri {
51
+ action request_uri {
52
52
  parser->request_uri(parser, PTR_TO(mark), LEN(mark, fpc));
53
53
  }
54
54
  action fragment {
@@ -56,11 +56,11 @@ static void snake_upcase_char(char *c)
56
56
  }
57
57
 
58
58
  action start_query { MARK(query_start, fpc); }
59
- action query_string {
59
+ action query_string {
60
60
  parser->query_string(parser, PTR_TO(query_start), LEN(query_start, fpc));
61
61
  }
62
62
 
63
- action http_version {
63
+ action http_version {
64
64
  parser->http_version(parser, PTR_TO(mark), LEN(mark, fpc));
65
65
  }
66
66
 
@@ -68,8 +68,8 @@ static void snake_upcase_char(char *c)
68
68
  parser->request_path(parser, PTR_TO(mark), LEN(mark,fpc));
69
69
  }
70
70
 
71
- action done {
72
- parser->body_start = fpc - buffer + 1;
71
+ action done {
72
+ parser->body_start = fpc - buffer + 1;
73
73
  parser->header_done(parser, fpc + 1, pe - fpc - 1);
74
74
  fbreak;
75
75
  }
@@ -109,7 +109,7 @@ size_t puma_parser_execute(puma_parser *parser, const char *buffer, size_t len,
109
109
  pe = buffer+len;
110
110
 
111
111
  /* assert(*pe == '\0' && "pointer does not end on NUL"); */
112
- assert(pe - p == len - off && "pointers aren't same distance");
112
+ assert((size_t) (pe - p) == len - off && "pointers aren't same distance");
113
113
 
114
114
  %% write exec;
115
115
 
@@ -161,6 +161,9 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
161
161
  ID sym_verify_mode = rb_intern("verify_mode");
162
162
  VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0);
163
163
 
164
+ ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
165
+ VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
166
+
164
167
  ctx = SSL_CTX_new(SSLv23_server_method());
165
168
  conn->ctx = ctx;
166
169
 
@@ -175,7 +178,13 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
175
178
  SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION);
176
179
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
177
180
 
178
- SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
181
+ if (!NIL_P(ssl_cipher_filter)) {
182
+ StringValue(ssl_cipher_filter);
183
+ SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter));
184
+ }
185
+ else {
186
+ SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
187
+ }
179
188
 
180
189
  DH *dh = get_dh1024();
181
190
  SSL_CTX_set_tmp_dh(ctx, dh);
@@ -244,7 +253,7 @@ void raise_error(SSL* ssl, int result) {
244
253
  const char* err_str;
245
254
  int err = errno;
246
255
  int ssl_err = SSL_get_error(ssl, result);
247
- int verify_err = SSL_get_verify_result(ssl);
256
+ int verify_err = (int) SSL_get_verify_result(ssl);
248
257
 
249
258
  if(SSL_ERROR_SYSCALL == ssl_err) {
250
259
  snprintf(msg, sizeof(msg), "System error: %s - %d", strerror(err), err);
@@ -257,7 +266,7 @@ void raise_error(SSL* ssl, int result) {
257
266
  err_str, verify_err);
258
267
 
259
268
  } else {
260
- err = ERR_get_error();
269
+ err = (int) ERR_get_error();
261
270
  ERR_error_string_n(err, buf, sizeof(buf));
262
271
  snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, err);
263
272
 
@@ -411,6 +420,11 @@ VALUE noop(VALUE self) {
411
420
  void Init_mini_ssl(VALUE puma) {
412
421
  VALUE mod, eng;
413
422
 
423
+ /* Fake operation for documentation (RDoc, YARD) */
424
+ #if 0 == 1
425
+ puma = rb_define_module("Puma");
426
+ #endif
427
+
414
428
  SSL_library_init();
415
429
  OpenSSL_add_ssl_algorithms();
416
430
  SSL_load_error_strings();
@@ -447,7 +461,7 @@ VALUE raise_error(VALUE self) {
447
461
  }
448
462
 
449
463
  void Init_mini_ssl(VALUE puma) {
450
- VALUE mod, eng;
464
+ VALUE mod;
451
465
 
452
466
  mod = rb_define_module_under(puma, "MiniSSL");
453
467
  rb_define_class_under(mod, "SSLError", rb_eStandardError);
@@ -182,9 +182,6 @@ static final int puma_parser_start = 1;
182
182
  static final int puma_parser_first_final = 47;
183
183
  static final int puma_parser_error = 0;
184
184
 
185
- static final int puma_parser_en_main = 1;
186
-
187
-
188
185
  // line 69 "ext/puma_http11/http11_parser.java.rl"
189
186
 
190
187
  public static interface ElementCB {
@@ -220,7 +217,7 @@ static final int puma_parser_en_main = 1;
220
217
  public void init() {
221
218
  cs = 0;
222
219
 
223
-
220
+
224
221
  // line 225 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
225
222
  {
226
223
  cs = puma_parser_start;
@@ -252,7 +249,7 @@ static final int puma_parser_en_main = 1;
252
249
  byte[] data = buffer.bytes();
253
250
  parser.buffer = buffer;
254
251
 
255
-
252
+
256
253
  // line 257 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
257
254
  {
258
255
  int _klen;
@@ -347,7 +344,7 @@ case 1:
347
344
  break;
348
345
  case 3:
349
346
  // line 17 "ext/puma_http11/http11_parser.java.rl"
350
- {
347
+ {
351
348
  parser.field_len = p-parser.field_start;
352
349
  }
353
350
  break;
@@ -357,7 +354,7 @@ case 1:
357
354
  break;
358
355
  case 5:
359
356
  // line 22 "ext/puma_http11/http11_parser.java.rl"
360
- {
357
+ {
361
358
  if(parser.http_field != null) {
362
359
  parser.http_field.call(parser.data, parser.field_start, parser.field_len, parser.mark, p-parser.mark);
363
360
  }
@@ -365,21 +362,21 @@ case 1:
365
362
  break;
366
363
  case 6:
367
364
  // line 27 "ext/puma_http11/http11_parser.java.rl"
368
- {
369
- if(parser.request_method != null)
365
+ {
366
+ if(parser.request_method != null)
370
367
  parser.request_method.call(parser.data, parser.mark, p-parser.mark);
371
368
  }
372
369
  break;
373
370
  case 7:
374
371
  // line 31 "ext/puma_http11/http11_parser.java.rl"
375
- {
372
+ {
376
373
  if(parser.request_uri != null)
377
374
  parser.request_uri.call(parser.data, parser.mark, p-parser.mark);
378
375
  }
379
376
  break;
380
377
  case 8:
381
378
  // line 35 "ext/puma_http11/http11_parser.java.rl"
382
- {
379
+ {
383
380
  if(parser.fragment != null)
384
381
  parser.fragment.call(parser.data, parser.mark, p-parser.mark);
385
382
  }
@@ -390,14 +387,14 @@ case 1:
390
387
  break;
391
388
  case 10:
392
389
  // line 41 "ext/puma_http11/http11_parser.java.rl"
393
- {
390
+ {
394
391
  if(parser.query_string != null)
395
392
  parser.query_string.call(parser.data, parser.query_start, p-parser.query_start);
396
393
  }
397
394
  break;
398
395
  case 11:
399
396
  // line 46 "ext/puma_http11/http11_parser.java.rl"
400
- {
397
+ {
401
398
  if(parser.http_version != null)
402
399
  parser.http_version.call(parser.data, parser.mark, p-parser.mark);
403
400
  }
@@ -411,8 +408,8 @@ case 1:
411
408
  break;
412
409
  case 13:
413
410
  // line 56 "ext/puma_http11/http11_parser.java.rl"
414
- {
415
- parser.body_start = p + 1;
411
+ {
412
+ parser.body_start = p + 1;
416
413
  if(parser.header_done != null)
417
414
  parser.header_done.call(parser.data, p + 1, pe - p - 1);
418
415
  { p += 1; _goto_targ = 5; if (true) continue _goto;}
@@ -442,7 +439,7 @@ case 5:
442
439
 
443
440
  parser.cs = cs;
444
441
  parser.nread += (p - off);
445
-
442
+
446
443
  assert p <= pe : "buffer overflow after parsing execute";
447
444
  assert parser.nread <= len : "nread longer than length";
448
445
  assert parser.body_start <= len : "body starts after buffer end";
@@ -170,6 +170,12 @@ public class MiniSSL extends RubyObject {
170
170
  engine.setNeedClientAuth(true);
171
171
  }
172
172
 
173
+ IRubyObject sslCipherListObject = miniSSLContext.callMethod(threadContext, "ssl_cipher_list");
174
+ if (!sslCipherListObject.isNil()) {
175
+ String[] sslCipherList = sslCipherListObject.convertToString().asJavaString().split(",");
176
+ engine.setEnabledCipherSuites(sslCipherList);
177
+ }
178
+
173
179
  SSLSession session = engine.getSession();
174
180
  inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
175
181
  outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize());
@@ -12,4 +12,12 @@ module Puma
12
12
  autoload :Const, 'puma/const'
13
13
  autoload :Server, 'puma/server'
14
14
  autoload :Launcher, 'puma/launcher'
15
+
16
+ def self.stats_object=(val)
17
+ @get_stats = val
18
+ end
19
+
20
+ def self.stats
21
+ @get_stats.stats
22
+ end
15
23
  end
@@ -55,6 +55,14 @@ module Puma
55
55
  return rack_response(200, OK_STATUS)
56
56
  end
57
57
 
58
+ when /\/gc$/
59
+ GC.start
60
+ return rack_response(200, OK_STATUS)
61
+
62
+ when /\/gc-stats$/
63
+ json = "{" + GC.stat.map { |k, v| "\"#{k}\": #{v}" }.join(",") + "}"
64
+ return rack_response(200, json)
65
+
58
66
  when /\/stats$/
59
67
  return rack_response(200, @cli.stats)
60
68
  else
@@ -120,7 +120,7 @@ module Puma
120
120
 
121
121
  umask = nil
122
122
  mode = nil
123
- backlog = nil
123
+ backlog = 1024
124
124
 
125
125
  if uri.query
126
126
  params = Util.parse_query uri.query
@@ -162,6 +162,7 @@ module Puma
162
162
  end
163
163
 
164
164
  ctx.keystore_pass = params['keystore-pass']
165
+ ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
165
166
  else
166
167
  unless params['key']
167
168
  @events.error "Please specify the SSL key via 'key='"
@@ -182,6 +183,7 @@ module Puma
182
183
  end
183
184
 
184
185
  ctx.ca = params['ca'] if params['ca']
186
+ ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
185
187
  end
186
188
 
187
189
  if params['verify_mode']
@@ -245,9 +247,10 @@ module Puma
245
247
  end
246
248
  end
247
249
 
248
- def localhost_addresses
249
- addrs = TCPSocket.gethostbyname "localhost"
250
- addrs[3..-1].uniq
250
+ def loopback_addresses
251
+ Socket.ip_address_list.select do |addrinfo|
252
+ addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
253
+ end.map { |addrinfo| addrinfo.ip_address }.uniq
251
254
  end
252
255
 
253
256
  # Tell the server to listen on host +host+, port +port+.
@@ -259,7 +262,7 @@ module Puma
259
262
  #
260
263
  def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
261
264
  if host == "localhost"
262
- localhost_addresses.each do |addr|
265
+ loopback_addresses.each do |addr|
263
266
  add_tcp_listener addr, port, optimize_for_latency, backlog
264
267
  end
265
268
  return
@@ -298,7 +301,7 @@ module Puma
298
301
  MiniSSL.check
299
302
 
300
303
  if host == "localhost"
301
- localhost_addresses.each do |addr|
304
+ loopback_addresses.each do |addr|
302
305
  add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
303
306
  end
304
307
  return
@@ -312,6 +315,7 @@ module Puma
312
315
  s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
313
316
  s.listen backlog
314
317
 
318
+
315
319
  ssl = MiniSSL::Server.new s, ctx
316
320
  env = @proto_env.dup
317
321
  env[HTTPS_KEY] = HTTPS
@@ -343,7 +347,7 @@ module Puma
343
347
 
344
348
  # Tell the server to listen on +path+ as a UNIX domain socket.
345
349
  #
346
- def add_unix_listener(path, umask=nil, mode=nil, backlog=nil)
350
+ def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
347
351
  @unix_paths << path
348
352
 
349
353
  # Let anyone connect by default
@@ -364,7 +368,7 @@ module Puma
364
368
  end
365
369
 
366
370
  s = UNIXServer.new(path)
367
- s.listen backlog if backlog
371
+ s.listen backlog
368
372
  @ios << s
369
373
  ensure
370
374
  File.umask old_mask
@@ -1,6 +1,7 @@
1
1
  require 'optparse'
2
2
  require 'uri'
3
3
 
4
+ require 'puma'
4
5
  require 'puma/configuration'
5
6
  require 'puma/launcher'
6
7
  require 'puma/const'
@@ -83,6 +84,14 @@ module Puma
83
84
  raise UnsupportedOption
84
85
  end
85
86
 
87
+ def configure_control_url(command_line_arg)
88
+ if command_line_arg
89
+ @control_url = command_line_arg
90
+ elsif Puma.jruby?
91
+ unsupported "No default url available on JRuby"
92
+ end
93
+ end
94
+
86
95
  # Build the OptionParser object to handle the available options.
87
96
  #
88
97
 
@@ -97,13 +106,13 @@ module Puma
97
106
  file_config.load arg
98
107
  end
99
108
 
100
- o.on "--control URL", "The bind url to use for the control server",
101
- "Use 'auto' to use temp unix server" do |arg|
102
- if arg
103
- @control_url = arg
104
- elsif Puma.jruby?
105
- unsupported "No default url available on JRuby"
106
- end
109
+ o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
110
+ configure_control_url(arg)
111
+ end
112
+
113
+ # alias --control-url for backwards-compatibility
114
+ o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
115
+ configure_control_url(arg)
107
116
  end
108
117
 
109
118
  o.on "--control-token TOKEN",
@@ -181,6 +190,10 @@ module Puma
181
190
  user_config.tcp_mode!
182
191
  end
183
192
 
193
+ o.on "--early-hints", "Enable early hints support" do
194
+ user_config.early_hints
195
+ end
196
+
184
197
  o.on "-V", "--version", "Print the version information" do
185
198
  puts "puma version #{Puma::Const::VERSION}"
186
199
  exit 0
@@ -21,6 +21,17 @@ module Puma
21
21
 
22
22
  class ConnectionError < RuntimeError; end
23
23
 
24
+ # An instance of this class represents a unique request from a client.
25
+ # For example a web request from a browser or from CURL. This
26
+ #
27
+ # An instance of `Puma::Client` can be used as if it were an IO object
28
+ # for example it is passed into `IO.select` inside of the `Puma::Reactor`.
29
+ # This is accomplished by the `to_io` method which gets called on any
30
+ # non-IO objects being used with the IO api such as `IO.select.
31
+ #
32
+ # Instances of this class are responsible for knowing if
33
+ # the header and body are fully buffered via the `try_to_finish` method.
34
+ # They can be used to "time out" a response via the `timeout_at` reader.
24
35
  class Client
25
36
  include Puma::Const
26
37
  extend Puma::Delegation
@@ -111,6 +122,7 @@ module Puma
111
122
  begin
112
123
  @io.close
113
124
  rescue IOError
125
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
114
126
  end
115
127
  end
116
128
 
@@ -283,6 +295,14 @@ module Puma
283
295
  raise ConnectionError, "Connection error detected during read"
284
296
  end
285
297
 
298
+ # No data means a closed socket
299
+ unless data
300
+ @buffer = nil
301
+ @requests_served += 1
302
+ @ready = true
303
+ raise EOFError
304
+ end
305
+
286
306
  if @buffer
287
307
  @buffer << data
288
308
  else
@@ -312,6 +332,14 @@ module Puma
312
332
  raise e
313
333
  end
314
334
 
335
+ # No data means a closed socket
336
+ unless data
337
+ @buffer = nil
338
+ @requests_served += 1
339
+ @ready = true
340
+ raise EOFError
341
+ end
342
+
315
343
  if @buffer
316
344
  @buffer << data
317
345
  else