puma 3.12.6 → 6.3.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 +4 -4
- data/History.md +1806 -451
- data/LICENSE +23 -20
- data/README.md +217 -65
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +59 -21
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +69 -58
- 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/docs/kubernetes.md +66 -0
- data/docs/nginx.md +2 -2
- data/docs/plugins.md +22 -12
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +47 -22
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +94 -120
- 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 -2
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +61 -3
- data/ext/puma_http11/http11_parser.c +103 -117
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +22 -38
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +6 -6
- data/ext/puma_http11/mini_ssl.c +389 -99
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +248 -92
- data/ext/puma_http11/puma_http11.c +49 -57
- data/lib/puma/app/status.rb +71 -49
- data/lib/puma/binder.rb +244 -150
- data/lib/puma/cli.rb +38 -34
- data/lib/puma/client.rb +388 -244
- data/lib/puma/cluster/worker.rb +180 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +261 -243
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +116 -88
- data/lib/puma/const.rb +154 -104
- data/lib/puma/control_cli.rb +115 -70
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +764 -134
- data/lib/puma/error_logger.rb +113 -0
- data/lib/puma/events.rb +16 -112
- data/lib/puma/io_buffer.rb +42 -5
- 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 +184 -133
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +93 -0
- data/lib/puma/minissl.rb +263 -70
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +3 -1
- data/lib/puma/plugin.rb +7 -13
- data/lib/puma/rack/builder.rb +9 -11
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +21 -4
- data/lib/puma/reactor.rb +93 -315
- data/lib/puma/request.rb +671 -0
- data/lib/puma/runner.rb +94 -69
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +327 -772
- data/lib/puma/single.rb +20 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/thread_pool.rb +146 -92
- data/lib/puma/util.rb +22 -10
- data/lib/puma.rb +60 -5
- data/lib/rack/handler/puma.rb +116 -90
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +54 -32
- data/ext/puma_http11/io_buffer.c +0 -155
- data/lib/puma/accept_nonblock.rb +0 -23
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -25
- data/lib/puma/daemon_ext.rb +0 -33
- data/lib/puma/delegation.rb +0 -13
- data/lib/puma/java_io_buffer.rb +0 -47
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- 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/{tools → docs}/jungle/rc.d/puma.conf +0 -0
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,9 +74,9 @@ 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
|
|
81
81
|
# For some HTTP status codes the client only expects headers.
|
82
82
|
#
|
@@ -85,7 +85,7 @@ module Puma
|
|
85
85
|
204 => true,
|
86
86
|
205 => true,
|
87
87
|
304 => true
|
88
|
-
}
|
88
|
+
}.freeze
|
89
89
|
|
90
90
|
# Frequently used constants when constructing requests or responses. Many times
|
91
91
|
# the constant just refers to a string with the same contents. Using these constants
|
@@ -100,56 +100,41 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "3.
|
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.0"
|
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
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
ERROR_503_RESPONSE = "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
115
|
+
REQUEST_URI= "REQUEST_URI"
|
116
|
+
REQUEST_PATH = "REQUEST_PATH"
|
117
|
+
QUERY_STRING = "QUERY_STRING"
|
118
|
+
CONTENT_LENGTH = "CONTENT_LENGTH"
|
119
|
+
|
120
|
+
PATH_INFO = "PATH_INFO"
|
121
|
+
|
122
|
+
PUMA_TMP_BASE = "puma"
|
123
|
+
|
124
|
+
ERROR_RESPONSE = {
|
125
|
+
# Indicate that we couldn't parse the request
|
126
|
+
400 => "HTTP/1.1 400 Bad Request\r\n\r\n",
|
127
|
+
# The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
|
128
|
+
404 => "HTTP/1.1 404 Not Found\r\nConnection: close\r\n\r\n",
|
129
|
+
# The standard empty 408 response for requests that timed out.
|
130
|
+
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n",
|
131
|
+
# Indicate that there was an internal error, obviously.
|
132
|
+
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n",
|
133
|
+
# Incorrect or invalid header value
|
134
|
+
501 => "HTTP/1.1 501 Not Implemented\r\n\r\n",
|
135
|
+
# A common header for indicating the server is too busy. Not used yet.
|
136
|
+
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\n"
|
137
|
+
}.freeze
|
153
138
|
|
154
139
|
# The basic max request size we'll try to read.
|
155
140
|
CHUNK_SIZE = 16 * 1024
|
@@ -161,79 +146,144 @@ module Puma
|
|
161
146
|
# Maximum request body size before it is moved out of memory and into a tempfile for reading.
|
162
147
|
MAX_BODY = MAX_HEADER
|
163
148
|
|
164
|
-
REQUEST_METHOD = "REQUEST_METHOD"
|
165
|
-
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
|
+
|
166
200
|
# ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
|
167
|
-
LINE_END = "\r\n"
|
168
|
-
REMOTE_ADDR = "REMOTE_ADDR"
|
169
|
-
HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"
|
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 = "::"
|
218
|
+
|
219
|
+
SERVER_PROTOCOL = "SERVER_PROTOCOL"
|
220
|
+
HTTP_11 = "HTTP/1.1"
|
221
|
+
|
222
|
+
SERVER_SOFTWARE = "SERVER_SOFTWARE"
|
223
|
+
GATEWAY_INTERFACE = "GATEWAY_INTERFACE"
|
224
|
+
CGI_VER = "CGI/1.2"
|
170
225
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
PORT_80 = "80".freeze
|
175
|
-
PORT_443 = "443".freeze
|
176
|
-
LOCALHOST = "localhost".freeze
|
177
|
-
LOCALHOST_IP = "127.0.0.1".freeze
|
178
|
-
LOCALHOST_ADDR = "127.0.0.1:0".freeze
|
226
|
+
STOP_COMMAND = "?"
|
227
|
+
HALT_COMMAND = "!"
|
228
|
+
RESTART_COMMAND = "R"
|
179
229
|
|
180
|
-
|
181
|
-
|
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"
|
182
236
|
|
183
|
-
|
184
|
-
|
185
|
-
CGI_VER = "CGI/1.2".freeze
|
237
|
+
HTTP = "http"
|
238
|
+
HTTPS = "https"
|
186
239
|
|
187
|
-
|
188
|
-
HALT_COMMAND = "!".freeze
|
189
|
-
RESTART_COMMAND = "R".freeze
|
240
|
+
HTTPS_KEY = "HTTPS"
|
190
241
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
PUMA_CONFIG = "puma.config".freeze
|
196
|
-
PUMA_PEERCERT = "puma.peercert".freeze
|
242
|
+
HTTP_VERSION = "HTTP_VERSION"
|
243
|
+
HTTP_CONNECTION = "HTTP_CONNECTION"
|
244
|
+
HTTP_EXPECT = "HTTP_EXPECT"
|
245
|
+
CONTINUE = "100-continue"
|
197
246
|
|
198
|
-
|
199
|
-
|
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"
|
200
250
|
|
201
|
-
|
251
|
+
CLOSE = "close"
|
252
|
+
KEEP_ALIVE = "keep-alive"
|
202
253
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
254
|
+
CONTENT_LENGTH2 = "content-length"
|
255
|
+
CONTENT_LENGTH_S = "Content-Length: "
|
256
|
+
TRANSFER_ENCODING = "transfer-encoding"
|
257
|
+
TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING"
|
207
258
|
|
208
|
-
|
209
|
-
|
210
|
-
HTTP_10_200 = "HTTP/1.0 200 OK\r\n".freeze
|
259
|
+
CONNECTION_CLOSE = "Connection: close\r\n"
|
260
|
+
CONNECTION_KEEP_ALIVE = "Connection: Keep-Alive\r\n"
|
211
261
|
|
212
|
-
|
213
|
-
|
262
|
+
TRANSFER_ENCODING_CHUNKED = "Transfer-Encoding: chunked\r\n"
|
263
|
+
CLOSE_CHUNKED = "0\r\n\r\n"
|
214
264
|
|
215
|
-
|
216
|
-
CONTENT_LENGTH_S = "Content-Length: ".freeze
|
217
|
-
TRANSFER_ENCODING = "transfer-encoding".freeze
|
218
|
-
TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING".freeze
|
265
|
+
CHUNKED = "chunked"
|
219
266
|
|
220
|
-
|
221
|
-
CONNECTION_KEEP_ALIVE = "Connection: Keep-Alive\r\n".freeze
|
267
|
+
COLON = ": "
|
222
268
|
|
223
|
-
|
224
|
-
CLOSE_CHUNKED = "0\r\n\r\n".freeze
|
269
|
+
NEWLINE = "\n"
|
225
270
|
|
226
|
-
|
271
|
+
HIJACK_P = "rack.hijack?"
|
272
|
+
HIJACK = "rack.hijack"
|
273
|
+
HIJACK_IO = "rack.hijack_io"
|
227
274
|
|
228
|
-
|
275
|
+
EARLY_HINTS = "rack.early_hints"
|
229
276
|
|
230
|
-
|
231
|
-
|
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
|
232
283
|
|
233
|
-
|
234
|
-
|
235
|
-
HIJACK_IO = "rack.hijack_io".freeze
|
284
|
+
# Banned keys of response header
|
285
|
+
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
236
286
|
|
237
|
-
|
287
|
+
PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
|
238
288
|
end
|
239
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,6 +48,7 @@ module Puma
|
|
22
48
|
@control_auth_token = nil
|
23
49
|
@config_file = nil
|
24
50
|
@command = nil
|
51
|
+
@environment = ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
25
52
|
|
26
53
|
@argv = argv.dup
|
27
54
|
@stdout = stdout
|
@@ -29,7 +56,7 @@ module Puma
|
|
29
56
|
@cli_options = {}
|
30
57
|
|
31
58
|
opts = OptionParser.new do |o|
|
32
|
-
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("|")})"
|
33
60
|
|
34
61
|
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
|
35
62
|
@state = arg
|
@@ -59,13 +86,18 @@ module Puma
|
|
59
86
|
@config_file = arg
|
60
87
|
end
|
61
88
|
|
89
|
+
o.on "-e", "--environment ENVIRONMENT",
|
90
|
+
"The environment to run the Rack app on (default development)" do |arg|
|
91
|
+
@environment = arg
|
92
|
+
end
|
93
|
+
|
62
94
|
o.on_tail("-H", "--help", "Show this message") do
|
63
95
|
@stdout.puts o
|
64
96
|
exit
|
65
97
|
end
|
66
98
|
|
67
99
|
o.on_tail("-V", "--version", "Show version") do
|
68
|
-
puts Const::PUMA_VERSION
|
100
|
+
@stdout.puts Const::PUMA_VERSION
|
69
101
|
exit
|
70
102
|
end
|
71
103
|
end
|
@@ -75,9 +107,22 @@ module Puma
|
|
75
107
|
|
76
108
|
@command = argv.shift
|
77
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
|
+
|
78
119
|
unless @config_file == '-'
|
79
|
-
|
80
|
-
|
120
|
+
environment = @environment || 'development'
|
121
|
+
|
122
|
+
if @config_file.nil?
|
123
|
+
@config_file = %W(config/puma/#{environment}.rb config/puma.rb).find do |f|
|
124
|
+
File.exist?(f)
|
125
|
+
end
|
81
126
|
end
|
82
127
|
|
83
128
|
if @config_file
|
@@ -89,19 +134,8 @@ module Puma
|
|
89
134
|
@pidfile ||= config.options[:pidfile]
|
90
135
|
end
|
91
136
|
end
|
92
|
-
|
93
|
-
# check present of command
|
94
|
-
unless @command
|
95
|
-
raise "Available commands: #{COMMANDS.join(", ")}"
|
96
|
-
end
|
97
|
-
|
98
|
-
unless COMMANDS.include? @command
|
99
|
-
raise "Invalid command: #{@command}"
|
100
|
-
end
|
101
|
-
|
102
137
|
rescue => e
|
103
138
|
@stdout.puts e.message
|
104
|
-
@stdout.puts e.backtrace
|
105
139
|
exit 1
|
106
140
|
end
|
107
141
|
|
@@ -123,7 +157,7 @@ module Puma
|
|
123
157
|
@pid = sf.pid
|
124
158
|
elsif @pidfile
|
125
159
|
# get pid from pid_file
|
126
|
-
@pid = File.
|
160
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
127
161
|
end
|
128
162
|
end
|
129
163
|
|
@@ -131,17 +165,27 @@ module Puma
|
|
131
165
|
uri = URI.parse @control_url
|
132
166
|
|
133
167
|
# create server object by scheme
|
134
|
-
server =
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
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'
|
145
189
|
else
|
146
190
|
url = "/#{@command}"
|
147
191
|
|
@@ -149,10 +193,10 @@ module Puma
|
|
149
193
|
url = url + "?token=#{@control_auth_token}"
|
150
194
|
end
|
151
195
|
|
152
|
-
server
|
196
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
153
197
|
|
154
198
|
unless data = server.read
|
155
|
-
raise
|
199
|
+
raise 'Server closed connection before responding'
|
156
200
|
end
|
157
201
|
|
158
202
|
response = data.split("\r\n")
|
@@ -161,57 +205,59 @@ module Puma
|
|
161
205
|
raise "Server sent empty response"
|
162
206
|
end
|
163
207
|
|
164
|
-
|
208
|
+
@http, @code, @message = response.first.split(' ',3)
|
165
209
|
|
166
|
-
if @code ==
|
167
|
-
raise
|
168
|
-
elsif @code ==
|
210
|
+
if @code == '403'
|
211
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
212
|
+
elsif @code == '404'
|
169
213
|
raise "Command error: #{response.last}"
|
170
|
-
elsif @code !=
|
214
|
+
elsif @code != '200'
|
171
215
|
raise "Bad response from server: #{@code}"
|
172
216
|
end
|
173
217
|
|
174
218
|
message "Command #{@command} sent success"
|
175
|
-
message response.last if @command
|
219
|
+
message response.last if PRINTABLE_COMMANDS.include?(@command)
|
176
220
|
end
|
177
221
|
ensure
|
178
|
-
|
222
|
+
if server
|
223
|
+
if uri.scheme == 'ssl'
|
224
|
+
server.sysclose
|
225
|
+
else
|
226
|
+
server.close unless server.closed?
|
227
|
+
end
|
228
|
+
end
|
179
229
|
end
|
180
230
|
|
181
231
|
def send_signal
|
182
232
|
unless @pid
|
183
|
-
raise
|
233
|
+
raise 'Neither pid nor control url available'
|
184
234
|
end
|
185
235
|
|
186
236
|
begin
|
237
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
187
238
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
when "halt"
|
193
|
-
Process.kill "QUIT", @pid
|
194
|
-
|
195
|
-
when "stop"
|
196
|
-
Process.kill "SIGTERM", @pid
|
197
|
-
|
198
|
-
when "stats"
|
199
|
-
puts "Stats not available via pid only"
|
200
|
-
return
|
201
|
-
|
202
|
-
when "reload-worker-directory"
|
203
|
-
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
|
204
242
|
return
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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'
|
250
|
+
begin
|
251
|
+
Process.kill 0, @pid
|
252
|
+
@stdout.puts 'Puma is started'
|
253
|
+
@stdout.flush unless @stdout.sync
|
254
|
+
rescue Errno::ESRCH
|
255
|
+
raise 'Puma is not running'
|
256
|
+
end
|
210
257
|
return
|
211
258
|
end
|
212
|
-
|
213
259
|
rescue SystemCallError
|
214
|
-
if @command ==
|
260
|
+
if @command == 'restart'
|
215
261
|
start
|
216
262
|
else
|
217
263
|
raise "No pid '#{@pid}' found"
|
@@ -222,25 +268,23 @@ module Puma
|
|
222
268
|
end
|
223
269
|
|
224
270
|
def run
|
225
|
-
return start if @command ==
|
226
|
-
|
271
|
+
return start if @command == 'start'
|
227
272
|
prepare_configuration
|
228
273
|
|
229
|
-
if Puma.windows?
|
274
|
+
if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
|
230
275
|
send_request
|
231
276
|
else
|
232
|
-
|
277
|
+
send_signal
|
233
278
|
end
|
234
279
|
|
235
280
|
rescue => e
|
236
281
|
message e.message
|
237
|
-
message e.backtrace
|
238
282
|
exit 1
|
239
283
|
end
|
240
284
|
|
241
|
-
|
285
|
+
private
|
242
286
|
def start
|
243
|
-
|
287
|
+
require_relative 'cli'
|
244
288
|
|
245
289
|
run_args = []
|
246
290
|
|
@@ -250,14 +294,15 @@ module Puma
|
|
250
294
|
run_args += ["--control-url", @control_url] if @control_url
|
251
295
|
run_args += ["--control-token", @control_auth_token] if @control_auth_token
|
252
296
|
run_args += ["-C", @config_file] if @config_file
|
297
|
+
run_args += ["-e", @environment] if @environment
|
253
298
|
|
254
|
-
|
299
|
+
log_writer = Puma::LogWriter.new(@stdout, @stderr)
|
255
300
|
|
256
301
|
# replace $0 because puma use it to generate restart command
|
257
302
|
puma_cmd = $0.gsub(/pumactl$/, 'puma')
|
258
303
|
$0 = puma_cmd if File.exist?(puma_cmd)
|
259
304
|
|
260
|
-
cli = Puma::CLI.new run_args,
|
305
|
+
cli = Puma::CLI.new run_args, log_writer
|
261
306
|
cli.run
|
262
307
|
end
|
263
308
|
end
|