puma 3.12.1 → 4.1.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.
- checksums.yaml +4 -4
- data/History.md +75 -5
- data/README.md +90 -33
- data/docs/architecture.md +1 -0
- data/docs/deployment.md +24 -4
- data/docs/restart.md +4 -2
- data/docs/systemd.md +27 -9
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/extconf.rb +8 -0
- data/ext/puma_http11/mini_ssl.c +78 -8
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -4
- data/lib/puma.rb +2 -0
- data/lib/puma/accept_nonblock.rb +2 -0
- data/lib/puma/app/status.rb +7 -2
- data/lib/puma/binder.rb +11 -1
- data/lib/puma/client.rb +64 -30
- data/lib/puma/cluster.rb +44 -17
- data/lib/puma/configuration.rb +4 -3
- data/lib/puma/const.rb +9 -2
- data/lib/puma/control_cli.rb +10 -2
- data/lib/puma/dsl.rb +280 -74
- data/lib/puma/events.rb +6 -3
- data/lib/puma/io_buffer.rb +1 -6
- data/lib/puma/launcher.rb +15 -14
- data/lib/puma/minissl.rb +35 -17
- data/lib/puma/plugin/tmp_restart.rb +2 -0
- data/lib/puma/rack/builder.rb +2 -0
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +104 -53
- data/lib/puma/runner.rb +2 -1
- data/lib/puma/server.rb +44 -36
- data/lib/puma/single.rb +3 -3
- data/lib/puma/thread_pool.rb +5 -1
- data/lib/puma/util.rb +1 -6
- data/lib/rack/handler/puma.rb +3 -1
- data/tools/jungle/init.d/puma +6 -6
- metadata +22 -11
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/java_io_buffer.rb +0 -47
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
data/docs/systemd.md
CHANGED
@@ -32,21 +32,26 @@ Type=simple
|
|
32
32
|
# Preferably configure a non-privileged user
|
33
33
|
# User=
|
34
34
|
|
35
|
-
# The path to the
|
36
|
-
# Also replace the "<
|
37
|
-
|
35
|
+
# The path to the your application code root directory.
|
36
|
+
# Also replace the "<YOUR_APP_PATH>" place holders below with this path.
|
37
|
+
# Example /home/username/myapp
|
38
|
+
WorkingDirectory=<YOUR_APP_PATH>
|
38
39
|
|
39
40
|
# Helpful for debugging socket activation, etc.
|
40
41
|
# Environment=PUMA_DEBUG=1
|
41
42
|
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
ExecStart
|
43
|
+
# SystemD will not run puma even if it is in your path. You must specify
|
44
|
+
# an absolute URL to puma. For example /usr/local/bin/puma
|
45
|
+
# Alternatively, create a binstub with `bundle binstubs puma --path ./sbin` in the WorkingDirectory
|
46
|
+
ExecStart=/<FULLPATH>/bin/puma -C <YOUR_APP_PATH>/puma.rb
|
47
|
+
|
48
|
+
# Variant: Rails start.
|
49
|
+
# ExecStart=/<FULLPATH>/bin/puma -C <YOUR_APP_PATH>/config/puma.rb ../config.ru
|
46
50
|
|
47
|
-
# Variant: Use config file with `bind` directives instead:
|
48
|
-
# ExecStart=<WD>/sbin/puma -C config.rb
|
49
51
|
# Variant: Use `bundle exec --keep-file-descriptors puma` instead of binstub
|
52
|
+
# Variant: Specify directives inline.
|
53
|
+
# ExecStart=/<FULLPATH>/puma -b tcp://0.0.0.0:9292 -b ssl://0.0.0.0:9293?key=key.pem&cert=cert.pem
|
54
|
+
|
50
55
|
|
51
56
|
Restart=always
|
52
57
|
|
@@ -66,6 +71,13 @@ listening sockets open across puma restarts and achieves graceful
|
|
66
71
|
restarts, including when upgraded puma, and is compatible with both
|
67
72
|
clustered mode and application preload.
|
68
73
|
|
74
|
+
**Note:** Any wrapper scripts which `exec`, or other indirections in
|
75
|
+
`ExecStart`, may result in activated socket file descriptors being closed
|
76
|
+
before they reach the puma master process. For example, if using `bundle exec`,
|
77
|
+
pass the `--keep-file-descriptors` flag. `bundle exec` can be avoided by using a
|
78
|
+
`puma` executable generated by `bundle binstubs puma`. This is tracked in
|
79
|
+
[#1499].
|
80
|
+
|
69
81
|
**Note:** Socket activation doesn't currently work on jruby. This is
|
70
82
|
tracked in [#1367].
|
71
83
|
|
@@ -247,6 +259,12 @@ PIDFile=<WD>/shared/tmp/pids/puma.pid
|
|
247
259
|
# reconsider if you actually need the forking config.
|
248
260
|
Restart=no
|
249
261
|
|
262
|
+
# `puma_ctl restart` wouldn't work without this. It's because `pumactl`
|
263
|
+
# changes PID on restart and systemd stops the service afterwards
|
264
|
+
# because of the PID change. This option prevents stopping after PID
|
265
|
+
# change.
|
266
|
+
RemainAfterExit=yes
|
267
|
+
|
250
268
|
[Install]
|
251
269
|
WantedBy=multi-user.target
|
252
270
|
~~~~
|
@@ -6,11 +6,13 @@ 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;
|
9
10
|
import org.jruby.puma.MiniSSL;
|
10
11
|
|
11
12
|
public class PumaHttp11Service implements BasicLibraryService {
|
12
13
|
public boolean basicLoad(final Ruby runtime) throws IOException {
|
13
14
|
Http11.createHttp11(runtime);
|
15
|
+
IOBuffer.createIOBuffer(runtime);
|
14
16
|
MiniSSL.createMiniSSL(runtime);
|
15
17
|
return true;
|
16
18
|
}
|
data/ext/puma_http11/extconf.rb
CHANGED
@@ -9,6 +9,14 @@ unless ENV["DISABLE_SSL"]
|
|
9
9
|
%w'ssl ssleay32'.find {|ssl| have_library(ssl, 'SSL_CTX_new')}
|
10
10
|
|
11
11
|
have_header "openssl/bio.h"
|
12
|
+
|
13
|
+
# below is yes for 1.0.2 & later
|
14
|
+
have_func "DTLS_method" , "openssl/ssl.h"
|
15
|
+
|
16
|
+
# below are yes for 1.1.0 & later, may need to check func rather than macro
|
17
|
+
# with versions after 1.1.1
|
18
|
+
have_func "TLS_server_method" , "openssl/ssl.h"
|
19
|
+
have_macro "SSL_CTX_set_min_proto_version", "openssl/ssl.h"
|
12
20
|
end
|
13
21
|
end
|
14
22
|
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -142,6 +142,7 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
142
142
|
VALUE obj;
|
143
143
|
SSL_CTX* ctx;
|
144
144
|
SSL* ssl;
|
145
|
+
int min, ssl_options;
|
145
146
|
|
146
147
|
ms_conn* conn = engine_alloc(self, &obj);
|
147
148
|
|
@@ -164,7 +165,17 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
164
165
|
ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
|
165
166
|
VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
|
166
167
|
|
168
|
+
ID sym_no_tlsv1 = rb_intern("no_tlsv1");
|
169
|
+
VALUE no_tlsv1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1, 0);
|
170
|
+
|
171
|
+
ID sym_no_tlsv1_1 = rb_intern("no_tlsv1_1");
|
172
|
+
VALUE no_tlsv1_1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1_1, 0);
|
173
|
+
|
174
|
+
#ifdef HAVE_TLS_SERVER_METHOD
|
175
|
+
ctx = SSL_CTX_new(TLS_server_method());
|
176
|
+
#else
|
167
177
|
ctx = SSL_CTX_new(SSLv23_server_method());
|
178
|
+
#endif
|
168
179
|
conn->ctx = ctx;
|
169
180
|
|
170
181
|
SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
|
@@ -175,7 +186,36 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
175
186
|
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
|
176
187
|
}
|
177
188
|
|
178
|
-
|
189
|
+
ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
|
190
|
+
|
191
|
+
#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
|
192
|
+
if (RTEST(no_tlsv1_1)) {
|
193
|
+
min = TLS1_2_VERSION;
|
194
|
+
}
|
195
|
+
else if (RTEST(no_tlsv1)) {
|
196
|
+
min = TLS1_1_VERSION;
|
197
|
+
}
|
198
|
+
else {
|
199
|
+
min = TLS1_VERSION;
|
200
|
+
}
|
201
|
+
|
202
|
+
SSL_CTX_set_min_proto_version(ctx, min);
|
203
|
+
|
204
|
+
SSL_CTX_set_options(ctx, ssl_options);
|
205
|
+
|
206
|
+
#else
|
207
|
+
/* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */
|
208
|
+
ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;
|
209
|
+
|
210
|
+
if (RTEST(no_tlsv1)) {
|
211
|
+
ssl_options |= SSL_OP_NO_TLSv1;
|
212
|
+
}
|
213
|
+
if(RTEST(no_tlsv1_1)) {
|
214
|
+
ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
|
215
|
+
}
|
216
|
+
SSL_CTX_set_options(ctx, ssl_options);
|
217
|
+
#endif
|
218
|
+
|
179
219
|
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
180
220
|
|
181
221
|
if (!NIL_P(ssl_cipher_filter)) {
|
@@ -189,12 +229,18 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
189
229
|
DH *dh = get_dh1024();
|
190
230
|
SSL_CTX_set_tmp_dh(ctx, dh);
|
191
231
|
|
192
|
-
#
|
193
|
-
|
232
|
+
#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
233
|
+
// Remove this case if OpenSSL 1.0.1 (now EOL) support is no
|
234
|
+
// longer needed.
|
235
|
+
EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
194
236
|
if (ecdh) {
|
195
237
|
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
196
238
|
EC_KEY_free(ecdh);
|
197
239
|
}
|
240
|
+
#elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
241
|
+
// Prior to OpenSSL 1.1.0, servers must manually enable server-side ECDH
|
242
|
+
// negotiation.
|
243
|
+
SSL_CTX_set_ecdh_auto(ctx, 1);
|
198
244
|
#endif
|
199
245
|
|
200
246
|
ssl = SSL_new(ctx);
|
@@ -216,8 +262,11 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
216
262
|
VALUE engine_init_client(VALUE klass) {
|
217
263
|
VALUE obj;
|
218
264
|
ms_conn* conn = engine_alloc(klass, &obj);
|
219
|
-
|
265
|
+
#ifdef HAVE_DTLS_METHOD
|
266
|
+
conn->ctx = SSL_CTX_new(DTLS_method());
|
267
|
+
#else
|
220
268
|
conn->ctx = SSL_CTX_new(DTLSv1_method());
|
269
|
+
#endif
|
221
270
|
conn->ssl = SSL_new(conn->ctx);
|
222
271
|
SSL_set_app_data(conn->ssl, NULL);
|
223
272
|
SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
|
@@ -436,14 +485,35 @@ void Init_mini_ssl(VALUE puma) {
|
|
436
485
|
// OpenSSL Build / Runtime/Load versions
|
437
486
|
|
438
487
|
/* Version of OpenSSL that Puma was compiled with */
|
439
|
-
|
488
|
+
rb_define_const(mod, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT));
|
440
489
|
|
441
490
|
#if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
|
442
|
-
|
443
|
-
|
491
|
+
/* Version of OpenSSL that Puma loaded with */
|
492
|
+
rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION)));
|
444
493
|
#else
|
445
|
-
|
494
|
+
rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION)));
|
446
495
|
#endif
|
496
|
+
|
497
|
+
#if defined(OPENSSL_NO_SSL3) || defined(OPENSSL_NO_SSL3_METHOD)
|
498
|
+
/* True if SSL3 is not available */
|
499
|
+
rb_define_const(mod, "OPENSSL_NO_SSL3", Qtrue);
|
500
|
+
#else
|
501
|
+
rb_define_const(mod, "OPENSSL_NO_SSL3", Qfalse);
|
502
|
+
#endif
|
503
|
+
|
504
|
+
#if defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD)
|
505
|
+
/* True if TLS1 is not available */
|
506
|
+
rb_define_const(mod, "OPENSSL_NO_TLS1", Qtrue);
|
507
|
+
#else
|
508
|
+
rb_define_const(mod, "OPENSSL_NO_TLS1", Qfalse);
|
509
|
+
#endif
|
510
|
+
|
511
|
+
#if defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD)
|
512
|
+
/* True if TLS1_1 is not available */
|
513
|
+
rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qtrue);
|
514
|
+
#else
|
515
|
+
rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qfalse);
|
516
|
+
#endif
|
447
517
|
|
448
518
|
rb_define_singleton_method(mod, "check", noop, 0);
|
449
519
|
|
@@ -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
|
+
}
|
@@ -23,6 +23,7 @@ import javax.net.ssl.SSLPeerUnverifiedException;
|
|
23
23
|
import javax.net.ssl.SSLSession;
|
24
24
|
import java.io.FileInputStream;
|
25
25
|
import java.io.IOException;
|
26
|
+
import java.nio.Buffer;
|
26
27
|
import java.nio.ByteBuffer;
|
27
28
|
import java.security.KeyManagementException;
|
28
29
|
import java.security.KeyStore;
|
@@ -65,7 +66,7 @@ public class MiniSSL extends RubyObject {
|
|
65
66
|
|
66
67
|
public void clear() { buffer.clear(); }
|
67
68
|
public void compact() { buffer.compact(); }
|
68
|
-
public void flip() { buffer.flip(); }
|
69
|
+
public void flip() { ((Buffer) buffer).flip(); }
|
69
70
|
public boolean hasRemaining() { return buffer.hasRemaining(); }
|
70
71
|
public int position() { return buffer.position(); }
|
71
72
|
|
@@ -89,7 +90,7 @@ public class MiniSSL extends RubyObject {
|
|
89
90
|
public void resize(int newCapacity) {
|
90
91
|
if (newCapacity > buffer.capacity()) {
|
91
92
|
ByteBuffer dstTmp = ByteBuffer.allocate(newCapacity);
|
92
|
-
|
93
|
+
flip();
|
93
94
|
dstTmp.put(buffer);
|
94
95
|
buffer = dstTmp;
|
95
96
|
} else {
|
@@ -101,7 +102,7 @@ public class MiniSSL extends RubyObject {
|
|
101
102
|
* Drains the buffer to a ByteList, or returns null for an empty buffer
|
102
103
|
*/
|
103
104
|
public ByteList asByteList() {
|
104
|
-
|
105
|
+
flip();
|
105
106
|
if (!buffer.hasRemaining()) {
|
106
107
|
buffer.clear();
|
107
108
|
return null;
|
@@ -158,7 +159,17 @@ public class MiniSSL extends RubyObject {
|
|
158
159
|
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
159
160
|
engine = sslCtx.createSSLEngine();
|
160
161
|
|
161
|
-
String[] protocols
|
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
|
+
|
162
173
|
engine.setEnabledProtocols(protocols);
|
163
174
|
engine.setUseClientMode(false);
|
164
175
|
|
data/lib/puma.rb
CHANGED
data/lib/puma/accept_nonblock.rb
CHANGED
data/lib/puma/app/status.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
1
5
|
module Puma
|
2
6
|
module App
|
7
|
+
# Check out {#call}'s source code to see what actions this web application
|
8
|
+
# can respond to.
|
3
9
|
class Status
|
4
10
|
def initialize(cli)
|
5
11
|
@cli = cli
|
@@ -60,8 +66,7 @@ module Puma
|
|
60
66
|
return rack_response(200, OK_STATUS)
|
61
67
|
|
62
68
|
when /\/gc-stats$/
|
63
|
-
|
64
|
-
return rack_response(200, json)
|
69
|
+
return rack_response(200, GC.stat.to_json)
|
65
70
|
|
66
71
|
when /\/stats$/
|
67
72
|
return rack_response(200, @cli.stats)
|
data/lib/puma/binder.rb
CHANGED
@@ -50,7 +50,14 @@ module Puma
|
|
50
50
|
|
51
51
|
def close
|
52
52
|
@ios.each { |i| i.close }
|
53
|
-
@unix_paths.each
|
53
|
+
@unix_paths.each do |i|
|
54
|
+
# Errno::ENOENT is intermittently raised
|
55
|
+
begin
|
56
|
+
unix_socket = UNIXSocket.new i
|
57
|
+
unix_socket.close
|
58
|
+
rescue Errno::ENOENT
|
59
|
+
end
|
60
|
+
end
|
54
61
|
end
|
55
62
|
|
56
63
|
def import_from_env
|
@@ -187,6 +194,9 @@ module Puma
|
|
187
194
|
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
188
195
|
end
|
189
196
|
|
197
|
+
ctx.no_tlsv1 = true if params['no_tlsv1'] == 'true'
|
198
|
+
ctx.no_tlsv1_1 = true if params['no_tlsv1_1'] == 'true'
|
199
|
+
|
190
200
|
if params['verify_mode']
|
191
201
|
ctx.verify_mode = case params['verify_mode']
|
192
202
|
when "peer"
|
data/lib/puma/client.rb
CHANGED
@@ -27,9 +27,10 @@ module Puma
|
|
27
27
|
# For example a web request from a browser or from CURL. This
|
28
28
|
#
|
29
29
|
# An instance of `Puma::Client` can be used as if it were an IO object
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
30
|
+
# by the reactor, that's because the latter is expected to call `#to_io`
|
31
|
+
# on any non-IO objects it polls. For example nio4r internally calls
|
32
|
+
# `IO::try_convert` (which may call `#to_io`) when a new socket is
|
33
|
+
# registered.
|
33
34
|
#
|
34
35
|
# Instances of this class are responsible for knowing if
|
35
36
|
# the header and body are fully buffered via the `try_to_finish` method.
|
@@ -54,6 +55,7 @@ module Puma
|
|
54
55
|
@ready = false
|
55
56
|
|
56
57
|
@body = nil
|
58
|
+
@body_read_start = nil
|
57
59
|
@buffer = nil
|
58
60
|
@tempfile = nil
|
59
61
|
|
@@ -64,6 +66,10 @@ module Puma
|
|
64
66
|
|
65
67
|
@peerip = nil
|
66
68
|
@remote_addr_header = nil
|
69
|
+
|
70
|
+
@body_remain = 0
|
71
|
+
|
72
|
+
@in_last_chunk = false
|
67
73
|
end
|
68
74
|
|
69
75
|
attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
|
@@ -102,6 +108,9 @@ module Puma
|
|
102
108
|
@tempfile = nil
|
103
109
|
@parsed_bytes = 0
|
104
110
|
@ready = false
|
111
|
+
@body_remain = 0
|
112
|
+
@peerip = nil
|
113
|
+
@in_last_chunk = false
|
105
114
|
|
106
115
|
if @buffer
|
107
116
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
@@ -114,9 +123,16 @@ module Puma
|
|
114
123
|
end
|
115
124
|
|
116
125
|
return false
|
117
|
-
|
118
|
-
|
119
|
-
|
126
|
+
else
|
127
|
+
begin
|
128
|
+
if fast_check &&
|
129
|
+
IO.select([@to_io], nil, nil, FAST_TRACK_KA_TIMEOUT)
|
130
|
+
return try_to_finish
|
131
|
+
end
|
132
|
+
rescue IOError
|
133
|
+
# swallow it
|
134
|
+
end
|
135
|
+
|
120
136
|
end
|
121
137
|
end
|
122
138
|
|
@@ -147,10 +163,13 @@ module Puma
|
|
147
163
|
def decode_chunk(chunk)
|
148
164
|
if @partial_part_left > 0
|
149
165
|
if @partial_part_left <= chunk.size
|
150
|
-
|
166
|
+
if @partial_part_left > 2
|
167
|
+
@body << chunk[0..(@partial_part_left-3)] # skip the \r\n
|
168
|
+
end
|
151
169
|
chunk = chunk[@partial_part_left..-1]
|
170
|
+
@partial_part_left = 0
|
152
171
|
else
|
153
|
-
@body << chunk
|
172
|
+
@body << chunk if @partial_part_left > 2 # don't include the last \r\n
|
154
173
|
@partial_part_left -= chunk.size
|
155
174
|
return false
|
156
175
|
end
|
@@ -168,13 +187,20 @@ module Puma
|
|
168
187
|
if line.end_with?("\r\n")
|
169
188
|
len = line.strip.to_i(16)
|
170
189
|
if len == 0
|
190
|
+
@in_last_chunk = true
|
171
191
|
@body.rewind
|
172
192
|
rest = io.read
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
193
|
+
last_crlf_size = "\r\n".bytesize
|
194
|
+
if rest.bytesize < last_crlf_size
|
195
|
+
@buffer = nil
|
196
|
+
@partial_part_left = last_crlf_size - rest.bytesize
|
197
|
+
return false
|
198
|
+
else
|
199
|
+
@buffer = rest[last_crlf_size..-1]
|
200
|
+
@buffer = nil if @buffer.empty?
|
201
|
+
set_ready
|
202
|
+
return true
|
203
|
+
end
|
178
204
|
end
|
179
205
|
|
180
206
|
len += 2
|
@@ -204,14 +230,19 @@ module Puma
|
|
204
230
|
end
|
205
231
|
end
|
206
232
|
|
207
|
-
|
233
|
+
if @in_last_chunk
|
234
|
+
set_ready
|
235
|
+
true
|
236
|
+
else
|
237
|
+
false
|
238
|
+
end
|
208
239
|
end
|
209
240
|
|
210
241
|
def read_chunked_body
|
211
242
|
while true
|
212
243
|
begin
|
213
244
|
chunk = @io.read_nonblock(4096)
|
214
|
-
rescue
|
245
|
+
rescue IO::WaitReadable
|
215
246
|
return false
|
216
247
|
rescue SystemCallError, IOError
|
217
248
|
raise ConnectionError, "Connection error detected during read"
|
@@ -221,8 +252,7 @@ module Puma
|
|
221
252
|
unless chunk
|
222
253
|
@body.close
|
223
254
|
@buffer = nil
|
224
|
-
|
225
|
-
@ready = true
|
255
|
+
set_ready
|
226
256
|
raise EOFError
|
227
257
|
end
|
228
258
|
|
@@ -231,6 +261,8 @@ module Puma
|
|
231
261
|
end
|
232
262
|
|
233
263
|
def setup_body
|
264
|
+
@body_read_start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
|
265
|
+
|
234
266
|
if @env[HTTP_EXPECT] == CONTINUE
|
235
267
|
# TODO allow a hook here to check the headers before
|
236
268
|
# going forward
|
@@ -255,8 +287,7 @@ module Puma
|
|
255
287
|
unless cl
|
256
288
|
@buffer = body.empty? ? nil : body
|
257
289
|
@body = EmptyBody
|
258
|
-
|
259
|
-
@ready = true
|
290
|
+
set_ready
|
260
291
|
return true
|
261
292
|
end
|
262
293
|
|
@@ -265,8 +296,7 @@ module Puma
|
|
265
296
|
if remain <= 0
|
266
297
|
@body = StringIO.new(body)
|
267
298
|
@buffer = nil
|
268
|
-
|
269
|
-
@ready = true
|
299
|
+
set_ready
|
270
300
|
return true
|
271
301
|
end
|
272
302
|
|
@@ -294,15 +324,14 @@ module Puma
|
|
294
324
|
data = @io.read_nonblock(CHUNK_SIZE)
|
295
325
|
rescue Errno::EAGAIN
|
296
326
|
return false
|
297
|
-
rescue SystemCallError, IOError
|
327
|
+
rescue SystemCallError, IOError, EOFError
|
298
328
|
raise ConnectionError, "Connection error detected during read"
|
299
329
|
end
|
300
330
|
|
301
331
|
# No data means a closed socket
|
302
332
|
unless data
|
303
333
|
@buffer = nil
|
304
|
-
|
305
|
-
@ready = true
|
334
|
+
set_ready
|
306
335
|
raise EOFError
|
307
336
|
end
|
308
337
|
|
@@ -338,8 +367,7 @@ module Puma
|
|
338
367
|
# No data means a closed socket
|
339
368
|
unless data
|
340
369
|
@buffer = nil
|
341
|
-
|
342
|
-
@ready = true
|
370
|
+
set_ready
|
343
371
|
raise EOFError
|
344
372
|
end
|
345
373
|
|
@@ -416,8 +444,7 @@ module Puma
|
|
416
444
|
unless chunk
|
417
445
|
@body.close
|
418
446
|
@buffer = nil
|
419
|
-
|
420
|
-
@ready = true
|
447
|
+
set_ready
|
421
448
|
raise EOFError
|
422
449
|
end
|
423
450
|
|
@@ -426,8 +453,7 @@ module Puma
|
|
426
453
|
if remain <= 0
|
427
454
|
@body.rewind
|
428
455
|
@buffer = nil
|
429
|
-
|
430
|
-
@ready = true
|
456
|
+
set_ready
|
431
457
|
return true
|
432
458
|
end
|
433
459
|
|
@@ -436,6 +462,14 @@ module Puma
|
|
436
462
|
false
|
437
463
|
end
|
438
464
|
|
465
|
+
def set_ready
|
466
|
+
if @body_read_start
|
467
|
+
@env['puma.request_body_wait'] = Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond) - @body_read_start
|
468
|
+
end
|
469
|
+
@requests_served += 1
|
470
|
+
@ready = true
|
471
|
+
end
|
472
|
+
|
439
473
|
def write_400
|
440
474
|
begin
|
441
475
|
@io << ERROR_400_RESPONSE
|