experella-proxy 0.0.10 → 0.0.11
Sign up to get free protection for your applications and to get access to all the features.
- 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
|