-
Notifications
You must be signed in to change notification settings - Fork 4
/
options.rb
217 lines (164 loc) · 7.58 KB
/
options.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# frozen_string_literal: true
require 'set'
require 'optparse'
require 'spider-gazelle/logger'
module SpiderGazelle
module Options
DEFAULTS = {
host: "0.0.0.0",
port: 3000,
verbose: false,
tls: false,
backlog: 4096,
rackup: "#{Dir.pwd}/config.ru",
mode: :thread,
isolate: true
}.freeze
# Options that can't be used when more than one set of options is being applied
APP_OPTIONS = [:port, :host, :verbose, :debug, :environment, :rackup, :mode, :backlog, :count, :name, :loglevel].freeze
MUTUALLY_EXCLUSIVE = {
# Only :password is valid when this option is present
update: APP_OPTIONS
}.freeze
def self.parse(args)
options = {}
parser = OptionParser.new do |opts|
# ================
# STANDARD OPTIONS
# ================
opts.on "-p", "--port PORT", Integer, "Define what port TCP port to bind to (default: 3000)" do |arg|
options[:port] = arg
end
opts.on "-h", "--host ADDRESS", "bind to address (default: 0.0.0.0)" do |arg|
options[:host] = arg
end
opts.on "-v", "--verbose", "loud output" do
options[:verbose] = true
end
opts.on "-d", "--debug", "debugging mode with lowered security and manual processes" do
options[:debug] = true
end
opts.on "-e", "--environment ENVIRONMENT", "The environment to run the Rack app on (default: development)" do |arg|
options[:environment] = arg
end
opts.on "-r", "--rackup FILE", "Load Rack config from this file (default: config.ru)" do |arg|
options[:rackup] = arg
end
opts.on "-rl", "--rack-lint", "enable rack lint on all requests" do
options[:lint] = true
end
opts.on "-m", "--mode MODE", MODES, "Either reactor, thread or inline (default: reactor)" do |arg|
options[:mode] = arg
end
opts.on "-b", "--backlog BACKLOG", Integer, "Number of pending connections allowed (default: 5000)" do |arg|
options[:backlog] = arg
end
# =================
# TLS Configuration
# =================
opts.on "-t", "--use-tls PRIVATE_KEY_FILE", "Enables TLS on the port specified using the provided private key in PEM format" do |arg|
options[:tls] = true
options[:private_key] = arg
end
opts.on "-tc", "--tls-chain-file CERT_CHAIN_FILE", "The certificate chain to provide clients" do |arg|
options[:cert_chain] = arg
end
opts.on "-ts", "--tls-ciphers CIPHER_LIST", "A list of Ciphers that the server will accept" do |arg|
options[:ciphers] = arg
end
opts.on "-tv", "--tls-verify-peer", "Do we want to verify the client connections? (default: false)" do
options[:verify_peer] = true
end
# ========================
# CHILD PROCESS INDICATORS
# ========================
opts.on "-g", "--gazelle PASSWORD", 'For internal use only' do |arg|
options[:gazelle] = arg
end
opts.on "-f", "--file IPC", 'For internal use only' do |arg|
options[:gazelle_ipc] = arg
end
opts.on "-s", "--spider PASSWORD", 'For internal use only' do |arg|
options[:spider] = arg
end
opts.on "-i", "--interactive-mode", 'Loads a multi-process version of spider-gazelle that can live update your app' do
options[:isolate] = false
end
opts.on "-c", "--count NUMBER", Integer, "Number of gazelle processes to launch (default: number of CPU cores)" do |arg|
options[:count] = arg
end
# ==================
# SIGNALLING OPTIONS
# ==================
opts.on "-u", "--update", "Live migrates to a new process without dropping existing connections" do |arg|
options[:update] = true
end
opts.on "-up", "--update-password PASSWORD", "Sets a password for performing updates" do |arg|
options[:password] = arg
end
opts.on "-l", "--loglevel LEVEL", Logger::LEVELS, "Sets the log level" do |arg|
options[:loglevel] = arg
end
end
parser.banner = "sg <options> <rackup file>"
parser.on_tail "-h", "--help", "Show help" do
puts parser
exit 1
end
parser.parse!(args)
# Check for rackup file
if args.last =~ /\.ru$/
options[:rackup] = args.last
end
# Unless this is a signal then we want to include the default options
unless options[:update]
options = DEFAULTS.merge(options)
unless File.exist? options[:rackup]
abort "No rackup found at #{options[:rackup]}"
end
options[:environment] ||= ENV['RACK_ENV'] || 'development'
ENV['RACK_ENV'] = options[:environment]
# isolation and process mode don't mix
options[:isolate] = false if options[:mode] == :process
# Force no_ipc mode on Windows (sockets over pipes are not working in threaded mode)
options[:mode] = :no_ipc if ::FFI::Platform.windows? && options[:mode] == :thread
end
options
end
def self.sanitize(args)
# Use "\0" as this character won't be used in the command
cmdline = args.join("\0")
components = cmdline.split("\0--", -1)
# Ensure there is at least one component
# (This will occur when no options are provided)
components << String.new if components.empty?
# Parse the commandline options
options = []
components.each do |app_opts|
options << parse(app_opts.split(/\0+/))
end
# Check for any invalid requests
exclusive = Set.new(MUTUALLY_EXCLUSIVE.keys)
if options.length > 1
# Some options can only be used by themselves
options.each do |opt|
keys = Set.new(opt.keys)
if exclusive.intersect? keys
invalid = exclusive & keys
abort "The requested actions can only be used in isolation: #{invalid.to_a}"
end
end
# Ensure there are no conflicting ports
ports = [options[0][:port]]
options[1..-1].each do |opt|
# If there is a clash we'll increment the port by 1
while ports.include? opt[:port]
opt[:port] += 1
end
ports << opt[:port]
end
end
options
end
end
end