puma 5.0.0.beta2-java → 5.0.4-java

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1194 -570
  3. data/README.md +12 -5
  4. data/bin/puma-wild +3 -9
  5. data/docs/deployment.md +5 -6
  6. data/docs/jungle/README.md +0 -4
  7. data/docs/jungle/rc.d/puma +2 -2
  8. data/docs/nginx.md +1 -1
  9. data/docs/restart.md +46 -23
  10. data/docs/signals.md +3 -3
  11. data/docs/systemd.md +1 -1
  12. data/ext/puma_http11/ext_help.h +1 -1
  13. data/ext/puma_http11/mini_ssl.c +42 -37
  14. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  15. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +40 -12
  16. data/ext/puma_http11/puma_http11.c +21 -10
  17. data/lib/puma.rb +15 -0
  18. data/lib/puma/app/status.rb +44 -43
  19. data/lib/puma/binder.rb +35 -8
  20. data/lib/puma/client.rb +32 -73
  21. data/lib/puma/cluster.rb +32 -191
  22. data/lib/puma/cluster/worker.rb +170 -0
  23. data/lib/puma/cluster/worker_handle.rb +83 -0
  24. data/lib/puma/configuration.rb +9 -7
  25. data/lib/puma/const.rb +2 -1
  26. data/lib/puma/control_cli.rb +2 -0
  27. data/lib/puma/detect.rb +9 -0
  28. data/lib/puma/dsl.rb +74 -36
  29. data/lib/puma/error_logger.rb +3 -2
  30. data/lib/puma/events.rb +7 -3
  31. data/lib/puma/launcher.rb +15 -8
  32. data/lib/puma/minissl.rb +28 -15
  33. data/lib/puma/minissl/context_builder.rb +0 -3
  34. data/lib/puma/puma_http11.jar +0 -0
  35. data/lib/puma/queue_close.rb +26 -0
  36. data/lib/puma/reactor.rb +77 -373
  37. data/lib/puma/request.rb +438 -0
  38. data/lib/puma/runner.rb +6 -18
  39. data/lib/puma/server.rb +192 -509
  40. data/lib/puma/single.rb +3 -2
  41. data/lib/puma/thread_pool.rb +27 -3
  42. data/lib/puma/util.rb +12 -0
  43. metadata +9 -8
  44. data/docs/jungle/upstart/README.md +0 -61
  45. data/docs/jungle/upstart/puma-manager.conf +0 -31
  46. data/docs/jungle/upstart/puma.conf +0 -69
  47. data/lib/puma/accept_nonblock.rb +0 -29
@@ -0,0 +1,15 @@
1
+ package puma;
2
+
3
+ import java.io.IOException;
4
+
5
+ import org.jruby.Ruby;
6
+ import org.jruby.runtime.load.BasicLibraryService;
7
+
8
+ import org.jruby.puma.Http11;
9
+
10
+ public class PumaHttp11Service implements BasicLibraryService {
11
+ public boolean basicLoad(final Ruby runtime) throws IOException {
12
+ Http11.createHttp11(runtime);
13
+ return true;
14
+ }
15
+ }
@@ -22,6 +22,7 @@ import javax.net.ssl.SSLException;
22
22
  import javax.net.ssl.SSLPeerUnverifiedException;
23
23
  import javax.net.ssl.SSLSession;
24
24
  import java.io.FileInputStream;
25
+ import java.io.InputStream;
25
26
  import java.io.IOException;
26
27
  import java.nio.Buffer;
27
28
  import java.nio.ByteBuffer;
@@ -32,6 +33,8 @@ import java.security.NoSuchAlgorithmException;
32
33
  import java.security.UnrecoverableKeyException;
33
34
  import java.security.cert.CertificateEncodingException;
34
35
  import java.security.cert.CertificateException;
36
+ import java.util.concurrent.ConcurrentHashMap;
37
+ import java.util.Map;
35
38
 
36
39
  import static javax.net.ssl.SSLEngineResult.Status;
37
40
  import static javax.net.ssl.SSLEngineResult.HandshakeStatus;
@@ -130,10 +133,39 @@ public class MiniSSL extends RubyObject {
130
133
  super(runtime, klass);
131
134
  }
132
135
 
136
+ private static Map<String, KeyManagerFactory> keyManagerFactoryMap = new ConcurrentHashMap<String, KeyManagerFactory>();
137
+ private static Map<String, TrustManagerFactory> trustManagerFactoryMap = new ConcurrentHashMap<String, TrustManagerFactory>();
138
+
133
139
  @JRubyMethod(meta = true)
134
- public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext) {
135
- RubyClass klass = (RubyClass) recv;
140
+ public static synchronized IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext)
141
+ throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
142
+ // Create the KeyManagerFactory and TrustManagerFactory for this server
143
+ String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString();
144
+ char[] password = miniSSLContext.callMethod(context, "keystore_pass").convertToString().asJavaString().toCharArray();
145
+
146
+ KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
147
+ InputStream is = new FileInputStream(keystoreFile);
148
+ try {
149
+ ks.load(is, password);
150
+ } finally {
151
+ is.close();
152
+ }
153
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
154
+ kmf.init(ks, password);
155
+ keyManagerFactoryMap.put(keystoreFile, kmf);
156
+
157
+ KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
158
+ is = new FileInputStream(keystoreFile);
159
+ try {
160
+ ts.load(is, password);
161
+ } finally {
162
+ is.close();
163
+ }
164
+ TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
165
+ tmf.init(ts);
166
+ trustManagerFactoryMap.put(keystoreFile, tmf);
136
167
 
168
+ RubyClass klass = (RubyClass) recv;
137
169
  return klass.newInstance(context,
138
170
  new IRubyObject[] { miniSSLContext },
139
171
  Block.NULL_BLOCK);
@@ -141,20 +173,16 @@ public class MiniSSL extends RubyObject {
141
173
 
142
174
  @JRubyMethod
143
175
  public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
144
- throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
176
+ throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
145
177
  KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
146
178
  KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
147
179
 
148
- char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray();
149
180
  String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
150
- ks.load(new FileInputStream(keystoreFile), password);
151
- ts.load(new FileInputStream(keystoreFile), password);
152
-
153
- KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
154
- kmf.init(ks, password);
155
-
156
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
157
- tmf.init(ts);
181
+ KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
182
+ TrustManagerFactory tmf = trustManagerFactoryMap.get(keystoreFile);
183
+ if(kmf == null || tmf == null) {
184
+ throw new KeyStoreException("Could not find KeyManagerFactory/TrustManagerFactory for keystore: " + keystoreFile);
185
+ }
158
186
 
159
187
  SSLContext sslCtx = SSLContext.getInstance("TLS");
160
188
 
@@ -253,11 +253,18 @@ void HttpParser_free(void *data) {
253
253
  }
254
254
  }
255
255
 
256
- void HttpParser_mark(puma_parser* hp) {
256
+ void HttpParser_mark(void *ptr) {
257
+ puma_parser *hp = ptr;
257
258
  if(hp->request) rb_gc_mark(hp->request);
258
259
  if(hp->body) rb_gc_mark(hp->body);
259
260
  }
260
261
 
262
+ const rb_data_type_t HttpParser_data_type = {
263
+ "HttpParser",
264
+ { HttpParser_mark, HttpParser_free, 0 },
265
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
266
+ };
267
+
261
268
  VALUE HttpParser_alloc(VALUE klass)
262
269
  {
263
270
  puma_parser *hp = ALLOC_N(puma_parser, 1);
@@ -274,7 +281,7 @@ VALUE HttpParser_alloc(VALUE klass)
274
281
 
275
282
  puma_parser_init(hp);
276
283
 
277
- return Data_Wrap_Struct(klass, HttpParser_mark, HttpParser_free, hp);
284
+ return TypedData_Wrap_Struct(klass, &HttpParser_data_type, hp);
278
285
  }
279
286
 
280
287
  /**
@@ -286,7 +293,7 @@ VALUE HttpParser_alloc(VALUE klass)
286
293
  VALUE HttpParser_init(VALUE self)
287
294
  {
288
295
  puma_parser *http = NULL;
289
- DATA_GET(self, puma_parser, http);
296
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
290
297
  puma_parser_init(http);
291
298
 
292
299
  return self;
@@ -303,7 +310,7 @@ VALUE HttpParser_init(VALUE self)
303
310
  VALUE HttpParser_reset(VALUE self)
304
311
  {
305
312
  puma_parser *http = NULL;
306
- DATA_GET(self, puma_parser, http);
313
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
307
314
  puma_parser_init(http);
308
315
 
309
316
  return Qnil;
@@ -320,7 +327,7 @@ VALUE HttpParser_reset(VALUE self)
320
327
  VALUE HttpParser_finish(VALUE self)
321
328
  {
322
329
  puma_parser *http = NULL;
323
- DATA_GET(self, puma_parser, http);
330
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
324
331
  puma_parser_finish(http);
325
332
 
326
333
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
@@ -351,7 +358,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
351
358
  char *dptr = NULL;
352
359
  long dlen = 0;
353
360
 
354
- DATA_GET(self, puma_parser, http);
361
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
355
362
 
356
363
  from = FIX2INT(start);
357
364
  dptr = rb_extract_chars(data, &dlen);
@@ -385,7 +392,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
385
392
  VALUE HttpParser_has_error(VALUE self)
386
393
  {
387
394
  puma_parser *http = NULL;
388
- DATA_GET(self, puma_parser, http);
395
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
389
396
 
390
397
  return puma_parser_has_error(http) ? Qtrue : Qfalse;
391
398
  }
@@ -400,7 +407,7 @@ VALUE HttpParser_has_error(VALUE self)
400
407
  VALUE HttpParser_is_finished(VALUE self)
401
408
  {
402
409
  puma_parser *http = NULL;
403
- DATA_GET(self, puma_parser, http);
410
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
404
411
 
405
412
  return puma_parser_is_finished(http) ? Qtrue : Qfalse;
406
413
  }
@@ -416,7 +423,7 @@ VALUE HttpParser_is_finished(VALUE self)
416
423
  VALUE HttpParser_nread(VALUE self)
417
424
  {
418
425
  puma_parser *http = NULL;
419
- DATA_GET(self, puma_parser, http);
426
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
420
427
 
421
428
  return INT2FIX(http->nread);
422
429
  }
@@ -429,12 +436,14 @@ VALUE HttpParser_nread(VALUE self)
429
436
  */
430
437
  VALUE HttpParser_body(VALUE self) {
431
438
  puma_parser *http = NULL;
432
- DATA_GET(self, puma_parser, http);
439
+ DATA_GET(self, puma_parser, &HttpParser_data_type, http);
433
440
 
434
441
  return http->body;
435
442
  }
436
443
 
444
+ #ifdef HAVE_OPENSSL_BIO_H
437
445
  void Init_mini_ssl(VALUE mod);
446
+ #endif
438
447
 
439
448
  void Init_puma_http11()
440
449
  {
@@ -463,5 +472,7 @@ void Init_puma_http11()
463
472
  rb_define_method(cHttpParser, "body", HttpParser_body, 0);
464
473
  init_common_fields();
465
474
 
475
+ #ifdef HAVE_OPENSSL_BIO_H
466
476
  Init_mini_ssl(mPuma);
477
+ #endif
467
478
  }
@@ -10,20 +10,27 @@ require 'stringio'
10
10
 
11
11
  require 'thread'
12
12
 
13
+ require 'puma/puma_http11'
14
+ require 'puma/detect'
15
+
13
16
  module Puma
14
17
  autoload :Const, 'puma/const'
15
18
  autoload :Server, 'puma/server'
16
19
  autoload :Launcher, 'puma/launcher'
17
20
 
21
+ # @!attribute [rw] stats_object=
18
22
  def self.stats_object=(val)
19
23
  @get_stats = val
20
24
  end
21
25
 
26
+ # @!attribute [rw] stats_object
22
27
  def self.stats
23
28
  require 'json'
24
29
  @get_stats.stats.to_json
25
30
  end
26
31
 
32
+ # @!attribute [r] stats_hash
33
+ # @version 5.0.0
27
34
  def self.stats_hash
28
35
  @get_stats.stats
29
36
  end
@@ -33,4 +40,12 @@ module Puma
33
40
  return unless Thread.current.respond_to?(:name=)
34
41
  Thread.current.name = "puma #{name}"
35
42
  end
43
+
44
+ unless HAS_SSL
45
+ module MiniSSL
46
+ # this class is defined so that it exists when Puma is compiled
47
+ # without ssl support, as Server and Reactor use it in rescue statements.
48
+ class SSLError < StandardError ; end
49
+ end
50
+ end
36
51
  end
@@ -7,11 +7,16 @@ module Puma
7
7
  class Status
8
8
  OK_STATUS = '{ "status": "ok" }'.freeze
9
9
 
10
- def initialize(cli, token = nil)
11
- @cli = cli
10
+ # @param launcher [::Puma::Launcher]
11
+ # @param token [String, nil] the token used for authentication
12
+ #
13
+ def initialize(launcher, token = nil)
14
+ @launcher = launcher
12
15
  @auth_token = token
13
16
  end
14
17
 
18
+ # most commands call methods in `::Puma::Launcher` based on command in
19
+ # `env['PATH_INFO']`
15
20
  def call(env)
16
21
  unless authenticate(env)
17
22
  return rack_response(403, 'Invalid auth token', 'text/plain')
@@ -21,57 +26,53 @@ module Puma
21
26
  require 'json'
22
27
  end
23
28
 
24
- case env['PATH_INFO']
25
- when /\/stop$/
26
- @cli.stop
27
- rack_response(200, OK_STATUS)
29
+ # resp_type is processed by following case statement, return
30
+ # is a number (status) or a string used as the body of a 200 response
31
+ resp_type =
32
+ case env['PATH_INFO'][/\/([^\/]+)$/, 1]
33
+ when 'stop'
34
+ @launcher.stop ; 200
28
35
 
29
- when /\/halt$/
30
- @cli.halt
31
- rack_response(200, OK_STATUS)
36
+ when 'halt'
37
+ @launcher.halt ; 200
32
38
 
33
- when /\/restart$/
34
- @cli.restart
35
- rack_response(200, OK_STATUS)
39
+ when 'restart'
40
+ @launcher.restart ; 200
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
43
-
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
42
+ when 'phased-restart'
43
+ @launcher.phased_restart ? 200 : 404
50
44
 
51
- when /\/gc$/
52
- GC.start
53
- rack_response(200, OK_STATUS)
45
+ when 'reload-worker-directory'
46
+ @launcher.send(:reload_worker_directory) ? 200 : 404
54
47
 
55
- when /\/gc-stats$/
56
- rack_response(200, GC.stat.to_json)
48
+ when 'gc'
49
+ GC.start ; 200
57
50
 
58
- when /\/stats$/
59
- rack_response(200, @cli.stats.to_json)
51
+ when 'gc-stats'
52
+ GC.stat.to_json
60
53
 
61
- when /\/thread-backtraces$/
62
- backtraces = []
63
- @cli.thread_status do |name, backtrace|
64
- backtraces << { name: name, backtrace: backtrace }
65
- end
54
+ when 'stats'
55
+ @launcher.stats.to_json
66
56
 
67
- rack_response(200, backtraces.to_json)
57
+ when 'thread-backtraces'
58
+ backtraces = []
59
+ @launcher.thread_status do |name, backtrace|
60
+ backtraces << { name: name, backtrace: backtrace }
61
+ end
62
+ backtraces.to_json
68
63
 
69
- when /\/refork$/
70
- Process.kill "SIGURG", $$
71
- rack_response(200, OK_STATUS)
64
+ else
65
+ return rack_response(404, "Unsupported action", 'text/plain')
66
+ end
72
67
 
73
- else
74
- 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\" }"
75
76
  end
76
77
  end
77
78
 
@@ -5,10 +5,24 @@ require 'socket'
5
5
 
6
6
  require 'puma/const'
7
7
  require 'puma/util'
8
- require 'puma/minissl/context_builder'
9
8
  require 'puma/configuration'
10
9
 
11
10
  module Puma
11
+
12
+ if HAS_SSL
13
+ require 'puma/minissl'
14
+ require 'puma/minissl/context_builder'
15
+
16
+ # Odd bug in 'pure Ruby' nio4r verion 2.5.2, which installs with Ruby 2.3.
17
+ # NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
18
+ # The bug was that it did not require openssl.
19
+ # @todo remove when Ruby 2.3 support is dropped
20
+ #
21
+ if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
22
+ require 'openssl'
23
+ end
24
+ end
25
+
12
26
  class Binder
13
27
  include Puma::Const
14
28
 
@@ -44,7 +58,12 @@ module Puma
44
58
  @ios = []
45
59
  end
46
60
 
47
- attr_reader :ios, :listeners, :unix_paths, :proto_env, :envs, :activated_sockets, :inherited_fds
61
+ attr_reader :ios
62
+
63
+ # @version 5.0.0
64
+ attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
65
+
66
+ # @version 5.0.0
48
67
  attr_writer :ios, :listeners
49
68
 
50
69
  def env(sock)
@@ -55,10 +74,13 @@ module Puma
55
74
  @ios.each { |i| i.close }
56
75
  end
57
76
 
77
+ # @!attribute [r] connected_ports
78
+ # @version 5.0.0
58
79
  def connected_ports
59
80
  ios.map { |io| io.addr[1] }.uniq
60
81
  end
61
82
 
83
+ # @version 5.0.0
62
84
  def create_inherited_fds(env_hash)
63
85
  env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
64
86
  fd, url = v.split(":", 2)
@@ -69,7 +91,9 @@ module Puma
69
91
  # systemd socket activation.
70
92
  # LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
71
93
  # LISTEN_PID = PID of the service process, aka us
72
- # see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
94
+ # @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
95
+ # @version 5.0.0
96
+ #
73
97
  def create_activated_fds(env_hash)
74
98
  return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
75
99
  env_hash['LISTEN_FDS'].to_i.times do |index|
@@ -155,6 +179,9 @@ module Puma
155
179
 
156
180
  @listeners << [str, io]
157
181
  when "ssl"
182
+
183
+ raise "Puma compiled without SSL support" unless HAS_SSL
184
+
158
185
  params = Util.parse_query uri.query
159
186
  ctx = MiniSSL::ContextBuilder.new(params, @events).context
160
187
 
@@ -245,9 +272,8 @@ module Puma
245
272
 
246
273
  def add_ssl_listener(host, port, ctx,
247
274
  optimize_for_latency=true, backlog=1024)
248
- require 'puma/minissl'
249
275
 
250
- MiniSSL.check
276
+ raise "Puma compiled without SSL support" unless HAS_SSL
251
277
 
252
278
  if host == "localhost"
253
279
  loopback_addresses.each do |addr|
@@ -264,7 +290,6 @@ module Puma
264
290
  s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
265
291
  s.listen backlog
266
292
 
267
-
268
293
  ssl = MiniSSL::Server.new s, ctx
269
294
  env = @proto_env.dup
270
295
  env[HTTPS_KEY] = HTTPS
@@ -275,8 +300,7 @@ module Puma
275
300
  end
276
301
 
277
302
  def inherit_ssl_listener(fd, ctx)
278
- require 'puma/minissl'
279
- MiniSSL.check
303
+ raise "Puma compiled without SSL support" unless HAS_SSL
280
304
 
281
305
  if fd.kind_of? TCPServer
282
306
  s = fd
@@ -367,6 +391,7 @@ module Puma
367
391
  redirects
368
392
  end
369
393
 
394
+ # @version 5.0.0
370
395
  def redirects_for_restart_env
371
396
  listeners.each_with_object({}).with_index do |(listen, memo), i|
372
397
  memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
@@ -375,12 +400,14 @@ module Puma
375
400
 
376
401
  private
377
402
 
403
+ # @!attribute [r] loopback_addresses
378
404
  def loopback_addresses
379
405
  Socket.ip_address_list.select do |addrinfo|
380
406
  addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
381
407
  end.map { |addrinfo| addrinfo.ip_address }.uniq
382
408
  end
383
409
 
410
+ # @version 5.0.0
384
411
  def socket_activation_fd(int)
385
412
  int + 3 # 3 is the magic number you add to follow the SA protocol
386
413
  end