puma 3.6.2 → 3.7.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.

@@ -1,6 +1,6 @@
1
1
  DEPLOYMENT.md
2
2
  Gemfile
3
- History.txt
3
+ History.md
4
4
  LICENSE
5
5
  Manifest.txt
6
6
  README.md
@@ -51,8 +51,6 @@ lib/puma/minissl.rb
51
51
  lib/puma/null_io.rb
52
52
  lib/puma/plugin.rb
53
53
  lib/puma/plugin/tmp_restart.rb
54
- lib/puma/rack/backports/uri/common_18.rb
55
- lib/puma/rack/backports/uri/common_192.rb
56
54
  lib/puma/rack/backports/uri/common_193.rb
57
55
  lib/puma/rack/builder.rb
58
56
  lib/puma/rack/urlmap.rb
data/README.md CHANGED
@@ -213,6 +213,12 @@ Puma comes with a builtin status/control app that can be used to query and contr
213
213
 
214
214
  This directs Puma to start the control server on localhost port 9293. Additionally, all requests to the control server will need to include `token=foo` as a query parameter. This allows for simple authentication. Check out [status.rb](https://github.com/puma/puma/blob/master/lib/puma/app/status.rb) to see what the app has available.
215
215
 
216
+ Keep in mind that the status/control server accepts `pumactl` commands. To demonstrate, you can run the following command with the foo `--control-token` as such to restart:
217
+
218
+ $ pumactl restart --control-token foo
219
+
220
+ To see a list of `pumactl` options, please see `pumactl --help` as stated in the [`pumactl`](https://github.com/puma/puma#pumactl) section.
221
+
216
222
  ### Configuration file
217
223
 
218
224
  You can also provide a configuration file which Puma will use with the `-C` (or `--config`) flag:
@@ -269,7 +275,7 @@ A detailed guide to using UNIX signals with Puma can be found in the [signals do
269
275
 
270
276
  ### Release Directory
271
277
 
272
- If you symlink releases into a common working directory (i.e., `/current` from Capistrano), Puma won't pick up your new changes when running phased restarts without additional configuration. You should set your working directory within Puma's config to specify the directory it should use. This is a change from earlier versions of Puma (< 2.15) that would infer the directory for you.
278
+ If your symlink releases into a common working directory (i.e., `/current` from Capistrano), Puma won't pick up your new changes when running phased restarts without additional configuration. You should set your working directory within Puma's config to specify the directory it should use. This is a change from earlier versions of Puma (< 2.15) that would infer the directory for you.
273
279
 
274
280
  ```ruby
275
281
  # config/puma.rb
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ require "bundler/setup"
1
2
  require "hoe"
2
3
  require "rake/extensiontask"
3
4
  require "rake/javaextensiontask"
@@ -18,9 +19,9 @@ HOE = Hoe.spec "puma" do
18
19
  spec_extras[:executables] = ['puma', 'pumactl']
19
20
  spec_extras[:homepage] = self.urls.first
20
21
 
21
- require_ruby_version ">= 1.8.7"
22
+ require_ruby_version ">= 1.9.3"
22
23
 
23
- dependency "rack", [">= 1.1", "< 2.0"], :development
24
+ dependency "rack", [">= 1.1", "< 3.0"], :development
24
25
 
25
26
  extra_dev_deps << ["rake-compiler", "~> 0.8"]
26
27
  end
@@ -66,7 +67,7 @@ task "changelog" do
66
67
  $changes[code] << line
67
68
  end
68
69
 
69
- puts "=== #{ENV['VERSION'] || 'NEXT'} / #{now}"
70
+ puts "## #{ENV['VERSION'] || 'NEXT'} / #{now}"
70
71
  puts
71
72
  changelog_section :major
72
73
  changelog_section :minor
@@ -155,4 +156,3 @@ namespace :test do
155
156
  task :all => [:test, "test:integration"]
156
157
  end
157
158
  end
158
-
@@ -168,3 +168,30 @@ Apr 07 08:40:19 hx puma[28320]: * Activated tcp://0.0.0.0:9233
168
168
  Apr 07 08:40:19 hx puma[28320]: * Activated ssl://0.0.0.0:9234?key=key.pem&cert=cert.pem
169
169
  Apr 07 08:40:19 hx puma[28320]: Use Ctrl-C to stop
170
170
  ~~~~
171
+
172
+ ## Alternative background process configuration
173
+
174
+ If Capistrano and [capistrano3-puma](https://github.com/seuros/capistrano-puma) tasks are used you can use the following configuration. In this case, you would skip systemd Socket Activation, since Puma handles the socket by itself:
175
+
176
+ ~~~~
177
+ [Service]
178
+ # Background process configuration (use with --daemon in ExecStart)
179
+ Type=forking
180
+
181
+ # To learn which exact command is to be used to execute at "ExecStart" of this
182
+ # Service, ask Capistrano: `cap <stage> puma:start --dry-run`. Your result
183
+ # may differ from this example, for example if you use a Ruby version
184
+ # manager. `<WD>` is short for "your working directory". Replace it with your
185
+ # path.
186
+ ExecStart=bundle exec puma -C <WD>/shared/puma.rb --daemon
187
+
188
+ # To learn which exact command is to be used to execute at "ExecStop" of this
189
+ # Service, ask Capistrano: `cap <stage> puma:stop --dry-run`. Your result
190
+ # may differ from this example, for example if you use a Ruby version
191
+ # manager. `<WD>` is short for "your working directory". Replace it with your
192
+ # path.
193
+ ExecStop=bundle exec pumactl -S <WD>/shared/tmp/pids/puma.state stop
194
+
195
+ # PIDFile setting is required in order to work properly
196
+ PIDFile=<WD>/shared/tmp/pids/puma.pid
197
+ ~~~~
@@ -3,6 +3,7 @@
3
3
  /**
4
4
  * Copyright (c) 2005 Zed A. Shaw
5
5
  * You can redistribute it and/or modify it under the same terms as Ruby.
6
+ * License 3-clause BSD
6
7
  */
7
8
  #include "http11_parser.h"
8
9
  #include <stdio.h>
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Copyright (c) 2005 Zed A. Shaw
3
3
  * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ * License 3-clause BSD
4
5
  */
5
6
 
6
7
  #ifndef http11_parser_h
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Copyright (c) 2005 Zed A. Shaw
3
3
  * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ * License 3-clause BSD
4
5
  */
5
6
  #include "http11_parser.h"
6
7
  #include <stdio.h>
@@ -14,8 +14,8 @@ struct buf_int {
14
14
  #define BUF_TOLERANCE 32
15
15
 
16
16
  static void buf_free(struct buf_int* internal) {
17
- free(internal->top);
18
- free(internal);
17
+ xfree(internal->top);
18
+ xfree(internal);
19
19
  }
20
20
 
21
21
  static VALUE buf_alloc(VALUE self) {
@@ -25,7 +25,7 @@ static VALUE buf_alloc(VALUE self) {
25
25
  buf = Data_Make_Struct(self, struct buf_int, 0, buf_free, internal);
26
26
 
27
27
  internal->size = BUF_DEFAULT_SIZE;
28
- internal->top = malloc(BUF_DEFAULT_SIZE);
28
+ internal->top = ALLOC_N(uint8_t, BUF_DEFAULT_SIZE);
29
29
  internal->cur = internal->top;
30
30
 
31
31
  return buf;
@@ -51,13 +51,13 @@ static VALUE buf_append(VALUE self, VALUE str) {
51
51
 
52
52
  new_size = (n > new_size ? n : new_size + BUF_TOLERANCE);
53
53
 
54
- top = malloc(new_size);
54
+ top = ALLOC_N(uint8_t, new_size);
55
55
  old = b->top;
56
56
  memcpy(top, old, used);
57
57
  b->top = top;
58
58
  b->cur = top + used;
59
59
  b->size = new_size;
60
- free(old);
60
+ xfree(old);
61
61
  }
62
62
 
63
63
  memcpy(b->cur, RSTRING_PTR(str), str_len);
@@ -92,13 +92,13 @@ static VALUE buf_append2(int argc, VALUE* argv, VALUE self) {
92
92
 
93
93
  new_size = (n > new_size ? n : new_size + BUF_TOLERANCE);
94
94
 
95
- top = malloc(new_size);
95
+ top = ALLOC_N(uint8_t, new_size);
96
96
  old = b->top;
97
97
  memcpy(top, old, used);
98
98
  b->top = top;
99
99
  b->cur = top + used;
100
100
  b->size = new_size;
101
- free(old);
101
+ xfree(old);
102
102
  }
103
103
 
104
104
  for(i = 0; i < argc; i++) {
@@ -87,6 +87,8 @@ DH *get_dh1024() {
87
87
 
88
88
  DH *dh;
89
89
  dh = DH_new();
90
+
91
+ #if OPENSSL_VERSION_NUMBER < 0x10100005L
90
92
  dh->p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
91
93
  dh->g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
92
94
 
@@ -94,6 +96,18 @@ DH *get_dh1024() {
94
96
  DH_free(dh);
95
97
  return NULL;
96
98
  }
99
+ #else
100
+ BIGNUM *p, *g;
101
+ p = BN_bin2bn(dh1024_p, sizeof(dh1024_p), NULL);
102
+ g = BN_bin2bn(dh1024_g, sizeof(dh1024_g), NULL);
103
+
104
+ if (p == NULL || g == NULL || !DH_set0_pqg(dh, p, NULL, g)) {
105
+ DH_free(dh);
106
+ BN_free(p);
107
+ BN_free(g);
108
+ return NULL;
109
+ }
110
+ #endif
97
111
 
98
112
  return dh;
99
113
  }
@@ -157,7 +171,7 @@ VALUE engine_init_server(VALUE self, VALUE mini_ssl_ctx) {
157
171
  StringValue(ca);
158
172
  SSL_CTX_load_verify_locations(ctx, RSTRING_PTR(ca), NULL);
159
173
  }
160
-
174
+
161
175
  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);
162
176
  SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
163
177
 
@@ -329,8 +343,7 @@ VALUE engine_extract(VALUE self) {
329
343
 
330
344
  VALUE engine_shutdown(VALUE self) {
331
345
  ms_conn* conn;
332
- int ok, err;
333
- char buf[512];
346
+ int ok;
334
347
 
335
348
  Data_Get_Struct(self, ms_conn, conn);
336
349
 
@@ -346,8 +359,6 @@ VALUE engine_shutdown(VALUE self) {
346
359
 
347
360
  VALUE engine_init(VALUE self) {
348
361
  ms_conn* conn;
349
- int ok, err;
350
- char buf[512];
351
362
 
352
363
  Data_Get_Struct(self, ms_conn, conn);
353
364
 
@@ -404,7 +415,7 @@ void Init_mini_ssl(VALUE puma) {
404
415
  OpenSSL_add_ssl_algorithms();
405
416
  SSL_load_error_strings();
406
417
  ERR_load_crypto_strings();
407
-
418
+
408
419
  mod = rb_define_module_under(puma, "MiniSSL");
409
420
  eng = rb_define_class_under(mod, "Engine", rb_cObject);
410
421
 
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Copyright (c) 2005 Zed A. Shaw
3
3
  * You can redistribute it and/or modify it under the same terms as Ruby.
4
+ * License 3-clause BSD
4
5
  */
5
6
 
6
7
  #define RSTRING_NOT_MODIFIED 1
@@ -1,7 +1,6 @@
1
1
  # Standard libraries
2
2
  require 'socket'
3
3
  require 'tempfile'
4
- require 'yaml'
5
4
  require 'time'
6
5
  require 'etc'
7
6
  require 'uri'
@@ -9,7 +8,8 @@ require 'stringio'
9
8
 
10
9
  require 'thread'
11
10
 
12
- # Ruby Puma
13
- require 'puma/const'
14
- require 'puma/server'
15
- require 'puma/launcher'
11
+ module Puma
12
+ autoload :Const, 'puma/const'
13
+ autoload :Server, 'puma/server'
14
+ autoload :Launcher, 'puma/launcher'
15
+ end
@@ -1,5 +1,8 @@
1
- require 'puma/const'
2
1
  require 'uri'
2
+ require 'socket'
3
+
4
+ require 'puma/const'
5
+ require 'puma/util'
3
6
 
4
7
  module Puma
5
8
  class Binder
@@ -140,11 +143,11 @@ module Puma
140
143
 
141
144
  @listeners << [str, io]
142
145
  when "ssl"
143
- MiniSSL.check
144
-
145
146
  params = Util.parse_query uri.query
146
147
  require 'puma/minissl'
147
148
 
149
+ MiniSSL.check
150
+
148
151
  ctx = MiniSSL::Context.new
149
152
 
150
153
  if defined?(JRUBY_VERSION)
@@ -1,7 +1,10 @@
1
1
  require 'optparse'
2
2
  require 'uri'
3
3
 
4
+ require 'puma/configuration'
4
5
  require 'puma/launcher'
6
+ require 'puma/const'
7
+ require 'puma/events'
5
8
 
6
9
  module Puma
7
10
  class << self
@@ -155,7 +155,8 @@ module Puma
155
155
  len = line.strip.to_i(16)
156
156
  if len == 0
157
157
  @body.rewind
158
- @buffer = nil
158
+ rest = io.read
159
+ @buffer = rest.empty? ? nil : rest
159
160
  @requests_served += 1
160
161
  @ready = true
161
162
  return true
@@ -215,14 +216,13 @@ module Puma
215
216
  end
216
217
 
217
218
  def setup_body
218
- @in_data_phase = true
219
219
  @read_header = false
220
220
 
221
221
  body = @parser.body
222
222
 
223
223
  te = @env[TRANSFER_ENCODING2]
224
224
 
225
- if te == CHUNKED
225
+ if te && CHUNKED.casecmp(te) == 0
226
226
  return setup_chunked_body(body)
227
227
  end
228
228
 
@@ -290,7 +290,7 @@ module Puma
290
290
  raise HttpParserError,
291
291
  "HEADER is longer than allowed, aborting client early."
292
292
  end
293
-
293
+
294
294
  false
295
295
  end
296
296
 
@@ -1,8 +1,13 @@
1
1
  require 'puma/runner'
2
+ require 'puma/util'
3
+ require 'puma/plugin'
4
+
2
5
  require 'time'
3
6
 
4
7
  module Puma
5
8
  class Cluster < Runner
9
+ WORKER_CHECK_INTERVAL = 5
10
+
6
11
  def initialize(cli, events)
7
12
  super cli, events
8
13
 
@@ -110,6 +115,7 @@ module Puma
110
115
 
111
116
  def spawn_workers
112
117
  diff = @options[:workers] - @workers.size
118
+ return if diff < 1
113
119
 
114
120
  master = Process.pid
115
121
 
@@ -135,6 +141,21 @@ module Puma
135
141
  end
136
142
  end
137
143
 
144
+ def cull_workers
145
+ diff = @workers.size - @options[:workers]
146
+ return if diff < 1
147
+
148
+ debug "Culling #{diff.inspect} workers"
149
+
150
+ workers_to_cull = @workers[-diff,diff]
151
+ debug "Workers to cull: #{workers_to_cull.inspect}"
152
+
153
+ workers_to_cull.each do |worker|
154
+ log "- Worker #{worker.index} (pid: #{worker.pid}) terminating"
155
+ worker.term
156
+ end
157
+ end
158
+
138
159
  def next_worker_index
139
160
  all_positions = 0...@options[:workers]
140
161
  occupied_positions = @workers.map { |w| w.index }
@@ -149,7 +170,7 @@ module Puma
149
170
  def check_workers(force=false)
150
171
  return if !force && @next_check && @next_check >= Time.now
151
172
 
152
- @next_check = Time.now + 5
173
+ @next_check = Time.now + WORKER_CHECK_INTERVAL
153
174
 
154
175
  any = false
155
176
 
@@ -175,6 +196,7 @@ module Puma
175
196
 
176
197
  @workers.delete_if(&:dead?)
177
198
 
199
+ cull_workers
178
200
  spawn_workers
179
201
 
180
202
  if all_workers_booted?
@@ -253,7 +275,7 @@ module Puma
253
275
  base_payload = "p#{Process.pid}"
254
276
 
255
277
  while true
256
- sleep 5
278
+ sleep WORKER_CHECK_INTERVAL
257
279
  begin
258
280
  b = server.backlog
259
281
  r = server.running
@@ -337,7 +359,6 @@ module Puma
337
359
 
338
360
  Signal.trap "TTOU" do
339
361
  @options[:workers] -= 1 if @options[:workers] >= 2
340
- @workers.last.term
341
362
  wakeup!
342
363
  end
343
364
 
@@ -445,7 +466,7 @@ module Puma
445
466
 
446
467
  force_check = false
447
468
 
448
- res = IO.select([read], nil, nil, 5)
469
+ res = IO.select([read], nil, nil, WORKER_CHECK_INTERVAL)
449
470
 
450
471
  if res
451
472
  req = read.read_nonblock(1)
@@ -21,6 +21,13 @@ module Puma
21
21
  # %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
22
22
  FORMAT = %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n}
23
23
 
24
+ HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
25
+
26
+ CONTENT_LENGTH = 'Content-Length'.freeze
27
+ PATH_INFO = 'PATH_INFO'.freeze
28
+ QUERY_STRING = 'QUERY_STRING'.freeze
29
+ REQUEST_METHOD = 'REQUEST_METHOD'.freeze
30
+
24
31
  def initialize(app, logger=nil)
25
32
  @app = app
26
33
  @logger = logger
@@ -42,36 +49,23 @@ module Puma
42
49
  [status, header, body]
43
50
  end
44
51
 
45
- HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
46
-
47
52
  private
48
53
 
49
54
  def log_hijacking(env, status, header, began_at)
50
55
  now = Time.now
51
56
 
52
- logger = @logger || env['rack.errors']
53
- logger.write HIJACK_FORMAT % [
57
+ msg = HIJACK_FORMAT % [
54
58
  env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
55
59
  env["REMOTE_USER"] || "-",
56
60
  now.strftime("%d/%b/%Y %H:%M:%S"),
57
- env["REQUEST_METHOD"],
58
- env["PATH_INFO"],
59
- env["QUERY_STRING"].empty? ? "" : "?"+env["QUERY_STRING"],
61
+ env[REQUEST_METHOD],
62
+ env[PATH_INFO],
63
+ env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
60
64
  env["HTTP_VERSION"],
61
65
  now - began_at ]
62
- end
63
66
 
64
- PATH_INFO = 'PATH_INFO'.freeze
65
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
66
- SCRIPT_NAME = 'SCRIPT_NAME'.freeze
67
- QUERY_STRING = 'QUERY_STRING'.freeze
68
- CACHE_CONTROL = 'Cache-Control'.freeze
69
- CONTENT_LENGTH = 'Content-Length'.freeze
70
- CONTENT_TYPE = 'Content-Type'.freeze
71
-
72
- GET = 'GET'.freeze
73
- HEAD = 'HEAD'.freeze
74
-
67
+ write(msg)
68
+ end
75
69
 
76
70
  def log(env, status, header, began_at)
77
71
  now = Time.now
@@ -83,13 +77,18 @@ module Puma
83
77
  now.strftime("%d/%b/%Y:%H:%M:%S %z"),
84
78
  env[REQUEST_METHOD],
85
79
  env[PATH_INFO],
86
- env[QUERY_STRING].empty? ? "" : "?"+env[QUERY_STRING],
80
+ env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
87
81
  env["HTTP_VERSION"],
88
82
  status.to_s[0..3],
89
83
  length,
90
84
  now - began_at ]
91
85
 
86
+ write(msg)
87
+ end
88
+
89
+ def write(msg)
92
90
  logger = @logger || env['rack.errors']
91
+
93
92
  # Standard library logger doesn't support write but it supports << which actually
94
93
  # calls to write on the log device without formatting
95
94
  if logger.respond_to?(:write)