lib/invoker/power/balancer.rb in invoker-1.1.0 vs lib/invoker/power/balancer.rb in invoker-1.2.0.pre

- old
+ new

@@ -1,19 +1,26 @@ require 'em-proxy' require 'http-parser' module Invoker module Power - class BalancerConnection < EventMachine::ProxyServer::Connection + class InvokerHttpProxy < EventMachine::ProxyServer::Connection attr_accessor :host, :ip, :port def set_host(host, selected_backend) self.host = host self.ip = selected_backend[:host] self.port = selected_backend[:port] end end + class InvokerHttpsProxy < InvokerHttpProxy + def post_init + super + start_tls + end + end + class BalancerParser attr_accessor :host, :parser def initialize @parser = HTTP::Parser.new() @header = {} @@ -42,41 +49,58 @@ @parser << data end end class Balancer - attr_accessor :connection, :http_parser, :session + attr_accessor :connection, :http_parser, :session, :protocol DEV_MATCH_REGEX = /([\w-]+)\.dev(\:\d+)?$/ def self.run(options = {}) - EventMachine.start_server('0.0.0.0', Invoker.config.http_port, - BalancerConnection, options) do |connection| - balancer = Balancer.new(connection) + start_http_proxy(InvokerHttpProxy, 'http', options) + start_http_proxy(InvokerHttpsProxy, 'https', options) + end + + def self.start_http_proxy(proxy_class, protocol, options) + port = protocol == 'http' ? Invoker.config.http_port : Invoker.config.https_port + EventMachine.start_server('0.0.0.0', port, + proxy_class, options) do |connection| + balancer = Balancer.new(connection, protocol) balancer.install_callbacks end end - def initialize(connection) + def initialize(connection, protocol) @connection = connection + @protocol = protocol @http_parser = BalancerParser.new() @session = nil @buffer = [] end + # insert X_FORWARDED_PROTO_ so as rails can identify the request as coming from + # https + def insert_forwarded_proto_header(data) + if data =~ /\r\n\r\n/ && data !~ /X_FORWARDED_PROTO/i && protocol == 'https' + data.gsub(/\r\n\r\n/, "\r\nX_FORWARDED_PROTO: #{protocol}\r\n\r\n") + else + data + end + end + def install_callbacks http_parser.on_headers_complete { |header| headers_received(header) } - connection.on_data {|data| upstream_data(data) } + connection.on_data { |data| upstream_data(data) } connection.on_response { |backend, data| backend_data(backend, data) } connection.on_finish { |backend, name| frontend_disconnect(backend, name) } end def headers_received(header) @session = UUID.generate() dns_check_response = select_backend_config(header['Host']) if dns_check_response && dns_check_response.port connection.server(session, host: '0.0.0.0', port: dns_check_response.port) - connection.relay_to_servers(@buffer.join) + connection.relay_to_servers(insert_forwarded_proto_header(@buffer.join)) @buffer = [] else return_error_page(404) connection.close_connection_after_writing end @@ -86,11 +110,11 @@ unless session @buffer << data append_for_http_parsing(data) nil else - data + insert_forwarded_proto_header(data) end end def append_for_http_parsing(data) http_parser << data @@ -105,10 +129,10 @@ end def frontend_disconnect(backend, name) http_parser.reset unless @backend_data - Invoker::Logger.puts("\nApplication not running. Returning error page.".color(:red)) + Invoker::Logger.puts("\nApplication #{name} not running. Returning error page.".color(:red)) return_error_page(503) end @backend_data = false connection.close_connection_after_writing if backend == session end