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
@@ -0,0 +1,167 @@
1
+ module ExperellaProxy
2
+
3
+ # static getter for the connection_manager variable
4
+ #
5
+ # @return [ConnectionManager] connection_manager
6
+ def self.connection_manager
7
+ @connection_manager
8
+ end
9
+
10
+ # The ConnectionManager is responsible for queueing and matching frontend {Connection} and {BackendServer} objects
11
+ class ConnectionManager
12
+
13
+ # The constructor
14
+ #
15
+ def initialize
16
+ @connection_queue = [] #array queue of client connection objects
17
+ @backend_queue = [] #array queue of available backend servers
18
+ @backend_list = {} #list of all backend servers
19
+ end
20
+
21
+ # Matches {Request} to queued {BackendServer}
22
+ #
23
+ # Removes first matching {BackendServer} from queue and returns it.
24
+ # It will requeue the {BackendServer} instantly,
25
+ # if {BackendServer#workload} is smaller than {BackendServer#concurrency}
26
+ #
27
+ # Queues {Request#conn} if no available {BackendServer} matches
28
+ #
29
+ # Returns false if no registered {BackendServer} matches
30
+ #
31
+ # @return [BackendServer] first matching BackendServer from the queue
32
+ # @return [Symbol] :queued if Connection was queued
33
+ # @return [Boolean] false if no registered Backend matches the Request
34
+ def backend_available?(request)
35
+ @backend_queue.each do |backend|
36
+ if backend.accept?(request)
37
+ #connect backend to requests connection if request matches
38
+ backend.workload += 1
39
+ ret = @backend_queue.delete(backend)
40
+ #requeue backend if concurrency isnt maxed
41
+ @backend_queue.push(backend) if backend.workload < backend.concurrency
42
+ return ret
43
+ end
44
+ end
45
+ if match_any_backend?(request)
46
+ #push requests connection on queue if no backend was connected
47
+ @connection_queue.push(request.conn)
48
+ :queued
49
+ else
50
+ false
51
+ end
52
+ end
53
+
54
+ # Called by a {Connection} when the {BackendServer} is done.
55
+ #
56
+ # Connects backend to a matching queued {Connection} or pushes server back on queue
57
+ #
58
+ # @param backend [BackendServer] BackendServer which got free
59
+ # @return [NilClass]
60
+ def free_backend(backend)
61
+ #check if any queued connections match new available backend
62
+ conn = match_connections(backend)
63
+ if conn
64
+ #return matching connection
65
+ #you should try to connect the new backend to this connection
66
+ return conn
67
+ else
68
+ #push free backend on queue if it wasn't used for a queued conn or is already queued (concurrency)
69
+ @backend_queue.push(backend) if @backend_list.include?(backend.name) && !@backend_queue.include?(backend)
70
+ backend.workload -= 1
71
+ end
72
+ nil
73
+ end
74
+
75
+ # Adds a new {BackendServer} to the list and queues or connects it
76
+ #
77
+ # @param backend [BackendServer] a new BackendServer
78
+ # @return [Connection] a queued connection that would match the BackendServer
79
+ # @return [Boolean] true if backend was added to list
80
+ def add_backend(backend)
81
+
82
+ @backend_list[backend.name] = backend
83
+
84
+ #check if any queued connections match new available backend
85
+ conn = match_connections(backend)
86
+ if conn
87
+ #return matching connection
88
+ #you should try to connect the new backend to this connection
89
+ return conn
90
+ else
91
+ #queue new backend
92
+ @backend_queue.push(backend)
93
+ end
94
+ true
95
+ end
96
+
97
+ # Removes a {BackendServer} from list and queue
98
+ #
99
+ # @param backend [BackendServer] the BackendServer to be removed
100
+ # @return [Boolean] true if a backend was removed, else returns false
101
+ def remove_backend(backend)
102
+
103
+ ret = @backend_list.delete(backend.name)
104
+ @backend_queue.delete(backend)
105
+
106
+ if ret
107
+ true
108
+ else
109
+ false
110
+ end
111
+ end
112
+
113
+ # Removes a connection from the connection_queue
114
+ #
115
+ # @param conn [Connection] Connection to be removed
116
+ def free_connection(conn)
117
+ @connection_queue.delete(conn)
118
+ end
119
+
120
+ # returns the count of the currently queued {BackendServer}s
121
+ #
122
+ # @return [int]
123
+ def backend_queue_count
124
+ @backend_queue.size
125
+ end
126
+
127
+ # returns the count of the registered{BackendServer}s
128
+ #
129
+ # @return [int]
130
+ def backend_count
131
+ @backend_list.size
132
+ end
133
+
134
+ # returns the count of the currently queued connections
135
+ #
136
+ # @return [int]
137
+ def connection_count
138
+ @connection_queue.size
139
+ end
140
+
141
+ private
142
+
143
+ # Matches request to all known backends
144
+ #
145
+ # @return [Boolean] true if it can be matched, false if request is not accepted at all
146
+ def match_any_backend?(request)
147
+ @backend_list.each_value do |v|
148
+ return true if v.accept?(request)
149
+ end
150
+ false
151
+ end
152
+
153
+ # Matches queued connections with a backend
154
+ #
155
+ # @return [Connection] matching connection
156
+ # @return [NilClass] nothing matched
157
+ def match_connections(backend)
158
+ @connection_queue.each do |conn|
159
+ if backend.accept?(conn.get_request)
160
+ return conn
161
+ end
162
+ end
163
+ nil
164
+ end
165
+
166
+ end
167
+ end
@@ -0,0 +1,37 @@
1
+ module ExperellaProxy
2
+
3
+ # defined hop by hop header fields
4
+ HOP_HEADERS = ["Connection", "Keep-Alive", "Proxy-Authorization", "TE", "Trailer", "Transfer-Encoding", "Upgrade"]
5
+
6
+ # Provides getters for global variables
7
+ #
8
+ # All methods are private. The module needs to be included in every Class which needs it.
9
+ module Globals
10
+
11
+ private
12
+
13
+ # @!visibility public
14
+
15
+ # Get the global config
16
+ #
17
+ # @return [Configuration] config object
18
+ def config
19
+ ExperellaProxy.config
20
+ end
21
+
22
+ # Get the global logger
23
+ #
24
+ # @return [Logger] logger set in config object
25
+ def log
26
+ ExperellaProxy.config.logger
27
+ end
28
+
29
+ # Get the global connection manager
30
+ #
31
+ # @return [ConnectionManager] connection_manager object
32
+ def connection_manager
33
+ ExperellaProxy.connection_manager
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,45 @@
1
+ module ExperellaProxy
2
+
3
+ # Every standard HTTP code mapped to the appropriate message.
4
+ #
5
+ # @author Mongrel.
6
+ HTTP_STATUS_CODES = {
7
+ 100 => 'Continue',
8
+ 101 => 'Switching Protocols',
9
+ 200 => 'OK',
10
+ 201 => 'Created',
11
+ 202 => 'Accepted',
12
+ 203 => 'Non-Authoritative Information',
13
+ 204 => 'No Content',
14
+ 205 => 'Reset Content',
15
+ 206 => 'Partial Content',
16
+ 300 => 'Multiple Choices',
17
+ 301 => 'Moved Permanently',
18
+ 302 => 'Moved Temporarily',
19
+ 303 => 'See Other',
20
+ 304 => 'Not Modified',
21
+ 305 => 'Use Proxy',
22
+ 400 => 'Bad Request',
23
+ 401 => 'Unauthorized',
24
+ 402 => 'Payment Required',
25
+ 403 => 'Forbidden',
26
+ 404 => 'Not Found',
27
+ 405 => 'Method Not Allowed',
28
+ 406 => 'Not Acceptable',
29
+ 407 => 'Proxy Authentication Required',
30
+ 408 => 'Request Time-out',
31
+ 409 => 'Conflict',
32
+ 410 => 'Gone',
33
+ 411 => 'Length Required',
34
+ 412 => 'Precondition Failed',
35
+ 413 => 'Request Entity Too Large',
36
+ 414 => 'Request-URI Too Large',
37
+ 415 => 'Unsupported Media Type',
38
+ 500 => 'Internal Server Error',
39
+ 501 => 'Not Implemented',
40
+ 502 => 'Bad Gateway',
41
+ 503 => 'Service Unavailable',
42
+ 504 => 'Gateway Time-out',
43
+ 505 => 'HTTP Version not supported'
44
+ }
45
+ end
@@ -0,0 +1,61 @@
1
+ module ExperellaProxy
2
+ # The proxy
3
+ #
4
+ # Controls the EventMachine, initializes backends from config and starts proxy servers
5
+ class Proxy
6
+ extend ExperellaProxy::Globals
7
+
8
+ # Starts the Eventmachine, initializes backends in {ConnectionManager} and starts the servers
9
+ # defined in config the proxy should listen on
10
+ #
11
+ # @param options [Hash] option Hash passed to the {Connection}
12
+ # @param blk [Block] Block evaluated in each new {Connection}
13
+ def self.start(options, &blk)
14
+
15
+ #initalize backend servers from config
16
+ config.backends.each do |backend|
17
+ connection_manager.add_backend(BackendServer.new(backend[:host], backend[:port], backend))
18
+ log.info "Initializing backend #{backend[:name]} at #{backend[:host]}:#{backend[:port]} with concurrency\
19
+ #{backend[:concurrency]}"
20
+ log.info "Backend accepts: #{backend[:accepts].inspect}"
21
+ log.info "Backend mangles: #{backend[:mangle].inspect}"
22
+ end
23
+
24
+ #start eventmachine
25
+ EM.epoll
26
+ EM.run do
27
+ trap("TERM") { stop }
28
+ trap("INT") { stop }
29
+
30
+ if config.proxy.empty?
31
+ log.fatal "No proxy host:port address configured. Stopping experella-proxy."
32
+ return stop
33
+ else
34
+ config.proxy.each do |proxy|
35
+ opts = options
36
+ # pass proxy specific options
37
+ unless proxy[:options].nil?
38
+ opts = options.merge(proxy[:options])
39
+ end
40
+ log.info "Launching experella-proxy at #{proxy[:host]}:#{proxy[:port]} with #{config.timeout}s timeout..."
41
+ log.info "with options: #{opts.inspect}"
42
+ EventMachine::start_server(proxy[:host], proxy[:port],
43
+ Connection, opts) do |conn|
44
+ conn.instance_eval(&blk)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+
51
+ #
52
+ # Stops the Eventmachine and terminates all connections
53
+ #
54
+ def self.stop
55
+ if EM.reactor_running?
56
+ log.info("Terminating experella-proxy")
57
+ EventMachine::stop_event_loop
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,106 @@
1
+ module ExperellaProxy
2
+ #
3
+ # Request is used to store incoming (HTTP) requests and parsed data
4
+ #
5
+ # Every Request belongs to a client {Connection}
6
+ #
7
+ class Request
8
+
9
+ include ExperellaProxy::Globals
10
+
11
+ attr_accessor :keep_alive, :chunked
12
+ attr_reader :conn, :header, :uri, :response
13
+
14
+ # The constructor
15
+ #
16
+ # @param conn [Connection] Connection the request belongs to
17
+ def initialize(conn)
18
+ @conn = conn
19
+ @header = {}
20
+ @chunked = false # if true the parsed body will be chunked
21
+ @uri = {} #contains port, path and query information for faster backend selection
22
+ @keep_alive = true
23
+ @send_buffer = String.new
24
+ @response = Response.new(self)
25
+ end
26
+
27
+ # Adds data to the request object
28
+ #
29
+ # data must be formatted as string
30
+ #
31
+ # @param str [String] data as string
32
+ def <<(str)
33
+ @send_buffer << str
34
+ end
35
+
36
+ # Adds a hash with uri information to {#uri}
37
+ #
38
+ # duplicate key values will be overwritten with hsh values
39
+ #
40
+ # @param hsh [Hash] hash with keys :port :path :query containing URI information
41
+ def add_uri(hsh)
42
+ @uri.update(hsh)
43
+ log.debug hsh
44
+ end
45
+
46
+ # Returns the data in send_buffer and empties the send_buffer
47
+ #
48
+ # @return [String] data to send
49
+ def flush
50
+ @send_buffer.slice!(0, @send_buffer.length)
51
+ end
52
+
53
+ # Returns if the send_buffer is flushed? (empty)
54
+ #
55
+ # @return [Boolean]
56
+ def flushed?
57
+ @send_buffer.empty?
58
+ end
59
+
60
+ # Reconstructs modified http request in send_buffer
61
+ #
62
+ # Reconstructed request must be a valid request according to the HTTP Protocol
63
+ #
64
+ # First Header after Startline will always be "Host: ", after that order is determined by {#header}.each
65
+ #
66
+ def reconstruct_header
67
+ #split send_buffer into header and body part
68
+ buf = @send_buffer.split(/\r\n\r\n/, 2) unless flushed?
69
+ @send_buffer = ""
70
+ #start line
71
+ @send_buffer << @header[:http_method] + ' '
72
+ @send_buffer << @header[:request_url] + ' '
73
+ @send_buffer << "HTTP/1.1\r\n"
74
+ @send_buffer << "Host: " + @header[:Host] + "\r\n" #add Host first for better header readability
75
+ #header fields
76
+ @header.each do |key, value|
77
+ unless key == :http_method || key == :request_url || key == :http_version || key == :Host #exclude startline parameters
78
+ @send_buffer << key.to_s + ": "
79
+ if value.is_a?(Array)
80
+ @send_buffer << value.shift
81
+ until value.empty? do
82
+ @send_buffer << "," + value.shift
83
+ end
84
+ else
85
+ @send_buffer << value
86
+ end
87
+ @send_buffer << "\r\n"
88
+ end
89
+ end
90
+ @send_buffer << "\r\n"
91
+ #reconstruction complete
92
+ @send_buffer << buf[1] unless buf.nil? #append buffered body
93
+ log.debug [:reconstructed_sendbuffer, @send_buffer]
94
+ end
95
+
96
+ # Adds a hash to {#header}
97
+ #
98
+ # symbolizes hsh keys, duplicate key values will be overwritten with hsh values
99
+ #
100
+ # @param hsh [Hash] hash with HTTP header Key:Value pairs
101
+ def update_header(hsh)
102
+ hsh = hsh.inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo }
103
+ @header.update(hsh)
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,204 @@
1
+ module ExperellaProxy
2
+ #
3
+ # Response is used to store incoming (HTTP) responses and parsed data
4
+ #
5
+ # Every Response belongs to a request {Request}
6
+ #
7
+ class Response
8
+
9
+ include ExperellaProxy::Globals
10
+
11
+ attr_reader :header
12
+
13
+ # The constructor
14
+ #
15
+ # @param request [Request] Request the response belongs to
16
+ def initialize(request)
17
+ @request = request
18
+ @conn = request.conn
19
+ @header = {}
20
+ @status_code = 500
21
+ @chunked = false # if true the parsed body will be chunked
22
+ @buffer = false # default is false, so incoming data will be streamed,
23
+ # used for http1.0 clients and transfer-encoding chunked backend responses
24
+ @send_buffer = String.new
25
+ @response_parser = Http::Parser.new
26
+ init_http_parser
27
+ end
28
+
29
+ # Adds data to the response object
30
+ #
31
+ # data must be formatted as string
32
+ #
33
+ # On Http::Parser::Error parsing gets interrupted and the connection closed
34
+ #
35
+ # @param str [String] data as string
36
+ def <<(str)
37
+ begin
38
+ @response_parser << str
39
+ rescue Http::Parser::Error
40
+ log.warn ["Parser error caused by invalid response data", "@#{@conn.signature}"]
41
+ # on error unbind response_parser object, so additional data doesn't get parsed anymore
42
+ #
43
+ # assigning a string to the parser variable, will cause incoming data to get buffered
44
+ # imho this is a better solution than adding a condition for this rare error case
45
+ @response_parser = ""
46
+ @conn.close
47
+ end
48
+ end
49
+
50
+ # Returns the data in send_buffer and empties the send_buffer
51
+ #
52
+ # @return [String] data to send
53
+ def flush
54
+ log.debug [:data_to_user, @send_buffer]
55
+ @send_buffer.slice!(0, @send_buffer.length)
56
+ end
57
+
58
+ # Returns if the send_buffer is flushed? (empty)
59
+ #
60
+ # @return [Boolean]
61
+ def flushed?
62
+ @send_buffer.empty?
63
+ end
64
+
65
+ # Reconstructs modified http response in send_buffer
66
+ #
67
+ # Reconstructed response must be a valid response according to the HTTP Protocol
68
+ #
69
+ # Header order is determined by {#header}.each
70
+ #
71
+ def reconstruct_header
72
+ @send_buffer = ""
73
+ #start line
74
+ @send_buffer << "HTTP/1.1 "
75
+ @send_buffer << @status_code.to_s + ' '
76
+ @send_buffer << HTTP_STATUS_CODES[@status_code] + "\r\n"
77
+ #header fields
78
+ @header.each do |key, value|
79
+ @send_buffer << key.to_s + ": "
80
+ if value.is_a?(Array)
81
+ @send_buffer << value.shift
82
+ until value.empty? do
83
+ @send_buffer << "," + value.shift
84
+ end
85
+ else
86
+ @send_buffer << value
87
+ end
88
+ @send_buffer << "\r\n"
89
+ end
90
+ @send_buffer << "\r\n"
91
+ #reconstruction complete
92
+ log.debug [:response_reconstructed_header, @send_buffer]
93
+ end
94
+
95
+ # Adds a hash to {#header}
96
+ #
97
+ # symbolizes hsh keys, duplicate key values will be overwritten with hsh values
98
+ #
99
+ # @param hsh [Hash] hash with HTTP header Key:Value pairs
100
+ def update_header(hsh)
101
+ hsh = hsh.inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo }
102
+ @header.update(hsh)
103
+ end
104
+
105
+ private
106
+
107
+ # initializes the response http parser
108
+ def init_http_parser
109
+ #called when response headers are completely parsed (first \r\n\r\n triggers this)
110
+ @response_parser.on_headers_complete = proc do |h|
111
+
112
+ @status_code = @response_parser.status_code
113
+
114
+ if @request.keep_alive
115
+ @header[:Connection] = "Keep-Alive"
116
+ end
117
+
118
+ # handle the transfer-encoding
119
+ #
120
+ # if no transfer encoding and no content-length is given, terminate connection after backend unbind
121
+ #
122
+ # if no transfer encoding is given, but there is content-length, just keep the content-length and send the message
123
+ #
124
+ # if a transfer-encoding is given, continue with Transfer-Encoding chunked and remove false content-length
125
+ # header if present. Old Transfer-Encoding header will be removed with all other hop-by-hop headers
126
+ #
127
+ if h["Transfer-Encoding"].nil?
128
+ # if no transfer-encoding and no content-length is present, set Connection: close
129
+ if h["Content-Length"].nil?
130
+ @request.keep_alive = false
131
+ @header[:Connection] = "close"
132
+ end
133
+ #chunked encoded
134
+ else
135
+ # buffer response data if client uses http 1.0 until message complete
136
+ if @request.header[:http_version][0] == 1 && @request.header[:http_version][1] == 0
137
+ @buffer = true
138
+ else
139
+ h.delete("Content-Length")
140
+ @chunked = true unless @request.header[:http_method] == "HEAD"
141
+ @header[:"Transfer-Encoding"] = "chunked"
142
+ end
143
+ end
144
+
145
+ # remove all hop-by-hop header fields
146
+ unless h["Connection"].nil?
147
+ h["Connection"].each do |s|
148
+ h.delete(s)
149
+ end
150
+ end
151
+ HOP_HEADERS.each do |s|
152
+ h.delete(s)
153
+ end
154
+
155
+
156
+ via = h.delete("Via")
157
+ if via.nil?
158
+ via = "1.1 experella"
159
+ else
160
+ via << "1.1 experella"
161
+ end
162
+ @header[:Via] = via
163
+
164
+
165
+ update_header(h)
166
+ unless @buffer
167
+ # called before any data is put into send_buffer
168
+ reconstruct_header
169
+ @conn.send_data flush
170
+ end
171
+ end
172
+
173
+ @response_parser.on_body = proc do |chunk|
174
+ if @chunked
175
+ # add hexadecimal chunk size
176
+ @send_buffer << chunk.size.to_s(16)
177
+ @send_buffer << "\r\n"
178
+ @send_buffer << chunk
179
+ @send_buffer << "\r\n"
180
+ else
181
+ @send_buffer << chunk
182
+ end
183
+ unless @buffer
184
+ @conn.send_data flush
185
+ end
186
+ end
187
+
188
+ @response_parser.on_message_complete = proc do
189
+ if @chunked
190
+ # send closing chunk
191
+ @send_buffer << "0\r\n\r\n"
192
+ @conn.send_data flush
193
+ elsif @buffer
194
+ @header[:"Content-Length"] = @send_buffer.size.to_s
195
+ body = flush
196
+ reconstruct_header
197
+ @send_buffer << body
198
+ @conn.send_data flush
199
+ end
200
+ end
201
+
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,68 @@
1
+ module ExperellaProxy
2
+ # The server starts the {Proxy} and provides callbacks/block hooks for client {Connection}s
3
+ class Server
4
+ include ExperellaProxy::Globals
5
+
6
+ # Constructor
7
+ #
8
+ # @param options [Hash] options Hash passed to the proxy
9
+ def initialize(options)
10
+ @options=options
11
+ end
12
+
13
+ attr_reader :options
14
+
15
+ # Runs the proxy server with given options
16
+ #
17
+ # Opens a block passed to every {Connection}
18
+ #
19
+ # You can add logic to
20
+ #
21
+ # {Connection#connected} in on_connect
22
+ # {Connection#receive_data} in on_data, must return data
23
+ # {Connection#relay_from_backend} in on_response, must return resp
24
+ # {Connection#unbind_backend} in on_finish
25
+ # {Connection#unbind} in on_unbind
26
+ #
27
+ def run
28
+
29
+ Proxy.start(options = {}) do |conn|
30
+
31
+ log.info msec + "new Connection @" + signature.to_s
32
+
33
+ # called on successful backend connection
34
+ # backend is the name of the connected server
35
+ conn.on_connect do |backend|
36
+
37
+ end
38
+
39
+ # modify / process request stream
40
+ # and return modified data
41
+ conn.on_data do |data|
42
+ data
43
+ end
44
+
45
+ # modify / process response stream
46
+ # and return modified response
47
+ conn.on_response do |backend, resp|
48
+ resp
49
+ end
50
+
51
+ # termination logic
52
+ conn.on_finish do |backend|
53
+
54
+ end
55
+
56
+ # called if client terminates connection
57
+ # or timeout occurs
58
+ conn.on_unbind do
59
+
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
@@ -0,0 +1,15 @@
1
+ module ExperellaProxy
2
+ # ExperellaProxy Gemversion
3
+ # 0.0.6
4
+ # * updated homepage
5
+ # 0.0.5
6
+ # * added :host_port option to backend configuration
7
+ # 0.0.4
8
+ # * added lambda for accept filtering
9
+ #
10
+ # 0.0.3
11
+ # * added self-signed SSL certificate for TLS/HTTPS
12
+ # * added config template init functionality
13
+ #
14
+ VERSION = "0.0.6"
15
+ end