puma 5.5.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.

Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +336 -3
  3. data/README.md +61 -16
  4. data/bin/puma-wild +1 -1
  5. data/docs/architecture.md +4 -4
  6. data/docs/compile_options.md +34 -0
  7. data/docs/fork_worker.md +1 -3
  8. data/docs/nginx.md +1 -1
  9. data/docs/signals.md +1 -0
  10. data/docs/systemd.md +1 -2
  11. data/docs/testing_benchmarks_local_files.md +150 -0
  12. data/docs/testing_test_rackup_ci_files.md +36 -0
  13. data/ext/puma_http11/extconf.rb +28 -14
  14. data/ext/puma_http11/http11_parser.c +1 -1
  15. data/ext/puma_http11/http11_parser.h +1 -1
  16. data/ext/puma_http11/http11_parser.java.rl +2 -2
  17. data/ext/puma_http11/http11_parser.rl +2 -2
  18. data/ext/puma_http11/http11_parser_common.rl +2 -2
  19. data/ext/puma_http11/mini_ssl.c +135 -23
  20. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  21. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  22. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
  23. data/ext/puma_http11/puma_http11.c +18 -10
  24. data/lib/puma/app/status.rb +7 -4
  25. data/lib/puma/binder.rb +62 -51
  26. data/lib/puma/cli.rb +19 -20
  27. data/lib/puma/client.rb +108 -26
  28. data/lib/puma/cluster/worker.rb +23 -16
  29. data/lib/puma/cluster/worker_handle.rb +8 -1
  30. data/lib/puma/cluster.rb +62 -41
  31. data/lib/puma/commonlogger.rb +21 -14
  32. data/lib/puma/configuration.rb +76 -55
  33. data/lib/puma/const.rb +133 -97
  34. data/lib/puma/control_cli.rb +21 -18
  35. data/lib/puma/detect.rb +12 -2
  36. data/lib/puma/dsl.rb +270 -55
  37. data/lib/puma/error_logger.rb +18 -9
  38. data/lib/puma/events.rb +6 -126
  39. data/lib/puma/io_buffer.rb +39 -4
  40. data/lib/puma/jruby_restart.rb +2 -1
  41. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  42. data/lib/puma/launcher.rb +114 -175
  43. data/lib/puma/log_writer.rb +147 -0
  44. data/lib/puma/minissl/context_builder.rb +30 -16
  45. data/lib/puma/minissl.rb +126 -17
  46. data/lib/puma/null_io.rb +5 -0
  47. data/lib/puma/plugin/systemd.rb +90 -0
  48. data/lib/puma/plugin/tmp_restart.rb +1 -1
  49. data/lib/puma/plugin.rb +1 -1
  50. data/lib/puma/rack/builder.rb +6 -6
  51. data/lib/puma/rack_default.rb +19 -4
  52. data/lib/puma/reactor.rb +19 -10
  53. data/lib/puma/request.rb +365 -161
  54. data/lib/puma/runner.rb +55 -22
  55. data/lib/puma/sd_notify.rb +149 -0
  56. data/lib/puma/server.rb +91 -94
  57. data/lib/puma/single.rb +13 -11
  58. data/lib/puma/state_file.rb +39 -7
  59. data/lib/puma/thread_pool.rb +25 -21
  60. data/lib/puma/util.rb +12 -14
  61. data/lib/puma.rb +12 -11
  62. data/lib/rack/handler/puma.rb +113 -86
  63. data/tools/Dockerfile +1 -1
  64. metadata +11 -6
  65. data/lib/puma/queue_close.rb +0 -26
  66. data/lib/puma/systemd.rb +0 -46
@@ -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
- mPuma.defineClassUnder("SSLError",
54
- runtime.getClass("IOError"),
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
- public void put(byte[] bytes) {
84
- if (buffer.remaining() < bytes.length) {
85
- 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);
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").convertToString().asJavaString();
144
- char[] password = miniSSLContext.callMethod(context, "keystore_pass").convertToString().asJavaString().toCharArray();
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(KeyStore.getDefaultType());
176
+ KeyStore ks = KeyStore.getInstance(keystoreType);
147
177
  InputStream is = new FileInputStream(keystoreFile);
148
178
  try {
149
- ks.load(is, password);
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, password);
184
+ kmf.init(ks, keystorePass);
155
185
  keyManagerFactoryMap.put(keystoreFile, kmf);
156
186
 
157
- KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
158
- is = new FileInputStream(keystoreFile);
159
- try {
160
- ts.load(is, password);
161
- } finally {
162
- is.close();
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
- new IRubyObject[] { miniSSLContext },
171
- Block.NULL_BLOCK);
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 threadContext, IRubyObject miniSSLContext)
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(threadContext, "keystore").convertToString().asJavaString();
217
+ String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString();
181
218
  KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
182
- TrustManagerFactory tmf = trustManagerFactoryMap.get(keystoreFile);
183
- if(kmf == null || tmf == null) {
184
- throw new KeyStoreException("Could not find KeyManagerFactory/TrustManagerFactory for keystore: " + 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);
185
224
  }
186
225
 
187
226
  SSLContext sslCtx = SSLContext.getInstance("TLS");
188
227
 
189
- sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
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[] protocols;
195
- if(miniSSLContext.callMethod(threadContext, "no_tlsv1").isTrue()) {
196
- protocols = new String[] { "TLSv1.1", "TLSv1.2" };
197
- } else {
198
- protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
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
- if(miniSSLContext.callMethod(threadContext, "no_tlsv1_1").isTrue()) {
202
- protocols = new String[] { "TLSv1.2" };
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(threadContext, "verify_mode").convertToInteger("to_i").getLongValue();
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 sslCipherListObject = miniSSLContext.callMethod(threadContext, "ssl_cipher_list");
217
- if (!sslCipherListObject.isNil()) {
218
- String[] sslCipherList = sslCipherListObject.convertToString().asJavaString().split(",");
219
- 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());
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
- try {
234
- byte[] bytes = arg.convertToString().getBytes();
235
- inboundNetData.put(bytes);
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 IllegalStateException("Unknown SSLOperation: " + sslOp);
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() throws Exception {
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 str = getRuntime().newString("");
346
- str.setValue(appDataByteList);
347
- return str;
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
- try {
356
- byte[] bls = arg.convertToString().getBytes();
357
- outboundAppData = new MiniSSLBuffer(bls);
435
+ byte[] bls = arg.convertToString().getBytes();
436
+ outboundAppData = new MiniSSLBuffer(bls);
358
437
 
359
- return getRuntime().newFixnum(bls.length);
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() throws SSLException {
442
+ public IRubyObject extract(ThreadContext context) {
368
443
  try {
369
444
  ByteList dataByteList = outboundNetData.asByteList();
370
445
  if (dataByteList != null) {
371
- RubyString str = getRuntime().newString("");
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 getRuntime().getNil();
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 getRuntime().getNil();
457
+ return context.nil;
385
458
  }
386
459
 
387
- RubyString str = getRuntime().newString("");
388
- str.setValue(dataByteList);
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
- return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
401
- } catch (SSLPeerUnverifiedException ex) {
402
- return getRuntime().getNil();
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
  }
@@ -36,13 +36,13 @@ static VALUE global_request_method;
36
36
  static VALUE global_request_uri;
37
37
  static VALUE global_fragment;
38
38
  static VALUE global_query_string;
39
- static VALUE global_http_version;
39
+ static VALUE global_server_protocol;
40
40
  static VALUE global_request_path;
41
41
 
42
42
  /** Defines common length and error messages for input length validation. */
43
43
  #define QUOTE(s) #s
44
- #define EXPLAIN_MAX_LENGTH_VALUE(s) QUOTE(s)
45
- #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " EXPLAIN_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
44
+ #define EXPAND_MAX_LENGTH_VALUE(s) QUOTE(s)
45
+ #define DEF_MAX_LENGTH(N,length) const size_t MAX_##N##_LENGTH = length; const char *MAX_##N##_LENGTH_ERR = "HTTP element " # N " is longer than the " EXPAND_MAX_LENGTH_VALUE(length) " allowed length (was %d)"
46
46
 
47
47
  /** Validates the max length of given input and throws an HttpParserError exception if over. */
48
48
  #define VALIDATE_MAX_LENGTH(len, N) if(len > MAX_##N##_LENGTH) { rb_raise(eHttpParserError, MAX_##N##_LENGTH_ERR, len); }
@@ -52,15 +52,23 @@ static VALUE global_request_path;
52
52
 
53
53
 
54
54
  /* Defines the maximum allowed lengths for various input elements.*/
55
+ #ifndef PUMA_REQUEST_URI_MAX_LENGTH
56
+ #define PUMA_REQUEST_URI_MAX_LENGTH (1024 * 12)
57
+ #endif
58
+
59
+ #ifndef PUMA_REQUEST_PATH_MAX_LENGTH
60
+ #define PUMA_REQUEST_PATH_MAX_LENGTH (8192)
61
+ #endif
62
+
55
63
  #ifndef PUMA_QUERY_STRING_MAX_LENGTH
56
64
  #define PUMA_QUERY_STRING_MAX_LENGTH (1024 * 10)
57
65
  #endif
58
66
 
59
67
  DEF_MAX_LENGTH(FIELD_NAME, 256);
60
68
  DEF_MAX_LENGTH(FIELD_VALUE, 80 * 1024);
61
- DEF_MAX_LENGTH(REQUEST_URI, 1024 * 12);
69
+ DEF_MAX_LENGTH(REQUEST_URI, PUMA_REQUEST_URI_MAX_LENGTH);
62
70
  DEF_MAX_LENGTH(FRAGMENT, 1024); /* Don't know if this length is specified somewhere or not */
63
- DEF_MAX_LENGTH(REQUEST_PATH, 8192);
71
+ DEF_MAX_LENGTH(REQUEST_PATH, PUMA_REQUEST_PATH_MAX_LENGTH);
64
72
  DEF_MAX_LENGTH(QUERY_STRING, PUMA_QUERY_STRING_MAX_LENGTH);
65
73
  DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32)));
66
74
 
@@ -236,10 +244,10 @@ void query_string(puma_parser* hp, const char *at, size_t length)
236
244
  rb_hash_aset(hp->request, global_query_string, val);
237
245
  }
238
246
 
239
- void http_version(puma_parser* hp, const char *at, size_t length)
247
+ void server_protocol(puma_parser* hp, const char *at, size_t length)
240
248
  {
241
249
  VALUE val = rb_str_new(at, length);
242
- rb_hash_aset(hp->request, global_http_version, val);
250
+ rb_hash_aset(hp->request, global_server_protocol, val);
243
251
  }
244
252
 
245
253
  /** Finalizes the request header to have a bunch of stuff that's
@@ -281,7 +289,7 @@ VALUE HttpParser_alloc(VALUE klass)
281
289
  hp->fragment = fragment;
282
290
  hp->request_path = request_path;
283
291
  hp->query_string = query_string;
284
- hp->http_version = http_version;
292
+ hp->server_protocol = server_protocol;
285
293
  hp->header_done = header_done;
286
294
  hp->request = Qnil;
287
295
 
@@ -451,7 +459,7 @@ VALUE HttpParser_body(VALUE self) {
451
459
  void Init_mini_ssl(VALUE mod);
452
460
  #endif
453
461
 
454
- void Init_puma_http11()
462
+ void Init_puma_http11(void)
455
463
  {
456
464
 
457
465
  VALUE mPuma = rb_define_module("Puma");
@@ -461,7 +469,7 @@ void Init_puma_http11()
461
469
  DEF_GLOBAL(request_uri, "REQUEST_URI");
462
470
  DEF_GLOBAL(fragment, "FRAGMENT");
463
471
  DEF_GLOBAL(query_string, "QUERY_STRING");
464
- DEF_GLOBAL(http_version, "HTTP_VERSION");
472
+ DEF_GLOBAL(server_protocol, "SERVER_PROTOCOL");
465
473
  DEF_GLOBAL(request_path, "REQUEST_PATH");
466
474
 
467
475
  eHttpParserError = rb_define_class_under(mPuma, "HttpParserError", rb_eIOError);
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'puma/json_serialization'
2
+ require_relative '../json_serialization'
3
3
 
4
4
  module Puma
5
5
  module App
@@ -39,6 +39,9 @@ module Puma
39
39
  when 'phased-restart'
40
40
  @launcher.phased_restart ? 200 : 404
41
41
 
42
+ when 'refork'
43
+ @launcher.refork ? 200 : 404
44
+
42
45
  when 'reload-worker-directory'
43
46
  @launcher.send(:reload_worker_directory) ? 200 : 404
44
47
 
@@ -77,13 +80,13 @@ module Puma
77
80
 
78
81
  def authenticate(env)
79
82
  return true unless @auth_token
80
- env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
83
+ env['QUERY_STRING'].to_s.split('&;').include? "token=#{@auth_token}"
81
84
  end
82
85
 
83
86
  def rack_response(status, body, content_type='application/json')
84
87
  headers = {
85
- 'Content-Type' => content_type,
86
- 'Content-Length' => body.bytesize.to_s
88
+ 'content-type' => content_type,
89
+ 'content-length' => body.bytesize.to_s
87
90
  }
88
91
 
89
92
  [status, headers, [body]]