puma 5.6.4 → 6.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.
- checksums.yaml +4 -4
- data/History.md +275 -4
- data/LICENSE +0 -0
- data/README.md +60 -20
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +0 -0
- data/docs/compile_options.md +34 -0
- data/docs/deployment.md +0 -0
- data/docs/fork_worker.md +1 -3
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +0 -0
- data/docs/jungle/rc.d/README.md +0 -0
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +0 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +0 -0
- data/docs/rails_dev_mode.md +0 -0
- data/docs/restart.md +0 -0
- data/docs/signals.md +0 -0
- data/docs/stats.md +0 -0
- data/docs/systemd.md +1 -2
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +18 -10
- data/ext/puma_http11/http11_parser.c +1 -1
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +2 -2
- data/ext/puma_http11/http11_parser.rl +2 -2
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +93 -26
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +166 -65
- data/ext/puma_http11/puma_http11.c +17 -9
- data/lib/puma/app/status.rb +7 -4
- data/lib/puma/binder.rb +49 -52
- data/lib/puma/cli.rb +12 -18
- data/lib/puma/client.rb +69 -23
- data/lib/puma/cluster/worker.rb +18 -11
- data/lib/puma/cluster/worker_handle.rb +4 -1
- data/lib/puma/cluster.rb +33 -30
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +76 -58
- data/lib/puma/const.rb +129 -92
- data/lib/puma/control_cli.rb +21 -18
- data/lib/puma/detect.rb +4 -0
- data/lib/puma/dsl.rb +187 -49
- data/lib/puma/error_logger.rb +18 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +39 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/json_serialization.rb +0 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +113 -175
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +24 -12
- data/lib/puma/minissl.rb +108 -15
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +0 -0
- data/lib/puma/rack/builder.rb +6 -6
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +365 -166
- data/lib/puma/runner.rb +52 -20
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +73 -73
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +2 -4
- data/lib/puma/thread_pool.rb +23 -19
- data/lib/puma/util.rb +12 -14
- data/lib/puma.rb +12 -11
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +0 -0
- data/tools/trickletest.rb +0 -0
- metadata +10 -5
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
data/lib/puma/runner.rb
CHANGED
@@ -1,23 +1,29 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative 'server'
|
4
|
+
require_relative 'const'
|
5
5
|
|
6
6
|
module Puma
|
7
7
|
# Generic class that is used by `Puma::Cluster` and `Puma::Single` to
|
8
8
|
# serve requests. This class spawns a new instance of `Puma::Server` via
|
9
9
|
# a call to `start_server`.
|
10
10
|
class Runner
|
11
|
-
def initialize(
|
12
|
-
@launcher =
|
13
|
-
@
|
14
|
-
@
|
11
|
+
def initialize(launcher)
|
12
|
+
@launcher = launcher
|
13
|
+
@log_writer = launcher.log_writer
|
14
|
+
@events = launcher.events
|
15
|
+
@config = launcher.config
|
16
|
+
@options = launcher.options
|
15
17
|
@app = nil
|
16
18
|
@control = nil
|
17
19
|
@started_at = Time.now
|
18
20
|
@wakeup = nil
|
19
21
|
end
|
20
22
|
|
23
|
+
# Returns the hash of configuration options.
|
24
|
+
# @return [Puma::UserFileDefaultOptions]
|
25
|
+
attr_reader :options
|
26
|
+
|
21
27
|
def wakeup!
|
22
28
|
return unless @wakeup
|
23
29
|
|
@@ -36,27 +42,27 @@ module Puma
|
|
36
42
|
end
|
37
43
|
|
38
44
|
def log(str)
|
39
|
-
@
|
45
|
+
@log_writer.log str
|
40
46
|
end
|
41
47
|
|
42
48
|
# @version 5.0.0
|
43
49
|
def stop_control
|
44
|
-
@control
|
50
|
+
@control&.stop true
|
45
51
|
end
|
46
52
|
|
47
53
|
def error(str)
|
48
|
-
@
|
54
|
+
@log_writer.error str
|
49
55
|
end
|
50
56
|
|
51
57
|
def debug(str)
|
52
|
-
@
|
58
|
+
@log_writer.log "- #{str}" if @options[:debug]
|
53
59
|
end
|
54
60
|
|
55
61
|
def start_control
|
56
62
|
str = @options[:control_url]
|
57
63
|
return unless str
|
58
64
|
|
59
|
-
|
65
|
+
require_relative 'app/status'
|
60
66
|
|
61
67
|
if token = @options[:control_auth_token]
|
62
68
|
token = nil if token.empty? || token == 'none'
|
@@ -64,10 +70,12 @@ module Puma
|
|
64
70
|
|
65
71
|
app = Puma::App::Status.new @launcher, token
|
66
72
|
|
67
|
-
|
68
|
-
|
73
|
+
# A Reactor is not created aand nio4r is not loaded when 'queue_requests: false'
|
74
|
+
# Use `nil` for events, no hooks in control server
|
75
|
+
control = Puma::Server.new app, nil,
|
76
|
+
{ min_threads: 0, max_threads: 1, queue_requests: false, log_writer: @log_writer }
|
69
77
|
|
70
|
-
control.binder.parse [str],
|
78
|
+
control.binder.parse [str], nil, 'Starting control server'
|
71
79
|
|
72
80
|
control.run thread_name: 'ctl'
|
73
81
|
@control = control
|
@@ -141,29 +149,29 @@ module Puma
|
|
141
149
|
end
|
142
150
|
|
143
151
|
def load_and_bind
|
144
|
-
unless @
|
152
|
+
unless @config.app_configured?
|
145
153
|
error "No application configured, nothing to run"
|
146
154
|
exit 1
|
147
155
|
end
|
148
156
|
|
149
157
|
begin
|
150
|
-
@app = @
|
158
|
+
@app = @config.app
|
151
159
|
rescue Exception => e
|
152
160
|
log "! Unable to load application: #{e.class}: #{e.message}"
|
153
161
|
raise e
|
154
162
|
end
|
155
163
|
|
156
|
-
@launcher.binder.parse @options[:binds]
|
164
|
+
@launcher.binder.parse @options[:binds]
|
157
165
|
end
|
158
166
|
|
159
167
|
# @!attribute [r] app
|
160
168
|
def app
|
161
|
-
@app ||= @
|
169
|
+
@app ||= @config.app
|
162
170
|
end
|
163
171
|
|
164
172
|
def start_server
|
165
|
-
server = Puma::Server.new
|
166
|
-
server.inherit_binder
|
173
|
+
server = Puma::Server.new(app, @events, @options)
|
174
|
+
server.inherit_binder(@launcher.binder)
|
167
175
|
server
|
168
176
|
end
|
169
177
|
|
@@ -173,5 +181,29 @@ module Puma
|
|
173
181
|
raise "Cannot redirect #{io_name} to #{path}"
|
174
182
|
end
|
175
183
|
end
|
184
|
+
|
185
|
+
def utc_iso8601(val)
|
186
|
+
"#{val.utc.strftime '%FT%T'}Z"
|
187
|
+
end
|
188
|
+
|
189
|
+
def stats
|
190
|
+
{
|
191
|
+
versions: {
|
192
|
+
puma: Puma::Const::PUMA_VERSION,
|
193
|
+
ruby: {
|
194
|
+
engine: RUBY_ENGINE,
|
195
|
+
version: RUBY_VERSION,
|
196
|
+
patchlevel: RUBY_PATCHLEVEL
|
197
|
+
}
|
198
|
+
}
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
# this method call should always be guarded by `@log_writer.debug?`
|
203
|
+
def debug_loaded_extensions(str)
|
204
|
+
@log_writer.debug "────────────────────────────────── #{str}"
|
205
|
+
re_ext = /\.#{RbConfig::CONFIG['DLEXT']}\z/i
|
206
|
+
$LOADED_FEATURES.grep(re_ext).each { |f| @log_writer.debug(" #{f}") }
|
207
|
+
end
|
176
208
|
end
|
177
209
|
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "socket"
|
4
|
+
|
5
|
+
module Puma
|
6
|
+
# The MIT License
|
7
|
+
#
|
8
|
+
# Copyright (c) 2017-2022 Agis Anastasopoulos
|
9
|
+
#
|
10
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
11
|
+
# this software and associated documentation files (the "Software"), to deal in
|
12
|
+
# the Software without restriction, including without limitation the rights to
|
13
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
14
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
15
|
+
# subject to the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be included in all
|
18
|
+
# copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
23
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
24
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
25
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
#
|
27
|
+
# This is a copy of https://github.com/agis/ruby-sdnotify as of commit cca575c
|
28
|
+
# The only changes made was "rehoming" it within the Puma module to avoid
|
29
|
+
# namespace collisions and applying standard's code formatting style.
|
30
|
+
#
|
31
|
+
# SdNotify is a pure-Ruby implementation of sd_notify(3). It can be used to
|
32
|
+
# notify systemd about state changes. Methods of this package are no-op on
|
33
|
+
# non-systemd systems (eg. Darwin).
|
34
|
+
#
|
35
|
+
# The API maps closely to the original implementation of sd_notify(3),
|
36
|
+
# therefore be sure to check the official man pages prior to using SdNotify.
|
37
|
+
#
|
38
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
39
|
+
module SdNotify
|
40
|
+
# Exception raised when there's an error writing to the notification socket
|
41
|
+
class NotifyError < RuntimeError; end
|
42
|
+
|
43
|
+
READY = "READY=1"
|
44
|
+
RELOADING = "RELOADING=1"
|
45
|
+
STOPPING = "STOPPING=1"
|
46
|
+
STATUS = "STATUS="
|
47
|
+
ERRNO = "ERRNO="
|
48
|
+
MAINPID = "MAINPID="
|
49
|
+
WATCHDOG = "WATCHDOG=1"
|
50
|
+
FDSTORE = "FDSTORE=1"
|
51
|
+
|
52
|
+
def self.ready(unset_env=false)
|
53
|
+
notify(READY, unset_env)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.reloading(unset_env=false)
|
57
|
+
notify(RELOADING, unset_env)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.stopping(unset_env=false)
|
61
|
+
notify(STOPPING, unset_env)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param status [String] a custom status string that describes the current
|
65
|
+
# state of the service
|
66
|
+
def self.status(status, unset_env=false)
|
67
|
+
notify("#{STATUS}#{status}", unset_env)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param errno [Integer]
|
71
|
+
def self.errno(errno, unset_env=false)
|
72
|
+
notify("#{ERRNO}#{errno}", unset_env)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param pid [Integer]
|
76
|
+
def self.mainpid(pid, unset_env=false)
|
77
|
+
notify("#{MAINPID}#{pid}", unset_env)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.watchdog(unset_env=false)
|
81
|
+
notify(WATCHDOG, unset_env)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.fdstore(unset_env=false)
|
85
|
+
notify(FDSTORE, unset_env)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param [Boolean] true if the service manager expects watchdog keep-alive
|
89
|
+
# notification messages to be sent from this process.
|
90
|
+
#
|
91
|
+
# If the $WATCHDOG_USEC environment variable is set,
|
92
|
+
# and the $WATCHDOG_PID variable is unset or set to the PID of the current
|
93
|
+
# process
|
94
|
+
#
|
95
|
+
# @note Unlike sd_watchdog_enabled(3), this method does not mutate the
|
96
|
+
# environment.
|
97
|
+
def self.watchdog?
|
98
|
+
wd_usec = ENV["WATCHDOG_USEC"]
|
99
|
+
wd_pid = ENV["WATCHDOG_PID"]
|
100
|
+
|
101
|
+
return false if !wd_usec
|
102
|
+
|
103
|
+
begin
|
104
|
+
wd_usec = Integer(wd_usec)
|
105
|
+
rescue
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
|
109
|
+
return false if wd_usec <= 0
|
110
|
+
return true if !wd_pid || wd_pid == $$.to_s
|
111
|
+
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
115
|
+
# Notify systemd with the provided state, via the notification socket, if
|
116
|
+
# any.
|
117
|
+
#
|
118
|
+
# Generally this method will be used indirectly through the other methods
|
119
|
+
# of the library.
|
120
|
+
#
|
121
|
+
# @param state [String]
|
122
|
+
# @param unset_env [Boolean]
|
123
|
+
#
|
124
|
+
# @return [Fixnum, nil] the number of bytes written to the notification
|
125
|
+
# socket or nil if there was no socket to report to (eg. the program wasn't
|
126
|
+
# started by systemd)
|
127
|
+
#
|
128
|
+
# @raise [NotifyError] if there was an error communicating with the systemd
|
129
|
+
# socket
|
130
|
+
#
|
131
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
132
|
+
def self.notify(state, unset_env=false)
|
133
|
+
sock = ENV["NOTIFY_SOCKET"]
|
134
|
+
|
135
|
+
return nil if !sock
|
136
|
+
|
137
|
+
ENV.delete("NOTIFY_SOCKET") if unset_env
|
138
|
+
|
139
|
+
begin
|
140
|
+
Addrinfo.unix(sock, :DGRAM).connect do |s|
|
141
|
+
s.close_on_exec = true
|
142
|
+
s.write(state)
|
143
|
+
end
|
144
|
+
rescue StandardError => e
|
145
|
+
raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/puma/server.rb
CHANGED
@@ -2,19 +2,19 @@
|
|
2
2
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
require_relative 'thread_pool'
|
6
|
+
require_relative 'const'
|
7
|
+
require_relative 'log_writer'
|
8
|
+
require_relative 'events'
|
9
|
+
require_relative 'null_io'
|
10
|
+
require_relative 'reactor'
|
11
|
+
require_relative 'client'
|
12
|
+
require_relative 'binder'
|
13
|
+
require_relative 'util'
|
14
|
+
require_relative 'request'
|
15
15
|
|
16
16
|
require 'socket'
|
17
|
-
require 'io/wait'
|
17
|
+
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
18
18
|
require 'forwardable'
|
19
19
|
|
20
20
|
module Puma
|
@@ -30,12 +30,12 @@ module Puma
|
|
30
30
|
#
|
31
31
|
# Each `Puma::Server` will have one reactor and one thread pool.
|
32
32
|
class Server
|
33
|
-
|
34
33
|
include Puma::Const
|
35
34
|
include Request
|
36
35
|
extend Forwardable
|
37
36
|
|
38
37
|
attr_reader :thread
|
38
|
+
attr_reader :log_writer
|
39
39
|
attr_reader :events
|
40
40
|
attr_reader :min_threads, :max_threads # for #stats
|
41
41
|
attr_reader :requests_count # @version 5.0.0
|
@@ -45,23 +45,19 @@ module Puma
|
|
45
45
|
:leak_stack_on_error,
|
46
46
|
:persistent_timeout, :reaping_time
|
47
47
|
|
48
|
-
# @deprecated v6.0.0
|
49
|
-
attr_writer :auto_trim_time, :early_hints, :first_data_timeout,
|
50
|
-
:leak_stack_on_error, :min_threads, :max_threads,
|
51
|
-
:persistent_timeout, :reaping_time
|
52
|
-
|
53
48
|
attr_accessor :app
|
54
49
|
attr_accessor :binder
|
55
50
|
|
56
51
|
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener,
|
57
52
|
:add_unix_listener, :connected_ports
|
58
53
|
|
59
|
-
|
54
|
+
THREAD_LOCAL_KEY = :puma_server
|
60
55
|
|
61
56
|
# Create a server for the rack app +app+.
|
62
57
|
#
|
63
|
-
# +
|
64
|
-
#
|
58
|
+
# +log_writer+ is a Puma::LogWriter object used to log info and error messages.
|
59
|
+
#
|
60
|
+
# +events+ is a Puma::Events object used to notify application status events.
|
65
61
|
#
|
66
62
|
# Server#run returns a thread that you can join on to wait for the server
|
67
63
|
# to do its work.
|
@@ -70,34 +66,53 @@ module Puma
|
|
70
66
|
# and have default values set via +fetch+. Normally the values are set via
|
71
67
|
# `::Puma::Configuration.puma_default_options`.
|
72
68
|
#
|
73
|
-
|
69
|
+
# @note The `events` parameter is set to nil, and set to `Events.new` in code.
|
70
|
+
# Often `options` needs to be passed, but `events` does not. Using nil allows
|
71
|
+
# calling code to not require events.rb.
|
72
|
+
#
|
73
|
+
def initialize(app, events = nil, options = {})
|
74
74
|
@app = app
|
75
|
-
@events = events
|
75
|
+
@events = events || Events.new
|
76
76
|
|
77
77
|
@check, @notify = nil
|
78
78
|
@status = :stop
|
79
79
|
|
80
|
-
@auto_trim_time = 30
|
81
|
-
@reaping_time = 1
|
82
|
-
|
83
80
|
@thread = nil
|
84
81
|
@thread_pool = nil
|
85
82
|
|
86
|
-
@options = options
|
83
|
+
@options = if options.is_a?(UserFileDefaultOptions)
|
84
|
+
options
|
85
|
+
else
|
86
|
+
UserFileDefaultOptions.new(options, Configuration::DEFAULTS)
|
87
|
+
end
|
87
88
|
|
88
|
-
@
|
89
|
-
@
|
90
|
-
@
|
91
|
-
@
|
92
|
-
@
|
93
|
-
@
|
94
|
-
@
|
95
|
-
@
|
89
|
+
@log_writer = @options.fetch :log_writer, LogWriter.stdio
|
90
|
+
@early_hints = @options[:early_hints]
|
91
|
+
@first_data_timeout = @options[:first_data_timeout]
|
92
|
+
@min_threads = @options[:min_threads]
|
93
|
+
@max_threads = @options[:max_threads]
|
94
|
+
@persistent_timeout = @options[:persistent_timeout]
|
95
|
+
@queue_requests = @options[:queue_requests]
|
96
|
+
@max_fast_inline = @options[:max_fast_inline]
|
97
|
+
@io_selector_backend = @options[:io_selector_backend]
|
98
|
+
@http_content_length_limit = @options[:http_content_length_limit]
|
99
|
+
|
100
|
+
# make this a hash, since we prefer `key?` over `include?`
|
101
|
+
@supported_http_methods =
|
102
|
+
if @options[:supported_http_methods] == :any
|
103
|
+
:any
|
104
|
+
else
|
105
|
+
if (ary = @options[:supported_http_methods])
|
106
|
+
ary
|
107
|
+
else
|
108
|
+
SUPPORTED_HTTP_METHODS
|
109
|
+
end.sort.product([nil]).to_h.freeze
|
110
|
+
end
|
96
111
|
|
97
112
|
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
98
113
|
@leak_stack_on_error = @options[:environment] ? temp : true
|
99
114
|
|
100
|
-
@binder = Binder.new(
|
115
|
+
@binder = Binder.new(log_writer)
|
101
116
|
|
102
117
|
ENV['RACK_ENV'] ||= "development"
|
103
118
|
|
@@ -115,7 +130,7 @@ module Puma
|
|
115
130
|
class << self
|
116
131
|
# @!attribute [r] current
|
117
132
|
def current
|
118
|
-
Thread.current[
|
133
|
+
Thread.current[THREAD_LOCAL_KEY]
|
119
134
|
end
|
120
135
|
|
121
136
|
# :nodoc:
|
@@ -193,12 +208,12 @@ module Puma
|
|
193
208
|
|
194
209
|
# @!attribute [r] backlog
|
195
210
|
def backlog
|
196
|
-
@thread_pool
|
211
|
+
@thread_pool&.backlog
|
197
212
|
end
|
198
213
|
|
199
214
|
# @!attribute [r] running
|
200
215
|
def running
|
201
|
-
@thread_pool
|
216
|
+
@thread_pool&.spawned
|
202
217
|
end
|
203
218
|
|
204
219
|
|
@@ -211,7 +226,7 @@ module Puma
|
|
211
226
|
# value would be 4 until it finishes processing.
|
212
227
|
# @!attribute [r] pool_capacity
|
213
228
|
def pool_capacity
|
214
|
-
@thread_pool
|
229
|
+
@thread_pool&.pool_capacity
|
215
230
|
end
|
216
231
|
|
217
232
|
# Runs the server.
|
@@ -227,29 +242,16 @@ module Puma
|
|
227
242
|
|
228
243
|
@status = :run
|
229
244
|
|
230
|
-
@thread_pool = ThreadPool.new(
|
231
|
-
thread_name,
|
232
|
-
@min_threads,
|
233
|
-
@max_threads,
|
234
|
-
::Puma::IOBuffer,
|
235
|
-
&method(:process_client)
|
236
|
-
)
|
237
|
-
|
238
|
-
@thread_pool.out_of_band_hook = @options[:out_of_band]
|
239
|
-
@thread_pool.clean_thread_locals = @options[:clean_thread_locals]
|
245
|
+
@thread_pool = ThreadPool.new(thread_name, @options) { |client| process_client client }
|
240
246
|
|
241
247
|
if @queue_requests
|
242
|
-
@reactor = Reactor.new(@io_selector_backend
|
248
|
+
@reactor = Reactor.new(@io_selector_backend) { |c| reactor_wakeup c }
|
243
249
|
@reactor.run
|
244
250
|
end
|
245
251
|
|
246
|
-
if @reaping_time
|
247
|
-
@thread_pool.auto_reap!(@reaping_time)
|
248
|
-
end
|
249
252
|
|
250
|
-
if @
|
251
|
-
|
252
|
-
end
|
253
|
+
@thread_pool.auto_reap! if @options[:reaping_time]
|
254
|
+
@thread_pool.auto_trim! if @options[:auto_trim_time]
|
253
255
|
|
254
256
|
@check, @notify = Puma::Util.pipe unless @notify
|
255
257
|
|
@@ -345,6 +347,7 @@ module Puma
|
|
345
347
|
drain += 1 if shutting_down?
|
346
348
|
pool << Client.new(io, @binder.env(sock)).tap { |c|
|
347
349
|
c.listener = sock
|
350
|
+
c.http_content_length_limit = @http_content_length_limit
|
348
351
|
c.send(addr_send_name, addr_value) if addr_value
|
349
352
|
}
|
350
353
|
end
|
@@ -353,11 +356,11 @@ module Puma
|
|
353
356
|
# In the case that any of the sockets are unexpectedly close.
|
354
357
|
raise
|
355
358
|
rescue StandardError => e
|
356
|
-
@
|
359
|
+
@log_writer.unknown_error e, nil, "Listen loop"
|
357
360
|
end
|
358
361
|
end
|
359
362
|
|
360
|
-
@
|
363
|
+
@log_writer.debug "Drained #{drain} additional connections." if drain
|
361
364
|
@events.fire :state, @status
|
362
365
|
|
363
366
|
if queue_requests
|
@@ -366,14 +369,13 @@ module Puma
|
|
366
369
|
end
|
367
370
|
graceful_shutdown if @status == :stop || @status == :restart
|
368
371
|
rescue Exception => e
|
369
|
-
@
|
372
|
+
@log_writer.unknown_error e, nil, "Exception handling servers"
|
370
373
|
ensure
|
371
|
-
# RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
|
372
374
|
# Errno::EBADF is infrequently raised
|
373
375
|
[@check, @notify].each do |io|
|
374
376
|
begin
|
375
377
|
io.close unless io.closed?
|
376
|
-
rescue Errno::EBADF
|
378
|
+
rescue Errno::EBADF
|
377
379
|
end
|
378
380
|
end
|
379
381
|
@notify = nil
|
@@ -412,9 +414,9 @@ module Puma
|
|
412
414
|
# returning.
|
413
415
|
#
|
414
416
|
# Return true if one or more requests were processed.
|
415
|
-
def process_client(client
|
417
|
+
def process_client(client)
|
416
418
|
# Advertise this server into the thread
|
417
|
-
Thread.current[
|
419
|
+
Thread.current[THREAD_LOCAL_KEY] = self
|
418
420
|
|
419
421
|
clean_thread_locals = @options[:clean_thread_locals]
|
420
422
|
close_socket = true
|
@@ -438,15 +440,13 @@ module Puma
|
|
438
440
|
|
439
441
|
while true
|
440
442
|
@requests_count += 1
|
441
|
-
case handle_request(client,
|
443
|
+
case handle_request(client, requests + 1)
|
442
444
|
when false
|
443
445
|
break
|
444
446
|
when :async
|
445
447
|
close_socket = false
|
446
448
|
break
|
447
449
|
when true
|
448
|
-
buffer.reset
|
449
|
-
|
450
450
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
451
451
|
|
452
452
|
requests += 1
|
@@ -480,7 +480,7 @@ module Puma
|
|
480
480
|
# The ensure tries to close +client+ down
|
481
481
|
requests > 0
|
482
482
|
ensure
|
483
|
-
|
483
|
+
client.io_buffer.reset
|
484
484
|
|
485
485
|
begin
|
486
486
|
client.close if close_socket
|
@@ -488,7 +488,7 @@ module Puma
|
|
488
488
|
Puma::Util.purge_interrupt_queue
|
489
489
|
# Already closed
|
490
490
|
rescue StandardError => e
|
491
|
-
@
|
491
|
+
@log_writer.unknown_error e, nil, "Client"
|
492
492
|
end
|
493
493
|
end
|
494
494
|
end
|
@@ -511,16 +511,16 @@ module Puma
|
|
511
511
|
lowlevel_error(e, client.env)
|
512
512
|
case e
|
513
513
|
when MiniSSL::SSLError
|
514
|
-
@
|
514
|
+
@log_writer.ssl_error e, client.io
|
515
515
|
when HttpParserError
|
516
516
|
client.write_error(400)
|
517
|
-
@
|
517
|
+
@log_writer.parse_error e, client
|
518
518
|
when HttpParserError501
|
519
519
|
client.write_error(501)
|
520
|
-
@
|
520
|
+
@log_writer.parse_error e, client
|
521
521
|
else
|
522
522
|
client.write_error(500)
|
523
|
-
@
|
523
|
+
@log_writer.unknown_error e, nil, "Read"
|
524
524
|
end
|
525
525
|
end
|
526
526
|
|
@@ -578,7 +578,7 @@ module Puma
|
|
578
578
|
|
579
579
|
def notify_safely(message)
|
580
580
|
@notify << message
|
581
|
-
rescue IOError, NoMethodError, Errno::EPIPE
|
581
|
+
rescue IOError, NoMethodError, Errno::EPIPE, Errno::EBADF
|
582
582
|
# The server, in another thread, is shutting down
|
583
583
|
Puma::Util.purge_interrupt_queue
|
584
584
|
rescue RuntimeError => e
|
data/lib/puma/single.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require_relative 'runner'
|
4
|
+
require_relative 'detect'
|
5
|
+
require_relative 'plugin'
|
6
6
|
|
7
7
|
module Puma
|
8
8
|
# This class is instantiated by the `Puma::Launcher` and used
|
@@ -16,26 +16,26 @@ module Puma
|
|
16
16
|
# @!attribute [r] stats
|
17
17
|
def stats
|
18
18
|
{
|
19
|
-
started_at: @started_at
|
20
|
-
}.merge(@server.stats)
|
19
|
+
started_at: utc_iso8601(@started_at)
|
20
|
+
}.merge(@server.stats).merge(super)
|
21
21
|
end
|
22
22
|
|
23
23
|
def restart
|
24
|
-
@server
|
24
|
+
@server&.begin_restart
|
25
25
|
end
|
26
26
|
|
27
27
|
def stop
|
28
|
-
@server
|
28
|
+
@server&.stop false
|
29
29
|
end
|
30
30
|
|
31
31
|
def halt
|
32
|
-
@server
|
32
|
+
@server&.halt
|
33
33
|
end
|
34
34
|
|
35
35
|
def stop_blocked
|
36
36
|
log "- Gracefully stopping, waiting for requests to finish"
|
37
|
-
@control
|
38
|
-
@server
|
37
|
+
@control&.stop true
|
38
|
+
@server&.stop true
|
39
39
|
end
|
40
40
|
|
41
41
|
def run
|
@@ -55,7 +55,9 @@ module Puma
|
|
55
55
|
log "Use Ctrl-C to stop"
|
56
56
|
redirect_io
|
57
57
|
|
58
|
-
@
|
58
|
+
@events.fire_on_booted!
|
59
|
+
|
60
|
+
debug_loaded_extensions("Loaded Extensions:") if @log_writer.debug?
|
59
61
|
|
60
62
|
begin
|
61
63
|
server_thread.join
|