swiftiply 0.6.1.1 → 1.0.0
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.
- checksums.yaml +7 -0
- data/CONTRIBUTORS +2 -0
- data/README.md +62 -0
- data/bin/{mongrel_rails → evented_mongrel_rails} +6 -14
- data/bin/swiftiplied_mongrel_rails +246 -0
- data/bin/swiftiply +136 -116
- data/bin/swiftiply_mongrel_rails +2 -2
- data/bin/swiftiplyctl +283 -0
- data/cleanup.sh +5 -0
- data/ext/deque/extconf.rb +162 -0
- data/ext/deque/swiftcore/rubymain.cpp +435 -0
- data/ext/fastfilereader/extconf.rb +2 -2
- data/ext/fastfilereader/mapper.cpp +2 -0
- data/ext/map/extconf.rb +161 -0
- data/ext/map/rubymain.cpp +500 -0
- data/ext/splaytree/extconf.rb +161 -0
- data/ext/splaytree/swiftcore/rubymain.cpp +580 -0
- data/ext/splaytree/swiftcore/splay_map.h +635 -0
- data/ext/splaytree/swiftcore/splay_set.h +575 -0
- data/ext/splaytree/swiftcore/splay_tree.h +1127 -0
- data/external/httpclient.rb +231 -0
- data/external/package.rb +13 -13
- data/setup.rb +18 -2
- data/src/swiftcore/Swiftiply.rb +417 -773
- data/src/swiftcore/Swiftiply/backend_protocol.rb +213 -0
- data/src/swiftcore/Swiftiply/cache_base.rb +49 -0
- data/src/swiftcore/Swiftiply/cache_base_mixin.rb +52 -0
- data/src/swiftcore/Swiftiply/cluster_managers/rest_based_cluster_manager.rb +9 -0
- data/src/swiftcore/Swiftiply/cluster_protocol.rb +70 -0
- data/src/swiftcore/Swiftiply/config.rb +370 -0
- data/src/swiftcore/Swiftiply/config/rest_updater.rb +26 -0
- data/src/swiftcore/Swiftiply/constants.rb +101 -0
- data/src/swiftcore/Swiftiply/content_cache_entry.rb +44 -0
- data/src/swiftcore/Swiftiply/content_response.rb +45 -0
- data/src/swiftcore/Swiftiply/control_protocol.rb +49 -0
- data/src/swiftcore/Swiftiply/dynamic_request_cache.rb +41 -0
- data/src/swiftcore/Swiftiply/etag_cache.rb +64 -0
- data/src/swiftcore/Swiftiply/file_cache.rb +46 -0
- data/src/swiftcore/Swiftiply/hash_cache_base.rb +22 -0
- data/src/swiftcore/Swiftiply/http_recognizer.rb +267 -0
- data/src/swiftcore/Swiftiply/loggers/Analogger.rb +21 -0
- data/src/swiftcore/Swiftiply/loggers/stderror.rb +13 -0
- data/src/swiftcore/Swiftiply/mocklog.rb +10 -0
- data/src/swiftcore/Swiftiply/proxy.rb +15 -0
- data/src/swiftcore/Swiftiply/proxy_backends/keepalive.rb +286 -0
- data/src/swiftcore/Swiftiply/proxy_backends/traditional.rb +286 -0
- data/src/swiftcore/Swiftiply/proxy_backends/traditional/redis_directory.rb +87 -0
- data/src/swiftcore/Swiftiply/proxy_backends/traditional/static_directory.rb +69 -0
- data/src/swiftcore/Swiftiply/proxy_bag.rb +716 -0
- data/src/swiftcore/Swiftiply/rest_based_cluster_manager.rb +15 -0
- data/src/swiftcore/Swiftiply/splay_cache_base.rb +21 -0
- data/src/swiftcore/Swiftiply/support_pagecache.rb +6 -3
- data/src/swiftcore/Swiftiply/swiftiply_2_http_proxy.rb +7 -0
- data/src/swiftcore/Swiftiply/swiftiply_client.rb +20 -5
- data/src/swiftcore/Swiftiply/version.rb +5 -0
- data/src/swiftcore/evented_mongrel.rb +26 -8
- data/src/swiftcore/hash.rb +43 -0
- data/src/swiftcore/method_builder.rb +28 -0
- data/src/swiftcore/streamer.rb +46 -0
- data/src/swiftcore/swiftiplied_mongrel.rb +91 -23
- data/src/swiftcore/types.rb +20 -3
- data/swiftiply.gemspec +14 -8
- data/test/TC_Deque.rb +152 -0
- data/test/TC_ProxyBag.rb +147 -166
- data/test/TC_Swiftiply.rb +576 -169
- data/test/TC_Swiftiply/mongrel/evented_hello.rb +1 -1
- data/test/TC_Swiftiply/mongrel/swiftiplied_hello.rb +1 -1
- data/test/TC_Swiftiply/test_serve_static_file_xsendfile/sendfile_client.rb +27 -0
- data/test/TC_Swiftiply/test_ssl/bin/validate_ssl_capability.rb +21 -0
- data/test/TC_Swiftiply/test_ssl/test.cert +16 -0
- data/test/TC_Swiftiply/test_ssl/test.key +15 -0
- data/{bin → test/bin}/echo_client +0 -0
- metadata +136 -94
- data/README +0 -126
- data/ext/swiftiply_parse/parse.rl +0 -90
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 41ffdd88a623bd6b34c2cb105d8e34286a4f226a
|
4
|
+
data.tar.gz: 2fe4a945f3b6008882f52e6e16174fd77603182f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fdbe79598ec795b05ed861a7fe446da09a7c00f510d468f62c2e1473980f964acaaee63c26919509da5fad293de5bc2f19348ccad22e79d58f4b9c5948d8b1ed
|
7
|
+
data.tar.gz: efe8bf53cf9756435a9924cc08eb5b8eef01d4ea5e69b899648be187ed26bfb5dbdbf2fab3edd4daf9702398d4cb74affc5cc0eb7d4df8b63ad20bd4d5d032ed
|
data/CONTRIBUTORS
CHANGED
data/README.md
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# Swiftiply v1.0.0 (http://github.com/wyhaines/swiftiply)
|
2
|
+
|
3
|
+
Swiftiply is a backend agnostic clustering proxy for web applications that is
|
4
|
+
specifically designed to support HTTP traffic from web frameworks. It is a targeted
|
5
|
+
proxy, intended specifically for use in front of web frameworks, and is not a
|
6
|
+
general purpose proxy.
|
7
|
+
|
8
|
+
What it is, though, is a very fast, narrowly targeted clustering proxy, with the
|
9
|
+
current implementation being written in Ruby.
|
10
|
+
|
11
|
+
Swiftiply works differently from a traditional proxy. In Swiftiply, the
|
12
|
+
backend processes are clients of the Swiftiply server -- they make persistent
|
13
|
+
socket connections to Swiftiply. One of the major advantages to this
|
14
|
+
architecture is that it allows one to start or stop backend processes at will,
|
15
|
+
with no configuration of the proxy. The proxy always knows exactly what resources
|
16
|
+
it has available to handle a given request. The obvious disadvantage is that this is
|
17
|
+
not behavior that web applications typically expect.
|
18
|
+
|
19
|
+
Swiftiply was originally written in an era when Mongrel was the preferred deployment
|
20
|
+
method for most Ruby frameworks. Swiftiply includes a version of Mongrel(found in
|
21
|
+
swiftcore/swiftiplied_mongrel.rb) that has been modified to work as a swiftiply client.
|
22
|
+
This should be transparent to any existing Mongrel handlers, allowing them all to with
|
23
|
+
Swiftiply.
|
24
|
+
|
25
|
+
Swiftiply also provides a traditional proxy model, allowing it to be used as a proxy
|
26
|
+
in front of any web application.
|
27
|
+
|
28
|
+
TODO: Provide an implementation of a swiftiply access proxy. This is a Swiftiply
|
29
|
+
TODO: "client" that maintains N connections into Swiftiply, but that operates as
|
30
|
+
TODO: a traditional proxy on the web application facing side. This lets an individual
|
31
|
+
TODO: server modulate the total number of connections that it is willing to handle
|
32
|
+
TODO: simultaneously, while not requiring the applications themselves to know anything
|
33
|
+
TODO: about it.
|
34
|
+
|
35
|
+
CONFIGURATION
|
36
|
+
|
37
|
+
Swiftiply takes a single configuration file which defines for it where it
|
38
|
+
should listen for incoming connections, whether it should daemonize itself,
|
39
|
+
and then provides a map of incoming domain names and the address/port to
|
40
|
+
proxy that traffic to. That outgoing address/port is where the backends for
|
41
|
+
that site will connect to.
|
42
|
+
|
43
|
+
Here's an example:
|
44
|
+
|
45
|
+
cluster_address: swiftcore.org
|
46
|
+
cluster_port: 80
|
47
|
+
daemonize: true
|
48
|
+
map:
|
49
|
+
- incoming:
|
50
|
+
- swiftcore.org
|
51
|
+
- www.swiftcore.org
|
52
|
+
outgoing: 127.0.0.1:30000
|
53
|
+
default: true
|
54
|
+
- incoming: iowa.swiftcore.org
|
55
|
+
outgoing: 127.0.0.1:30010
|
56
|
+
- incoming: analogger.swiftcore.org
|
57
|
+
outgoing: 127.0.0.1:30020
|
58
|
+
- incoming:
|
59
|
+
- swiftiply.com
|
60
|
+
- www.swiftiply.com
|
61
|
+
- swiftiply.swiftcore.org
|
62
|
+
outgoing: 127.0.0.1:30030
|
@@ -1,21 +1,13 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# Additional work donated by contributors. See http://mongrel.rubyforge.org/attributions.html
|
6
|
-
# for more information.
|
2
|
+
|
3
|
+
# No more mucking with event variables. Run swiftiplied_mongrel_rails to run
|
4
|
+
# a mongrel_rails setup for Swiftiply.
|
7
5
|
|
8
6
|
require 'rubygems'
|
9
7
|
require 'yaml'
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
elsif ENV['SWIFT']
|
14
|
-
require 'swiftcore/swiftiplied_mongrel'
|
15
|
-
puts "Using Evented Mongrel"
|
16
|
-
else
|
17
|
-
require 'mongrel'
|
18
|
-
end
|
8
|
+
|
9
|
+
require 'swiftcore/evented_mongrel'
|
10
|
+
|
19
11
|
require 'mongrel/rails'
|
20
12
|
require 'etc'
|
21
13
|
require 'cgi_multipart_eof_fix' rescue nil
|
@@ -0,0 +1,246 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# No more mucking with event variables. Run swiftiplied_mongrel_rails to run
|
4
|
+
# a mongrel_rails setup for Swiftiply.
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
require 'yaml'
|
8
|
+
|
9
|
+
require 'swiftcore/swiftiplied_mongrel'
|
10
|
+
|
11
|
+
require 'mongrel/rails'
|
12
|
+
require 'etc'
|
13
|
+
require 'cgi_multipart_eof_fix' rescue nil
|
14
|
+
|
15
|
+
module Mongrel
|
16
|
+
class Start < GemPlugin::Plugin "/commands"
|
17
|
+
include Mongrel::Command::Base
|
18
|
+
|
19
|
+
def configure
|
20
|
+
options [
|
21
|
+
["-e", "--environment ENV", "Rails environment to run as", :@environment, ENV['RAILS_ENV'] || "development"],
|
22
|
+
["-d", "--daemonize", "Run daemonized in the background", :@daemon, false],
|
23
|
+
['-p', '--port PORT', "Which port to bind to", :@port, 3000],
|
24
|
+
['-a', '--address ADDR', "Address to bind to", :@address, "0.0.0.0"],
|
25
|
+
['-l', '--log FILE', "Where to write log messages", :@log_file, "log/mongrel.log"],
|
26
|
+
['-P', '--pid FILE', "Where to write the PID", :@pid_file, "log/mongrel.pid"],
|
27
|
+
['-n', '--num-procs INT', "Number of processors active before clients denied", :@num_procs, 1024],
|
28
|
+
['-t', '--timeout TIME', "Timeout all requests after 100th seconds time", :@timeout, 0],
|
29
|
+
['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil],
|
30
|
+
['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, Dir.pwd],
|
31
|
+
['-r', '--root PATH', "Set the document root (default 'public')", :@docroot, "public"],
|
32
|
+
['-B', '--debug', "Enable debugging mode", :@debug, false],
|
33
|
+
['-C', '--config PATH', "Use a config file", :@config_file, nil],
|
34
|
+
['-S', '--script PATH', "Load the given file as an extra config script", :@config_script, nil],
|
35
|
+
['-G', '--generate PATH', "Generate a config file for use with -C", :@generate, nil],
|
36
|
+
['', '--user USER', "User to run as", :@user, nil],
|
37
|
+
['', '--group GROUP', "Group to run as", :@group, nil],
|
38
|
+
['', '--prefix PATH', "URL prefix for Rails app", :@prefix, nil]
|
39
|
+
]
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate
|
43
|
+
@cwd = File.expand_path(@cwd)
|
44
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
45
|
+
|
46
|
+
# Change there to start, then we'll have to come back after daemonize
|
47
|
+
Dir.chdir(@cwd)
|
48
|
+
|
49
|
+
valid?(@prefix[0].chr == "/" && @prefix[-1].chr != "/", "Prefix must begin with / and not end in /") if @prefix
|
50
|
+
valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file"
|
51
|
+
valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"
|
52
|
+
valid_dir? @docroot, "Path to docroot not valid: #@docroot"
|
53
|
+
valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map
|
54
|
+
valid_exists? @config_file, "Config file not there: #@config_file" if @config_file
|
55
|
+
valid_dir? File.dirname(@generate), "Problem accessing directory to #@generate" if @generate
|
56
|
+
valid_user? @user if @user
|
57
|
+
valid_group? @group if @group
|
58
|
+
|
59
|
+
return @valid
|
60
|
+
end
|
61
|
+
|
62
|
+
def run
|
63
|
+
# Config file settings will override command line settings
|
64
|
+
settings = { :host => @address, :port => @port, :cwd => @cwd,
|
65
|
+
:log_file => @log_file, :pid_file => @pid_file, :environment => @environment,
|
66
|
+
:docroot => @docroot, :mime_map => @mime_map, :daemon => @daemon,
|
67
|
+
:debug => @debug, :includes => ["mongrel"], :config_script => @config_script,
|
68
|
+
:num_processors => @num_procs, :timeout => @timeout,
|
69
|
+
:user => @user, :group => @group, :prefix => @prefix, :config_file => @config_file
|
70
|
+
}
|
71
|
+
|
72
|
+
if @generate
|
73
|
+
STDERR.puts "** Writing config to \"#@generate\"."
|
74
|
+
open(@generate, "w") {|f| f.write(settings.to_yaml) }
|
75
|
+
STDERR.puts "** Finished. Run \"mongrel_rails -C #@generate\" to use the config file."
|
76
|
+
exit 0
|
77
|
+
end
|
78
|
+
|
79
|
+
if @config_file
|
80
|
+
settings.merge! YAML.load_file(@config_file)
|
81
|
+
STDERR.puts "** Loading settings from #{@config_file} (they override command line)." unless settings[:daemon]
|
82
|
+
end
|
83
|
+
|
84
|
+
config = Mongrel::Rails::RailsConfigurator.new(settings) do
|
85
|
+
if defaults[:daemon]
|
86
|
+
if File.exist? defaults[:pid_file]
|
87
|
+
log "!!! PID file #{defaults[:pid_file]} already exists. Mongrel could be running already. Check your #{defaults[:log_file]} for errors."
|
88
|
+
log "!!! Exiting with error. You must stop mongrel and clear the .pid before I'll attempt a start."
|
89
|
+
exit 1
|
90
|
+
end
|
91
|
+
|
92
|
+
daemonize
|
93
|
+
log "Daemonized, any open files are closed. Look at #{defaults[:pid_file]} and #{defaults[:log_file]} for info."
|
94
|
+
log "Settings loaded from #{@config_file} (they override command line)." if @config_file
|
95
|
+
end
|
96
|
+
|
97
|
+
log "Starting Mongrel listening at #{defaults[:host]}:#{defaults[:port]}"
|
98
|
+
|
99
|
+
listener do
|
100
|
+
mime = {}
|
101
|
+
if defaults[:mime_map]
|
102
|
+
log "Loading additional MIME types from #{defaults[:mime_map]}"
|
103
|
+
mime = load_mime_map(defaults[:mime_map], mime)
|
104
|
+
end
|
105
|
+
|
106
|
+
if defaults[:debug]
|
107
|
+
log "Installing debugging prefixed filters. Look in log/mongrel_debug for the files."
|
108
|
+
debug "/"
|
109
|
+
end
|
110
|
+
|
111
|
+
log "Starting Rails with #{defaults[:environment]} environment..."
|
112
|
+
log "Mounting Rails at #{defaults[:prefix]}..." if defaults[:prefix]
|
113
|
+
uri defaults[:prefix] || "/", :handler => rails(:mime => mime, :prefix => defaults[:prefix])
|
114
|
+
log "Rails loaded."
|
115
|
+
|
116
|
+
log "Loading any Rails specific GemPlugins"
|
117
|
+
load_plugins
|
118
|
+
|
119
|
+
if defaults[:config_script]
|
120
|
+
log "Loading #{defaults[:config_script]} external config script"
|
121
|
+
run_config(defaults[:config_script])
|
122
|
+
end
|
123
|
+
|
124
|
+
setup_rails_signals
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
config.run
|
129
|
+
config.log "Mongrel available at #{settings[:host]}:#{settings[:port]}"
|
130
|
+
|
131
|
+
if config.defaults[:daemon]
|
132
|
+
config.write_pid_file
|
133
|
+
else
|
134
|
+
config.log "Use CTRL-C to stop."
|
135
|
+
end
|
136
|
+
|
137
|
+
config.join
|
138
|
+
|
139
|
+
if config.needs_restart
|
140
|
+
if RUBY_PLATFORM !~ /mswin/
|
141
|
+
cmd = "ruby #{__FILE__} start #{original_args.join(' ')}"
|
142
|
+
config.log "Restarting with arguments: #{cmd}"
|
143
|
+
config.stop
|
144
|
+
config.remove_pid_file
|
145
|
+
|
146
|
+
if config.defaults[:daemon]
|
147
|
+
system cmd
|
148
|
+
else
|
149
|
+
STDERR.puts "Can't restart unless in daemon mode."
|
150
|
+
exit 1
|
151
|
+
end
|
152
|
+
else
|
153
|
+
config.log "Win32 does not support restarts. Exiting."
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def Mongrel::send_signal(signal, pid_file)
|
160
|
+
pid = open(pid_file).read.to_i
|
161
|
+
print "Sending #{signal} to Mongrel at PID #{pid}..."
|
162
|
+
begin
|
163
|
+
Process.kill(signal, pid)
|
164
|
+
rescue Errno::ESRCH
|
165
|
+
puts "Process does not exist. Not running."
|
166
|
+
end
|
167
|
+
|
168
|
+
puts "Done."
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
class Stop < GemPlugin::Plugin "/commands"
|
173
|
+
include Mongrel::Command::Base
|
174
|
+
|
175
|
+
def configure
|
176
|
+
options [
|
177
|
+
['-c', '--chdir PATH', "Change to dir before starting (will be expanded).", :@cwd, "."],
|
178
|
+
['-f', '--force', "Force the shutdown (kill -9).", :@force, false],
|
179
|
+
['-w', '--wait SECONDS', "Wait SECONDS before forcing shutdown", :@wait, "0"],
|
180
|
+
['-P', '--pid FILE', "Where the PID file is located.", :@pid_file, "log/mongrel.pid"]
|
181
|
+
]
|
182
|
+
end
|
183
|
+
|
184
|
+
def validate
|
185
|
+
@cwd = File.expand_path(@cwd)
|
186
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
187
|
+
|
188
|
+
Dir.chdir @cwd
|
189
|
+
|
190
|
+
valid_exists? @pid_file, "PID file #@pid_file does not exist. Not running?"
|
191
|
+
return @valid
|
192
|
+
end
|
193
|
+
|
194
|
+
def run
|
195
|
+
if @force
|
196
|
+
@wait.to_i.times do |waiting|
|
197
|
+
exit(0) if not File.exist? @pid_file
|
198
|
+
sleep 1
|
199
|
+
end
|
200
|
+
|
201
|
+
Mongrel::send_signal("KILL", @pid_file) if File.exist? @pid_file
|
202
|
+
else
|
203
|
+
Mongrel::send_signal("TERM", @pid_file)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
|
209
|
+
class Restart < GemPlugin::Plugin "/commands"
|
210
|
+
include Mongrel::Command::Base
|
211
|
+
|
212
|
+
def configure
|
213
|
+
options [
|
214
|
+
['-c', '--chdir PATH', "Change to dir before starting (will be expanded)", :@cwd, '.'],
|
215
|
+
['-s', '--soft', "Do a soft restart rather than a process exit restart", :@soft, false],
|
216
|
+
['-P', '--pid FILE', "Where the PID file is located", :@pid_file, "log/mongrel.pid"]
|
217
|
+
]
|
218
|
+
end
|
219
|
+
|
220
|
+
def validate
|
221
|
+
@cwd = File.expand_path(@cwd)
|
222
|
+
valid_dir? @cwd, "Invalid path to change to during daemon mode: #@cwd"
|
223
|
+
|
224
|
+
Dir.chdir @cwd
|
225
|
+
|
226
|
+
valid_exists? @pid_file, "PID file #@pid_file does not exist. Not running?"
|
227
|
+
return @valid
|
228
|
+
end
|
229
|
+
|
230
|
+
def run
|
231
|
+
if @soft
|
232
|
+
Mongrel::send_signal("HUP", @pid_file)
|
233
|
+
else
|
234
|
+
Mongrel::send_signal("USR2", @pid_file)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
GemPlugin::Manager.instance.load "mongrel" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE
|
242
|
+
|
243
|
+
|
244
|
+
if not Mongrel::Command::Registry.instance.run ARGV
|
245
|
+
exit 1
|
246
|
+
end
|
data/bin/swiftiply
CHANGED
@@ -4,33 +4,33 @@ require 'optparse'
|
|
4
4
|
require 'yaml'
|
5
5
|
|
6
6
|
begin
|
7
|
-
|
8
|
-
|
7
|
+
load_attempted ||= false
|
8
|
+
require 'swiftcore/Swiftiply'
|
9
9
|
rescue LoadError => e
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
unless load_attempted
|
11
|
+
load_attempted = true
|
12
|
+
require 'rubygems'
|
13
|
+
retry
|
14
|
+
end
|
15
|
+
raise e
|
16
16
|
end
|
17
17
|
|
18
18
|
module Swiftcore
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
19
|
+
class SwiftiplyExec
|
20
|
+
Ccluster_address = 'cluster_address'.freeze
|
21
|
+
Ccluster_port = 'cluster_port'.freeze
|
22
|
+
Cconfig_file = 'config_file'.freeze
|
23
|
+
Cbackend_address = 'backend_address'.freeze
|
24
|
+
Cbackend_port = 'backend_port'.freeze
|
25
|
+
Cmap = 'map'.freeze
|
26
|
+
Cincoming = 'incoming'.freeze
|
27
|
+
Coutgoing = 'outgoing'.freeze
|
28
|
+
Ckeepalive = 'keepalive'.freeze
|
29
|
+
Cdaemonize = 'daemonize'.freeze
|
30
|
+
Curl = 'url'.freeze
|
31
|
+
Chost = 'host'.freeze
|
32
|
+
Cport = 'port'.freeze
|
33
|
+
Ctimeout = 'timeout'.freeze
|
34
34
|
|
35
35
|
#####
|
36
36
|
#
|
@@ -49,101 +49,121 @@ module Swiftcore
|
|
49
49
|
# - 127.0.0.1:8080
|
50
50
|
# - foo.bar.com:8090
|
51
51
|
# url:
|
52
|
-
#
|
53
|
-
#
|
52
|
+
# keepalive:
|
53
|
+
# outgoing:
|
54
54
|
#
|
55
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
|
-
|
56
|
+
def self.parse_options
|
57
|
+
config = @cliconfig || {}
|
58
|
+
@print = false
|
59
|
+
|
60
|
+
OptionParser.new do |opts|
|
61
|
+
opts.banner = 'Usage: swiftiply.rb [options]'
|
62
|
+
opts.separator ''
|
63
|
+
opts.on('-c','--config CONFFILE',"The configuration file to read.") do |conf|
|
64
|
+
config[Cconfig_file] = conf
|
65
|
+
end
|
66
|
+
opts.on('--cluster-address [ADDRESS]',String,'The hostname/IP address that swiftiply will listen for connections on.') do |address|
|
67
|
+
config[Ccluster_address] = address
|
68
|
+
end
|
69
|
+
opts.on('--cluster-port [PORT]',Integer,'The port that swiftiply will listen for connections on.') do |port|
|
70
|
+
config[Ccluster_port] = port
|
71
|
+
end
|
72
|
+
opts.on('--backend-address [ADDRESS]',String,'The hostname/IP address that swiftiply will listen for backend connections on.') do |address|
|
73
|
+
config[Cbackend_address] = address
|
74
|
+
end
|
75
|
+
opts.on('--backend-port [PORT]',Integer,'The port that swiftiply will listen for backend connections on.') do |port|
|
76
|
+
config[Cbackend_port] = port
|
77
|
+
end
|
78
|
+
opts.on('-d','--daemonize [YN]',[:y,:yes,:n,:no],'Whether swiftiply should put itself into the background.') do |yn|
|
79
|
+
config[Cdaemonize] = yn.to_s =~ /^y/i
|
80
|
+
end
|
81
|
+
opts.on('-t','--timeout [SECONDS]',Integer,'The server unavailable timeout. Defaults to 3 seconds.') do |timeout|
|
82
|
+
config[Ctimeout] = timeout
|
83
|
+
end
|
84
|
+
opts.on('-p','--print-config','Print the full configuration.') do
|
85
|
+
@print = true
|
86
|
+
end
|
87
|
+
opts.on('-v','--version','Show the version number, then exit.') do
|
88
|
+
puts "Swiftiply v. #{Swiftcore::Swiftiply::VERSION}"
|
89
|
+
exit 0
|
90
|
+
end
|
91
|
+
opts.on('-f','--pid PIDFILE',"Path of the pid file to write.") do |pidfile|
|
92
|
+
config['pidfile'] = pidfile
|
93
|
+
end
|
94
|
+
|
95
|
+
end.parse!
|
96
|
+
@cliconfig ||= config
|
97
|
+
|
98
|
+
fileconfig = {}
|
99
|
+
fileconfig = YAML.load(File.open(config['config_file'])) if config['config_file']
|
100
|
+
config = fileconfig.merge(@cliconfig)
|
101
|
+
postprocess_config_load(config)
|
102
|
+
(puts("Configuration failed validation; exiting.") && exit(1)) unless verify_config(config)
|
103
|
+
|
104
|
+
if @print
|
105
|
+
require 'pp'
|
106
|
+
pp config
|
107
|
+
exit 0
|
108
|
+
end
|
109
|
+
|
110
|
+
config
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.daemonize(pidfile = nil)
|
114
|
+
if (child_pid = fork)
|
115
|
+
if pidfile
|
116
|
+
begin
|
117
|
+
File.open(pidfile,"w+") {|fh| fh.puts "#{child_pid}"}
|
118
|
+
rescue Exception => e
|
119
|
+
puts "Failed to write PID file #{pidfile}\n#{e}\n\nPID #{child_pid}"
|
120
|
+
end
|
121
|
+
else
|
122
|
+
puts "PID #{child_pid}"
|
123
|
+
end
|
124
|
+
|
125
|
+
exit! 0
|
126
|
+
end
|
127
|
+
|
128
|
+
Process.setsid
|
129
|
+
|
130
|
+
rescue Exception
|
131
|
+
puts "Platform (#{RUBY_PLATFORM}) does not appear to support fork/setsid; skipping"
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.postprocess_config_load(config)
|
135
|
+
config[Cmap] = [] unless Array === config[Cmap]
|
136
|
+
config[Ctimeout] ||= 3
|
137
|
+
config[Cmap].each do |m|
|
138
|
+
m[Ckeepalive] = true unless m.has_key?(Ckeepalive)
|
139
|
+
m[Ckeepalive] = !!m[Ckeepalive]
|
140
|
+
m[Coutgoing] = [m[Coutgoing]] unless Array === m[Coutgoing]
|
141
|
+
m[Cincoming] = [m[Cincoming]] unless Array === m[Cincoming]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.verify_config(config)
|
146
|
+
return nil if config[Ccluster_address].nil?
|
147
|
+
return nil if config[Ccluster_port].nil?
|
148
|
+
|
149
|
+
if config[Cbackend_address] and config[Cbackend_port]
|
150
|
+
config[Cmap] << {Cincoming => nil, Curl => nil, Coutgoing => "#{config[Cbackend_address]}:#{config[Cbackend_port]}", Ckeepalive => true}
|
151
|
+
end
|
152
|
+
true
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.run
|
156
|
+
config = parse_options
|
157
|
+
daemonize(config['pidfile']) if config[Cdaemonize]
|
158
|
+
Swiftcore::Swiftiply.run(config)
|
159
|
+
end
|
160
|
+
|
161
|
+
def self.on_windows?
|
162
|
+
return @on_windows unless @on_windows.nil?
|
163
|
+
@on_windows = !![/mswin/i, /cygwin/i, /mingw/i, /bccwin/i, /wince/i].find {|p| RUBY_PLATFORM =~ p}
|
164
|
+
end
|
165
|
+
end
|
147
166
|
end
|
148
167
|
|
149
168
|
Swiftcore::SwiftiplyExec.run
|
169
|
+
exit 0
|