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.

Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +131 -10
  3. data/README.md +24 -2
  4. data/docs/architecture.md +22 -18
  5. data/docs/compile_options.md +6 -6
  6. data/docs/deployment.md +2 -2
  7. data/docs/jungle/rc.d/README.md +1 -1
  8. data/docs/kubernetes.md +66 -0
  9. data/docs/plugins.md +2 -2
  10. data/docs/rails_dev_mode.md +29 -0
  11. data/docs/restart.md +1 -1
  12. data/docs/stats.md +142 -0
  13. data/docs/systemd.md +1 -1
  14. data/ext/puma_http11/extconf.rb +14 -0
  15. data/ext/puma_http11/http11_parser.c +19 -21
  16. data/ext/puma_http11/http11_parser.h +1 -1
  17. data/ext/puma_http11/http11_parser.java.rl +1 -1
  18. data/ext/puma_http11/http11_parser.rl +1 -1
  19. data/ext/puma_http11/mini_ssl.c +162 -84
  20. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
  21. data/ext/puma_http11/puma_http11.c +2 -2
  22. data/lib/puma.rb +34 -8
  23. data/lib/puma/binder.rb +50 -43
  24. data/lib/puma/client.rb +5 -3
  25. data/lib/puma/cluster.rb +40 -8
  26. data/lib/puma/cluster/worker_handle.rb +4 -0
  27. data/lib/puma/configuration.rb +4 -1
  28. data/lib/puma/const.rb +3 -3
  29. data/lib/puma/control_cli.rb +5 -1
  30. data/lib/puma/detect.rb +14 -10
  31. data/lib/puma/dsl.rb +56 -4
  32. data/lib/puma/error_logger.rb +12 -5
  33. data/lib/puma/events.rb +2 -3
  34. data/lib/puma/launcher.rb +4 -3
  35. data/lib/puma/minissl.rb +48 -17
  36. data/lib/puma/minissl/context_builder.rb +6 -0
  37. data/lib/puma/null_io.rb +12 -0
  38. data/lib/puma/queue_close.rb +7 -7
  39. data/lib/puma/reactor.rb +7 -2
  40. data/lib/puma/request.rb +9 -4
  41. data/lib/puma/runner.rb +8 -3
  42. data/lib/puma/server.rb +46 -112
  43. data/lib/puma/thread_pool.rb +4 -3
  44. data/lib/rack/handler/puma.rb +1 -0
  45. 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` is disabled (it is disabled by default).
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 /etc/systemd/system/puma.service, or if
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
@@ -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 55 "ext/puma_http11/http11_parser.c"
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 89 "ext/puma_http11/http11_parser.c"
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 120 "ext/puma_http11/http11_parser.c"
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 145 "ext/puma_http11/http11_parser.c"
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 169 "ext/puma_http11/http11_parser.c"
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 231 "ext/puma_http11/http11_parser.c"
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 243 "ext/puma_http11/http11_parser.c"
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 324 "ext/puma_http11/http11_parser.c"
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 375 "ext/puma_http11/http11_parser.c"
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 391 "ext/puma_http11/http11_parser.c"
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 430 "ext/puma_http11/http11_parser.c"
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 444 "ext/puma_http11/http11_parser.c"
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 490 "ext/puma_http11/http11_parser.c"
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 511 "ext/puma_http11/http11_parser.c"
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 532 "ext/puma_http11/http11_parser.c"
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 557 "ext/puma_http11/http11_parser.c"
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 577 "ext/puma_http11/http11_parser.c"
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 600 "ext/puma_http11/http11_parser.c"
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 620 "ext/puma_http11/http11_parser.c"
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;
@@ -29,8 +29,8 @@ typedef void (*field_cb)(struct puma_parser* hp,
29
29
 
30
30
  typedef struct puma_parser {
31
31
  int cs;
32
- size_t body_start;
33
32
  int content_len;
33
+ size_t body_start;
34
34
  size_t nread;
35
35
  size_t mark;
36
36
  size_t field_start;
@@ -58,7 +58,7 @@ public class Http11Parser {
58
58
  }%%
59
59
 
60
60
  /** Data **/
61
- %% write data;
61
+ %% write data noentry;
62
62
 
63
63
  public static interface ElementCB {
64
64
  public void call(Ruby runtime, RubyHash data, ByteList buffer, int at, int length);
@@ -81,7 +81,7 @@ static void snake_upcase_char(char *c)
81
81
  }%%
82
82
 
83
83
  /** Data **/
84
- %% write data;
84
+ %% write data noentry;
85
85
 
86
86
  int puma_parser_init(puma_parser *parser) {
87
87
  int cs = 0;
@@ -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
- ms_conn* engine_alloc(VALUE klass, VALUE* obj) {
51
- ms_conn* conn;
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
- * MIGHAoGBALPwcEv0OstmQCZdfHw0N5r+07lmXMxkpQacy1blwj0LUqC+Divp6pBk
71
- * usTJ9W2/dOYr1X7zi6yXNLp4oLzc/31PUL3D9q8CpGS7vPz5gijKSw9BwCTT5z9+
72
- * KF9v46qw8XqT5HHV87sWFlGQcVFq+pEkA2kPikkKZ/X/CCcpCAV7AgEC
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 dh1024_p[] = {
76
- 0xB3,0xF0,0x70,0x4B,0xF4,0x3A,0xCB,0x66,0x40,0x26,0x5D,0x7C,
77
- 0x7C,0x34,0x37,0x9A,0xFE,0xD3,0xB9,0x66,0x5C,0xCC,0x64,0xA5,
78
- 0x06,0x9C,0xCB,0x56,0xE5,0xC2,0x3D,0x0B,0x52,0xA0,0xBE,0x0E,
79
- 0x2B,0xE9,0xEA,0x90,0x64,0xBA,0xC4,0xC9,0xF5,0x6D,0xBF,0x74,
80
- 0xE6,0x2B,0xD5,0x7E,0xF3,0x8B,0xAC,0x97,0x34,0xBA,0x78,0xA0,
81
- 0xBC,0xDC,0xFF,0x7D,0x4F,0x50,0xBD,0xC3,0xF6,0xAF,0x02,0xA4,
82
- 0x64,0xBB,0xBC,0xFC,0xF9,0x82,0x28,0xCA,0x4B,0x0F,0x41,0xC0,
83
- 0x24,0xD3,0xE7,0x3F,0x7E,0x28,0x5F,0x6F,0xE3,0xAA,0xB0,0xF1,
84
- 0x7A,0x93,0xE4,0x71,0xD5,0xF3,0xBB,0x16,0x16,0x51,0x90,0x71,
85
- 0x51,0x6A,0xFA,0x91,0x24,0x03,0x69,0x0F,0x8A,0x49,0x0A,0x67,
86
- 0xF5,0xFF,0x08,0x27,0x29,0x08,0x05,0x7B
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 dh1024_g[] = { 0x02 };
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(dh1024_p, sizeof(dh1024_p), NULL);
95
- dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
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
- BIGNUM *p, *g;
103
- p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
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
- VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
144
- VALUE obj;
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
- ms_conn* conn = engine_alloc(self, &obj);
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
- ID sym_key = rb_intern("key");
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
- ID sym_cert = rb_intern("cert");
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
- ID sym_ca = rb_intern("ca");
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
- ID sym_verify_mode = rb_intern("verify_mode");
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
- ID sym_ssl_cipher_filter = rb_intern("ssl_cipher_filter");
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
- ID sym_no_tlsv1 = rb_intern("no_tlsv1");
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
- ID sym_no_tlsv1_1 = rb_intern("no_tlsv1_1");
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
- DH *dh = get_dh1024();
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
- EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
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
- /* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
308
+ /* SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); */
254
309
  } else {
255
- SSL_set_verify(ssl, NUM2INT(verify_mode), engine_verify_callback);
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
- SSL_set_bio(ssl, conn->read, conn->write);
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
- static VALUE eError;
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
- int errexp = err & mask;
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
- char buf[512];
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) {