experella-proxy 0.0.6

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 (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