puma 3.8.2 → 4.3.12

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 (91) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +305 -0
  3. data/LICENSE +0 -0
  4. data/README.md +162 -224
  5. data/bin/puma-wild +0 -0
  6. data/docs/architecture.md +37 -0
  7. data/{DEPLOYMENT.md → docs/deployment.md} +24 -4
  8. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  9. data/docs/images/puma-connection-flow.png +0 -0
  10. data/docs/images/puma-general-arch.png +0 -0
  11. data/docs/nginx.md +0 -0
  12. data/docs/plugins.md +38 -0
  13. data/docs/restart.md +41 -0
  14. data/docs/signals.md +56 -3
  15. data/docs/systemd.md +130 -37
  16. data/docs/tcp_mode.md +96 -0
  17. data/ext/puma_http11/PumaHttp11Service.java +2 -0
  18. data/ext/puma_http11/ext_help.h +0 -0
  19. data/ext/puma_http11/extconf.rb +21 -0
  20. data/ext/puma_http11/http11_parser.c +134 -144
  21. data/ext/puma_http11/http11_parser.h +0 -0
  22. data/ext/puma_http11/http11_parser.java.rl +21 -37
  23. data/ext/puma_http11/http11_parser.rl +12 -10
  24. data/ext/puma_http11/http11_parser_common.rl +4 -4
  25. data/ext/puma_http11/io_buffer.c +0 -0
  26. data/ext/puma_http11/mini_ssl.c +165 -34
  27. data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
  28. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +85 -101
  29. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  30. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +30 -6
  31. data/ext/puma_http11/puma_http11.c +3 -0
  32. data/lib/puma/accept_nonblock.rb +7 -1
  33. data/lib/puma/app/status.rb +42 -26
  34. data/lib/puma/binder.rb +57 -74
  35. data/lib/puma/cli.rb +26 -7
  36. data/lib/puma/client.rb +307 -191
  37. data/lib/puma/cluster.rb +78 -34
  38. data/lib/puma/commonlogger.rb +2 -0
  39. data/lib/puma/configuration.rb +24 -16
  40. data/lib/puma/const.rb +41 -20
  41. data/lib/puma/control_cli.rb +46 -19
  42. data/lib/puma/detect.rb +2 -0
  43. data/lib/puma/dsl.rb +329 -68
  44. data/lib/puma/events.rb +6 -2
  45. data/lib/puma/io_buffer.rb +3 -6
  46. data/lib/puma/jruby_restart.rb +2 -1
  47. data/lib/puma/launcher.rb +125 -61
  48. data/lib/puma/minissl/context_builder.rb +76 -0
  49. data/lib/puma/minissl.rb +85 -28
  50. data/lib/puma/null_io.rb +2 -0
  51. data/lib/puma/plugin/tmp_restart.rb +2 -1
  52. data/lib/puma/plugin.rb +7 -2
  53. data/lib/puma/rack/builder.rb +4 -1
  54. data/lib/puma/rack/urlmap.rb +2 -0
  55. data/lib/puma/rack_default.rb +2 -0
  56. data/lib/puma/reactor.rb +224 -34
  57. data/lib/puma/runner.rb +27 -6
  58. data/lib/puma/server.rb +212 -68
  59. data/lib/puma/single.rb +16 -5
  60. data/lib/puma/state_file.rb +2 -0
  61. data/lib/puma/tcp_logger.rb +2 -0
  62. data/lib/puma/thread_pool.rb +67 -36
  63. data/lib/puma/util.rb +2 -6
  64. data/lib/puma.rb +16 -0
  65. data/lib/rack/handler/puma.rb +16 -5
  66. data/tools/docker/Dockerfile +16 -0
  67. data/tools/jungle/README.md +12 -2
  68. data/tools/jungle/init.d/README.md +2 -0
  69. data/tools/jungle/init.d/puma +8 -8
  70. data/tools/jungle/init.d/run-puma +1 -1
  71. data/tools/jungle/rc.d/README.md +74 -0
  72. data/tools/jungle/rc.d/puma +61 -0
  73. data/tools/jungle/rc.d/puma.conf +10 -0
  74. data/tools/jungle/upstart/README.md +0 -0
  75. data/tools/jungle/upstart/puma-manager.conf +0 -0
  76. data/tools/jungle/upstart/puma.conf +0 -0
  77. data/tools/trickletest.rb +1 -2
  78. metadata +32 -93
  79. data/.github/issue_template.md +0 -20
  80. data/Gemfile +0 -12
  81. data/Manifest.txt +0 -78
  82. data/Rakefile +0 -158
  83. data/Release.md +0 -9
  84. data/gemfiles/2.1-Gemfile +0 -12
  85. data/lib/puma/compat.rb +0 -14
  86. data/lib/puma/convenient.rb +0 -23
  87. data/lib/puma/daemon_ext.rb +0 -31
  88. data/lib/puma/delegation.rb +0 -11
  89. data/lib/puma/java_io_buffer.rb +0 -45
  90. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  91. data/puma.gemspec +0 -52
@@ -0,0 +1,72 @@
1
+ package org.jruby.puma;
2
+
3
+ import org.jruby.*;
4
+ import org.jruby.anno.JRubyMethod;
5
+ import org.jruby.runtime.ObjectAllocator;
6
+ import org.jruby.runtime.ThreadContext;
7
+ import org.jruby.runtime.builtin.IRubyObject;
8
+ import org.jruby.util.ByteList;
9
+
10
+ /**
11
+ * @author kares
12
+ */
13
+ public class IOBuffer extends RubyObject {
14
+
15
+ private static final ObjectAllocator ALLOCATOR = new ObjectAllocator() {
16
+ public IRubyObject allocate(Ruby runtime, RubyClass klass) {
17
+ return new IOBuffer(runtime, klass);
18
+ }
19
+ };
20
+
21
+ public static void createIOBuffer(Ruby runtime) {
22
+ RubyModule mPuma = runtime.defineModule("Puma");
23
+ RubyClass cIOBuffer = mPuma.defineClassUnder("IOBuffer", runtime.getObject(), ALLOCATOR);
24
+ cIOBuffer.defineAnnotatedMethods(IOBuffer.class);
25
+ }
26
+
27
+ private static final int DEFAULT_SIZE = 4096;
28
+
29
+ final ByteList buffer = new ByteList(DEFAULT_SIZE);
30
+
31
+ IOBuffer(Ruby runtime, RubyClass klass) {
32
+ super(runtime, klass);
33
+ }
34
+
35
+ @JRubyMethod
36
+ public RubyInteger used(ThreadContext context) {
37
+ return context.runtime.newFixnum(buffer.getRealSize());
38
+ }
39
+
40
+ @JRubyMethod
41
+ public RubyInteger capacity(ThreadContext context) {
42
+ return context.runtime.newFixnum(buffer.unsafeBytes().length);
43
+ }
44
+
45
+ @JRubyMethod
46
+ public IRubyObject reset() {
47
+ buffer.setRealSize(0);
48
+ return this;
49
+ }
50
+
51
+ @JRubyMethod(name = { "to_s", "to_str" })
52
+ public RubyString to_s(ThreadContext context) {
53
+ return RubyString.newStringShared(context.runtime, buffer.unsafeBytes(), 0, buffer.getRealSize());
54
+ }
55
+
56
+ @JRubyMethod(name = "<<")
57
+ public IRubyObject add(IRubyObject str) {
58
+ addImpl(str.convertToString());
59
+ return this;
60
+ }
61
+
62
+ @JRubyMethod(rest = true)
63
+ public IRubyObject append(IRubyObject[] strs) {
64
+ for (IRubyObject str : strs) addImpl(str.convertToString());
65
+ return this;
66
+ }
67
+
68
+ private void addImpl(RubyString str) {
69
+ buffer.append(str.getByteList());
70
+ }
71
+
72
+ }
@@ -6,6 +6,7 @@ import org.jruby.RubyModule;
6
6
  import org.jruby.RubyObject;
7
7
  import org.jruby.RubyString;
8
8
  import org.jruby.anno.JRubyMethod;
9
+ import org.jruby.javasupport.JavaEmbedUtils;
9
10
  import org.jruby.runtime.Block;
10
11
  import org.jruby.runtime.ObjectAllocator;
11
12
  import org.jruby.runtime.ThreadContext;
@@ -18,15 +19,18 @@ import javax.net.ssl.SSLContext;
18
19
  import javax.net.ssl.SSLEngine;
19
20
  import javax.net.ssl.SSLEngineResult;
20
21
  import javax.net.ssl.SSLException;
22
+ import javax.net.ssl.SSLPeerUnverifiedException;
21
23
  import javax.net.ssl.SSLSession;
22
24
  import java.io.FileInputStream;
23
25
  import java.io.IOException;
26
+ import java.nio.Buffer;
24
27
  import java.nio.ByteBuffer;
25
28
  import java.security.KeyManagementException;
26
29
  import java.security.KeyStore;
27
30
  import java.security.KeyStoreException;
28
31
  import java.security.NoSuchAlgorithmException;
29
32
  import java.security.UnrecoverableKeyException;
33
+ import java.security.cert.CertificateEncodingException;
30
34
  import java.security.cert.CertificateException;
31
35
 
32
36
  import static javax.net.ssl.SSLEngineResult.Status;
@@ -62,7 +66,7 @@ public class MiniSSL extends RubyObject {
62
66
 
63
67
  public void clear() { buffer.clear(); }
64
68
  public void compact() { buffer.compact(); }
65
- public void flip() { buffer.flip(); }
69
+ public void flip() { ((Buffer) buffer).flip(); }
66
70
  public boolean hasRemaining() { return buffer.hasRemaining(); }
67
71
  public int position() { return buffer.position(); }
68
72
 
@@ -86,7 +90,7 @@ public class MiniSSL extends RubyObject {
86
90
  public void resize(int newCapacity) {
87
91
  if (newCapacity > buffer.capacity()) {
88
92
  ByteBuffer dstTmp = ByteBuffer.allocate(newCapacity);
89
- buffer.flip();
93
+ flip();
90
94
  dstTmp.put(buffer);
91
95
  buffer = dstTmp;
92
96
  } else {
@@ -98,7 +102,7 @@ public class MiniSSL extends RubyObject {
98
102
  * Drains the buffer to a ByteList, or returns null for an empty buffer
99
103
  */
100
104
  public ByteList asByteList() {
101
- buffer.flip();
105
+ flip();
102
106
  if (!buffer.hasRemaining()) {
103
107
  buffer.clear();
104
108
  return null;
@@ -155,7 +159,17 @@ public class MiniSSL extends RubyObject {
155
159
  sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
156
160
  engine = sslCtx.createSSLEngine();
157
161
 
158
- String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
162
+ String[] protocols;
163
+ if(miniSSLContext.callMethod(threadContext, "no_tlsv1").isTrue()) {
164
+ protocols = new String[] { "TLSv1.1", "TLSv1.2" };
165
+ } else {
166
+ protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
167
+ }
168
+
169
+ if(miniSSLContext.callMethod(threadContext, "no_tlsv1_1").isTrue()) {
170
+ protocols = new String[] { "TLSv1.2" };
171
+ }
172
+
159
173
  engine.setEnabledProtocols(protocols);
160
174
  engine.setUseClientMode(false);
161
175
 
@@ -167,6 +181,12 @@ public class MiniSSL extends RubyObject {
167
181
  engine.setNeedClientAuth(true);
168
182
  }
169
183
 
184
+ IRubyObject sslCipherListObject = miniSSLContext.callMethod(threadContext, "ssl_cipher_list");
185
+ if (!sslCipherListObject.isNil()) {
186
+ String[] sslCipherList = sslCipherListObject.convertToString().asJavaString().split(",");
187
+ engine.setEnabledCipherSuites(sslCipherList);
188
+ }
189
+
170
190
  SSLSession session = engine.getSession();
171
191
  inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
172
192
  outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize());
@@ -333,7 +353,11 @@ public class MiniSSL extends RubyObject {
333
353
  }
334
354
 
335
355
  @JRubyMethod
336
- public IRubyObject peercert() {
337
- return getRuntime().getNil();
356
+ public IRubyObject peercert() throws CertificateEncodingException {
357
+ try {
358
+ return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
359
+ } catch (SSLPeerUnverifiedException ex) {
360
+ return getRuntime().getNil();
361
+ }
338
362
  }
339
363
  }
@@ -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
@@ -200,6 +201,8 @@ void http_field(puma_parser* hp, const char *field, size_t flen,
200
201
  f = rb_str_new(hp->buf, new_size);
201
202
  }
202
203
 
204
+ while (vlen > 0 && isspace(value[vlen - 1])) vlen--;
205
+
203
206
  /* check for duplicate header */
204
207
  v = rb_hash_aref(hp->request, f);
205
208
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
4
 
3
5
  module OpenSSL
@@ -13,7 +15,11 @@ module OpenSSL
13
15
  ssl.accept if @start_immediately
14
16
  ssl
15
17
  rescue SSLError => ex
16
- sock.close
18
+ if ssl
19
+ ssl.close
20
+ else
21
+ sock.close
22
+ end
17
23
  raise ex
18
24
  end
19
25
  end
@@ -1,26 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  module App
5
+ # Check out {#call}'s source code to see what actions this web application
6
+ # can respond to.
3
7
  class Status
4
- def initialize(cli)
5
- @cli = cli
6
- @auth_token = nil
7
- end
8
8
  OK_STATUS = '{ "status": "ok" }'.freeze
9
9
 
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]]
10
+ def initialize(cli, token = nil)
11
+ @cli = cli
12
+ @auth_token = token
24
13
  end
25
14
 
26
15
  def call(env)
@@ -28,39 +17,66 @@ module Puma
28
17
  return rack_response(403, 'Invalid auth token', 'text/plain')
29
18
  end
30
19
 
20
+ if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
21
+ require 'json'
22
+ end
23
+
31
24
  case env['PATH_INFO']
32
25
  when /\/stop$/
33
26
  @cli.stop
34
- return rack_response(200, OK_STATUS)
27
+ rack_response(200, OK_STATUS)
35
28
 
36
29
  when /\/halt$/
37
30
  @cli.halt
38
- return rack_response(200, OK_STATUS)
31
+ rack_response(200, OK_STATUS)
39
32
 
40
33
  when /\/restart$/
41
34
  @cli.restart
42
- return rack_response(200, OK_STATUS)
35
+ rack_response(200, OK_STATUS)
43
36
 
44
37
  when /\/phased-restart$/
45
38
  if !@cli.phased_restart
46
- return rack_response(404, '{ "error": "phased restart not available" }')
39
+ rack_response(404, '{ "error": "phased restart not available" }')
47
40
  else
48
- return rack_response(200, OK_STATUS)
41
+ rack_response(200, OK_STATUS)
49
42
  end
50
43
 
51
44
  when /\/reload-worker-directory$/
52
45
  if !@cli.send(:reload_worker_directory)
53
- return rack_response(404, '{ "error": "reload_worker_directory not available" }')
46
+ rack_response(404, '{ "error": "reload_worker_directory not available" }')
54
47
  else
55
- return rack_response(200, OK_STATUS)
48
+ rack_response(200, OK_STATUS)
56
49
  end
57
50
 
51
+ when /\/gc$/
52
+ GC.start
53
+ rack_response(200, OK_STATUS)
54
+
55
+ when /\/gc-stats$/
56
+ rack_response(200, GC.stat.to_json)
57
+
58
58
  when /\/stats$/
59
- return rack_response(200, @cli.stats)
59
+ rack_response(200, @cli.stats)
60
60
  else
61
61
  rack_response 404, "Unsupported action", 'text/plain'
62
62
  end
63
63
  end
64
+
65
+ private
66
+
67
+ def authenticate(env)
68
+ return true unless @auth_token
69
+ env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
70
+ end
71
+
72
+ def rack_response(status, body, content_type='application/json')
73
+ headers = {
74
+ 'Content-Type' => content_type,
75
+ 'Content-Length' => body.bytesize.to_s
76
+ }
77
+
78
+ [status, headers, [body]]
79
+ end
64
80
  end
65
81
  end
66
82
  end
data/lib/puma/binder.rb CHANGED
@@ -1,8 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
4
  require 'socket'
3
5
 
4
6
  require 'puma/const'
5
7
  require 'puma/util'
8
+ require 'puma/minissl/context_builder'
6
9
 
7
10
  module Puma
8
11
  class Binder
@@ -40,7 +43,7 @@ module Puma
40
43
  @ios = []
41
44
  end
42
45
 
43
- attr_reader :listeners, :ios
46
+ attr_reader :ios
44
47
 
45
48
  def env(sock)
46
49
  @envs.fetch(sock, @proto_env)
@@ -48,7 +51,6 @@ module Puma
48
51
 
49
52
  def close
50
53
  @ios.each { |i| i.close }
51
- @unix_paths.each { |i| File.unlink i }
52
54
  end
53
55
 
54
56
  def import_from_env
@@ -90,19 +92,29 @@ module Puma
90
92
  case uri.scheme
91
93
  when "tcp"
92
94
  if fd = @inherited_fds.delete(str)
93
- logger.log "* Inherited #{str}"
94
95
  io = inherit_tcp_listener uri.host, uri.port, fd
96
+ logger.log "* Inherited #{str}"
95
97
  elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
96
- logger.log "* Activated #{str}"
97
98
  io = inherit_tcp_listener uri.host, uri.port, sock
99
+ logger.log "* Activated #{str}"
98
100
  else
99
101
  params = Util.parse_query uri.query
100
102
 
101
103
  opt = params.key?('low_latency')
102
104
  bak = params.fetch('backlog', 1024).to_i
103
105
 
104
- logger.log "* Listening on #{str}"
105
106
  io = add_tcp_listener uri.host, uri.port, opt, bak
107
+
108
+ @ios.each do |i|
109
+ next unless TCPServer === i
110
+ addr = if i.local_address.ipv6?
111
+ "[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
112
+ else
113
+ i.local_address.ip_unpack.join(':')
114
+ end
115
+
116
+ logger.log "* Listening on tcp://#{addr}"
117
+ end
106
118
  end
107
119
 
108
120
  @listeners << [str, io] if io
@@ -110,17 +122,15 @@ module Puma
110
122
  path = "#{uri.host}#{uri.path}".gsub("%20", " ")
111
123
 
112
124
  if fd = @inherited_fds.delete(str)
113
- logger.log "* Inherited #{str}"
114
125
  io = inherit_unix_listener path, fd
126
+ logger.log "* Inherited #{str}"
115
127
  elsif sock = @activated_sockets.delete([ :unix, path ])
116
- logger.log "* Activated #{str}"
117
128
  io = inherit_unix_listener path, sock
129
+ logger.log "* Activated #{str}"
118
130
  else
119
- logger.log "* Listening on #{str}"
120
-
121
131
  umask = nil
122
132
  mode = nil
123
- backlog = nil
133
+ backlog = 1024
124
134
 
125
135
  if uri.query
126
136
  params = Util.parse_query uri.query
@@ -139,74 +149,23 @@ module Puma
139
149
  end
140
150
 
141
151
  io = add_unix_listener path, umask, mode, backlog
152
+ logger.log "* Listening on #{str}"
142
153
  end
143
154
 
144
155
  @listeners << [str, io]
145
156
  when "ssl"
146
157
  params = Util.parse_query uri.query
147
- require 'puma/minissl'
148
-
149
- MiniSSL.check
150
-
151
- ctx = MiniSSL::Context.new
152
-
153
- if defined?(JRUBY_VERSION)
154
- unless params['keystore']
155
- @events.error "Please specify the Java keystore via 'keystore='"
156
- end
157
-
158
- ctx.keystore = params['keystore']
159
-
160
- unless params['keystore-pass']
161
- @events.error "Please specify the Java keystore password via 'keystore-pass='"
162
- end
163
-
164
- ctx.keystore_pass = params['keystore-pass']
165
- else
166
- unless params['key']
167
- @events.error "Please specify the SSL key via 'key='"
168
- end
169
-
170
- ctx.key = params['key']
171
-
172
- unless params['cert']
173
- @events.error "Please specify the SSL cert via 'cert='"
174
- end
175
-
176
- ctx.cert = params['cert']
177
-
178
- if ['peer', 'force_peer'].include?(params['verify_mode'])
179
- unless params['ca']
180
- @events.error "Please specify the SSL ca via 'ca='"
181
- end
182
- end
183
-
184
- ctx.ca = params['ca'] if params['ca']
185
- end
186
-
187
- if params['verify_mode']
188
- ctx.verify_mode = case params['verify_mode']
189
- when "peer"
190
- MiniSSL::VERIFY_PEER
191
- when "force_peer"
192
- MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
193
- when "none"
194
- MiniSSL::VERIFY_NONE
195
- else
196
- @events.error "Please specify a valid verify_mode="
197
- MiniSSL::VERIFY_NONE
198
- end
199
- end
158
+ ctx = MiniSSL::ContextBuilder.new(params, @events).context
200
159
 
201
160
  if fd = @inherited_fds.delete(str)
202
161
  logger.log "* Inherited #{str}"
203
162
  io = inherit_ssl_listener fd, ctx
204
163
  elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
205
- logger.log "* Activated #{str}"
206
164
  io = inherit_ssl_listener sock, ctx
165
+ logger.log "* Activated #{str}"
207
166
  else
208
- logger.log "* Listening on #{str}"
209
167
  io = add_ssl_listener uri.host, uri.port, ctx
168
+ logger.log "* Listening on #{str}"
210
169
  end
211
170
 
212
171
  @listeners << [str, io] if io
@@ -245,9 +204,10 @@ module Puma
245
204
  end
246
205
  end
247
206
 
248
- def localhost_addresses
249
- addrs = TCPSocket.gethostbyname "localhost"
250
- addrs[3..-1].uniq
207
+ def loopback_addresses
208
+ Socket.ip_address_list.select do |addrinfo|
209
+ addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
210
+ end.map { |addrinfo| addrinfo.ip_address }.uniq
251
211
  end
252
212
 
253
213
  # Tell the server to listen on host +host+, port +port+.
@@ -259,7 +219,7 @@ module Puma
259
219
  #
260
220
  def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
261
221
  if host == "localhost"
262
- localhost_addresses.each do |addr|
222
+ loopback_addresses.each do |addr|
263
223
  add_tcp_listener addr, port, optimize_for_latency, backlog
264
224
  end
265
225
  return
@@ -298,7 +258,7 @@ module Puma
298
258
  MiniSSL.check
299
259
 
300
260
  if host == "localhost"
301
- localhost_addresses.each do |addr|
261
+ loopback_addresses.each do |addr|
302
262
  add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
303
263
  end
304
264
  return
@@ -312,6 +272,7 @@ module Puma
312
272
  s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
313
273
  s.listen backlog
314
274
 
275
+
315
276
  ssl = MiniSSL::Server.new s, ctx
316
277
  env = @proto_env.dup
317
278
  env[HTTPS_KEY] = HTTPS
@@ -343,8 +304,8 @@ module Puma
343
304
 
344
305
  # Tell the server to listen on +path+ as a UNIX domain socket.
345
306
  #
346
- def add_unix_listener(path, umask=nil, mode=nil, backlog=nil)
347
- @unix_paths << path
307
+ def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
308
+ @unix_paths << path unless File.exist? path
348
309
 
349
310
  # Let anyone connect by default
350
311
  umask ||= 0
@@ -364,7 +325,7 @@ module Puma
364
325
  end
365
326
 
366
327
  s = UNIXServer.new(path)
367
- s.listen backlog if backlog
328
+ s.listen backlog
368
329
  @ios << s
369
330
  ensure
370
331
  File.umask old_mask
@@ -382,7 +343,7 @@ module Puma
382
343
  end
383
344
 
384
345
  def inherit_unix_listener(path, fd)
385
- @unix_paths << path
346
+ @unix_paths << path unless File.exist? path
386
347
 
387
348
  if fd.kind_of? TCPServer
388
349
  s = fd
@@ -398,5 +359,27 @@ module Puma
398
359
  s
399
360
  end
400
361
 
362
+ def close_listeners
363
+ @listeners.each do |l, io|
364
+ io.close
365
+ uri = URI.parse(l)
366
+ next unless uri.scheme == 'unix'
367
+ unix_path = "#{uri.host}#{uri.path}"
368
+ File.unlink unix_path if @unix_paths.include? unix_path
369
+ end
370
+ end
371
+
372
+ def close_unix_paths
373
+ @unix_paths.each { |up| File.unlink(up) if File.exist? up }
374
+ end
375
+
376
+ def redirects_for_restart
377
+ redirects = {:close_others => true}
378
+ @listeners.each_with_index do |(l, io), i|
379
+ ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
380
+ redirects[io.to_i] = io.to_i
381
+ end
382
+ redirects
383
+ end
401
384
  end
402
385
  end
data/lib/puma/cli.rb CHANGED
@@ -1,6 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
  require 'uri'
3
5
 
6
+ require 'puma'
4
7
  require 'puma/configuration'
5
8
  require 'puma/launcher'
6
9
  require 'puma/const'
@@ -83,6 +86,14 @@ module Puma
83
86
  raise UnsupportedOption
84
87
  end
85
88
 
89
+ def configure_control_url(command_line_arg)
90
+ if command_line_arg
91
+ @control_url = command_line_arg
92
+ elsif Puma.jruby?
93
+ unsupported "No default url available on JRuby"
94
+ end
95
+ end
96
+
86
97
  # Build the OptionParser object to handle the available options.
87
98
  #
88
99
 
@@ -97,13 +108,13 @@ module Puma
97
108
  file_config.load arg
98
109
  end
99
110
 
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
111
+ o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
112
+ configure_control_url(arg)
113
+ end
114
+
115
+ # alias --control-url for backwards-compatibility
116
+ o.on "--control URL", "DEPRECATED alias for --control-url" do |arg|
117
+ configure_control_url(arg)
107
118
  end
108
119
 
109
120
  o.on "--control-token TOKEN",
@@ -150,6 +161,10 @@ module Puma
150
161
  user_config.prune_bundler
151
162
  end
152
163
 
164
+ o.on "--extra-runtime-dependencies GEM1,GEM2", "Defines any extra needed gems when using --prune-bundler" do |arg|
165
+ user_config.extra_runtime_dependencies arg.split(',')
166
+ end
167
+
153
168
  o.on "-q", "--quiet", "Do not log requests internally (default true)" do
154
169
  user_config.quiet
155
170
  end
@@ -181,6 +196,10 @@ module Puma
181
196
  user_config.tcp_mode!
182
197
  end
183
198
 
199
+ o.on "--early-hints", "Enable early hints support" do
200
+ user_config.early_hints
201
+ end
202
+
184
203
  o.on "-V", "--version", "Print the version information" do
185
204
  puts "puma version #{Puma::Const::VERSION}"
186
205
  exit 0