puma 4.3.3-java → 5.0.0.beta2-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.
- checksums.yaml +4 -4
- data/History.md +79 -8
- data/LICENSE +23 -20
- data/README.md +18 -12
- data/docs/architecture.md +3 -3
- data/docs/deployment.md +9 -3
- data/docs/fork_worker.md +31 -0
- data/docs/jungle/README.md +13 -0
- data/{tools → docs}/jungle/rc.d/README.md +0 -0
- data/{tools → docs}/jungle/rc.d/puma +0 -0
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/{tools → docs}/jungle/upstart/README.md +0 -0
- data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
- data/{tools → docs}/jungle/upstart/puma.conf +0 -0
- data/docs/signals.md +5 -4
- data/docs/systemd.md +1 -63
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/extconf.rb +4 -3
- data/ext/puma_http11/http11_parser.c +3 -1
- data/ext/puma_http11/http11_parser.rl +3 -1
- data/ext/puma_http11/mini_ssl.c +12 -2
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +37 -6
- data/ext/puma_http11/puma_http11.c +3 -38
- data/lib/puma.rb +5 -0
- data/lib/puma/app/status.rb +18 -3
- data/lib/puma/binder.rb +66 -63
- data/lib/puma/cli.rb +7 -15
- data/lib/puma/client.rb +64 -14
- data/lib/puma/cluster.rb +183 -74
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +30 -42
- data/lib/puma/const.rb +2 -3
- data/lib/puma/control_cli.rb +27 -17
- data/lib/puma/detect.rb +8 -0
- data/lib/puma/dsl.rb +72 -36
- data/lib/puma/error_logger.rb +96 -0
- data/lib/puma/events.rb +33 -31
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +46 -31
- data/lib/puma/minissl.rb +47 -10
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +8 -3
- data/lib/puma/runner.rb +6 -35
- data/lib/puma/server.rb +138 -182
- data/lib/puma/single.rb +7 -64
- data/lib/puma/state_file.rb +6 -3
- data/lib/puma/thread_pool.rb +90 -49
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +18 -21
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
@@ -1,18 +1,16 @@
|
|
1
1
|
package puma;
|
2
2
|
|
3
3
|
import java.io.IOException;
|
4
|
-
|
4
|
+
|
5
5
|
import org.jruby.Ruby;
|
6
6
|
import org.jruby.runtime.load.BasicLibraryService;
|
7
7
|
|
8
8
|
import org.jruby.puma.Http11;
|
9
|
-
import org.jruby.puma.IOBuffer;
|
10
9
|
import org.jruby.puma.MiniSSL;
|
11
10
|
|
12
|
-
public class PumaHttp11Service implements BasicLibraryService {
|
11
|
+
public class PumaHttp11Service implements BasicLibraryService {
|
13
12
|
public boolean basicLoad(final Ruby runtime) throws IOException {
|
14
13
|
Http11.createHttp11(runtime);
|
15
|
-
IOBuffer.createIOBuffer(runtime);
|
16
14
|
MiniSSL.createMiniSSL(runtime);
|
17
15
|
return true;
|
18
16
|
}
|
data/ext/puma_http11/extconf.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'mkmf'
|
2
2
|
|
3
3
|
dir_config("puma_http11")
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
|
5
|
+
if $mingw && RUBY_VERSION >= '2.4'
|
6
|
+
append_cflags '-fstack-protector-strong -D_FORTIFY_SOURCE=2'
|
7
|
+
append_ldflags '-fstack-protector-strong -l:libssp.a'
|
7
8
|
have_library 'ssp'
|
8
9
|
end
|
9
10
|
|
@@ -14,12 +14,14 @@
|
|
14
14
|
|
15
15
|
/*
|
16
16
|
* capitalizes all lower-case ASCII characters,
|
17
|
-
* converts dashes to underscores.
|
17
|
+
* converts dashes to underscores, and underscores to commas.
|
18
18
|
*/
|
19
19
|
static void snake_upcase_char(char *c)
|
20
20
|
{
|
21
21
|
if (*c >= 'a' && *c <= 'z')
|
22
22
|
*c &= ~0x20;
|
23
|
+
else if (*c == '_')
|
24
|
+
*c = ',';
|
23
25
|
else if (*c == '-')
|
24
26
|
*c = '_';
|
25
27
|
}
|
@@ -12,12 +12,14 @@
|
|
12
12
|
|
13
13
|
/*
|
14
14
|
* capitalizes all lower-case ASCII characters,
|
15
|
-
* converts dashes to underscores.
|
15
|
+
* converts dashes to underscores, and underscores to commas.
|
16
16
|
*/
|
17
17
|
static void snake_upcase_char(char *c)
|
18
18
|
{
|
19
19
|
if (*c >= 'a' && *c <= 'z')
|
20
20
|
*c &= ~0x20;
|
21
|
+
else if (*c == '_')
|
22
|
+
*c = ',';
|
21
23
|
else if (*c == '-')
|
22
24
|
*c = '_';
|
23
25
|
}
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -301,6 +301,7 @@ void raise_error(SSL* ssl, int result) {
|
|
301
301
|
char msg[512];
|
302
302
|
const char* err_str;
|
303
303
|
int err = errno;
|
304
|
+
int mask = 4095;
|
304
305
|
int ssl_err = SSL_get_error(ssl, result);
|
305
306
|
int verify_err = (int) SSL_get_verify_result(ssl);
|
306
307
|
|
@@ -317,8 +318,8 @@ void raise_error(SSL* ssl, int result) {
|
|
317
318
|
} else {
|
318
319
|
err = (int) ERR_get_error();
|
319
320
|
ERR_error_string_n(err, buf, sizeof(buf));
|
320
|
-
|
321
|
-
|
321
|
+
int errexp = err & mask;
|
322
|
+
snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, errexp);
|
322
323
|
}
|
323
324
|
} else {
|
324
325
|
snprintf(msg, sizeof(msg), "Unknown OpenSSL error: %d", ssl_err);
|
@@ -462,6 +463,13 @@ VALUE engine_peercert(VALUE self) {
|
|
462
463
|
return rb_cert_buf;
|
463
464
|
}
|
464
465
|
|
466
|
+
static VALUE
|
467
|
+
engine_ssl_vers_st(VALUE self) {
|
468
|
+
ms_conn* conn;
|
469
|
+
Data_Get_Struct(self, ms_conn, conn);
|
470
|
+
return rb_ary_new3(2, rb_str_new2(SSL_get_version(conn->ssl)), rb_str_new2(SSL_state_string(conn->ssl)));
|
471
|
+
}
|
472
|
+
|
465
473
|
VALUE noop(VALUE self) {
|
466
474
|
return Qnil;
|
467
475
|
}
|
@@ -533,6 +541,8 @@ void Init_mini_ssl(VALUE puma) {
|
|
533
541
|
rb_define_method(eng, "init?", engine_init, 0);
|
534
542
|
|
535
543
|
rb_define_method(eng, "peercert", engine_peercert, 0);
|
544
|
+
|
545
|
+
rb_define_method(eng, "ssl_vers_st", engine_ssl_vers_st, 0);
|
536
546
|
}
|
537
547
|
|
538
548
|
#else
|
@@ -30,8 +30,8 @@ public class Http11 extends RubyObject {
|
|
30
30
|
public final static String MAX_REQUEST_URI_LENGTH_ERR = "HTTP element REQUEST_URI is longer than the 12288 allowed length.";
|
31
31
|
public final static int MAX_FRAGMENT_LENGTH = 1024;
|
32
32
|
public final static String MAX_FRAGMENT_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 1024 allowed length.";
|
33
|
-
public final static int MAX_REQUEST_PATH_LENGTH =
|
34
|
-
public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the
|
33
|
+
public final static int MAX_REQUEST_PATH_LENGTH = 8192;
|
34
|
+
public final static String MAX_REQUEST_PATH_LENGTH_ERR = "HTTP element REQUEST_PATH is longer than the 8192 allowed length.";
|
35
35
|
public final static int MAX_QUERY_STRING_LENGTH = 1024 * 10;
|
36
36
|
public final static String MAX_QUERY_STRING_LENGTH_ERR = "HTTP element QUERY_STRING is longer than the 10240 allowed length.";
|
37
37
|
public final static int MAX_HEADER_LENGTH = 1024 * (80 + 32);
|
@@ -197,7 +197,7 @@ public class Http11 extends RubyObject {
|
|
197
197
|
validateMaxLength(runtime, parser.nread,MAX_HEADER_LENGTH, MAX_HEADER_LENGTH_ERR);
|
198
198
|
|
199
199
|
if(hp.has_error()) {
|
200
|
-
throw newHTTPParserError(runtime, "Invalid HTTP format, parsing fails.");
|
200
|
+
throw newHTTPParserError(runtime, "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
|
201
201
|
} else {
|
202
202
|
return runtime.newFixnum(parser.nread);
|
203
203
|
}
|
@@ -120,6 +120,8 @@ public class MiniSSL extends RubyObject {
|
|
120
120
|
}
|
121
121
|
|
122
122
|
private SSLEngine engine;
|
123
|
+
private boolean closed;
|
124
|
+
private boolean handshake;
|
123
125
|
private MiniSSLBuffer inboundNetData;
|
124
126
|
private MiniSSLBuffer outboundAppData;
|
125
127
|
private MiniSSLBuffer outboundNetData;
|
@@ -157,6 +159,8 @@ public class MiniSSL extends RubyObject {
|
|
157
159
|
SSLContext sslCtx = SSLContext.getInstance("TLS");
|
158
160
|
|
159
161
|
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
162
|
+
closed = false;
|
163
|
+
handshake = false;
|
160
164
|
engine = sslCtx.createSSLEngine();
|
161
165
|
|
162
166
|
String[] protocols;
|
@@ -173,7 +177,7 @@ public class MiniSSL extends RubyObject {
|
|
173
177
|
engine.setEnabledProtocols(protocols);
|
174
178
|
engine.setUseClientMode(false);
|
175
179
|
|
176
|
-
long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue();
|
180
|
+
long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger("to_i").getLongValue();
|
177
181
|
if ((verify_mode & 0x1) != 0) { // 'peer'
|
178
182
|
engine.setWantClientAuth(true);
|
179
183
|
}
|
@@ -240,14 +244,21 @@ public class MiniSSL extends RubyObject {
|
|
240
244
|
// need to wait for more data to come in before we retry
|
241
245
|
retryOp = false;
|
242
246
|
break;
|
247
|
+
case CLOSED:
|
248
|
+
closed = true;
|
249
|
+
retryOp = false;
|
250
|
+
break;
|
243
251
|
default:
|
244
|
-
// other
|
252
|
+
// other case is OK. We're done here.
|
245
253
|
retryOp = false;
|
246
254
|
}
|
255
|
+
if (res.getHandshakeStatus() == HandshakeStatus.FINISHED) {
|
256
|
+
handshake = true;
|
257
|
+
}
|
247
258
|
}
|
248
259
|
|
249
260
|
// after each op, run any delegated tasks if needed
|
250
|
-
if(
|
261
|
+
if(res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
|
251
262
|
Runnable runnable;
|
252
263
|
while ((runnable = engine.getDelegatedTask()) != null) {
|
253
264
|
runnable.run();
|
@@ -271,13 +282,14 @@ public class MiniSSL extends RubyObject {
|
|
271
282
|
|
272
283
|
HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
|
273
284
|
boolean done = false;
|
285
|
+
SSLEngineResult res = null;
|
274
286
|
while (!done) {
|
275
287
|
switch (handshakeStatus) {
|
276
288
|
case NEED_WRAP:
|
277
|
-
doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
|
289
|
+
res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
|
278
290
|
break;
|
279
291
|
case NEED_UNWRAP:
|
280
|
-
|
292
|
+
res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
|
281
293
|
if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
|
282
294
|
// need more data before we can shake more hands
|
283
295
|
done = true;
|
@@ -286,7 +298,9 @@ public class MiniSSL extends RubyObject {
|
|
286
298
|
default:
|
287
299
|
done = true;
|
288
300
|
}
|
289
|
-
|
301
|
+
if (!done) {
|
302
|
+
handshakeStatus = res.getHandshakeStatus();
|
303
|
+
}
|
290
304
|
}
|
291
305
|
|
292
306
|
if (inboundNetData.hasRemaining()) {
|
@@ -360,4 +374,21 @@ public class MiniSSL extends RubyObject {
|
|
360
374
|
return getRuntime().getNil();
|
361
375
|
}
|
362
376
|
}
|
377
|
+
|
378
|
+
@JRubyMethod(name = "init?")
|
379
|
+
public IRubyObject isInit(ThreadContext context) {
|
380
|
+
return handshake ? getRuntime().getFalse() : getRuntime().getTrue();
|
381
|
+
}
|
382
|
+
|
383
|
+
@JRubyMethod
|
384
|
+
public IRubyObject shutdown() {
|
385
|
+
if (closed || engine.isInboundDone() && engine.isOutboundDone()) {
|
386
|
+
if (engine.isOutboundDone()) {
|
387
|
+
engine.closeOutbound();
|
388
|
+
}
|
389
|
+
return getRuntime().getTrue();
|
390
|
+
} else {
|
391
|
+
return getRuntime().getFalse();
|
392
|
+
}
|
393
|
+
}
|
363
394
|
}
|
@@ -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
|
@@ -53,7 +54,7 @@ DEF_MAX_LENGTH(FIELD_NAME, 256);
|
|
53
54
|
DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
|
54
55
|
DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
|
55
56
|
DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
|
56
|
-
DEF_MAX_LENGTH(REQUEST_PATH,
|
57
|
+
DEF_MAX_LENGTH(REQUEST_PATH, 8192);
|
57
58
|
DEF_MAX_LENGTH(QUERY_STRING, (1024 * 10));
|
58
59
|
DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
|
59
60
|
|
@@ -111,21 +112,6 @@ static struct common_field common_http_fields[] = {
|
|
111
112
|
# undef f
|
112
113
|
};
|
113
114
|
|
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
115
|
static void init_common_fields(void)
|
130
116
|
{
|
131
117
|
unsigned i;
|
@@ -142,28 +128,10 @@ static void init_common_fields(void)
|
|
142
128
|
}
|
143
129
|
rb_global_variable(&cf->value);
|
144
130
|
}
|
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
131
|
}
|
153
132
|
|
154
133
|
static VALUE find_common_field_value(const char *field, size_t flen)
|
155
134
|
{
|
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
135
|
unsigned i;
|
168
136
|
struct common_field *cf = common_http_fields;
|
169
137
|
for(i = 0; i < ARRAY_SIZE(common_http_fields); i++, cf++) {
|
@@ -171,7 +139,6 @@ static VALUE find_common_field_value(const char *field, size_t flen)
|
|
171
139
|
return cf->value;
|
172
140
|
}
|
173
141
|
return Qnil;
|
174
|
-
#endif /* !HAVE_QSORT_BSEARCH */
|
175
142
|
}
|
176
143
|
|
177
144
|
void http_field(puma_parser* hp, const char *field, size_t flen,
|
@@ -400,7 +367,7 @@ VALUE HttpParser_execute(VALUE self, VALUE req_hash, VALUE data, VALUE start)
|
|
400
367
|
VALIDATE_MAX_LENGTH(puma_parser_nread(http), HEADER);
|
401
368
|
|
402
369
|
if(puma_parser_has_error(http)) {
|
403
|
-
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails.");
|
370
|
+
rb_raise(eHttpParserError, "%s", "Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?");
|
404
371
|
} else {
|
405
372
|
return INT2FIX(puma_parser_nread(http));
|
406
373
|
}
|
@@ -467,7 +434,6 @@ VALUE HttpParser_body(VALUE self) {
|
|
467
434
|
return http->body;
|
468
435
|
}
|
469
436
|
|
470
|
-
void Init_io_buffer(VALUE puma);
|
471
437
|
void Init_mini_ssl(VALUE mod);
|
472
438
|
|
473
439
|
void Init_puma_http11()
|
@@ -497,6 +463,5 @@ void Init_puma_http11()
|
|
497
463
|
rb_define_method(cHttpParser, "body", HttpParser_body, 0);
|
498
464
|
init_common_fields();
|
499
465
|
|
500
|
-
Init_io_buffer(mPuma);
|
501
466
|
Init_mini_ssl(mPuma);
|
502
467
|
}
|
data/lib/puma.rb
CHANGED
data/lib/puma/app/status.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'json'
|
4
|
-
|
5
3
|
module Puma
|
6
4
|
module App
|
7
5
|
# Check out {#call}'s source code to see what actions this web application
|
@@ -19,6 +17,10 @@ module Puma
|
|
19
17
|
return rack_response(403, 'Invalid auth token', 'text/plain')
|
20
18
|
end
|
21
19
|
|
20
|
+
if env['PATH_INFO'] =~ /\/(gc-stats|stats|thread-backtraces)$/
|
21
|
+
require 'json'
|
22
|
+
end
|
23
|
+
|
22
24
|
case env['PATH_INFO']
|
23
25
|
when /\/stop$/
|
24
26
|
@cli.stop
|
@@ -54,7 +56,20 @@ module Puma
|
|
54
56
|
rack_response(200, GC.stat.to_json)
|
55
57
|
|
56
58
|
when /\/stats$/
|
57
|
-
rack_response(200, @cli.stats)
|
59
|
+
rack_response(200, @cli.stats.to_json)
|
60
|
+
|
61
|
+
when /\/thread-backtraces$/
|
62
|
+
backtraces = []
|
63
|
+
@cli.thread_status do |name, backtrace|
|
64
|
+
backtraces << { name: name, backtrace: backtrace }
|
65
|
+
end
|
66
|
+
|
67
|
+
rack_response(200, backtraces.to_json)
|
68
|
+
|
69
|
+
when /\/refork$/
|
70
|
+
Process.kill "SIGURG", $$
|
71
|
+
rack_response(200, OK_STATUS)
|
72
|
+
|
58
73
|
else
|
59
74
|
rack_response 404, "Unsupported action", 'text/plain'
|
60
75
|
end
|
data/lib/puma/binder.rb
CHANGED
@@ -6,14 +6,15 @@ require 'socket'
|
|
6
6
|
require 'puma/const'
|
7
7
|
require 'puma/util'
|
8
8
|
require 'puma/minissl/context_builder'
|
9
|
+
require 'puma/configuration'
|
9
10
|
|
10
11
|
module Puma
|
11
12
|
class Binder
|
12
13
|
include Puma::Const
|
13
14
|
|
14
|
-
RACK_VERSION = [1,
|
15
|
+
RACK_VERSION = [1,6].freeze
|
15
16
|
|
16
|
-
def initialize(events)
|
17
|
+
def initialize(events, conf = Configuration.new)
|
17
18
|
@events = events
|
18
19
|
@listeners = []
|
19
20
|
@inherited_fds = {}
|
@@ -23,8 +24,8 @@ module Puma
|
|
23
24
|
@proto_env = {
|
24
25
|
"rack.version".freeze => RACK_VERSION,
|
25
26
|
"rack.errors".freeze => events.stderr,
|
26
|
-
"rack.multithread".freeze =>
|
27
|
-
"rack.multiprocess".freeze =>
|
27
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
28
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
28
29
|
"rack.run_once".freeze => false,
|
29
30
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
30
31
|
|
@@ -43,7 +44,8 @@ module Puma
|
|
43
44
|
@ios = []
|
44
45
|
end
|
45
46
|
|
46
|
-
attr_reader :ios
|
47
|
+
attr_reader :ios, :listeners, :unix_paths, :proto_env, :envs, :activated_sockets, :inherited_fds
|
48
|
+
attr_writer :ios, :listeners
|
47
49
|
|
48
50
|
def env(sock)
|
49
51
|
@envs.fetch(sock, @proto_env)
|
@@ -53,40 +55,39 @@ module Puma
|
|
53
55
|
@ios.each { |i| i.close }
|
54
56
|
end
|
55
57
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
fd = num + 3
|
67
|
-
sock = TCPServer.for_fd(fd)
|
68
|
-
begin
|
69
|
-
key = [ :unix, Socket.unpack_sockaddr_un(sock.getsockname) ]
|
70
|
-
rescue ArgumentError
|
71
|
-
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
72
|
-
if addr =~ /\:/
|
73
|
-
addr = "[#{addr}]"
|
74
|
-
end
|
75
|
-
key = [ :tcp, addr, port ]
|
76
|
-
end
|
77
|
-
@activated_sockets[key] = sock
|
78
|
-
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
79
|
-
end
|
80
|
-
remove << k << 'LISTEN_PID'
|
81
|
-
end
|
82
|
-
end
|
58
|
+
def connected_ports
|
59
|
+
ios.map { |io| io.addr[1] }.uniq
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_inherited_fds(env_hash)
|
63
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
64
|
+
fd, url = v.split(":", 2)
|
65
|
+
@inherited_fds[url] = fd.to_i
|
66
|
+
end.keys # pass keys back for removal
|
67
|
+
end
|
83
68
|
|
84
|
-
|
85
|
-
|
69
|
+
# systemd socket activation.
|
70
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
71
|
+
# LISTEN_PID = PID of the service process, aka us
|
72
|
+
# see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
73
|
+
def create_activated_fds(env_hash)
|
74
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
75
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
76
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
77
|
+
key = begin # Try to parse as a path
|
78
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
79
|
+
rescue ArgumentError # Try to parse as a port/ip
|
80
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
81
|
+
addr = "[#{addr}]" if addr =~ /\:/
|
82
|
+
[:tcp, addr, port]
|
83
|
+
end
|
84
|
+
@activated_sockets[key] = sock
|
85
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
86
86
|
end
|
87
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
87
88
|
end
|
88
89
|
|
89
|
-
def parse(binds, logger)
|
90
|
+
def parse(binds, logger, log_msg = 'Listening')
|
90
91
|
binds.each do |str|
|
91
92
|
uri = URI.parse str
|
92
93
|
case uri.scheme
|
@@ -113,7 +114,7 @@ module Puma
|
|
113
114
|
i.local_address.ip_unpack.join(':')
|
114
115
|
end
|
115
116
|
|
116
|
-
logger.log "*
|
117
|
+
logger.log "* #{log_msg} on http://#{addr}"
|
117
118
|
end
|
118
119
|
end
|
119
120
|
|
@@ -149,7 +150,7 @@ module Puma
|
|
149
150
|
end
|
150
151
|
|
151
152
|
io = add_unix_listener path, umask, mode, backlog
|
152
|
-
logger.log "*
|
153
|
+
logger.log "* #{log_msg} on #{str}"
|
153
154
|
end
|
154
155
|
|
155
156
|
@listeners << [str, io]
|
@@ -204,12 +205,6 @@ module Puma
|
|
204
205
|
end
|
205
206
|
end
|
206
207
|
|
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
|
211
|
-
end
|
212
|
-
|
213
208
|
# Tell the server to listen on host +host+, port +port+.
|
214
209
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
215
210
|
# will be optimized for latency over throughput.
|
@@ -226,20 +221,17 @@ module Puma
|
|
226
221
|
end
|
227
222
|
|
228
223
|
host = host[1..-2] if host and host[0..0] == '['
|
229
|
-
|
224
|
+
tcp_server = TCPServer.new(host, port)
|
230
225
|
if optimize_for_latency
|
231
|
-
|
226
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
232
227
|
end
|
233
|
-
|
234
|
-
|
235
|
-
@connected_port = s.addr[1]
|
228
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
229
|
+
tcp_server.listen backlog
|
236
230
|
|
237
|
-
@ios <<
|
238
|
-
|
231
|
+
@ios << tcp_server
|
232
|
+
tcp_server
|
239
233
|
end
|
240
234
|
|
241
|
-
attr_reader :connected_port
|
242
|
-
|
243
235
|
def inherit_tcp_listener(host, port, fd)
|
244
236
|
if fd.kind_of? TCPServer
|
245
237
|
s = fd
|
@@ -360,26 +352,37 @@ module Puma
|
|
360
352
|
end
|
361
353
|
|
362
354
|
def close_listeners
|
363
|
-
|
364
|
-
io.close
|
355
|
+
listeners.each do |l, io|
|
356
|
+
io.close unless io.closed? # Ruby 2.2 issue
|
365
357
|
uri = URI.parse(l)
|
366
358
|
next unless uri.scheme == 'unix'
|
367
359
|
unix_path = "#{uri.host}#{uri.path}"
|
368
|
-
File.unlink unix_path if
|
360
|
+
File.unlink unix_path if unix_paths.include? unix_path
|
369
361
|
end
|
370
362
|
end
|
371
363
|
|
372
|
-
def
|
373
|
-
|
364
|
+
def redirects_for_restart
|
365
|
+
redirects = listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
366
|
+
redirects[:close_others] = true
|
367
|
+
redirects
|
374
368
|
end
|
375
369
|
|
376
|
-
def
|
377
|
-
|
378
|
-
|
379
|
-
ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
|
380
|
-
redirects[io.to_i] = io.to_i
|
370
|
+
def redirects_for_restart_env
|
371
|
+
listeners.each_with_object({}).with_index do |(listen, memo), i|
|
372
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
381
373
|
end
|
382
|
-
|
374
|
+
end
|
375
|
+
|
376
|
+
private
|
377
|
+
|
378
|
+
def loopback_addresses
|
379
|
+
Socket.ip_address_list.select do |addrinfo|
|
380
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
381
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
382
|
+
end
|
383
|
+
|
384
|
+
def socket_activation_fd(int)
|
385
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
383
386
|
end
|
384
387
|
end
|
385
388
|
end
|