puma 2.2.0 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (136) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +3334 -0
  3. data/LICENSE +23 -20
  4. data/README.md +387 -100
  5. data/bin/puma-wild +25 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/5.0-Upgrade.md +98 -0
  8. data/docs/6.0-Upgrade.md +56 -0
  9. data/docs/7.0-Upgrade.md +52 -0
  10. data/docs/8.0-Upgrade.md +100 -0
  11. data/docs/architecture.md +74 -0
  12. data/docs/compile_options.md +55 -0
  13. data/docs/deployment.md +137 -0
  14. data/docs/fork_worker.md +41 -0
  15. data/docs/grpc.md +62 -0
  16. data/docs/images/favicon.svg +1 -0
  17. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  18. data/docs/images/puma-connection-flow.png +0 -0
  19. data/docs/images/puma-general-arch.png +0 -0
  20. data/docs/images/running-puma.svg +1 -0
  21. data/docs/images/standard-logo.svg +1 -0
  22. data/docs/java_options.md +54 -0
  23. data/docs/jungle/README.md +9 -0
  24. data/docs/jungle/rc.d/README.md +74 -0
  25. data/docs/jungle/rc.d/puma +61 -0
  26. data/docs/jungle/rc.d/puma.conf +10 -0
  27. data/docs/kubernetes.md +73 -0
  28. data/docs/nginx.md +5 -5
  29. data/docs/plugins.md +42 -0
  30. data/docs/rails_dev_mode.md +28 -0
  31. data/docs/restart.md +65 -0
  32. data/docs/signals.md +98 -0
  33. data/docs/stats.md +148 -0
  34. data/docs/systemd.md +253 -0
  35. data/docs/testing_benchmarks_local_files.md +150 -0
  36. data/docs/testing_test_rackup_ci_files.md +36 -0
  37. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  38. data/ext/puma_http11/extconf.rb +59 -2
  39. data/ext/puma_http11/http11_parser.c +319 -487
  40. data/ext/puma_http11/http11_parser.h +15 -13
  41. data/ext/puma_http11/http11_parser.java.rl +64 -94
  42. data/ext/puma_http11/http11_parser.rl +27 -24
  43. data/ext/puma_http11/http11_parser_common.rl +8 -8
  44. data/ext/puma_http11/mini_ssl.c +696 -38
  45. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  46. data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
  47. data/ext/puma_http11/org/jruby/puma/Http11.java +241 -145
  48. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +174 -221
  49. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +413 -193
  50. data/ext/puma_http11/puma_http11.c +183 -175
  51. data/lib/puma/app/status.rb +77 -29
  52. data/lib/puma/binder.rb +349 -119
  53. data/lib/puma/cli.rb +163 -801
  54. data/lib/puma/client.rb +627 -144
  55. data/lib/puma/client_env.rb +171 -0
  56. data/lib/puma/cluster/worker.rb +183 -0
  57. data/lib/puma/cluster/worker_handle.rb +127 -0
  58. data/lib/puma/cluster.rb +634 -0
  59. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  60. data/lib/puma/commonlogger.rb +115 -0
  61. data/lib/puma/configuration.rb +420 -198
  62. data/lib/puma/const.rb +266 -115
  63. data/lib/puma/control_cli.rb +219 -120
  64. data/lib/puma/detect.rb +56 -2
  65. data/lib/puma/dsl.rb +1562 -0
  66. data/lib/puma/error_logger.rb +115 -0
  67. data/lib/puma/events.rb +44 -60
  68. data/lib/puma/io_buffer.rb +48 -5
  69. data/lib/puma/jruby_restart.rb +2 -51
  70. data/lib/puma/json_serialization.rb +96 -0
  71. data/lib/puma/launcher/bundle_pruner.rb +102 -0
  72. data/lib/puma/launcher.rb +501 -0
  73. data/lib/puma/log_writer.rb +153 -0
  74. data/lib/puma/minissl/context_builder.rb +96 -0
  75. data/lib/puma/minissl.rb +355 -37
  76. data/lib/puma/null_io.rb +79 -12
  77. data/lib/puma/plugin/systemd.rb +90 -0
  78. data/lib/puma/plugin/tmp_restart.rb +36 -0
  79. data/lib/puma/plugin.rb +111 -0
  80. data/lib/puma/rack/builder.rb +297 -0
  81. data/lib/puma/rack/urlmap.rb +93 -0
  82. data/lib/puma/rack_default.rb +21 -4
  83. data/lib/puma/reactor.rb +102 -133
  84. data/lib/puma/response.rb +532 -0
  85. data/lib/puma/runner.rb +211 -0
  86. data/lib/puma/sd_notify.rb +146 -0
  87. data/lib/puma/server.rb +582 -445
  88. data/lib/puma/server_plugin_control.rb +32 -0
  89. data/lib/puma/single.rb +72 -0
  90. data/lib/puma/state_file.rb +69 -0
  91. data/lib/puma/thread_pool.rb +389 -57
  92. data/lib/puma/util.rb +125 -0
  93. data/lib/puma.rb +80 -6
  94. data/lib/rack/handler/puma.rb +125 -47
  95. data/tools/Dockerfile +26 -0
  96. data/tools/trickletest.rb +44 -0
  97. metadata +88 -148
  98. data/COPYING +0 -55
  99. data/Gemfile +0 -10
  100. data/History.txt +0 -302
  101. data/Manifest.txt +0 -60
  102. data/Rakefile +0 -165
  103. data/TODO +0 -5
  104. data/docs/config.md +0 -0
  105. data/ext/puma_http11/ext_help.h +0 -15
  106. data/ext/puma_http11/io_buffer.c +0 -154
  107. data/lib/puma/accept_nonblock.rb +0 -23
  108. data/lib/puma/capistrano.rb +0 -34
  109. data/lib/puma/compat.rb +0 -11
  110. data/lib/puma/daemon_ext.rb +0 -20
  111. data/lib/puma/delegation.rb +0 -11
  112. data/lib/puma/java_io_buffer.rb +0 -45
  113. data/lib/puma/rack_patch.rb +0 -25
  114. data/puma.gemspec +0 -45
  115. data/test/test_app_status.rb +0 -88
  116. data/test/test_cli.rb +0 -171
  117. data/test/test_config.rb +0 -16
  118. data/test/test_http10.rb +0 -27
  119. data/test/test_http11.rb +0 -126
  120. data/test/test_integration.rb +0 -154
  121. data/test/test_iobuffer.rb +0 -38
  122. data/test/test_minissl.rb +0 -25
  123. data/test/test_null_io.rb +0 -31
  124. data/test/test_persistent.rb +0 -238
  125. data/test/test_puma_server.rb +0 -224
  126. data/test/test_rack_handler.rb +0 -10
  127. data/test/test_rack_server.rb +0 -141
  128. data/test/test_thread_pool.rb +0 -146
  129. data/test/test_unix_socket.rb +0 -39
  130. data/test/test_ws.rb +0 -89
  131. data/tools/jungle/init.d/README.md +0 -54
  132. data/tools/jungle/init.d/puma +0 -332
  133. data/tools/jungle/init.d/run-puma +0 -3
  134. data/tools/jungle/upstart/README.md +0 -61
  135. data/tools/jungle/upstart/puma-manager.conf +0 -31
  136. data/tools/jungle/upstart/puma.conf +0 -52
@@ -1,8 +1,20 @@
1
+ #define RSTRING_NOT_MODIFIED 1
2
+
1
3
  #include <ruby.h>
2
- #include <rubyio.h>
4
+ #include <ruby/version.h>
5
+ #include <ruby/io.h>
6
+
7
+ #ifdef HAVE_OPENSSL_BIO_H
8
+
3
9
  #include <openssl/bio.h>
4
10
  #include <openssl/ssl.h>
11
+ #include <openssl/dh.h>
5
12
  #include <openssl/err.h>
13
+ #include <openssl/x509.h>
14
+
15
+ #ifndef SSL_OP_NO_COMPRESSION
16
+ #define SSL_OP_NO_COMPRESSION 0
17
+ #endif
6
18
 
7
19
  typedef struct {
8
20
  BIO* read;
@@ -11,17 +23,135 @@ typedef struct {
11
23
  SSL_CTX* ctx;
12
24
  } ms_conn;
13
25
 
14
- void engine_free(ms_conn* conn) {
15
- BIO_free(conn->read);
16
- BIO_free(conn->write);
26
+ typedef struct {
27
+ unsigned char* buf;
28
+ int bytes;
29
+ } ms_cert_buf;
30
+
31
+ VALUE eError;
32
+
33
+ NORETURN(void raise_file_error(const char* caller, const char *filename));
34
+
35
+ void raise_file_error(const char* caller, const char *filename) {
36
+ rb_raise(eError, "%s: error in file '%s': %s", caller, filename, ERR_error_string(ERR_get_error(), NULL));
37
+ }
38
+
39
+ NORETURN(void raise_param_error(const char* caller, const char *param));
40
+
41
+ void raise_param_error(const char* caller, const char *param) {
42
+ rb_raise(eError, "%s: error with parameter '%s': %s", caller, param, ERR_error_string(ERR_get_error(), NULL));
43
+ }
44
+
45
+ void engine_free(void *ptr) {
46
+ ms_conn *conn = ptr;
47
+ ms_cert_buf* cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
48
+ if(cert_buf) {
49
+ OPENSSL_free(cert_buf->buf);
50
+ free(cert_buf);
51
+ }
52
+ SSL_free(conn->ssl);
53
+ SSL_CTX_free(conn->ctx);
17
54
 
18
55
  free(conn);
19
56
  }
20
57
 
58
+ const rb_data_type_t engine_data_type = {
59
+ "MiniSSL/ENGINE",
60
+ { 0, engine_free, 0 },
61
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
62
+ };
63
+
64
+ #ifndef HAVE_SSL_CTX_SET_DH_AUTO
65
+ DH *get_dh2048(void) {
66
+ /* `openssl dhparam -C 2048`
67
+ * -----BEGIN DH PARAMETERS-----
68
+ * MIIBCAKCAQEAjmh1uQHdTfxOyxEbKAV30fUfzqMDF/ChPzjfyzl2jcrqQMhrk76o
69
+ * 2NPNXqxHwsddMZ1RzvU8/jl+uhRuPWjXCFZbhET4N1vrviZM3VJhV8PPHuiVOACO
70
+ * y32jFd+Szx4bo2cXSK83hJ6jRd+0asP1awWjz9/06dFkrILCXMIfQLo0D8rqmppn
71
+ * EfDDAwuudCpM9kcDmBRAm9JsKbQ6gzZWjkc5+QWSaQofojIHbjvj3xzguaCJn+oQ
72
+ * vHWM+hsAnaOgEwCyeZ3xqs+/5lwSbkE/tqJW98cEZGygBUVo9jxZRZx6KOfjpdrb
73
+ * yenO9LJr/qtyrZB31WJbqxI0m0AKTAO8UwIBAg==
74
+ * -----END DH PARAMETERS-----
75
+ */
76
+ static unsigned char dh2048_p[] = {
77
+ 0x8E, 0x68, 0x75, 0xB9, 0x01, 0xDD, 0x4D, 0xFC, 0x4E, 0xCB,
78
+ 0x11, 0x1B, 0x28, 0x05, 0x77, 0xD1, 0xF5, 0x1F, 0xCE, 0xA3,
79
+ 0x03, 0x17, 0xF0, 0xA1, 0x3F, 0x38, 0xDF, 0xCB, 0x39, 0x76,
80
+ 0x8D, 0xCA, 0xEA, 0x40, 0xC8, 0x6B, 0x93, 0xBE, 0xA8, 0xD8,
81
+ 0xD3, 0xCD, 0x5E, 0xAC, 0x47, 0xC2, 0xC7, 0x5D, 0x31, 0x9D,
82
+ 0x51, 0xCE, 0xF5, 0x3C, 0xFE, 0x39, 0x7E, 0xBA, 0x14, 0x6E,
83
+ 0x3D, 0x68, 0xD7, 0x08, 0x56, 0x5B, 0x84, 0x44, 0xF8, 0x37,
84
+ 0x5B, 0xEB, 0xBE, 0x26, 0x4C, 0xDD, 0x52, 0x61, 0x57, 0xC3,
85
+ 0xCF, 0x1E, 0xE8, 0x95, 0x38, 0x00, 0x8E, 0xCB, 0x7D, 0xA3,
86
+ 0x15, 0xDF, 0x92, 0xCF, 0x1E, 0x1B, 0xA3, 0x67, 0x17, 0x48,
87
+ 0xAF, 0x37, 0x84, 0x9E, 0xA3, 0x45, 0xDF, 0xB4, 0x6A, 0xC3,
88
+ 0xF5, 0x6B, 0x05, 0xA3, 0xCF, 0xDF, 0xF4, 0xE9, 0xD1, 0x64,
89
+ 0xAC, 0x82, 0xC2, 0x5C, 0xC2, 0x1F, 0x40, 0xBA, 0x34, 0x0F,
90
+ 0xCA, 0xEA, 0x9A, 0x9A, 0x67, 0x11, 0xF0, 0xC3, 0x03, 0x0B,
91
+ 0xAE, 0x74, 0x2A, 0x4C, 0xF6, 0x47, 0x03, 0x98, 0x14, 0x40,
92
+ 0x9B, 0xD2, 0x6C, 0x29, 0xB4, 0x3A, 0x83, 0x36, 0x56, 0x8E,
93
+ 0x47, 0x39, 0xF9, 0x05, 0x92, 0x69, 0x0A, 0x1F, 0xA2, 0x32,
94
+ 0x07, 0x6E, 0x3B, 0xE3, 0xDF, 0x1C, 0xE0, 0xB9, 0xA0, 0x89,
95
+ 0x9F, 0xEA, 0x10, 0xBC, 0x75, 0x8C, 0xFA, 0x1B, 0x00, 0x9D,
96
+ 0xA3, 0xA0, 0x13, 0x00, 0xB2, 0x79, 0x9D, 0xF1, 0xAA, 0xCF,
97
+ 0xBF, 0xE6, 0x5C, 0x12, 0x6E, 0x41, 0x3F, 0xB6, 0xA2, 0x56,
98
+ 0xF7, 0xC7, 0x04, 0x64, 0x6C, 0xA0, 0x05, 0x45, 0x68, 0xF6,
99
+ 0x3C, 0x59, 0x45, 0x9C, 0x7A, 0x28, 0xE7, 0xE3, 0xA5, 0xDA,
100
+ 0xDB, 0xC9, 0xE9, 0xCE, 0xF4, 0xB2, 0x6B, 0xFE, 0xAB, 0x72,
101
+ 0xAD, 0x90, 0x77, 0xD5, 0x62, 0x5B, 0xAB, 0x12, 0x34, 0x9B,
102
+ 0x40, 0x0A, 0x4C, 0x03, 0xBC, 0x53
103
+ };
104
+ static unsigned char dh2048_g[] = { 0x02 };
105
+
106
+ DH *dh;
107
+ #if !(OPENSSL_VERSION_NUMBER < 0x10100005L)
108
+ BIGNUM *p, *g;
109
+ #endif
110
+
111
+ dh = DH_new();
112
+
113
+ #if OPENSSL_VERSION_NUMBER < 0x10100005L
114
+ dh->p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
115
+ dh->g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL);
116
+
117
+ if ((dh->p == NULL) || (dh->g == NULL)) {
118
+ DH_free(dh);
119
+ return NULL;
120
+ }
121
+ #else
122
+ p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
123
+ g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL);
124
+
125
+ if (p == NULL || g == NULL || !DH_set0_pqg(dh, p, NULL, g)) {
126
+ DH_free(dh);
127
+ BN_free(p);
128
+ BN_free(g);
129
+ return NULL;
130
+ }
131
+ #endif
132
+
133
+ return dh;
134
+ }
135
+ #endif
136
+
137
+ static void
138
+ sslctx_free(void *ptr) {
139
+ SSL_CTX *ctx = ptr;
140
+ SSL_CTX_free(ctx);
141
+ }
142
+
143
+ static const rb_data_type_t sslctx_type = {
144
+ "MiniSSL/SSLContext",
145
+ {
146
+ 0, sslctx_free,
147
+ },
148
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
149
+ };
150
+
21
151
  ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
22
152
  ms_conn* conn;
23
153
 
24
- *obj = Data_Make_Struct(klass, ms_conn, 0, engine_free, conn);
154
+ *obj = TypedData_Make_Struct(klass, ms_conn, &engine_data_type, conn);
25
155
 
26
156
  conn->read = BIO_new(BIO_s_mem());
27
157
  BIO_set_nbio(conn->read, 1);
@@ -35,30 +165,349 @@ ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
35
165
  return conn;
36
166
  }
37
167
 
38
- VALUE engine_init_server(VALUE self, VALUE key, VALUE cert) {
39
- VALUE obj;
40
- SSL_CTX* ctx;
168
+ static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
169
+ X509* err_cert;
41
170
  SSL* ssl;
171
+ int bytes;
172
+ unsigned char* buf = NULL;
173
+
174
+ if(!preverify_ok) {
175
+ err_cert = X509_STORE_CTX_get_current_cert(ctx);
176
+ if(err_cert) {
177
+ /*
178
+ * Save the failed certificate for inspection/logging.
179
+ */
180
+ bytes = i2d_X509(err_cert, &buf);
181
+ if(bytes > 0) {
182
+ ms_cert_buf* cert_buf = (ms_cert_buf*)malloc(sizeof(ms_cert_buf));
183
+ cert_buf->buf = buf;
184
+ cert_buf->bytes = bytes;
185
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
186
+ SSL_set_app_data(ssl, cert_buf);
187
+ }
188
+ }
189
+ }
42
190
 
43
- ms_conn* conn = engine_alloc(self, &obj);
191
+ return preverify_ok;
192
+ }
44
193
 
45
- StringValue(key);
46
- StringValue(cert);
194
+ static int password_callback(char *buf, int size, int rwflag, void *userdata) {
195
+ const char *password = (const char *) userdata;
196
+ size_t len = strlen(password);
47
197
 
48
- ctx = SSL_CTX_new(SSLv23_server_method());
49
- conn->ctx = ctx;
198
+ if (len > (size_t) size) {
199
+ return 0;
200
+ }
50
201
 
51
- SSL_CTX_use_certificate_file(ctx, RSTRING_PTR(cert), SSL_FILETYPE_PEM);
52
- SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
53
- /* SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE); */
202
+ memcpy(buf, password, len);
203
+ return (int) len;
204
+ }
54
205
 
55
- ssl = SSL_new(ctx);
56
- conn->ssl = ssl;
206
+ static VALUE
207
+ sslctx_alloc(VALUE klass) {
208
+ SSL_CTX *ctx;
209
+ long mode = 0 |
210
+ SSL_MODE_ENABLE_PARTIAL_WRITE |
211
+ SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
212
+ SSL_MODE_RELEASE_BUFFERS;
213
+
214
+ #ifdef HAVE_TLS_SERVER_METHOD
215
+ ctx = SSL_CTX_new(TLS_method());
216
+ // printf("\nctx using TLS_method security_level %d\n", SSL_CTX_get_security_level(ctx));
217
+ #else
218
+ ctx = SSL_CTX_new(SSLv23_method());
219
+ #endif
220
+ if (!ctx) {
221
+ rb_raise(eError, "SSL_CTX_new");
222
+ }
223
+ SSL_CTX_set_mode(ctx, mode);
57
224
 
58
- /* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
225
+ return TypedData_Wrap_Struct(klass, &sslctx_type, ctx);
226
+ }
59
227
 
60
- SSL_set_bio(ssl, conn->read, conn->write);
228
+ VALUE
229
+ sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
230
+ SSL_CTX* ctx;
231
+ int ssl_options;
232
+ VALUE key, cert, ca, verify_mode, ssl_cipher_filter, ssl_ciphersuites, no_tlsv1, no_tlsv1_1,
233
+ verification_flags, session_id_bytes, cert_pem, key_pem, key_password_command, key_password;
234
+ BIO *bio;
235
+ X509 *x509 = NULL;
236
+ EVP_PKEY *pkey;
237
+ pem_password_cb *password_cb = NULL;
238
+ const char *password = NULL;
239
+ #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
240
+ int min;
241
+ #endif
242
+ #ifndef HAVE_SSL_CTX_SET_DH_AUTO
243
+ DH *dh;
244
+ #endif
245
+ #if OPENSSL_VERSION_NUMBER < 0x10002000L
246
+ EC_KEY *ecdh;
247
+ #endif
248
+ #ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
249
+ VALUE reuse, reuse_cache_size, reuse_timeout;
250
+
251
+ reuse = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse"), 0);
252
+ reuse_cache_size = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse_cache_size"), 0);
253
+ reuse_timeout = rb_funcall(mini_ssl_ctx, rb_intern_const("reuse_timeout"), 0);
254
+ #endif
255
+
256
+ key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0);
257
+
258
+ key_password_command = rb_funcall(mini_ssl_ctx, rb_intern_const("key_password_command"), 0);
259
+
260
+ cert = rb_funcall(mini_ssl_ctx, rb_intern_const("cert"), 0);
261
+
262
+ ca = rb_funcall(mini_ssl_ctx, rb_intern_const("ca"), 0);
263
+
264
+ cert_pem = rb_funcall(mini_ssl_ctx, rb_intern_const("cert_pem"), 0);
265
+
266
+ key_pem = rb_funcall(mini_ssl_ctx, rb_intern_const("key_pem"), 0);
267
+
268
+ verify_mode = rb_funcall(mini_ssl_ctx, rb_intern_const("verify_mode"), 0);
269
+
270
+ ssl_cipher_filter = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_cipher_filter"), 0);
271
+
272
+ ssl_ciphersuites = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_ciphersuites"), 0);
273
+
274
+ no_tlsv1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1"), 0);
275
+
276
+ no_tlsv1_1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0);
277
+
278
+ TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx);
279
+
280
+ if (!NIL_P(cert)) {
281
+ StringValue(cert);
282
+
283
+ if (SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert)) != 1) {
284
+ raise_file_error("SSL_CTX_use_certificate_chain_file", RSTRING_PTR(cert));
285
+ }
286
+ }
287
+
288
+ if (!NIL_P(key_password_command)) {
289
+ key_password = rb_funcall(mini_ssl_ctx, rb_intern_const("key_password"), 0);
290
+
291
+ if (!NIL_P(key_password)) {
292
+ StringValue(key_password);
293
+ password_cb = password_callback;
294
+ password = RSTRING_PTR(key_password);
295
+ SSL_CTX_set_default_passwd_cb(ctx, password_cb);
296
+ SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *) password);
297
+ }
298
+ }
299
+
300
+ if (!NIL_P(key)) {
301
+ StringValue(key);
302
+
303
+ if (SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM) != 1) {
304
+ raise_file_error("SSL_CTX_use_PrivateKey_file", RSTRING_PTR(key));
305
+ }
306
+ }
307
+
308
+ if (!NIL_P(cert_pem)) {
309
+ X509 *ca = NULL;
310
+ unsigned long err;
311
+
312
+ bio = BIO_new(BIO_s_mem());
313
+ BIO_puts(bio, RSTRING_PTR(cert_pem));
314
+
315
+ /**
316
+ * Much of this pulled as a simplified version of the `use_certificate_chain_file` method
317
+ * from openssl's `ssl_rsa.c` file.
318
+ */
319
+
320
+ /* first read the cert as the first item in the pem file */
321
+ x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
322
+ if (NULL == x509) {
323
+ BIO_free_all(bio);
324
+ raise_param_error("PEM_read_bio_X509", "cert_pem");
325
+ }
326
+
327
+ /* Add the cert to the context */
328
+ /* 1 is success - otherwise check the error codes */
329
+ if (1 != SSL_CTX_use_certificate(ctx, x509)) {
330
+ BIO_free_all(bio);
331
+ raise_param_error("SSL_CTX_use_certificate", "cert_pem");
332
+ }
333
+
334
+ X509_free(x509); /* no longer need our reference */
335
+
336
+ /* Now lets load up the rest of the certificate chain */
337
+ /* 1 is success 0 is error */
338
+ if (0 == SSL_CTX_clear_chain_certs(ctx)) {
339
+ BIO_free_all(bio);
340
+ raise_param_error("SSL_CTX_clear_chain_certs","cert_pem");
341
+ }
342
+
343
+ while (1) {
344
+ ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
345
+
346
+ if (NULL == ca) {
347
+ break;
348
+ }
349
+
350
+ if (0 == SSL_CTX_add0_chain_cert(ctx, ca)) {
351
+ BIO_free_all(bio);
352
+ raise_param_error("SSL_CTX_add0_chain_cert","cert_pem");
353
+ }
354
+ /* don't free ca - its now owned by the context */
355
+ }
356
+
357
+ /* ca is NULL - so its either the end of the file or an error */
358
+ err = ERR_peek_last_error();
359
+
360
+ /* If its the end of the file - then we are done, in any case free the bio */
361
+ BIO_free_all(bio);
362
+
363
+ if ((ERR_GET_LIB(err) == ERR_LIB_PEM) && (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) {
364
+ ERR_clear_error();
365
+ } else {
366
+ raise_param_error("PEM_read_bio_X509","cert_pem");
367
+ }
368
+ }
369
+
370
+ if (!NIL_P(key_pem)) {
371
+ bio = BIO_new(BIO_s_mem());
372
+ BIO_puts(bio, RSTRING_PTR(key_pem));
373
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, password_cb, (void *) password);
374
+
375
+ if (SSL_CTX_use_PrivateKey(ctx, pkey) != 1) {
376
+ BIO_free(bio);
377
+ raise_file_error("SSL_CTX_use_PrivateKey", RSTRING_PTR(key_pem));
378
+ }
379
+ EVP_PKEY_free(pkey);
380
+ BIO_free(bio);
381
+ }
382
+
383
+ verification_flags = rb_funcall(mini_ssl_ctx, rb_intern_const("verification_flags"), 0);
384
+
385
+ if (!NIL_P(verification_flags)) {
386
+ X509_VERIFY_PARAM *param = SSL_CTX_get0_param(ctx);
387
+ X509_VERIFY_PARAM_set_flags(param, NUM2INT(verification_flags));
388
+ SSL_CTX_set1_param(ctx, param);
389
+ }
390
+
391
+ if (!NIL_P(ca)) {
392
+ StringValue(ca);
393
+ if (SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL) != 1) {
394
+ raise_file_error("SSL_CTX_load_verify_locations", RSTRING_PTR(ca));
395
+ }
396
+ }
397
+
398
+ ssl_options = SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION;
399
+
400
+ #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
401
+ if (RTEST(no_tlsv1_1)) {
402
+ min = TLS1_2_VERSION;
403
+ }
404
+ else if (RTEST(no_tlsv1)) {
405
+ min = TLS1_1_VERSION;
406
+ }
407
+ else {
408
+ min = TLS1_VERSION;
409
+ }
410
+
411
+ SSL_CTX_set_min_proto_version(ctx, min);
412
+
413
+ #else
414
+ /* As of 1.0.2f, SSL_OP_SINGLE_DH_USE key use is always on */
415
+ ssl_options |= SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE;
416
+
417
+ if (RTEST(no_tlsv1)) {
418
+ ssl_options |= SSL_OP_NO_TLSv1;
419
+ }
420
+ if(RTEST(no_tlsv1_1)) {
421
+ ssl_options |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1;
422
+ }
423
+ #endif
424
+
425
+ #ifdef HAVE_SSL_CTX_SET_SESSION_CACHE_MODE
426
+ if (!NIL_P(reuse)) {
427
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
428
+ if (!NIL_P(reuse_cache_size)) {
429
+ SSL_CTX_sess_set_cache_size(ctx, NUM2INT(reuse_cache_size));
430
+ }
431
+ if (!NIL_P(reuse_timeout)) {
432
+ SSL_CTX_set_timeout(ctx, NUM2INT(reuse_timeout));
433
+ }
434
+ } else {
435
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
436
+ }
437
+ #endif
438
+
439
+ SSL_CTX_set_options(ctx, ssl_options);
440
+
441
+ if (!NIL_P(ssl_cipher_filter)) {
442
+ StringValue(ssl_cipher_filter);
443
+ SSL_CTX_set_cipher_list(ctx, RSTRING_PTR(ssl_cipher_filter));
444
+ }
445
+ else {
446
+ SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
447
+ }
448
+
449
+ #if HAVE_SSL_CTX_SET_CIPHERSUITES
450
+ // Only override OpenSSL default ciphersuites if config option is supplied.
451
+ if (!NIL_P(ssl_ciphersuites)) {
452
+ StringValue(ssl_ciphersuites);
453
+ SSL_CTX_set_ciphersuites(ctx, RSTRING_PTR(ssl_ciphersuites));
454
+ }
455
+ #endif
456
+
457
+ #if OPENSSL_VERSION_NUMBER < 0x10002000L
458
+ // Remove this case if OpenSSL 1.0.1 (now EOL) support is no longer needed.
459
+ ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
460
+ if (ecdh) {
461
+ SSL_CTX_set_tmp_ecdh(ctx, ecdh);
462
+ EC_KEY_free(ecdh);
463
+ }
464
+ #elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
465
+ SSL_CTX_set_ecdh_auto(ctx, 1);
466
+ #endif
467
+
468
+ if (NIL_P(verify_mode)) {
469
+ /* SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); */
470
+ } else {
471
+ SSL_CTX_set_verify(ctx, NUM2INT(verify_mode), engine_verify_callback);
472
+ }
473
+
474
+ session_id_bytes = rb_funcall(
475
+ rb_cRandom,
476
+ rb_intern_const("bytes"),
477
+ 1, ULL2NUM(SSL_MAX_SSL_SESSION_ID_LENGTH));
478
+
479
+ SSL_CTX_set_session_id_context(ctx,
480
+ (unsigned char *) RSTRING_PTR(session_id_bytes),
481
+ SSL_MAX_SSL_SESSION_ID_LENGTH);
61
482
 
483
+ // printf("\ninitialize end security_level %d\n", SSL_CTX_get_security_level(ctx));
484
+
485
+ #ifdef HAVE_SSL_CTX_SET_DH_AUTO
486
+ // https://www.openssl.org/docs/man3.0/man3/SSL_CTX_set_dh_auto.html
487
+ SSL_CTX_set_dh_auto(ctx, 1);
488
+ #else
489
+ dh = get_dh2048();
490
+ SSL_CTX_set_tmp_dh(ctx, dh);
491
+ #endif
492
+
493
+ rb_obj_freeze(self);
494
+ return self;
495
+ }
496
+
497
+ VALUE engine_init_server(VALUE self, VALUE sslctx) {
498
+ ms_conn* conn;
499
+ VALUE obj;
500
+ SSL_CTX* ctx;
501
+ SSL* ssl;
502
+
503
+ conn = engine_alloc(self, &obj);
504
+
505
+ TypedData_Get_Struct(sslctx, SSL_CTX, &sslctx_type, ctx);
506
+
507
+ ssl = SSL_new(ctx);
508
+ conn->ssl = ssl;
509
+ SSL_set_app_data(ssl, NULL);
510
+ SSL_set_bio(ssl, conn->read, conn->write);
62
511
  SSL_set_accept_state(ssl);
63
512
  return obj;
64
513
  }
@@ -66,9 +515,13 @@ VALUE engine_init_server(VALUE self, VALUE key, VALUE cert) {
66
515
  VALUE engine_init_client(VALUE klass) {
67
516
  VALUE obj;
68
517
  ms_conn* conn = engine_alloc(klass, &obj);
69
-
518
+ #ifdef HAVE_DTLS_METHOD
519
+ conn->ctx = SSL_CTX_new(DTLS_method());
520
+ #else
70
521
  conn->ctx = SSL_CTX_new(DTLSv1_method());
522
+ #endif
71
523
  conn->ssl = SSL_new(conn->ctx);
524
+ SSL_set_app_data(conn->ssl, NULL);
72
525
  SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
73
526
 
74
527
  SSL_set_bio(conn->ssl, conn->read, conn->write);
@@ -81,7 +534,7 @@ VALUE engine_inject(VALUE self, VALUE str) {
81
534
  ms_conn* conn;
82
535
  long used;
83
536
 
84
- Data_Get_Struct(self, ms_conn, conn);
537
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
85
538
 
86
539
  StringValue(str);
87
540
 
@@ -94,22 +547,48 @@ VALUE engine_inject(VALUE self, VALUE str) {
94
547
  return INT2FIX(used);
95
548
  }
96
549
 
97
- static VALUE eError;
550
+ NORETURN(void raise_error(SSL* ssl, int result));
98
551
 
99
552
  void raise_error(SSL* ssl, int result) {
100
- int error = SSL_get_error(ssl, result);
101
- char* msg = ERR_error_string(error, NULL);
553
+ char buf[512];
554
+ char msg[768];
555
+ const char* err_str;
556
+ int err = errno;
557
+ int mask = 4095;
558
+ int ssl_err = SSL_get_error(ssl, result);
559
+ int verify_err = (int) SSL_get_verify_result(ssl);
560
+
561
+ if(SSL_ERROR_SYSCALL == ssl_err) {
562
+ snprintf(msg, sizeof(msg), "System error: %s - %d", strerror(err), err);
563
+
564
+ } else if(SSL_ERROR_SSL == ssl_err) {
565
+ if(X509_V_OK != verify_err) {
566
+ err_str = X509_verify_cert_error_string(verify_err);
567
+ snprintf(msg, sizeof(msg),
568
+ "OpenSSL certificate verification error: %s - %d",
569
+ err_str, verify_err);
570
+
571
+ } else {
572
+ err = (int) ERR_get_error();
573
+ ERR_error_string_n(err, buf, sizeof(buf));
574
+ snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, err & mask);
575
+ }
576
+ } else {
577
+ snprintf(msg, sizeof(msg), "Unknown OpenSSL error: %d", ssl_err);
578
+ }
102
579
 
103
580
  ERR_clear_error();
104
- rb_raise(eError, "OpenSSL error: %s - %d", msg, error);
581
+ rb_raise(eError, "%s", msg);
105
582
  }
106
583
 
107
584
  VALUE engine_read(VALUE self) {
108
585
  ms_conn* conn;
109
586
  char buf[512];
110
- int bytes, n;
587
+ int bytes, error;
588
+
589
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
111
590
 
112
- Data_Get_Struct(self, ms_conn, conn);
591
+ ERR_clear_error();
113
592
 
114
593
  bytes = SSL_read(conn->ssl, (void*)buf, sizeof(buf));
115
594
 
@@ -119,24 +598,27 @@ VALUE engine_read(VALUE self) {
119
598
 
120
599
  if(SSL_want_read(conn->ssl)) return Qnil;
121
600
 
122
- if(SSL_get_error(conn->ssl, bytes) == SSL_ERROR_ZERO_RETURN) {
601
+ error = SSL_get_error(conn->ssl, bytes);
602
+
603
+ if(error == SSL_ERROR_ZERO_RETURN) {
123
604
  rb_eof_error();
605
+ } else {
606
+ raise_error(conn->ssl, bytes);
124
607
  }
125
608
 
126
- raise_error(conn->ssl, bytes);
127
-
128
609
  return Qnil;
129
610
  }
130
611
 
131
612
  VALUE engine_write(VALUE self, VALUE str) {
132
613
  ms_conn* conn;
133
- char buf[512];
134
614
  int bytes;
135
615
 
136
- Data_Get_Struct(self, ms_conn, conn);
616
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
137
617
 
138
618
  StringValue(str);
139
619
 
620
+ ERR_clear_error();
621
+
140
622
  bytes = SSL_write(conn->ssl, (void*)RSTRING_PTR(str), (int)RSTRING_LEN(str));
141
623
  if(bytes > 0) {
142
624
  return INT2FIX(bytes);
@@ -153,9 +635,11 @@ VALUE engine_extract(VALUE self) {
153
635
  ms_conn* conn;
154
636
  int bytes;
155
637
  size_t pending;
156
- char buf[512];
638
+ // https://www.openssl.org/docs/manmaster/man3/BIO_f_buffer.html
639
+ // crypto/bio/bf_buff.c DEFAULT_BUFFER_SIZE
640
+ char buf[4096];
157
641
 
158
- Data_Get_Struct(self, ms_conn, conn);
642
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
159
643
 
160
644
  pending = BIO_pending(conn->write);
161
645
  if(pending > 0) {
@@ -170,20 +654,164 @@ VALUE engine_extract(VALUE self) {
170
654
  return Qnil;
171
655
  }
172
656
 
657
+ VALUE engine_shutdown(VALUE self) {
658
+ ms_conn* conn;
659
+ int ok;
660
+
661
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
662
+
663
+ if (SSL_in_init(conn->ssl)) {
664
+ // Avoid "shutdown while in init" error
665
+ // See https://github.com/openssl/openssl/blob/openssl-3.5.2/ssl/ssl_lib.c#L2827-L2828
666
+ return Qtrue;
667
+ }
668
+
669
+ ERR_clear_error();
670
+
671
+ ok = SSL_shutdown(conn->ssl);
672
+ // See https://github.com/openssl/openssl/blob/openssl-3.5.2/ssl/ssl_lib.c#L2792-L2797
673
+ // for description of SSL_shutdown return values.
674
+ switch (ok) {
675
+ case 0:
676
+ // "close notify" alert is sent by us.
677
+ return Qfalse;
678
+ case 1:
679
+ // "close notify" alert was received from peer.
680
+ return Qtrue;
681
+ default:
682
+ raise_error(conn->ssl, ok);
683
+ }
684
+
685
+ return Qnil;
686
+ }
687
+
688
+ VALUE engine_init(VALUE self) {
689
+ ms_conn* conn;
690
+
691
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
692
+
693
+ return SSL_in_init(conn->ssl) ? Qtrue : Qfalse;
694
+ }
695
+
696
+ VALUE engine_peercert(VALUE self) {
697
+ ms_conn* conn;
698
+ X509* cert;
699
+ int bytes;
700
+ unsigned char* buf = NULL;
701
+ ms_cert_buf* cert_buf = NULL;
702
+ VALUE rb_cert_buf;
703
+
704
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
705
+
706
+ #ifdef HAVE_SSL_GET1_PEER_CERTIFICATE
707
+ cert = SSL_get1_peer_certificate(conn->ssl);
708
+ #else
709
+ cert = SSL_get_peer_certificate(conn->ssl);
710
+ #endif
711
+ if(!cert) {
712
+ /*
713
+ * See if there was a failed certificate associated with this client.
714
+ */
715
+ cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
716
+ if(!cert_buf) {
717
+ return Qnil;
718
+ }
719
+ buf = cert_buf->buf;
720
+ bytes = cert_buf->bytes;
721
+
722
+ } else {
723
+ bytes = i2d_X509(cert, &buf);
724
+ X509_free(cert);
725
+
726
+ if(bytes < 0) {
727
+ return Qnil;
728
+ }
729
+ }
730
+
731
+ rb_cert_buf = rb_str_new((const char*)(buf), bytes);
732
+ if(!cert_buf) {
733
+ OPENSSL_free(buf);
734
+ }
735
+
736
+ return rb_cert_buf;
737
+ }
738
+
739
+ /* @see Puma::MiniSSL::Socket#ssl_version_state
740
+ * @version 5.0.0
741
+ */
742
+ static VALUE
743
+ engine_ssl_vers_st(VALUE self) {
744
+ ms_conn* conn;
745
+ TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
746
+ return rb_ary_new3(2, rb_str_new2(SSL_get_version(conn->ssl)), rb_str_new2(SSL_state_string(conn->ssl)));
747
+ }
748
+
749
+ VALUE noop(VALUE self) {
750
+ return Qnil;
751
+ }
752
+
173
753
  void Init_mini_ssl(VALUE puma) {
174
- VALUE mod, eng;
754
+ VALUE mod, eng, sslctx;
755
+
756
+ /* Fake operation for documentation (RDoc, YARD) */
757
+ #if 0 == 1
758
+ puma = rb_define_module("Puma");
759
+ #endif
175
760
 
176
761
  SSL_library_init();
177
762
  OpenSSL_add_ssl_algorithms();
178
763
  SSL_load_error_strings();
179
764
  ERR_load_crypto_strings();
180
-
765
+
181
766
  mod = rb_define_module_under(puma, "MiniSSL");
767
+
182
768
  eng = rb_define_class_under(mod, "Engine", rb_cObject);
769
+ rb_undef_alloc_func(eng);
770
+
771
+ sslctx = rb_define_class_under(mod, "SSLContext", rb_cObject);
772
+ rb_define_alloc_func(sslctx, sslctx_alloc);
773
+ rb_define_method(sslctx, "initialize", sslctx_initialize, 1);
774
+ rb_undef_method(sslctx, "initialize_copy");
775
+
776
+
777
+ // OpenSSL Build / Runtime/Load versions
778
+
779
+ /* Version of OpenSSL that Puma was compiled with */
780
+ rb_define_const(mod, "OPENSSL_VERSION", rb_str_new2(OPENSSL_VERSION_TEXT));
781
+
782
+ #if !defined(LIBRESSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000
783
+ /* Version of OpenSSL that Puma loaded with */
784
+ rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(OpenSSL_version(OPENSSL_VERSION)));
785
+ #else
786
+ rb_define_const(mod, "OPENSSL_LIBRARY_VERSION", rb_str_new2(SSLeay_version(SSLEAY_VERSION)));
787
+ #endif
788
+
789
+ #if defined(OPENSSL_NO_SSL3) || defined(OPENSSL_NO_SSL3_METHOD)
790
+ /* True if SSL3 is not available */
791
+ rb_define_const(mod, "OPENSSL_NO_SSL3", Qtrue);
792
+ #else
793
+ rb_define_const(mod, "OPENSSL_NO_SSL3", Qfalse);
794
+ #endif
795
+
796
+ #if defined(OPENSSL_NO_TLS1) || defined(OPENSSL_NO_TLS1_METHOD)
797
+ /* True if TLS1 is not available */
798
+ rb_define_const(mod, "OPENSSL_NO_TLS1", Qtrue);
799
+ #else
800
+ rb_define_const(mod, "OPENSSL_NO_TLS1", Qfalse);
801
+ #endif
802
+
803
+ #if defined(OPENSSL_NO_TLS1_1) || defined(OPENSSL_NO_TLS1_1_METHOD)
804
+ /* True if TLS1_1 is not available */
805
+ rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qtrue);
806
+ #else
807
+ rb_define_const(mod, "OPENSSL_NO_TLS1_1", Qfalse);
808
+ #endif
809
+
810
+ rb_define_singleton_method(mod, "check", noop, 0);
183
811
 
184
812
  eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
185
813
 
186
- rb_define_singleton_method(eng, "server", engine_init_server, 2);
814
+ rb_define_singleton_method(eng, "server", engine_init_server, 1);
187
815
  rb_define_singleton_method(eng, "client", engine_init_client, 0);
188
816
 
189
817
  rb_define_method(eng, "inject", engine_inject, 1);
@@ -191,4 +819,34 @@ void Init_mini_ssl(VALUE puma) {
191
819
 
192
820
  rb_define_method(eng, "write", engine_write, 1);
193
821
  rb_define_method(eng, "extract", engine_extract, 0);
822
+
823
+ rb_define_method(eng, "shutdown", engine_shutdown, 0);
824
+
825
+ rb_define_method(eng, "init?", engine_init, 0);
826
+
827
+ /* @!attribute [r] peercert
828
+ * Returns `nil` when `MiniSSL::Context#verify_mode` is set to `VERIFY_NONE`.
829
+ * @return [String, nil] DER encoded cert
830
+ */
831
+ rb_define_method(eng, "peercert", engine_peercert, 0);
832
+
833
+ rb_define_method(eng, "ssl_vers_st", engine_ssl_vers_st, 0);
834
+ }
835
+
836
+ #else
837
+
838
+ NORETURN(VALUE raise_error(VALUE self));
839
+
840
+ VALUE raise_error(VALUE self) {
841
+ rb_raise(rb_eStandardError, "SSL not available in this build");
842
+ }
843
+
844
+ void Init_mini_ssl(VALUE puma) {
845
+ VALUE mod;
846
+
847
+ mod = rb_define_module_under(puma, "MiniSSL");
848
+ rb_define_class_under(mod, "SSLError", rb_eStandardError);
849
+
850
+ rb_define_singleton_method(mod, "check", raise_error, 0);
194
851
  }
852
+ #endif