ed-precompiled_puma 7.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +3172 -0
  3. data/LICENSE +29 -0
  4. data/README.md +477 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +25 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +74 -0
  9. data/docs/compile_options.md +55 -0
  10. data/docs/deployment.md +102 -0
  11. data/docs/fork_worker.md +41 -0
  12. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  13. data/docs/images/puma-connection-flow.png +0 -0
  14. data/docs/images/puma-general-arch.png +0 -0
  15. data/docs/java_options.md +54 -0
  16. data/docs/jungle/README.md +9 -0
  17. data/docs/jungle/rc.d/README.md +74 -0
  18. data/docs/jungle/rc.d/puma +61 -0
  19. data/docs/jungle/rc.d/puma.conf +10 -0
  20. data/docs/kubernetes.md +80 -0
  21. data/docs/nginx.md +80 -0
  22. data/docs/plugins.md +42 -0
  23. data/docs/rails_dev_mode.md +28 -0
  24. data/docs/restart.md +65 -0
  25. data/docs/signals.md +98 -0
  26. data/docs/stats.md +148 -0
  27. data/docs/systemd.md +253 -0
  28. data/docs/testing_benchmarks_local_files.md +150 -0
  29. data/docs/testing_test_rackup_ci_files.md +36 -0
  30. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  31. data/ext/puma_http11/ext_help.h +15 -0
  32. data/ext/puma_http11/extconf.rb +65 -0
  33. data/ext/puma_http11/http11_parser.c +1057 -0
  34. data/ext/puma_http11/http11_parser.h +65 -0
  35. data/ext/puma_http11/http11_parser.java.rl +145 -0
  36. data/ext/puma_http11/http11_parser.rl +149 -0
  37. data/ext/puma_http11/http11_parser_common.rl +54 -0
  38. data/ext/puma_http11/mini_ssl.c +852 -0
  39. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  40. data/ext/puma_http11/org/jruby/puma/Http11.java +257 -0
  41. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +455 -0
  42. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +509 -0
  43. data/ext/puma_http11/puma_http11.c +507 -0
  44. data/lib/puma/app/status.rb +96 -0
  45. data/lib/puma/binder.rb +511 -0
  46. data/lib/puma/cli.rb +245 -0
  47. data/lib/puma/client.rb +720 -0
  48. data/lib/puma/cluster/worker.rb +182 -0
  49. data/lib/puma/cluster/worker_handle.rb +127 -0
  50. data/lib/puma/cluster.rb +635 -0
  51. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  52. data/lib/puma/commonlogger.rb +115 -0
  53. data/lib/puma/configuration.rb +452 -0
  54. data/lib/puma/const.rb +307 -0
  55. data/lib/puma/control_cli.rb +320 -0
  56. data/lib/puma/detect.rb +47 -0
  57. data/lib/puma/dsl.rb +1480 -0
  58. data/lib/puma/error_logger.rb +115 -0
  59. data/lib/puma/events.rb +72 -0
  60. data/lib/puma/io_buffer.rb +50 -0
  61. data/lib/puma/jruby_restart.rb +11 -0
  62. data/lib/puma/json_serialization.rb +96 -0
  63. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  64. data/lib/puma/launcher.rb +496 -0
  65. data/lib/puma/log_writer.rb +147 -0
  66. data/lib/puma/minissl/context_builder.rb +96 -0
  67. data/lib/puma/minissl.rb +463 -0
  68. data/lib/puma/null_io.rb +101 -0
  69. data/lib/puma/plugin/systemd.rb +90 -0
  70. data/lib/puma/plugin/tmp_restart.rb +36 -0
  71. data/lib/puma/plugin.rb +111 -0
  72. data/lib/puma/rack/builder.rb +297 -0
  73. data/lib/puma/rack/urlmap.rb +93 -0
  74. data/lib/puma/rack_default.rb +24 -0
  75. data/lib/puma/reactor.rb +140 -0
  76. data/lib/puma/request.rb +701 -0
  77. data/lib/puma/runner.rb +211 -0
  78. data/lib/puma/sd_notify.rb +146 -0
  79. data/lib/puma/server.rb +734 -0
  80. data/lib/puma/single.rb +72 -0
  81. data/lib/puma/state_file.rb +69 -0
  82. data/lib/puma/thread_pool.rb +402 -0
  83. data/lib/puma/util.rb +134 -0
  84. data/lib/puma.rb +93 -0
  85. data/lib/rack/handler/puma.rb +144 -0
  86. data/tools/Dockerfile +18 -0
  87. data/tools/trickletest.rb +44 -0
  88. metadata +152 -0
data/lib/puma/const.rb ADDED
@@ -0,0 +1,307 @@
1
+ #encoding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module Puma
5
+ class UnsupportedOption < RuntimeError
6
+ end
7
+
8
+ # Every standard HTTP code mapped to the appropriate message. These are
9
+ # used so frequently that they are placed directly in Puma for easy
10
+ # access rather than Puma::Const itself.
11
+
12
+ # Every standard HTTP code mapped to the appropriate message.
13
+ # Generated with:
14
+ # curl -s https://www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
15
+ # ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
16
+ # puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
17
+ HTTP_STATUS_CODES = {
18
+ 100 => 'Continue',
19
+ 101 => 'Switching Protocols',
20
+ 102 => 'Processing',
21
+ 103 => 'Early Hints',
22
+ 200 => 'OK',
23
+ 201 => 'Created',
24
+ 202 => 'Accepted',
25
+ 203 => 'Non-Authoritative Information',
26
+ 204 => 'No Content',
27
+ 205 => 'Reset Content',
28
+ 206 => 'Partial Content',
29
+ 207 => 'Multi-Status',
30
+ 208 => 'Already Reported',
31
+ 226 => 'IM Used',
32
+ 300 => 'Multiple Choices',
33
+ 301 => 'Moved Permanently',
34
+ 302 => 'Found',
35
+ 303 => 'See Other',
36
+ 304 => 'Not Modified',
37
+ 305 => 'Use Proxy',
38
+ 307 => 'Temporary Redirect',
39
+ 308 => 'Permanent Redirect',
40
+ 400 => 'Bad Request',
41
+ 401 => 'Unauthorized',
42
+ 402 => 'Payment Required',
43
+ 403 => 'Forbidden',
44
+ 404 => 'Not Found',
45
+ 405 => 'Method Not Allowed',
46
+ 406 => 'Not Acceptable',
47
+ 407 => 'Proxy Authentication Required',
48
+ 408 => 'Request Timeout',
49
+ 409 => 'Conflict',
50
+ 410 => 'Gone',
51
+ 411 => 'Length Required',
52
+ 412 => 'Precondition Failed',
53
+ 413 => 'Content Too Large',
54
+ 414 => 'URI Too Long',
55
+ 415 => 'Unsupported Media Type',
56
+ 416 => 'Range Not Satisfiable',
57
+ 417 => 'Expectation Failed',
58
+ 421 => 'Misdirected Request',
59
+ 422 => 'Unprocessable Content',
60
+ 423 => 'Locked',
61
+ 424 => 'Failed Dependency',
62
+ 425 => 'Too Early',
63
+ 426 => 'Upgrade Required',
64
+ 428 => 'Precondition Required',
65
+ 429 => 'Too Many Requests',
66
+ 431 => 'Request Header Fields Too Large',
67
+ 451 => 'Unavailable For Legal Reasons',
68
+ 500 => 'Internal Server Error',
69
+ 501 => 'Not Implemented',
70
+ 502 => 'Bad Gateway',
71
+ 503 => 'Service Unavailable',
72
+ 504 => 'Gateway Timeout',
73
+ 505 => 'HTTP Version Not Supported',
74
+ 506 => 'Variant Also Negotiates',
75
+ 507 => 'Insufficient Storage',
76
+ 508 => 'Loop Detected',
77
+ 510 => 'Not Extended (OBSOLETED)',
78
+ 511 => 'Network Authentication Required'
79
+ }.freeze
80
+
81
+ # For some HTTP status codes the client only expects headers.
82
+ #
83
+
84
+ STATUS_WITH_NO_ENTITY_BODY = {
85
+ 204 => true,
86
+ 205 => true,
87
+ 304 => true
88
+ }.freeze
89
+
90
+ # Frequently used constants when constructing requests or responses. Many times
91
+ # the constant just refers to a string with the same contents. Using these constants
92
+ # gave about a 3% to 10% performance improvement over using the strings directly.
93
+ #
94
+ # The constants are frozen because Hash#[]= when called with a String key dups
95
+ # the String UNLESS the String is frozen. This saves us therefore 2 object
96
+ # allocations when creating the env hash later.
97
+ #
98
+ # While Puma does try to emulate the CGI/1.2 protocol, it does not use the REMOTE_IDENT,
99
+ # REMOTE_USER, or REMOTE_HOST parameters since those are either a security problem or
100
+ # too taxing on performance.
101
+ module Const
102
+
103
+ PUMA_VERSION = VERSION = "7.0.4"
104
+ CODE_NAME = "Romantic Warrior"
105
+
106
+ PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
107
+
108
+ # How long to wait when getting some write blocking on the socket when
109
+ # sending data back
110
+ WRITE_TIMEOUT = 10
111
+
112
+ # The original URI requested by the client.
113
+ REQUEST_URI= "REQUEST_URI"
114
+ REQUEST_PATH = "REQUEST_PATH"
115
+ QUERY_STRING = "QUERY_STRING"
116
+ CONTENT_LENGTH = "CONTENT_LENGTH"
117
+
118
+ PATH_INFO = "PATH_INFO"
119
+
120
+ PUMA_TMP_BASE = "puma"
121
+
122
+ ERROR_RESPONSE = {
123
+ # Indicate that we couldn't parse the request
124
+ 400 => "HTTP/1.1 400 Bad Request\r\n\r\n",
125
+ # The standard empty 404 response for bad requests. Use Error4040Handler for custom stuff.
126
+ 404 => "HTTP/1.1 404 Not Found\r\nconnection: close\r\n\r\n",
127
+ # The standard empty 408 response for requests that timed out.
128
+ 408 => "HTTP/1.1 408 Request Timeout\r\nconnection: close\r\n\r\n",
129
+ # Indicate that there was an internal error, obviously.
130
+ 500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n",
131
+ # Incorrect or invalid header value
132
+ 501 => "HTTP/1.1 501 Not Implemented\r\n\r\n",
133
+ # A common header for indicating the server is too busy. Not used yet.
134
+ 503 => "HTTP/1.1 503 Service Unavailable\r\n\r\n"
135
+ }.freeze
136
+
137
+ # The basic max request size we'll try to read.
138
+ CHUNK_SIZE = 64 * 1024
139
+
140
+ # This is the maximum header that is allowed before a client is booted. The parser detects
141
+ # this, but we'd also like to do this as well.
142
+ MAX_HEADER = 1024 * (80 + 32)
143
+
144
+ # Maximum request body size before it is moved out of memory and into a tempfile for reading.
145
+ MAX_BODY = MAX_HEADER
146
+
147
+ REQUEST_METHOD = "REQUEST_METHOD"
148
+ HEAD = "HEAD"
149
+
150
+ # based on https://www.rfc-editor.org/rfc/rfc9110.html#name-overview,
151
+ # with CONNECT removed, and PATCH added
152
+ SUPPORTED_HTTP_METHODS = %w[HEAD GET POST PUT DELETE OPTIONS TRACE PATCH].freeze
153
+
154
+ # list from https://www.iana.org/assignments/http-methods/http-methods.xhtml
155
+ # as of 04-May-23
156
+ IANA_HTTP_METHODS = %w[
157
+ ACL
158
+ BASELINE-CONTROL
159
+ BIND
160
+ CHECKIN
161
+ CHECKOUT
162
+ CONNECT
163
+ COPY
164
+ DELETE
165
+ GET
166
+ HEAD
167
+ LABEL
168
+ LINK
169
+ LOCK
170
+ MERGE
171
+ MKACTIVITY
172
+ MKCALENDAR
173
+ MKCOL
174
+ MKREDIRECTREF
175
+ MKWORKSPACE
176
+ MOVE
177
+ OPTIONS
178
+ ORDERPATCH
179
+ PATCH
180
+ POST
181
+ PRI
182
+ PROPFIND
183
+ PROPPATCH
184
+ PUT
185
+ REBIND
186
+ REPORT
187
+ SEARCH
188
+ TRACE
189
+ UNBIND
190
+ UNCHECKOUT
191
+ UNLINK
192
+ UNLOCK
193
+ UPDATE
194
+ UPDATEREDIRECTREF
195
+ VERSION-CONTROL
196
+ ].freeze
197
+
198
+ # ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
199
+ LINE_END = "\r\n"
200
+ REMOTE_ADDR = "REMOTE_ADDR"
201
+ HTTP_X_FORWARDED_FOR = "HTTP_X_FORWARDED_FOR"
202
+ HTTP_X_FORWARDED_SSL = "HTTP_X_FORWARDED_SSL"
203
+ HTTP_X_FORWARDED_SCHEME = "HTTP_X_FORWARDED_SCHEME"
204
+ HTTP_X_FORWARDED_PROTO = "HTTP_X_FORWARDED_PROTO"
205
+
206
+ SERVER_NAME = "SERVER_NAME"
207
+ SERVER_PORT = "SERVER_PORT"
208
+ HTTP_HOST = "HTTP_HOST"
209
+ PORT_80 = "80"
210
+ PORT_443 = "443"
211
+ LOCALHOST = "localhost"
212
+ LOCALHOST_IPV4 = "127.0.0.1"
213
+ LOCALHOST_IPV6 = "::1"
214
+ UNSPECIFIED_IPV4 = "0.0.0.0"
215
+ UNSPECIFIED_IPV6 = "::"
216
+
217
+ SERVER_PROTOCOL = "SERVER_PROTOCOL"
218
+ HTTP_11 = "HTTP/1.1"
219
+
220
+ SERVER_SOFTWARE = "SERVER_SOFTWARE"
221
+ GATEWAY_INTERFACE = "GATEWAY_INTERFACE"
222
+ CGI_VER = "CGI/1.2"
223
+
224
+ STOP_COMMAND = "?"
225
+ HALT_COMMAND = "!"
226
+ RESTART_COMMAND = "R"
227
+
228
+ RACK_INPUT = "rack.input"
229
+ RACK_URL_SCHEME = "rack.url_scheme"
230
+ RACK_AFTER_REPLY = "rack.after_reply"
231
+ RACK_RESPONSE_FINISHED = "rack.response_finished"
232
+ PUMA_SOCKET = "puma.socket"
233
+ PUMA_CONFIG = "puma.config"
234
+ PUMA_PEERCERT = "puma.peercert"
235
+
236
+ HTTP = "http"
237
+ HTTPS = "https"
238
+
239
+ HTTPS_KEY = "HTTPS"
240
+
241
+ HTTP_VERSION = "HTTP_VERSION"
242
+ HTTP_CONNECTION = "HTTP_CONNECTION"
243
+ HTTP_EXPECT = "HTTP_EXPECT"
244
+ CONTINUE = "100-continue"
245
+
246
+ HTTP_11_100 = "HTTP/1.1 100 Continue\r\n\r\n"
247
+ HTTP_11_200 = "HTTP/1.1 200 OK\r\n"
248
+ HTTP_10_200 = "HTTP/1.0 200 OK\r\n"
249
+
250
+ CLOSE = "close"
251
+ KEEP_ALIVE = "keep-alive"
252
+
253
+ CONTENT_LENGTH2 = "content-length"
254
+ CONTENT_LENGTH_S = "content-length: "
255
+ TRANSFER_ENCODING = "transfer-encoding"
256
+ TRANSFER_ENCODING2 = "HTTP_TRANSFER_ENCODING"
257
+
258
+ CONNECTION_CLOSE = "connection: close\r\n"
259
+ CONNECTION_KEEP_ALIVE = "connection: keep-alive\r\n"
260
+
261
+ TRANSFER_ENCODING_CHUNKED = "transfer-encoding: chunked\r\n"
262
+ CLOSE_CHUNKED = "0\r\n\r\n"
263
+
264
+ CHUNKED = "chunked"
265
+
266
+ COLON = ": "
267
+
268
+ NEWLINE = "\n"
269
+
270
+ HIJACK_P = "rack.hijack?"
271
+ HIJACK = "rack.hijack"
272
+ HIJACK_IO = "rack.hijack_io"
273
+
274
+ EARLY_HINTS = "rack.early_hints"
275
+
276
+ # Illegal character in the key or value of response header
277
+ DQUOTE = "\""
278
+ HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
279
+ ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
280
+ # header values can contain HTAB?
281
+ ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
282
+
283
+ # The keys of headers that should not be convert to underscore
284
+ # normalized versions. These headers are ignored at the request reading layer,
285
+ # but if we normalize them after reading, it's just confusing for the application.
286
+ UNMASKABLE_HEADERS = {
287
+ "HTTP_TRANSFER,ENCODING" => true,
288
+ "HTTP_CONTENT,LENGTH" => true,
289
+ }
290
+
291
+ # Banned keys of response header
292
+ BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
293
+
294
+ PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
295
+
296
+ # All constants are prefixed with `PIPE_` to avoid name collisions.
297
+ module PipeRequest
298
+ PIPE_WAKEUP = "!"
299
+ PIPE_BOOT = "b"
300
+ PIPE_FORK = "f"
301
+ PIPE_EXTERNAL_TERM = "e"
302
+ PIPE_TERM = "t"
303
+ PIPE_PING = "p"
304
+ PIPE_IDLE = "i"
305
+ end
306
+ end
307
+ end
@@ -0,0 +1,320 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+ require_relative 'const'
5
+ require_relative 'detect'
6
+ require 'uri'
7
+ require 'socket'
8
+
9
+ module Puma
10
+ class ControlCLI
11
+
12
+ # values must be string or nil
13
+ # value of `nil` means command cannot be processed via signal
14
+ # @version 5.0.3
15
+ CMD_PATH_SIG_MAP = {
16
+ 'gc' => nil,
17
+ 'gc-stats' => nil,
18
+ 'halt' => 'SIGQUIT',
19
+ 'info' => 'SIGINFO',
20
+ 'phased-restart' => 'SIGUSR1',
21
+ 'refork' => 'SIGURG',
22
+ 'reload-worker-directory' => nil,
23
+ 'reopen-log' => 'SIGHUP',
24
+ 'restart' => 'SIGUSR2',
25
+ 'start' => nil,
26
+ 'stats' => nil,
27
+ 'status' => '',
28
+ 'stop' => 'SIGTERM',
29
+ 'thread-backtraces' => nil,
30
+ 'worker-count-down' => 'SIGTTOU',
31
+ 'worker-count-up' => 'SIGTTIN'
32
+ }.freeze
33
+
34
+ # commands that cannot be used in a request
35
+ NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
36
+
37
+ # @version 5.0.0
38
+ PRINTABLE_COMMANDS = %w[gc-stats stats thread-backtraces].freeze
39
+
40
+ def initialize(argv, stdout=STDOUT, stderr=STDERR, env: ENV)
41
+ @state = nil
42
+ @quiet = false
43
+ @pidfile = nil
44
+ @pid = nil
45
+ @control_url = nil
46
+ @control_auth_token = nil
47
+ @config_file = nil
48
+ @command = nil
49
+ @environment = env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV']
50
+
51
+ @argv = argv.dup
52
+ @stdout = stdout
53
+ @stderr = stderr
54
+ @cli_options = {}
55
+
56
+ opts = OptionParser.new do |o|
57
+ o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
58
+
59
+ o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
60
+ @state = arg
61
+ end
62
+
63
+ o.on "-Q", "--quiet", "Do not display messages" do |arg|
64
+ @quiet = true
65
+ end
66
+
67
+ o.on "-P", "--pidfile PATH", "Pid file" do |arg|
68
+ @pidfile = arg
69
+ end
70
+
71
+ o.on "-p", "--pid PID", "Pid" do |arg|
72
+ @pid = arg.to_i
73
+ end
74
+
75
+ o.on "-C", "--control-url URL", "The bind url to use for the control server" do |arg|
76
+ @control_url = arg
77
+ end
78
+
79
+ o.on "-T", "--control-token TOKEN", "The token to use as authentication for the control server" do |arg|
80
+ @control_auth_token = arg
81
+ end
82
+
83
+ o.on "-F", "--config-file PATH", "Puma config script" do |arg|
84
+ @config_file = arg
85
+ end
86
+
87
+ o.on "-e", "--environment ENVIRONMENT",
88
+ "The environment to run the Rack app on (default development)" do |arg|
89
+ @environment = arg
90
+ end
91
+
92
+ o.on_tail("-H", "--help", "Show this message") do
93
+ @stdout.puts o
94
+ exit
95
+ end
96
+
97
+ o.on_tail("-V", "--version", "Show version") do
98
+ @stdout.puts Const::PUMA_VERSION
99
+ exit
100
+ end
101
+ end
102
+
103
+ opts.order!(argv) { |a| opts.terminate a }
104
+ opts.parse!
105
+
106
+ @command = argv.shift
107
+
108
+ # check presence of command
109
+ unless @command
110
+ raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
111
+ end
112
+
113
+ unless CMD_PATH_SIG_MAP.key? @command
114
+ raise "Invalid command: #{@command}"
115
+ end
116
+
117
+ unless @config_file == '-'
118
+ environment = @environment || 'development'
119
+
120
+ if @config_file.nil?
121
+ @config_file = %W(config/puma/#{environment}.rb config/puma.rb).find do |f|
122
+ File.exist?(f)
123
+ end
124
+ end
125
+
126
+ if @config_file
127
+ # needed because neither `Puma::CLI` or `Puma::Server` are loaded
128
+ require_relative '../puma'
129
+
130
+ require_relative 'configuration'
131
+ require_relative 'log_writer'
132
+
133
+ config = Puma::Configuration.new({ config_files: [@config_file] }, {} , env)
134
+ config.clamp
135
+
136
+ @state ||= config.options[:state]
137
+ @control_url ||= config.options[:control_url]
138
+ @control_auth_token ||= config.options[:control_auth_token]
139
+ @pidfile ||= config.options[:pidfile]
140
+ end
141
+ end
142
+ rescue => e
143
+ @stdout.puts e.message
144
+ exit 1
145
+ end
146
+
147
+ def message(msg)
148
+ @stdout.puts msg unless @quiet
149
+ end
150
+
151
+ def prepare_configuration
152
+ if @state
153
+ unless File.exist? @state
154
+ raise "State file not found: #{@state}"
155
+ end
156
+
157
+ require_relative 'state_file'
158
+
159
+ sf = Puma::StateFile.new
160
+ sf.load @state
161
+
162
+ @control_url = sf.control_url
163
+ @control_auth_token = sf.control_auth_token
164
+ @pid = sf.pid
165
+ elsif @pidfile
166
+ # get pid from pid_file
167
+ @pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
168
+ end
169
+ end
170
+
171
+ def send_request
172
+ uri = URI.parse @control_url
173
+
174
+ host = uri.host
175
+
176
+ # create server object by scheme
177
+ server =
178
+ case uri.scheme
179
+ when 'ssl'
180
+ require 'openssl'
181
+ host = host[1..-2] if host&.start_with? '['
182
+ OpenSSL::SSL::SSLSocket.new(
183
+ TCPSocket.new(host, uri.port),
184
+ OpenSSL::SSL::SSLContext.new)
185
+ .tap { |ssl| ssl.sync_close = true } # default is false
186
+ .tap(&:connect)
187
+ when 'tcp'
188
+ host = host[1..-2] if host&.start_with? '['
189
+ TCPSocket.new host, uri.port
190
+ when 'unix'
191
+ # check for abstract UNIXSocket
192
+ UNIXSocket.new(@control_url.start_with?('unix://@') ?
193
+ "\0#{host}#{uri.path}" : "#{host}#{uri.path}")
194
+ else
195
+ raise "Invalid scheme: #{uri.scheme}"
196
+ end
197
+
198
+ if @command == 'status'
199
+ message 'Puma is started'
200
+ else
201
+ url = "/#{@command}"
202
+
203
+ if @control_auth_token
204
+ url = url + "?token=#{@control_auth_token}"
205
+ end
206
+
207
+ server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
208
+
209
+ unless data = server.read
210
+ raise 'Server closed connection before responding'
211
+ end
212
+
213
+ response = data.split("\r\n")
214
+
215
+ if response.empty?
216
+ raise "Server sent empty response"
217
+ end
218
+
219
+ @http, @code, @message = response.first.split(' ',3)
220
+
221
+ if @code == '403'
222
+ raise 'Unauthorized access to server (wrong auth token)'
223
+ elsif @code == '404'
224
+ raise "Command error: #{response.last}"
225
+ elsif @code != '200'
226
+ raise "Bad response from server: #{@code}"
227
+ end
228
+
229
+ message "Command #{@command} sent success"
230
+ message response.last if PRINTABLE_COMMANDS.include?(@command)
231
+ end
232
+ ensure
233
+ if server
234
+ if uri.scheme == 'ssl'
235
+ server.sysclose
236
+ else
237
+ server.close unless server.closed?
238
+ end
239
+ end
240
+ end
241
+
242
+ def send_signal
243
+ unless @pid
244
+ raise 'Neither pid nor control url available'
245
+ end
246
+
247
+ begin
248
+ sig = CMD_PATH_SIG_MAP[@command]
249
+
250
+ if sig.nil?
251
+ @stdout.puts "'#{@command}' not available via pid only"
252
+ @stdout.flush unless @stdout.sync
253
+ return
254
+ elsif sig.start_with? 'SIG'
255
+ if Signal.list.key? sig.delete_prefix('SIG')
256
+ Process.kill sig, @pid
257
+ else
258
+ raise "Signal '#{sig}' not available'"
259
+ end
260
+ elsif @command == 'status'
261
+ begin
262
+ Process.kill 0, @pid
263
+ @stdout.puts 'Puma is started'
264
+ @stdout.flush unless @stdout.sync
265
+ rescue Errno::ESRCH
266
+ raise 'Puma is not running'
267
+ end
268
+ return
269
+ end
270
+ rescue SystemCallError
271
+ if @command == 'restart'
272
+ start
273
+ else
274
+ raise "No pid '#{@pid}' found"
275
+ end
276
+ end
277
+
278
+ message "Command #{@command} sent success"
279
+ end
280
+
281
+ def run
282
+ return start if @command == 'start'
283
+ prepare_configuration
284
+
285
+ if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
286
+ send_request
287
+ else
288
+ send_signal
289
+ end
290
+
291
+ rescue => e
292
+ message e.message
293
+ exit 1
294
+ end
295
+
296
+ private
297
+ def start
298
+ require_relative 'cli'
299
+
300
+ run_args = []
301
+
302
+ run_args += ["-S", @state] if @state
303
+ run_args += ["-q"] if @quiet
304
+ run_args += ["--pidfile", @pidfile] if @pidfile
305
+ run_args += ["--control-url", @control_url] if @control_url
306
+ run_args += ["--control-token", @control_auth_token] if @control_auth_token
307
+ run_args += ["-C", @config_file] if @config_file
308
+ run_args += ["-e", @environment] if @environment
309
+
310
+ log_writer = Puma::LogWriter.new(@stdout, @stderr)
311
+
312
+ # replace $0 because puma use it to generate restart command
313
+ puma_cmd = $0.gsub(/pumactl$/, 'puma')
314
+ $0 = puma_cmd if File.exist?(puma_cmd)
315
+
316
+ cli = Puma::CLI.new run_args, log_writer
317
+ cli.run
318
+ end
319
+ end
320
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
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
+
7
+ module Puma
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_DESCRIPTION.include? 'darwin'
16
+
17
+ IS_WINDOWS = RUBY_DESCRIPTION.match?(/mswin|ming|cygwin/)
18
+
19
+ IS_LINUX = !(IS_OSX || IS_WINDOWS)
20
+
21
+ IS_ARM = RUBY_PLATFORM.include? 'aarch64'
22
+
23
+ # @version 5.2.0
24
+ IS_MRI = RUBY_ENGINE == 'ruby'
25
+
26
+ def self.jruby?
27
+ IS_JRUBY
28
+ end
29
+
30
+ def self.osx?
31
+ IS_OSX
32
+ end
33
+
34
+ def self.windows?
35
+ IS_WINDOWS
36
+ end
37
+
38
+ # @version 5.0.0
39
+ def self.mri?
40
+ IS_MRI
41
+ end
42
+
43
+ # @version 5.0.0
44
+ def self.forkable?
45
+ HAS_FORK
46
+ end
47
+ end