puma 2.12.3 → 2.16.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/DEPLOYMENT.md +3 -3
- data/History.txt +164 -0
- data/README.md +21 -2
- data/docs/nginx.md +1 -1
- data/ext/puma_http11/extconf.rb +8 -4
- data/ext/puma_http11/mini_ssl.c +36 -3
- data/ext/puma_http11/org/jruby/puma/Http11.java +12 -3
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +21 -74
- data/ext/puma_http11/puma_http11.c +12 -4
- data/lib/puma/binder.rb +9 -2
- data/lib/puma/capistrano.rb +2 -0
- data/lib/puma/cli.rb +57 -52
- data/lib/puma/client.rb +19 -0
- data/lib/puma/cluster.rb +24 -3
- data/lib/puma/configuration.rb +63 -12
- data/lib/puma/const.rb +6 -5
- data/lib/puma/control_cli.rb +24 -14
- data/lib/puma/dsl.rb +71 -3
- data/lib/puma/events.rb +1 -1
- data/lib/puma/minissl.rb +2 -0
- data/lib/puma/runner.rb +2 -1
- data/lib/puma/server.rb +23 -7
- data/lib/puma/single.rb +1 -1
- data/lib/puma/thread_pool.rb +5 -7
- data/lib/rack/handler/puma.rb +13 -9
- data/tools/jungle/init.d/puma +50 -3
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 761911bec0cc543faf03994736a763dc7d87b6eb
|
4
|
+
data.tar.gz: 20cd1109cf893a9940549520e1f2e6c875532f2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c655e44ef966f2a4d8acd9b3d96b11a13072993edb1049ca65470cc0af3460bcd2e0d3f57f8afcec203cf6d9caad85cc7f1849454374ed65949b4284bd27ea33
|
7
|
+
data.tar.gz: 9b0e3acb94d4b0e3ae1a0f1eb1279dba87d600df67c928b455d8ea3ad47f3f90fb0a6854920776bad809dd46d013441e95385b981472d5f81f88d19ecbf4560e
|
data/DEPLOYMENT.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Deployment engineering for puma
|
2
2
|
|
3
3
|
Puma is software that is expected to be run in a deployed environment eventually.
|
4
|
-
You can
|
4
|
+
You can certainly use it as your dev server only, but most people look to use
|
5
5
|
it in their production deployments as well.
|
6
6
|
|
7
7
|
To that end, this is meant to serve as a foundation of wisdom how to do that
|
@@ -17,7 +17,7 @@ Welcome back!
|
|
17
17
|
|
18
18
|
## Single vs Cluster mode
|
19
19
|
|
20
|
-
Puma was originally
|
20
|
+
Puma was originally conceived as a thread-only webserver, but grew the ability to
|
21
21
|
also use processes in version 2.
|
22
22
|
|
23
23
|
Here are some rules of thumb:
|
@@ -27,7 +27,7 @@ Here are some rules of thumb:
|
|
27
27
|
* Use cluster mode and set the number of workers to 1.5x the number of cpu cores
|
28
28
|
in the machine, minimum 2.
|
29
29
|
* Set the number of threads to desired concurrent requests / number of workers.
|
30
|
-
Puma defaults to 8 and
|
30
|
+
Puma defaults to 8 and that's a decent number.
|
31
31
|
|
32
32
|
#### Migrating from Unicorn
|
33
33
|
|
data/History.txt
CHANGED
@@ -1,3 +1,167 @@
|
|
1
|
+
=== 2.16.0 / 2016-01-27
|
2
|
+
|
3
|
+
* 7 minor features:
|
4
|
+
|
5
|
+
* Add 'set_remote_address' config option
|
6
|
+
* Allow to run puma in silent mode
|
7
|
+
* Expose cli options in DSL
|
8
|
+
* Support passing JRuby keystore info in ssl_bind DSL
|
9
|
+
* Allow umask for unix:/// style control urls
|
10
|
+
* Expose `old_worker_count` in stats url
|
11
|
+
* Support TLS client auth (verify_mode) in jruby
|
12
|
+
|
13
|
+
* 7 bug fixes:
|
14
|
+
|
15
|
+
* Don't persist before_fork hook in state file
|
16
|
+
* Reload bundler before pulling in rack. Fixes #859
|
17
|
+
* Remove NEWRELIC_DISPATCHER env variable
|
18
|
+
* Cleanup C code
|
19
|
+
* Use Timeout.timeout instead of Object.timeout
|
20
|
+
* Make phased restarts faster
|
21
|
+
* Ignore the case of certain headers, because HTTP
|
22
|
+
|
23
|
+
* 1 doc changes:
|
24
|
+
|
25
|
+
* Test against the latest Ruby 2.1, 2.2, 2.3, head and JRuby 9.0.4.0 on Travis
|
26
|
+
|
27
|
+
* 12 merged PRs
|
28
|
+
* Merge pull request #822 from kwugirl/remove_NEWRELIC_DISPATCHER
|
29
|
+
* Merge pull request #833 from joemiller/jruby-client-tls-auth
|
30
|
+
* Merge pull request #837 from YuriSolovyov/ssl-keystore-jruby
|
31
|
+
* Merge pull request #839 from mezuka/master
|
32
|
+
* Merge pull request #845 from deepj/timeout-deprecation
|
33
|
+
* Merge pull request #846 from sriedel/strip_before_fork
|
34
|
+
* Merge pull request #850 from deepj/travis
|
35
|
+
* Merge pull request #853 from Jeffrey6052/patch-1
|
36
|
+
* Merge pull request #857 from zendesk/faster_phased_restarts
|
37
|
+
* Merge pull request #858 from mlarraz/fix_some_warnings
|
38
|
+
* Merge pull request #860 from zendesk/expose_old_worker_count
|
39
|
+
* Merge pull request #861 from zendesk/allow_control_url_umask
|
40
|
+
|
41
|
+
=== 2.15.3 / 2015-11-07
|
42
|
+
|
43
|
+
* 1 bug fix:
|
44
|
+
|
45
|
+
* Fix JRuby parser
|
46
|
+
|
47
|
+
=== 2.15.2 / 2015-11-06
|
48
|
+
|
49
|
+
* 2 bug fixes:
|
50
|
+
* ext/puma_http11: handle duplicate headers as per RFC
|
51
|
+
* Only set ctx.ca iff there is a params['ca'] to set with.
|
52
|
+
|
53
|
+
* 2 PRs merged:
|
54
|
+
* Merge pull request #818 from unleashed/support-duplicate-headers
|
55
|
+
* Merge pull request #819 from VictorLowther/fix-ca-and-verify_null-exception
|
56
|
+
|
57
|
+
=== 2.15.1 / 2015-11-06
|
58
|
+
|
59
|
+
* 1 bug fix:
|
60
|
+
|
61
|
+
* Allow older openssl versions
|
62
|
+
|
63
|
+
=== 2.15.0 / 2015-11-06
|
64
|
+
|
65
|
+
* 6 minor features:
|
66
|
+
* Allow setting ca without setting a verify mode
|
67
|
+
* Make jungle for init.d support rbenv
|
68
|
+
* Use SSL_CTX_use_certificate_chain_file for full chain
|
69
|
+
* cluster: add worker_boot_timeout option
|
70
|
+
* configuration: allow empty tags to mean no tag desired
|
71
|
+
* puma/cli: support specifying STD{OUT,ERR} redirections and append mode
|
72
|
+
|
73
|
+
* 5 bug fixes:
|
74
|
+
* Disable SSL Compression
|
75
|
+
* Fix bug setting worker_directory when using a symlink directory
|
76
|
+
* Fix error message in DSL that was slightly inaccurate
|
77
|
+
* Pumactl: set correct process name. Fixes #563
|
78
|
+
* thread_pool: fix race condition when shutting down workers
|
79
|
+
|
80
|
+
* 10 doc fixes:
|
81
|
+
* Add before_fork explanation in Readme.md
|
82
|
+
* Correct spelling in DEPLOYMENT.md
|
83
|
+
* Correct spelling in docs/nginx.md
|
84
|
+
* Fix spelling errors.
|
85
|
+
* Fix typo in deployment description
|
86
|
+
* Fix typos (it's -> its) in events.rb and server.rb
|
87
|
+
* fixing for typo mentioned in #803
|
88
|
+
* Spelling correction for README
|
89
|
+
* thread_pool: fix typos in comment
|
90
|
+
* More explicit docs for worker_timeout
|
91
|
+
|
92
|
+
* 18 PRs merged:
|
93
|
+
* Merge pull request #768 from nathansamson/patch-1
|
94
|
+
* Merge pull request #773 from rossta/spelling_corrections
|
95
|
+
* Merge pull request #774 from snow/master
|
96
|
+
* Merge pull request #781 from sunsations/fix-typo
|
97
|
+
* Merge pull request #791 from unleashed/allow_empty_tags
|
98
|
+
* Merge pull request #793 from robdimarco/fix-working-directory-symlink-bug
|
99
|
+
* Merge pull request #794 from peterkeen/patch-1
|
100
|
+
* Merge pull request #795 from unleashed/redirects-from-cmdline
|
101
|
+
* Merge pull request #796 from cschneid/fix_dsl_message
|
102
|
+
* Merge pull request #799 from annafw/master
|
103
|
+
* Merge pull request #800 from liamseanbrady/fix_typo
|
104
|
+
* Merge pull request #801 from scottjg/ssl-chain-file
|
105
|
+
* Merge pull request #802 from scottjg/ssl-crimes
|
106
|
+
* Merge pull request #804 from burningTyger/patch-2
|
107
|
+
* Merge pull request #809 from unleashed/threadpool-fix-race-in-shutdown
|
108
|
+
* Merge pull request #810 from vlmonk/fix-pumactl-restart-bug
|
109
|
+
* Merge pull request #814 from schneems/schneems/worker_timeout-docs
|
110
|
+
* Merge pull request #817 from unleashed/worker-boot-timeout
|
111
|
+
|
112
|
+
=== 2.14.0 / 2015-09-18
|
113
|
+
|
114
|
+
* 1 minor feature:
|
115
|
+
* Make building with SSL support optional
|
116
|
+
|
117
|
+
* 1 bug fix:
|
118
|
+
* Use Rack::Builder if available. Fixes #735
|
119
|
+
|
120
|
+
=== 2.13.4 / 2015-08-16
|
121
|
+
|
122
|
+
* 1 bug fix:
|
123
|
+
* Use the environment possible set by the config early and from
|
124
|
+
the config file later (if set).
|
125
|
+
|
126
|
+
=== 2.13.3 / 2015-08-15
|
127
|
+
|
128
|
+
Seriously, I need to revamp config with tests.
|
129
|
+
|
130
|
+
* 1 bug fix:
|
131
|
+
* Fix preserving options before cleaning for state. Fixes #769
|
132
|
+
|
133
|
+
=== 2.13.2 / 2015-08-15
|
134
|
+
|
135
|
+
The "clearly I don't have enough tests for the config" release.
|
136
|
+
|
137
|
+
* 1 bug fix:
|
138
|
+
* Fix another place binds wasn't initialized. Fixes #767
|
139
|
+
|
140
|
+
=== 2.13.1 / 2015-08-15
|
141
|
+
|
142
|
+
* 2 bug fixes:
|
143
|
+
* Fix binds being masked in config files. Fixes #765
|
144
|
+
* Use options from the config file properly in pumactl. Fixes #764
|
145
|
+
|
146
|
+
=== 2.13.0 / 2015-08-14
|
147
|
+
|
148
|
+
* 1 minor feature:
|
149
|
+
* Add before_fork hooks option.
|
150
|
+
|
151
|
+
* 3 bug fixes:
|
152
|
+
* Check for OPENSSL_NO_ECDH before using ECDH
|
153
|
+
* Eliminate logging overhead from JRuby SSL
|
154
|
+
* Prefer cli options over config file ones. Fixes #669
|
155
|
+
|
156
|
+
* 1 deprecation:
|
157
|
+
* Add deprecation warning to capistrano.rb. Fixes #673
|
158
|
+
|
159
|
+
* 4 PRs merged:
|
160
|
+
* Merge pull request #668 from kcollignon/patch-1
|
161
|
+
* Merge pull request #754 from nathansamson/before_boot
|
162
|
+
* Merge pull request #759 from BenV/fix-centos6-build
|
163
|
+
* Merge pull request #761 from looker/no-log
|
164
|
+
|
1
165
|
=== 2.12.3 / 2015-08-03
|
2
166
|
|
3
167
|
* 8 minor bugs fixed:
|
data/README.md
CHANGED
@@ -117,6 +117,25 @@ If you're preloading your application and using ActiveRecord, it's recommend you
|
|
117
117
|
end
|
118
118
|
end
|
119
119
|
|
120
|
+
On top of that, you can specify a block in your configuration file that will be run before workers are forked
|
121
|
+
|
122
|
+
# config/puma.rb
|
123
|
+
before_fork do
|
124
|
+
# configuration here
|
125
|
+
end
|
126
|
+
|
127
|
+
This code can be used to clean up before forking to clients, allowing
|
128
|
+
you to do some Puma-specific things that you don't want to embed in your application.
|
129
|
+
|
130
|
+
If you're preloading your application and using ActiveRecord, it's recommend you close any connections to the database here to prevent connection leakage:
|
131
|
+
|
132
|
+
# config/puma.rb
|
133
|
+
before_fork do
|
134
|
+
ActiveRecord::Base.connection_pool.disconnect!
|
135
|
+
end
|
136
|
+
|
137
|
+
This rule applies to any connections to external services (Redis, databases, memcache, ...) that might be started automatically by the framework.
|
138
|
+
|
120
139
|
When you use preload_app, your new code goes all in the master process, and is then copied in the workers (meaning it’s only compatible with cluster mode). General rule is to use preload_app when your workers die often and need fast starts. If you don’t have many workers, you probably should not use preload_app.
|
121
140
|
|
122
141
|
Note that preload_app can’t be used with phased restart, since phased restart kills and restarts workers one-by-one, and preload_app is all about copying the code of master into the workers.
|
@@ -167,7 +186,7 @@ You can also provide a configuration file which Puma will use with the `-C` (or
|
|
167
186
|
|
168
187
|
$ puma -C /path/to/config
|
169
188
|
|
170
|
-
By default, if no configuration file is
|
189
|
+
By default, if no configuration file is specified, Puma will look for a configuration file at config/puma.rb. If an environment is specified, either via the `-e` and `--environment` flags, or through the `RACK_ENV` environment variable, the default file location will be config/puma/environment_name.rb.
|
171
190
|
|
172
191
|
If you want to prevent Puma from looking for a configuration file in those locations, provide a dash as the argument to the `-C` (or `--config`) flag:
|
173
192
|
|
@@ -248,7 +267,7 @@ and then
|
|
248
267
|
$ bundle exec cap puma:start
|
249
268
|
$ bundle exec cap puma:restart
|
250
269
|
$ bundle exec cap puma:stop
|
251
|
-
$ bundle exec cap puma:
|
270
|
+
$ bundle exec cap puma:phased-restart
|
252
271
|
```
|
253
272
|
|
254
273
|
## Contributing
|
data/docs/nginx.md
CHANGED
@@ -40,7 +40,7 @@ server {
|
|
40
40
|
}
|
41
41
|
|
42
42
|
# check for index.html for directory index
|
43
|
-
# if
|
43
|
+
# if it's there on the filesystem then rewrite
|
44
44
|
# the url to add /index.html to the end of it
|
45
45
|
# and then break to send it to the next config rules.
|
46
46
|
if (-f $request_filename/index.html) {
|
data/ext/puma_http11/extconf.rb
CHANGED
@@ -2,8 +2,12 @@ require 'mkmf'
|
|
2
2
|
|
3
3
|
dir_config("puma_http11")
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
unless ENV["DISABLE_SSL"]
|
6
|
+
if %w'crypto libeay32'.find {|crypto| have_library(crypto, 'BIO_read')} and
|
7
|
+
%w'ssl ssleay32'.find {|ssl| have_library(ssl, 'SSL_CTX_new')}
|
8
|
+
|
9
|
+
have_header "openssl/bio.h"
|
10
|
+
end
|
9
11
|
end
|
12
|
+
|
13
|
+
create_makefile("puma/puma_http11")
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -1,12 +1,20 @@
|
|
1
1
|
#define RSTRING_NOT_MODIFIED 1
|
2
|
+
|
2
3
|
#include <ruby.h>
|
3
4
|
#include <rubyio.h>
|
5
|
+
|
6
|
+
#ifdef HAVE_OPENSSL_BIO_H
|
7
|
+
|
4
8
|
#include <openssl/bio.h>
|
5
9
|
#include <openssl/ssl.h>
|
6
10
|
#include <openssl/dh.h>
|
7
11
|
#include <openssl/err.h>
|
8
12
|
#include <openssl/x509.h>
|
9
13
|
|
14
|
+
#ifndef SSL_OP_NO_COMPRESSION
|
15
|
+
#define SSL_OP_NO_COMPRESSION 0
|
16
|
+
#endif
|
17
|
+
|
10
18
|
typedef struct {
|
11
19
|
BIO* read;
|
12
20
|
BIO* write;
|
@@ -132,14 +140,14 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
132
140
|
ctx = SSL_CTX_new(SSLv23_server_method());
|
133
141
|
conn->ctx = ctx;
|
134
142
|
|
135
|
-
|
143
|
+
SSL_CTX_use_certificate_chain_file(ctx, RSTRING_PTR(cert));
|
136
144
|
SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
|
137
145
|
|
138
146
|
if (!NIL_P(ca)) {
|
139
147
|
SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
|
140
148
|
}
|
141
149
|
|
142
|
-
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE);
|
150
|
+
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE | SSL_OP_SINGLE_ECDH_USE | SSL_OP_NO_COMPRESSION);
|
143
151
|
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
|
144
152
|
|
145
153
|
SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL@STRENGTH");
|
@@ -147,11 +155,13 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
|
|
147
155
|
DH *dh = get_dh1024();
|
148
156
|
SSL_CTX_set_tmp_dh(ctx, dh);
|
149
157
|
|
158
|
+
#ifndef OPENSSL_NO_ECDH
|
150
159
|
EC_KEY *ecdh = EC_KEY_new_by_curve_name(NID_secp521r1);
|
151
160
|
if (ecdh) {
|
152
161
|
SSL_CTX_set_tmp_ecdh(ctx, ecdh);
|
153
162
|
EC_KEY_free(ecdh);
|
154
163
|
}
|
164
|
+
#endif
|
155
165
|
|
156
166
|
ssl = SSL_new(ctx);
|
157
167
|
conn->ssl = ssl;
|
@@ -238,7 +248,7 @@ void raise_error(SSL* ssl, int result) {
|
|
238
248
|
VALUE engine_read(VALUE self) {
|
239
249
|
ms_conn* conn;
|
240
250
|
char buf[512];
|
241
|
-
int bytes,
|
251
|
+
int bytes, error;
|
242
252
|
|
243
253
|
Data_Get_Struct(self, ms_conn, conn);
|
244
254
|
|
@@ -345,6 +355,10 @@ VALUE engine_peercert(VALUE self) {
|
|
345
355
|
return rb_cert_buf;
|
346
356
|
}
|
347
357
|
|
358
|
+
VALUE noop(VALUE self) {
|
359
|
+
return Qnil;
|
360
|
+
}
|
361
|
+
|
348
362
|
void Init_mini_ssl(VALUE puma) {
|
349
363
|
VALUE mod, eng;
|
350
364
|
|
@@ -356,6 +370,8 @@ void Init_mini_ssl(VALUE puma) {
|
|
356
370
|
mod = rb_define_module_under(puma, "MiniSSL");
|
357
371
|
eng = rb_define_class_under(mod, "Engine", rb_cObject);
|
358
372
|
|
373
|
+
rb_define_singleton_method(mod, "check", noop, 0);
|
374
|
+
|
359
375
|
eError = rb_define_class_under(mod, "SSLError", rb_eStandardError);
|
360
376
|
|
361
377
|
rb_define_singleton_method(eng, "server", engine_init_server, 1);
|
@@ -369,3 +385,20 @@ void Init_mini_ssl(VALUE puma) {
|
|
369
385
|
|
370
386
|
rb_define_method(eng, "peercert", engine_peercert, 0);
|
371
387
|
}
|
388
|
+
|
389
|
+
#else
|
390
|
+
|
391
|
+
VALUE raise_error(VALUE self) {
|
392
|
+
rb_raise(rb_eStandardError, "SSL not available in this build");
|
393
|
+
return Qnil;
|
394
|
+
}
|
395
|
+
|
396
|
+
void Init_mini_ssl(VALUE puma) {
|
397
|
+
VALUE mod, eng;
|
398
|
+
|
399
|
+
mod = rb_define_module_under(puma, "MiniSSL");
|
400
|
+
rb_define_class_under(mod, "SSLError", rb_eStandardError);
|
401
|
+
|
402
|
+
rb_define_singleton_method(mod, "check", raise_error, 0);
|
403
|
+
}
|
404
|
+
#endif
|
@@ -82,11 +82,11 @@ public class Http11 extends RubyObject {
|
|
82
82
|
private Http11Parser.FieldCB http_field = new Http11Parser.FieldCB() {
|
83
83
|
public void call(Object data, int field, int flen, int value, int vlen) {
|
84
84
|
RubyHash req = (RubyHash)data;
|
85
|
-
RubyString
|
85
|
+
RubyString f;
|
86
|
+
IRubyObject v;
|
86
87
|
validateMaxLength(flen, MAX_FIELD_NAME_LENGTH, MAX_FIELD_NAME_LENGTH_ERR);
|
87
88
|
validateMaxLength(vlen, MAX_FIELD_VALUE_LENGTH, MAX_FIELD_VALUE_LENGTH_ERR);
|
88
89
|
|
89
|
-
v = RubyString.newString(runtime, new ByteList(Http11.this.hp.parser.buffer,value,vlen));
|
90
90
|
ByteList b = new ByteList(Http11.this.hp.parser.buffer,field,flen);
|
91
91
|
for(int i = 0,j = b.length();i<j;i++) {
|
92
92
|
if((b.get(i) & 0xFF) == '-') {
|
@@ -104,7 +104,16 @@ public class Http11 extends RubyObject {
|
|
104
104
|
f = RubyString.newString(runtime, "HTTP_");
|
105
105
|
f.cat(b);
|
106
106
|
}
|
107
|
-
|
107
|
+
|
108
|
+
b = new ByteList(Http11.this.hp.parser.buffer, value, vlen);
|
109
|
+
v = req.op_aref(req.getRuntime().getCurrentContext(), f);
|
110
|
+
if (v.isNil()) {
|
111
|
+
req.op_aset(req.getRuntime().getCurrentContext(), f, RubyString.newString(runtime, b));
|
112
|
+
} else {
|
113
|
+
RubyString vs = v.convertToString();
|
114
|
+
vs.cat(RubyString.newString(runtime, ", "));
|
115
|
+
vs.cat(b);
|
116
|
+
}
|
108
117
|
}
|
109
118
|
};
|
110
119
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
package org.jruby.puma;
|
2
2
|
|
3
3
|
import org.jruby.Ruby;
|
4
|
-
import org.jruby.RubyBoolean;
|
5
4
|
import org.jruby.RubyClass;
|
6
5
|
import org.jruby.RubyModule;
|
7
6
|
import org.jruby.RubyObject;
|
@@ -14,6 +13,7 @@ import org.jruby.runtime.builtin.IRubyObject;
|
|
14
13
|
import org.jruby.util.ByteList;
|
15
14
|
|
16
15
|
import javax.net.ssl.KeyManagerFactory;
|
16
|
+
import javax.net.ssl.TrustManagerFactory;
|
17
17
|
import javax.net.ssl.SSLContext;
|
18
18
|
import javax.net.ssl.SSLEngine;
|
19
19
|
import javax.net.ssl.SSLEngineResult;
|
@@ -39,9 +39,6 @@ public class MiniSSL extends RubyObject {
|
|
39
39
|
}
|
40
40
|
};
|
41
41
|
|
42
|
-
// set to true to switch on our low-fi trace logging
|
43
|
-
private static boolean DEBUG = false;
|
44
|
-
|
45
42
|
public static void createMiniSSL(Ruby runtime) {
|
46
43
|
RubyModule mPuma = runtime.defineModule("Puma");
|
47
44
|
RubyModule ssl = mPuma.defineModuleUnder("MiniSSL");
|
@@ -140,23 +137,36 @@ public class MiniSSL extends RubyObject {
|
|
140
137
|
public IRubyObject initialize(ThreadContext threadContext, IRubyObject miniSSLContext)
|
141
138
|
throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException {
|
142
139
|
KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
|
140
|
+
KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());
|
143
141
|
|
144
142
|
char[] password = miniSSLContext.callMethod(threadContext, "keystore_pass").convertToString().asJavaString().toCharArray();
|
145
|
-
|
146
|
-
|
143
|
+
String keystoreFile = miniSSLContext.callMethod(threadContext, "keystore").convertToString().asJavaString();
|
144
|
+
ks.load(new FileInputStream(keystoreFile), password);
|
145
|
+
ts.load(new FileInputStream(keystoreFile), password);
|
147
146
|
|
148
147
|
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
|
149
148
|
kmf.init(ks, password);
|
150
149
|
|
150
|
+
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
|
151
|
+
tmf.init(ts);
|
152
|
+
|
151
153
|
SSLContext sslCtx = SSLContext.getInstance("TLS");
|
152
154
|
|
153
|
-
sslCtx.init(kmf.getKeyManagers(),
|
155
|
+
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
|
154
156
|
engine = sslCtx.createSSLEngine();
|
155
157
|
|
156
158
|
String[] protocols = new String[] { "TLSv1", "TLSv1.1", "TLSv1.2" };
|
157
159
|
engine.setEnabledProtocols(protocols);
|
158
160
|
engine.setUseClientMode(false);
|
159
161
|
|
162
|
+
long verify_mode = miniSSLContext.callMethod(threadContext, "verify_mode").convertToInteger().getLongValue();
|
163
|
+
if ((verify_mode & 0x1) != 0) { // 'peer'
|
164
|
+
engine.setWantClientAuth(true);
|
165
|
+
}
|
166
|
+
if ((verify_mode & 0x2) != 0) { // 'force_peer'
|
167
|
+
engine.setNeedClientAuth(true);
|
168
|
+
}
|
169
|
+
|
160
170
|
SSLSession session = engine.getSession();
|
161
171
|
inboundNetData = new MiniSSLBuffer(session.getPacketBufferSize());
|
162
172
|
outboundAppData = new MiniSSLBuffer(session.getApplicationBufferSize());
|
@@ -170,12 +180,7 @@ public class MiniSSL extends RubyObject {
|
|
170
180
|
public IRubyObject inject(IRubyObject arg) {
|
171
181
|
try {
|
172
182
|
byte[] bytes = arg.convertToString().getBytes();
|
173
|
-
|
174
|
-
log("Net Data post pre-inject: " + inboundNetData);
|
175
183
|
inboundNetData.put(bytes);
|
176
|
-
log("Net Data post post-inject: " + inboundNetData);
|
177
|
-
|
178
|
-
log("inject(): " + bytes.length + " encrypted bytes from request");
|
179
184
|
return this;
|
180
185
|
} catch (Exception e) {
|
181
186
|
e.printStackTrace();
|
@@ -205,8 +210,6 @@ public class MiniSSL extends RubyObject {
|
|
205
210
|
|
206
211
|
switch (res.getStatus()) {
|
207
212
|
case BUFFER_OVERFLOW:
|
208
|
-
log("SSLOp#doRun(): overflow");
|
209
|
-
log("SSLOp#doRun(): dst data at overflow: " + dst);
|
210
213
|
// increase the buffer size to accommodate the overflowing data
|
211
214
|
int newSize = Math.max(engine.getSession().getPacketBufferSize(), engine.getSession().getApplicationBufferSize());
|
212
215
|
dst.resize(newSize + dst.position());
|
@@ -214,8 +217,6 @@ public class MiniSSL extends RubyObject {
|
|
214
217
|
retryOp = true;
|
215
218
|
break;
|
216
219
|
case BUFFER_UNDERFLOW:
|
217
|
-
log("SSLOp#doRun(): underflow");
|
218
|
-
log("SSLOp#doRun(): src data at underflow: " + src);
|
219
220
|
// need to wait for more data to come in before we retry
|
220
221
|
retryOp = false;
|
221
222
|
break;
|
@@ -245,25 +246,18 @@ public class MiniSSL extends RubyObject {
|
|
245
246
|
return getRuntime().getNil();
|
246
247
|
}
|
247
248
|
|
248
|
-
log("read(): inboundNetData prepped for read: " + inboundNetData);
|
249
|
-
|
250
249
|
MiniSSLBuffer inboundAppData = new MiniSSLBuffer(engine.getSession().getApplicationBufferSize());
|
251
|
-
|
252
|
-
log("read(): after initial unwrap", engine, res);
|
253
|
-
|
254
|
-
log("read(): Net Data post unwrap: " + inboundNetData);
|
250
|
+
doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
|
255
251
|
|
256
252
|
HandshakeStatus handshakeStatus = engine.getHandshakeStatus();
|
257
253
|
boolean done = false;
|
258
254
|
while (!done) {
|
259
255
|
switch (handshakeStatus) {
|
260
256
|
case NEED_WRAP:
|
261
|
-
|
262
|
-
log("read(): after handshake wrap", engine, res);
|
257
|
+
doOp(SSLOperation.WRAP, inboundAppData, outboundNetData);
|
263
258
|
break;
|
264
259
|
case NEED_UNWRAP:
|
265
|
-
res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
|
266
|
-
log("read(): after handshake unwrap", engine, res);
|
260
|
+
SSLEngineResult res = doOp(SSLOperation.UNWRAP, inboundNetData, inboundAppData);
|
267
261
|
if (res.getStatus() == Status.BUFFER_UNDERFLOW) {
|
268
262
|
// need more data before we can shake more hands
|
269
263
|
done = true;
|
@@ -276,13 +270,9 @@ public class MiniSSL extends RubyObject {
|
|
276
270
|
}
|
277
271
|
|
278
272
|
if (inboundNetData.hasRemaining()) {
|
279
|
-
log("Net Data post pre-compact: " + inboundNetData);
|
280
273
|
inboundNetData.compact();
|
281
|
-
log("Net Data post post-compact: " + inboundNetData);
|
282
274
|
} else {
|
283
|
-
log("Net Data post pre-reset: " + inboundNetData);
|
284
275
|
inboundNetData.clear();
|
285
|
-
log("Net Data post post-reset: " + inboundNetData);
|
286
276
|
}
|
287
277
|
|
288
278
|
ByteList appDataByteList = inboundAppData.asByteList();
|
@@ -292,54 +282,15 @@ public class MiniSSL extends RubyObject {
|
|
292
282
|
|
293
283
|
RubyString str = getRuntime().newString("");
|
294
284
|
str.setValue(appDataByteList);
|
295
|
-
|
296
|
-
logPlain("\n");
|
297
|
-
log("read(): begin dump of request data >>>>\n");
|
298
|
-
if (str.asJavaString().getBytes().length < 1000) {
|
299
|
-
logPlain(str.asJavaString() + "\n");
|
300
|
-
}
|
301
|
-
logPlain("Num bytes: " + str.asJavaString().getBytes().length + "\n");
|
302
|
-
log("read(): end dump of request data <<<<\n");
|
303
285
|
return str;
|
304
286
|
} catch (Exception e) {
|
305
|
-
if (DEBUG) {
|
306
|
-
e.printStackTrace();
|
307
|
-
}
|
308
287
|
throw getRuntime().newEOFError(e.getMessage());
|
309
288
|
}
|
310
289
|
}
|
311
290
|
|
312
|
-
private static void log(String str, SSLEngine engine, SSLEngineResult result) {
|
313
|
-
if (DEBUG) {
|
314
|
-
log(str + " " + result.getStatus() + "/" + engine.getHandshakeStatus() +
|
315
|
-
"---bytes consumed: " + result.bytesConsumed() +
|
316
|
-
", bytes produced: " + result.bytesProduced());
|
317
|
-
}
|
318
|
-
}
|
319
|
-
|
320
|
-
private static void log(String str) {
|
321
|
-
if (DEBUG) {
|
322
|
-
System.out.println("MiniSSL.java: " + str);
|
323
|
-
}
|
324
|
-
}
|
325
|
-
|
326
|
-
private static void logPlain(String str) {
|
327
|
-
if (DEBUG) {
|
328
|
-
System.out.println(str);
|
329
|
-
}
|
330
|
-
}
|
331
|
-
|
332
291
|
@JRubyMethod
|
333
292
|
public IRubyObject write(IRubyObject arg) {
|
334
293
|
try {
|
335
|
-
log("write(): begin dump of response data >>>>\n");
|
336
|
-
logPlain("\n");
|
337
|
-
if (arg.asJavaString().getBytes().length < 1000) {
|
338
|
-
logPlain(arg.asJavaString() + "\n");
|
339
|
-
}
|
340
|
-
logPlain("Num bytes: " + arg.asJavaString().getBytes().length + "\n");
|
341
|
-
log("write(): end dump of response data <<<<\n");
|
342
|
-
|
343
294
|
byte[] bls = arg.convertToString().getBytes();
|
344
295
|
outboundAppData = new MiniSSLBuffer(bls);
|
345
296
|
|
@@ -365,9 +316,7 @@ public class MiniSSL extends RubyObject {
|
|
365
316
|
}
|
366
317
|
|
367
318
|
outboundNetData.clear();
|
368
|
-
|
369
|
-
log("extract(): bytes consumed: " + res.bytesConsumed() + "\n");
|
370
|
-
log("extract(): bytes produced: " + res.bytesProduced() + "\n");
|
319
|
+
doOp(SSLOperation.WRAP, outboundAppData, outboundNetData);
|
371
320
|
dataByteList = outboundNetData.asByteList();
|
372
321
|
if (dataByteList == null) {
|
373
322
|
return getRuntime().getNil();
|
@@ -376,8 +325,6 @@ public class MiniSSL extends RubyObject {
|
|
376
325
|
RubyString str = getRuntime().newString("");
|
377
326
|
str.setValue(dataByteList);
|
378
327
|
|
379
|
-
log("extract(): " + dataByteList.getRealSize() + " encrypted bytes for response");
|
380
|
-
|
381
328
|
return str;
|
382
329
|
} catch (Exception e) {
|
383
330
|
e.printStackTrace();
|
@@ -176,14 +176,12 @@ static VALUE find_common_field_value(const char *field, size_t flen)
|
|
176
176
|
void http_field(puma_parser* hp, const char *field, size_t flen,
|
177
177
|
const char *value, size_t vlen)
|
178
178
|
{
|
179
|
-
VALUE v = Qnil;
|
180
179
|
VALUE f = Qnil;
|
180
|
+
VALUE v;
|
181
181
|
|
182
182
|
VALIDATE_MAX_LENGTH(flen, FIELD_NAME);
|
183
183
|
VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE);
|
184
184
|
|
185
|
-
v = rb_str_new(value, vlen);
|
186
|
-
|
187
185
|
f = find_common_field_value(field, flen);
|
188
186
|
|
189
187
|
if (f == Qnil) {
|
@@ -201,7 +199,17 @@ void http_field(puma_parser* hp, const char *field, size_t flen,
|
|
201
199
|
f = rb_str_new(hp->buf, new_size);
|
202
200
|
}
|
203
201
|
|
204
|
-
|
202
|
+
/* check for duplicate header */
|
203
|
+
v = rb_hash_aref(hp->request, f);
|
204
|
+
|
205
|
+
if (v == Qnil) {
|
206
|
+
v = rb_str_new(value, vlen);
|
207
|
+
rb_hash_aset(hp->request, f, v);
|
208
|
+
} else {
|
209
|
+
/* if duplicate header, normalize to comma-separated values */
|
210
|
+
rb_str_cat2(v, ", ");
|
211
|
+
rb_str_cat(v, value, vlen);
|
212
|
+
}
|
205
213
|
}
|
206
214
|
|
207
215
|
void request_method(puma_parser* hp, const char *at, size_t length)
|