puma 5.1.1 → 5.3.1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +131 -10
- data/README.md +24 -2
- data/docs/architecture.md +22 -18
- data/docs/compile_options.md +6 -6
- data/docs/deployment.md +2 -2
- data/docs/jungle/rc.d/README.md +1 -1
- data/docs/kubernetes.md +66 -0
- data/docs/plugins.md +2 -2
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +1 -1
- data/docs/stats.md +142 -0
- data/docs/systemd.md +1 -1
- data/ext/puma_http11/extconf.rb +14 -0
- data/ext/puma_http11/http11_parser.c +19 -21
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +162 -84
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
- data/ext/puma_http11/puma_http11.c +2 -2
- data/lib/puma.rb +34 -8
- data/lib/puma/binder.rb +50 -43
- data/lib/puma/client.rb +5 -3
- data/lib/puma/cluster.rb +40 -8
- data/lib/puma/cluster/worker_handle.rb +4 -0
- data/lib/puma/configuration.rb +4 -1
- data/lib/puma/const.rb +3 -3
- data/lib/puma/control_cli.rb +5 -1
- data/lib/puma/detect.rb +14 -10
- data/lib/puma/dsl.rb +56 -4
- data/lib/puma/error_logger.rb +12 -5
- data/lib/puma/events.rb +2 -3
- data/lib/puma/launcher.rb +4 -3
- data/lib/puma/minissl.rb +48 -17
- data/lib/puma/minissl/context_builder.rb +6 -0
- data/lib/puma/null_io.rb +12 -0
- data/lib/puma/queue_close.rb +7 -7
- data/lib/puma/reactor.rb +7 -2
- data/lib/puma/request.rb +9 -4
- data/lib/puma/runner.rb +8 -3
- data/lib/puma/server.rb +46 -112
- data/lib/puma/thread_pool.rb +4 -3
- data/lib/rack/handler/puma.rb +1 -0
- metadata +6 -3
@@ -0,0 +1,29 @@
|
|
1
|
+
# Running Puma in Rails Development Mode
|
2
|
+
|
3
|
+
## "Loopback requests"
|
4
|
+
|
5
|
+
Be cautious of "loopback requests", where a Rails application executes a request to a server that in turn, results in another request back to the same Rails application before the first request is completed. Having a loopback request will trigger [Rails' load interlock](https://guides.rubyonrails.org/threading_and_code_execution.html#load-interlock) mechanism. The load interlock mechanism prevents a thread from using Rails autoloading mechanism to load constants while the application code is still running inside another thread.
|
6
|
+
|
7
|
+
This issue only occurs in the development environment as Rails' load interlock is not used in production environments. Although we're not sure, we believe this issue may not occur with the new `zeitwerk` code loader.
|
8
|
+
|
9
|
+
### Solutions
|
10
|
+
|
11
|
+
|
12
|
+
#### 1. Bypass Rails' load interlock with `.permit_concurrent_loads`
|
13
|
+
|
14
|
+
Wrap the first request inside a block that will allow concurrent loads, [`ActiveSupport::Dependencies.interlock.permit_concurrent_loads`](https://guides.rubyonrails.org/threading_and_code_execution.html#permit-concurrent-loads). Anything wrapped inside the `.permit_concurrent_loads` block will bypass the load interlock mechanism, allowing new threads to access the Rails environment and boot properly.
|
15
|
+
|
16
|
+
###### Example
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
response = ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
|
20
|
+
# Your HTTP request code here. For example:
|
21
|
+
Faraday.post url, data: 'foo'
|
22
|
+
end
|
23
|
+
|
24
|
+
do_something_with response
|
25
|
+
```
|
26
|
+
|
27
|
+
#### 2. Use multiple processes on Puma
|
28
|
+
|
29
|
+
Alternatively, you may also enable multiple (single-threaded) workers on Puma. By doing so, you are sidestepping the problem by creating multiple processes rather than new threads. However, this workaround is not ideal because debugging tools such as [byebug](https://github.com/deivid-rodriguez/byebug/issues/487) and [pry](https://github.com/pry/pry/issues/2153), work poorly with any multi-process web server.
|
data/docs/restart.md
CHANGED
@@ -45,7 +45,7 @@ Any of the following will cause a Puma server to perform a phased restart:
|
|
45
45
|
### Supported configurations
|
46
46
|
|
47
47
|
* Works in cluster mode only
|
48
|
-
* To support upgrading the application that Puma is serving, ensure `prune_bundler` is enabled and that `preload_app
|
48
|
+
* To support upgrading the application that Puma is serving, ensure `prune_bundler` is enabled and that `preload_app!` is disabled
|
49
49
|
* Supported on all platforms where cluster mode is supported
|
50
50
|
|
51
51
|
### Client experience
|
data/docs/stats.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
## accessing stats
|
2
|
+
|
3
|
+
Stats can be accessed in two ways:
|
4
|
+
|
5
|
+
### control server
|
6
|
+
|
7
|
+
`$ pumactl stats` or `GET /stats`
|
8
|
+
|
9
|
+
[Read more about `pumactl` and the control server in the README.](https://github.com/puma/puma#controlstatus-server).
|
10
|
+
|
11
|
+
### Puma.stats
|
12
|
+
|
13
|
+
`Puma.stats` produces a JSON string. `Puma.stats_hash` produces a ruby hash.
|
14
|
+
|
15
|
+
#### in single mode
|
16
|
+
|
17
|
+
Invoke `Puma.stats` anywhere in runtime, e.g. in a rails initializer:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
# config/initializers/puma_stats.rb
|
21
|
+
|
22
|
+
Thread.new do
|
23
|
+
loop do
|
24
|
+
sleep 30
|
25
|
+
puts Puma.stats
|
26
|
+
end
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
#### in cluster mode
|
31
|
+
|
32
|
+
Invoke `Puma.stats` from the master process
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
# config/puma.rb
|
36
|
+
|
37
|
+
before_fork do
|
38
|
+
Thread.new do
|
39
|
+
loop do
|
40
|
+
puts Puma.stats
|
41
|
+
sleep 30
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
```
|
46
|
+
|
47
|
+
|
48
|
+
## Explanation of stats
|
49
|
+
|
50
|
+
`Puma.stats` returns different information and a different structure depending on if Puma is in single vs cluster mode. There is one top-level attribute that is common to both modes:
|
51
|
+
|
52
|
+
* started_at: when puma was started
|
53
|
+
|
54
|
+
### single mode and individual workers in cluster mode
|
55
|
+
|
56
|
+
When Puma is run in single mode, these stats are available at the top level. When Puma is run in cluster mode, these stats are available within the `worker_status` array in a hash labeled `last_status`, in an array of hashes, one hash for each worker.
|
57
|
+
|
58
|
+
* backlog: requests that are waiting for an available thread to be available. if this is above 0, you need more capacity [always true?]
|
59
|
+
* running: how many threads are running
|
60
|
+
* pool_capacity: the number of requests that the server is capable of taking right now. For example if the number is 5 then it means there are 5 threads sitting idle ready to take a request. If one request comes in, then the value would be 4 until it finishes processing. If the minimum threads allowed is zero, this number will still have a maximum value of the maximum threads allowed.
|
61
|
+
* max_threads: the maximum number of threads puma is configured to spool up per worker
|
62
|
+
* requests_count: the number of requests this worker has served since starting
|
63
|
+
|
64
|
+
|
65
|
+
### cluster mode
|
66
|
+
|
67
|
+
* phase: which phase of restart the process is in, during [phased restart](https://github.com/puma/puma/blob/master/docs/restart.md)
|
68
|
+
* workers: ??
|
69
|
+
* booted_workers: how many workers currently running?
|
70
|
+
* old_workers: ??
|
71
|
+
* worker_status: array of hashes of info for each worker (see below)
|
72
|
+
|
73
|
+
### worker status
|
74
|
+
|
75
|
+
* started_at: when the worker was started
|
76
|
+
* pid: the process id of the worker process
|
77
|
+
* index: each worker gets a number. if puma is configured to have 3 workers, then this will be 0, 1, or 2
|
78
|
+
* booted: if it's done booting [?]
|
79
|
+
* last_checkin: Last time the worker responded to the master process' heartbeat check.
|
80
|
+
* last_status: a hash of info about the worker's state handling requests. See the explanation for this in "single mode and individual workers in cluster mode" section above.
|
81
|
+
|
82
|
+
|
83
|
+
## Examples
|
84
|
+
|
85
|
+
Here are two example stats hashes produced by `Puma.stats`:
|
86
|
+
|
87
|
+
### single
|
88
|
+
|
89
|
+
```json
|
90
|
+
{
|
91
|
+
"started_at": "2021-01-14T07:12:35Z",
|
92
|
+
"backlog": 0,
|
93
|
+
"running": 5,
|
94
|
+
"pool_capacity": 5,
|
95
|
+
"max_threads": 5,
|
96
|
+
"requests_count": 3
|
97
|
+
}
|
98
|
+
```
|
99
|
+
|
100
|
+
### cluster
|
101
|
+
|
102
|
+
```json
|
103
|
+
{
|
104
|
+
"started_at": "2021-01-14T07:09:17Z",
|
105
|
+
"workers": 2,
|
106
|
+
"phase": 0,
|
107
|
+
"booted_workers": 2,
|
108
|
+
"old_workers": 0,
|
109
|
+
"worker_status": [
|
110
|
+
{
|
111
|
+
"started_at": "2021-01-14T07:09:24Z",
|
112
|
+
"pid": 64136,
|
113
|
+
"index": 0,
|
114
|
+
"phase": 0,
|
115
|
+
"booted": true,
|
116
|
+
"last_checkin": "2021-01-14T07:11:09Z",
|
117
|
+
"last_status": {
|
118
|
+
"backlog": 0,
|
119
|
+
"running": 5,
|
120
|
+
"pool_capacity": 5,
|
121
|
+
"max_threads": 5,
|
122
|
+
"requests_count": 2
|
123
|
+
}
|
124
|
+
},
|
125
|
+
{
|
126
|
+
"started_at": "2021-01-14T07:09:24Z",
|
127
|
+
"pid": 64137,
|
128
|
+
"index": 1,
|
129
|
+
"phase": 0,
|
130
|
+
"booted": true,
|
131
|
+
"last_checkin": "2021-01-14T07:11:09Z",
|
132
|
+
"last_status": {
|
133
|
+
"backlog": 0,
|
134
|
+
"running": 5,
|
135
|
+
"pool_capacity": 5,
|
136
|
+
"max_threads": 5,
|
137
|
+
"requests_count": 1
|
138
|
+
}
|
139
|
+
}
|
140
|
+
]
|
141
|
+
}
|
142
|
+
```
|
data/docs/systemd.md
CHANGED
@@ -8,7 +8,7 @@ useful features for running Puma in production.
|
|
8
8
|
## Service Configuration
|
9
9
|
|
10
10
|
Below is a sample puma.service configuration file for systemd, which
|
11
|
-
can be copied or symlinked to
|
11
|
+
can be copied or symlinked to `/etc/systemd/system/puma.service`, or if
|
12
12
|
desired, using an application or instance specific name.
|
13
13
|
|
14
14
|
Note that this uses the systemd preferred "simple" type where the
|
data/ext/puma_http11/extconf.rb
CHANGED
@@ -22,6 +22,20 @@ unless ENV["DISABLE_SSL"]
|
|
22
22
|
# below are yes for 1.1.0 & later
|
23
23
|
have_func "TLS_server_method" , "openssl/ssl.h"
|
24
24
|
have_func "SSL_CTX_set_min_proto_version(NULL, 0)", "openssl/ssl.h"
|
25
|
+
|
26
|
+
have_func "X509_STORE_up_ref"
|
27
|
+
have_func("SSL_CTX_set_ecdh_auto(NULL, 0)", "openssl/ssl.h")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if ENV["MAKE_WARNINGS_INTO_ERRORS"]
|
32
|
+
# Make all warnings into errors
|
33
|
+
# Except `implicit-fallthrough` since most failures comes from ragel state machine generated code
|
34
|
+
if respond_to? :append_cflags
|
35
|
+
append_cflags config_string 'WERRORFLAG'
|
36
|
+
append_cflags '-Wno-implicit-fallthrough'
|
37
|
+
else
|
38
|
+
$CFLAGS += ' ' << (config_string 'WERRORFLAG') << ' -Wno-implicit-fallthrough'
|
25
39
|
end
|
26
40
|
end
|
27
41
|
|
@@ -43,15 +43,13 @@ static const int puma_parser_start = 1;
|
|
43
43
|
static const int puma_parser_first_final = 46;
|
44
44
|
static const int puma_parser_error = 0;
|
45
45
|
|
46
|
-
static const int puma_parser_en_main = 1;
|
47
|
-
|
48
46
|
|
49
47
|
#line 85 "ext/puma_http11/http11_parser.rl"
|
50
48
|
|
51
49
|
int puma_parser_init(puma_parser *parser) {
|
52
50
|
int cs = 0;
|
53
51
|
|
54
|
-
#line
|
52
|
+
#line 53 "ext/puma_http11/http11_parser.c"
|
55
53
|
{
|
56
54
|
cs = puma_parser_start;
|
57
55
|
}
|
@@ -85,7 +83,7 @@ size_t puma_parser_execute(puma_parser *parser, const char *buffer, size_t len,
|
|
85
83
|
assert((size_t) (pe - p) == len - off && "pointers aren't same distance");
|
86
84
|
|
87
85
|
|
88
|
-
#line
|
86
|
+
#line 87 "ext/puma_http11/http11_parser.c"
|
89
87
|
{
|
90
88
|
if ( p == pe )
|
91
89
|
goto _test_eof;
|
@@ -116,7 +114,7 @@ st2:
|
|
116
114
|
if ( ++p == pe )
|
117
115
|
goto _test_eof2;
|
118
116
|
case 2:
|
119
|
-
#line
|
117
|
+
#line 118 "ext/puma_http11/http11_parser.c"
|
120
118
|
switch( (*p) ) {
|
121
119
|
case 32: goto tr2;
|
122
120
|
case 36: goto st27;
|
@@ -141,7 +139,7 @@ st3:
|
|
141
139
|
if ( ++p == pe )
|
142
140
|
goto _test_eof3;
|
143
141
|
case 3:
|
144
|
-
#line
|
142
|
+
#line 143 "ext/puma_http11/http11_parser.c"
|
145
143
|
switch( (*p) ) {
|
146
144
|
case 42: goto tr4;
|
147
145
|
case 43: goto tr5;
|
@@ -165,7 +163,7 @@ st4:
|
|
165
163
|
if ( ++p == pe )
|
166
164
|
goto _test_eof4;
|
167
165
|
case 4:
|
168
|
-
#line
|
166
|
+
#line 167 "ext/puma_http11/http11_parser.c"
|
169
167
|
switch( (*p) ) {
|
170
168
|
case 32: goto tr8;
|
171
169
|
case 35: goto tr9;
|
@@ -227,7 +225,7 @@ st5:
|
|
227
225
|
if ( ++p == pe )
|
228
226
|
goto _test_eof5;
|
229
227
|
case 5:
|
230
|
-
#line
|
228
|
+
#line 229 "ext/puma_http11/http11_parser.c"
|
231
229
|
if ( (*p) == 72 )
|
232
230
|
goto tr10;
|
233
231
|
goto st0;
|
@@ -239,7 +237,7 @@ st6:
|
|
239
237
|
if ( ++p == pe )
|
240
238
|
goto _test_eof6;
|
241
239
|
case 6:
|
242
|
-
#line
|
240
|
+
#line 241 "ext/puma_http11/http11_parser.c"
|
243
241
|
if ( (*p) == 84 )
|
244
242
|
goto st7;
|
245
243
|
goto st0;
|
@@ -320,7 +318,7 @@ st14:
|
|
320
318
|
if ( ++p == pe )
|
321
319
|
goto _test_eof14;
|
322
320
|
case 14:
|
323
|
-
#line
|
321
|
+
#line 322 "ext/puma_http11/http11_parser.c"
|
324
322
|
if ( (*p) == 10 )
|
325
323
|
goto st15;
|
326
324
|
goto st0;
|
@@ -371,7 +369,7 @@ st46:
|
|
371
369
|
if ( ++p == pe )
|
372
370
|
goto _test_eof46;
|
373
371
|
case 46:
|
374
|
-
#line
|
372
|
+
#line 373 "ext/puma_http11/http11_parser.c"
|
375
373
|
goto st0;
|
376
374
|
tr21:
|
377
375
|
#line 40 "ext/puma_http11/http11_parser.rl"
|
@@ -387,7 +385,7 @@ st17:
|
|
387
385
|
if ( ++p == pe )
|
388
386
|
goto _test_eof17;
|
389
387
|
case 17:
|
390
|
-
#line
|
388
|
+
#line 389 "ext/puma_http11/http11_parser.c"
|
391
389
|
switch( (*p) ) {
|
392
390
|
case 33: goto tr23;
|
393
391
|
case 58: goto tr24;
|
@@ -426,7 +424,7 @@ st18:
|
|
426
424
|
if ( ++p == pe )
|
427
425
|
goto _test_eof18;
|
428
426
|
case 18:
|
429
|
-
#line
|
427
|
+
#line 428 "ext/puma_http11/http11_parser.c"
|
430
428
|
switch( (*p) ) {
|
431
429
|
case 13: goto tr26;
|
432
430
|
case 32: goto tr27;
|
@@ -440,7 +438,7 @@ st19:
|
|
440
438
|
if ( ++p == pe )
|
441
439
|
goto _test_eof19;
|
442
440
|
case 19:
|
443
|
-
#line
|
441
|
+
#line 442 "ext/puma_http11/http11_parser.c"
|
444
442
|
if ( (*p) == 13 )
|
445
443
|
goto tr29;
|
446
444
|
goto st19;
|
@@ -486,7 +484,7 @@ st20:
|
|
486
484
|
if ( ++p == pe )
|
487
485
|
goto _test_eof20;
|
488
486
|
case 20:
|
489
|
-
#line
|
487
|
+
#line 488 "ext/puma_http11/http11_parser.c"
|
490
488
|
switch( (*p) ) {
|
491
489
|
case 32: goto tr31;
|
492
490
|
case 60: goto st0;
|
@@ -507,7 +505,7 @@ st21:
|
|
507
505
|
if ( ++p == pe )
|
508
506
|
goto _test_eof21;
|
509
507
|
case 21:
|
510
|
-
#line
|
508
|
+
#line 509 "ext/puma_http11/http11_parser.c"
|
511
509
|
switch( (*p) ) {
|
512
510
|
case 32: goto tr33;
|
513
511
|
case 60: goto st0;
|
@@ -528,7 +526,7 @@ st22:
|
|
528
526
|
if ( ++p == pe )
|
529
527
|
goto _test_eof22;
|
530
528
|
case 22:
|
531
|
-
#line
|
529
|
+
#line 530 "ext/puma_http11/http11_parser.c"
|
532
530
|
switch( (*p) ) {
|
533
531
|
case 43: goto st22;
|
534
532
|
case 58: goto st23;
|
@@ -553,7 +551,7 @@ st23:
|
|
553
551
|
if ( ++p == pe )
|
554
552
|
goto _test_eof23;
|
555
553
|
case 23:
|
556
|
-
#line
|
554
|
+
#line 555 "ext/puma_http11/http11_parser.c"
|
557
555
|
switch( (*p) ) {
|
558
556
|
case 32: goto tr8;
|
559
557
|
case 34: goto st0;
|
@@ -573,7 +571,7 @@ st24:
|
|
573
571
|
if ( ++p == pe )
|
574
572
|
goto _test_eof24;
|
575
573
|
case 24:
|
576
|
-
#line
|
574
|
+
#line 575 "ext/puma_http11/http11_parser.c"
|
577
575
|
switch( (*p) ) {
|
578
576
|
case 32: goto tr37;
|
579
577
|
case 34: goto st0;
|
@@ -596,7 +594,7 @@ st25:
|
|
596
594
|
if ( ++p == pe )
|
597
595
|
goto _test_eof25;
|
598
596
|
case 25:
|
599
|
-
#line
|
597
|
+
#line 598 "ext/puma_http11/http11_parser.c"
|
600
598
|
switch( (*p) ) {
|
601
599
|
case 32: goto tr41;
|
602
600
|
case 34: goto st0;
|
@@ -616,7 +614,7 @@ st26:
|
|
616
614
|
if ( ++p == pe )
|
617
615
|
goto _test_eof26;
|
618
616
|
case 26:
|
619
|
-
#line
|
617
|
+
#line 618 "ext/puma_http11/http11_parser.c"
|
620
618
|
switch( (*p) ) {
|
621
619
|
case 32: goto tr44;
|
622
620
|
case 34: goto st0;
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -28,6 +28,8 @@ typedef struct {
|
|
28
28
|
int bytes;
|
29
29
|
} ms_cert_buf;
|
30
30
|
|
31
|
+
VALUE eError;
|
32
|
+
|
31
33
|
void engine_free(void *ptr) {
|
32
34
|
ms_conn *conn = ptr;
|
33
35
|
ms_cert_buf* cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
|
@@ -47,61 +49,65 @@ const rb_data_type_t engine_data_type = {
|
|
47
49
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
|
48
50
|
};
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
*obj = TypedData_Make_Struct(klass, ms_conn, &engine_data_type, conn);
|
54
|
-
|
55
|
-
conn->read = BIO_new(BIO_s_mem());
|
56
|
-
BIO_set_nbio(conn->read, 1);
|
57
|
-
|
58
|
-
conn->write = BIO_new(BIO_s_mem());
|
59
|
-
BIO_set_nbio(conn->write, 1);
|
60
|
-
|
61
|
-
conn->ssl = 0;
|
62
|
-
conn->ctx = 0;
|
63
|
-
|
64
|
-
return conn;
|
65
|
-
}
|
66
|
-
|
67
|
-
DH *get_dh1024() {
|
68
|
-
/* `openssl dhparam 1024 -C`
|
52
|
+
DH *get_dh2048() {
|
53
|
+
/* `openssl dhparam -C 2048`
|
69
54
|
* -----BEGIN DH PARAMETERS-----
|
70
|
-
*
|
71
|
-
*
|
72
|
-
*
|
55
|
+
* MIIBCAKCAQEAjmh1uQHdTfxOyxEbKAV30fUfzqMDF/ChPzjfyzl2jcrqQMhrk76o
|
56
|
+
* 2NPNXqxHwsddMZ1RzvU8/jl+uhRuPWjXCFZbhET4N1vrviZM3VJhV8PPHuiVOACO
|
57
|
+
* y32jFd+Szx4bo2cXSK83hJ6jRd+0asP1awWjz9/06dFkrILCXMIfQLo0D8rqmppn
|
58
|
+
* EfDDAwuudCpM9kcDmBRAm9JsKbQ6gzZWjkc5+QWSaQofojIHbjvj3xzguaCJn+oQ
|
59
|
+
* vHWM+hsAnaOgEwCyeZ3xqs+/5lwSbkE/tqJW98cEZGygBUVo9jxZRZx6KOfjpdrb
|
60
|
+
* yenO9LJr/qtyrZB31WJbqxI0m0AKTAO8UwIBAg==
|
73
61
|
* -----END DH PARAMETERS-----
|
74
62
|
*/
|
75
|
-
static unsigned char
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
63
|
+
static unsigned char dh2048_p[] = {
|
64
|
+
0x8E, 0x68, 0x75, 0xB9, 0x01, 0xDD, 0x4D, 0xFC, 0x4E, 0xCB,
|
65
|
+
0x11, 0x1B, 0x28, 0x05, 0x77, 0xD1, 0xF5, 0x1F, 0xCE, 0xA3,
|
66
|
+
0x03, 0x17, 0xF0, 0xA1, 0x3F, 0x38, 0xDF, 0xCB, 0x39, 0x76,
|
67
|
+
0x8D, 0xCA, 0xEA, 0x40, 0xC8, 0x6B, 0x93, 0xBE, 0xA8, 0xD8,
|
68
|
+
0xD3, 0xCD, 0x5E, 0xAC, 0x47, 0xC2, 0xC7, 0x5D, 0x31, 0x9D,
|
69
|
+
0x51, 0xCE, 0xF5, 0x3C, 0xFE, 0x39, 0x7E, 0xBA, 0x14, 0x6E,
|
70
|
+
0x3D, 0x68, 0xD7, 0x08, 0x56, 0x5B, 0x84, 0x44, 0xF8, 0x37,
|
71
|
+
0x5B, 0xEB, 0xBE, 0x26, 0x4C, 0xDD, 0x52, 0x61, 0x57, 0xC3,
|
72
|
+
0xCF, 0x1E, 0xE8, 0x95, 0x38, 0x00, 0x8E, 0xCB, 0x7D, 0xA3,
|
73
|
+
0x15, 0xDF, 0x92, 0xCF, 0x1E, 0x1B, 0xA3, 0x67, 0x17, 0x48,
|
74
|
+
0xAF, 0x37, 0x84, 0x9E, 0xA3, 0x45, 0xDF, 0xB4, 0x6A, 0xC3,
|
75
|
+
0xF5, 0x6B, 0x05, 0xA3, 0xCF, 0xDF, 0xF4, 0xE9, 0xD1, 0x64,
|
76
|
+
0xAC, 0x82, 0xC2, 0x5C, 0xC2, 0x1F, 0x40, 0xBA, 0x34, 0x0F,
|
77
|
+
0xCA, 0xEA, 0x9A, 0x9A, 0x67, 0x11, 0xF0, 0xC3, 0x03, 0x0B,
|
78
|
+
0xAE, 0x74, 0x2A, 0x4C, 0xF6, 0x47, 0x03, 0x98, 0x14, 0x40,
|
79
|
+
0x9B, 0xD2, 0x6C, 0x29, 0xB4, 0x3A, 0x83, 0x36, 0x56, 0x8E,
|
80
|
+
0x47, 0x39, 0xF9, 0x05, 0x92, 0x69, 0x0A, 0x1F, 0xA2, 0x32,
|
81
|
+
0x07, 0x6E, 0x3B, 0xE3, 0xDF, 0x1C, 0xE0, 0xB9, 0xA0, 0x89,
|
82
|
+
0x9F, 0xEA, 0x10, 0xBC, 0x75, 0x8C, 0xFA, 0x1B, 0x00, 0x9D,
|
83
|
+
0xA3, 0xA0, 0x13, 0x00, 0xB2, 0x79, 0x9D, 0xF1, 0xAA, 0xCF,
|
84
|
+
0xBF, 0xE6, 0x5C, 0x12, 0x6E, 0x41, 0x3F, 0xB6, 0xA2, 0x56,
|
85
|
+
0xF7, 0xC7, 0x04, 0x64, 0x6C, 0xA0, 0x05, 0x45, 0x68, 0xF6,
|
86
|
+
0x3C, 0x59, 0x45, 0x9C, 0x7A, 0x28, 0xE7, 0xE3, 0xA5, 0xDA,
|
87
|
+
0xDB, 0xC9, 0xE9, 0xCE, 0xF4, 0xB2, 0x6B, 0xFE, 0xAB, 0x72,
|
88
|
+
0xAD, 0x90, 0x77, 0xD5, 0x62, 0x5B, 0xAB, 0x12, 0x34, 0x9B,
|
89
|
+
0x40, 0x0A, 0x4C, 0x03, 0xBC, 0x53
|
87
90
|
};
|
88
|
-
static unsigned char
|
91
|
+
static unsigned char dh2048_g[] = { 0x02 };
|
89
92
|
|
90
93
|
DH *dh;
|
94
|
+
#if !(OPENSSL_VERSION_NUMBER < 0x10100005L || defined(LIBRESSL_VERSION_NUMBER))
|
95
|
+
BIGNUM *p, *g;
|
96
|
+
#endif
|
97
|
+
|
91
98
|
dh = DH_new();
|
92
99
|
|
93
100
|
#if OPENSSL_VERSION_NUMBER < 0x10100005L || defined(LIBRESSL_VERSION_NUMBER)
|
94
|
-
dh->p = BN_bin2bn(
|
95
|
-
dh->g = BN_bin2bn(
|
101
|
+
dh->p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
|
102
|
+
dh->g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL);
|
96
103
|
|
97
104
|
if ((dh->p == NULL) || (dh->g == NULL)) {
|
98
105
|
DH_free(dh);
|
99
106
|
return NULL;
|
100
107
|
}
|
101
108
|
#else
|
102
|
-
|
103
|
-
|
104
|
-
g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
|
109
|
+
p = BN_bin2bn(dh2048_p, sizeof(dh2048_p), NULL);
|
110
|
+
g = BN_bin2bn(dh2048_g, sizeof(dh2048_g), NULL);
|
105
111
|
|
106
112
|
if (p == NULL || g == NULL || !DH_set0_pqg(dh, p, NULL, g)) {
|
107
113
|
DH_free(dh);
|
@@ -114,6 +120,37 @@ DH *get_dh1024() {
|
|
114
120
|
return dh;
|
115
121
|
}
|
116
122
|
|
123
|
+
static void
|
124
|
+
sslctx_free(void *ptr) {
|
125
|
+
SSL_CTX *ctx = ptr;
|
126
|
+
SSL_CTX_free(ctx);
|
127
|
+
}
|
128
|
+
|
129
|
+
static const rb_data_type_t sslctx_type = {
|
130
|
+
"MiniSSL/SSLContext",
|
131
|
+
{
|
132
|
+
0, sslctx_free,
|
133
|
+
},
|
134
|
+
0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
|
135
|
+
};
|
136
|
+
|
137
|
+
ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
|
138
|
+
ms_conn* conn;
|
139
|
+
|
140
|
+
*obj = TypedData_Make_Struct(klass, ms_conn, &engine_data_type, conn);
|
141
|
+
|
142
|
+
conn->read = BIO_new(BIO_s_mem());
|
143
|
+
BIO_set_nbio(conn->read, 1);
|
144
|
+
|
145
|
+
conn->write = BIO_new(BIO_s_mem());
|
146
|
+
BIO_set_nbio(conn->write, 1);
|
147
|
+
|
148
|
+
conn->ssl = 0;
|
149
|
+
conn->ctx = 0;
|
150
|
+
|
151
|
+
return conn;
|
152
|
+
}
|
153
|
+
|
117
154
|
static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
|
118
155
|
X509* err_cert;
|
119
156
|
SSL* ssl;
|
@@ -140,49 +177,73 @@ static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
|
|
140
177
|
return preverify_ok;
|
141
178
|
}
|
142
179
|
|
143
|
-
|
144
|
-
|
180
|
+
static VALUE
|
181
|
+
sslctx_alloc(VALUE klass) {
|
182
|
+
SSL_CTX *ctx;
|
183
|
+
long mode = 0 |
|
184
|
+
SSL_MODE_ENABLE_PARTIAL_WRITE |
|
185
|
+
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER |
|
186
|
+
SSL_MODE_RELEASE_BUFFERS;
|
187
|
+
|
188
|
+
#ifdef HAVE_TLS_SERVER_METHOD
|
189
|
+
ctx = SSL_CTX_new(TLS_method());
|
190
|
+
// printf("\nctx using TLS_method security_level %d\n", SSL_CTX_get_security_level(ctx));
|
191
|
+
#else
|
192
|
+
ctx = SSL_CTX_new(SSLv23_method());
|
193
|
+
#endif
|
194
|
+
if (!ctx) {
|
195
|
+
rb_raise(eError, "SSL_CTX_new");
|
196
|
+
}
|
197
|
+
SSL_CTX_set_mode(ctx, mode);
|
198
|
+
|
199
|
+
return TypedData_Wrap_Struct(klass, &sslctx_type, ctx);
|
200
|
+
}
|
201
|
+
|
202
|
+
VALUE
|
203
|
+
sslctx_initialize(VALUE self, VALUE mini_ssl_ctx) {
|
145
204
|
SSL_CTX* ctx;
|
146
|
-
SSL* ssl;
|
147
|
-
int min, ssl_options;
|
148
205
|
|
149
|
-
|
206
|
+
#ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
|
207
|
+
int min;
|
208
|
+
#endif
|
209
|
+
int ssl_options;
|
210
|
+
VALUE key, cert, ca, verify_mode, ssl_cipher_filter, no_tlsv1, no_tlsv1_1,
|
211
|
+
verification_flags;
|
212
|
+
DH *dh;
|
213
|
+
|
214
|
+
#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
215
|
+
EC_KEY *ecdh;
|
216
|
+
#endif
|
150
217
|
|
151
|
-
|
152
|
-
VALUE key = rb_funcall(mini_ssl_ctx, sym_key, 0);
|
218
|
+
TypedData_Get_Struct(self, SSL_CTX, &sslctx_type, ctx);
|
153
219
|
|
220
|
+
key = rb_funcall(mini_ssl_ctx, rb_intern_const("key"), 0);
|
154
221
|
StringValue(key);
|
155
222
|
|
156
|
-
|
157
|
-
VALUE cert = rb_funcall(mini_ssl_ctx, sym_cert, 0);
|
158
|
-
|
223
|
+
cert = rb_funcall(mini_ssl_ctx, rb_intern_const("cert"), 0);
|
159
224
|
StringValue(cert);
|
160
225
|
|
161
|
-
|
162
|
-
VALUE ca = rb_funcall(mini_ssl_ctx, sym_ca, 0);
|
226
|
+
ca = rb_funcall(mini_ssl_ctx, rb_intern_const("ca"), 0);
|
163
227
|
|
164
|
-
|
165
|
-
VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0);
|
228
|
+
verify_mode = rb_funcall(mini_ssl_ctx, rb_intern_const("verify_mode"), 0);
|
166
229
|
|
167
|
-
|
168
|
-
VALUE ssl_cipher_filter = rb_funcall(mini_ssl_ctx, sym_ssl_cipher_filter, 0);
|
230
|
+
ssl_cipher_filter = rb_funcall(mini_ssl_ctx, rb_intern_const("ssl_cipher_filter"), 0);
|
169
231
|
|
170
|
-
|
171
|
-
VALUE no_tlsv1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1, 0);
|
232
|
+
no_tlsv1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1"), 0);
|
172
233
|
|
173
|
-
|
174
|
-
VALUE no_tlsv1_1 = rb_funcall(mini_ssl_ctx, sym_no_tlsv1_1, 0);
|
175
|
-
|
176
|
-
#ifdef HAVE_TLS_SERVER_METHOD
|
177
|
-
ctx = SSL_CTX_new(TLS_server_method());
|
178
|
-
#else
|
179
|
-
ctx = SSL_CTX_new(SSLv23_server_method());
|
180
|
-
#endif
|
181
|
-
conn->ctx = ctx;
|
234
|
+
no_tlsv1_1 = rb_funcall(mini_ssl_ctx, rb_intern_const("no_tlsv1_1"), 0);
|
182
235
|
|
183
236
|
SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
|
184
237
|
SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
|
185
238
|
|
239
|
+
verification_flags = rb_funcall(mini_ssl_ctx, rb_intern_const("verification_flags"), 0);
|
240
|
+
|
241
|
+
if (!NIL_P(verification_flags)) {
|
242
|
+
X509_VERIFY_PARAM *param = SSL_CTX_get0_param(ctx);
|
243
|
+
X509_VERIFY_PARAM_set_flags(param, NUM2INT(verification_flags));
|
244
|
+
SSL_CTX_set1_param(ctx, param);
|
245
|
+
}
|
246
|
+
|
186
247
|
if (!NIL_P(ca)) {
|
187
248
|
StringValue(ca);
|
188
249
|
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
|
@@ -228,35 +289,45 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
228
289
|
SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
|
229
290
|
}
|
230
291
|
|
231
|
-
|
292
|
+
dh = get_dh2048();
|
232
293
|
SSL_CTX_set_tmp_dh(ctx, dh);
|
233
294
|
|
234
295
|
#if OPENSSL_VERSION_NUMBER < 0x10002000L
|
235
296
|
// Remove this case if OpenSSL 1.0.1 (now EOL) support is no
|
236
297
|
// longer needed.
|
237
|
-
|
298
|
+
ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
|
238
299
|
if (ecdh) {
|
239
300
|
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
240
301
|
EC_KEY_free(ecdh);
|
241
302
|
}
|
242
303
|
#elif OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
|
243
|
-
// Prior to OpenSSL 1.1.0, servers must manually enable server-side ECDH
|
244
|
-
// negotiation.
|
245
304
|
SSL_CTX_set_ecdh_auto(ctx, 1);
|
246
305
|
#endif
|
247
306
|
|
248
|
-
ssl = SSL_new(ctx);
|
249
|
-
conn->ssl = ssl;
|
250
|
-
SSL_set_app_data(ssl, NULL);
|
251
|
-
|
252
307
|
if (NIL_P(verify_mode)) {
|
253
|
-
/*
|
308
|
+
/* SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); */
|
254
309
|
} else {
|
255
|
-
|
310
|
+
SSL_CTX_set_verify(ctx, NUM2INT(verify_mode), engine_verify_callback);
|
256
311
|
}
|
312
|
+
// printf("\ninitialize end security_level %d\n", SSL_CTX_get_security_level(ctx));
|
313
|
+
rb_obj_freeze(self);
|
314
|
+
return self;
|
315
|
+
}
|
257
316
|
|
258
|
-
|
317
|
+
VALUE engine_init_server(VALUE self, VALUE sslctx) {
|
318
|
+
ms_conn* conn;
|
319
|
+
VALUE obj;
|
320
|
+
SSL_CTX* ctx;
|
321
|
+
SSL* ssl;
|
322
|
+
|
323
|
+
conn = engine_alloc(self, &obj);
|
324
|
+
|
325
|
+
TypedData_Get_Struct(sslctx, SSL_CTX, &sslctx_type, ctx);
|
259
326
|
|
327
|
+
ssl = SSL_new(ctx);
|
328
|
+
conn->ssl = ssl;
|
329
|
+
SSL_set_app_data(ssl, NULL);
|
330
|
+
SSL_set_bio(ssl, conn->read, conn->write);
|
260
331
|
SSL_set_accept_state(ssl);
|
261
332
|
return obj;
|
262
333
|
}
|
@@ -296,7 +367,7 @@ VALUE engine_inject(VALUE self, VALUE str) {
|
|
296
367
|
return INT2FIX(used);
|
297
368
|
}
|
298
369
|
|
299
|
-
|
370
|
+
NORETURN(void raise_error(SSL* ssl, int result));
|
300
371
|
|
301
372
|
void raise_error(SSL* ssl, int result) {
|
302
373
|
char buf[512];
|
@@ -320,8 +391,7 @@ void raise_error(SSL* ssl, int result) {
|
|
320
391
|
} else {
|
321
392
|
err = (int) ERR_get_error();
|
322
393
|
ERR_error_string_n(err, buf, sizeof(buf));
|
323
|
-
|
324
|
-
snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, errexp);
|
394
|
+
snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, err & mask);
|
325
395
|
}
|
326
396
|
} else {
|
327
397
|
snprintf(msg, sizeof(msg), "Unknown OpenSSL error: %d", ssl_err);
|
@@ -385,7 +455,9 @@ VALUE engine_extract(VALUE self) {
|
|
385
455
|
ms_conn* conn;
|
386
456
|
int bytes;
|
387
457
|
size_t pending;
|
388
|
-
|
458
|
+
// https://www.openssl.org/docs/manmaster/man3/BIO_f_buffer.html
|
459
|
+
// crypto/bio/bf_buff.c DEFAULT_BUFFER_SIZE
|
460
|
+
char buf[4096];
|
389
461
|
|
390
462
|
TypedData_Get_Struct(self, ms_conn, &engine_data_type, conn);
|
391
463
|
|
@@ -480,7 +552,7 @@ VALUE noop(VALUE self) {
|
|
480
552
|
}
|
481
553
|
|
482
554
|
void Init_mini_ssl(VALUE puma) {
|
483
|
-
VALUE mod, eng;
|
555
|
+
VALUE mod, eng, sslctx;
|
484
556
|
|
485
557
|
/* Fake operation for documentation (RDoc, YARD) */
|
486
558
|
#if 0 == 1
|
@@ -494,6 +566,11 @@ void Init_mini_ssl(VALUE puma) {
|
|
494
566
|
|
495
567
|
mod = rb_define_module_under(puma, "MiniSSL");
|
496
568
|
eng = rb_define_class_under(mod, "Engine", rb_cObject);
|
569
|
+
sslctx = rb_define_class_under(mod, "SSLContext", rb_cObject);
|
570
|
+
rb_define_alloc_func(sslctx, sslctx_alloc);
|
571
|
+
rb_define_method(sslctx, "initialize", sslctx_initialize, 1);
|
572
|
+
rb_undef_method(sslctx, "initialize_copy");
|
573
|
+
|
497
574
|
|
498
575
|
// OpenSSL Build / Runtime/Load versions
|
499
576
|
|
@@ -552,9 +629,10 @@ void Init_mini_ssl(VALUE puma) {
|
|
552
629
|
|
553
630
|
#else
|
554
631
|
|
632
|
+
NORETURN(VALUE raise_error(VALUE self));
|
633
|
+
|
555
634
|
VALUE raise_error(VALUE self) {
|
556
635
|
rb_raise(rb_eStandardError, "SSL not available in this build");
|
557
|
-
return Qnil;
|
558
636
|
}
|
559
637
|
|
560
638
|
void Init_mini_ssl(VALUE puma) {
|