puma 5.2.2 → 6.3.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 +483 -4
- data/README.md +101 -20
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +50 -16
- data/docs/compile_options.md +38 -2
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +1 -3
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +1 -1
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +7 -7
- data/docs/signals.md +11 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +65 -69
- 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 +44 -13
- data/ext/puma_http11/http11_parser.c +24 -11
- data/ext/puma_http11/http11_parser.h +2 -2
- 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 +3 -3
- data/ext/puma_http11/mini_ssl.c +150 -23
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +50 -48
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
- data/ext/puma_http11/puma_http11.c +18 -10
- data/lib/puma/app/status.rb +10 -7
- data/lib/puma/binder.rb +112 -62
- data/lib/puma/cli.rb +24 -20
- data/lib/puma/client.rb +162 -36
- data/lib/puma/cluster/worker.rb +31 -27
- data/lib/puma/cluster/worker_handle.rb +12 -1
- data/lib/puma/cluster.rb +102 -61
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +78 -54
- data/lib/puma/const.rb +135 -97
- data/lib/puma/control_cli.rb +25 -20
- data/lib/puma/detect.rb +12 -2
- data/lib/puma/dsl.rb +308 -58
- data/lib/puma/error_logger.rb +20 -11
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +39 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +114 -173
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +30 -16
- data/lib/puma/minissl.rb +132 -38
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/rack/builder.rb +7 -7
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +373 -153
- data/lib/puma/runner.rb +74 -28
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +127 -136
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +39 -7
- data/lib/puma/thread_pool.rb +33 -26
- data/lib/puma/util.rb +20 -15
- data/lib/puma.rb +28 -11
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +1 -1
- metadata +15 -10
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
@@ -34,9 +34,9 @@ private static short[] init__puma_parser_key_offsets_0()
|
|
34
34
|
{
|
35
35
|
return new short [] {
|
36
36
|
0, 0, 8, 17, 27, 29, 30, 31, 32, 33, 34, 36,
|
37
|
-
39, 41, 44, 45, 61, 62, 78,
|
38
|
-
|
39
|
-
|
37
|
+
39, 41, 44, 45, 61, 62, 78, 85, 91, 99, 107, 117,
|
38
|
+
125, 134, 142, 150, 159, 168, 177, 186, 195, 204, 213, 222,
|
39
|
+
231, 240, 249, 258, 267, 276, 285, 294, 303, 312, 313
|
40
40
|
};
|
41
41
|
}
|
42
42
|
|
@@ -52,26 +52,27 @@ private static char[] init__puma_parser_trans_keys_0()
|
|
52
52
|
46, 48, 57, 48, 57, 13, 48, 57, 10, 13, 33, 124,
|
53
53
|
126, 35, 39, 42, 43, 45, 46, 48, 57, 65, 90, 94,
|
54
54
|
122, 10, 33, 58, 124, 126, 35, 39, 42, 43, 45, 46,
|
55
|
-
48, 57, 65, 90, 94, 122, 13, 32,
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
63
|
-
36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95,
|
64
|
-
46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48,
|
65
|
-
65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
66
|
-
36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95,
|
67
|
-
46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48,
|
68
|
-
65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
69
|
-
36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95,
|
70
|
-
46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48,
|
71
|
-
65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
72
|
-
36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95,
|
73
|
-
46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48,
|
74
|
-
65, 90, 32,
|
55
|
+
48, 57, 65, 90, 94, 122, 13, 32, 127, 0, 8, 10,
|
56
|
+
31, 13, 127, 0, 8, 10, 31, 32, 60, 62, 127, 0,
|
57
|
+
31, 34, 35, 32, 60, 62, 127, 0, 31, 34, 35, 43,
|
58
|
+
58, 45, 46, 48, 57, 65, 90, 97, 122, 32, 34, 35,
|
59
|
+
60, 62, 127, 0, 31, 32, 34, 35, 60, 62, 63, 127,
|
60
|
+
0, 31, 32, 34, 35, 60, 62, 127, 0, 31, 32, 34,
|
61
|
+
35, 60, 62, 127, 0, 31, 32, 36, 95, 45, 46, 48,
|
62
|
+
57, 65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
63
|
+
32, 36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95,
|
64
|
+
45, 46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48,
|
65
|
+
57, 65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
66
|
+
32, 36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95,
|
67
|
+
45, 46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48,
|
68
|
+
57, 65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
69
|
+
32, 36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95,
|
70
|
+
45, 46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48,
|
71
|
+
57, 65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
72
|
+
32, 36, 95, 45, 46, 48, 57, 65, 90, 32, 36, 95,
|
73
|
+
45, 46, 48, 57, 65, 90, 32, 36, 95, 45, 46, 48,
|
74
|
+
57, 65, 90, 32, 36, 95, 45, 46, 48, 57, 65, 90,
|
75
|
+
32, 0
|
75
76
|
};
|
76
77
|
}
|
77
78
|
|
@@ -82,7 +83,7 @@ private static byte[] init__puma_parser_single_lengths_0()
|
|
82
83
|
{
|
83
84
|
return new byte [] {
|
84
85
|
0, 2, 3, 4, 2, 1, 1, 1, 1, 1, 0, 1,
|
85
|
-
0, 1, 1, 4, 1, 4,
|
86
|
+
0, 1, 1, 4, 1, 4, 3, 2, 4, 4, 2, 6,
|
86
87
|
7, 6, 6, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
87
88
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 0
|
88
89
|
};
|
@@ -95,7 +96,7 @@ private static byte[] init__puma_parser_range_lengths_0()
|
|
95
96
|
{
|
96
97
|
return new byte [] {
|
97
98
|
0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 1, 1,
|
98
|
-
1, 1, 0, 6, 0, 6,
|
99
|
+
1, 1, 0, 6, 0, 6, 2, 2, 2, 2, 4, 1,
|
99
100
|
1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
100
101
|
3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0
|
101
102
|
};
|
@@ -108,9 +109,9 @@ private static short[] init__puma_parser_index_offsets_0()
|
|
108
109
|
{
|
109
110
|
return new short [] {
|
110
111
|
0, 0, 6, 13, 21, 24, 26, 28, 30, 32, 34, 36,
|
111
|
-
39, 41, 44, 46, 57, 59, 70,
|
112
|
-
|
113
|
-
|
112
|
+
39, 41, 44, 46, 57, 59, 70, 76, 81, 88, 95, 102,
|
113
|
+
110, 119, 127, 135, 142, 149, 156, 163, 170, 177, 184, 191,
|
114
|
+
198, 205, 212, 219, 226, 233, 240, 247, 254, 261, 263
|
114
115
|
};
|
115
116
|
}
|
116
117
|
|
@@ -126,22 +127,23 @@ private static byte[] init__puma_parser_indicies_0()
|
|
126
127
|
16, 15, 1, 17, 1, 18, 17, 1, 19, 1, 20, 21,
|
127
128
|
21, 21, 21, 21, 21, 21, 21, 21, 1, 22, 1, 23,
|
128
129
|
24, 23, 23, 23, 23, 23, 23, 23, 23, 1, 26, 27,
|
129
|
-
|
130
|
-
1, 1, 1, 1,
|
131
|
-
|
132
|
-
1,
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
63, 63, 1, 2, 1, 1,
|
130
|
+
1, 1, 1, 25, 29, 1, 1, 1, 28, 30, 1, 1,
|
131
|
+
1, 1, 1, 31, 32, 1, 1, 1, 1, 1, 33, 34,
|
132
|
+
35, 34, 34, 34, 34, 1, 8, 1, 9, 1, 1, 1,
|
133
|
+
1, 35, 36, 1, 38, 1, 1, 39, 1, 1, 37, 40,
|
134
|
+
1, 42, 1, 1, 1, 1, 41, 43, 1, 45, 1, 1,
|
135
|
+
1, 1, 44, 2, 46, 46, 46, 46, 46, 1, 2, 47,
|
136
|
+
47, 47, 47, 47, 1, 2, 48, 48, 48, 48, 48, 1,
|
137
|
+
2, 49, 49, 49, 49, 49, 1, 2, 50, 50, 50, 50,
|
138
|
+
50, 1, 2, 51, 51, 51, 51, 51, 1, 2, 52, 52,
|
139
|
+
52, 52, 52, 1, 2, 53, 53, 53, 53, 53, 1, 2,
|
140
|
+
54, 54, 54, 54, 54, 1, 2, 55, 55, 55, 55, 55,
|
141
|
+
1, 2, 56, 56, 56, 56, 56, 1, 2, 57, 57, 57,
|
142
|
+
57, 57, 1, 2, 58, 58, 58, 58, 58, 1, 2, 59,
|
143
|
+
59, 59, 59, 59, 1, 2, 60, 60, 60, 60, 60, 1,
|
144
|
+
2, 61, 61, 61, 61, 61, 1, 2, 62, 62, 62, 62,
|
145
|
+
62, 1, 2, 63, 63, 63, 63, 63, 1, 2, 1, 1,
|
146
|
+
0
|
145
147
|
};
|
146
148
|
}
|
147
149
|
|
@@ -210,7 +212,7 @@ static final int puma_parser_error = 0;
|
|
210
212
|
cs = 0;
|
211
213
|
|
212
214
|
|
213
|
-
// line
|
215
|
+
// line 216 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
|
214
216
|
{
|
215
217
|
cs = puma_parser_start;
|
216
218
|
}
|
@@ -242,7 +244,7 @@ static final int puma_parser_error = 0;
|
|
242
244
|
parser.buffer = buffer;
|
243
245
|
|
244
246
|
|
245
|
-
// line
|
247
|
+
// line 248 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
|
246
248
|
{
|
247
249
|
int _klen;
|
248
250
|
int _trans = 0;
|
@@ -381,7 +383,7 @@ case 1:
|
|
381
383
|
case 11:
|
382
384
|
// line 42 "ext/puma_http11/http11_parser.java.rl"
|
383
385
|
{
|
384
|
-
Http11.
|
386
|
+
Http11.server_protocol(runtime, parser.data, parser.buffer, parser.mark, p-parser.mark);
|
385
387
|
}
|
386
388
|
break;
|
387
389
|
case 12:
|
@@ -398,7 +400,7 @@ case 1:
|
|
398
400
|
{ p += 1; _goto_targ = 5; if (true) continue _goto;}
|
399
401
|
}
|
400
402
|
break;
|
401
|
-
// line
|
403
|
+
// line 404 "ext/puma_http11/org/jruby/puma/Http11Parser.java"
|
402
404
|
}
|
403
405
|
}
|
404
406
|
}
|
@@ -1,11 +1,13 @@
|
|
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;
|
7
8
|
import org.jruby.RubyString;
|
8
9
|
import org.jruby.anno.JRubyMethod;
|
10
|
+
import org.jruby.exceptions.RaiseException;
|
9
11
|
import org.jruby.javasupport.JavaEmbedUtils;
|
10
12
|
import org.jruby.runtime.Block;
|
11
13
|
import org.jruby.runtime.ObjectAllocator;
|
@@ -14,6 +16,7 @@ import org.jruby.runtime.builtin.IRubyObject;
|
|
14
16
|
import org.jruby.util.ByteList;
|
15
17
|
|
16
18
|
import javax.net.ssl.KeyManagerFactory;
|
19
|
+
import javax.net.ssl.TrustManager;
|
17
20
|
import javax.net.ssl.TrustManagerFactory;
|
18
21
|
import javax.net.ssl.SSLContext;
|
19
22
|
import javax.net.ssl.SSLEngine;
|
@@ -21,6 +24,7 @@ import javax.net.ssl.SSLEngineResult;
|
|
21
24
|
import javax.net.ssl.SSLException;
|
22
25
|
import javax.net.ssl.SSLPeerUnverifiedException;
|
23
26
|
import javax.net.ssl.SSLSession;
|
27
|
+
import javax.net.ssl.X509TrustManager;
|
24
28
|
import java.io.FileInputStream;
|
25
29
|
import java.io.InputStream;
|
26
30
|
import java.io.IOException;
|
@@ -31,15 +35,18 @@ import java.security.KeyStore;
|
|
31
35
|
import java.security.KeyStoreException;
|
32
36
|
import java.security.NoSuchAlgorithmException;
|
33
37
|
import java.security.UnrecoverableKeyException;
|
38
|
+
import java.security.cert.Certificate;
|
34
39
|
import java.security.cert.CertificateEncodingException;
|
35
40
|
import java.security.cert.CertificateException;
|
41
|
+
import java.security.cert.X509Certificate;
|
36
42
|
import java.util.concurrent.ConcurrentHashMap;
|
37
43
|
import java.util.Map;
|
44
|
+
import java.util.function.Supplier;
|
38
45
|
|
39
46
|
import static javax.net.ssl.SSLEngineResult.Status;
|
40
47
|
import static javax.net.ssl.SSLEngineResult.HandshakeStatus;
|
41
48
|
|
42
|
-
public class MiniSSL extends RubyObject {
|
49
|
+
public class MiniSSL extends RubyObject { // MiniSSL::Engine
|
43
50
|
private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
|
44
51
|
public IRubyObject allocate(Ruby runtime, RubyClass klass) {
|
45
52
|
return new MiniSSL(runtime, klass);
|
@@ -50,11 +57,10 @@ public class MiniSSL extends RubyObject {
|
|
50
57
|
RubyModule mPuma = runtime.defineModule("Puma");
|
51
58
|
RubyModule ssl = mPuma.defineModuleUnder("MiniSSL");
|
52
59
|
|
53
|
-
|
54
|
-
|
55
|
-
runtime.getClass("IOError").getAllocator());
|
60
|
+
// Puma::MiniSSL::SSLError
|
61
|
+
ssl.defineClassUnder("SSLError", runtime.getStandardError(), runtime.getStandardError().getAllocator());
|
56
62
|
|
57
|
-
RubyClass eng = ssl.defineClassUnder("Engine",runtime.getObject(),ALLOCATOR);
|
63
|
+
RubyClass eng = ssl.defineClassUnder("Engine", runtime.getObject(), ALLOCATOR);
|
58
64
|
eng.defineAnnotatedMethods(MiniSSL.class);
|
59
65
|
}
|
60
66
|
|
@@ -80,11 +86,11 @@ public class MiniSSL extends RubyObject {
|
|
80
86
|
/**
|
81
87
|
* Writes bytes to the buffer after ensuring there's room
|
82
88
|
*/
|
83
|
-
|
84
|
-
if (buffer.remaining() <
|
85
|
-
resize(buffer.limit() +
|
89
|
+
private void put(byte[] bytes, final int offset, final int length) {
|
90
|
+
if (buffer.remaining() < length) {
|
91
|
+
resize(buffer.limit() + length);
|
86
92
|
}
|
87
|
-
buffer.put(bytes);
|
93
|
+
buffer.put(bytes, offset, length);
|
88
94
|
}
|
89
95
|
|
90
96
|
/**
|
@@ -115,7 +121,7 @@ public class MiniSSL extends RubyObject {
|
|
115
121
|
|
116
122
|
buffer.get(bss);
|
117
123
|
buffer.clear();
|
118
|
-
return new ByteList(bss);
|
124
|
+
return new ByteList(bss, false);
|
119
125
|
}
|
120
126
|
|
121
127
|
@Override
|
@@ -136,76 +142,116 @@ public class MiniSSL extends RubyObject {
|
|
136
142
|
private static Map<String, KeyManagerFactory> keyManagerFactoryMap = new ConcurrentHashMap<String, KeyManagerFactory>();
|
137
143
|
private static Map<String, TrustManagerFactory> trustManagerFactoryMap = new ConcurrentHashMap<String, TrustManagerFactory>();
|
138
144
|
|
139
|
-
@JRubyMethod(meta = true)
|
145
|
+
@JRubyMethod(meta = true) // Engine.server
|
140
146
|
public static synchronized IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext)
|
141
147
|
throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
|
142
148
|
// Create the KeyManagerFactory and TrustManagerFactory for this server
|
143
|
-
String keystoreFile = miniSSLContext.callMethod(context, "keystore")
|
144
|
-
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
|
+
}
|
145
175
|
|
146
|
-
KeyStore ks = KeyStore.getInstance(
|
176
|
+
KeyStore ks = KeyStore.getInstance(keystoreType);
|
147
177
|
InputStream is = new FileInputStream(keystoreFile);
|
148
178
|
try {
|
149
|
-
ks.load(is,
|
179
|
+
ks.load(is, keystorePass);
|
150
180
|
} finally {
|
151
181
|
is.close();
|
152
182
|
}
|
153
183
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
154
|
-
kmf.init(ks,
|
184
|
+
kmf.init(ks, keystorePass);
|
155
185
|
keyManagerFactoryMap.put(keystoreFile, kmf);
|
156
186
|
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
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);
|
163
198
|
}
|
164
|
-
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
165
|
-
tmf.init(ts);
|
166
|
-
trustManagerFactoryMap.put(keystoreFile, tmf);
|
167
199
|
|
168
200
|
RubyClass klass = (RubyClass) recv;
|
169
|
-
return klass.newInstance(context,
|
170
|
-
|
171
|
-
|
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);
|
172
211
|
}
|
173
212
|
|
174
213
|
@JRubyMethod
|
175
|
-
public IRubyObject initialize(ThreadContext
|
214
|
+
public IRubyObject initialize(ThreadContext context, IRubyObject miniSSLContext)
|
176
215
|
throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
|
177
|
-
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
178
|
-
KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
|
179
216
|
|
180
|
-
String keystoreFile = miniSSLContext.callMethod(
|
217
|
+
String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString();
|
181
218
|
KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
|
182
|
-
|
183
|
-
|
184
|
-
|
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);
|
185
224
|
}
|
186
225
|
|
187
226
|
SSLContext sslCtx = SSLContext.getInstance("TLS");
|
188
227
|
|
189
|
-
sslCtx.init(kmf.getKeyManagers(),
|
228
|
+
sslCtx.init(kmf.getKeyManagers(), getTrustManagers(tmf), null);
|
190
229
|
closed = false;
|
191
230
|
handshake = false;
|
192
231
|
engine = sslCtx.createSSLEngine();
|
193
232
|
|
194
|
-
String[]
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
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
|
+
}
|
200
241
|
|
201
|
-
|
202
|
-
|
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());
|
203
249
|
}
|
250
|
+
engine.setEnabledProtocols(enabledProtocols);
|
204
251
|
|
205
|
-
engine.setEnabledProtocols(protocols);
|
206
252
|
engine.setUseClientMode(false);
|
207
253
|
|
208
|
-
long verify_mode = miniSSLContext.callMethod(
|
254
|
+
long verify_mode = miniSSLContext.callMethod(context, "verify_mode").convertToInteger("to_i").getLongValue();
|
209
255
|
if ((verify_mode & 0x1) != 0) { // 'peer'
|
210
256
|
engine.setWantClientAuth(true);
|
211
257
|
}
|
@@ -213,10 +259,11 @@ public class MiniSSL extends RubyObject {
|
|
213
259
|
engine.setNeedClientAuth(true);
|
214
260
|
}
|
215
261
|
|
216
|
-
IRubyObject
|
217
|
-
if (
|
218
|
-
String[]
|
219
|
-
|
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());
|
220
267
|
}
|
221
268
|
|
222
269
|
SSLSession session = engine.getSession();
|
@@ -228,16 +275,53 @@ public class MiniSSL extends RubyObject {
|
|
228
275
|
return this;
|
229
276
|
}
|
230
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
|
+
|
231
320
|
@JRubyMethod
|
232
321
|
public IRubyObject inject(IRubyObject arg) {
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
return this;
|
237
|
-
} catch (Exception e) {
|
238
|
-
e.printStackTrace();
|
239
|
-
throw new RuntimeException(e);
|
240
|
-
}
|
322
|
+
ByteList bytes = arg.convertToString().getByteList();
|
323
|
+
inboundNetData.put(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize());
|
324
|
+
return this;
|
241
325
|
}
|
242
326
|
|
243
327
|
private enum SSLOperation {
|
@@ -257,7 +341,7 @@ public class MiniSSL extends RubyObject {
|
|
257
341
|
res = engine.unwrap(src.getRawBuffer(), dst.getRawBuffer());
|
258
342
|
break;
|
259
343
|
default:
|
260
|
-
throw new
|
344
|
+
throw new AssertionError("Unknown SSLOperation: " + sslOp);
|
261
345
|
}
|
262
346
|
|
263
347
|
switch (res.getStatus()) {
|
@@ -285,19 +369,11 @@ public class MiniSSL extends RubyObject {
|
|
285
369
|
}
|
286
370
|
}
|
287
371
|
|
288
|
-
// after each op, run any delegated tasks if needed
|
289
|
-
if(res.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
|
290
|
-
Runnable runnable;
|
291
|
-
while ((runnable = engine.getDelegatedTask()) != null) {
|
292
|
-
runnable.run();
|
293
|
-
}
|
294
|
-
}
|
295
|
-
|
296
372
|
return res;
|
297
373
|
}
|
298
374
|
|
299
375
|
@JRubyMethod
|
300
|
-
public IRubyObject read()
|
376
|
+
public IRubyObject read() {
|
301
377
|
try {
|
302
378
|
inboundNetData.flip();
|
303
379
|
|
@@ -310,11 +386,12 @@ public class MiniSSL extends RubyObject {
|
|
310
386
|
|
311
387
|
HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
|
312
388
|
boolean done = false;
|
313
|
-
SSLEngineResult res = null;
|
314
389
|
while (!done) {
|
390
|
+
SSLEngineResult res;
|
315
391
|
switch (handshakeStatus) {
|
316
392
|
case NEED_WRAP:
|
317
393
|
res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
|
394
|
+
handshakeStatus = res.getHandshakeStatus();
|
318
395
|
break;
|
319
396
|
case NEED_UNWRAP:
|
320
397
|
res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
|
@@ -322,13 +399,18 @@ public class MiniSSL extends RubyObject {
|
|
322
399
|
// need more data before we can shake more hands
|
323
400
|
done = true;
|
324
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();
|
325
410
|
break;
|
326
411
|
default:
|
327
412
|
done = true;
|
328
413
|
}
|
329
|
-
if (!done) {
|
330
|
-
handshakeStatus = res.getHandshakeStatus();
|
331
|
-
}
|
332
414
|
}
|
333
415
|
|
334
416
|
if (inboundNetData.hasRemaining()) {
|
@@ -342,65 +424,54 @@ public class MiniSSL extends RubyObject {
|
|
342
424
|
return getRuntime().getNil();
|
343
425
|
}
|
344
426
|
|
345
|
-
RubyString
|
346
|
-
|
347
|
-
|
348
|
-
} catch (Exception e) {
|
349
|
-
throw getRuntime().newEOFError(e.getMessage());
|
427
|
+
return RubyString.newString(getRuntime(), appDataByteList);
|
428
|
+
} catch (SSLException e) {
|
429
|
+
throw newSSLError(getRuntime(), e);
|
350
430
|
}
|
351
431
|
}
|
352
432
|
|
353
433
|
@JRubyMethod
|
354
434
|
public IRubyObject write(IRubyObject arg) {
|
355
|
-
|
356
|
-
|
357
|
-
outboundAppData = new MiniSSLBuffer(bls);
|
435
|
+
byte[] bls = arg.convertToString().getBytes();
|
436
|
+
outboundAppData = new MiniSSLBuffer(bls);
|
358
437
|
|
359
|
-
|
360
|
-
} catch (Exception e) {
|
361
|
-
e.printStackTrace();
|
362
|
-
throw new RuntimeException(e);
|
363
|
-
}
|
438
|
+
return getRuntime().newFixnum(bls.length);
|
364
439
|
}
|
365
440
|
|
366
441
|
@JRubyMethod
|
367
|
-
public IRubyObject extract()
|
442
|
+
public IRubyObject extract(ThreadContext context) {
|
368
443
|
try {
|
369
444
|
ByteList dataByteList = outboundNetData.asByteList();
|
370
445
|
if (dataByteList != null) {
|
371
|
-
RubyString
|
372
|
-
str.setValue(dataByteList);
|
373
|
-
return str;
|
446
|
+
return RubyString.newString(context.runtime, dataByteList);
|
374
447
|
}
|
375
448
|
|
376
449
|
if (!outboundAppData.hasRemaining()) {
|
377
|
-
return
|
450
|
+
return context.nil;
|
378
451
|
}
|
379
452
|
|
380
453
|
outboundNetData.clear();
|
381
454
|
doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
|
382
455
|
dataByteList = outboundNetData.asByteList();
|
383
456
|
if (dataByteList == null) {
|
384
|
-
return
|
457
|
+
return context.nil;
|
385
458
|
}
|
386
459
|
|
387
|
-
RubyString
|
388
|
-
|
389
|
-
|
390
|
-
return str;
|
391
|
-
} catch (Exception e) {
|
392
|
-
e.printStackTrace();
|
393
|
-
throw new RuntimeException(e);
|
460
|
+
return RubyString.newString(context.runtime, dataByteList);
|
461
|
+
} catch (SSLException e) {
|
462
|
+
throw newSSLError(getRuntime(), e);
|
394
463
|
}
|
395
464
|
}
|
396
465
|
|
397
466
|
@JRubyMethod
|
398
|
-
public IRubyObject peercert() throws CertificateEncodingException {
|
467
|
+
public IRubyObject peercert(ThreadContext context) throws CertificateEncodingException {
|
468
|
+
Certificate peerCert;
|
399
469
|
try {
|
400
|
-
|
401
|
-
} catch (SSLPeerUnverifiedException
|
402
|
-
|
470
|
+
peerCert = engine.getSession().getPeerCertificates()[0];
|
471
|
+
} catch (SSLPeerUnverifiedException e) {
|
472
|
+
peerCert = lastCheckedCert0; // null if trust check did not happen
|
403
473
|
}
|
474
|
+
return peerCert == null ? context.nil : JavaEmbedUtils.javaToRuby(context.runtime, peerCert.getEncoded());
|
404
475
|
}
|
405
476
|
|
406
477
|
@JRubyMethod(name = "init?")
|
@@ -419,4 +490,19 @@ public class MiniSSL extends RubyObject {
|
|
419
490
|
return getRuntime().getFalse();
|
420
491
|
}
|
421
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
|
+
|
422
508
|
}
|