puma 2.11.3 → 2.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 62b8ddbb9917e4501d7b02a0b84a31adac56f5be
4
- data.tar.gz: b390e97322dc3d14a51f586985c44fcc3d99b1f8
3
+ metadata.gz: b265c1a60c1ca0d8edcf9a4d90a834bd38de84ae
4
+ data.tar.gz: e6df6927092b0a4ccb22ccd25b6f637daea3bc16
5
5
  SHA512:
6
- metadata.gz: 83f84184db14ac4140e72e65704fdcc97936a4e0adba8bc4e88895819db2e5ab57626e5bd594e74912f3b79bd9c54b900dd25bf61ff60277531f17c4cfdfd7b6
7
- data.tar.gz: 015d21282805c0bca821b0381d675edb2356ff4f6975ba97df01612d2aa53476a2c1896d3434ec954ec3aa86c1a243e911333fcbfbf4b9b89c2500baa755bda7
6
+ metadata.gz: 9643ddea6fda97a21137ce6633521960a582a7bf552466272af27c27362f848366f143727da6f997bae7fa6499acf86031e7dfb73d8513d9907d413dd3429df1
7
+ data.tar.gz: 4a8b43b176ec67cdc8c7022c7b9d62d991088613eca8ae618030d4a499aba0386546245c79dc4cb31323ed9981bcc2d5c629a165a7e2c23a04c0880eed204486
@@ -76,8 +76,7 @@ puma to start running that new code. Minimizing the amount of time the server
76
76
  is unavailable would be nice as well. Here's how to do it:
77
77
 
78
78
  1. Don't use `preload!`. This dirties the master process and means it will have
79
- to shutdown all the workers and re-exec itself to get your new code, which means
80
- much higher waiting around for things to load.
79
+ to shutdown all the workers and re-exec itself to get your new code. It is not compatible with phased-restart and `prune_bundler` as well.
81
80
 
82
81
  1. Use `prune_bundler`. This makes it so that the cluster master will detach itself
83
82
  from a Bundler context on start. This allows the cluster workers to load your app
data/Gemfile CHANGED
@@ -5,9 +5,9 @@ gem "hoe-git"
5
5
  gem "hoe-ignore"
6
6
  gem "rdoc"
7
7
  gem "rake-compiler"
8
- gem "rack"
9
8
  gem "test-unit", "~> 3.0"
10
9
 
10
+ gem "rack"
11
11
  gem 'minitest', '~> 4.0'
12
12
 
13
13
  gem "jruby-openssl", :platform => "jruby"
@@ -1,3 +1,52 @@
1
+ === 2.12.0 / 2015-07-14
2
+
3
+ * X bug fixes:
4
+ * Add thread reaping to thread pool
5
+ * Do not automatically use chunked responses when hijacked
6
+ * Do not suppress Content-Length on partial hijack
7
+ * Don't allow any exceptions to terminate a thread
8
+ * Handle ENOTCONN client disconnects when setting REMOTE_ADDR
9
+ * Handle very early exit of cluster mode. Fixes #722
10
+ * Install rack when running tests on travis to use rack/lint
11
+ * Make puma -v and -h return success exit code
12
+ * Make pumactl load config/puma.rb by default
13
+ * Pass options from pumactl properly when pruning. Fixes #694
14
+ * Remove rack dependency. Fixes #705
15
+ * Remove the default Content-Type: text/plain
16
+ * Add Client Side Certificate Auth
17
+
18
+ * X doc/test changes:
19
+ * Added example sourcing of environment vars
20
+ * Added tests for bind configuration on rackup file
21
+ * Fix example config text
22
+ * Update DEPLOYMENT.md
23
+ * Update Readme with example of custom error handler
24
+ * ci: Improve Travis settings
25
+ * ci: Start running tests against JRuby 9k on Travis
26
+ * ci: Convert to container infrastructure for travisci
27
+
28
+ * X ops changes:
29
+ * Check for system-wide rbenv
30
+ * capistrano: Add additional env when start rails
31
+
32
+ * X PRs merged:
33
+ * Merge pull request #686 from jjb/patch-2
34
+ * Merge pull request #693 from rob-murray/update-example-config
35
+ * Merge pull request #697 from spk/tests-bind-on-rackup-file
36
+ * Merge pull request #699 from deees/fix/require_rack_builder
37
+ * Merge pull request #701 from deepj/master
38
+ * Merge pull request #702 from Jimdo/thread-reaping
39
+ * Merge pull request #703 from deepj/travis
40
+ * Merge pull request #704 from grega/master
41
+ * Merge pull request #709 from lian/master
42
+ * Merge pull request #711 from julik/master
43
+ * Merge pull request #712 from yakara-ltd/pumactl-default-config
44
+ * Merge pull request #715 from RobotJiang/master
45
+ * Merge pull request #725 from rwz/master
46
+ * Merge pull request #726 from strenuus/handle-client-disconnect
47
+ * Merge pull request #729 from allaire/patch-1
48
+ * Merge pull request #730 from iamjarvo/container-infrastructure
49
+
1
50
  === 2.11.3 / 2015-05-18
2
51
 
3
52
  * 5 bug fixes:
@@ -34,6 +34,7 @@ lib/puma/capistrano.rb
34
34
  lib/puma/cli.rb
35
35
  lib/puma/client.rb
36
36
  lib/puma/cluster.rb
37
+ lib/puma/commonlogger.rb
37
38
  lib/puma/compat.rb
38
39
  lib/puma/configuration.rb
39
40
  lib/puma/const.rb
@@ -48,8 +49,11 @@ lib/puma/java_io_buffer.rb
48
49
  lib/puma/jruby_restart.rb
49
50
  lib/puma/minissl.rb
50
51
  lib/puma/null_io.rb
52
+ lib/puma/rack/backports/uri/common_18.rb
53
+ lib/puma/rack/backports/uri/common_192.rb
54
+ lib/puma/rack/backports/uri/common_193.rb
55
+ lib/puma/rack/builder.rb
51
56
  lib/puma/rack_default.rb
52
- lib/puma/rack_patch.rb
53
57
  lib/puma/reactor.rb
54
58
  lib/puma/runner.rb
55
59
  lib/puma/server.rb
data/README.md CHANGED
@@ -121,6 +121,20 @@ When you use preload_app, your new code goes all in the master process, and is t
121
121
 
122
122
  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.
123
123
 
124
+ ### Error handler for low-level errors
125
+
126
+ If puma encounters an error outside of the context of your application, it will respond with a 500 and a simple
127
+ textual error message (see `lowlevel_error` in [this file](https://github.com/puma/puma/blob/master/lib/puma/server.rb)).
128
+ You can specify custom behavior for this scenario. For example, you can report the error to your third-party
129
+ error-tracking service (in this example, [rollbar](http://rollbar.com)):
130
+
131
+ ```ruby
132
+ lowlevel_error_handler do |e|
133
+ Rollbar.critical(e)
134
+ [500, {}, ["An error has occured, and engineers have been informed. Please reload the page. If you continue to have problems, contact support@example.com\n"]]
135
+ end
136
+ ```
137
+
124
138
  ### Binding TCP / Sockets
125
139
 
126
140
  In contrast to many other server configs which require multiple flags, Puma simply uses one URI parameter with the `-b` (or `--bind`) flag:
data/Rakefile CHANGED
@@ -20,7 +20,7 @@ HOE = Hoe.spec "puma" do
20
20
 
21
21
  require_ruby_version ">= 1.8.7"
22
22
 
23
- dependency "rack", [">= 1.1", "< 2.0"]
23
+ dependency "rack", [">= 1.1", "< 2.0"], :development
24
24
 
25
25
  extra_dev_deps << ["rake-compiler", "~> 0.8"]
26
26
  end
@@ -5,6 +5,7 @@
5
5
  #include <openssl/ssl.h>
6
6
  #include <openssl/dh.h>
7
7
  #include <openssl/err.h>
8
+ #include <openssl/x509.h>
8
9
 
9
10
  typedef struct {
10
11
  BIO* read;
@@ -13,7 +14,17 @@ typedef struct {
13
14
  SSL_CTX* ctx;
14
15
  } ms_conn;
15
16
 
17
+ typedef struct {
18
+ unsigned char* buf;
19
+ int bytes;
20
+ } ms_cert_buf;
21
+
16
22
  void engine_free(ms_conn* conn) {
23
+ ms_cert_buf* cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
24
+ if(cert_buf) {
25
+ OPENSSL_free(cert_buf->buf);
26
+ free(cert_buf);
27
+ }
17
28
  SSL_free(conn->ssl);
18
29
  SSL_CTX_free(conn->ctx);
19
30
 
@@ -73,6 +84,32 @@ DH *get_dh1024() {
73
84
  return dh;
74
85
  }
75
86
 
87
+ static int engine_verify_callback(int preverify_ok, X509_STORE_CTX* ctx) {
88
+ X509* err_cert;
89
+ SSL* ssl;
90
+ int bytes;
91
+ unsigned char* buf = NULL;
92
+
93
+ if(!preverify_ok) {
94
+ err_cert = X509_STORE_CTX_get_current_cert(ctx);
95
+ if(err_cert) {
96
+ /*
97
+ * Save the failed certificate for inspection/logging.
98
+ */
99
+ bytes = i2d_X509(err_cert, &buf);
100
+ if(bytes > 0) {
101
+ ms_cert_buf* cert_buf = (ms_cert_buf*)malloc(sizeof(ms_cert_buf));
102
+ cert_buf->buf = buf;
103
+ cert_buf->bytes = bytes;
104
+ ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
105
+ SSL_set_app_data(ssl, cert_buf);
106
+ }
107
+ }
108
+ }
109
+
110
+ return preverify_ok;
111
+ }
112
+
76
113
  VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
77
114
  VALUE obj;
78
115
  SSL_CTX* ctx;
@@ -86,11 +123,21 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
86
123
  ID sym_cert = rb_intern("cert");
87
124
  VALUE cert = rb_funcall(mini_ssl_ctx, sym_cert, 0);
88
125
 
126
+ ID sym_ca = rb_intern("ca");
127
+ VALUE ca = rb_funcall(mini_ssl_ctx, sym_ca, 0);
128
+
129
+ ID sym_verify_mode = rb_intern("verify_mode");
130
+ VALUE verify_mode = rb_funcall(mini_ssl_ctx, sym_verify_mode, 0);
131
+
89
132
  ctx = SSL_CTX_new(SSLv23_server_method());
90
133
  conn->ctx = ctx;
91
134
 
92
135
  SSL_CTX_use_certificate_file(ctx, RSTRING_PTR(cert), SSL_FILETYPE_PEM);
93
136
  SSL_CTX_use_PrivateKey_file(ctx, RSTRING_PTR(key), SSL_FILETYPE_PEM);
137
+
138
+ if (!NIL_P(ca)) {
139
+ SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
140
+ }
94
141
 
95
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);
96
143
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
@@ -106,10 +153,15 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
106
153
  EC_KEY_free(ecdh);
107
154
  }
108
155
 
109
- ssl = SSL_new(ctx);
156
+ ssl = SSL_new(ctx);
110
157
  conn->ssl = ssl;
158
+ SSL_set_app_data(ssl, NULL);
111
159
 
112
- /* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
160
+ if (NIL_P(verify_mode)) {
161
+ /* SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL); */
162
+ } else {
163
+ SSL_set_verify(ssl, NUM2INT(verify_mode), engine_verify_callback);
164
+ }
113
165
 
114
166
  SSL_set_bio(ssl, conn->read, conn->write);
115
167
 
@@ -123,6 +175,7 @@ VALUE engine_init_client(VALUE klass) {
123
175
 
124
176
  conn->ctx = SSL_CTX_new(DTLSv1_method());
125
177
  conn->ssl = SSL_new(conn->ctx);
178
+ SSL_set_app_data(conn->ssl, NULL);
126
179
  SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
127
180
 
128
181
  SSL_set_bio(conn->ssl, conn->read, conn->write);
@@ -151,11 +204,36 @@ VALUE engine_inject(VALUE self, VALUE str) {
151
204
  static VALUE eError;
152
205
 
153
206
  void raise_error(SSL* ssl, int result) {
154
- int error = SSL_get_error(ssl, result);
155
- char* msg = ERR_error_string(error, NULL);
207
+ char buf[512];
208
+ char msg[512];
209
+ const char* err_str;
210
+ int err = errno;
211
+ int ssl_err = SSL_get_error(ssl, result);
212
+ int verify_err = SSL_get_verify_result(ssl);
213
+
214
+ if(SSL_ERROR_SYSCALL == ssl_err) {
215
+ strerror_r(err, buf, sizeof(buf));
216
+ snprintf(msg, sizeof(msg), "System error: %s - %d", buf, err);
217
+
218
+ } else if(SSL_ERROR_SSL == ssl_err) {
219
+ if(X509_V_OK != verify_err) {
220
+ err_str = X509_verify_cert_error_string(verify_err);
221
+ snprintf(msg, sizeof(msg),
222
+ "OpenSSL certificate verification error: %s - %d",
223
+ err_str, verify_err);
224
+
225
+ } else {
226
+ err = ERR_get_error();
227
+ ERR_error_string_n(err, buf, sizeof(buf));
228
+ snprintf(msg, sizeof(msg), "OpenSSL error: %s - %d", buf, err);
229
+
230
+ }
231
+ } else {
232
+ snprintf(msg, sizeof(msg), "Unknown OpenSSL error: %d", ssl_err);
233
+ }
156
234
 
157
235
  ERR_clear_error();
158
- rb_raise(eError, "OpenSSL error: %s - %d", msg, error);
236
+ rb_raise(eError, msg);
159
237
  }
160
238
 
161
239
  VALUE engine_read(VALUE self) {
@@ -165,6 +243,8 @@ VALUE engine_read(VALUE self) {
165
243
 
166
244
  Data_Get_Struct(self, ms_conn, conn);
167
245
 
246
+ ERR_clear_error();
247
+
168
248
  bytes = SSL_read(conn->ssl, (void*)buf, sizeof(buf));
169
249
 
170
250
  if(bytes > 0) {
@@ -174,24 +254,26 @@ VALUE engine_read(VALUE self) {
174
254
  if(SSL_want_read(conn->ssl)) return Qnil;
175
255
 
176
256
  error = SSL_get_error(conn->ssl, bytes);
177
- if(error == SSL_ERROR_ZERO_RETURN || error == SSL_ERROR_SSL) {
257
+
258
+ if(error == SSL_ERROR_ZERO_RETURN) {
178
259
  rb_eof_error();
260
+ } else {
261
+ raise_error(conn->ssl, bytes);
179
262
  }
180
263
 
181
- raise_error(conn->ssl, bytes);
182
-
183
264
  return Qnil;
184
265
  }
185
266
 
186
267
  VALUE engine_write(VALUE self, VALUE str) {
187
268
  ms_conn* conn;
188
- char buf[512];
189
269
  int bytes;
190
270
 
191
271
  Data_Get_Struct(self, ms_conn, conn);
192
272
 
193
273
  StringValue(str);
194
274
 
275
+ ERR_clear_error();
276
+
195
277
  bytes = SSL_write(conn->ssl, (void*)RSTRING_PTR(str), (int)RSTRING_LEN(str));
196
278
  if(bytes > 0) {
197
279
  return INT2FIX(bytes);
@@ -225,6 +307,45 @@ VALUE engine_extract(VALUE self) {
225
307
  return Qnil;
226
308
  }
227
309
 
310
+ VALUE engine_peercert(VALUE self) {
311
+ ms_conn* conn;
312
+ X509* cert;
313
+ int bytes;
314
+ unsigned char* buf = NULL;
315
+ ms_cert_buf* cert_buf = NULL;
316
+ VALUE rb_cert_buf;
317
+
318
+ Data_Get_Struct(self, ms_conn, conn);
319
+
320
+ cert = SSL_get_peer_certificate(conn->ssl);
321
+ if(!cert) {
322
+ /*
323
+ * See if there was a failed certificate associated with this client.
324
+ */
325
+ cert_buf = (ms_cert_buf*)SSL_get_app_data(conn->ssl);
326
+ if(!cert_buf) {
327
+ return Qnil;
328
+ }
329
+ buf = cert_buf->buf;
330
+ bytes = cert_buf->bytes;
331
+
332
+ } else {
333
+ bytes = i2d_X509(cert, &buf);
334
+ X509_free(cert);
335
+
336
+ if(bytes < 0) {
337
+ return Qnil;
338
+ }
339
+ }
340
+
341
+ rb_cert_buf = rb_str_new(buf, bytes);
342
+ if(!cert_buf) {
343
+ OPENSSL_free(buf);
344
+ }
345
+
346
+ return rb_cert_buf;
347
+ }
348
+
228
349
  void Init_mini_ssl(VALUE puma) {
229
350
  VALUE mod, eng;
230
351
 
@@ -246,4 +367,6 @@ void Init_mini_ssl(VALUE puma) {
246
367
 
247
368
  rb_define_method(eng, "write", engine_write, 1);
248
369
  rb_define_method(eng, "extract", engine_extract, 0);
370
+
371
+ rb_define_method(eng, "peercert", engine_peercert, 0);
249
372
  }
@@ -4,6 +4,8 @@ module Puma
4
4
  class Binder
5
5
  include Puma::Const
6
6
 
7
+ RACK_VERSION = [1,3].freeze
8
+
7
9
  def initialize(events)
8
10
  @events = events
9
11
  @listeners = []
@@ -11,17 +13,17 @@ module Puma
11
13
  @unix_paths = []
12
14
 
13
15
  @proto_env = {
14
- "rack.version".freeze => Rack::VERSION,
16
+ "rack.version".freeze => RACK_VERSION,
15
17
  "rack.errors".freeze => events.stderr,
16
18
  "rack.multithread".freeze => true,
17
19
  "rack.multiprocess".freeze => false,
18
20
  "rack.run_once".freeze => false,
19
21
  "SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
20
22
 
21
- # Rack blows up if this is an empty string, and Rack::Lint
22
- # blows up if it's nil. So 'text/plain' seems like the most
23
- # sensible default value.
24
- "CONTENT_TYPE".freeze => "text/plain",
23
+ # I'd like to set a default CONTENT_TYPE here but some things
24
+ # depend on their not being a default set and infering
25
+ # it from the content. And so if i set it here, it won't
26
+ # infer properly.
25
27
 
26
28
  "QUERY_STRING".freeze => "",
27
29
  SERVER_PROTOCOL => HTTP_11,
@@ -87,7 +89,7 @@ module Puma
87
89
  logger.log "* Inherited #{str}"
88
90
  io = inherit_tcp_listener uri.host, uri.port, fd
89
91
  else
90
- params = Rack::Utils.parse_query uri.query
92
+ params = Util.parse_query uri.query
91
93
 
92
94
  opt = params.key?('low_latency')
93
95
  bak = params.fetch('backlog', 1024).to_i
@@ -110,7 +112,7 @@ module Puma
110
112
  mode = nil
111
113
 
112
114
  if uri.query
113
- params = Rack::Utils.parse_query uri.query
115
+ params = Util.parse_query uri.query
114
116
  if u = params['umask']
115
117
  # Use Integer() to respect the 0 prefix as octal
116
118
  umask = Integer(u)
@@ -126,7 +128,7 @@ module Puma
126
128
 
127
129
  @listeners << [str, io]
128
130
  when "ssl"
129
- params = Rack::Utils.parse_query uri.query
131
+ params = Util.parse_query uri.query
130
132
  require 'puma/minissl'
131
133
 
132
134
  ctx = MiniSSL::Context.new
@@ -155,9 +157,28 @@ module Puma
155
157
  end
156
158
 
157
159
  ctx.cert = params['cert']
158
- end
159
160
 
160
- ctx.verify_mode = MiniSSL::VERIFY_NONE
161
+ if ['peer', 'force_peer'].include?(params['verify_mode'])
162
+ unless params['ca']
163
+ @events.error "Please specify the SSL ca via 'ca='"
164
+ end
165
+ ctx.ca = params['ca']
166
+ end
167
+
168
+ if params['verify_mode']
169
+ ctx.verify_mode = case params['verify_mode']
170
+ when "peer"
171
+ MiniSSL::VERIFY_PEER
172
+ when "force_peer"
173
+ MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
174
+ when "none"
175
+ MiniSSL::VERIFY_NONE
176
+ else
177
+ @events.error "Please specify a valid verify_mode="
178
+ MiniSSL::VERIFY_NONE
179
+ end
180
+ end
181
+ end
161
182
 
162
183
  if fd = @inherited_fds.delete(str)
163
184
  logger.log "* Inherited #{str}"