puma 5.6.9 → 6.0.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +96 -28
  3. data/LICENSE +0 -0
  4. data/README.md +21 -17
  5. data/bin/puma-wild +1 -1
  6. data/docs/architecture.md +0 -0
  7. data/docs/compile_options.md +34 -0
  8. data/docs/deployment.md +0 -0
  9. data/docs/fork_worker.md +1 -3
  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 +0 -0
  14. data/docs/jungle/rc.d/README.md +0 -0
  15. data/docs/jungle/rc.d/puma.conf +0 -0
  16. data/docs/kubernetes.md +0 -0
  17. data/docs/nginx.md +0 -0
  18. data/docs/plugins.md +0 -0
  19. data/docs/rails_dev_mode.md +0 -0
  20. data/docs/restart.md +0 -0
  21. data/docs/signals.md +0 -0
  22. data/docs/stats.md +0 -0
  23. data/docs/systemd.md +0 -0
  24. data/docs/testing_benchmarks_local_files.md +150 -0
  25. data/docs/testing_test_rackup_ci_files.md +36 -0
  26. data/ext/puma_http11/PumaHttp11Service.java +0 -0
  27. data/ext/puma_http11/ext_help.h +0 -0
  28. data/ext/puma_http11/extconf.rb +11 -8
  29. data/ext/puma_http11/http11_parser.c +1 -1
  30. data/ext/puma_http11/http11_parser.h +1 -1
  31. data/ext/puma_http11/http11_parser.java.rl +2 -2
  32. data/ext/puma_http11/http11_parser.rl +2 -2
  33. data/ext/puma_http11/http11_parser_common.rl +2 -2
  34. data/ext/puma_http11/mini_ssl.c +36 -15
  35. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
  36. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -5
  37. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  38. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +156 -53
  39. data/ext/puma_http11/puma_http11.c +17 -9
  40. data/lib/puma/app/status.rb +3 -3
  41. data/lib/puma/binder.rb +36 -42
  42. data/lib/puma/cli.rb +11 -17
  43. data/lib/puma/client.rb +29 -53
  44. data/lib/puma/cluster/worker.rb +13 -11
  45. data/lib/puma/cluster/worker_handle.rb +4 -1
  46. data/lib/puma/cluster.rb +28 -25
  47. data/lib/puma/commonlogger.rb +0 -0
  48. data/lib/puma/configuration.rb +74 -58
  49. data/lib/puma/const.rb +14 -26
  50. data/lib/puma/control_cli.rb +3 -6
  51. data/lib/puma/detect.rb +2 -0
  52. data/lib/puma/dsl.rb +93 -52
  53. data/lib/puma/error_logger.rb +17 -9
  54. data/lib/puma/events.rb +6 -126
  55. data/lib/puma/io_buffer.rb +29 -4
  56. data/lib/puma/jruby_restart.rb +2 -1
  57. data/lib/puma/json_serialization.rb +0 -0
  58. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  59. data/lib/puma/launcher.rb +96 -156
  60. data/lib/puma/log_writer.rb +137 -0
  61. data/lib/puma/minissl/context_builder.rb +23 -12
  62. data/lib/puma/minissl.rb +82 -11
  63. data/lib/puma/null_io.rb +0 -0
  64. data/lib/puma/plugin/tmp_restart.rb +1 -1
  65. data/lib/puma/plugin.rb +0 -0
  66. data/lib/puma/rack/builder.rb +4 -4
  67. data/lib/puma/rack/urlmap.rb +0 -0
  68. data/lib/puma/rack_default.rb +1 -1
  69. data/lib/puma/reactor.rb +3 -3
  70. data/lib/puma/request.rb +295 -177
  71. data/lib/puma/runner.rb +41 -20
  72. data/lib/puma/server.rb +53 -66
  73. data/lib/puma/single.rb +10 -10
  74. data/lib/puma/state_file.rb +1 -4
  75. data/lib/puma/systemd.rb +3 -2
  76. data/lib/puma/thread_pool.rb +16 -13
  77. data/lib/puma/util.rb +0 -11
  78. data/lib/puma.rb +10 -9
  79. data/lib/rack/handler/puma.rb +9 -9
  80. data/tools/Dockerfile +0 -0
  81. data/tools/trickletest.rb +0 -0
  82. metadata +9 -6
  83. data/lib/puma/queue_close.rb +0 -26
  84. data/lib/rack/version_restriction.rb +0 -15
@@ -297,7 +297,7 @@ case 13:
297
297
  tr18:
298
298
  #line 65 "ext/puma_http11/http11_parser.rl"
299
299
  {
300
- parser->http_version(parser, PTR_TO(mark), LEN(mark, p));
300
+ parser->server_protocol(parser, PTR_TO(mark), LEN(mark, p));
301
301
  }
302
302
  goto st14;
303
303
  tr26:
@@ -46,7 +46,7 @@ typedef struct puma_parser {
46
46
  element_cb fragment;
47
47
  element_cb request_path;
48
48
  element_cb query_string;
49
- element_cb http_version;
49
+ element_cb server_protocol;
50
50
  element_cb header_done;
51
51
 
52
52
  char buf[BUFFER_LEN];
@@ -39,8 +39,8 @@ public class Http11Parser {
39
39
  Http11.query_string(runtime, parser.data, parser.buffer, parser.query_start, fpc-parser.query_start);
40
40
  }
41
41
 
42
- action http_version {
43
- Http11.http_version(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
42
+ action server_protocol {
43
+ Http11.server_protocol(runtime, parser.data, parser.buffer, parser.mark, fpc-parser.mark);
44
44
  }
45
45
 
46
46
  action request_path {
@@ -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 http_version {
66
- parser->http_version(parser, PTR_TO(mark), LEN(mark, fpc));
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
- HTTP_Version = ( "HTTP/" http_number ) >mark %http_version ;
42
- Request_Line = ( Method " " Request_URI ("#" Fragment){0,1} " " HTTP_Version CRLF ) ;
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
 
@@ -210,25 +210,28 @@ sslctx_alloc(VALUE klass) {
210
210
  VALUE
211
211
  sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
212
212
  SSL_CTX* ctx;
213
-
214
- #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
215
- int min;
216
- #endif
217
213
  int ssl_options;
218
214
  VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
219
215
  verification_flags, session_id_bytes, cert_pem, key_pem;
220
- #ifndef HAVE_SSL_CTX_SET_DH_AUTO
221
- DH *dh;
222
- #endif
223
216
  BIO *bio;
224
217
  X509 *x509;
225
218
  EVP_PKEY *pkey;
226
-
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
227
225
  #if OPENSSL_VERSION_NUMBER < 0x10002000L
228
226
  EC_KEY *ecdh;
229
227
  #endif
228
+ #ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
229
+ VALUE reuse, reuse_cache_size, reuse_timeout;
230
230
 
231
- TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx);
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
232
235
 
233
236
  key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0);
234
237
 
@@ -248,6 +251,8 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
248
251
 
249
252
  no_tlsv1_1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0);
250
253
 
254
+ TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx);
255
+
251
256
  if (!NIL_P(cert)) {
252
257
  StringValue(cert);
253
258
 
@@ -270,8 +275,11 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
270
275
  x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
271
276
 
272
277
  if (SSL_CTX_use_certificate(ctx, x509) != 1) {
278
+ BIO_free(bio);
273
279
  raise_file_error("SSL_CTX_use_certificate", RSTRING_PTR(cert_pem));
274
280
  }
281
+ X509_free(x509);
282
+ BIO_free(bio);
275
283
  }
276
284
 
277
285
  if (!NIL_P(key_pem)) {
@@ -280,8 +288,11 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
280
288
  pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
281
289
 
282
290
  if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
291
+ BIO_free(bio);
283
292
  raise_file_error("SSL_CTX_use_PrivateKey", RSTRING_PTR(key_pem));
284
293
  }
294
+ EVP_PKEY_free(pkey);
295
+ BIO_free(bio);
285
296
  }
286
297
 
287
298
  verification_flags = rb_funcall(mini_ssl_ctx, rb_intern_const("verification_flags"), 0);
@@ -314,8 +325,6 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
314
325
 
315
326
  SSL_CTX_set_min_proto_version(ctx, min);
316
327
 
317
- SSL_CTX_set_options(ctx, ssl_options);
318
-
319
328
  #else
320
329
  /* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */
321
330
  ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;
@@ -326,10 +335,23 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
326
335
  if(RTEST(no_tlsv1_1)) {
327
336
  ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
328
337
  }
329
- SSL_CTX_set_options(ctx, ssl_options);
330
338
  #endif
331
339
 
332
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
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);
333
355
 
334
356
  if (!NIL_P(ssl_cipher_filter)) {
335
357
  StringValue(ssl_cipher_filter);
@@ -340,8 +362,7 @@ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
340
362
  }
341
363
 
342
364
  #if OPENSSL_VERSION_NUMBER < 0x10002000L
343
- // Remove this case if OpenSSL 1.0.1 (now EOL) support is no
344
- // longer needed.
365
+ // Remove this case if OpenSSL 1.0.1 (now EOL) support is no longer needed.
345
366
  ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
346
367
  if (ecdh) {
347
368
  SSL_CTX_set_tmp_ecdh(ctx, ecdh);
File without changes
@@ -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 HTTP_VERSION_BYTELIST = new ByteList(ByteList.plain("HTTP_VERSION"));
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) {
@@ -99,8 +99,6 @@ public class Http11 extends RubyObject {
99
99
  int bite = b.get(i) & 0xFF;
100
100
  if(bite == '-') {
101
101
  b.set(i, (byte)'_');
102
- } else if(bite == '_') {
103
- b.set(i, (byte)',');
104
102
  } else {
105
103
  b.set(i, (byte)Character.toUpperCase(bite));
106
104
  }
@@ -155,9 +153,9 @@ public class Http11 extends RubyObject {
155
153
  req.fastASet(RubyString.newStringShared(runtime, QUERY_STRING_BYTELIST),val);
156
154
  }
157
155
 
158
- public static void http_version(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
156
+ public static void server_protocol(Ruby runtime, RubyHash req, ByteList buffer, int at, int length) {
159
157
  RubyString val = RubyString.newString(runtime,new ByteList(buffer,at,length));
160
- req.fastASet(RubyString.newStringShared(runtime, HTTP_VERSION_BYTELIST),val);
158
+ req.fastASet(RubyString.newStringShared(runtime, SERVER_PROTOCOL_BYTELIST),val);
161
159
  }
162
160
 
163
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.http_version(runtime, parser.data, parser.buffer, parser.mark, p-parser.mark);
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
- mPuma.defineClassUnder("SSLError",
55
- runtime.getClass("IOError"),
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").convertToString().asJavaString();
145
- 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
+ }
146
175
 
147
- KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
176
+ KeyStore ks = KeyStore.getInstance(keystoreType);
148
177
  InputStream is = new FileInputStream(keystoreFile);
149
178
  try {
150
- ks.load(is, password);
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, password);
184
+ kmf.init(ks, keystorePass);
156
185
  keyManagerFactoryMap.put(keystoreFile, kmf);
157
186
 
158
- KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
159
- is = new FileInputStream(keystoreFile);
160
- try {
161
- ts.load(is, password);
162
- } finally {
163
- 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);
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
- new IRubyObject[] { miniSSLContext },
172
- 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);
173
211
  }
174
212
 
175
213
  @JRubyMethod
176
- public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
214
+ public IRubyObject initialize(ThreadContext context, IRubyObject miniSSLContext)
177
215
  throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
178
216
 
179
- String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
217
+ String keystoreFile = miniSSLContext.callMethod(context, "keystore").convertToString().asJavaString();
180
218
  KeyManagerFactory kmf = keyManagerFactoryMap.get(keystoreFile);
181
- TrustManagerFactory tmf = trustManagerFactoryMap.get(keystoreFile);
182
- if(kmf == null || tmf == null) {
183
- 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);
184
224
  }
185
225
 
186
226
  SSLContext sslCtx = SSLContext.getInstance("TLS");
187
227
 
188
- sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
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[] protocols;
194
- if(miniSSLContext.callMethod(threadContext, "no_tlsv1").isTrue()) {
195
- protocols = new String[] { "TLSv1.1", "TLSv1.2" };
196
- } else {
197
- protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
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
- if(miniSSLContext.callMethod(threadContext, "no_tlsv1_1").isTrue()) {
201
- 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());
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(threadContext, "verify_mode").convertToInteger("to_i").getLongValue();
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 sslCipherListObject = miniSSLContext.callMethod(threadContext, "ssl_cipher_list");
216
- if (!sslCipherListObject.isNil()) {
217
- String[] sslCipherList = sslCipherListObject.convertToString().asJavaString().split(",");
218
- 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());
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 IllegalStateException("Unknown SSLOperation: " + sslOp);
344
+ throw new AssertionError("Unknown SSLOperation: " + sslOp);
255
345
  }
256
346
 
257
347
  switch (res.getStatus()) {
@@ -336,9 +426,7 @@ public class MiniSSL extends RubyObject {
336
426
 
337
427
  return RubyString.newString(getRuntime(), appDataByteList);
338
428
  } catch (SSLException e) {
339
- RaiseException re = getRuntime().newEOFError(e.getMessage());
340
- re.initCause(e);
341
- throw re;
429
+ throw newSSLError(getRuntime(), e);
342
430
  }
343
431
  }
344
432
 
@@ -371,19 +459,19 @@ public class MiniSSL extends RubyObject {
371
459
 
372
460
  return RubyString.newString(context.runtime, dataByteList);
373
461
  } catch (SSLException e) {
374
- RaiseException ex = context.runtime.newRuntimeError(e.toString());
375
- ex.initCause(e);
376
- throw ex;
462
+ throw newSSLError(getRuntime(), e);
377
463
  }
378
464
  }
379
465
 
380
466
  @JRubyMethod
381
- public IRubyObject peercert() throws CertificateEncodingException {
467
+ public IRubyObject peercert(ThreadContext context) throws CertificateEncodingException {
468
+ Certificate peerCert;
382
469
  try {
383
- return JavaEmbedUtils.javaToRuby(getRuntime(), engine.getSession().getPeerCertificates()[0].getEncoded());
470
+ peerCert = engine.getSession().getPeerCertificates()[0];
384
471
  } catch (SSLPeerUnverifiedException e) {
385
- return getRuntime().getNil();
472
+ peerCert = lastCheckedCert0; // null if trust check did not happen
386
473
  }
474
+ return peerCert == null ? context.nil : JavaEmbedUtils.javaToRuby(context.runtime, peerCert.getEncoded());
387
475
  }
388
476
 
389
477
  @JRubyMethod(name = "init?")
@@ -402,4 +490,19 @@ public class MiniSSL extends RubyObject {
402
490
  return getRuntime().getFalse();
403
491
  }
404
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
+
405
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
 
@@ -461,7 +469,7 @@ void Init_puma_http11(void)
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
@@ -85,8 +85,8 @@ module Puma
85
85
 
86
86
  def rack_response(status, body, content_type='application/json')
87
87
  headers = {
88
- 'Content-Type' => content_type,
89
- 'Content-Length' => body.bytesize.to_s
88
+ 'content-type' => content_type,
89
+ 'content-length' => body.bytesize.to_s
90
90
  }
91
91
 
92
92
  [status, headers, [body]]