experella-proxy 0.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +15 -0
- data/Gemfile +3 -0
- data/README.md +219 -0
- data/Rakefile +25 -0
- data/TODO.txt +20 -0
- data/bin/experella-proxy +54 -0
- data/config/default/404.html +16 -0
- data/config/default/503.html +18 -0
- data/config/default/config.rb +64 -0
- data/config/default/ssl/certs/experella-proxy.pem +18 -0
- data/config/default/ssl/private/experella-proxy.key +28 -0
- data/dev/experella-proxy +62 -0
- data/experella-proxy.gemspec +39 -0
- data/lib/experella-proxy/backend.rb +58 -0
- data/lib/experella-proxy/backend_server.rb +100 -0
- data/lib/experella-proxy/configuration.rb +154 -0
- data/lib/experella-proxy/connection.rb +557 -0
- data/lib/experella-proxy/connection_manager.rb +167 -0
- data/lib/experella-proxy/globals.rb +37 -0
- data/lib/experella-proxy/http_status_codes.rb +45 -0
- data/lib/experella-proxy/proxy.rb +61 -0
- data/lib/experella-proxy/request.rb +106 -0
- data/lib/experella-proxy/response.rb +204 -0
- data/lib/experella-proxy/server.rb +68 -0
- data/lib/experella-proxy/version.rb +15 -0
- data/lib/experella-proxy.rb +93 -0
- data/spec/echo-server/echo_server.rb +49 -0
- data/spec/experella-proxy/backend_server_spec.rb +101 -0
- data/spec/experella-proxy/configuration_spec.rb +27 -0
- data/spec/experella-proxy/connection_manager_spec.rb +159 -0
- data/spec/experella-proxy/experella-proxy_spec.rb +471 -0
- data/spec/experella-proxy/request_spec.rb +88 -0
- data/spec/experella-proxy/response_spec.rb +44 -0
- data/spec/fixtures/404.html +16 -0
- data/spec/fixtures/503.html +18 -0
- data/spec/fixtures/spec.log +331 -0
- data/spec/fixtures/test_config.rb +34 -0
- data/spec/spec.log +235 -0
- data/spec/spec_helper.rb +35 -0
- data/test/sinatra/hello_world_server.rb +17 -0
- data/test/sinatra/server_one.rb +89 -0
- data/test/sinatra/server_two.rb +89 -0
- metadata +296 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,219 @@
|
|
1
|
+
#Experella-Proxy
|
2
|
+
|
3
|
+
A balancing EventMachine reverse proxy based on [em-proxy](https://github.com/igrigorik/em-proxy)
|
4
|
+
|
5
|
+
Configurable in pure ruby!
|
6
|
+
|
7
|
+
Supports:
|
8
|
+
|
9
|
+
+ Persistent connections and HTTP Pipelining for clients
|
10
|
+
+ Response streaming
|
11
|
+
+ Post Request data streaming
|
12
|
+
+ Request header routing logic completely configurable for each server
|
13
|
+
+ Request header manipulation completely configurable for each server
|
14
|
+
+ Daemonized control using ruby [Daemons](http://daemons.rubyforge.org/)
|
15
|
+
+ TLS support and a default self-signed ssl certification
|
16
|
+
|
17
|
+
Proxy uses [http_parser](https://github.com/tmm1/http_parser.rb) to parse http data and is thereby subject to the parsers restrictions
|
18
|
+
|
19
|
+
The proxy is build for low proxy to server latency and does not support persistent connections to servers. Keep that in mind
|
20
|
+
as i can severely influence proxy performance overhead.
|
21
|
+
|
22
|
+
It balances for every single http-request and not per client/connection.
|
23
|
+
|
24
|
+
##Install as Gem
|
25
|
+
|
26
|
+
To use experella-proxy simply install it as a gem.
|
27
|
+
|
28
|
+
```
|
29
|
+
gem install experella-proxy
|
30
|
+
```
|
31
|
+
|
32
|
+
##How to start
|
33
|
+
|
34
|
+
Experella-Proxy is controlled by ruby Daemons default commands (run, start, restart, stop) and provides
|
35
|
+
a template config initialization command (init destination).
|
36
|
+
|
37
|
+
run, start and restart require an absolute config file path.
|
38
|
+
|
39
|
+
To initialize the proxy with default config files init the proxy to a directory of your choice.
|
40
|
+
|
41
|
+
For example
|
42
|
+
|
43
|
+
```
|
44
|
+
$> experella-proxy init ~
|
45
|
+
```
|
46
|
+
|
47
|
+
will initialize default config to $HOME/proxy
|
48
|
+
|
49
|
+
|
50
|
+
Then simply use:
|
51
|
+
|
52
|
+
```
|
53
|
+
$> experella-proxy start -- --config=~/proxy/config.rb
|
54
|
+
$> experella-proxy restart -- --config=~/proxy/config.rb
|
55
|
+
$> experella-proxy stop
|
56
|
+
```
|
57
|
+
to control the proxy with the default config file.
|
58
|
+
|
59
|
+
|
60
|
+
### BASE_PORT option for default config
|
61
|
+
|
62
|
+
To set other base port you have to prefix the previous commands with BASE_PORT=xxxx e.g.
|
63
|
+
|
64
|
+
Running ports below 1024 probably requires "rvmsudo" to run properly
|
65
|
+
|
66
|
+
```
|
67
|
+
$> BASE_PORT=3000 experella-proxy start -- --config=~/proxy/config.rb
|
68
|
+
```
|
69
|
+
|
70
|
+
##Config file
|
71
|
+
|
72
|
+
You need to provide a valid config file for the proxy to run.
|
73
|
+
|
74
|
+
Config files use a ruby DSL with the following options
|
75
|
+
|
76
|
+
###Backend Server
|
77
|
+
|
78
|
+
Each server is configured independent of other servers, so each desired routing dependency has to be added manually.
|
79
|
+
i.e. if you want to route an inimitable request to an unique backend, you have to exclude that match in all other servers.
|
80
|
+
|
81
|
+
|
82
|
+
```
|
83
|
+
backend Takes an options hash defining a backend_server
|
84
|
+
required keys => :host Host address of the Server, can be IP or domain, String
|
85
|
+
:port Port of the Server, Integervalue as String
|
86
|
+
|
87
|
+
optional keys => :name Name of the Server used in the Logger and Cashing, String
|
88
|
+
Will default to #{host}:#{port} but needs to be unique, though theoretical there can be
|
89
|
+
multiple servers with the same host:port value pair!
|
90
|
+
|
91
|
+
:concurrency max Number of concurrent connections, Integervalue as String, Default is 1
|
92
|
+
|
93
|
+
:accepts Hash containing keys matching HTTP headers or URI :path, :port, :query
|
94
|
+
Values are Regexp as Strings or as Regex, use ^((?!pattern).)*$ to negate matching
|
95
|
+
Care: Will match any Header/value pairs not defined in the accepts Hash
|
96
|
+
|
97
|
+
:mangle Hash containing keys matching HTTP headers. Values can be callable block or Strings
|
98
|
+
Mangle modifies the header value based on the given block
|
99
|
+
or replaces the header value with the String
|
100
|
+
```
|
101
|
+
|
102
|
+
####Example
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
backend(:name => "Srv1", :host => "192.168.0.10", :port => "80", :concurrency => "1",
|
106
|
+
:accepts => {"request_url" => "^((?!/(#{not-for-srv1})($|/)).)*$"},
|
107
|
+
:mangle => {"Host" => lambda{ |host|
|
108
|
+
if host.match(/localhost/)
|
109
|
+
'www.host-i-need.com'
|
110
|
+
else
|
111
|
+
host
|
112
|
+
end
|
113
|
+
}
|
114
|
+
}
|
115
|
+
)
|
116
|
+
```
|
117
|
+
###Logging
|
118
|
+
|
119
|
+
```
|
120
|
+
set_logger specifies the Logger used by the program. The Logger must support debug/info/warn/error/fatal functions
|
121
|
+
```
|
122
|
+
|
123
|
+
###Proxy Server
|
124
|
+
|
125
|
+
```
|
126
|
+
set_proxy Add proxy as Hash with :host => "string-ip/domain", :port => Fixnum, :options => Hash
|
127
|
+
The proxy will listen on every host:port hash added with this function.
|
128
|
+
:options can activate :tls with given file paths to :private_key_file and :cert_chain_file
|
129
|
+
file paths are relative to the config file directory
|
130
|
+
```
|
131
|
+
####Example
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
set_proxy(:host => "127.0.0.1", :port => 8080)
|
135
|
+
set_proxy(:host => "127.0.0.1", :port => 443,
|
136
|
+
:options => {:tls => true,
|
137
|
+
:private_key_file => 'ssl/private/experella_proxy.key',
|
138
|
+
:cert_chain_file => 'ssl/certs/experella_proxy.pem'})
|
139
|
+
set_proxy(:host => "127.0.0.2", :port => 8080)
|
140
|
+
set_proxy(:host => "192.168.100.168", :port => 6666)
|
141
|
+
```
|
142
|
+
|
143
|
+
###Connection timeout
|
144
|
+
|
145
|
+
```
|
146
|
+
set_timeout Time as float when an idle persistent connection gets closed (no receive/send events occured)
|
147
|
+
```
|
148
|
+
|
149
|
+
###Error pages
|
150
|
+
|
151
|
+
```
|
152
|
+
set_error_pages Add html error-pages to the proxy, requires 2 arguments
|
153
|
+
1st arg: the error code as Fixnum
|
154
|
+
2nd arg: path to an error page html file relative to the config file directory
|
155
|
+
Currently 404 and 503 error codes are supported
|
156
|
+
```
|
157
|
+
|
158
|
+
####Example
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
set_error_pages(404, "404.html")
|
162
|
+
set_error_pages(503, "503.html")
|
163
|
+
```
|
164
|
+
|
165
|
+
|
166
|
+
## Modify connection logic and data streams
|
167
|
+
|
168
|
+
Override server's run function
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
def run
|
172
|
+
|
173
|
+
Proxy.start(options = {}) do |conn|
|
174
|
+
|
175
|
+
log.info msec + "new Connection @" + signature.to_s
|
176
|
+
|
177
|
+
# called on successful backend connection
|
178
|
+
conn.on_connect do |backend|
|
179
|
+
|
180
|
+
end
|
181
|
+
|
182
|
+
# modify / process request stream
|
183
|
+
# and return modified data
|
184
|
+
conn.on_data do |data|
|
185
|
+
data
|
186
|
+
end
|
187
|
+
|
188
|
+
# modify / process response stream+
|
189
|
+
# and return modified response
|
190
|
+
conn.on_response do |backend, resp|
|
191
|
+
resp
|
192
|
+
end
|
193
|
+
|
194
|
+
# termination logic
|
195
|
+
conn.on_finish do |backend|
|
196
|
+
|
197
|
+
end
|
198
|
+
|
199
|
+
# called if client finishes connection
|
200
|
+
conn.on_unbind do
|
201
|
+
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
## Additional Information
|
209
|
+
|
210
|
+
+ [em-proxy](https://github.com/igrigorik/em-proxy)
|
211
|
+
+ [http_parser](https://github.com/tmm1/http_parser.rb)
|
212
|
+
+ [Eventmachine](https://github.com/eventmachine/eventmachine)
|
213
|
+
+ [Daemons](http://daemons.rubyforge.org/)
|
214
|
+
+ [What proxies must do](http://www.mnot.net/blog/2011/07/11/what_proxies_must_do)
|
215
|
+
|
216
|
+
|
217
|
+
## License
|
218
|
+
|
219
|
+
MIT License - Copyright (c) 2014 Dennis-Florian Herr @Experteer GmbH
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'yard'
|
4
|
+
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
YARD::Rake::YardocTask.new(:yardoc)
|
8
|
+
|
9
|
+
desc "Start Sinatra Server one on port 4567"
|
10
|
+
task :sinatra_one do
|
11
|
+
require 'test/sinatra/server_one'
|
12
|
+
ServerOne.run!(:port => 4567)
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Start Sinatra Server two port 4568"
|
16
|
+
task :sinatra_two do
|
17
|
+
require 'test/sinatra/server_two'
|
18
|
+
ServerTwo.run!(:port => 4568)
|
19
|
+
end
|
20
|
+
|
21
|
+
desc "Start Hello World! Sinatra Server port 4569"
|
22
|
+
task :sinatra_hello_world do
|
23
|
+
require 'test/sinatra/hello_world_server'
|
24
|
+
HelloWorldServer.run!(:port => 4569)
|
25
|
+
end
|
data/TODO.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
TODO:
|
2
|
+
|
3
|
+
General:
|
4
|
+
|
5
|
+
- Benchmarks for readme
|
6
|
+
- Configuration routing debugging tool
|
7
|
+
|
8
|
+
Performance:
|
9
|
+
|
10
|
+
- improve server matching
|
11
|
+
* avoid negative regexp, e.g. add reject option instead of negative accept
|
12
|
+
* group servers which match same requests
|
13
|
+
* probably add a option for "fallback" servers
|
14
|
+
|
15
|
+
Features:
|
16
|
+
|
17
|
+
- self configuring backends
|
18
|
+
* detect disconnected backends
|
19
|
+
- improve middleware support
|
20
|
+
- SPDY
|
data/bin/experella-proxy
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
lib = File.expand_path(File.join(File.dirname(__FILE__), '../lib/'))
|
4
|
+
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
ARGV << '--help' if ARGV.empty?
|
8
|
+
|
9
|
+
if ARGV[0] == "init"
|
10
|
+
if ARGV[1] && File.directory?(File.expand_path(ARGV[1]))
|
11
|
+
source_dir = File.expand_path(File.join(File.dirname(__FILE__), "/../config/default/"))
|
12
|
+
dest_dir = File.join(File.expand_path(ARGV[1]), "proxy")
|
13
|
+
FileUtils.copy_entry source_dir, dest_dir
|
14
|
+
puts "Initialized template config to #{dest_dir}"
|
15
|
+
else
|
16
|
+
puts "ERROR: #{File.expand_path(ARGV[1])} is not a directory"
|
17
|
+
puts "Please provide an existing directory to initialize to as second argument"
|
18
|
+
end
|
19
|
+
elsif ["start", "stop", "restart", "reload", "run", "zap", "status"].include? ARGV[0]
|
20
|
+
require 'daemons'
|
21
|
+
Daemons.run(File.join(lib, 'experella-proxy.rb'))
|
22
|
+
else
|
23
|
+
unless ARGV[0] == "--help" || ARGV[0] == "-h"
|
24
|
+
puts "ERROR no command given"
|
25
|
+
puts
|
26
|
+
end
|
27
|
+
puts "To control the proxy use: experella-proxy <command> <options> -- <application options>"
|
28
|
+
puts
|
29
|
+
puts "* where <command> is one of Daemons:"
|
30
|
+
puts " start start an instance of the application"
|
31
|
+
puts " stop stop all instances of the application"
|
32
|
+
puts " restart stop all instances and restart them afterwards"
|
33
|
+
puts " reload send a SIGHUP to all instances of the application"
|
34
|
+
puts " run start the application and stay on top"
|
35
|
+
puts " zap set the application to a stopped state"
|
36
|
+
puts " status show status (PID) of application instances"
|
37
|
+
puts
|
38
|
+
puts "* and where <options> may contain several of the following:"
|
39
|
+
puts " -t, --ontop Stay on top (does not daemonize)"
|
40
|
+
puts " -f, --force Force operation"
|
41
|
+
puts " -n, --no_wait Do not wait for processes to stop"
|
42
|
+
puts
|
43
|
+
puts "* and where <applicaion options> are"
|
44
|
+
puts " --config=CONFIGFILE absolute path to the proxy config file"
|
45
|
+
puts
|
46
|
+
puts
|
47
|
+
puts "To initialize the proxy use: experella-proxy init <path>"
|
48
|
+
puts
|
49
|
+
puts "* where <path> is the location the config files get copied to"
|
50
|
+
puts
|
51
|
+
puts "Common options: "
|
52
|
+
puts " -h, --help Show this message"
|
53
|
+
puts " --version Show daemons version"
|
54
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<style type="text/css"> body {
|
5
|
+
text-align: center;
|
6
|
+
font-family: helvetica, arial;
|
7
|
+
font-size: 22px;
|
8
|
+
color: #000;
|
9
|
+
margin: 20px
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
</head>
|
13
|
+
<body>
|
14
|
+
<h2>404 Page not found!</h2>
|
15
|
+
</body>
|
16
|
+
</html>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<style type="text/css"> body {
|
5
|
+
text-align: center;
|
6
|
+
font-family: helvetica, arial;
|
7
|
+
font-size: 22px;
|
8
|
+
color: #000;
|
9
|
+
margin: 20px
|
10
|
+
|
11
|
+
}
|
12
|
+
|
13
|
+
</style>
|
14
|
+
</head>
|
15
|
+
<body>
|
16
|
+
<h2> 503 Service unavailable!</h2>
|
17
|
+
</body>
|
18
|
+
</html>
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#Backend Server
|
2
|
+
base_backend_port=(ENV["BASE_PORT"] : 4000).to_i
|
3
|
+
backend1_port=base_backend_port+1
|
4
|
+
backend2_port=base_backend_port+2
|
5
|
+
|
6
|
+
# add servers in schema, :name, :host, :port, :concurrency,
|
7
|
+
# :host and :port are required, everything else is optional though name needs to be unique!
|
8
|
+
# :accepts {"header" => regex} hash, :mangle {"header" => "string"||lambda do |header-value|}
|
9
|
+
# accepts hash can additionally use uri :port, :path, :query for more efficient comparison
|
10
|
+
# mangle hash can modify any http headers, use strings for simple replace, use lambda for logic
|
11
|
+
# accepts and mangle are optional, but care: not using accepts means the server accepts every request and backends will
|
12
|
+
# always ignore the settings of any other backend server.
|
13
|
+
# use ^((?!pattern).)*$ to negate matching
|
14
|
+
|
15
|
+
#template backend servers
|
16
|
+
|
17
|
+
backend(:name => "srv1", :host => "localhost", :port => backend1_port, :concurrency => "1000",
|
18
|
+
:accepts => {"request_url" => "^((?!/(srv2)($|/)).)*$"}
|
19
|
+
)
|
20
|
+
|
21
|
+
backend(:name => "srv2", :host_port => "localhost:#{backend2_port}", :concurrency => "1",
|
22
|
+
:accepts => {"request_url" => "/(srv2)($|/)"},
|
23
|
+
:mangle => {"Host" => lambda { |host|
|
24
|
+
if host.match(/127.0.0/)
|
25
|
+
'localhost'
|
26
|
+
else
|
27
|
+
host
|
28
|
+
end
|
29
|
+
}
|
30
|
+
}
|
31
|
+
)
|
32
|
+
|
33
|
+
# experimental!
|
34
|
+
# web support if used as a forward proxy. care, not all webservers accept http-proxy standard (e.g. wikimedia)
|
35
|
+
# Host and Port will be set according to Requests Host header instead of :host and :port keys.
|
36
|
+
# backend(:name => "web", :host => "0.0.0.0", :port => "80", :concurrency => "1000",
|
37
|
+
# :accepts => {"Host" => "^((?!localhost).)*$"}
|
38
|
+
# )
|
39
|
+
|
40
|
+
require 'logger'
|
41
|
+
|
42
|
+
# set a logger. Has to support debug/info/warn/error/fatal logger functions
|
43
|
+
set_logger Logger.new($stdout)
|
44
|
+
logger.level = Logger::WARN
|
45
|
+
|
46
|
+
# set proxy servers here, listens on every host, port hash pair
|
47
|
+
# you can add one pair per call to set_proxy(hsh)
|
48
|
+
# additionally you can activate ssl with provided private_key and cert_chain files
|
49
|
+
set_proxy(:host => "localhost", :port => base_backend_port)
|
50
|
+
set_proxy(:host => "localhost", :port => 443,
|
51
|
+
:options => {:tls => true,
|
52
|
+
:private_key_file => 'ssl/private/experella-proxy.key',
|
53
|
+
:cert_chain_file => 'ssl/certs/experella-proxy.pem'}
|
54
|
+
)
|
55
|
+
|
56
|
+
|
57
|
+
# set the timeout in seconds. Will unbind a keep-alive connection
|
58
|
+
# if no send/receive event occured in specified seconds
|
59
|
+
set_timeout(30.0)
|
60
|
+
|
61
|
+
# provide errorpage locations. first arguument is error code, second is (html) file location in configfile folder
|
62
|
+
set_error_pages(404, "404.html")
|
63
|
+
set_error_pages(503, "503.html")
|
64
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIC4TCCAcmgAwIBAgIJAN8pDjDjYU8xMA0GCSqGSIb3DQEBBQUAMBQxEjAQBgNV
|
3
|
+
BAMTCWxvY2FsaG9zdDAeFw0xNDAxMjIxNTI2NTZaFw0yNDAxMjAxNTI2NTZaMBQx
|
4
|
+
EjAQBgNVBAMTCWxvY2FsaG9zdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC
|
5
|
+
ggEBAMOf6ah6DhHpIjEkJDrNVddo82Z+Bzk3OCyqaGhyWSDN7kQermErxqw0ioeK
|
6
|
+
1p6j+od1aVcwjWds6TdRjPPlIb1ZmlAhwrCedF7TAN76jRVoay4reRzA5SJ4lR9i
|
7
|
+
BkpYJbE7bx1l16dvnpwfP44thiPp4jHGwtkgZCnUsqyIW03Ssn6P5hGQqSBgy6GO
|
8
|
+
Hb0ZuyyVIENCHaRMuFjjU4jrZzwcIt/nVWx2M1njxFDHt2FLpvtKtGkp8abrrQO6
|
9
|
+
TeEpTUDQcncrMzXhm80Kr6PY9FVNauuOpWZXhbK0lndxyXQC3eN+8eeCFDV0EfU8
|
10
|
+
pRkc+dfGFBI8zuHZY0VSeUsc8uMCAwEAAaM2MDQwCQYDVR0TBAIwADAnBgNVHREE
|
11
|
+
IDAehwR/AAABhwR/AAAChwR/AAADhwR/AAAEhwR/AAAFMA0GCSqGSIb3DQEBBQUA
|
12
|
+
A4IBAQBv4/A9F7lQ/TGVO28RoWGZrCouMhkmVC/mo1SQxpkGY0VlydU04mWAiayv
|
13
|
+
32Fyy0SPq/2YgpFzAyXLVZ2hO/4NEHjjpd1utHWEXxVGy8nqMS73y7XOuc7bkT7V
|
14
|
+
uqvxA0WgwtD9EOG+XIiRszybcR/C1Dpg37Ct9ZA1wmFQr5C19Hl0ki6PKkaGuy/I
|
15
|
+
NGMtdgxGSKzCGTwQD7rclUbvf5QZYN+uoyZoC3yJ47sAER8CMpAYTl/GU34vL7p2
|
16
|
+
NJiPH3Y570l6II96JU7nsdC6UA4sUx/H9HhDsd0TSXtLONqQU03M9qDckgmf+x0C
|
17
|
+
oIjTcEDbcsGuPuo6j1HYrAzNEkso
|
18
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,28 @@
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
2
|
+
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDDn+moeg4R6SIx
|
3
|
+
JCQ6zVXXaPNmfgc5Nzgsqmhoclkgze5EHq5hK8asNIqHitaeo/qHdWlXMI1nbOk3
|
4
|
+
UYzz5SG9WZpQIcKwnnRe0wDe+o0VaGsuK3kcwOUieJUfYgZKWCWxO28dZdenb56c
|
5
|
+
Hz+OLYYj6eIxxsLZIGQp1LKsiFtN0rJ+j+YRkKkgYMuhjh29GbsslSBDQh2kTLhY
|
6
|
+
41OI62c8HCLf51VsdjNZ48RQx7dhS6b7SrRpKfGm660Duk3hKU1A0HJ3KzM14ZvN
|
7
|
+
Cq+j2PRVTWrrjqVmV4WytJZ3ccl0At3jfvHnghQ1dBH1PKUZHPnXxhQSPM7h2WNF
|
8
|
+
UnlLHPLjAgMBAAECggEACqmInrooUimWx37kyp+uchMyUP/FfQTZdvXCww6YdQVE
|
9
|
+
W0ogzwMHzRMACeszT2o4mXF40FvBGUYCYBV9zT1L3Xoowv1UEzRPu2tcvMqDLguG
|
10
|
+
6/lKnJqW4o4X8qHxHdQ4GbTXlY6bdnDLAE9js2gfy41P2s6uiA8P5ofFRUoZT+B3
|
11
|
+
jczy86d5FFD0RYnFOIAxR2s1MNKzbh+400L+PdLu3p8+PsdCgFD703eyCtnLIxFm
|
12
|
+
NkGNpNnBwKSD3kufwOqtht+iIOrpR2LwBAZszO7h4P9/1gwX++296sq4uK7px3hh
|
13
|
+
2n/vnv/k/IB0Ewp20vyVibTzrJees+A2x28GduW6wQKBgQDv/pJ/EakoMDWs5fFq
|
14
|
+
4DFaiy+YznE5iUce74yxZusxTaCBMacbPYWLqs3ZtZ2weo161XLcyNEKkOALoslt
|
15
|
+
4tcLuLlzbBBR3o0Z6ggmXgvgrvtd4hN7IfjcxzRp7jAhlZx2j7JfXiNsmJ+yQ+UZ
|
16
|
+
FJejcaf4zNUtD1tP/Pc91bwCWwKBgQDQq9Cgk/DpTXp8lVaNbrk8QC3e48RCXLsj
|
17
|
+
qUeeWPVEcMSr2yafH38fMdfi+FhTYbAIn/uvCHPzPbsJ0DT04cQtWmztOuex4A7j
|
18
|
+
biiazkfbwb4ROe8fXzGFDCLAyhItkyLwuXCvR8GO7iRQiQaSOHOGrsEgCK40WU6V
|
19
|
+
dTwiqq+oGQKBgQDZd0tV2zH1maG5lu7oUlhGXGL3yvEsGqmYbaaGReUOWvH6v3xV
|
20
|
+
oay2P0KiaQCHnrU+vdWEHG/XbCmoQYzWhrsoAu4AmcY7+TbQaZGS+ps2sDhT/Px8
|
21
|
+
Ee+IVvW2mz9/3yrVACa4ZfwhdjcW+JDK5i71ypJTT4BHgWfTljyPCnJ/iwKBgQCC
|
22
|
+
7huEde8mY//j5cweCAhgHqDphp5hSU/+qzuNxqF2ZQ9yCZrtItUfnKWp+0toXhYX
|
23
|
+
A1LnW3VtfSE7ohFoSbRPBLruCxTPzhdpang90grSf3kj0+cPFyq6IYg40VGgmP7G
|
24
|
+
boSe5nYzmYbdpWyjGJqcOlBhKjEsFZEyRBIMam63GQKBgC76YY/TEoPV7BPcvaDc
|
25
|
+
4p7d4YC+KLidy6tyOx2hn8a0FAUmgrQP0Jbl9pMf+oVTGJm460ASW+z6ZQjilx0s
|
26
|
+
4PCGWXNioJJOdTWRRHB2RjrHOU3MXKbBJB4JBg4n+fBtEEE9nP0orCpsDVnGSlep
|
27
|
+
t7n4VlIzT0CrlpXYiJQaG6hq
|
28
|
+
-----END PRIVATE KEY-----
|
data/dev/experella-proxy
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# binary file for developers
|
4
|
+
require 'rubygems'
|
5
|
+
require 'bundler'
|
6
|
+
Bundler.setup
|
7
|
+
|
8
|
+
#NOTE: This is exactly the same as /bin/experella-proxy from here
|
9
|
+
|
10
|
+
lib = File.expand_path(File.join(File.dirname(__FILE__), '../lib/'))
|
11
|
+
|
12
|
+
require 'fileutils'
|
13
|
+
|
14
|
+
ARGV << '--help' if ARGV.empty?
|
15
|
+
|
16
|
+
if ARGV[0] == "init"
|
17
|
+
if ARGV[1] && File.directory?(File.expand_path(ARGV[1]))
|
18
|
+
source_dir = File.expand_path(File.join(File.dirname(__FILE__), "/../config/default/"))
|
19
|
+
dest_dir = File.join(File.expand_path(ARGV[1]), "proxy")
|
20
|
+
FileUtils.copy_entry source_dir, dest_dir
|
21
|
+
puts "Initialized template config to #{dest_dir}"
|
22
|
+
else
|
23
|
+
puts "ERROR: #{File.expand_path(ARGV[1])} is not a directory"
|
24
|
+
puts "Please provide an existing directory to initialize to as second argument"
|
25
|
+
end
|
26
|
+
elsif ["start", "stop", "restart", "reload", "run", "zap", "status"].include? ARGV[0]
|
27
|
+
require 'daemons'
|
28
|
+
Daemons.run(File.join(lib, 'experella-proxy.rb'))
|
29
|
+
else
|
30
|
+
unless ARGV[0] == "--help" || ARGV[0] == "-h"
|
31
|
+
puts "ERROR no command given"
|
32
|
+
puts
|
33
|
+
end
|
34
|
+
puts "To control the proxy use: experella-proxy <command> <options> -- <application options>"
|
35
|
+
puts
|
36
|
+
puts "* where <command> is one of Daemons:"
|
37
|
+
puts " start start an instance of the application"
|
38
|
+
puts " stop stop all instances of the application"
|
39
|
+
puts " restart stop all instances and restart them afterwards"
|
40
|
+
puts " reload send a SIGHUP to all instances of the application"
|
41
|
+
puts " run start the application and stay on top"
|
42
|
+
puts " zap set the application to a stopped state"
|
43
|
+
puts " status show status (PID) of application instances"
|
44
|
+
puts
|
45
|
+
puts "* and where <options> may contain several of the following:"
|
46
|
+
puts " -t, --ontop Stay on top (does not daemonize)"
|
47
|
+
puts " -f, --force Force operation"
|
48
|
+
puts " -n, --no_wait Do not wait for processes to stop"
|
49
|
+
puts
|
50
|
+
puts "* and where <applicaion options> are"
|
51
|
+
puts " --config=CONFIGFILE absolute path to the proxy config file"
|
52
|
+
puts
|
53
|
+
puts
|
54
|
+
puts "To initialize the proxy use: experella-proxy init <path>"
|
55
|
+
puts
|
56
|
+
puts "* where <path> is the location the config files get copied to"
|
57
|
+
puts
|
58
|
+
puts "Common options: "
|
59
|
+
puts " -h, --help Show this message"
|
60
|
+
puts " --version Show daemons version"
|
61
|
+
end
|
62
|
+
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'experella-proxy/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "experella-proxy"
|
8
|
+
spec.version = ExperellaProxy::VERSION
|
9
|
+
spec.authors = ["Dennis-Florian Herr"]
|
10
|
+
spec.email = ["dennis.herr@experteer.com"]
|
11
|
+
spec.description = 'a balancing & routing proxy, see README for more details'
|
12
|
+
spec.summary = 'experella-proxy gem'
|
13
|
+
spec.homepage = "https://github.com/experteer/experella-proxy"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.add_runtime_dependency "daemons", "~> 1.1.4"
|
17
|
+
spec.add_runtime_dependency "eventmachine", "~> 1.0.3"
|
18
|
+
spec.add_runtime_dependency "http_parser.rb", "~> 0.5.3"
|
19
|
+
|
20
|
+
spec.add_development_dependency "rake", "~> 10.1.0"
|
21
|
+
# specs
|
22
|
+
spec.add_development_dependency "rspec", "2.14.1"
|
23
|
+
spec.add_development_dependency "posix-spawn"
|
24
|
+
spec.add_development_dependency "em-http-request"
|
25
|
+
# dev testing
|
26
|
+
spec.add_development_dependency "sinatra", "1.4.3"
|
27
|
+
spec.add_development_dependency "thin", "~> 1.5.1"
|
28
|
+
# documentation tool
|
29
|
+
spec.add_development_dependency "yard", "~> 0.8.7.3"
|
30
|
+
spec.add_development_dependency "redcarpet", "~> 2.3.0"
|
31
|
+
|
32
|
+
spec.files = Dir["bin/*"] + Dir["dev/*"] + Dir["lib/**/*"] + Dir["config/default/**/*"]
|
33
|
+
spec.files += Dir["spec/**/*"] + Dir["test/sinatra/*"]
|
34
|
+
spec.files += [".gitignore", "Gemfile", "experella-proxy.gemspec", "README.md", "TODO.txt", "Rakefile"]
|
35
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
36
|
+
spec.test_files = spec.files.grep(%r{^(test|spec)/})
|
37
|
+
spec.extra_rdoc_files = ['README.md']
|
38
|
+
spec.require_paths = ["lib"]
|
39
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ExperellaProxy
|
2
|
+
# The Proxies connection to the backend server
|
3
|
+
#
|
4
|
+
# This class will never be directly initiated by user code, but initializing gets triggered in client {Connection}
|
5
|
+
class Backend < EventMachine::Connection
|
6
|
+
|
7
|
+
include ExperellaProxy::Globals
|
8
|
+
|
9
|
+
# @!visibility private
|
10
|
+
attr_accessor :plexer, :name
|
11
|
+
|
12
|
+
# Called by the EventMachine loop when a remote TCP connection attempt completes successfully
|
13
|
+
#
|
14
|
+
# Calls client {Connection} {Connection#connected} method
|
15
|
+
#
|
16
|
+
def connection_completed
|
17
|
+
log.debug [@name, :conn_complete]
|
18
|
+
@plexer.connected(@name)
|
19
|
+
@connected.succeed
|
20
|
+
end
|
21
|
+
|
22
|
+
# Called by the EventMachine loop whenever data has been received by the network connection.
|
23
|
+
# It is never called by user code. {#receive_data} is called with a single parameter,
|
24
|
+
# a String containing the network protocol data, which may of course be binary.
|
25
|
+
#
|
26
|
+
# Data gets passed to client {Connection} through {Connection#relay_from_backend}
|
27
|
+
#
|
28
|
+
# @param data [String] Opaque response data
|
29
|
+
def receive_data(data)
|
30
|
+
log.debug [:receive_backend, @name, data]
|
31
|
+
@plexer.relay_from_backend(@name, data)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Buffer data for send until the connection to the backend server is established and is ready for use.
|
35
|
+
#
|
36
|
+
# @param data [String] data to be send to the connected backend server
|
37
|
+
def send(data)
|
38
|
+
log.debug [:send_backend, data]
|
39
|
+
@connected.callback { send_data data }
|
40
|
+
end
|
41
|
+
|
42
|
+
# Notify upstream plexer that the backend server is done processing the request
|
43
|
+
#
|
44
|
+
def unbind
|
45
|
+
log.debug [@name, :unbind]
|
46
|
+
@plexer.unbind_backend(@name)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# @private constructor, gets called by EventMachine::Connection's overwritten new method
|
52
|
+
#
|
53
|
+
def initialize
|
54
|
+
@connected = EM::DefaultDeferrable.new
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|