puma 4.3.12 → 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 +1729 -521
- data/LICENSE +23 -20
- data/README.md +169 -45
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +63 -26
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +60 -69
- data/docs/fork_worker.md +31 -0
- 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 +9 -0
- data/{tools → docs}/jungle/rc.d/README.md +1 -1
- data/{tools → docs}/jungle/rc.d/puma +2 -2
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +2 -2
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +84 -128
- 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 +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +49 -12
- data/ext/puma_http11/http11_parser.c +46 -48
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +3 -3
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +2 -2
- data/ext/puma_http11/mini_ssl.c +278 -93
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +4 -6
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
- data/ext/puma_http11/puma_http11.c +46 -57
- data/lib/puma/app/status.rb +53 -39
- data/lib/puma/binder.rb +237 -121
- data/lib/puma/cli.rb +34 -34
- data/lib/puma/client.rb +172 -98
- data/lib/puma/cluster/worker.rb +180 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +226 -231
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +114 -87
- data/lib/puma/const.rb +139 -95
- data/lib/puma/control_cli.rb +99 -79
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +516 -110
- data/lib/puma/error_logger.rb +113 -0
- data/lib/puma/events.rb +16 -115
- data/lib/puma/io_buffer.rb +44 -2
- data/lib/puma/jruby_restart.rb +2 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +164 -155
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +36 -19
- data/lib/puma/minissl.rb +230 -55
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +3 -12
- data/lib/puma/rack/builder.rb +7 -11
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +93 -368
- data/lib/puma/request.rb +671 -0
- data/lib/puma/runner.rb +92 -75
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +321 -794
- data/lib/puma/single.rb +20 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/thread_pool.rb +140 -68
- data/lib/puma/util.rb +21 -4
- data/lib/puma.rb +54 -7
- data/lib/rack/handler/puma.rb +113 -87
- data/tools/{docker/Dockerfile → Dockerfile} +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +33 -24
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/const.rb
CHANGED
@@ -5,7 +5,6 @@ module Puma
|
|
5
5
|
class UnsupportedOption < RuntimeError
|
6
6
|
end
|
7
7
|
|
8
|
-
|
9
8
|
# Every standard HTTP code mapped to the appropriate message. These are
|
10
9
|
# used so frequently that they are placed directly in Puma for easy
|
11
10
|
# access rather than Puma::Const itself.
|
@@ -19,6 +18,7 @@ module Puma
|
|
19
18
|
100 => 'Continue',
|
20
19
|
101 => 'Switching Protocols',
|
21
20
|
102 => 'Processing',
|
21
|
+
103 => 'Early Hints',
|
22
22
|
200 => 'OK',
|
23
23
|
201 => 'Created',
|
24
24
|
202 => 'Accepted',
|
@@ -50,16 +50,16 @@ module Puma
|
|
50
50
|
410 => 'Gone',
|
51
51
|
411 => 'Length Required',
|
52
52
|
412 => 'Precondition Failed',
|
53
|
-
413 => '
|
53
|
+
413 => 'Content Too Large',
|
54
54
|
414 => 'URI Too Long',
|
55
55
|
415 => 'Unsupported Media Type',
|
56
56
|
416 => 'Range Not Satisfiable',
|
57
57
|
417 => 'Expectation Failed',
|
58
|
-
418 => 'I\'m A Teapot',
|
59
58
|
421 => 'Misdirected Request',
|
60
|
-
422 => 'Unprocessable
|
59
|
+
422 => 'Unprocessable Content',
|
61
60
|
423 => 'Locked',
|
62
61
|
424 => 'Failed Dependency',
|
62
|
+
425 => 'Too Early',
|
63
63
|
426 => 'Upgrade Required',
|
64
64
|
428 => 'Precondition Required',
|
65
65
|
429 => 'Too Many Requests',
|
@@ -74,7 +74,7 @@ module Puma
|
|
74
74
|
506 => 'Variant Also Negotiates',
|
75
75
|
507 => 'Insufficient Storage',
|
76
76
|
508 => 'Loop Detected',
|
77
|
-
510 => 'Not Extended',
|
77
|
+
510 => 'Not Extended (OBSOLETED)',
|
78
78
|
511 => 'Network Authentication Required'
|
79
79
|
}.freeze
|
80
80
|
|
@@ -100,54 +100,40 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "
|
104
|
-
CODE_NAME = "
|
105
|
-
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
106
|
-
|
107
|
-
FAST_TRACK_KA_TIMEOUT = 0.2
|
103
|
+
PUMA_VERSION = VERSION = "6.3.1"
|
104
|
+
CODE_NAME = "Mugi No Toki Itaru"
|
108
105
|
|
109
|
-
|
110
|
-
# session.
|
111
|
-
PERSISTENT_TIMEOUT = 20
|
106
|
+
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
112
107
|
|
113
|
-
|
114
|
-
# for the request
|
115
|
-
FIRST_DATA_TIMEOUT = 30
|
108
|
+
FAST_TRACK_KA_TIMEOUT = 0.2
|
116
109
|
|
117
110
|
# How long to wait when getting some write blocking on the socket when
|
118
111
|
# sending data back
|
119
112
|
WRITE_TIMEOUT = 10
|
120
113
|
|
121
|
-
# How many requests to attempt inline before sending a client back to
|
122
|
-
# the reactor to be subject to normal ordering. The idea here is that
|
123
|
-
# we amortize the cost of going back to the reactor for a well behaved
|
124
|
-
# but very "greedy" client across 10 requests. This prevents a not
|
125
|
-
# well behaved client from monopolizing the thread forever.
|
126
|
-
MAX_FAST_INLINE = 10
|
127
|
-
|
128
114
|
# The original URI requested by the client.
|
129
|
-
REQUEST_URI=
|
130
|
-
REQUEST_PATH =
|
131
|
-
QUERY_STRING =
|
132
|
-
CONTENT_LENGTH = "CONTENT_LENGTH"
|
115
|
+
REQUEST_URI= "REQUEST_URI"
|
116
|
+
REQUEST_PATH = "REQUEST_PATH"
|
117
|
+
QUERY_STRING = "QUERY_STRING"
|
118
|
+
CONTENT_LENGTH = "CONTENT_LENGTH"
|
133
119
|
|
134
|
-
PATH_INFO =
|
120
|
+
PATH_INFO = "PATH_INFO"
|
135
121
|
|
136
|
-
PUMA_TMP_BASE = "puma"
|
122
|
+
PUMA_TMP_BASE = "puma"
|
137
123
|
|
138
124
|
ERROR_RESPONSE = {
|
139
125
|
# Indicate that we couldn't parse the request
|
140
|
-
400 => "HTTP/1.1 400 Bad Request\r\n\r\n"
|
126
|
+
400 => "HTTP/1.1 400 Bad Request\r\n\r\n",
|
141
127
|
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
142
|
-
404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\
|
128
|
+
404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n",
|
143
129
|
# The standard empty 408 response for requests that timed out.
|
144
|
-
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\
|
130
|
+
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n",
|
145
131
|
# Indicate that there was an internal error, obviously.
|
146
|
-
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n"
|
132
|
+
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n",
|
147
133
|
# Incorrect or invalid header value
|
148
|
-
501 => "HTTP/1.1 501 Not Implemented\r\n\r\n"
|
134
|
+
501 => "HTTP/1.1 501 Not Implemented\r\n\r\n",
|
149
135
|
# A common header for indicating the server is too busy. Not used yet.
|
150
|
-
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\
|
136
|
+
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\n"
|
151
137
|
}.freeze
|
152
138
|
|
153
139
|
# The basic max request size we'll try to read.
|
@@ -160,86 +146,144 @@ module Puma
|
|
160
146
|
# Maximum request body size before it is moved out of memory and into a tempfile for reading.
|
161
147
|
MAX_BODY = MAX_HEADER
|
162
148
|
|
163
|
-
REQUEST_METHOD = "REQUEST_METHOD"
|
164
|
-
HEAD = "HEAD"
|
149
|
+
REQUEST_METHOD = "REQUEST_METHOD"
|
150
|
+
HEAD = "HEAD"
|
151
|
+
|
152
|
+
# based on https://www.rfc-editor.org/rfc/rfc9110.html#name-overview,
|
153
|
+
# with CONNECT removed, and PATCH added
|
154
|
+
SUPPORTED_HTTP_METHODS = %w[HEAD GET POST PUT DELETE OPTIONS TRACE PATCH].freeze
|
155
|
+
|
156
|
+
# list from https://www.iana.org/assignments/http-methods/http-methods.xhtml
|
157
|
+
# as of 04-May-23
|
158
|
+
IANA_HTTP_METHODS = %w[
|
159
|
+
ACL
|
160
|
+
BASELINE-CONTROL
|
161
|
+
BIND
|
162
|
+
CHECKIN
|
163
|
+
CHECKOUT
|
164
|
+
CONNECT
|
165
|
+
COPY
|
166
|
+
DELETE
|
167
|
+
GET
|
168
|
+
HEAD
|
169
|
+
LABEL
|
170
|
+
LINK
|
171
|
+
LOCK
|
172
|
+
MERGE
|
173
|
+
MKACTIVITY
|
174
|
+
MKCALENDAR
|
175
|
+
MKCOL
|
176
|
+
MKREDIRECTREF
|
177
|
+
MKWORKSPACE
|
178
|
+
MOVE
|
179
|
+
OPTIONS
|
180
|
+
ORDERPATCH
|
181
|
+
PATCH
|
182
|
+
POST
|
183
|
+
PRI
|
184
|
+
PROPFIND
|
185
|
+
PROPPATCH
|
186
|
+
PUT
|
187
|
+
REBIND
|
188
|
+
REPORT
|
189
|
+
SEARCH
|
190
|
+
TRACE
|
191
|
+
UNBIND
|
192
|
+
UNCHECKOUT
|
193
|
+
UNLINK
|
194
|
+
UNLOCK
|
195
|
+
UPDATE
|
196
|
+
UPDATEREDIRECTREF
|
197
|
+
VERSION-CONTROL
|
198
|
+
].freeze
|
199
|
+
|
165
200
|
# ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
|
166
|
-
LINE_END = "\r\n"
|
167
|
-
REMOTE_ADDR = "REMOTE_ADDR"
|
168
|
-
HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"
|
169
|
-
HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL"
|
170
|
-
HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME"
|
171
|
-
HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO"
|
201
|
+
LINE_END = "\r\n"
|
202
|
+
REMOTE_ADDR = "REMOTE_ADDR"
|
203
|
+
HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"
|
204
|
+
HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL"
|
205
|
+
HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME"
|
206
|
+
HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO"
|
207
|
+
|
208
|
+
SERVER_NAME = "SERVER_NAME"
|
209
|
+
SERVER_PORT = "SERVER_PORT"
|
210
|
+
HTTP_HOST = "HTTP_HOST"
|
211
|
+
PORT_80 = "80"
|
212
|
+
PORT_443 = "443"
|
213
|
+
LOCALHOST = "localhost"
|
214
|
+
LOCALHOST_IPV4 = "127.0.0.1"
|
215
|
+
LOCALHOST_IPV6 = "::1"
|
216
|
+
UNSPECIFIED_IPV4 = "0.0.0.0"
|
217
|
+
UNSPECIFIED_IPV6 = "::"
|
172
218
|
|
173
|
-
|
174
|
-
|
175
|
-
HTTP_HOST = "HTTP_HOST".freeze
|
176
|
-
PORT_80 = "80".freeze
|
177
|
-
PORT_443 = "443".freeze
|
178
|
-
LOCALHOST = "localhost".freeze
|
179
|
-
LOCALHOST_IP = "127.0.0.1".freeze
|
180
|
-
LOCALHOST_ADDR = "127.0.0.1:0".freeze
|
219
|
+
SERVER_PROTOCOL = "SERVER_PROTOCOL"
|
220
|
+
HTTP_11 = "HTTP/1.1"
|
181
221
|
|
182
|
-
|
183
|
-
|
222
|
+
SERVER_SOFTWARE = "SERVER_SOFTWARE"
|
223
|
+
GATEWAY_INTERFACE = "GATEWAY_INTERFACE"
|
224
|
+
CGI_VER = "CGI/1.2"
|
184
225
|
|
185
|
-
|
186
|
-
|
187
|
-
|
226
|
+
STOP_COMMAND = "?"
|
227
|
+
HALT_COMMAND = "!"
|
228
|
+
RESTART_COMMAND = "R"
|
188
229
|
|
189
|
-
|
190
|
-
|
191
|
-
|
230
|
+
RACK_INPUT = "rack.input"
|
231
|
+
RACK_URL_SCHEME = "rack.url_scheme"
|
232
|
+
RACK_AFTER_REPLY = "rack.after_reply"
|
233
|
+
PUMA_SOCKET = "puma.socket"
|
234
|
+
PUMA_CONFIG = "puma.config"
|
235
|
+
PUMA_PEERCERT = "puma.peercert"
|
192
236
|
|
193
|
-
|
194
|
-
|
195
|
-
RACK_AFTER_REPLY = "rack.after_reply".freeze
|
196
|
-
PUMA_SOCKET = "puma.socket".freeze
|
197
|
-
PUMA_CONFIG = "puma.config".freeze
|
198
|
-
PUMA_PEERCERT = "puma.peercert".freeze
|
237
|
+
HTTP = "http"
|
238
|
+
HTTPS = "https"
|
199
239
|
|
200
|
-
|
201
|
-
HTTPS = "https".freeze
|
240
|
+
HTTPS_KEY = "HTTPS"
|
202
241
|
|
203
|
-
|
242
|
+
HTTP_VERSION = "HTTP_VERSION"
|
243
|
+
HTTP_CONNECTION = "HTTP_CONNECTION"
|
244
|
+
HTTP_EXPECT = "HTTP_EXPECT"
|
245
|
+
CONTINUE = "100-continue"
|
204
246
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
CONTINUE = "100-continue".freeze
|
247
|
+
HTTP_11_100 = "HTTP/1.1 100 Continue\r\n\r\n"
|
248
|
+
HTTP_11_200 = "HTTP/1.1 200 OK\r\n"
|
249
|
+
HTTP_10_200 = "HTTP/1.0 200 OK\r\n"
|
209
250
|
|
210
|
-
|
211
|
-
|
212
|
-
HTTP_10_200 = "HTTP/1.0 200 OK\r\n".freeze
|
251
|
+
CLOSE = "close"
|
252
|
+
KEEP_ALIVE = "keep-alive"
|
213
253
|
|
214
|
-
|
215
|
-
|
254
|
+
CONTENT_LENGTH2 = "content-length"
|
255
|
+
CONTENT_LENGTH_S = "Content-Length: "
|
256
|
+
TRANSFER_ENCODING = "transfer-encoding"
|
257
|
+
TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING"
|
216
258
|
|
217
|
-
|
218
|
-
|
219
|
-
TRANSFER_ENCODING = "transfer-encoding".freeze
|
220
|
-
TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING".freeze
|
259
|
+
CONNECTION_CLOSE = "Connection: close\r\n"
|
260
|
+
CONNECTION_KEEP_ALIVE = "Connection: Keep-Alive\r\n"
|
221
261
|
|
222
|
-
|
223
|
-
|
262
|
+
TRANSFER_ENCODING_CHUNKED = "Transfer-Encoding: chunked\r\n"
|
263
|
+
CLOSE_CHUNKED = "0\r\n\r\n"
|
224
264
|
|
225
|
-
|
226
|
-
CLOSE_CHUNKED = "0\r\n\r\n".freeze
|
265
|
+
CHUNKED = "chunked"
|
227
266
|
|
228
|
-
|
267
|
+
COLON = ": "
|
229
268
|
|
230
|
-
|
269
|
+
NEWLINE = "\n"
|
231
270
|
|
232
|
-
|
233
|
-
|
271
|
+
HIJACK_P = "rack.hijack?"
|
272
|
+
HIJACK = "rack.hijack"
|
273
|
+
HIJACK_IO = "rack.hijack_io"
|
234
274
|
|
235
|
-
|
236
|
-
HIJACK = "rack.hijack".freeze
|
237
|
-
HIJACK_IO = "rack.hijack_io".freeze
|
275
|
+
EARLY_HINTS = "rack.early_hints"
|
238
276
|
|
239
|
-
|
277
|
+
# Illegal character in the key or value of response header
|
278
|
+
DQUOTE = "\""
|
279
|
+
HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
|
280
|
+
ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
|
281
|
+
# header values can contain HTAB?
|
282
|
+
ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
|
240
283
|
|
241
|
-
#
|
242
|
-
|
284
|
+
# Banned keys of response header
|
285
|
+
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
243
286
|
|
287
|
+
PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
|
244
288
|
end
|
245
289
|
end
|
data/lib/puma/control_cli.rb
CHANGED
@@ -11,7 +11,33 @@ require 'socket'
|
|
11
11
|
module Puma
|
12
12
|
class ControlCLI
|
13
13
|
|
14
|
-
|
14
|
+
# values must be string or nil
|
15
|
+
# value of `nil` means command cannot be processed via signal
|
16
|
+
# @version 5.0.3
|
17
|
+
CMD_PATH_SIG_MAP = {
|
18
|
+
'gc' => nil,
|
19
|
+
'gc-stats' => nil,
|
20
|
+
'halt' => 'SIGQUIT',
|
21
|
+
'info' => 'SIGINFO',
|
22
|
+
'phased-restart' => 'SIGUSR1',
|
23
|
+
'refork' => 'SIGURG',
|
24
|
+
'reload-worker-directory' => nil,
|
25
|
+
'reopen-log' => 'SIGHUP',
|
26
|
+
'restart' => 'SIGUSR2',
|
27
|
+
'start' => nil,
|
28
|
+
'stats' => nil,
|
29
|
+
'status' => '',
|
30
|
+
'stop' => 'SIGTERM',
|
31
|
+
'thread-backtraces' => nil,
|
32
|
+
'worker-count-down' => 'SIGTTOU',
|
33
|
+
'worker-count-up' => 'SIGTTIN'
|
34
|
+
}.freeze
|
35
|
+
|
36
|
+
# commands that cannot be used in a request
|
37
|
+
NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
|
38
|
+
|
39
|
+
# @version 5.0.0
|
40
|
+
PRINTABLE_COMMANDS = %w[gc-stats stats thread-backtraces].freeze
|
15
41
|
|
16
42
|
def initialize(argv, stdout=STDOUT, stderr=STDERR)
|
17
43
|
@state = nil
|
@@ -22,7 +48,7 @@ module Puma
|
|
22
48
|
@control_auth_token = nil
|
23
49
|
@config_file = nil
|
24
50
|
@command = nil
|
25
|
-
@environment = ENV['RACK_ENV']
|
51
|
+
@environment = ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
26
52
|
|
27
53
|
@argv = argv.dup
|
28
54
|
@stdout = stdout
|
@@ -30,7 +56,7 @@ module Puma
|
|
30
56
|
@cli_options = {}
|
31
57
|
|
32
58
|
opts = OptionParser.new do |o|
|
33
|
-
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{
|
59
|
+
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
|
34
60
|
|
35
61
|
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
|
36
62
|
@state = arg
|
@@ -71,7 +97,7 @@ module Puma
|
|
71
97
|
end
|
72
98
|
|
73
99
|
o.on_tail("-V", "--version", "Show version") do
|
74
|
-
puts Const::PUMA_VERSION
|
100
|
+
@stdout.puts Const::PUMA_VERSION
|
75
101
|
exit
|
76
102
|
end
|
77
103
|
end
|
@@ -81,6 +107,15 @@ module Puma
|
|
81
107
|
|
82
108
|
@command = argv.shift
|
83
109
|
|
110
|
+
# check presence of command
|
111
|
+
unless @command
|
112
|
+
raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
|
113
|
+
end
|
114
|
+
|
115
|
+
unless CMD_PATH_SIG_MAP.key? @command
|
116
|
+
raise "Invalid command: #{@command}"
|
117
|
+
end
|
118
|
+
|
84
119
|
unless @config_file == '-'
|
85
120
|
environment = @environment || 'development'
|
86
121
|
|
@@ -99,16 +134,6 @@ module Puma
|
|
99
134
|
@pidfile ||= config.options[:pidfile]
|
100
135
|
end
|
101
136
|
end
|
102
|
-
|
103
|
-
# check present of command
|
104
|
-
unless @command
|
105
|
-
raise "Available commands: #{COMMANDS.join(", ")}"
|
106
|
-
end
|
107
|
-
|
108
|
-
unless COMMANDS.include? @command
|
109
|
-
raise "Invalid command: #{@command}"
|
110
|
-
end
|
111
|
-
|
112
137
|
rescue => e
|
113
138
|
@stdout.puts e.message
|
114
139
|
exit 1
|
@@ -132,7 +157,7 @@ module Puma
|
|
132
157
|
@pid = sf.pid
|
133
158
|
elsif @pidfile
|
134
159
|
# get pid from pid_file
|
135
|
-
File.
|
160
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
136
161
|
end
|
137
162
|
end
|
138
163
|
|
@@ -140,23 +165,27 @@ module Puma
|
|
140
165
|
uri = URI.parse @control_url
|
141
166
|
|
142
167
|
# create server object by scheme
|
143
|
-
server =
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
168
|
+
server =
|
169
|
+
case uri.scheme
|
170
|
+
when 'ssl'
|
171
|
+
require 'openssl'
|
172
|
+
OpenSSL::SSL::SSLSocket.new(
|
173
|
+
TCPSocket.new(uri.host, uri.port),
|
174
|
+
OpenSSL::SSL::SSLContext.new)
|
175
|
+
.tap { |ssl| ssl.sync_close = true } # default is false
|
176
|
+
.tap(&:connect)
|
177
|
+
when 'tcp'
|
178
|
+
TCPSocket.new uri.host, uri.port
|
179
|
+
when 'unix'
|
180
|
+
# check for abstract UNIXSocket
|
181
|
+
UNIXSocket.new(@control_url.start_with?('unix://@') ?
|
182
|
+
"\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
|
183
|
+
else
|
184
|
+
raise "Invalid scheme: #{uri.scheme}"
|
185
|
+
end
|
186
|
+
|
187
|
+
if @command == 'status'
|
188
|
+
message 'Puma is started'
|
160
189
|
else
|
161
190
|
url = "/#{@command}"
|
162
191
|
|
@@ -164,10 +193,10 @@ module Puma
|
|
164
193
|
url = url + "?token=#{@control_auth_token}"
|
165
194
|
end
|
166
195
|
|
167
|
-
server
|
196
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
168
197
|
|
169
198
|
unless data = server.read
|
170
|
-
raise
|
199
|
+
raise 'Server closed connection before responding'
|
171
200
|
end
|
172
201
|
|
173
202
|
response = data.split("\r\n")
|
@@ -176,67 +205,59 @@ module Puma
|
|
176
205
|
raise "Server sent empty response"
|
177
206
|
end
|
178
207
|
|
179
|
-
|
208
|
+
@http, @code, @message = response.first.split(' ',3)
|
180
209
|
|
181
|
-
if @code ==
|
182
|
-
raise
|
183
|
-
elsif @code ==
|
210
|
+
if @code == '403'
|
211
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
212
|
+
elsif @code == '404'
|
184
213
|
raise "Command error: #{response.last}"
|
185
|
-
elsif @code !=
|
214
|
+
elsif @code != '200'
|
186
215
|
raise "Bad response from server: #{@code}"
|
187
216
|
end
|
188
217
|
|
189
218
|
message "Command #{@command} sent success"
|
190
|
-
message response.last if @command
|
219
|
+
message response.last if PRINTABLE_COMMANDS.include?(@command)
|
191
220
|
end
|
192
221
|
ensure
|
193
|
-
|
222
|
+
if server
|
223
|
+
if uri.scheme == 'ssl'
|
224
|
+
server.sysclose
|
225
|
+
else
|
226
|
+
server.close unless server.closed?
|
227
|
+
end
|
228
|
+
end
|
194
229
|
end
|
195
230
|
|
196
231
|
def send_signal
|
197
232
|
unless @pid
|
198
|
-
raise
|
233
|
+
raise 'Neither pid nor control url available'
|
199
234
|
end
|
200
235
|
|
201
236
|
begin
|
237
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
202
238
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
when "halt"
|
208
|
-
Process.kill "QUIT", @pid
|
209
|
-
|
210
|
-
when "stop"
|
211
|
-
Process.kill "SIGTERM", @pid
|
212
|
-
|
213
|
-
when "stats"
|
214
|
-
puts "Stats not available via pid only"
|
215
|
-
return
|
216
|
-
|
217
|
-
when "reload-worker-directory"
|
218
|
-
puts "reload-worker-directory not available via pid only"
|
239
|
+
if sig.nil?
|
240
|
+
@stdout.puts "'#{@command}' not available via pid only"
|
241
|
+
@stdout.flush unless @stdout.sync
|
219
242
|
return
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
243
|
+
elsif sig.start_with? 'SIG'
|
244
|
+
if Signal.list.key? sig.sub(/\ASIG/, '')
|
245
|
+
Process.kill sig, @pid
|
246
|
+
else
|
247
|
+
raise "Signal '#{sig}' not available'"
|
248
|
+
end
|
249
|
+
elsif @command == 'status'
|
225
250
|
begin
|
226
251
|
Process.kill 0, @pid
|
227
|
-
puts
|
252
|
+
@stdout.puts 'Puma is started'
|
253
|
+
@stdout.flush unless @stdout.sync
|
228
254
|
rescue Errno::ESRCH
|
229
|
-
raise
|
255
|
+
raise 'Puma is not running'
|
230
256
|
end
|
231
|
-
|
232
|
-
return
|
233
|
-
|
234
|
-
else
|
235
257
|
return
|
236
258
|
end
|
237
|
-
|
238
259
|
rescue SystemCallError
|
239
|
-
if @command ==
|
260
|
+
if @command == 'restart'
|
240
261
|
start
|
241
262
|
else
|
242
263
|
raise "No pid '#{@pid}' found"
|
@@ -247,14 +268,13 @@ module Puma
|
|
247
268
|
end
|
248
269
|
|
249
270
|
def run
|
250
|
-
return start if @command ==
|
251
|
-
|
271
|
+
return start if @command == 'start'
|
252
272
|
prepare_configuration
|
253
273
|
|
254
|
-
if Puma.windows?
|
274
|
+
if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
|
255
275
|
send_request
|
256
276
|
else
|
257
|
-
|
277
|
+
send_signal
|
258
278
|
end
|
259
279
|
|
260
280
|
rescue => e
|
@@ -262,9 +282,9 @@ module Puma
|
|
262
282
|
exit 1
|
263
283
|
end
|
264
284
|
|
265
|
-
|
285
|
+
private
|
266
286
|
def start
|
267
|
-
|
287
|
+
require_relative 'cli'
|
268
288
|
|
269
289
|
run_args = []
|
270
290
|
|
@@ -276,13 +296,13 @@ module Puma
|
|
276
296
|
run_args += ["-C", @config_file] if @config_file
|
277
297
|
run_args += ["-e", @environment] if @environment
|
278
298
|
|
279
|
-
|
299
|
+
log_writer = Puma::LogWriter.new(@stdout, @stderr)
|
280
300
|
|
281
301
|
# replace $0 because puma use it to generate restart command
|
282
302
|
puma_cmd = $0.gsub(/pumactl$/, 'puma')
|
283
303
|
$0 = puma_cmd if File.exist?(puma_cmd)
|
284
304
|
|
285
|
-
cli = Puma::CLI.new run_args,
|
305
|
+
cli = Puma::CLI.new run_args, log_writer
|
286
306
|
cli.run
|
287
307
|
end
|
288
308
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -1,15 +1,46 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This file can be loaded independently of puma.rb, so it cannot have any code
|
4
|
+
# that assumes puma.rb is loaded.
|
5
|
+
|
6
|
+
|
3
7
|
module Puma
|
4
|
-
|
8
|
+
# @version 5.2.1
|
9
|
+
HAS_FORK = ::Process.respond_to? :fork
|
10
|
+
|
11
|
+
HAS_NATIVE_IO_WAIT = ::IO.public_instance_methods(false).include? :wait_readable
|
12
|
+
|
13
|
+
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
14
|
+
|
15
|
+
IS_OSX = RUBY_PLATFORM.include? 'darwin'
|
16
|
+
|
17
|
+
IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) ||
|
18
|
+
IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
|
19
|
+
|
20
|
+
IS_LINUX = !(IS_OSX || IS_WINDOWS)
|
21
|
+
|
22
|
+
# @version 5.2.0
|
23
|
+
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
5
24
|
|
6
25
|
def self.jruby?
|
7
26
|
IS_JRUBY
|
8
27
|
end
|
9
28
|
|
10
|
-
|
29
|
+
def self.osx?
|
30
|
+
IS_OSX
|
31
|
+
end
|
11
32
|
|
12
33
|
def self.windows?
|
13
34
|
IS_WINDOWS
|
14
35
|
end
|
36
|
+
|
37
|
+
# @version 5.0.0
|
38
|
+
def self.mri?
|
39
|
+
IS_MRI
|
40
|
+
end
|
41
|
+
|
42
|
+
# @version 5.0.0
|
43
|
+
def self.forkable?
|
44
|
+
HAS_FORK
|
45
|
+
end
|
15
46
|
end
|