itrg-invoker 1.6.0
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.
- checksums.yaml +7 -0
 - data/bin/invoker +7 -0
 - data/lib/invoker/cli/pinger.rb +23 -0
 - data/lib/invoker/cli/question.rb +15 -0
 - data/lib/invoker/cli/tail.rb +34 -0
 - data/lib/invoker/cli/tail_watcher.rb +34 -0
 - data/lib/invoker/cli.rb +197 -0
 - data/lib/invoker/command_worker.rb +64 -0
 - data/lib/invoker/commander.rb +101 -0
 - data/lib/invoker/daemon.rb +126 -0
 - data/lib/invoker/dns_cache.rb +23 -0
 - data/lib/invoker/errors.rb +17 -0
 - data/lib/invoker/event/manager.rb +79 -0
 - data/lib/invoker/ipc/add_command.rb +12 -0
 - data/lib/invoker/ipc/add_http_command.rb +10 -0
 - data/lib/invoker/ipc/base_command.rb +24 -0
 - data/lib/invoker/ipc/client_handler.rb +26 -0
 - data/lib/invoker/ipc/dns_check_command.rb +17 -0
 - data/lib/invoker/ipc/list_command.rb +11 -0
 - data/lib/invoker/ipc/message/list_response.rb +35 -0
 - data/lib/invoker/ipc/message/tail_response.rb +10 -0
 - data/lib/invoker/ipc/message.rb +170 -0
 - data/lib/invoker/ipc/ping_command.rb +10 -0
 - data/lib/invoker/ipc/reload_command.rb +12 -0
 - data/lib/invoker/ipc/remove_command.rb +12 -0
 - data/lib/invoker/ipc/server.rb +26 -0
 - data/lib/invoker/ipc/tail_command.rb +11 -0
 - data/lib/invoker/ipc/unix_client.rb +60 -0
 - data/lib/invoker/ipc.rb +45 -0
 - data/lib/invoker/logger.rb +13 -0
 - data/lib/invoker/parsers/config.rb +184 -0
 - data/lib/invoker/parsers/procfile.rb +86 -0
 - data/lib/invoker/power/balancer.rb +133 -0
 - data/lib/invoker/power/config.rb +77 -0
 - data/lib/invoker/power/dns.rb +38 -0
 - data/lib/invoker/power/http_parser.rb +68 -0
 - data/lib/invoker/power/http_response.rb +81 -0
 - data/lib/invoker/power/port_finder.rb +49 -0
 - data/lib/invoker/power/power.rb +3 -0
 - data/lib/invoker/power/powerup.rb +29 -0
 - data/lib/invoker/power/setup/distro/arch.rb +15 -0
 - data/lib/invoker/power/setup/distro/base.rb +80 -0
 - data/lib/invoker/power/setup/distro/debian.rb +11 -0
 - data/lib/invoker/power/setup/distro/opensuse.rb +11 -0
 - data/lib/invoker/power/setup/distro/redhat.rb +11 -0
 - data/lib/invoker/power/setup/distro/ubuntu.rb +46 -0
 - data/lib/invoker/power/setup/files/invoker_forwarder.sh.erb +17 -0
 - data/lib/invoker/power/setup/files/socat_invoker.service +12 -0
 - data/lib/invoker/power/setup/linux_setup.rb +97 -0
 - data/lib/invoker/power/setup/osx_setup.rb +137 -0
 - data/lib/invoker/power/setup.rb +93 -0
 - data/lib/invoker/power/templates/400.html +40 -0
 - data/lib/invoker/power/templates/404.html +40 -0
 - data/lib/invoker/power/templates/503.html +40 -0
 - data/lib/invoker/power/url_rewriter.rb +40 -0
 - data/lib/invoker/process_manager.rb +201 -0
 - data/lib/invoker/process_printer.rb +59 -0
 - data/lib/invoker/reactor/reader.rb +65 -0
 - data/lib/invoker/reactor.rb +37 -0
 - data/lib/invoker/version.rb +47 -0
 - data/lib/invoker.rb +151 -0
 - data/spec/invoker/cli/pinger_spec.rb +22 -0
 - data/spec/invoker/cli/tail_watcher_spec.rb +39 -0
 - data/spec/invoker/cli_spec.rb +27 -0
 - data/spec/invoker/command_worker_spec.rb +45 -0
 - data/spec/invoker/commander_spec.rb +152 -0
 - data/spec/invoker/config_spec.rb +361 -0
 - data/spec/invoker/daemon_spec.rb +34 -0
 - data/spec/invoker/event/manager_spec.rb +67 -0
 - data/spec/invoker/invoker_spec.rb +71 -0
 - data/spec/invoker/ipc/client_handler_spec.rb +54 -0
 - data/spec/invoker/ipc/dns_check_command_spec.rb +32 -0
 - data/spec/invoker/ipc/message/list_response_spec.rb +24 -0
 - data/spec/invoker/ipc/message_spec.rb +49 -0
 - data/spec/invoker/ipc/unix_client_spec.rb +29 -0
 - data/spec/invoker/power/balancer_spec.rb +53 -0
 - data/spec/invoker/power/config_spec.rb +18 -0
 - data/spec/invoker/power/http_parser_spec.rb +32 -0
 - data/spec/invoker/power/http_response_spec.rb +34 -0
 - data/spec/invoker/power/port_finder_spec.rb +16 -0
 - data/spec/invoker/power/setup/linux_setup_spec.rb +166 -0
 - data/spec/invoker/power/setup/osx_setup_spec.rb +105 -0
 - data/spec/invoker/power/setup_spec.rb +4 -0
 - data/spec/invoker/power/url_rewriter_spec.rb +69 -0
 - data/spec/invoker/power/web_sockets_spec.rb +61 -0
 - data/spec/invoker/process_manager_spec.rb +130 -0
 - data/spec/invoker/reactor_spec.rb +6 -0
 - data/spec/spec_helper.rb +43 -0
 - metadata +376 -0
 
| 
         @@ -0,0 +1,184 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'iniparse'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Parsers
         
     | 
| 
      
 5 
     | 
    
         
            +
                class Config
         
     | 
| 
      
 6 
     | 
    
         
            +
                  PORT_REGEX = /\$PORT/
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  attr_accessor :processes, :power_config
         
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_reader :filename
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  # initialize takes a port form cli and decrements it by 1 and sets the
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # instance variable @port. This port value is used as the environment
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # variable $PORT mentioned inside invoker.ini. When method pick_port gets
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # fired it increments the value of port by 1, subsequently when pick_port
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # again gets fired, for another command, it will again increment port
         
     | 
| 
      
 16 
     | 
    
         
            +
                  # value by 1, that way generating different ports for different commands.
         
     | 
| 
      
 17 
     | 
    
         
            +
                  def initialize(filename, port)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @filename = filename || autodetect_config_file
         
     | 
| 
      
 19 
     | 
    
         
            +
                    print_message_and_abort if invalid_config_file?
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    @port = port - 1
         
     | 
| 
      
 22 
     | 
    
         
            +
                    @processes = load_config
         
     | 
| 
      
 23 
     | 
    
         
            +
                    if Invoker.can_run_balancer?
         
     | 
| 
      
 24 
     | 
    
         
            +
                      @power_config = Invoker::Power::Config.load_config()
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                  def http_port
         
     | 
| 
      
 29 
     | 
    
         
            +
                    power_config && power_config.http_port
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def dns_port
         
     | 
| 
      
 33 
     | 
    
         
            +
                    power_config && power_config.dns_port
         
     | 
| 
      
 34 
     | 
    
         
            +
                  end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def https_port
         
     | 
| 
      
 37 
     | 
    
         
            +
                    power_config && power_config.https_port
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def tld
         
     | 
| 
      
 41 
     | 
    
         
            +
                    power_config && power_config.tld
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def autorunnable_processes
         
     | 
| 
      
 45 
     | 
    
         
            +
                    process_to_run = processes.reject(&:disable_autorun)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    process_to_run.sort_by { |process| process.index }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def process(label)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    processes.detect { |pconfig| pconfig.label == label }
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  private
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def autodetect_config_file
         
     | 
| 
      
 56 
     | 
    
         
            +
                    Dir.glob("{invoker.ini,Procfile.dev,Procfile}").first
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def invalid_config_file?
         
     | 
| 
      
 60 
     | 
    
         
            +
                    @filename.nil?
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def load_config
         
     | 
| 
      
 64 
     | 
    
         
            +
                    @filename = to_global_file if is_global?
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
                    if is_ini?
         
     | 
| 
      
 67 
     | 
    
         
            +
                      process_ini
         
     | 
| 
      
 68 
     | 
    
         
            +
                    elsif is_procfile?
         
     | 
| 
      
 69 
     | 
    
         
            +
                      process_procfile
         
     | 
| 
      
 70 
     | 
    
         
            +
                    else
         
     | 
| 
      
 71 
     | 
    
         
            +
                      print_message_and_abort
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  def process_ini
         
     | 
| 
      
 76 
     | 
    
         
            +
                    ini_content = File.read(@filename)
         
     | 
| 
      
 77 
     | 
    
         
            +
                    document = IniParse.parse(ini_content)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    document.map do |section|
         
     | 
| 
      
 79 
     | 
    
         
            +
                      check_directory(section["directory"])
         
     | 
| 
      
 80 
     | 
    
         
            +
                      process_command_from_section(section)
         
     | 
| 
      
 81 
     | 
    
         
            +
                    end
         
     | 
| 
      
 82 
     | 
    
         
            +
                  end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  def process_procfile
         
     | 
| 
      
 85 
     | 
    
         
            +
                    procfile = Invoker::Parsers::Procfile.new(@filename)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    procfile.entries.map do |name, command|
         
     | 
| 
      
 87 
     | 
    
         
            +
                      section = { "label" => name, "command" => command }
         
     | 
| 
      
 88 
     | 
    
         
            +
                      process_command_from_section(section)
         
     | 
| 
      
 89 
     | 
    
         
            +
                    end
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                  def print_message_and_abort
         
     | 
| 
      
 93 
     | 
    
         
            +
                    Invoker::Logger.puts("\n Invalid config file. Invoker requires an ini or a Procfile.".colorize(:red))
         
     | 
| 
      
 94 
     | 
    
         
            +
                    abort
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
                  def process_command_from_section(section)
         
     | 
| 
      
 98 
     | 
    
         
            +
                    if supports_subdomain?(section)
         
     | 
| 
      
 99 
     | 
    
         
            +
                      port = pick_port(section)
         
     | 
| 
      
 100 
     | 
    
         
            +
                      if port
         
     | 
| 
      
 101 
     | 
    
         
            +
                        command = replace_port_in_command(section['command'], port)
         
     | 
| 
      
 102 
     | 
    
         
            +
                        section['port'], section['command'] = port, command
         
     | 
| 
      
 103 
     | 
    
         
            +
                      end
         
     | 
| 
      
 104 
     | 
    
         
            +
                    end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                    make_pconfig(section)
         
     | 
| 
      
 107 
     | 
    
         
            +
                  end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                  def pick_port(section)
         
     | 
| 
      
 110 
     | 
    
         
            +
                    if section['command'] =~ PORT_REGEX
         
     | 
| 
      
 111 
     | 
    
         
            +
                      @port += 1
         
     | 
| 
      
 112 
     | 
    
         
            +
                    elsif section['port']
         
     | 
| 
      
 113 
     | 
    
         
            +
                      section['port']
         
     | 
| 
      
 114 
     | 
    
         
            +
                    else
         
     | 
| 
      
 115 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 116 
     | 
    
         
            +
                    end
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                  def make_pconfig(section)
         
     | 
| 
      
 120 
     | 
    
         
            +
                    pconfig = {
         
     | 
| 
      
 121 
     | 
    
         
            +
                      label: section["label"] || section.key,
         
     | 
| 
      
 122 
     | 
    
         
            +
                      dir: expand_directory(section["directory"]),
         
     | 
| 
      
 123 
     | 
    
         
            +
                      cmd: section["command"]
         
     | 
| 
      
 124 
     | 
    
         
            +
                    }
         
     | 
| 
      
 125 
     | 
    
         
            +
                    pconfig['port'] = section['port'] if section['port']
         
     | 
| 
      
 126 
     | 
    
         
            +
                    pconfig['disable_autorun'] = section['disable_autorun'] if section['disable_autorun']
         
     | 
| 
      
 127 
     | 
    
         
            +
                    pconfig['index'] = section['index'].to_i if section['index']
         
     | 
| 
      
 128 
     | 
    
         
            +
                    section_index = pconfig['index'].to_i
         
     | 
| 
      
 129 
     | 
    
         
            +
                    if section_index
         
     | 
| 
      
 130 
     | 
    
         
            +
                      pconfig['index'] = section_index
         
     | 
| 
      
 131 
     | 
    
         
            +
                    else
         
     | 
| 
      
 132 
     | 
    
         
            +
                      pconfig['index'] = 0
         
     | 
| 
      
 133 
     | 
    
         
            +
                    end
         
     | 
| 
      
 134 
     | 
    
         
            +
             
     | 
| 
      
 135 
     | 
    
         
            +
                    sleep_duration = section['sleep'].to_i
         
     | 
| 
      
 136 
     | 
    
         
            +
                    if sleep_duration >= 0
         
     | 
| 
      
 137 
     | 
    
         
            +
                      pconfig['sleep_duration'] = sleep_duration
         
     | 
| 
      
 138 
     | 
    
         
            +
                    else
         
     | 
| 
      
 139 
     | 
    
         
            +
                      pconfig['sleep_duration'] = 0
         
     | 
| 
      
 140 
     | 
    
         
            +
                    end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                    OpenStruct.new(pconfig)
         
     | 
| 
      
 143 
     | 
    
         
            +
                  end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                  def supports_subdomain?(section)
         
     | 
| 
      
 146 
     | 
    
         
            +
                    (section['command'] =~ PORT_REGEX) || section['port']
         
     | 
| 
      
 147 
     | 
    
         
            +
                  end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                  def check_directory(app_dir)
         
     | 
| 
      
 150 
     | 
    
         
            +
                    if app_dir && !app_dir.empty? && !File.directory?(expand_directory(app_dir))
         
     | 
| 
      
 151 
     | 
    
         
            +
                      raise Invoker::Errors::InvalidConfig.new("Invalid directory #{app_dir}")
         
     | 
| 
      
 152 
     | 
    
         
            +
                    end
         
     | 
| 
      
 153 
     | 
    
         
            +
                  end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                  def expand_directory(app_dir)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    File.expand_path(app_dir) if app_dir
         
     | 
| 
      
 157 
     | 
    
         
            +
                  end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                  def replace_port_in_command(command, port)
         
     | 
| 
      
 160 
     | 
    
         
            +
                    if command =~ PORT_REGEX
         
     | 
| 
      
 161 
     | 
    
         
            +
                      command.gsub(PORT_REGEX, port.to_s)
         
     | 
| 
      
 162 
     | 
    
         
            +
                    else
         
     | 
| 
      
 163 
     | 
    
         
            +
                      command
         
     | 
| 
      
 164 
     | 
    
         
            +
                    end
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                  def is_ini?
         
     | 
| 
      
 168 
     | 
    
         
            +
                    File.extname(@filename) == '.ini'
         
     | 
| 
      
 169 
     | 
    
         
            +
                  end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                  def is_procfile?
         
     | 
| 
      
 172 
     | 
    
         
            +
                    @filename =~ /Procfile/
         
     | 
| 
      
 173 
     | 
    
         
            +
                  end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                  def to_global_file
         
     | 
| 
      
 176 
     | 
    
         
            +
                    File.join(Invoker::Power::Config.config_dir, "#{@filename}.ini")
         
     | 
| 
      
 177 
     | 
    
         
            +
                  end
         
     | 
| 
      
 178 
     | 
    
         
            +
             
     | 
| 
      
 179 
     | 
    
         
            +
                  def is_global?
         
     | 
| 
      
 180 
     | 
    
         
            +
                    @filename =~ /^\w+$/ && File.exist?(to_global_file)
         
     | 
| 
      
 181 
     | 
    
         
            +
                  end
         
     | 
| 
      
 182 
     | 
    
         
            +
                end
         
     | 
| 
      
 183 
     | 
    
         
            +
              end
         
     | 
| 
      
 184 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,86 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Parsers
         
     | 
| 
      
 3 
     | 
    
         
            +
                # rip off from foreman
         
     | 
| 
      
 4 
     | 
    
         
            +
                class Procfile
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # Initialize a Procfile
         
     | 
| 
      
 6 
     | 
    
         
            +
                  #
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # @param [String] filename (nil)  An optional filename to read from
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def initialize(filename=nil)
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @entries = []
         
     | 
| 
      
 11 
     | 
    
         
            +
                    load(filename) if filename
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  # Yield each +Procfile+ entry in order
         
     | 
| 
      
 15 
     | 
    
         
            +
                  #
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def entries
         
     | 
| 
      
 17 
     | 
    
         
            +
                    return @entries unless block_given?
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @entries.each do |(name, command)|
         
     | 
| 
      
 19 
     | 
    
         
            +
                      yield name, command
         
     | 
| 
      
 20 
     | 
    
         
            +
                    end
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  # Retrieve a +Procfile+ command by name
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # @param [String] name  The name of the Procfile entry to retrieve
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #
         
     | 
| 
      
 27 
     | 
    
         
            +
                  def [](name)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    @entries.detect { |n,c| name == n }.last
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  # Create a +Procfile+ entry
         
     | 
| 
      
 32 
     | 
    
         
            +
                  #
         
     | 
| 
      
 33 
     | 
    
         
            +
                  # @param [String] name     The name of the +Procfile+ entry to create
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # @param [String] command  The command of the +Procfile+ entry to create
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #
         
     | 
| 
      
 36 
     | 
    
         
            +
                  def []=(name, command)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    delete name
         
     | 
| 
      
 38 
     | 
    
         
            +
                    @entries << [name, command]
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  # Remove a +Procfile+ entry
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #
         
     | 
| 
      
 43 
     | 
    
         
            +
                  # @param [String] name  The name of the +Procfile+ entry to remove
         
     | 
| 
      
 44 
     | 
    
         
            +
                  #
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def delete(name)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @entries.reject! { |n,c| name == n }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  # Load a Procfile from a file
         
     | 
| 
      
 50 
     | 
    
         
            +
                  #
         
     | 
| 
      
 51 
     | 
    
         
            +
                  # @param [String] filename  The filename of the +Procfile+ to load
         
     | 
| 
      
 52 
     | 
    
         
            +
                  #
         
     | 
| 
      
 53 
     | 
    
         
            +
                  def load(filename)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    @entries.replace parse(filename)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  # Save a Procfile to a file
         
     | 
| 
      
 58 
     | 
    
         
            +
                  #
         
     | 
| 
      
 59 
     | 
    
         
            +
                  # @param [String] filename  Save the +Procfile+ to this file
         
     | 
| 
      
 60 
     | 
    
         
            +
                  #
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def save(filename)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    File.open(filename, 'w') do |file|
         
     | 
| 
      
 63 
     | 
    
         
            +
                      file.puts self.to_s
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  # Get the +Procfile+ as a +String+
         
     | 
| 
      
 68 
     | 
    
         
            +
                  #
         
     | 
| 
      
 69 
     | 
    
         
            +
                  def to_s
         
     | 
| 
      
 70 
     | 
    
         
            +
                    @entries.map do |name, command|
         
     | 
| 
      
 71 
     | 
    
         
            +
                      [ name, command ].join(": ")
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end.join("\n")
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  private
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  def parse(filename)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    File.read(filename).gsub("\r\n","\n").split("\n").map do |line|
         
     | 
| 
      
 79 
     | 
    
         
            +
                      if line =~ /^([A-Za-z0-9_-]+):\s*(.+)$/
         
     | 
| 
      
 80 
     | 
    
         
            +
                        [$1, $2]
         
     | 
| 
      
 81 
     | 
    
         
            +
                      end
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end.compact
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,133 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'em-proxy'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'http-parser'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require "invoker/power/http_parser"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "invoker/power/url_rewriter"
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 7 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 8 
     | 
    
         
            +
                class InvokerHttpProxy < EventMachine::ProxyServer::Connection
         
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_accessor :host, :ip, :port
         
     | 
| 
      
 10 
     | 
    
         
            +
                  def set_host(host, selected_backend)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    self.host = host
         
     | 
| 
      
 12 
     | 
    
         
            +
                    self.ip = selected_backend[:host]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    self.port = selected_backend[:port]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                class InvokerHttpsProxy < InvokerHttpProxy
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def post_init
         
     | 
| 
      
 19 
     | 
    
         
            +
                    super
         
     | 
| 
      
 20 
     | 
    
         
            +
                    start_tls(private_key_file: Invoker.private_key, cert_chain_file: Invoker.certificate)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                class Balancer
         
     | 
| 
      
 25 
     | 
    
         
            +
                  attr_accessor :connection, :http_parser, :session, :protocol, :upgraded_to
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def self.run(options = {})
         
     | 
| 
      
 28 
     | 
    
         
            +
                    start_http_proxy(InvokerHttpProxy, 'http', options)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    start_http_proxy(InvokerHttpsProxy, 'https', options)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def self.start_http_proxy(proxy_class, protocol, options)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    port = protocol == 'http' ? Invoker.config.http_port : Invoker.config.https_port
         
     | 
| 
      
 34 
     | 
    
         
            +
                    EventMachine.start_server('0.0.0.0', port,
         
     | 
| 
      
 35 
     | 
    
         
            +
                                              proxy_class, options) do |connection|
         
     | 
| 
      
 36 
     | 
    
         
            +
                      balancer = Balancer.new(connection, protocol)
         
     | 
| 
      
 37 
     | 
    
         
            +
                      balancer.install_callbacks
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def initialize(connection, protocol)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    @connection = connection
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @protocol = protocol
         
     | 
| 
      
 44 
     | 
    
         
            +
                    @http_parser = HttpParser.new(protocol)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @session = nil
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @upgraded_to = nil
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @buffer = []
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def install_callbacks
         
     | 
| 
      
 51 
     | 
    
         
            +
                    http_parser.on_headers_complete { |headers| headers_received(headers) }
         
     | 
| 
      
 52 
     | 
    
         
            +
                    http_parser.on_message_complete { |full_message| complete_message_received(full_message) }
         
     | 
| 
      
 53 
     | 
    
         
            +
                    connection.on_data { |data| upstream_data(data) }
         
     | 
| 
      
 54 
     | 
    
         
            +
                    connection.on_response { |backend, data| backend_data(backend, data) }
         
     | 
| 
      
 55 
     | 
    
         
            +
                    connection.on_finish { |backend, name| frontend_disconnect(backend, name) }
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def complete_message_received(full_message)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    connection.relay_to_servers(full_message)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    http_parser.reset
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def headers_received(headers)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    if @session
         
     | 
| 
      
 65 
     | 
    
         
            +
                      return
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                    @session = UUID.generate()
         
     | 
| 
      
 68 
     | 
    
         
            +
                    headers = headers.transform_keys(&:downcase)
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
                    if !headers['host'] || headers['host'].empty?
         
     | 
| 
      
 71 
     | 
    
         
            +
                      return_error_page(400)
         
     | 
| 
      
 72 
     | 
    
         
            +
                      return
         
     | 
| 
      
 73 
     | 
    
         
            +
                    end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                    dns_check_response = UrlRewriter.new.select_backend_config(headers['host'])
         
     | 
| 
      
 76 
     | 
    
         
            +
                    if dns_check_response && dns_check_response.port
         
     | 
| 
      
 77 
     | 
    
         
            +
                      connection.server(session, host: dns_check_response.ip, port: dns_check_response.port)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    else
         
     | 
| 
      
 79 
     | 
    
         
            +
                      return_error_page(404)
         
     | 
| 
      
 80 
     | 
    
         
            +
                      http_parser.reset
         
     | 
| 
      
 81 
     | 
    
         
            +
                      connection.close_connection_after_writing
         
     | 
| 
      
 82 
     | 
    
         
            +
                    end
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  def upstream_data(data)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    if upgraded_to == "websocket"
         
     | 
| 
      
 87 
     | 
    
         
            +
                      data
         
     | 
| 
      
 88 
     | 
    
         
            +
                    else
         
     | 
| 
      
 89 
     | 
    
         
            +
                      append_for_http_parsing(data)
         
     | 
| 
      
 90 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 91 
     | 
    
         
            +
                    end
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  def append_for_http_parsing(data)
         
     | 
| 
      
 95 
     | 
    
         
            +
                    http_parser << data
         
     | 
| 
      
 96 
     | 
    
         
            +
                  rescue HTTP::Parser::Error
         
     | 
| 
      
 97 
     | 
    
         
            +
                    http_parser.reset
         
     | 
| 
      
 98 
     | 
    
         
            +
                    connection.close_connection_after_writing
         
     | 
| 
      
 99 
     | 
    
         
            +
                  end
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                  def backend_data(backend, data)
         
     | 
| 
      
 102 
     | 
    
         
            +
                    @backend_data = true
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                    # check backend data for websockets connection. check for upgrade headers
         
     | 
| 
      
 105 
     | 
    
         
            +
                    # - Upgrade: websocket\r\n
         
     | 
| 
      
 106 
     | 
    
         
            +
                    if data =~ /Upgrade: websocket/
         
     | 
| 
      
 107 
     | 
    
         
            +
                      @upgraded_to = "websocket"
         
     | 
| 
      
 108 
     | 
    
         
            +
                    end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                    data
         
     | 
| 
      
 111 
     | 
    
         
            +
                  end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                  def frontend_disconnect(backend, name)
         
     | 
| 
      
 114 
     | 
    
         
            +
                    http_parser.reset
         
     | 
| 
      
 115 
     | 
    
         
            +
                    unless @backend_data
         
     | 
| 
      
 116 
     | 
    
         
            +
                      return_error_page(503)
         
     | 
| 
      
 117 
     | 
    
         
            +
                    end
         
     | 
| 
      
 118 
     | 
    
         
            +
                    @backend_data = false
         
     | 
| 
      
 119 
     | 
    
         
            +
                    connection.close_connection_after_writing if backend == session
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                  private
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                  def return_error_page(status)
         
     | 
| 
      
 125 
     | 
    
         
            +
                    http_response = Invoker::Power::HttpResponse.new()
         
     | 
| 
      
 126 
     | 
    
         
            +
                    http_response.status = status
         
     | 
| 
      
 127 
     | 
    
         
            +
                    http_response['Content-Type'] = "text/html; charset=utf-8"
         
     | 
| 
      
 128 
     | 
    
         
            +
                    http_response.use_file_as_body(File.join(File.dirname(__FILE__), "templates/#{status}.html"))
         
     | 
| 
      
 129 
     | 
    
         
            +
                    connection.send_data(http_response.http_string)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  end
         
     | 
| 
      
 131 
     | 
    
         
            +
                end
         
     | 
| 
      
 132 
     | 
    
         
            +
              end
         
     | 
| 
      
 133 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,77 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "yaml"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 5 
     | 
    
         
            +
                # Save and Load Invoker::Power config
         
     | 
| 
      
 6 
     | 
    
         
            +
                class ConfigExists < StandardError; end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                class Config
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def self.has_config?
         
     | 
| 
      
 10 
     | 
    
         
            +
                    File.exist?(config_file)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  def self.create(options = {})
         
     | 
| 
      
 14 
     | 
    
         
            +
                    if has_config?
         
     | 
| 
      
 15 
     | 
    
         
            +
                      raise ConfigExists, "Config file already exists at location #{config_file}"
         
     | 
| 
      
 16 
     | 
    
         
            +
                    end
         
     | 
| 
      
 17 
     | 
    
         
            +
                    config = new(options)
         
     | 
| 
      
 18 
     | 
    
         
            +
                    config.save
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def self.delete
         
     | 
| 
      
 22 
     | 
    
         
            +
                    if File.exist?(config_file)
         
     | 
| 
      
 23 
     | 
    
         
            +
                      File.delete(config_file)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def self.config_file
         
     | 
| 
      
 28 
     | 
    
         
            +
                    File.join(Invoker.home, ".invoker", "config")
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  def self.config_dir
         
     | 
| 
      
 32 
     | 
    
         
            +
                    File.join(Invoker.home, ".invoker")
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def initialize(options = {})
         
     | 
| 
      
 36 
     | 
    
         
            +
                    @config = options
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  def self.load_config
         
     | 
| 
      
 40 
     | 
    
         
            +
                    config_hash = File.open(config_file, "r") { |fl| YAML.load(fl) }
         
     | 
| 
      
 41 
     | 
    
         
            +
                    new(config_hash)
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def dns_port=(dns_port)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    @config[:dns_port] = dns_port
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                  def http_port=(http_port)
         
     | 
| 
      
 49 
     | 
    
         
            +
                    @config[:http_port] = http_port
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                  def https_port=(https_port)
         
     | 
| 
      
 53 
     | 
    
         
            +
                    @config[:https_port] = https_port
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def ipfw_rule_number=(ipfw_rule_number)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    @config[:ipfw_rule_number] = ipfw_rule_number
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  def dns_port; @config[:dns_port]; end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def http_port; @config[:http_port]; end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  def ipfw_rule_number; @config[:ipfw_rule_number]; end
         
     | 
| 
      
 63 
     | 
    
         
            +
                  def https_port; @config[:https_port]; end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  def tld
         
     | 
| 
      
 66 
     | 
    
         
            +
                    @config[:tld] || Invoker.default_tld
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  def save
         
     | 
| 
      
 70 
     | 
    
         
            +
                    File.open(self.class.config_file, "w") do |fl|
         
     | 
| 
      
 71 
     | 
    
         
            +
                      YAML.dump(@config, fl)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
                    self
         
     | 
| 
      
 74 
     | 
    
         
            +
                  end
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
              end
         
     | 
| 
      
 77 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require "logger"
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'rubydns'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 6 
     | 
    
         
            +
                class DNS < RubyDNS::Server
         
     | 
| 
      
 7 
     | 
    
         
            +
                  def self.server_ports
         
     | 
| 
      
 8 
     | 
    
         
            +
                    [
         
     | 
| 
      
 9 
     | 
    
         
            +
                      [:udp, '127.0.0.1', Invoker.config.dns_port],
         
     | 
| 
      
 10 
     | 
    
         
            +
                      [:tcp, '127.0.0.1', Invoker.config.dns_port]
         
     | 
| 
      
 11 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @logger = ::Logger.new($stderr)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @logger.level = ::Logger::FATAL
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def process(name, resource_class, transaction)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    if name_matches?(name) && resource_class_matches?(resource_class)
         
     | 
| 
      
 21 
     | 
    
         
            +
                      transaction.respond!("127.0.0.1")
         
     | 
| 
      
 22 
     | 
    
         
            +
                    else
         
     | 
| 
      
 23 
     | 
    
         
            +
                      transaction.fail!(:NXDomain)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  private
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def resource_class_matches?(resource_class)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    resource_class == Resolv::DNS::Resource::IN::A
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def name_matches?(name)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    name =~ /.*\.#{Invoker.config.tld}/
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,68 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 3 
     | 
    
         
            +
                class HttpParser
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_accessor :host, :parser, :protocol
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(protocol)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @protocol = protocol
         
     | 
| 
      
 8 
     | 
    
         
            +
                    @parser = HTTP::Parser.new
         
     | 
| 
      
 9 
     | 
    
         
            +
                    @header = {}
         
     | 
| 
      
 10 
     | 
    
         
            +
                    initialize_message_content
         
     | 
| 
      
 11 
     | 
    
         
            +
                    parser.on_headers_complete { complete_headers_received }
         
     | 
| 
      
 12 
     | 
    
         
            +
                    parser.on_header_field { |field_name| @last_key = field_name }
         
     | 
| 
      
 13 
     | 
    
         
            +
                    parser.on_header_value { |field_value| header_value_received(field_value) }
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                    parser.on_message_complete { complete_message_received }
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  # define a callback for invoking when complete header is parsed
         
     | 
| 
      
 19 
     | 
    
         
            +
                  def on_headers_complete(&block)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    @on_headers_complete_callback = block
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def header_value_received(value)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    @header[@last_key] = value
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  # define a callback to invoke when a full http message is received
         
     | 
| 
      
 28 
     | 
    
         
            +
                  def on_message_complete(&block)
         
     | 
| 
      
 29 
     | 
    
         
            +
                    @on_message_complete_callback = block
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def reset
         
     | 
| 
      
 33 
     | 
    
         
            +
                    @header = {}
         
     | 
| 
      
 34 
     | 
    
         
            +
                    initialize_message_content
         
     | 
| 
      
 35 
     | 
    
         
            +
                    parser.reset
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                  def << data
         
     | 
| 
      
 39 
     | 
    
         
            +
                    @full_message.write(data)
         
     | 
| 
      
 40 
     | 
    
         
            +
                    parser << data
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  private
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def complete_message_received
         
     | 
| 
      
 46 
     | 
    
         
            +
                    full_message_string = @full_message.string.dup
         
     | 
| 
      
 47 
     | 
    
         
            +
                    if full_message_string =~ /\r\n\r\n/
         
     | 
| 
      
 48 
     | 
    
         
            +
                      full_message_string.sub!(/\r\n\r\n/, "\r\nX_FORWARDED_PROTO: #{protocol}\r\n\r\n")
         
     | 
| 
      
 49 
     | 
    
         
            +
                    end
         
     | 
| 
      
 50 
     | 
    
         
            +
                    if @on_message_complete_callback
         
     | 
| 
      
 51 
     | 
    
         
            +
                      @on_message_complete_callback.call(full_message_string)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  def initialize_message_content
         
     | 
| 
      
 56 
     | 
    
         
            +
                    @full_message = StringIO.new
         
     | 
| 
      
 57 
     | 
    
         
            +
                    @full_message.set_encoding('ASCII-8BIT')
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                  # gets invoker when complete header is received
         
     | 
| 
      
 61 
     | 
    
         
            +
                  def complete_headers_received
         
     | 
| 
      
 62 
     | 
    
         
            +
                    if @on_headers_complete_callback
         
     | 
| 
      
 63 
     | 
    
         
            +
                      @on_headers_complete_callback.call(@header)
         
     | 
| 
      
 64 
     | 
    
         
            +
                    end
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
                end
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,81 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'time'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Invoker
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Power
         
     | 
| 
      
 5 
     | 
    
         
            +
                class HttpResponse
         
     | 
| 
      
 6 
     | 
    
         
            +
                  STATUS_MAPS = {
         
     | 
| 
      
 7 
     | 
    
         
            +
                    200 => "OK",
         
     | 
| 
      
 8 
     | 
    
         
            +
                    201 => "Created",
         
     | 
| 
      
 9 
     | 
    
         
            +
                    202 => "Accepted",
         
     | 
| 
      
 10 
     | 
    
         
            +
                    204 => "No Content",
         
     | 
| 
      
 11 
     | 
    
         
            +
                    205 => "Reset Content",
         
     | 
| 
      
 12 
     | 
    
         
            +
                    206 => "Partial Content",
         
     | 
| 
      
 13 
     | 
    
         
            +
                    301 => "Moved Permanently",
         
     | 
| 
      
 14 
     | 
    
         
            +
                    302 => "Found",
         
     | 
| 
      
 15 
     | 
    
         
            +
                    304 => "Not Modified",
         
     | 
| 
      
 16 
     | 
    
         
            +
                    400 => "Bad Request",
         
     | 
| 
      
 17 
     | 
    
         
            +
                    401 => "Unauthorized",
         
     | 
| 
      
 18 
     | 
    
         
            +
                    402 => "Payment Required",
         
     | 
| 
      
 19 
     | 
    
         
            +
                    403 => "Forbidden",
         
     | 
| 
      
 20 
     | 
    
         
            +
                    404 => "Not Found",
         
     | 
| 
      
 21 
     | 
    
         
            +
                    411 => "Length Required",
         
     | 
| 
      
 22 
     | 
    
         
            +
                    500 => "Internal Server Error",
         
     | 
| 
      
 23 
     | 
    
         
            +
                    501 => "Not Implemented",
         
     | 
| 
      
 24 
     | 
    
         
            +
                    502 => "Bad Gateway",
         
     | 
| 
      
 25 
     | 
    
         
            +
                    503 => "Service Unavailable",
         
     | 
| 
      
 26 
     | 
    
         
            +
                    504 => "Gateway Timeout"
         
     | 
| 
      
 27 
     | 
    
         
            +
                  }
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  HTTP_HEADER_FIELDS = [
         
     | 
| 
      
 30 
     | 
    
         
            +
                    'Cache-Control', 'Connection', 'Date',
         
     | 
| 
      
 31 
     | 
    
         
            +
                    'Pragma', 'Trailer', 'Transfer-Encoding',
         
     | 
| 
      
 32 
     | 
    
         
            +
                    'Accept-Ranges', 'Age', 'Etag',
         
     | 
| 
      
 33 
     | 
    
         
            +
                    'Server', 'Location', 'Allow',
         
     | 
| 
      
 34 
     | 
    
         
            +
                    'Content-Encoding', 'Content-Language', 'Content-Location',
         
     | 
| 
      
 35 
     | 
    
         
            +
                    'Content-MD5', 'Content-Range',
         
     | 
| 
      
 36 
     | 
    
         
            +
                    'Content-Type', 'Expires',
         
     | 
| 
      
 37 
     | 
    
         
            +
                    'Last-Modified', 'extension-header'
         
     | 
| 
      
 38 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  attr_accessor :header, :body, :status
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  def initialize
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @header = {}
         
     | 
| 
      
 44 
     | 
    
         
            +
                    header['Server'] = "Invoker #{Invoker::VERSION}"
         
     | 
| 
      
 45 
     | 
    
         
            +
                    header['Date'] = Time.now.httpdate
         
     | 
| 
      
 46 
     | 
    
         
            +
                    @status = 200
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @body = ""
         
     | 
| 
      
 48 
     | 
    
         
            +
                  end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                  def []=(key, value)
         
     | 
| 
      
 51 
     | 
    
         
            +
                    header[key] = value
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def use_file_as_body(file_name)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    if file_name && File.exist?(file_name)
         
     | 
| 
      
 56 
     | 
    
         
            +
                      file_content = File.read(file_name)
         
     | 
| 
      
 57 
     | 
    
         
            +
                      self.body = file_content
         
     | 
| 
      
 58 
     | 
    
         
            +
                    else
         
     | 
| 
      
 59 
     | 
    
         
            +
                      raise Invoker::Errors::InvalidFile, "Invalid file as body"
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  def http_string
         
     | 
| 
      
 64 
     | 
    
         
            +
                    final_string = []
         
     | 
| 
      
 65 
     | 
    
         
            +
                    final_string << "HTTP/1.1 #{status} #{STATUS_MAPS[status]}"
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    if header['Transfer-Encoding'].nil? && body.empty?
         
     | 
| 
      
 68 
     | 
    
         
            +
                      header['Content-Length'] = body.length
         
     | 
| 
      
 69 
     | 
    
         
            +
                    end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                    HTTP_HEADER_FIELDS.each do |key|
         
     | 
| 
      
 72 
     | 
    
         
            +
                      if value = header[key]
         
     | 
| 
      
 73 
     | 
    
         
            +
                        final_string << "#{key}: #{value}"
         
     | 
| 
      
 74 
     | 
    
         
            +
                      end
         
     | 
| 
      
 75 
     | 
    
         
            +
                    end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                    final_string.join("\r\n") + "\r\n\r\n" + body
         
     | 
| 
      
 78 
     | 
    
         
            +
                  end
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
              end
         
     | 
| 
      
 81 
     | 
    
         
            +
            end
         
     |