experella-proxy 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +15 -0
  2. data/Gemfile +3 -0
  3. data/README.md +219 -0
  4. data/Rakefile +25 -0
  5. data/TODO.txt +20 -0
  6. data/bin/experella-proxy +54 -0
  7. data/config/default/404.html +16 -0
  8. data/config/default/503.html +18 -0
  9. data/config/default/config.rb +64 -0
  10. data/config/default/ssl/certs/experella-proxy.pem +18 -0
  11. data/config/default/ssl/private/experella-proxy.key +28 -0
  12. data/dev/experella-proxy +62 -0
  13. data/experella-proxy.gemspec +39 -0
  14. data/lib/experella-proxy/backend.rb +58 -0
  15. data/lib/experella-proxy/backend_server.rb +100 -0
  16. data/lib/experella-proxy/configuration.rb +154 -0
  17. data/lib/experella-proxy/connection.rb +557 -0
  18. data/lib/experella-proxy/connection_manager.rb +167 -0
  19. data/lib/experella-proxy/globals.rb +37 -0
  20. data/lib/experella-proxy/http_status_codes.rb +45 -0
  21. data/lib/experella-proxy/proxy.rb +61 -0
  22. data/lib/experella-proxy/request.rb +106 -0
  23. data/lib/experella-proxy/response.rb +204 -0
  24. data/lib/experella-proxy/server.rb +68 -0
  25. data/lib/experella-proxy/version.rb +15 -0
  26. data/lib/experella-proxy.rb +93 -0
  27. data/spec/echo-server/echo_server.rb +49 -0
  28. data/spec/experella-proxy/backend_server_spec.rb +101 -0
  29. data/spec/experella-proxy/configuration_spec.rb +27 -0
  30. data/spec/experella-proxy/connection_manager_spec.rb +159 -0
  31. data/spec/experella-proxy/experella-proxy_spec.rb +471 -0
  32. data/spec/experella-proxy/request_spec.rb +88 -0
  33. data/spec/experella-proxy/response_spec.rb +44 -0
  34. data/spec/fixtures/404.html +16 -0
  35. data/spec/fixtures/503.html +18 -0
  36. data/spec/fixtures/spec.log +331 -0
  37. data/spec/fixtures/test_config.rb +34 -0
  38. data/spec/spec.log +235 -0
  39. data/spec/spec_helper.rb +35 -0
  40. data/test/sinatra/hello_world_server.rb +17 -0
  41. data/test/sinatra/server_one.rb +89 -0
  42. data/test/sinatra/server_two.rb +89 -0
  43. metadata +296 -0
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ #IDE
2
+
3
+ .idea
4
+
5
+ #DEV
6
+ .ruby-gemset
7
+ .ruby-version
8
+
9
+ #LOGS
10
+ *.log
11
+
12
+ .rspec
13
+
14
+ Gemfile.lock
15
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
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
@@ -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-----
@@ -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