puma 3.7.1 → 3.8.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: 3bc9c71db58f1613d19d40776fec460e6887909d
4
- data.tar.gz: 21c878d86ea732127745213635b35f438b47369b
3
+ metadata.gz: 031dbe9ca0f1197eaf50d15b873064ef8e6a6e17
4
+ data.tar.gz: dc24ff6e9d693c62a860a649dfed4c201bd6458e
5
5
  SHA512:
6
- metadata.gz: f60605510ca798f5eba6baca17b95038f3932ae80a656586c5f0f40061b29fbd669d20d7409813b775105495e83acc5f8c524cd45249e2e251c88c723a5ab756
7
- data.tar.gz: 2467f31b4af42d9c8b2c483ebcc453d21e275f51233c03bf2656dc7d70d7fc1a624ffd7ea481d2b2fa9304bf012a3d43ae69bc1a54051e8576b1dbd654df6011
6
+ metadata.gz: c671408260c12b1f24a93ac0f114d03574381936e5221b8c787f4180db672d453ef6058621a8c3104728f46b094b5cbb68021965f27196d1087d33b868675ee4
7
+ data.tar.gz: c4f36e2dbe262f02aee3477203c3a39ef7529a81cdf88d62d0e73710d42daa9f292a7c2868f0a54db91aa5f27fca21a59d8f38047d6983c0af5dfa6a41f64ac4
data/History.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 3.8.0 / 2017-03-09
2
+
3
+ * bugfixes:
4
+ * Port from rack handler does not take precedence over config file in Rails 5.1.0.beta2+ and 5.0.1.rc3+ (#1234)
5
+ * The `tmp/restart.txt` plugin no longer restricts the user from running more than one server from the same folder at a time (#1226)
6
+
7
+ * features:
8
+ * Closed clients are aborted to save capacity (#1227)
9
+
10
+ * Refactors
11
+ * Bundler is no longer a dependency from tests (#1213)
12
+
1
13
  ## 3.7.1 / 2017-02-20
2
14
 
3
15
  * 2 bugfixes:
@@ -6,6 +6,7 @@ LICENSE
6
6
  Manifest.txt
7
7
  README.md
8
8
  Rakefile
9
+ Release.md
9
10
  bin/puma
10
11
  bin/puma-wild
11
12
  bin/pumactl
@@ -0,0 +1,9 @@
1
+ # Release process
2
+
3
+ Using "3.7.1" as a version string example.
4
+
5
+ 1. `be rake release_sanity VERSION=3.7.1`
6
+ 2. `be rake release VERSION=3.7.1`
7
+ 3. Switch to latest JRuby version
8
+ 4. `rake java gem`
9
+ 5. `gem push pkg/puma-3.7.1-java.gem`
@@ -47,21 +47,21 @@ module Puma
47
47
  @parser.parse! @argv
48
48
 
49
49
  if file = @argv.shift
50
- @conf.configure do |c|
51
- c.rackup file
50
+ @conf.configure do |user_config, file_config|
51
+ file_config.rackup file
52
52
  end
53
53
  end
54
54
  rescue UnsupportedOption
55
55
  exit 1
56
56
  end
57
57
 
58
- @conf.configure do |c|
58
+ @conf.configure do |user_config, file_config|
59
59
  if @stdout || @stderr
60
- c.stdout_redirect @stdout, @stderr, @append
60
+ user_config.stdout_redirect @stdout, @stderr, @append
61
61
  end
62
62
 
63
63
  if @control_url
64
- c.activate_control_app @control_url, @control_options
64
+ user_config.activate_control_app @control_url, @control_options
65
65
  end
66
66
  end
67
67
 
@@ -87,14 +87,14 @@ module Puma
87
87
  #
88
88
 
89
89
  def setup_options
90
- @conf = Configuration.new do |c|
90
+ @conf = Configuration.new do |user_config, file_config|
91
91
  @parser = OptionParser.new do |o|
92
92
  o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
93
- c.bind arg
93
+ user_config.bind arg
94
94
  end
95
95
 
96
96
  o.on "-C", "--config PATH", "Load PATH as a config file" do |arg|
97
- c.load arg
97
+ file_config.load arg
98
98
  end
99
99
 
100
100
  o.on "--control URL", "The bind url to use for the control server",
@@ -112,21 +112,21 @@ module Puma
112
112
  end
113
113
 
114
114
  o.on "-d", "--daemon", "Daemonize the server into the background" do
115
- c.daemonize
116
- c.quiet
115
+ user_config.daemonize
116
+ user_config.quiet
117
117
  end
118
118
 
119
119
  o.on "--debug", "Log lowlevel debugging information" do
120
- c.debug
120
+ user_config.debug
121
121
  end
122
122
 
123
123
  o.on "--dir DIR", "Change to DIR before starting" do |d|
124
- c.directory d
124
+ user_config.directory d
125
125
  end
126
126
 
127
127
  o.on "-e", "--environment ENVIRONMENT",
128
128
  "The environment to run the Rack app on (default development)" do |arg|
129
- c.environment arg
129
+ user_config.environment arg
130
130
  end
131
131
 
132
132
  o.on "-I", "--include PATH", "Specify $LOAD_PATH directories" do |arg|
@@ -135,50 +135,50 @@ module Puma
135
135
 
136
136
  o.on "-p", "--port PORT", "Define the TCP port to bind to",
137
137
  "Use -b for more advanced options" do |arg|
138
- c.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
138
+ user_config.bind "tcp://#{Configuration::DefaultTCPHost}:#{arg}"
139
139
  end
140
140
 
141
141
  o.on "--pidfile PATH", "Use PATH as a pidfile" do |arg|
142
- c.pidfile arg
142
+ user_config.pidfile arg
143
143
  end
144
144
 
145
145
  o.on "--preload", "Preload the app. Cluster mode only" do
146
- c.preload_app!
146
+ user_config.preload_app!
147
147
  end
148
148
 
149
149
  o.on "--prune-bundler", "Prune out the bundler env if possible" do
150
- c.prune_bundler
150
+ user_config.prune_bundler
151
151
  end
152
152
 
153
153
  o.on "-q", "--quiet", "Do not log requests internally (default true)" do
154
- c.quiet
154
+ user_config.quiet
155
155
  end
156
156
 
157
157
  o.on "-v", "--log-requests", "Log requests as they occur" do
158
- c.log_requests
158
+ user_config.log_requests
159
159
  end
160
160
 
161
161
  o.on "-R", "--restart-cmd CMD",
162
162
  "The puma command to run during a hot restart",
163
163
  "Default: inferred" do |cmd|
164
- c.restart_command cmd
164
+ user_config.restart_command cmd
165
165
  end
166
166
 
167
167
  o.on "-S", "--state PATH", "Where to store the state details" do |arg|
168
- c.state_path arg
168
+ user_config.state_path arg
169
169
  end
170
170
 
171
171
  o.on '-t', '--threads INT', "min:max threads to use (default 0:16)" do |arg|
172
172
  min, max = arg.split(":")
173
173
  if max
174
- c.threads min, max
174
+ user_config.threads min, max
175
175
  else
176
- c.threads min, min
176
+ user_config.threads min, min
177
177
  end
178
178
  end
179
179
 
180
180
  o.on "--tcp-mode", "Run the app in raw TCP mode instead of HTTP mode" do
181
- c.tcp_mode!
181
+ user_config.tcp_mode!
182
182
  end
183
183
 
184
184
  o.on "-V", "--version", "Print the version information" do
@@ -188,11 +188,11 @@ module Puma
188
188
 
189
189
  o.on "-w", "--workers COUNT",
190
190
  "Activate cluster mode: How many worker processes to create" do |arg|
191
- c.workers arg
191
+ user_config.workers arg
192
192
  end
193
193
 
194
194
  o.on "--tag NAME", "Additional text to display in process listing" do |arg|
195
- c.tag arg
195
+ user_config.tag arg
196
196
  end
197
197
 
198
198
  o.on "--redirect-stdout FILE", "Redirect STDOUT to a specific file" do |arg|
@@ -216,6 +216,13 @@ module Puma
216
216
  end
217
217
 
218
218
  def setup_body
219
+ if @env[HTTP_EXPECT] == CONTINUE
220
+ # TODO allow a hook here to check the headers before
221
+ # going forward
222
+ @io << HTTP_11_100
223
+ @io.flush
224
+ end
225
+
219
226
  @read_header = false
220
227
 
221
228
  body = @parser.body
@@ -13,152 +13,147 @@ module Puma
13
13
  DefaultWorkerShutdownTimeout = 30
14
14
  end
15
15
 
16
- class LeveledOptions
17
- def initialize(default_options, user_options)
18
- @cur = user_options
19
- @set = [@cur]
20
- @defaults = default_options.dup
21
- end
22
-
23
- def initialize_copy(other)
24
- @set = @set.map { |o| o.dup }
25
- @cur = @set.last
26
- end
27
-
28
- def shift
29
- @cur = {}
30
- @set << @cur
31
- end
32
-
33
- def reverse_shift
34
- @cur = {}
35
- @set.unshift(@cur)
36
- end
16
+ # A class used for storing "leveled" configuration options.
17
+ #
18
+ # In this class any "user" specified options take precedence over any
19
+ # "file" specified options, take precedence over any "default" options.
20
+ #
21
+ # User input is prefered over "defaults":
22
+ # user_options = { foo: "bar" }
23
+ # default_options = { foo: "zoo" }
24
+ # options = UserFileDefaultOptions.new(user_options, default_options)
25
+ # puts options[:foo]
26
+ # # => "bar"
27
+ #
28
+ # All values can be accessed via `all_of`
29
+ #
30
+ # puts options.all_of(:foo)
31
+ # # => ["bar", "zoo"]
32
+ #
33
+ # A "file" option can be set. This config will be prefered over "default" options
34
+ # but will defer to any available "user" specified options.
35
+ #
36
+ # user_options = { foo: "bar" }
37
+ # default_options = { rackup: "zoo.rb" }
38
+ # options = UserFileDefaultOptions.new(user_options, default_options)
39
+ # options.file_options[:rackup] = "sup.rb"
40
+ # puts options[:rackup]
41
+ # # => "sup.rb"
42
+ #
43
+ # The "default" options can be set via procs. These are resolved during runtime
44
+ # via calls to `finalize_values`
45
+ class UserFileDefaultOptions
46
+ def initialize(user_options, default_options)
47
+ @user_options = user_options
48
+ @file_options = {}
49
+ @default_options = default_options
50
+ end
51
+
52
+ attr_reader :user_options, :file_options, :default_options
37
53
 
38
54
  def [](key)
39
- @set.reverse_each do |o|
40
- if o.key? key
41
- return o[key]
42
- end
43
- end
44
-
45
- v = @defaults[key]
46
- if v.respond_to? :call
47
- v.call
48
- else
49
- v
50
- end
55
+ return user_options[key] if user_options.key?(key)
56
+ return file_options[key] if file_options.key?(key)
57
+ return default_options[key] if default_options.key?(key)
51
58
  end
52
59
 
53
- def fetch(key, default=nil)
54
- val = self[key]
55
- return val if val
56
- default
60
+ def []=(key, value)
61
+ user_options[key] = value
57
62
  end
58
63
 
59
- attr_reader :cur
60
-
61
- def all_of(key)
62
- all = []
63
-
64
- @set.each do |o|
65
- if v = o[key]
66
- if v.kind_of? Array
67
- all += v
68
- else
69
- all << v
70
- end
71
- end
72
- end
73
-
74
- all
75
- end
76
-
77
- def []=(key, val)
78
- @cur[key] = val
64
+ def fetch(key, default_value = nil)
65
+ self[key] || default_value
79
66
  end
80
67
 
81
- def key?(key)
82
- @set.each do |o|
83
- if o.key? key
84
- return true
85
- end
86
- end
87
-
88
- @default.key? key
89
- end
90
-
91
- def merge!(o)
92
- o.each do |k,v|
93
- @cur[k]= v
94
- end
95
- end
96
-
97
- def flatten
98
- options = {}
99
-
100
- @set.each do |o|
101
- o.each do |k,v|
102
- options[k] ||= v
103
- end
104
- end
105
-
106
- options
107
- end
68
+ def all_of(key)
69
+ user = user_options[key]
70
+ file = file_options[key]
71
+ default = default_options[key]
108
72
 
109
- def explain
110
- indent = ""
73
+ user = [user] unless user.is_a?(Array)
74
+ file = [file] unless file.is_a?(Array)
75
+ default = [default] unless default.is_a?(Array)
111
76
 
112
- @set.each do |o|
113
- o.keys.sort.each do |k|
114
- puts "#{indent}#{k}: #{o[k].inspect}"
115
- end
77
+ user.compact!
78
+ file.compact!
79
+ default.compact!
116
80
 
117
- indent = " #{indent}"
118
- end
81
+ user + file + default
119
82
  end
120
83
 
121
- def force_defaults
122
- @defaults.each do |k,v|
84
+ def finalize_values
85
+ @default_options.each do |k,v|
123
86
  if v.respond_to? :call
124
- @defaults[k] = v.call
87
+ @default_options[k] = v.call
125
88
  end
126
89
  end
127
90
  end
128
91
  end
129
92
 
93
+ # The main configuration class of Puma.
94
+ #
95
+ # It can be initialized with a set of "user" options and "default" options.
96
+ # Defaults will be merged with `Configuration.puma_default_options`.
97
+ #
98
+ # This class works together with 2 main other classes the `UserFileDefaultOptions`
99
+ # which stores configuration options in order so the precedence is that user
100
+ # set configuration wins over "file" based configuration wins over "default"
101
+ # configuration. These configurations are set via the `DSL` class. This
102
+ # class powers the Puma config file syntax and does double duty as a configuration
103
+ # DSL used by the `Puma::CLI` and Puma rack handler.
104
+ #
105
+ # It also handles loading plugins.
106
+ #
107
+ # > Note: `:port` and `:host` are not valid keys. By they time they make it to the
108
+ # configuration options they are expected to be incorporated into a `:binds` key.
109
+ # Under the hood the DSL maps `port` and `host` calls to `:binds`
110
+ #
111
+ # config = Configuration.new({}) do |user_config, file_config, default_config|
112
+ # user_config.port 3003
113
+ # end
114
+ # config.load
115
+ # puts config.options[:port]
116
+ # # => 3003
117
+ #
118
+ # It is expected that `load` is called on the configuration instance after setting
119
+ # config. This method expands any values in `config_file` and puts them into the
120
+ # correct configuration option hash.
121
+ #
122
+ # Once all configuration is complete it is expected that `clamp` will be called
123
+ # on the instance. This will expand any procs stored under "default" values. This
124
+ # is done because an environment variable may have been modified while loading
125
+ # configuration files.
130
126
  class Configuration
131
127
  include ConfigDefault
132
128
 
133
- def self.from_file(path)
134
- cfg = new
135
-
136
- DSL.new(cfg.options, cfg)._load_from path
129
+ def initialize(user_options={}, default_options = {}, &block)
130
+ default_options = self.puma_default_options.merge(default_options)
137
131
 
138
- return cfg
139
- end
140
-
141
- def initialize(options={}, &blk)
142
- @options = LeveledOptions.new(default_options, options)
143
-
144
- @plugins = PluginLoader.new
132
+ @options = UserFileDefaultOptions.new(user_options, default_options)
133
+ @plugins = PluginLoader.new
134
+ @user_dsl = DSL.new(@options.user_options, self)
135
+ @file_dsl = DSL.new(@options.file_options, self)
136
+ @default_dsl = DSL.new(@options.default_options, self)
145
137
 
146
- if blk
147
- configure(&blk)
138
+ if block
139
+ configure(&block)
148
140
  end
149
141
  end
150
142
 
151
143
  attr_reader :options, :plugins
152
144
 
153
- def configure(&blk)
154
- @options.shift
155
- DSL.new(@options, self)._run(&blk)
145
+ def configure
146
+ yield @user_dsl, @file_dsl, @default_dsl
147
+ ensure
148
+ @user_dsl._offer_plugins
149
+ @file_dsl._offer_plugins
150
+ @default_dsl._offer_plugins
156
151
  end
157
152
 
158
153
  def initialize_copy(other)
159
- @conf = nil
154
+ @conf = nil
160
155
  @cli_options = nil
161
- @options = @options.dup
156
+ @options = @options.dup
162
157
  end
163
158
 
164
159
  def flatten
@@ -170,7 +165,7 @@ module Puma
170
165
  self
171
166
  end
172
167
 
173
- def default_options
168
+ def puma_default_options
174
169
  {
175
170
  :min_threads => 0,
176
171
  :max_threads => 16,
@@ -185,7 +180,7 @@ module Puma
185
180
  :worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
186
181
  :remote_address => :socket,
187
182
  :tag => method(:infer_tag),
188
- :environment => lambda { ENV['RACK_ENV'] || "development" },
183
+ :environment => ->{ ENV['RACK_ENV'] || "development" },
189
184
  :rackup => DefaultRackup,
190
185
  :logger => STDOUT,
191
186
  :persistent_timeout => Const::PERSISTENT_TIMEOUT
@@ -206,18 +201,15 @@ module Puma
206
201
  end
207
202
 
208
203
  files.each do |f|
209
- @options.reverse_shift
210
-
211
- DSL.load @options, self, f
204
+ @file_dsl._load_from(f)
212
205
  end
213
- @options.shift
206
+ @options
214
207
  end
215
208
 
216
209
  # Call once all configuration (included from rackup files)
217
210
  # is loaded to flesh out any defaults
218
211
  def clamp
219
- @options.shift
220
- @options.force_defaults
212
+ @options.finalize_values
221
213
  end
222
214
 
223
215
  # Injects the Configuration object into the env
@@ -318,17 +310,15 @@ module Puma
318
310
  def load_rackup
319
311
  raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
320
312
 
321
- @options.shift
322
-
323
313
  rack_app, rack_options = rack_builder.parse_file(rackup)
324
- @options.merge!(rack_options)
314
+ @options.file_options.merge!(rack_options)
325
315
 
326
316
  config_ru_binds = []
327
317
  rack_options.each do |k, v|
328
318
  config_ru_binds << v if k.to_s.start_with?("bind")
329
319
  end
330
320
 
331
- @options[:binds] = config_ru_binds unless config_ru_binds.empty?
321
+ @options.file_options[:binds] = config_ru_binds unless config_ru_binds.empty?
332
322
 
333
323
  rack_app
334
324
  end
@@ -95,8 +95,8 @@ module Puma
95
95
  # too taxing on performance.
96
96
  module Const
97
97
 
98
- PUMA_VERSION = VERSION = "3.7.1".freeze
99
- CODE_NAME = "Snowy Sagebrush".freeze
98
+ PUMA_VERSION = VERSION = "3.8.0".freeze
99
+ CODE_NAME = "Sassy Salamander".freeze
100
100
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
101
101
 
102
102
  FAST_TRACK_KA_TIMEOUT = 0.2
@@ -3,12 +3,12 @@ require 'puma/configuration'
3
3
 
4
4
  module Puma
5
5
  def self.run(opts={})
6
- cfg = Puma::Configuration.new do |c|
6
+ cfg = Puma::Configuration.new do |user_config|
7
7
  if port = opts[:port]
8
- c.port port
8
+ user_config.port port
9
9
  end
10
10
 
11
- c.quiet
11
+ user_config.quiet
12
12
 
13
13
  yield c
14
14
  end
@@ -1,20 +1,35 @@
1
1
  module Puma
2
2
  # The methods that are available for use inside the config file.
3
+ # These same methods are used in Puma cli and the rack handler
4
+ # internally.
3
5
  #
6
+ # Used manually (via CLI class):
7
+ #
8
+ # config = Configuration.new({}) do |user_config|
9
+ # user_config.port 3001
10
+ # end
11
+ # config.load
12
+ #
13
+ # puts config.options[:binds]
14
+ # "tcp://127.0.0.1:3001"
15
+ #
16
+ # Used to load file:
17
+ #
18
+ # $ cat puma_config.rb
19
+ # port 3002
20
+ #
21
+ # config = Configuration.new(config_file: "puma_config.rb")
22
+ # config.load
23
+ #
24
+ # puts config.options[:binds]
25
+ # # => "tcp://127.0.0.1:3002"
26
+ #
27
+ # Detailed docs can be found in `examples/config.rb`
4
28
  class DSL
5
29
  include ConfigDefault
6
30
 
7
- def self.load(options, cfg, path)
8
- d = new(options, cfg)
9
- d._load_from(path)
10
-
11
- options
12
- ensure
13
- d._offer_plugins
14
- end
15
-
16
31
  def initialize(options, config)
17
- @config = config
32
+ @config = config
18
33
  @options = options
19
34
 
20
35
  @plugins = []
@@ -40,36 +55,10 @@ module Puma
40
55
  @plugins.clear
41
56
  end
42
57
 
43
- def _run(&blk)
44
- blk.call self
45
- ensure
46
- _offer_plugins
47
- end
48
-
49
58
  def inject(&blk)
50
59
  instance_eval(&blk)
51
60
  end
52
61
 
53
- # Load configuration from another named file. If the file name is absolute,
54
- # load the file as an absolute path. Otherwise load it relative to the
55
- # current config file.
56
- #
57
- def import(file)
58
- if File.extname(file) == ""
59
- file += ".rb"
60
- end
61
-
62
- if file[0,1] == "/"
63
- path = file
64
- elsif @path
65
- path = File.join File.dirname(@path), file
66
- else
67
- raise "No original configuration path to import relative to"
68
- end
69
-
70
- DSL.new(@options, @config)._load_from(path)
71
- end
72
-
73
62
  def get(key,default=nil)
74
63
  @options[key.to_sym] || default
75
64
  end
@@ -115,15 +104,18 @@ module Puma
115
104
  end
116
105
 
117
106
  # Load additional configuration from a file
107
+ # Files get loaded later via Configuration#load
118
108
  def load(file)
119
- _ary(:config_files) << file
109
+ @options[:config_files] ||= []
110
+ @options[:config_files] << file
120
111
  end
121
112
 
122
113
  # Bind the server to +url+. tcp:// and unix:// are the only accepted
123
114
  # protocols.
124
115
  #
125
116
  def bind(url)
126
- _ary(:binds) << url
117
+ @options[:binds] ||= []
118
+ @options[:binds] << url
127
119
  end
128
120
 
129
121
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
@@ -192,7 +184,8 @@ module Puma
192
184
  # This can be called multiple times to add code each time.
193
185
  #
194
186
  def on_restart(&block)
195
- _ary(:on_restart) << block
187
+ @options[:on_restart] ||= []
188
+ @options[:on_restart] << block
196
189
  end
197
190
 
198
191
  # Command to use to restart puma. This should be just how to
@@ -297,7 +290,8 @@ module Puma
297
290
  # This can be called multiple times to add hooks.
298
291
  #
299
292
  def before_fork(&block)
300
- _ary(:before_fork) << block
293
+ @options[:before_fork] ||= []
294
+ @options[:before_fork] << block
301
295
  end
302
296
 
303
297
  # *Cluster mode only* Code to run in a worker when it boots to setup
@@ -306,7 +300,8 @@ module Puma
306
300
  # This can be called multiple times to add hooks.
307
301
  #
308
302
  def on_worker_boot(&block)
309
- _ary(:before_worker_boot) << block
303
+ @options[:before_worker_boot] ||= []
304
+ @options[:before_worker_boot] << block
310
305
  end
311
306
 
312
307
  # *Cluster mode only* Code to run immediately before a worker shuts
@@ -317,7 +312,8 @@ module Puma
317
312
  # This can be called multiple times to add hooks.
318
313
  #
319
314
  def on_worker_shutdown(&block)
320
- _ary(:before_worker_shutdown) << block
315
+ @options[:before_worker_shutdown] ||= []
316
+ @options[:before_worker_shutdown] << block
321
317
  end
322
318
 
323
319
  # *Cluster mode only* Code to run in the master when it is
@@ -326,7 +322,8 @@ module Puma
326
322
  # This can be called multiple times to add hooks.
327
323
  #
328
324
  def on_worker_fork(&block)
329
- _ary(:before_worker_fork) << block
325
+ @options[:before_worker_fork] ||= []
326
+ @options[:before_worker_fork] << block
330
327
  end
331
328
 
332
329
  # *Cluster mode only* Code to run in the master after it starts
@@ -335,7 +332,8 @@ module Puma
335
332
  # This can be called multiple times to add hooks.
336
333
  #
337
334
  def after_worker_fork(&block)
338
- _ary(:after_worker_fork) << block
335
+ @options[:after_worker_fork] ||= []
336
+ @options[:after_worker_fork] = block
339
337
  end
340
338
 
341
339
  alias_method :after_worker_boot, :after_worker_fork
@@ -477,10 +475,5 @@ module Puma
477
475
  end
478
476
  end
479
477
 
480
- private
481
-
482
- def _ary(key)
483
- (@options.cur[key] ||= [])
484
- end
485
478
  end
486
479
  end
@@ -34,9 +34,9 @@ module Puma
34
34
  #
35
35
  # Examples:
36
36
  #
37
- # conf = Puma::Configuration.new do |c|
38
- # c.threads 1, 10
39
- # c.app do |env|
37
+ # conf = Puma::Configuration.new do |user_config|
38
+ # user_config.threads 1, 10
39
+ # user_config.app do |env|
40
40
  # [200, {}, ["hello world"]]
41
41
  # end
42
42
  # end
@@ -59,6 +59,7 @@ module Puma
59
59
  @config.load
60
60
 
61
61
  @options = @config.options
62
+ @config.clamp
62
63
 
63
64
  generate_restart_data
64
65
 
@@ -8,7 +8,7 @@ Puma::Plugin.create do
8
8
 
9
9
  # If we can't write to the path, then just don't bother with this plugin
10
10
  begin
11
- File.write path, ""
11
+ File.write(path, "") unless File.exist?(path)
12
12
  orig = File.stat(path).mtime
13
13
  rescue SystemCallError
14
14
  return
@@ -100,6 +100,8 @@ module Puma
100
100
  # packetizes our stream. This improves both latency and throughput.
101
101
  #
102
102
  if RUBY_PLATFORM =~ /linux/
103
+ UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
104
+
103
105
  # 6 == Socket::IPPROTO_TCP
104
106
  # 3 == TCP_CORK
105
107
  # 1/0 == turn on/off
@@ -116,12 +118,24 @@ module Puma
116
118
  rescue IOError, SystemCallError
117
119
  end
118
120
  end
121
+
122
+ def closed_socket?(socket)
123
+ return false unless socket.kind_of? TCPSocket
124
+ tcp_info = socket.getsockopt(Socket::SOL_TCP, Socket::TCP_INFO)
125
+ state = tcp_info.unpack(UNPACK_TCP_STATE_FROM_TCP_INFO)[0]
126
+ # TIME_WAIT: 6, CLOSE: 7, CLOSE_WAIT: 8, LAST_ACK: 9, CLOSING: 11
127
+ (state >= 6 && state <= 9) || state == 11
128
+ end
119
129
  else
120
130
  def cork_socket(socket)
121
131
  end
122
132
 
123
133
  def uncork_socket(socket)
124
134
  end
135
+
136
+ def closed_socket?(socket)
137
+ false
138
+ end
125
139
  end
126
140
 
127
141
  def backlog
@@ -404,10 +418,6 @@ module Puma
404
418
  def process_client(client, buffer)
405
419
  begin
406
420
 
407
- if client.env[HTTP_EXPECT] == CONTINUE
408
- client.io << HTTP_11_100
409
- end
410
-
411
421
  clean_thread_locals = @options[:clean_thread_locals]
412
422
  close_socket = true
413
423
 
@@ -550,6 +560,8 @@ module Puma
550
560
  env = req.env
551
561
  client = req.io
552
562
 
563
+ return false if closed_socket?(client)
564
+
553
565
  normalize_env env, req
554
566
 
555
567
  env[PUMA_SOCKET] = client
@@ -8,46 +8,52 @@ module Rack
8
8
  :Silent => false
9
9
  }
10
10
 
11
- def self.run(app, options = {})
11
+ def self.config(app, options = {})
12
12
  require 'puma/configuration'
13
13
  require 'puma/events'
14
14
  require 'puma/launcher'
15
15
 
16
- options = DEFAULT_OPTIONS.merge(options)
16
+ default_options = DEFAULT_OPTIONS.dup
17
17
 
18
- conf = ::Puma::Configuration.new(options) do |c|
19
- c.quiet
18
+ # Libraries pass in values such as :Port and there is no way to determine
19
+ # if it is a default provided by the library or a special value provided
20
+ # by the user. A special key `user_supplied_options` can be passed. This
21
+ # contains an array of all explicitly defined user options. We then
22
+ # know that all other values are defaults
23
+ if user_supplied_options = options.delete(:user_supplied_options)
24
+ (options.keys - user_supplied_options).each do |k, v|
25
+ default_options[k] = options.delete(k)
26
+ end
27
+ end
28
+
29
+ conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
30
+ user_config.quiet
20
31
 
21
32
  if options.delete(:Verbose)
22
33
  app = Rack::CommonLogger.new(app, STDOUT)
23
34
  end
24
35
 
25
36
  if options[:environment]
26
- c.environment options[:environment]
37
+ user_config.environment options[:environment]
27
38
  end
28
39
 
29
40
  if options[:Threads]
30
41
  min, max = options.delete(:Threads).split(':', 2)
31
- c.threads min, max
42
+ user_config.threads min, max
32
43
  end
33
44
 
34
- host = options[:Host]
45
+ self.set_host_port_to_config(options[:Host], options[:Port], user_config)
46
+ self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
35
47
 
36
- if host && (host[0,1] == '.' || host[0,1] == '/')
37
- c.bind "unix://#{host}"
38
- elsif host && host =~ /^ssl:\/\//
39
- uri = URI.parse(host)
40
- uri.port ||= options[:Port] || ::Puma::Configuration::DefaultTCPPort
41
- c.bind uri.to_s
42
- else
43
- host ||= ::Puma::Configuration::DefaultTCPHost
44
- port = options[:Port] || ::Puma::Configuration::DefaultTCPPort
48
+ user_config.app app
49
+ end
50
+ conf
51
+ end
45
52
 
46
- c.port port, host
47
- end
48
53
 
49
- c.app app
50
- end
54
+
55
+ def self.run(app, options = {})
56
+ conf = self.config(app, options)
51
57
 
52
58
  events = options.delete(:Silent) ? ::Puma::Events.strings : ::Puma::Events.stdio
53
59
 
@@ -71,6 +77,26 @@ module Rack
71
77
  "Verbose" => "Don't report each request (default: false)"
72
78
  }
73
79
  end
80
+ private
81
+ def self.set_host_port_to_config(host, port, config)
82
+ if host && (host[0,1] == '.' || host[0,1] == '/')
83
+ config.bind "unix://#{host}"
84
+ elsif host && host =~ /^ssl:\/\//
85
+ uri = URI.parse(host)
86
+ uri.port ||= port || ::Puma::Configuration::DefaultTCPPort
87
+ config.bind uri.to_s
88
+ else
89
+
90
+ if host
91
+ port ||= ::Puma::Configuration::DefaultTCPPort
92
+ end
93
+
94
+ if port
95
+ host ||= ::Puma::Configuration::DefaultTCPHost
96
+ config.port port, host
97
+ end
98
+ end
99
+ end
74
100
  end
75
101
 
76
102
  register :puma, Puma
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.7.1
4
+ version: 3.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-20 00:00:00.000000000 Z
11
+ date: 2017-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rack
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,20 +58,6 @@ dependencies:
44
58
  - - "~>"
45
59
  - !ruby/object:Gem::Version
46
60
  version: '0.8'
47
- - !ruby/object:Gem::Dependency
48
- name: rdoc
49
- requirement: !ruby/object:Gem::Requirement
50
- requirements:
51
- - - "~>"
52
- - !ruby/object:Gem::Version
53
- version: '4.0'
54
- type: :development
55
- prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
57
- requirements:
58
- - - "~>"
59
- - !ruby/object:Gem::Version
60
- version: '4.0'
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: hoe
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -89,6 +89,7 @@ extra_rdoc_files:
89
89
  - History.md
90
90
  - Manifest.txt
91
91
  - README.md
92
+ - Release.md
92
93
  - docs/nginx.md
93
94
  - docs/signals.md
94
95
  - docs/systemd.md
@@ -104,6 +105,7 @@ files:
104
105
  - Manifest.txt
105
106
  - README.md
106
107
  - Rakefile
108
+ - Release.md
107
109
  - bin/puma
108
110
  - bin/puma-wild
109
111
  - bin/pumactl