experella-proxy 0.0.10 → 0.0.11
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.
- data/bin/experella-proxy +5 -5
- data/config/default/config.rb +15 -16
- data/dev/experella-proxy +3 -4
- data/experella-proxy.gemspec +2 -2
- data/lib/experella-proxy/backend.rb +3 -5
- data/lib/experella-proxy/backend_server.rb +20 -22
- data/lib/experella-proxy/configuration.rb +19 -23
- data/lib/experella-proxy/connection.rb +29 -37
- data/lib/experella-proxy/connection_manager.rb +16 -21
- data/lib/experella-proxy/globals.rb +5 -7
- data/lib/experella-proxy/http_status_codes.rb +37 -38
- data/lib/experella-proxy/proxy.rb +8 -9
- data/lib/experella-proxy/request.rb +12 -13
- data/lib/experella-proxy/response.rb +28 -34
- data/lib/experella-proxy/server.rb +1 -6
- data/lib/experella-proxy/version.rb +5 -1
- data/lib/experella-proxy.rb +9 -10
- data/spec/echo-server/echo_server.rb +4 -6
- data/spec/experella-proxy/backend_server_spec.rb +16 -18
- data/spec/experella-proxy/configuration_spec.rb +4 -4
- data/spec/experella-proxy/connection_manager_spec.rb +15 -16
- data/spec/experella-proxy/experella-proxy_spec.rb +73 -76
- data/spec/experella-proxy/request_spec.rb +7 -7
- data/spec/experella-proxy/response_spec.rb +6 -6
- data/spec/fixtures/spec.log +187 -187
- data/spec/fixtures/test_config.rb +8 -10
- data/spec/spec_helper.rb +4 -5
- data/test/sinatra/hello_world_server.rb +0 -3
- data/test/sinatra/server_one.rb +1 -4
- data/test/sinatra/server_two.rb +2 -4
- metadata +3 -3
data/bin/experella-proxy
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
if ENV["COVERAGE"]
|
4
|
-
#simplecov support for integration specs
|
4
|
+
# simplecov support for integration specs
|
5
5
|
require 'simplecov'
|
6
6
|
SimpleCov.start do
|
7
7
|
command_name ENV["TESTNAME"] || "integration-test"
|
@@ -17,14 +17,14 @@ ARGV << '--help' if ARGV.empty?
|
|
17
17
|
if ARGV[0] == "init"
|
18
18
|
if ARGV[1] && File.directory?(File.expand_path(ARGV[1]))
|
19
19
|
source_dir = File.expand_path(File.join(File.dirname(__FILE__), "/../config/default/"))
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
dest_dir = File.join(File.expand_path(ARGV[1]), "proxy")
|
21
|
+
FileUtils.copy_entry source_dir, dest_dir
|
22
|
+
puts "Initialized template config to #{dest_dir}"
|
23
23
|
else
|
24
24
|
puts "ERROR: #{File.expand_path(ARGV[1])} is not a directory"
|
25
25
|
puts "Please provide an existing directory to initialize to as second argument"
|
26
26
|
end
|
27
|
-
elsif
|
27
|
+
elsif %w(start stop restart reload run zap status).include? ARGV[0]
|
28
28
|
require 'daemons'
|
29
29
|
Daemons.run(File.join(lib, 'experella-proxy.rb'))
|
30
30
|
else
|
data/config/default/config.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
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
|
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
5
|
|
6
6
|
# add servers in schema, :name, :host, :port, :concurrency,
|
7
7
|
# :host and :port are required, everything else is optional though name needs to be unique!
|
@@ -12,21 +12,21 @@ backend2_port=base_backend_port+2
|
|
12
12
|
# always ignore the settings of any other backend server.
|
13
13
|
# use ^((?!pattern).)*$ to negate matching
|
14
14
|
|
15
|
-
#template backend servers
|
15
|
+
# template backend servers
|
16
16
|
|
17
17
|
backend(:name => "srv1", :host => "localhost", :port => backend1_port, :concurrency => "1000",
|
18
|
-
:accepts => {"request_url" => "^((?!/(srv2)($|/)).)*$"}
|
18
|
+
:accepts => { "request_url" => "^((?!/(srv2)($|/)).)*$" }
|
19
19
|
)
|
20
20
|
|
21
21
|
backend(:name => "srv2", :host_port => "localhost:#{backend2_port}", :concurrency => "1",
|
22
|
-
:accepts => {"request_url" => "/(srv2)($|/)"},
|
23
|
-
:mangle => {"Host" => lambda
|
22
|
+
:accepts => { "request_url" => "/(srv2)($|/)" },
|
23
|
+
:mangle => { "Host" => lambda do |host|
|
24
24
|
if host.match(/127.0.0/)
|
25
25
|
'localhost'
|
26
26
|
else
|
27
27
|
host
|
28
28
|
end
|
29
|
-
|
29
|
+
end
|
30
30
|
}
|
31
31
|
)
|
32
32
|
|
@@ -48,12 +48,11 @@ logger.level = Logger::WARN
|
|
48
48
|
# additionally you can activate ssl with provided private_key and cert_chain files
|
49
49
|
set_proxy(:host => "localhost", :port => base_backend_port)
|
50
50
|
set_proxy(:host => "localhost", :port => base_backend_port + 443,
|
51
|
-
:options => {:tls
|
52
|
-
|
53
|
-
|
51
|
+
:options => { :tls => true,
|
52
|
+
:private_key_file => 'ssl/private/experella-proxy.key',
|
53
|
+
:cert_chain_file => 'ssl/certs/experella-proxy.pem' }
|
54
54
|
)
|
55
55
|
|
56
|
-
|
57
56
|
# set the timeout in seconds. Will unbind a keep-alive connection
|
58
57
|
# if no send/receive event occured in specified seconds
|
59
58
|
set_timeout(30.0)
|
@@ -62,11 +61,11 @@ set_timeout(30.0)
|
|
62
61
|
set_error_pages(404, "404.html")
|
63
62
|
set_error_pages(503, "503.html")
|
64
63
|
|
65
|
-
#you can log things as you want:
|
66
|
-
#set_on_event(lambda do |name,detail|
|
64
|
+
# you can log things as you want:
|
65
|
+
# set_on_event(lambda do |name,detail|
|
67
66
|
# if detail.delete(:error)
|
68
67
|
# Experella.config.logger.error([name,detail.inspect])
|
69
68
|
# else
|
70
69
|
# Experella.config.logger.debug([name,detail.inspect])
|
71
70
|
# end
|
72
|
-
#end)
|
71
|
+
# end)
|
data/dev/experella-proxy
CHANGED
@@ -5,10 +5,10 @@ require 'rubygems'
|
|
5
5
|
require 'bundler'
|
6
6
|
Bundler.setup
|
7
7
|
|
8
|
-
#NOTE: This is exactly the same as /bin/experella-proxy from here
|
8
|
+
# NOTE: This is exactly the same as /bin/experella-proxy from here
|
9
9
|
|
10
10
|
if ENV["COVERAGE"]
|
11
|
-
#simplecov support for integration specs
|
11
|
+
# simplecov support for integration specs
|
12
12
|
require 'simplecov'
|
13
13
|
SimpleCov.start do
|
14
14
|
command_name ENV["TESTNAME"] || "integration-test"
|
@@ -31,7 +31,7 @@ if ARGV[0] == "init"
|
|
31
31
|
puts "ERROR: #{File.expand_path(ARGV[1])} is not a directory"
|
32
32
|
puts "Please provide an existing directory to initialize to as second argument"
|
33
33
|
end
|
34
|
-
elsif
|
34
|
+
elsif %w(start stop restart reload run zap status).include? ARGV[0]
|
35
35
|
require 'daemons'
|
36
36
|
Daemons.run(File.join(lib, 'experella-proxy.rb'))
|
37
37
|
else
|
@@ -67,4 +67,3 @@ else
|
|
67
67
|
puts " -h, --help Show this message"
|
68
68
|
puts " --version Show daemons version"
|
69
69
|
end
|
70
|
-
|
data/experella-proxy.gemspec
CHANGED
@@ -28,13 +28,13 @@ Gem::Specification.new do |spec|
|
|
28
28
|
# documentation tool
|
29
29
|
spec.add_development_dependency "yard", "~> 0.8.7.3"
|
30
30
|
spec.add_development_dependency "redcarpet", "~> 2.3.0"
|
31
|
-
#code coverage
|
31
|
+
# code coverage
|
32
32
|
spec.add_development_dependency "simplecov", "~> 0.7.1"
|
33
33
|
|
34
34
|
spec.files = Dir["bin/*"] + Dir["dev/*"] + Dir["lib/**/*"] + Dir["config/default/**/*"]
|
35
35
|
spec.files += Dir["spec/**/*"] + Dir["test/sinatra/*"]
|
36
36
|
spec.files += [".gitignore", "Gemfile", "experella-proxy.gemspec", "README.md", "TODO.txt", "Rakefile"]
|
37
|
-
spec.executables = spec.files.grep(%r{^bin/})
|
37
|
+
spec.executables = spec.files.grep(%r{^bin/}){ |f| File.basename(f) }
|
38
38
|
spec.test_files = spec.files.grep(%r{^(test|spec)/})
|
39
39
|
spec.extra_rdoc_files = ['README.md']
|
40
40
|
spec.require_paths = ["lib"]
|
@@ -3,7 +3,6 @@ module ExperellaProxy
|
|
3
3
|
#
|
4
4
|
# This class will never be directly initiated by user code, but initializing gets triggered in client {Connection}
|
5
5
|
class Backend < EventMachine::Connection
|
6
|
-
|
7
6
|
include ExperellaProxy::Globals
|
8
7
|
|
9
8
|
# @!visibility private
|
@@ -36,23 +35,22 @@ module ExperellaProxy
|
|
36
35
|
# @param data [String] data to be send to the connected backend server
|
37
36
|
def send(data)
|
38
37
|
event(:backend_send_data, :data => data)
|
39
|
-
@connected.callback
|
38
|
+
@connected.callback{ send_data data }
|
40
39
|
end
|
41
40
|
|
42
41
|
# Notify upstream plexer that the backend server is done processing the request
|
43
42
|
#
|
44
43
|
def unbind
|
45
|
-
|
44
|
+
event(:backend_unbind, :name => @name)
|
46
45
|
@plexer.unbind_backend(@name)
|
47
46
|
end
|
48
47
|
|
49
|
-
|
48
|
+
private
|
50
49
|
|
51
50
|
# @private constructor, gets called by EventMachine::Connection's overwritten new method
|
52
51
|
#
|
53
52
|
def initialize
|
54
53
|
@connected = EM::DefaultDeferrable.new
|
55
54
|
end
|
56
|
-
|
57
55
|
end
|
58
56
|
end
|
@@ -1,12 +1,10 @@
|
|
1
1
|
module ExperellaProxy
|
2
|
-
|
3
2
|
# BackendServer objects contain information on available BackendServers
|
4
3
|
#
|
5
4
|
# Accepts Requests based on Request header information and it's message_matcher
|
6
5
|
#
|
7
6
|
# See {#initialize}
|
8
7
|
class BackendServer
|
9
|
-
|
10
8
|
attr_accessor :host, :port, :concurrency, :workload, :name
|
11
9
|
attr_reader :message_matcher, :mangle
|
12
10
|
|
@@ -42,9 +40,9 @@ module ExperellaProxy
|
|
42
40
|
# @option options [Hash|Proc] :accepts message_pattern that will be converted to a message_matcher or an arbitrary message_matcher as proc
|
43
41
|
# Empty Hash is default
|
44
42
|
# @option options [Hash] :mangle Hash which can modify request headers. Keys get symbolized. nil is default
|
45
|
-
def initialize(host, port, options
|
46
|
-
@host = host #host URL as string
|
47
|
-
@port = port #port as string
|
43
|
+
def initialize(host, port, options={})
|
44
|
+
@host = host # host URL as string
|
45
|
+
@port = port # port as string
|
48
46
|
@name = options[:name] || "#{host}:#{port}"
|
49
47
|
if options[:concurrency].nil?
|
50
48
|
@concurrency = 1
|
@@ -55,10 +53,10 @@ module ExperellaProxy
|
|
55
53
|
|
56
54
|
make_message_matcher(options[:accepts])
|
57
55
|
|
58
|
-
#mangle can be nil
|
56
|
+
# mangle can be nil
|
59
57
|
@mangle = options[:mangle]
|
60
|
-
#convert keys to symbols to match request header keys
|
61
|
-
@mangle = @mangle.
|
58
|
+
# convert keys to symbols to match request header keys
|
59
|
+
@mangle = @mangle.reduce({}){ |memo, (k, v)| memo[k.to_sym] = v; memo } unless @mangle.nil?
|
62
60
|
end
|
63
61
|
|
64
62
|
# compares Backend servers message_matcher to request object
|
@@ -66,8 +64,8 @@ module ExperellaProxy
|
|
66
64
|
# @param request [Request] a request object
|
67
65
|
# @return [Boolean] true if BackendServer accepts the Request, false otherwise
|
68
66
|
def accept?(request)
|
69
|
-
res
|
70
|
-
#puts "#{name} #{request.header['request_url']} #{res}"
|
67
|
+
res = @message_matcher.call(request)
|
68
|
+
# puts "#{name} #{request.header['request_url']} #{res}"
|
71
69
|
res
|
72
70
|
end
|
73
71
|
|
@@ -75,24 +73,24 @@ module ExperellaProxy
|
|
75
73
|
#
|
76
74
|
# @param obj [Hash|Proc] hash containing a message_pattern that will be converted to a message_matcher proc or an arbitrary own message_matcher
|
77
75
|
def make_message_matcher(obj)
|
78
|
-
@message_matcher =if obj.respond_to?(:call)
|
79
|
-
|
76
|
+
@message_matcher = if obj.respond_to?(:call)
|
77
|
+
obj
|
80
78
|
else
|
81
|
-
#precompile message pattern keys to symbols and values to regexp objects
|
82
|
-
keys = (obj||{}).
|
83
|
-
lambda do |request| #TODO: ugly!
|
84
|
-
ret=true
|
79
|
+
# precompile message pattern keys to symbols and values to regexp objects
|
80
|
+
keys = (obj || {}).reduce({}){ |memo, (k, v)| memo[k.to_sym] = Regexp.new(v); memo }
|
81
|
+
lambda do |request| # TODO: ugly!
|
82
|
+
ret = true
|
85
83
|
keys.each do |key, pattern|
|
86
|
-
#use uri for :port :path and :query keys
|
84
|
+
# use uri for :port :path and :query keys
|
87
85
|
if key == :port || key == :path || key == :query
|
88
|
-
ret=false unless request.uri[key] && request.uri[key].match(pattern)
|
89
|
-
else #use headers
|
90
|
-
ret=false unless request.header[key] && request.header[key].match(pattern)
|
86
|
+
ret = false unless request.uri[key] && request.uri[key].match(pattern)
|
87
|
+
else # use headers
|
88
|
+
ret = false unless request.header[key] && request.header[key].match(pattern)
|
91
89
|
end
|
92
|
-
break unless ret #stop as soon as possible
|
90
|
+
break unless ret # stop as soon as possible
|
93
91
|
end
|
94
92
|
ret
|
95
|
-
end #lambda
|
93
|
+
end # lambda
|
96
94
|
end
|
97
95
|
end
|
98
96
|
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
module ExperellaProxy
|
2
|
-
|
3
2
|
# static getter for the config variable
|
4
3
|
#
|
5
4
|
# @return [Configuration] config
|
@@ -12,7 +11,7 @@ module ExperellaProxy
|
|
12
11
|
# @param config [Configuration] a config object
|
13
12
|
# @return [Configuration] config
|
14
13
|
def self.config=(config)
|
15
|
-
@config=config
|
14
|
+
@config = config
|
16
15
|
end
|
17
16
|
# The Configuration loader
|
18
17
|
#
|
@@ -38,12 +37,10 @@ module ExperellaProxy
|
|
38
37
|
# Currently 404 and 503 error codes are supported
|
39
38
|
#
|
40
39
|
class Configuration
|
41
|
-
|
42
40
|
# Error raised if the Config couldn't be load successfully
|
43
41
|
#
|
44
42
|
# Currently only raised if config filepath is invalid
|
45
43
|
class NoConfigError < StandardError
|
46
|
-
|
47
44
|
end
|
48
45
|
|
49
46
|
attr_reader :logger, :proxy, :timeout, :backends, :error_pages, :on_event
|
@@ -55,18 +52,18 @@ module ExperellaProxy
|
|
55
52
|
# @param [Hash] options
|
56
53
|
# @option options [String] :configfile the config filepath
|
57
54
|
def initialize(options={})
|
58
|
-
@backends=[]
|
59
|
-
@proxy=[]
|
60
|
-
@error_pages = {404 => "", 503 => ""}
|
61
|
-
@on_event=lambda
|
62
|
-
default_options={:timeout => 15.0, :logger => Logger.new($stdout)}
|
63
|
-
options=options.
|
64
|
-
options=default_options.merge(options)
|
65
|
-
options.each do |k,v|
|
66
|
-
|
55
|
+
@backends = []
|
56
|
+
@proxy = []
|
57
|
+
@error_pages = { 404 => "", 503 => "" }
|
58
|
+
@on_event = lambda{ |name, data| }
|
59
|
+
default_options = { :timeout => 15.0, :logger => Logger.new($stdout) }
|
60
|
+
options = options.reduce({}){ |memo, (k, v)| memo[k.to_sym] = v; memo }
|
61
|
+
options = default_options.merge(options)
|
62
|
+
options.each do |k, v|
|
63
|
+
instance_variable_set("@#{k}", v)
|
67
64
|
end
|
68
65
|
read_config_file(@configfile) if @configfile
|
69
|
-
ExperellaProxy.config=self
|
66
|
+
ExperellaProxy.config = self
|
70
67
|
end
|
71
68
|
|
72
69
|
# Return filenames fullpath relative to configfile directory
|
@@ -82,16 +79,16 @@ module ExperellaProxy
|
|
82
79
|
# @param configfile [String] the config filepath
|
83
80
|
# @return [Boolean] true on success, false if file can't be found
|
84
81
|
def read_config_file(configfile)
|
85
|
-
|
82
|
+
unless File.exist?(configfile)
|
86
83
|
puts "error reading #{configfile}"
|
87
84
|
raise NoConfigError.new("unable to read config file #{configfile}")
|
88
85
|
end
|
89
|
-
content=File.read(configfile)
|
86
|
+
content = File.read(configfile)
|
90
87
|
instance_eval(content)
|
91
88
|
true
|
92
89
|
end
|
93
90
|
|
94
|
-
#DSL:
|
91
|
+
# DSL:
|
95
92
|
|
96
93
|
# Adds a {BackendServer} specified in the config file to {#backends}
|
97
94
|
# It allows some syntactic sugar to pass :host_port as an abbrev of host and port keys separated by ':'.
|
@@ -100,9 +97,9 @@ module ExperellaProxy
|
|
100
97
|
# @param backend_options [Hash] backends option hash
|
101
98
|
|
102
99
|
def backend(backend_options)
|
103
|
-
host_port=backend_options.delete(:host_port)
|
100
|
+
host_port = backend_options.delete(:host_port)
|
104
101
|
if host_port
|
105
|
-
host,port = host_port.split(":")
|
102
|
+
host, port = host_port.split(":")
|
106
103
|
backend_options[:host] = host
|
107
104
|
backend_options[:port] = port
|
108
105
|
end
|
@@ -110,13 +107,13 @@ module ExperellaProxy
|
|
110
107
|
end
|
111
108
|
|
112
109
|
def set_on_event(lambda)
|
113
|
-
@on_event=lambda
|
110
|
+
@on_event = lambda
|
114
111
|
end
|
115
112
|
# Sets the {Connection} timeout specified in the config file
|
116
113
|
#
|
117
114
|
# @param to [Float] timeout as float
|
118
115
|
def set_timeout(to)
|
119
|
-
@timeout=to
|
116
|
+
@timeout = to
|
120
117
|
end
|
121
118
|
|
122
119
|
# Sets the global Logger object specified in the config file
|
@@ -125,7 +122,7 @@ module ExperellaProxy
|
|
125
122
|
#
|
126
123
|
# @param logger [Logger] the logger object
|
127
124
|
def set_logger(logger)
|
128
|
-
@logger=logger
|
125
|
+
@logger = logger
|
129
126
|
end
|
130
127
|
|
131
128
|
# Adds a Proxy specified in the config file to {#proxy}
|
@@ -153,6 +150,5 @@ module ExperellaProxy
|
|
153
150
|
def set_error_pages(key, page_path)
|
154
151
|
@error_pages[key] = File.read(join_config_path(page_path))
|
155
152
|
end
|
156
|
-
|
157
153
|
end
|
158
154
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'uri'
|
2
2
|
|
3
3
|
module ExperellaProxy
|
4
|
-
|
5
4
|
# The proxies TCP Connection to the client
|
6
5
|
#
|
7
6
|
# Responsible for parsing and buffering the clients http requests,
|
@@ -105,11 +104,11 @@ module ExperellaProxy
|
|
105
104
|
def connect_backendserver(backend)
|
106
105
|
@backend = backend
|
107
106
|
connection_manager.free_connection(self)
|
108
|
-
#mangle http headers
|
107
|
+
# mangle http headers
|
109
108
|
mangle
|
110
109
|
# reconstruct the request header
|
111
110
|
get_request.reconstruct_header
|
112
|
-
#special web support for unknown hosts
|
111
|
+
# special web support for unknown hosts
|
113
112
|
if @backend.name.eql?("web")
|
114
113
|
xport = get_request.header[:Host].match(/:[0-9]+/)
|
115
114
|
if xport.nil? || xport.to_s.empty?
|
@@ -197,7 +196,7 @@ module ExperellaProxy
|
|
197
196
|
# @param name [String] name of the Server used for logging
|
198
197
|
# @param opts [Hash] Hash containing connection parameters
|
199
198
|
def server(name, opts)
|
200
|
-
srv = EventMachine
|
199
|
+
srv = EventMachine.bind_connect(opts[:bind_host], opts[:bind_port], opts[:host], opts[:port], Backend) do |c|
|
201
200
|
c.name = name
|
202
201
|
c.plexer = self
|
203
202
|
end
|
@@ -246,7 +245,6 @@ module ExperellaProxy
|
|
246
245
|
end
|
247
246
|
end
|
248
247
|
|
249
|
-
|
250
248
|
# Called by EventMachine when the SSL/TLS handshake has been completed, as a result of calling start_tls to
|
251
249
|
# initiate SSL/TLS on the connection.
|
252
250
|
#
|
@@ -275,7 +273,7 @@ module ExperellaProxy
|
|
275
273
|
|
276
274
|
@server = nil
|
277
275
|
|
278
|
-
#if backend responded or client unbound connection (timeout probably triggers this too)
|
276
|
+
# if backend responded or client unbound connection (timeout probably triggers this too)
|
279
277
|
if @got_response || @unbound
|
280
278
|
event(:connection_unbind_backend_request_done,
|
281
279
|
:msec => msec,
|
@@ -286,21 +284,21 @@ module ExperellaProxy
|
|
286
284
|
close
|
287
285
|
event(:connection_unbind_backend_close, :msec => msec, :signature => @signature)
|
288
286
|
end
|
289
|
-
@requests.shift #pop first element,request is done
|
290
|
-
@got_response = false #reset response flag
|
287
|
+
@requests.shift # pop first element,request is done
|
288
|
+
@got_response = false # reset response flag
|
291
289
|
|
292
|
-
#free backend server and connect to next conn if matching conn exists
|
290
|
+
# free backend server and connect to next conn if matching conn exists
|
293
291
|
unless @backend.nil?
|
294
292
|
connect_next
|
295
293
|
end
|
296
294
|
|
297
|
-
#check if queued requests find a matching backend
|
295
|
+
# check if queued requests find a matching backend
|
298
296
|
unless @requests.empty? || @unbound
|
299
|
-
#try to dispatch first request to backend
|
297
|
+
# try to dispatch first request to backend
|
300
298
|
dispatch_request
|
301
299
|
end
|
302
300
|
else
|
303
|
-
#handle no backend response here
|
301
|
+
# handle no backend response here
|
304
302
|
event(:connection_unbind_backend_error, :msec => msec, :error => true, :error_code => 503)
|
305
303
|
error_page = "HTTP/1.1 503 Service unavailable\r\nContent-Length: #{config.error_pages[503].length}\r\nContent-Type: text/html;charset=utf-8\r\nConnection: close\r\n\r\n"
|
306
304
|
unless get_request.header[:http_method].eql? "HEAD"
|
@@ -326,11 +324,11 @@ module ExperellaProxy
|
|
326
324
|
@on_unbind.call if @on_unbind
|
327
325
|
|
328
326
|
event(:connection_unbind_client, :msec => msec, :signature => @signature)
|
329
|
-
#lazy evaluated. if first is true, second would cause a nil-pointer!
|
330
|
-
unless @requests.empty? || get_request.flushed? #what does this mean?
|
331
|
-
#log.debug [msec, @requests.inspect]
|
327
|
+
# lazy evaluated. if first is true, second would cause a nil-pointer!
|
328
|
+
unless @requests.empty? || get_request.flushed? # what does this mean?
|
329
|
+
# log.debug [msec, @requests.inspect]
|
332
330
|
end
|
333
|
-
#delete conn from queue if still queued
|
331
|
+
# delete conn from queue if still queued
|
334
332
|
connection_manager.free_connection(self)
|
335
333
|
|
336
334
|
# reconnect backend to new connection if this has not happened already
|
@@ -343,8 +341,7 @@ module ExperellaProxy
|
|
343
341
|
end
|
344
342
|
end
|
345
343
|
|
346
|
-
|
347
|
-
private
|
344
|
+
private
|
348
345
|
|
349
346
|
# @private constructor, gets called by EventMachine::Connection's overwritten new method
|
350
347
|
# Initializes http parser and timeout_timer
|
@@ -354,7 +351,7 @@ module ExperellaProxy
|
|
354
351
|
@options = options
|
355
352
|
@backend = nil
|
356
353
|
@server = nil
|
357
|
-
@requests = [] #contains request objects
|
354
|
+
@requests = [] # contains request objects
|
358
355
|
@unbound = false
|
359
356
|
@got_response = false
|
360
357
|
@request_parser = Http::Parser.new
|
@@ -370,7 +367,7 @@ module ExperellaProxy
|
|
370
367
|
# did this for testability. 27.11.2013
|
371
368
|
#
|
372
369
|
def connect_next
|
373
|
-
#free backend server and connect to next conn if matching conn exists
|
370
|
+
# free backend server and connect to next conn if matching conn exists
|
374
371
|
next_conn = connection_manager.free_backend(@backend)
|
375
372
|
unless next_conn.nil?
|
376
373
|
next_conn.connect_backendserver(@backend)
|
@@ -407,14 +404,13 @@ module ExperellaProxy
|
|
407
404
|
|
408
405
|
# initializes http parser callbacks and blocks
|
409
406
|
def init_http_parser
|
410
|
-
|
411
407
|
@request_parser.on_message_begin = proc do
|
412
408
|
@requests.push(Request.new(self))
|
413
409
|
# this log also triggers if client sends new keep-alive request before backend was unbound
|
414
|
-
event(:connection_http_parser_start, :msec => msec, :pipelined => (@requests.length>1))
|
410
|
+
event(:connection_http_parser_start, :msec => msec, :pipelined => (@requests.length > 1))
|
415
411
|
end
|
416
412
|
|
417
|
-
#called when request headers are completely parsed (first \r\n\r\n triggers this)
|
413
|
+
# called when request headers are completely parsed (first \r\n\r\n triggers this)
|
418
414
|
@request_parser.on_headers_complete = proc do |h|
|
419
415
|
event(:connection_http_parser_headers_complete_start, :msec => msec, :signature => @signature, :request_path => @request_parser.request_url, :host => h["Host"])
|
420
416
|
request = @requests.last
|
@@ -425,13 +421,13 @@ module ExperellaProxy
|
|
425
421
|
else
|
426
422
|
request.keep_alive = false if h["Connection"].to_s.downcase.include? "close"
|
427
423
|
end
|
428
|
-
request.update_header(
|
424
|
+
request.update_header(:Connection => "close") # update Connection header to close for backends
|
429
425
|
|
430
426
|
# if there is a transfer-encoding, stream the message as Transfer-Encoding: chunked to backends
|
431
427
|
unless h["Transfer-Encoding"].nil?
|
432
428
|
h.delete("Content-Length")
|
433
429
|
request.chunked = true
|
434
|
-
request.update_header(
|
430
|
+
request.update_header(:"Transfer-Encoding" => "chunked")
|
435
431
|
end
|
436
432
|
|
437
433
|
# remove all hop-by-hop header fields
|
@@ -448,19 +444,18 @@ module ExperellaProxy
|
|
448
444
|
h.delete(s)
|
449
445
|
end
|
450
446
|
|
451
|
-
|
452
447
|
via = h.delete("Via")
|
453
448
|
if via.nil?
|
454
449
|
via = "1.1 experella"
|
455
450
|
else
|
456
451
|
via << "1.1 experella"
|
457
452
|
end
|
458
|
-
request.update_header(
|
453
|
+
request.update_header(:Via => via)
|
459
454
|
|
460
455
|
request.update_header(h)
|
461
|
-
request.update_header(
|
462
|
-
request.update_header(
|
463
|
-
request.update_header(
|
456
|
+
request.update_header(:http_version => @request_parser.http_version)
|
457
|
+
request.update_header(:http_method => @request_parser.http_method) # for requests
|
458
|
+
request.update_header(:request_url => @request_parser.request_url)
|
464
459
|
if @request_parser.request_url.include? "http://"
|
465
460
|
u = URI.parse(@request_parser.request_url)
|
466
461
|
request.update_header(:Host => u.host)
|
@@ -471,7 +466,6 @@ module ExperellaProxy
|
|
471
466
|
|
472
467
|
request.add_uri(:port => u.port, :path => u.path, :query => u.query)
|
473
468
|
|
474
|
-
|
475
469
|
# try to connect request to backend
|
476
470
|
# but only try to connect if this (.last) equals (.first), true at length == 1
|
477
471
|
# according to http-protocol requests must always be handled in order.
|
@@ -502,7 +496,6 @@ module ExperellaProxy
|
|
502
496
|
request << "0\r\n\r\n"
|
503
497
|
end
|
504
498
|
end
|
505
|
-
|
506
499
|
end
|
507
500
|
|
508
501
|
# Mangles http headers based on backend specific mangle configuration
|
@@ -511,9 +504,9 @@ module ExperellaProxy
|
|
511
504
|
unless @backend.mangle.nil?
|
512
505
|
@backend.mangle.each do |k, v|
|
513
506
|
if v.respond_to?(:call)
|
514
|
-
get_request.update_header(
|
507
|
+
get_request.update_header(k => v.call(get_request.header[k]))
|
515
508
|
else
|
516
|
-
get_request.update_header(
|
509
|
+
get_request.update_header(k => v)
|
517
510
|
end
|
518
511
|
end
|
519
512
|
end
|
@@ -527,7 +520,7 @@ module ExperellaProxy
|
|
527
520
|
# If the backend server is not yet connected, data is already buffered to be sent when the connection gets established
|
528
521
|
#
|
529
522
|
def relay_to_server
|
530
|
-
if @backend && !@requests.empty? && !get_request.flushed? &&
|
523
|
+
if @backend && !@requests.empty? && !get_request.flushed? && @server
|
531
524
|
# save some memory here if logger isn't set on debug
|
532
525
|
data = get_request.flush
|
533
526
|
@server.send_data data
|
@@ -539,7 +532,7 @@ module ExperellaProxy
|
|
539
532
|
end
|
540
533
|
end
|
541
534
|
|
542
|
-
#returns milliseconds since connection startup as string
|
535
|
+
# returns milliseconds since connection startup as string
|
543
536
|
def msec
|
544
537
|
(((Time.now.tv_sec - @start.tv_sec) * 1000) + ((Time.now.tv_usec - @start.tv_usec) / 1000.0)).to_s + "ms: "
|
545
538
|
end
|
@@ -556,6 +549,5 @@ module ExperellaProxy
|
|
556
549
|
end
|
557
550
|
end
|
558
551
|
end
|
559
|
-
|
560
552
|
end
|
561
553
|
end
|