puma 5.6.4-java → 6.0.0-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 +136 -3
- data/README.md +21 -17
- data/bin/puma-wild +1 -1
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +1 -3
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/extconf.rb +18 -10
- data/ext/puma_http11/http11_parser.c +1 -1
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +2 -2
- data/ext/puma_http11/http11_parser.rl +2 -2
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +63 -24
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +166 -65
- data/ext/puma_http11/puma_http11.c +17 -9
- data/lib/puma/app/status.rb +6 -3
- data/lib/puma/binder.rb +37 -43
- data/lib/puma/cli.rb +11 -17
- data/lib/puma/client.rb +22 -12
- data/lib/puma/cluster/worker.rb +13 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +28 -25
- data/lib/puma/configuration.rb +74 -58
- data/lib/puma/const.rb +14 -18
- data/lib/puma/control_cli.rb +21 -18
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +94 -49
- data/lib/puma/error_logger.rb +17 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +29 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +107 -156
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +23 -12
- data/lib/puma/minissl.rb +91 -15
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +4 -4
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +3 -3
- data/lib/puma/request.rb +291 -156
- data/lib/puma/runner.rb +41 -20
- data/lib/puma/server.rb +53 -64
- data/lib/puma/single.rb +10 -10
- data/lib/puma/state_file.rb +2 -4
- data/lib/puma/systemd.rb +3 -2
- data/lib/puma/thread_pool.rb +16 -13
- data/lib/puma/util.rb +12 -14
- data/lib/puma.rb +11 -8
- data/lib/rack/handler/puma.rb +9 -9
- metadata +7 -3
- data/lib/puma/queue_close.rb +0 -26
@@ -62,8 +62,8 @@ static void snake_upcase_char(char *c)
|
|
62
62
|
parser->query_string(parser, PTR_TO(query_start), LEN(query_start, fpc));
|
63
63
|
}
|
64
64
|
|
65
|
-
action
|
66
|
-
parser->
|
65
|
+
action server_protocol {
|
66
|
+
parser->server_protocol(parser, PTR_TO(mark), LEN(mark, fpc));
|
67
67
|
}
|
68
68
|
|
69
69
|
action request_path {
|
@@ -38,8 +38,8 @@
|
|
38
38
|
Method = ( upper | digit | safe ){1,20} >mark %request_method;
|
39
39
|
|
40
40
|
http_number = ( digit+ "." digit+ ) ;
|
41
|
-
|
42
|
-
Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " "
|
41
|
+
Server_Protocol = ( "HTTP/" http_number ) >mark %server_protocol ;
|
42
|
+
Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " Server_Protocol CRLF ) ;
|
43
43
|
|
44
44
|
field_name = ( token -- ":" )+ >start_field $snake_upcase_field %write_field;
|
45
45
|
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -30,6 +30,12 @@ typedef struct {
|
|
30
30
|
|
31
31
|
VALUE eError;
|
32
32
|
|
33
|
+
NORETURN(void raise_file_error(const char* caller, const char *filename));
|
34
|
+
|
35
|
+
void raise_file_error(const char* caller, const char *filename) {
|
36
|
+
rb_raise(eError, "%s: error in file '%s': %s", caller, filename, ERR_error_string(ERR_get_error(), NULL));
|
37
|
+
}
|
38
|
+
|
33
39
|
void engine_free(void *ptr) {
|
34
40
|
ms_conn *conn = ptr;
|
35
41
|
ms_cert_buf* cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
|
@@ -49,7 +55,7 @@ const rb_data_type_t engine_data_type = {
|
|
49
55
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
|
50
56
|
};
|
51
57
|
|
52
|
-
#ifndef
|
58
|
+
#ifndef HAVE_SSL_CTX_SET_DH_AUTO
|
53
59
|
DH *get_dh2048(void) {
|
54
60
|
/* `openssl dhparam -C 2048`
|
55
61
|
* -----BEGIN DH PARAMETERS-----
|
@@ -92,13 +98,13 @@ DH *get_dh2048(void) {
|
|
92
98
|
static unsigned char dh2048_g[] = { 0x02 };
|
93
99
|
|
94
100
|
DH *dh;
|
95
|
-
#if !(OPENSSL_VERSION_NUMBER < 0x10100005L
|
101
|
+
#if !(OPENSSL_VERSION_NUMBER < 0x10100005L)
|
96
102
|
BIGNUM *p, *g;
|
97
103
|
#endif
|
98
104
|
|
99
105
|
dh = DH_new();
|
100
106
|
|
101
|
-
#if OPENSSL_VERSION_NUMBER < 0x10100005L
|
107
|
+
#if OPENSSL_VERSION_NUMBER < 0x10100005L
|
102
108
|
dh->p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
|
103
109
|
dh->g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL);
|
104
110
|
|
@@ -204,25 +210,28 @@ sslctx_alloc(VALUE klass) {
|
|
204
210
|
VALUE
|
205
211
|
sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
206
212
|
SSL_CTX* ctx;
|
207
|
-
|
208
|
-
#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
|
209
|
-
int min;
|
210
|
-
#endif
|
211
213
|
int ssl_options;
|
212
214
|
VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
|
213
215
|
verification_flags, session_id_bytes, cert_pem, key_pem;
|
214
|
-
#ifndef HAVE_SSL_GET1_PEER_CERTIFICATE
|
215
|
-
DH *dh;
|
216
|
-
#endif
|
217
216
|
BIO *bio;
|
218
217
|
X509 *x509;
|
219
218
|
EVP_PKEY *pkey;
|
220
|
-
|
219
|
+
#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
|
220
|
+
int min;
|
221
|
+
#endif
|
222
|
+
#ifndef HAVE_SSL_CTX_SET_DH_AUTO
|
223
|
+
DH *dh;
|
224
|
+
#endif
|
221
225
|
#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
222
226
|
EC_KEY *ecdh;
|
223
227
|
#endif
|
228
|
+
#ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
|
229
|
+
VALUE reuse, reuse_cache_size, reuse_timeout;
|
224
230
|
|
225
|
-
|
231
|
+
reuse = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse"), 0);
|
232
|
+
reuse_cache_size = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse_cache_size"), 0);
|
233
|
+
reuse_timeout = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse_timeout"), 0);
|
234
|
+
#endif
|
226
235
|
|
227
236
|
key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0);
|
228
237
|
|
@@ -242,14 +251,22 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
242
251
|
|
243
252
|
no_tlsv1_1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0);
|
244
253
|
|
254
|
+
TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx);
|
255
|
+
|
245
256
|
if (!NIL_P(cert)) {
|
246
257
|
StringValue(cert);
|
247
|
-
|
258
|
+
|
259
|
+
if (SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert)) != 1) {
|
260
|
+
raise_file_error("SSL_CTX_use_certificate_chain_file", RSTRING_PTR(cert));
|
261
|
+
}
|
248
262
|
}
|
249
263
|
|
250
264
|
if (!NIL_P(key)) {
|
251
265
|
StringValue(key);
|
252
|
-
|
266
|
+
|
267
|
+
if (SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM) != 1) {
|
268
|
+
raise_file_error("SSL_CTX_use_PrivateKey_file", RSTRING_PTR(key));
|
269
|
+
}
|
253
270
|
}
|
254
271
|
|
255
272
|
if (!NIL_P(cert_pem)) {
|
@@ -257,7 +274,12 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
257
274
|
BIO_puts(bio, RSTRING_PTR(cert_pem));
|
258
275
|
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
259
276
|
|
260
|
-
SSL_CTX_use_certificate(ctx, x509)
|
277
|
+
if (SSL_CTX_use_certificate(ctx, x509) != 1) {
|
278
|
+
BIO_free(bio);
|
279
|
+
raise_file_error("SSL_CTX_use_certificate", RSTRING_PTR(cert_pem));
|
280
|
+
}
|
281
|
+
X509_free(x509);
|
282
|
+
BIO_free(bio);
|
261
283
|
}
|
262
284
|
|
263
285
|
if (!NIL_P(key_pem)) {
|
@@ -265,7 +287,12 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
265
287
|
BIO_puts(bio, RSTRING_PTR(key_pem));
|
266
288
|
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
267
289
|
|
268
|
-
SSL_CTX_use_PrivateKey(ctx, pkey)
|
290
|
+
if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
|
291
|
+
BIO_free(bio);
|
292
|
+
raise_file_error("SSL_CTX_use_PrivateKey", RSTRING_PTR(key_pem));
|
293
|
+
}
|
294
|
+
EVP_PKEY_free(pkey);
|
295
|
+
BIO_free(bio);
|
269
296
|
}
|
270
297
|
|
271
298
|
verification_flags = rb_funcall(mini_ssl_ctx, rb_intern_const("verification_flags"), 0);
|
@@ -278,7 +305,9 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
278
305
|
|
279
306
|
if (!NIL_P(ca)) {
|
280
307
|
StringValue(ca);
|
281
|
-
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL)
|
308
|
+
if (SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL) != 1) {
|
309
|
+
raise_file_error("SSL_CTX_load_verify_locations", RSTRING_PTR(ca));
|
310
|
+
}
|
282
311
|
}
|
283
312
|
|
284
313
|
ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
|
@@ -296,8 +325,6 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
296
325
|
|
297
326
|
SSL_CTX_set_min_proto_version(ctx, min);
|
298
327
|
|
299
|
-
SSL_CTX_set_options(ctx, ssl_options);
|
300
|
-
|
301
328
|
#else
|
302
329
|
/* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */
|
303
330
|
ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;
|
@@ -308,10 +335,23 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
308
335
|
if(RTEST(no_tlsv1_1)) {
|
309
336
|
ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
|
310
337
|
}
|
311
|
-
SSL_CTX_set_options(ctx, ssl_options);
|
312
338
|
#endif
|
313
339
|
|
314
|
-
|
340
|
+
#ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
|
341
|
+
if (!NIL_P(reuse)) {
|
342
|
+
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
|
343
|
+
if (!NIL_P(reuse_cache_size)) {
|
344
|
+
SSL_CTX_sess_set_cache_size(ctx, NUM2INT(reuse_cache_size));
|
345
|
+
}
|
346
|
+
if (!NIL_P(reuse_timeout)) {
|
347
|
+
SSL_CTX_set_timeout(ctx, NUM2INT(reuse_timeout));
|
348
|
+
}
|
349
|
+
} else {
|
350
|
+
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
351
|
+
}
|
352
|
+
#endif
|
353
|
+
|
354
|
+
SSL_CTX_set_options(ctx, ssl_options);
|
315
355
|
|
316
356
|
if (!NIL_P(ssl_cipher_filter)) {
|
317
357
|
StringValue(ssl_cipher_filter);
|
@@ -322,8 +362,7 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
322
362
|
}
|
323
363
|
|
324
364
|
#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
325
|
-
// Remove this case if OpenSSL 1.0.1 (now EOL) support is no
|
326
|
-
// longer needed.
|
365
|
+
// Remove this case if OpenSSL 1.0.1 (now EOL) support is no longer needed.
|
327
366
|
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
328
367
|
if (ecdh) {
|
329
368
|
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
@@ -355,7 +394,7 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
|
355
394
|
|
356
395
|
// printf("\ninitialize end security_level %d\n", SSL_CTX_get_security_level(ctx));
|
357
396
|
|
358
|
-
#ifdef
|
397
|
+
#ifdef HAVE_SSL_CTX_SET_DH_AUTO
|
359
398
|
// https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_dh_auto.html
|
360
399
|
SSL_CTX_set_dh_auto(ctx, 1);
|
361
400
|
#else
|
@@ -46,7 +46,7 @@ public class Http11 extends RubyObject {
|
|
46
46
|
public static final ByteList FRAGMENT_BYTELIST = new ByteList(ByteList.plain("FRAGMENT"));
|
47
47
|
public static final ByteList REQUEST_PATH_BYTELIST = new ByteList(ByteList.plain("REQUEST_PATH"));
|
48
48
|
public static final ByteList QUERY_STRING_BYTELIST = new ByteList(ByteList.plain("QUERY_STRING"));
|
49
|
-
public static final ByteList
|
49
|
+
public static final ByteList SERVER_PROTOCOL_BYTELIST = new ByteList(ByteList.plain("SERVER_PROTOCOL"));
|
50
50
|
|
51
51
|
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
52
52
|
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
@@ -153,9 +153,9 @@ public class Http11 extends RubyObject {
|
|
153
153
|
req.fastASet(RubyString.newStringShared(runtime, QUERY_STRING_BYTELIST),val);
|
154
154
|
}
|
155
155
|
|
156
|
-
public static void
|
156
|
+
public static void server_protocol(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
157
157
|
RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length));
|
158
|
-
req.fastASet(RubyString.newStringShared(runtime,
|
158
|
+
req.fastASet(RubyString.newStringShared(runtime, SERVER_PROTOCOL_BYTELIST),val);
|
159
159
|
}
|
160
160
|
|
161
161
|
public void header_done(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
|
@@ -383,7 +383,7 @@ case 1:
|
|
383
383
|
case 11:
|
384
384
|
// line 42 "ext/puma_http11/http11_parser.java.rl"
|
385
385
|
{
|
386
|
-
Http11.
|
386
|
+
Http11.server_protocol(runtime, parser.data, parser.buffer, parser.mark, p-parser.mark);
|
387
387
|
}
|
388
388
|
break;
|
389
389
|
case 12:
|
@@ -1,6 +1,7 @@
|
|
1
1
|
package org.jruby.puma;
|
2
2
|
|
3
3
|
import org.jruby.Ruby;
|
4
|
+
import org.jruby.RubyArray;
|
4
5
|
import org.jruby.RubyClass;
|
5
6
|
import org.jruby.RubyModule;
|
6
7
|
import org.jruby.RubyObject;
|
@@ -15,6 +16,7 @@ import org.jruby.runtime.builtin.IRubyObject;
|
|
15
16
|
import org.jruby.util.ByteList;
|
16
17
|
|
17
18
|
import javax.net.ssl.KeyManagerFactory;
|
19
|
+
import javax.net.ssl.TrustManager;
|
18
20
|
import javax.net.ssl.TrustManagerFactory;
|
19
21
|
import javax.net.ssl.SSLContext;
|
20
22
|
import javax.net.ssl.SSLEngine;
|
@@ -22,6 +24,7 @@ import javax.net.ssl.SSLEngineResult;
|
|
22
24
|
import javax.net.ssl.SSLException;
|
23
25
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
24
26
|
import javax.net.ssl.SSLSession;
|
27
|
+
import javax.net.ssl.X509TrustManager;
|
25
28
|
import java.io.FileInputStream;
|
26
29
|
import java.io.InputStream;
|
27
30
|
import java.io.IOException;
|
@@ -32,15 +35,18 @@ import java.security.KeyStore;
|
|
32
35
|
import java.security.KeyStoreException;
|
33
36
|
import java.security.NoSuchAlgorithmException;
|
34
37
|
import java.security.UnrecoverableKeyException;
|
38
|
+
import java.security.cert.Certificate;
|
35
39
|
import java.security.cert.CertificateEncodingException;
|
36
40
|
import java.security.cert.CertificateException;
|
41
|
+
import java.security.cert.X509Certificate;
|
37
42
|
import java.util.concurrent.ConcurrentHashMap;
|
38
43
|
import java.util.Map;
|
44
|
+
import java.util.function.Supplier;
|
39
45
|
|
40
46
|
import static javax.net.ssl.SSLEngineResult.Status;
|
41
47
|
import static javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
42
48
|
|
43
|
-
public class MiniSSL extends RubyObject {
|
49
|
+
public class MiniSSL extends RubyObject { // MiniSSL::Engine
|
44
50
|
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
45
51
|
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
46
52
|
return new MiniSSL(runtime, klass);
|
@@ -51,11 +57,10 @@ public class MiniSSL extends RubyObject {
|
|
51
57
|
RubyModule mPuma = runtime.defineModule("Puma");
|
52
58
|
RubyModule ssl = mPuma.defineModuleUnder("MiniSSL");
|
53
59
|
|
54
|
-
|
55
|
-
|
56
|
-
runtime.getClass("IOError").getAllocator());
|
60
|
+
// Puma::MiniSSL::SSLError
|
61
|
+
ssl.defineClassUnder("SSLError", runtime.getStandardError(), runtime.getStandardError().getAllocator());
|
57
62
|
|
58
|
-
RubyClass eng = ssl.defineClassUnder("Engine",runtime.getObject(),ALLOCATOR);
|
63
|
+
RubyClass eng = ssl.defineClassUnder("Engine", runtime.getObject(), ALLOCATOR);
|
59
64
|
eng.defineAnnotatedMethods(MiniSSL.class);
|
60
65
|
}
|
61
66
|
|
@@ -137,74 +142,116 @@ public class MiniSSL extends RubyObject {
|
|
137
142
|
private static Map<String, KeyManagerFactory> keyManagerFactoryMap = new ConcurrentHashMap<String, KeyManagerFactory>();
|
138
143
|
private static Map<String, TrustManagerFactory> trustManagerFactoryMap = new ConcurrentHashMap<String, TrustManagerFactory>();
|
139
144
|
|
140
|
-
@JRubyMethod(meta = true)
|
145
|
+
@JRubyMethod(meta = true) // Engine.server
|
141
146
|
public static synchronized IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext)
|
142
147
|
throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
|
143
148
|
// Create the KeyManagerFactory and TrustManagerFactory for this server
|
144
|
-
String keystoreFile = miniSSLContext.callMethod(context, "keystore")
|
145
|
-
char[]
|
149
|
+
String keystoreFile = asStringValue(miniSSLContext.callMethod(context, "keystore"), null);
|
150
|
+
char[] keystorePass = asStringValue(miniSSLContext.callMethod(context, "keystore_pass"), null).toCharArray();
|
151
|
+
String keystoreType = asStringValue(miniSSLContext.callMethod(context, "keystore_type"), KeyStore::getDefaultType);
|
152
|
+
|
153
|
+
String truststoreFile;
|
154
|
+
char[] truststorePass;
|
155
|
+
String truststoreType;
|
156
|
+
IRubyObject truststore = miniSSLContext.callMethod(context, "truststore");
|
157
|
+
if (truststore.isNil()) {
|
158
|
+
truststoreFile = keystoreFile;
|
159
|
+
truststorePass = keystorePass;
|
160
|
+
truststoreType = keystoreType;
|
161
|
+
} else if (!isDefaultSymbol(context, truststore)) {
|
162
|
+
truststoreFile = truststore.convertToString().asJavaString();
|
163
|
+
IRubyObject pass = miniSSLContext.callMethod(context, "truststore_pass");
|
164
|
+
if (pass.isNil()) {
|
165
|
+
truststorePass = null;
|
166
|
+
} else {
|
167
|
+
truststorePass = asStringValue(pass, null).toCharArray();
|
168
|
+
}
|
169
|
+
truststoreType = asStringValue(miniSSLContext.callMethod(context, "truststore_type"), KeyStore::getDefaultType);
|
170
|
+
} else { // self.truststore = :default
|
171
|
+
truststoreFile = null;
|
172
|
+
truststorePass = null;
|
173
|
+
truststoreType = null;
|
174
|
+
}
|
146
175
|
|
147
|
-
KeyStore ks = KeyStore.getInstance(
|
176
|
+
KeyStore ks = KeyStore.getInstance(keystoreType);
|
148
177
|
InputStream is = new FileInputStream(keystoreFile);
|
149
178
|
try {
|
150
|
-
ks.load(is,
|
179
|
+
ks.load(is, keystorePass);
|
151
180
|
} finally {
|
152
181
|
is.close();
|
153
182
|
}
|
154
183
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
155
|
-
kmf.init(ks,
|
184
|
+
kmf.init(ks, keystorePass);
|
156
185
|
keyManagerFactoryMap.put(keystoreFile, kmf);
|
157
186
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
187
|
+
if (truststoreFile != null) {
|
188
|
+
KeyStore ts = KeyStore.getInstance(truststoreType);
|
189
|
+
is = new FileInputStream(truststoreFile);
|
190
|
+
try {
|
191
|
+
ts.load(is, truststorePass);
|
192
|
+
} finally {
|
193
|
+
is.close();
|
194
|
+
}
|
195
|
+
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
196
|
+
tmf.init(ts);
|
197
|
+
trustManagerFactoryMap.put(truststoreFile, tmf);
|
164
198
|
}
|
165
|
-
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
166
|
-
tmf.init(ts);
|
167
|
-
trustManagerFactoryMap.put(keystoreFile, tmf);
|
168
199
|
|
169
200
|
RubyClass klass = (RubyClass) recv;
|
170
|
-
return klass.newInstance(context,
|
171
|
-
|
172
|
-
|
201
|
+
return klass.newInstance(context, miniSSLContext, Block.NULL_BLOCK);
|
202
|
+
}
|
203
|
+
|
204
|
+
private static String asStringValue(IRubyObject value, Supplier<String> defaultValue) {
|
205
|
+
if (defaultValue != null && value.isNil()) return defaultValue.get();
|
206
|
+
return value.convertToString().asJavaString();
|
207
|
+
}
|
208
|
+
|
209
|
+
private static boolean isDefaultSymbol(ThreadContext context, IRubyObject truststore) {
|
210
|
+
return context.runtime.newSymbol("default").equals(truststore);
|
173
211
|
}
|
174
212
|
|
175
213
|
@JRubyMethod
|
176
|
-
public IRubyObject initialize(ThreadContext
|
214
|
+
public IRubyObject initialize(ThreadContext context, IRubyObject miniSSLContext)
|
177
215
|
throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
|
178
216
|
|
179
|
-
String keystoreFile = miniSSLContext.callMethod(
|
217
|
+
String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString();
|
180
218
|
KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
|
181
|
-
|
182
|
-
|
183
|
-
|
219
|
+
IRubyObject truststore = miniSSLContext.callMethod(context, "truststore");
|
220
|
+
String truststoreFile = isDefaultSymbol(context, truststore) ? "" : asStringValue(truststore, () -> keystoreFile);
|
221
|
+
TrustManagerFactory tmf = trustManagerFactoryMap.get(truststoreFile); // null if self.truststore = :default
|
222
|
+
if (kmf == null) {
|
223
|
+
throw new KeyStoreException("Could not find KeyManagerFactory for keystore: " + keystoreFile + " truststore: " + truststoreFile);
|
184
224
|
}
|
185
225
|
|
186
226
|
SSLContext sslCtx = SSLContext.getInstance("TLS");
|
187
227
|
|
188
|
-
sslCtx.init(kmf.getKeyManagers(),
|
228
|
+
sslCtx.init(kmf.getKeyManagers(), getTrustManagers(tmf), null);
|
189
229
|
closed = false;
|
190
230
|
handshake = false;
|
191
231
|
engine = sslCtx.createSSLEngine();
|
192
232
|
|
193
|
-
String[]
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
233
|
+
String[] enabledProtocols;
|
234
|
+
IRubyObject protocols = miniSSLContext.callMethod(context, "protocols");
|
235
|
+
if (protocols.isNil()) {
|
236
|
+
if (miniSSLContext.callMethod(context, "no_tlsv1").isTrue()) {
|
237
|
+
enabledProtocols = new String[] { "TLSv1.1", "TLSv1.2", "TLSv1.3" };
|
238
|
+
} else {
|
239
|
+
enabledProtocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2", "TLSv1.3" };
|
240
|
+
}
|
199
241
|
|
200
|
-
|
201
|
-
|
242
|
+
if (miniSSLContext.callMethod(context, "no_tlsv1_1").isTrue()) {
|
243
|
+
enabledProtocols = new String[] { "TLSv1.2", "TLSv1.3" };
|
244
|
+
}
|
245
|
+
} else if (protocols instanceof RubyArray) {
|
246
|
+
enabledProtocols = (String[]) ((RubyArray) protocols).toArray(new String[0]);
|
247
|
+
} else {
|
248
|
+
throw context.runtime.newTypeError(protocols, context.runtime.getArray());
|
202
249
|
}
|
250
|
+
engine.setEnabledProtocols(enabledProtocols);
|
203
251
|
|
204
|
-
engine.setEnabledProtocols(protocols);
|
205
252
|
engine.setUseClientMode(false);
|
206
253
|
|
207
|
-
long verify_mode = miniSSLContext.callMethod(
|
254
|
+
long verify_mode = miniSSLContext.callMethod(context, "verify_mode").convertToInteger("to_i").getLongValue();
|
208
255
|
if ((verify_mode & 0x1) != 0) { // 'peer'
|
209
256
|
engine.setWantClientAuth(true);
|
210
257
|
}
|
@@ -212,10 +259,11 @@ public class MiniSSL extends RubyObject {
|
|
212
259
|
engine.setNeedClientAuth(true);
|
213
260
|
}
|
214
261
|
|
215
|
-
IRubyObject
|
216
|
-
if (
|
217
|
-
String[]
|
218
|
-
|
262
|
+
IRubyObject cipher_suites = miniSSLContext.callMethod(context, "cipher_suites");
|
263
|
+
if (cipher_suites instanceof RubyArray) {
|
264
|
+
engine.setEnabledCipherSuites((String[]) ((RubyArray) cipher_suites).toArray(new String[0]));
|
265
|
+
} else if (!cipher_suites.isNil()) {
|
266
|
+
throw context.runtime.newTypeError(cipher_suites, context.runtime.getArray());
|
219
267
|
}
|
220
268
|
|
221
269
|
SSLSession session = engine.getSession();
|
@@ -227,6 +275,48 @@ public class MiniSSL extends RubyObject {
|
|
227
275
|
return this;
|
228
276
|
}
|
229
277
|
|
278
|
+
private TrustManager[] getTrustManagers(TrustManagerFactory factory) {
|
279
|
+
if (factory == null) return null; // use JDK trust defaults
|
280
|
+
final TrustManager[] tms = factory.getTrustManagers();
|
281
|
+
if (tms != null) {
|
282
|
+
for (int i=0; i<tms.length; i++) {
|
283
|
+
final TrustManager tm = tms[i];
|
284
|
+
if (tm instanceof X509TrustManager) {
|
285
|
+
tms[i] = new TrustManagerWrapper((X509TrustManager) tm);
|
286
|
+
}
|
287
|
+
}
|
288
|
+
}
|
289
|
+
return tms;
|
290
|
+
}
|
291
|
+
|
292
|
+
private volatile transient X509Certificate lastCheckedCert0;
|
293
|
+
|
294
|
+
private class TrustManagerWrapper implements X509TrustManager {
|
295
|
+
|
296
|
+
private final X509TrustManager delegate;
|
297
|
+
|
298
|
+
TrustManagerWrapper(X509TrustManager delegate) {
|
299
|
+
this.delegate = delegate;
|
300
|
+
}
|
301
|
+
|
302
|
+
@Override
|
303
|
+
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
304
|
+
lastCheckedCert0 = chain.length > 0 ? chain[0] : null;
|
305
|
+
delegate.checkClientTrusted(chain, authType);
|
306
|
+
}
|
307
|
+
|
308
|
+
@Override
|
309
|
+
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
|
310
|
+
delegate.checkServerTrusted(chain, authType);
|
311
|
+
}
|
312
|
+
|
313
|
+
@Override
|
314
|
+
public X509Certificate[] getAcceptedIssuers() {
|
315
|
+
return delegate.getAcceptedIssuers();
|
316
|
+
}
|
317
|
+
|
318
|
+
}
|
319
|
+
|
230
320
|
@JRubyMethod
|
231
321
|
public IRubyObject inject(IRubyObject arg) {
|
232
322
|
ByteList bytes = arg.convertToString().getByteList();
|
@@ -251,7 +341,7 @@ public class MiniSSL extends RubyObject {
|
|
251
341
|
res = engine.unwrap(src.getRawBuffer(), dst.getRawBuffer());
|
252
342
|
break;
|
253
343
|
default:
|
254
|
-
throw new
|
344
|
+
throw new AssertionError("Unknown SSLOperation: " + sslOp);
|
255
345
|
}
|
256
346
|
|
257
347
|
switch (res.getStatus()) {
|
@@ -279,14 +369,6 @@ public class MiniSSL extends RubyObject {
|
|
279
369
|
}
|
280
370
|
}
|
281
371
|
|
282
|
-
// after each op, run any delegated tasks if needed
|
283
|
-
if(res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
|
284
|
-
Runnable runnable;
|
285
|
-
while ((runnable = engine.getDelegatedTask()) != null) {
|
286
|
-
runnable.run();
|
287
|
-
}
|
288
|
-
}
|
289
|
-
|
290
372
|
return res;
|
291
373
|
}
|
292
374
|
|
@@ -304,11 +386,12 @@ public class MiniSSL extends RubyObject {
|
|
304
386
|
|
305
387
|
HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
|
306
388
|
boolean done = false;
|
307
|
-
SSLEngineResult res = null;
|
308
389
|
while (!done) {
|
390
|
+
SSLEngineResult res;
|
309
391
|
switch (handshakeStatus) {
|
310
392
|
case NEED_WRAP:
|
311
393
|
res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
|
394
|
+
handshakeStatus = res.getHandshakeStatus();
|
312
395
|
break;
|
313
396
|
case NEED_UNWRAP:
|
314
397
|
res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
|
@@ -316,13 +399,18 @@ public class MiniSSL extends RubyObject {
|
|
316
399
|
// need more data before we can shake more hands
|
317
400
|
done = true;
|
318
401
|
}
|
402
|
+
handshakeStatus = res.getHandshakeStatus();
|
403
|
+
break;
|
404
|
+
case NEED_TASK:
|
405
|
+
Runnable runnable;
|
406
|
+
while ((runnable = engine.getDelegatedTask()) != null) {
|
407
|
+
runnable.run();
|
408
|
+
}
|
409
|
+
handshakeStatus = engine.getHandshakeStatus();
|
319
410
|
break;
|
320
411
|
default:
|
321
412
|
done = true;
|
322
413
|
}
|
323
|
-
if (!done) {
|
324
|
-
handshakeStatus = res.getHandshakeStatus();
|
325
|
-
}
|
326
414
|
}
|
327
415
|
|
328
416
|
if (inboundNetData.hasRemaining()) {
|
@@ -338,9 +426,7 @@ public class MiniSSL extends RubyObject {
|
|
338
426
|
|
339
427
|
return RubyString.newString(getRuntime(), appDataByteList);
|
340
428
|
} catch (SSLException e) {
|
341
|
-
|
342
|
-
re.initCause(e);
|
343
|
-
throw re;
|
429
|
+
throw newSSLError(getRuntime(), e);
|
344
430
|
}
|
345
431
|
}
|
346
432
|
|
@@ -373,19 +459,19 @@ public class MiniSSL extends RubyObject {
|
|
373
459
|
|
374
460
|
return RubyString.newString(context.runtime, dataByteList);
|
375
461
|
} catch (SSLException e) {
|
376
|
-
|
377
|
-
ex.initCause(e);
|
378
|
-
throw ex;
|
462
|
+
throw newSSLError(getRuntime(), e);
|
379
463
|
}
|
380
464
|
}
|
381
465
|
|
382
466
|
@JRubyMethod
|
383
|
-
public IRubyObject peercert() throws CertificateEncodingException {
|
467
|
+
public IRubyObject peercert(ThreadContext context) throws CertificateEncodingException {
|
468
|
+
Certificate peerCert;
|
384
469
|
try {
|
385
|
-
|
470
|
+
peerCert = engine.getSession().getPeerCertificates()[0];
|
386
471
|
} catch (SSLPeerUnverifiedException e) {
|
387
|
-
|
472
|
+
peerCert = lastCheckedCert0; // null if trust check did not happen
|
388
473
|
}
|
474
|
+
return peerCert == null ? context.nil : JavaEmbedUtils.javaToRuby(context.runtime, peerCert.getEncoded());
|
389
475
|
}
|
390
476
|
|
391
477
|
@JRubyMethod(name = "init?")
|
@@ -404,4 +490,19 @@ public class MiniSSL extends RubyObject {
|
|
404
490
|
return getRuntime().getFalse();
|
405
491
|
}
|
406
492
|
}
|
493
|
+
|
494
|
+
private static RubyClass getSSLError(Ruby runtime) {
|
495
|
+
return (RubyClass) ((RubyModule) runtime.getModule("Puma").getConstantAt("MiniSSL")).getConstantAt("SSLError");
|
496
|
+
}
|
497
|
+
|
498
|
+
private static RaiseException newSSLError(Ruby runtime, SSLException cause) {
|
499
|
+
return newError(runtime, getSSLError(runtime), cause.toString(), cause);
|
500
|
+
}
|
501
|
+
|
502
|
+
private static RaiseException newError(Ruby runtime, RubyClass errorClass, String message, Throwable cause) {
|
503
|
+
RaiseException ex = new RaiseException(runtime, errorClass, message, true);
|
504
|
+
ex.initCause(cause);
|
505
|
+
return ex;
|
506
|
+
}
|
507
|
+
|
407
508
|
}
|