puma 3.12.6 → 6.2.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (100) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1775 -451
  3. data/LICENSE +23 -20
  4. data/README.md +193 -65
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +59 -21
  7. data/docs/compile_options.md +55 -0
  8. data/docs/deployment.md +69 -58
  9. data/docs/fork_worker.md +31 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +9 -0
  14. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  15. data/{tools → docs}/jungle/rc.d/puma +2 -2
  16. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  17. data/docs/kubernetes.md +66 -0
  18. data/docs/nginx.md +2 -2
  19. data/docs/plugins.md +22 -12
  20. data/docs/rails_dev_mode.md +28 -0
  21. data/docs/restart.md +47 -22
  22. data/docs/signals.md +13 -11
  23. data/docs/stats.md +142 -0
  24. data/docs/systemd.md +94 -120
  25. data/docs/testing_benchmarks_local_files.md +150 -0
  26. data/docs/testing_test_rackup_ci_files.md +36 -0
  27. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  28. data/ext/puma_http11/ext_help.h +1 -1
  29. data/ext/puma_http11/extconf.rb +61 -3
  30. data/ext/puma_http11/http11_parser.c +103 -117
  31. data/ext/puma_http11/http11_parser.h +2 -2
  32. data/ext/puma_http11/http11_parser.java.rl +22 -38
  33. data/ext/puma_http11/http11_parser.rl +3 -3
  34. data/ext/puma_http11/http11_parser_common.rl +6 -6
  35. data/ext/puma_http11/mini_ssl.c +361 -99
  36. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  37. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  38. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
  39. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +248 -92
  40. data/ext/puma_http11/puma_http11.c +49 -57
  41. data/lib/puma/app/status.rb +71 -49
  42. data/lib/puma/binder.rb +242 -150
  43. data/lib/puma/cli.rb +38 -34
  44. data/lib/puma/client.rb +387 -244
  45. data/lib/puma/cluster/worker.rb +180 -0
  46. data/lib/puma/cluster/worker_handle.rb +97 -0
  47. data/lib/puma/cluster.rb +261 -243
  48. data/lib/puma/commonlogger.rb +21 -14
  49. data/lib/puma/configuration.rb +116 -88
  50. data/lib/puma/const.rb +101 -100
  51. data/lib/puma/control_cli.rb +115 -70
  52. data/lib/puma/detect.rb +33 -2
  53. data/lib/puma/dsl.rb +731 -134
  54. data/lib/puma/error_logger.rb +113 -0
  55. data/lib/puma/events.rb +16 -112
  56. data/lib/puma/io_buffer.rb +42 -5
  57. data/lib/puma/jruby_restart.rb +2 -59
  58. data/lib/puma/json_serialization.rb +96 -0
  59. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  60. data/lib/puma/launcher.rb +184 -133
  61. data/lib/puma/log_writer.rb +147 -0
  62. data/lib/puma/minissl/context_builder.rb +92 -0
  63. data/lib/puma/minissl.rb +246 -70
  64. data/lib/puma/null_io.rb +18 -1
  65. data/lib/puma/plugin/systemd.rb +90 -0
  66. data/lib/puma/plugin/tmp_restart.rb +3 -1
  67. data/lib/puma/plugin.rb +7 -13
  68. data/lib/puma/rack/builder.rb +7 -9
  69. data/lib/puma/rack/urlmap.rb +2 -0
  70. data/lib/puma/rack_default.rb +21 -4
  71. data/lib/puma/reactor.rb +85 -316
  72. data/lib/puma/request.rb +665 -0
  73. data/lib/puma/runner.rb +94 -69
  74. data/lib/puma/sd_notify.rb +149 -0
  75. data/lib/puma/server.rb +314 -771
  76. data/lib/puma/single.rb +20 -74
  77. data/lib/puma/state_file.rb +45 -8
  78. data/lib/puma/thread_pool.rb +142 -92
  79. data/lib/puma/util.rb +22 -10
  80. data/lib/puma.rb +60 -5
  81. data/lib/rack/handler/puma.rb +113 -91
  82. data/tools/Dockerfile +16 -0
  83. data/tools/trickletest.rb +0 -1
  84. metadata +54 -32
  85. data/ext/puma_http11/io_buffer.c +0 -155
  86. data/lib/puma/accept_nonblock.rb +0 -23
  87. data/lib/puma/compat.rb +0 -14
  88. data/lib/puma/convenient.rb +0 -25
  89. data/lib/puma/daemon_ext.rb +0 -33
  90. data/lib/puma/delegation.rb +0 -13
  91. data/lib/puma/java_io_buffer.rb +0 -47
  92. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  93. data/lib/puma/tcp_logger.rb +0 -41
  94. data/tools/jungle/README.md +0 -19
  95. data/tools/jungle/init.d/README.md +0 -61
  96. data/tools/jungle/init.d/puma +0 -421
  97. data/tools/jungle/init.d/run-puma +0 -18
  98. data/tools/jungle/upstart/README.md +0 -61
  99. data/tools/jungle/upstart/puma-manager.conf +0 -31
  100. data/tools/jungle/upstart/puma.conf +0 -69
@@ -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,21 +24,29 @@ 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;
29
+ import java.io.InputStream;
25
30
  import java.io.IOException;
31
+ import java.nio.Buffer;
26
32
  import java.nio.ByteBuffer;
27
33
  import java.security.KeyManagementException;
28
34
  import java.security.KeyStore;
29
35
  import java.security.KeyStoreException;
30
36
  import java.security.NoSuchAlgorithmException;
31
37
  import java.security.UnrecoverableKeyException;
38
+ import java.security.cert.Certificate;
32
39
  import java.security.cert.CertificateEncodingException;
33
40
  import java.security.cert.CertificateException;
41
+ import java.security.cert.X509Certificate;
42
+ import java.util.concurrent.ConcurrentHashMap;
43
+ import java.util.Map;
44
+ import java.util.function.Supplier;
34
45
 
35
46
  import static javax.net.ssl.SSLEngineResult.Status;
36
47
  import static javax.net.ssl.SSLEngineResult.HandshakeStatus;
37
48
 
38
- public class MiniSSL extends RubyObject {
49
+ public class MiniSSL extends RubyObject { // MiniSSL::Engine
39
50
  private static ObjectAllocator ALLOCATOR = new ObjectAllocator() {
40
51
  public IRubyObject allocate(Ruby runtime, RubyClass klass) {
41
52
  return new MiniSSL(runtime, klass);
@@ -46,11 +57,10 @@ public class MiniSSL extends RubyObject {
46
57
  RubyModule mPuma = runtime.defineModule("Puma");
47
58
  RubyModule ssl = mPuma.defineModuleUnder("MiniSSL");
48
59
 
49
- mPuma.defineClassUnder("SSLError",
50
- runtime.getClass("IOError"),
51
- runtime.getClass("IOError").getAllocator());
60
+ // Puma::MiniSSL::SSLError
61
+ ssl.defineClassUnder("SSLError", runtime.getStandardError(), runtime.getStandardError().getAllocator());
52
62
 
53
- RubyClass eng = ssl.defineClassUnder("Engine",runtime.getObject(),ALLOCATOR);
63
+ RubyClass eng = ssl.defineClassUnder("Engine", runtime.getObject(), ALLOCATOR);
54
64
  eng.defineAnnotatedMethods(MiniSSL.class);
55
65
  }
56
66
 
@@ -65,7 +75,7 @@ public class MiniSSL extends RubyObject {
65
75
 
66
76
  public void clear() { buffer.clear(); }
67
77
  public void compact() { buffer.compact(); }
68
- public void flip() { buffer.flip(); }
78
+ public void flip() { ((Buffer) buffer).flip(); }
69
79
  public boolean hasRemaining() { return buffer.hasRemaining(); }
70
80
  public int position() { return buffer.position(); }
71
81
 
@@ -76,11 +86,11 @@ public class MiniSSL extends RubyObject {
76
86
  /**
77
87
  * Writes bytes to the buffer after ensuring there's room
78
88
  */
79
- public void put(byte[] bytes) {
80
- if (buffer.remaining() < bytes.length) {
81
- resize(buffer.limit() + bytes.length);
89
+ private void put(byte[] bytes, final int offset, final int length) {
90
+ if (buffer.remaining() < length) {
91
+ resize(buffer.limit() + length);
82
92
  }
83
- buffer.put(bytes);
93
+ buffer.put(bytes, offset, length);
84
94
  }
85
95
 
86
96
  /**
@@ -89,7 +99,7 @@ public class MiniSSL extends RubyObject {
89
99
  public void resize(int newCapacity) {
90
100
  if (newCapacity > buffer.capacity()) {
91
101
  ByteBuffer dstTmp = ByteBuffer.allocate(newCapacity);
92
- buffer.flip();
102
+ flip();
93
103
  dstTmp.put(buffer);
94
104
  buffer = dstTmp;
95
105
  } else {
@@ -101,7 +111,7 @@ public class MiniSSL extends RubyObject {
101
111
  * Drains the buffer to a ByteList, or returns null for an empty buffer
102
112
  */
103
113
  public ByteList asByteList() {
104
- buffer.flip();
114
+ flip();
105
115
  if (!buffer.hasRemaining()) {
106
116
  buffer.clear();
107
117
  return null;
@@ -111,7 +121,7 @@ public class MiniSSL extends RubyObject {
111
121
 
112
122
  buffer.get(bss);
113
123
  buffer.clear();
114
- return new ByteList(bss);
124
+ return new ByteList(bss, false);
115
125
  }
116
126
 
117
127
  @Override
@@ -119,6 +129,8 @@ public class MiniSSL extends RubyObject {
119
129
  }
120
130
 
121
131
  private SSLEngine engine;
132
+ private boolean closed;
133
+ private boolean handshake;
122
134
  private MiniSSLBuffer inboundNetData;
123
135
  private MiniSSLBuffer outboundAppData;
124
136
  private MiniSSLBuffer outboundNetData;
@@ -127,42 +139,119 @@ public class MiniSSL extends RubyObject {
127
139
  super(runtime, klass);
128
140
  }
129
141
 
130
- @JRubyMethod(meta = true)
131
- public static IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext) {
132
- RubyClass klass = (RubyClass) recv;
142
+ private static Map<String, KeyManagerFactory> keyManagerFactoryMap = new ConcurrentHashMap<String, KeyManagerFactory>();
143
+ private static Map<String, TrustManagerFactory> trustManagerFactoryMap = new ConcurrentHashMap<String, TrustManagerFactory>();
144
+
145
+ @JRubyMethod(meta = true) // Engine.server
146
+ public static synchronized IRubyObject server(ThreadContext context, IRubyObject recv, IRubyObject miniSSLContext)
147
+ throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException {
148
+ // Create the KeyManagerFactory and TrustManagerFactory for this server
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
+ }
133
175
 
134
- return klass.newInstance(context,
135
- new IRubyObject[] { miniSSLContext },
136
- Block.NULL_BLOCK);
137
- }
176
+ KeyStore ks = KeyStore.getInstance(keystoreType);
177
+ InputStream is = new FileInputStream(keystoreFile);
178
+ try {
179
+ ks.load(is, keystorePass);
180
+ } finally {
181
+ is.close();
182
+ }
183
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
184
+ kmf.init(ks, keystorePass);
185
+ keyManagerFactoryMap.put(keystoreFile, kmf);
186
+
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);
198
+ }
138
199
 
139
- @JRubyMethod
140
- public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
141
- throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
142
- KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
143
- KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
200
+ RubyClass klass = (RubyClass) recv;
201
+ return klass.newInstance(context, miniSSLContext, Block.NULL_BLOCK);
202
+ }
144
203
 
145
- char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray();
146
- String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
147
- ks.load(new FileInputStream(keystoreFile), password);
148
- ts.load(new FileInputStream(keystoreFile), password);
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
+ }
149
208
 
150
- KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
151
- kmf.init(ks, password);
209
+ private static boolean isDefaultSymbol(ThreadContext context, IRubyObject truststore) {
210
+ return context.runtime.newSymbol("default").equals(truststore);
211
+ }
152
212
 
153
- TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
154
- tmf.init(ts);
213
+ @JRubyMethod
214
+ public IRubyObject initialize(ThreadContext context, IRubyObject miniSSLContext)
215
+ throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
216
+
217
+ String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString();
218
+ KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
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);
224
+ }
155
225
 
156
226
  SSLContext sslCtx = SSLContext.getInstance("TLS");
157
227
 
158
- sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
228
+ sslCtx.init(kmf.getKeyManagers(), getTrustManagers(tmf), null);
229
+ closed = false;
230
+ handshake = false;
159
231
  engine = sslCtx.createSSLEngine();
160
232
 
161
- String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
162
- engine.setEnabledProtocols(protocols);
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
+ }
241
+
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());
249
+ }
250
+ engine.setEnabledProtocols(enabledProtocols);
251
+
163
252
  engine.setUseClientMode(false);
164
253
 
165
- long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue();
254
+ long verify_mode = miniSSLContext.callMethod(context, "verify_mode").convertToInteger("to_i").getLongValue();
166
255
  if ((verify_mode & 0x1) != 0) { // 'peer'
167
256
  engine.setWantClientAuth(true);
168
257
  }
@@ -170,10 +259,11 @@ public class MiniSSL extends RubyObject {
170
259
  engine.setNeedClientAuth(true);
171
260
  }
172
261
 
173
- IRubyObject sslCipherListObject = miniSSLContext.callMethod(threadContext, "ssl_cipher_list");
174
- if (!sslCipherListObject.isNil()) {
175
- String[] sslCipherList = sslCipherListObject.convertToString().asJavaString().split(",");
176
- engine.setEnabledCipherSuites(sslCipherList);
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());
177
267
  }
178
268
 
179
269
  SSLSession session = engine.getSession();
@@ -185,16 +275,53 @@ public class MiniSSL extends RubyObject {
185
275
  return this;
186
276
  }
187
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
+
188
320
  @JRubyMethod
189
321
  public IRubyObject inject(IRubyObject arg) {
190
- try {
191
- byte[] bytes = arg.convertToString().getBytes();
192
- inboundNetData.put(bytes);
193
- return this;
194
- } catch (Exception e) {
195
- e.printStackTrace();
196
- throw new RuntimeException(e);
197
- }
322
+ ByteList bytes = arg.convertToString().getByteList();
323
+ inboundNetData.put(bytes.unsafeBytes(), bytes.getBegin(), bytes.getRealSize());
324
+ return this;
198
325
  }
199
326
 
200
327
  private enum SSLOperation {
@@ -214,7 +341,7 @@ public class MiniSSL extends RubyObject {
214
341
  res = engine.unwrap(src.getRawBuffer(), dst.getRawBuffer());
215
342
  break;
216
343
  default:
217
- throw new IllegalStateException("Unknown SSLOperation: " + sslOp);
344
+ throw new AssertionError("Unknown SSLOperation: " + sslOp);
218
345
  }
219
346
 
220
347
  switch (res.getStatus()) {
@@ -229,17 +356,16 @@ public class MiniSSL extends RubyObject {
229
356
  // need to wait for more data to come in before we retry
230
357
  retryOp = false;
231
358
  break;
359
+ case CLOSED:
360
+ closed = true;
361
+ retryOp = false;
362
+ break;
232
363
  default:
233
- // other cases are OK and CLOSED. We're done here.
364
+ // other case is OK. We're done here.
234
365
  retryOp = false;
235
366
  }
236
- }
237
-
238
- // after each op, run any delegated tasks if needed
239
- if(engine.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
240
- Runnable runnable;
241
- while ((runnable = engine.getDelegatedTask()) != null) {
242
- runnable.run();
367
+ if (res.getHandshakeStatus() == HandshakeStatus.FINISHED) {
368
+ handshake = true;
243
369
  }
244
370
  }
245
371
 
@@ -247,7 +373,7 @@ public class MiniSSL extends RubyObject {
247
373
  }
248
374
 
249
375
  @JRubyMethod
250
- public IRubyObject read() throws Exception {
376
+ public IRubyObject read() {
251
377
  try {
252
378
  inboundNetData.flip();
253
379
 
@@ -261,21 +387,30 @@ public class MiniSSL extends RubyObject {
261
387
  HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
262
388
  boolean done = false;
263
389
  while (!done) {
390
+ SSLEngineResult res;
264
391
  switch (handshakeStatus) {
265
392
  case NEED_WRAP:
266
- doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
393
+ res = doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
394
+ handshakeStatus = res.getHandshakeStatus();
267
395
  break;
268
396
  case NEED_UNWRAP:
269
- SSLEngineResult res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
397
+ res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
270
398
  if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
271
399
  // need more data before we can shake more hands
272
400
  done = true;
273
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();
274
410
  break;
275
411
  default:
276
412
  done = true;
277
413
  }
278
- handshakeStatus = engine.getHandshakeStatus();
279
414
  }
280
415
 
281
416
  if (inboundNetData.hasRemaining()) {
@@ -289,64 +424,85 @@ public class MiniSSL extends RubyObject {
289
424
  return getRuntime().getNil();
290
425
  }
291
426
 
292
- RubyString str = getRuntime().newString("");
293
- str.setValue(appDataByteList);
294
- return str;
295
- } catch (Exception e) {
296
- throw getRuntime().newEOFError(e.getMessage());
427
+ return RubyString.newString(getRuntime(), appDataByteList);
428
+ } catch (SSLException e) {
429
+ throw newSSLError(getRuntime(), e);
297
430
  }
298
431
  }
299
432
 
300
433
  @JRubyMethod
301
434
  public IRubyObject write(IRubyObject arg) {
302
- try {
303
- byte[] bls = arg.convertToString().getBytes();
304
- outboundAppData = new MiniSSLBuffer(bls);
435
+ byte[] bls = arg.convertToString().getBytes();
436
+ outboundAppData = new MiniSSLBuffer(bls);
305
437
 
306
- return getRuntime().newFixnum(bls.length);
307
- } catch (Exception e) {
308
- e.printStackTrace();
309
- throw new RuntimeException(e);
310
- }
438
+ return getRuntime().newFixnum(bls.length);
311
439
  }
312
440
 
313
441
  @JRubyMethod
314
- public IRubyObject extract() throws SSLException {
442
+ public IRubyObject extract(ThreadContext context) {
315
443
  try {
316
444
  ByteList dataByteList = outboundNetData.asByteList();
317
445
  if (dataByteList != null) {
318
- RubyString str = getRuntime().newString("");
319
- str.setValue(dataByteList);
320
- return str;
446
+ return RubyString.newString(context.runtime, dataByteList);
321
447
  }
322
448
 
323
449
  if (!outboundAppData.hasRemaining()) {
324
- return getRuntime().getNil();
450
+ return context.nil;
325
451
  }
326
452
 
327
453
  outboundNetData.clear();
328
454
  doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
329
455
  dataByteList = outboundNetData.asByteList();
330
456
  if (dataByteList == null) {
331
- return getRuntime().getNil();
457
+ return context.nil;
332
458
  }
333
459
 
334
- RubyString str = getRuntime().newString("");
335
- str.setValue(dataByteList);
336
-
337
- return str;
338
- } catch (Exception e) {
339
- e.printStackTrace();
340
- throw new RuntimeException(e);
460
+ return RubyString.newString(context.runtime, dataByteList);
461
+ } catch (SSLException e) {
462
+ throw newSSLError(getRuntime(), e);
341
463
  }
342
464
  }
343
465
 
344
466
  @JRubyMethod
345
- public IRubyObject peercert() throws CertificateEncodingException {
467
+ public IRubyObject peercert(ThreadContext context) throws CertificateEncodingException {
468
+ Certificate peerCert;
346
469
  try {
347
- return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
348
- } catch (SSLPeerUnverifiedException ex) {
349
- return getRuntime().getNil();
470
+ peerCert = engine.getSession().getPeerCertificates()[0];
471
+ } catch (SSLPeerUnverifiedException e) {
472
+ peerCert = lastCheckedCert0; // null if trust check did not happen
473
+ }
474
+ return peerCert == null ? context.nil : JavaEmbedUtils.javaToRuby(context.runtime, peerCert.getEncoded());
475
+ }
476
+
477
+ @JRubyMethod(name = "init?")
478
+ public IRubyObject isInit(ThreadContext context) {
479
+ return handshake ? getRuntime().getFalse() : getRuntime().getTrue();
480
+ }
481
+
482
+ @JRubyMethod
483
+ public IRubyObject shutdown() {
484
+ if (closed || engine.isInboundDone() && engine.isOutboundDone()) {
485
+ if (engine.isOutboundDone()) {
486
+ engine.closeOutbound();
487
+ }
488
+ return getRuntime().getTrue();
489
+ } else {
490
+ return getRuntime().getFalse();
350
491
  }
351
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
+
352
508
  }