stella 0.6.0 → 0.7.0.002
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES.txt +7 -15
- data/LICENSE.txt +1 -1
- data/README.rdoc +93 -63
- data/Rakefile +32 -42
- data/bin/stella +138 -0
- data/examples/basic/listing_ids.csv +7 -0
- data/examples/basic/plan.rb +71 -0
- data/lib/stella/cli.rb +66 -0
- data/lib/stella/client.rb +199 -0
- data/lib/stella/config.rb +87 -0
- data/lib/stella/data/http/body.rb +15 -0
- data/lib/stella/data/http/request.rb +116 -0
- data/lib/stella/data/http/response.rb +92 -0
- data/lib/stella/data/http.rb +2 -257
- data/lib/stella/data.rb +85 -0
- data/lib/stella/dsl.rb +5 -0
- data/lib/stella/engine/functional.rb +39 -0
- data/lib/stella/engine/load.rb +106 -0
- data/lib/stella/engine.rb +55 -0
- data/lib/stella/exceptions.rb +15 -0
- data/lib/stella/guidelines.rb +18 -0
- data/lib/stella/mixins.rb +2 -0
- data/lib/stella/stats.rb +3 -7
- data/lib/stella/testplan/stats.rb +26 -0
- data/lib/stella/testplan/usecase.rb +67 -0
- data/lib/stella/testplan.rb +95 -220
- data/lib/{util → stella/utils}/httputil.rb +0 -0
- data/lib/stella/utils.rb +126 -0
- data/lib/stella/version.rb +15 -0
- data/lib/stella.rb +58 -104
- data/lib/threadify.rb +0 -6
- data/stella.gemspec +43 -49
- data/support/example_webapp.rb +246 -0
- data/support/useragents.txt +75 -0
- metadata +68 -32
- data/bin/example_test.rb +0 -82
- data/bin/example_webapp.rb +0 -63
- data/lib/logger.rb +0 -79
- data/lib/stella/clients.rb +0 -161
- data/lib/stella/command/base.rb +0 -20
- data/lib/stella/command/form.rb +0 -36
- data/lib/stella/command/get.rb +0 -44
- data/lib/stella/common.rb +0 -53
- data/lib/stella/crypto.rb +0 -88
- data/lib/stella/data/domain.rb +0 -82
- data/lib/stella/environment.rb +0 -66
- data/lib/stella/functest.rb +0 -105
- data/lib/stella/loadtest.rb +0 -186
- data/lib/stella/testrunner.rb +0 -64
- data/lib/storable.rb +0 -280
- data/lib/timeunits.rb +0 -65
- data/tryouts/drb/drb_test.rb +0 -65
- data/tryouts/drb/open4.rb +0 -19
- data/tryouts/drb/slave.rb +0 -27
- data/tryouts/oo_tryout.rb +0 -30
| @@ -0,0 +1,199 @@ | |
| 1 | 
            +
            require "observer"
         | 
| 2 | 
            +
            require "tempfile"
         | 
| 3 | 
            +
            require 'httpclient'
         | 
| 4 | 
            +
            require 'nokogiri'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Stella
         | 
| 7 | 
            +
              class Client
         | 
| 8 | 
            +
                include Observable
         | 
| 9 | 
            +
                attr_reader :client_id
         | 
| 10 | 
            +
                attr_accessor :base_uri
         | 
| 11 | 
            +
                attr_accessor :proxy
         | 
| 12 | 
            +
                attr_reader :stats
         | 
| 13 | 
            +
                def initialize(base_uri=nil, client_id=1)
         | 
| 14 | 
            +
                  @base_uri, @client_id = base_uri, client_id
         | 
| 15 | 
            +
                  @cookie_file = Tempfile.new('stella-cookie')
         | 
| 16 | 
            +
                  @stats = Stella::Stats.new("Client #{@client_id}")
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
                
         | 
| 19 | 
            +
                def execute(usecase)
         | 
| 20 | 
            +
                  http_client = generate_http_client
         | 
| 21 | 
            +
                  container = Container.new(usecase)
         | 
| 22 | 
            +
                  counter = 0
         | 
| 23 | 
            +
                  usecase.requests.each do |req|
         | 
| 24 | 
            +
                    counter += 1
         | 
| 25 | 
            +
                    uri_obj = URI.parse(req.uri)
         | 
| 26 | 
            +
                    params = prepare_params(usecase, req.params)
         | 
| 27 | 
            +
                    uri = build_request_uri uri_obj, params, container
         | 
| 28 | 
            +
                    raise NoHostDefined, uri_obj if uri.host.nil? || uri.host.empty?
         | 
| 29 | 
            +
                    
         | 
| 30 | 
            +
                    meth = req.http_method.to_s.downcase
         | 
| 31 | 
            +
                    Stella.ld "#{meth}: " << "#{uri_obj.to_s} " << req.params.inspect
         | 
| 32 | 
            +
                    
         | 
| 33 | 
            +
                    changed and notify_observers(:send_request, @client_id, usecase, meth, uri, req, params, counter)
         | 
| 34 | 
            +
                    begin
         | 
| 35 | 
            +
                      container.response = http_client.send(meth, uri, params) # booya!
         | 
| 36 | 
            +
                      changed and notify_observers(:receive_response, @client_id, usecase, meth, uri, req, params, container)
         | 
| 37 | 
            +
                    rescue => ex
         | 
| 38 | 
            +
                      changed and notify_observers(:request_error, @client_id, usecase, meth, uri, req, params, ex)
         | 
| 39 | 
            +
                      next
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                    
         | 
| 42 | 
            +
                    
         | 
| 43 | 
            +
                    ret = execute_response_handler container, req
         | 
| 44 | 
            +
                    
         | 
| 45 | 
            +
                    Drydock::Screen.flush
         | 
| 46 | 
            +
                    
         | 
| 47 | 
            +
                    if ret.kind_of?(ResponseModifier)
         | 
| 48 | 
            +
                      case ret.class.to_s
         | 
| 49 | 
            +
                      when "Stella::Client::Repeat"
         | 
| 50 | 
            +
                        Stella.ld "REPETITION: #{counter} of #{ret.times+1}"
         | 
| 51 | 
            +
                        redo if counter <= ret.times
         | 
| 52 | 
            +
                      end
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                    
         | 
| 55 | 
            +
                    counter = 0 # reset
         | 
| 56 | 
            +
                    run_sleeper(req.wait) if req.wait && !benchmark?
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
                end
         | 
| 59 | 
            +
                
         | 
| 60 | 
            +
                def enable_benchmark_mode; @bm = true; end
         | 
| 61 | 
            +
                def disable_benchmark_mode; @bm = false; end
         | 
| 62 | 
            +
                def benchmark?; @bm == true; end
         | 
| 63 | 
            +
                  
         | 
| 64 | 
            +
              private
         | 
| 65 | 
            +
                def run_sleeper(wait)
         | 
| 66 | 
            +
                  if wait.is_a?(Range)
         | 
| 67 | 
            +
                    ms = rand(wait.last * 1000).to_f 
         | 
| 68 | 
            +
                    ms = wait.first if ms < wait.first
         | 
| 69 | 
            +
                  else
         | 
| 70 | 
            +
                    ms = wait * 1000
         | 
| 71 | 
            +
                  end
         | 
| 72 | 
            +
                  sleep ms / 1000
         | 
| 73 | 
            +
                end
         | 
| 74 | 
            +
                
         | 
| 75 | 
            +
                def generate_http_client
         | 
| 76 | 
            +
                  if @proxy
         | 
| 77 | 
            +
                    http_client = HTTPClient.new(@proxy.uri)
         | 
| 78 | 
            +
                    http_client.set_proxy_auth(@proxy.user, @proxy.pass) if @proxy.user
         | 
| 79 | 
            +
                  else
         | 
| 80 | 
            +
                    http_client = HTTPClient.new
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                  http_client.set_cookie_store @cookie_file.to_s
         | 
| 83 | 
            +
                  http_client
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
                
         | 
| 86 | 
            +
                def prepare_params(usecase, params)
         | 
| 87 | 
            +
                  newparams = {}
         | 
| 88 | 
            +
                  params.each_pair do |n,v|
         | 
| 89 | 
            +
                    v = usecase.instance_eval &v if v.is_a?(Proc)
         | 
| 90 | 
            +
                    newparams[n] = v
         | 
| 91 | 
            +
                  end
         | 
| 92 | 
            +
                  newparams
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
                
         | 
| 95 | 
            +
                # Testplan URIs can be relative or absolute. Either one can
         | 
| 96 | 
            +
                # contain variables in the form <tt>:varname</tt>, as in:
         | 
| 97 | 
            +
                #
         | 
| 98 | 
            +
                #     http://example.com/product/:productid
         | 
| 99 | 
            +
                # 
         | 
| 100 | 
            +
                # This method creates a new URI object using the @base_uri
         | 
| 101 | 
            +
                # if necessary and replaces all variables with literal values.
         | 
| 102 | 
            +
                # If no replacement value can be found, the variable is not touched. 
         | 
| 103 | 
            +
                def build_request_uri(requri, params, container)
         | 
| 104 | 
            +
                  uri = ""
         | 
| 105 | 
            +
                  request_uri = requri.to_s
         | 
| 106 | 
            +
                  if requri.host.nil?
         | 
| 107 | 
            +
                    uri = base_uri.to_s
         | 
| 108 | 
            +
                    uri.gsub! /\/$/, ''  # Don't double up on the first slash
         | 
| 109 | 
            +
                    request_uri = '/' << request_uri unless request_uri.match(/^\//)
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
                  # We call req.uri again because we need  
         | 
| 112 | 
            +
                  # to modify request_uri inside the loop. 
         | 
| 113 | 
            +
                  requri.to_s.scan(/:([a-z_]+)/i) do |instances|
         | 
| 114 | 
            +
                    instances.each do |varname|
         | 
| 115 | 
            +
                      val = find_replacement_value(varname, params, container)
         | 
| 116 | 
            +
                      #Stella.ld "FOUND: #{val}"
         | 
| 117 | 
            +
                      request_uri.gsub! /:#{varname}/, val.to_s unless val.nil?
         | 
| 118 | 
            +
                    end
         | 
| 119 | 
            +
                  end
         | 
| 120 | 
            +
                  uri << request_uri
         | 
| 121 | 
            +
                  URI.parse uri
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
                
         | 
| 124 | 
            +
                # Testplan URIs can contain variables in the form <tt>:varname</tt>. 
         | 
| 125 | 
            +
                # This method looks at the request parameters and then at the 
         | 
| 126 | 
            +
                # usecase's resource hash for a replacement value. 
         | 
| 127 | 
            +
                # If not found, returns nil. 
         | 
| 128 | 
            +
                def find_replacement_value(name, params, container)
         | 
| 129 | 
            +
                  value = nil
         | 
| 130 | 
            +
                  #Stella.ld "REPLACE: #{name}"
         | 
| 131 | 
            +
                  #Stella.ld "PARAMS: #{params.inspect}"
         | 
| 132 | 
            +
                  #Stella.ld "IVARS: #{container.instance_variables}"
         | 
| 133 | 
            +
                  value = params[name.to_sym] 
         | 
| 134 | 
            +
                  value = container.resource name.to_sym if value.nil?
         | 
| 135 | 
            +
                  value
         | 
| 136 | 
            +
                end 
         | 
| 137 | 
            +
                
         | 
| 138 | 
            +
                # Find the appropriate response handler by executing the
         | 
| 139 | 
            +
                # HTTP response status against the configured handlers. 
         | 
| 140 | 
            +
                # If several match, the first one is used. 
         | 
| 141 | 
            +
                def execute_response_handler(container, req)
         | 
| 142 | 
            +
                  handlers = req.response.select do |regex,handler|
         | 
| 143 | 
            +
                    regex = /#{regex}/ unless regex.is_a? Regexp
         | 
| 144 | 
            +
                    Stella.ld "HANDLER REGEX: #{regex} (#{container.status})"
         | 
| 145 | 
            +
                    container.status.to_s =~ regex
         | 
| 146 | 
            +
                  end
         | 
| 147 | 
            +
                  ret = nil
         | 
| 148 | 
            +
                  unless handlers.empty?
         | 
| 149 | 
            +
                    begin
         | 
| 150 | 
            +
                      changed
         | 
| 151 | 
            +
                      ret = container.instance_eval &handlers.values.first
         | 
| 152 | 
            +
                      notify_observers(:execute_response_handler, @client_id, req, container)
         | 
| 153 | 
            +
                    rescue => ex
         | 
| 154 | 
            +
                      notify_observers(:error_execute_response_handler, @client_id, ex, req, container)
         | 
| 155 | 
            +
                      Stella.ld ex.message, ex.backtrace
         | 
| 156 | 
            +
                    end
         | 
| 157 | 
            +
                  end
         | 
| 158 | 
            +
                  ret
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
                
         | 
| 161 | 
            +
                class Container
         | 
| 162 | 
            +
                  attr_accessor :usecase
         | 
| 163 | 
            +
                  attr_accessor :response
         | 
| 164 | 
            +
                  def initialize(usecase)
         | 
| 165 | 
            +
                    @usecase = usecase
         | 
| 166 | 
            +
                  end
         | 
| 167 | 
            +
                  
         | 
| 168 | 
            +
                  def doc
         | 
| 169 | 
            +
                    # NOTE: It's important to parse the document on every 
         | 
| 170 | 
            +
                    # request because this container is available for the
         | 
| 171 | 
            +
                    # entire life of a usecase. 
         | 
| 172 | 
            +
                    case @response.header['Content-Type']
         | 
| 173 | 
            +
                    when ['text/html']
         | 
| 174 | 
            +
                      Nokogiri::HTML(body)
         | 
| 175 | 
            +
                    when ['text/yaml']
         | 
| 176 | 
            +
                      YAML.load(body)
         | 
| 177 | 
            +
                    end
         | 
| 178 | 
            +
                  end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
                  def body; @response.body.content; end
         | 
| 181 | 
            +
                  def headers; @response.header; end
         | 
| 182 | 
            +
                    alias_method :header, :headers
         | 
| 183 | 
            +
                  def status; @response.status; end
         | 
| 184 | 
            +
                  def set(n, v); usecase.resource n, v; end
         | 
| 185 | 
            +
                  def resource(n);    usecase.resource n;    end
         | 
| 186 | 
            +
                  def wait(t); sleep t; end
         | 
| 187 | 
            +
                  
         | 
| 188 | 
            +
                  def repeat(t=1); Repeat.new(t); end
         | 
| 189 | 
            +
                end
         | 
| 190 | 
            +
                
         | 
| 191 | 
            +
                class ResponseModifier; end
         | 
| 192 | 
            +
                class Repeat < ResponseModifier; 
         | 
| 193 | 
            +
                  attr_accessor :times
         | 
| 194 | 
            +
                  def initialize(times)
         | 
| 195 | 
            +
                    @times = times
         | 
| 196 | 
            +
                  end
         | 
| 197 | 
            +
                end
         | 
| 198 | 
            +
              end
         | 
| 199 | 
            +
            end
         | 
| @@ -0,0 +1,87 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
             | 
| 3 | 
            +
            class Stella::Config < Storable
         | 
| 4 | 
            +
              include Gibbler::Complex
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              field :source
         | 
| 7 | 
            +
              field :apikey
         | 
| 8 | 
            +
              field :secret
         | 
| 9 | 
            +
                
         | 
| 10 | 
            +
               # Returns true when the current config matches the default config
         | 
| 11 | 
            +
              def default?; to_hash.gibbler == DEFAULT_CONFIG_HASH; end
         | 
| 12 | 
            +
              
         | 
| 13 | 
            +
              def self.each_path(&blk)
         | 
| 14 | 
            +
                [PROJECT_PATH, USER_PATH].each do |path|
         | 
| 15 | 
            +
                  Stella.ld "Loading #{path}"
         | 
| 16 | 
            +
                  blk.call(path) if File.exists? path
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
              
         | 
| 20 | 
            +
              def self.refresh
         | 
| 21 | 
            +
                conf = {}
         | 
| 22 | 
            +
                Stella::Config.each_path do |path| 
         | 
| 23 | 
            +
                  tmp = YAML.load_file path
         | 
| 24 | 
            +
                  conf.merge! tmp if tmp
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
                from_hash conf
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
              
         | 
| 29 | 
            +
              def self.init
         | 
| 30 | 
            +
                raise AlreadyInitialized, PROJECT_PATH if File.exists? PROJECT_PATH
         | 
| 31 | 
            +
                dir = File.dirname USER_PATH
         | 
| 32 | 
            +
                Dir.mkdir(dir, 0700) unless File.exists? dir
         | 
| 33 | 
            +
                unless File.exists? USER_PATH
         | 
| 34 | 
            +
                  Stella.li "Creating #{USER_PATH} (Add your credentials here)"
         | 
| 35 | 
            +
                  Stella::Utils.write_to_file(USER_PATH, DEFAULT_CONFIG, 'w', 0600)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
                
         | 
| 38 | 
            +
                dir = File.dirname PROJECT_PATH
         | 
| 39 | 
            +
                Dir.mkdir(dir, 0700) unless File.exists? dir
         | 
| 40 | 
            +
                
         | 
| 41 | 
            +
                Stella.li "Creating #{PROJECT_PATH}"
         | 
| 42 | 
            +
                Stella::Utils.write_to_file(PROJECT_PATH, 'target:', 'w', 0600)
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
              
         | 
| 45 | 
            +
              def self.blast
         | 
| 46 | 
            +
                if File.exists? USER_PATH
         | 
| 47 | 
            +
                  Stella.li "Blasting #{USER_PATH}"
         | 
| 48 | 
            +
                  FileUtils.rm_rf File.dirname(USER_PATH)
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
                if File.exists? PROJECT_PATH
         | 
| 51 | 
            +
                  Stella.li "Blasting #{PROJECT_PATH}"
         | 
| 52 | 
            +
                  FileUtils.rm_rf File.dirname(PROJECT_PATH)
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             
         | 
| 56 | 
            +
              
         | 
| 57 | 
            +
              private 
         | 
| 58 | 
            +
              
         | 
| 59 | 
            +
              def self.find_project_config
         | 
| 60 | 
            +
                dir = Dir.pwd.split File::SEPARATOR
         | 
| 61 | 
            +
                path = nil
         | 
| 62 | 
            +
                while !dir.empty?
         | 
| 63 | 
            +
                  tmp = File.join(dir.join(File::SEPARATOR), DIR_NAME, 'config')
         | 
| 64 | 
            +
                  Stella.ld " -> looking for #{tmp}"
         | 
| 65 | 
            +
                  path = tmp and break if File.exists? tmp
         | 
| 66 | 
            +
                  dir.pop
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
                path ||= File.join(Dir.pwd, DIR_NAME, 'config')
         | 
| 69 | 
            +
                path
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
              
         | 
| 72 | 
            +
              
         | 
| 73 | 
            +
              unless defined?(DIR_NAME)
         | 
| 74 | 
            +
                DIR_NAME = Stella.sysinfo.os == :windows ? 'Stella' : '.stella'
         | 
| 75 | 
            +
                USER_PATH = File.join(Stella.sysinfo.home, DIR_NAME, 'config')
         | 
| 76 | 
            +
                PROJECT_PATH = Stella::Config.find_project_config
         | 
| 77 | 
            +
                DEFAULT_CONFIG = <<CONF
         | 
| 78 | 
            +
            apikey: ''
         | 
| 79 | 
            +
            secret: ''
         | 
| 80 | 
            +
            remote: stella.solutious.com:443
         | 
| 81 | 
            +
            CONF
         | 
| 82 | 
            +
                DEFAULT_CONFIG_HASH = YAML.load(DEFAULT_CONFIG).gibbler
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
                
         | 
| 85 | 
            +
              class AlreadyInitialized < Stella::Error; end
         | 
| 86 | 
            +
            end
         | 
| 87 | 
            +
             | 
| @@ -0,0 +1,116 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Stella::Data::HTTP
         | 
| 4 | 
            +
              class Request < Storable
         | 
| 5 | 
            +
                include Gibbler::Complex
         | 
| 6 | 
            +
                include Stella::Data::Helpers
         | 
| 7 | 
            +
                
         | 
| 8 | 
            +
                # A hash containing blocks to be executed depending on the HTTP response status.
         | 
| 9 | 
            +
                # The hash keys are numeric HTTP Status Codes. 
         | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                #     200 => { ... }
         | 
| 12 | 
            +
                #     304 => { ... }
         | 
| 13 | 
            +
                #     500 => { ... }
         | 
| 14 | 
            +
                #
         | 
| 15 | 
            +
                attr_accessor :response_handler
         | 
| 16 | 
            +
                
         | 
| 17 | 
            +
                field :desc
         | 
| 18 | 
            +
                field :header 
         | 
| 19 | 
            +
                field :uri
         | 
| 20 | 
            +
                field :wait
         | 
| 21 | 
            +
                field :params
         | 
| 22 | 
            +
                field :body 
         | 
| 23 | 
            +
                field :http_method
         | 
| 24 | 
            +
                field :http_version
         | 
| 25 | 
            +
                field :content_type
         | 
| 26 | 
            +
                
         | 
| 27 | 
            +
                def has_body?
         | 
| 28 | 
            +
                  !@body.nil? && !@body.empty?
         | 
| 29 | 
            +
                end
         | 
| 30 | 
            +
                
         | 
| 31 | 
            +
                def initialize (method, uri_str, version="1.1", &definition)
         | 
| 32 | 
            +
                  @uri = uri_str
         | 
| 33 | 
            +
                  @http_method, @http_version = method, version
         | 
| 34 | 
            +
                  @headers, @params, @response_handler = {}, {}, {}
         | 
| 35 | 
            +
                  @wait = 0
         | 
| 36 | 
            +
                  @desc = "Request"
         | 
| 37 | 
            +
                  @body = Stella::Data::HTTP::Body.new
         | 
| 38 | 
            +
                  instance_eval &definition unless definition.nil?
         | 
| 39 | 
            +
                end
         | 
| 40 | 
            +
                
         | 
| 41 | 
            +
                def desc(*args)
         | 
| 42 | 
            +
                  @desc = args.first unless args.empty?
         | 
| 43 | 
            +
                  @desc
         | 
| 44 | 
            +
                end
         | 
| 45 | 
            +
                
         | 
| 46 | 
            +
                def content_type(*args)
         | 
| 47 | 
            +
                  @content_type = args.first unless args.empty?
         | 
| 48 | 
            +
                  @content_type
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
                
         | 
| 51 | 
            +
                def wait(*args)
         | 
| 52 | 
            +
                  @wait = args.first unless args.empty?
         | 
| 53 | 
            +
                  @wait
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
                alias_method :sleep, :wait
         | 
| 56 | 
            +
                
         | 
| 57 | 
            +
                def headers(*args)
         | 
| 58 | 
            +
                  @headers.merge! args.first unless args.empty?
         | 
| 59 | 
            +
                  @headers
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
                alias_method :header, :headers
         | 
| 62 | 
            +
                
         | 
| 63 | 
            +
                def params(*args)
         | 
| 64 | 
            +
                  @params.merge! args.first unless args.empty?
         | 
| 65 | 
            +
                  @params
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
                alias_method :param, :params
         | 
| 68 | 
            +
                
         | 
| 69 | 
            +
                def response(*args, &definition)
         | 
| 70 | 
            +
                  if definition.nil?
         | 
| 71 | 
            +
                    @response_handler
         | 
| 72 | 
            +
                  else
         | 
| 73 | 
            +
                    args << 200 if args.empty?
         | 
| 74 | 
            +
                    args.each do |status|
         | 
| 75 | 
            +
                      @response_handler[status] = definition
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
                
         | 
| 80 | 
            +
                # +content+ can be literal content or a file path
         | 
| 81 | 
            +
                def body(*args)
         | 
| 82 | 
            +
                  return @body if args.empty?
         | 
| 83 | 
            +
                  content, form_param, content_type = *args
         | 
| 84 | 
            +
                  
         | 
| 85 | 
            +
                  @body.form_param = form_param if form_param
         | 
| 86 | 
            +
                  @body.content_type = content_type if content_type
         | 
| 87 | 
            +
                  
         | 
| 88 | 
            +
                  if File.exists?(content)
         | 
| 89 | 
            +
                    @body.content = File.new(content)
         | 
| 90 | 
            +
                    @body.content_type ||= "application/x-www-form-urlencoded"
         | 
| 91 | 
            +
                  else
         | 
| 92 | 
            +
                    @body.content = content
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
                  
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
                
         | 
| 97 | 
            +
                def inspect
         | 
| 98 | 
            +
                  str = "%s %s HTTP/%s" % [http_method, uri.to_s, http_version]
         | 
| 99 | 
            +
                  #str << $/ + headers.join($/) unless headers.empty?
         | 
| 100 | 
            +
                  #str << $/ + $/ + body.to_s if body
         | 
| 101 | 
            +
                  str
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
                
         | 
| 104 | 
            +
                def to_s
         | 
| 105 | 
            +
                  str = "%s %s HTTP/%s" % [http_method, uri.to_s, http_version]
         | 
| 106 | 
            +
                  str
         | 
| 107 | 
            +
                end
         | 
| 108 | 
            +
                
         | 
| 109 | 
            +
                def cookies
         | 
| 110 | 
            +
                  return [] if !header.is_a?(Hash) || header[:Cookie].empty?
         | 
| 111 | 
            +
                  header[:Cookie] 
         | 
| 112 | 
            +
                end
         | 
| 113 | 
            +
                
         | 
| 114 | 
            +
              end
         | 
| 115 | 
            +
              
         | 
| 116 | 
            +
            end
         | 
| @@ -0,0 +1,92 @@ | |
| 1 | 
            +
             | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Stella::Data::HTTP
         | 
| 4 | 
            +
              
         | 
| 5 | 
            +
              class Response < Storable
         | 
| 6 | 
            +
                include Gibbler::Complex
         | 
| 7 | 
            +
                
         | 
| 8 | 
            +
                attr_reader :raw_data
         | 
| 9 | 
            +
                
         | 
| 10 | 
            +
                field :time => DateTime
         | 
| 11 | 
            +
                field :client_ip => String
         | 
| 12 | 
            +
                field :server_ip => String
         | 
| 13 | 
            +
                field :header => String
         | 
| 14 | 
            +
                field :body => String
         | 
| 15 | 
            +
                field :status => String
         | 
| 16 | 
            +
                field :message => String
         | 
| 17 | 
            +
                field :http_version => String
         | 
| 18 | 
            +
                
         | 
| 19 | 
            +
                def initialize(raw_data=nil)
         | 
| 20 | 
            +
                  @raw_data = raw_data
         | 
| 21 | 
            +
                  parse(@raw_data)
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
                
         | 
| 24 | 
            +
                def parse(raw)
         | 
| 25 | 
            +
                  return unless raw
         | 
| 26 | 
            +
                  @status, @http_version, @message, @header, @body = HTTPUtil::parse_http_response(raw)
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
                
         | 
| 29 | 
            +
                def has_body?
         | 
| 30 | 
            +
                  !@body.nil? && !@body.empty?
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
                def has_request?
         | 
| 33 | 
            +
                  false
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
                def has_response?
         | 
| 36 | 
            +
                  false
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
                
         | 
| 39 | 
            +
                
         | 
| 40 | 
            +
                def body
         | 
| 41 | 
            +
                  return nil unless @body
         | 
| 42 | 
            +
                  #TODO: Move to HTTPResponse::Body.to_s
         | 
| 43 | 
            +
                  if is_binary?
         | 
| 44 | 
            +
                   "[skipping binary content]"
         | 
| 45 | 
            +
                  elsif is_gzip?
         | 
| 46 | 
            +
                    #require 'zlib'
         | 
| 47 | 
            +
                    #Zlib::Inflate.inflate(@body)
         | 
| 48 | 
            +
                     "[skipping gzip content]"
         | 
| 49 | 
            +
                  else
         | 
| 50 | 
            +
                    @body
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
                
         | 
| 54 | 
            +
                def headers
         | 
| 55 | 
            +
                  headers = []
         | 
| 56 | 
            +
                  header.each_pair do |n,v|
         | 
| 57 | 
            +
                    headers << [n.to_s.gsub('_', '-'), v[0]]
         | 
| 58 | 
            +
                  end
         | 
| 59 | 
            +
                  headers
         | 
| 60 | 
            +
                end
         | 
| 61 | 
            +
                
         | 
| 62 | 
            +
                def is_binary?
         | 
| 63 | 
            +
                  (!is_text?) == true
         | 
| 64 | 
            +
                end
         | 
| 65 | 
            +
                
         | 
| 66 | 
            +
                def is_text?
         | 
| 67 | 
            +
                  (!header[:Content_Type].nil? && (header[:Content_Type][0].is_a? String) && header[:Content_Type][0][/text/] != nil)
         | 
| 68 | 
            +
                end
         | 
| 69 | 
            +
                
         | 
| 70 | 
            +
                def is_gzip?
         | 
| 71 | 
            +
                  (!header[:Content_Encoding].nil? && (header[:Content_Encoding][0].is_a? String) && header[:Content_Encoding][0][/gzip/] != nil)
         | 
| 72 | 
            +
                end
         | 
| 73 | 
            +
                
         | 
| 74 | 
            +
                def inspect
         | 
| 75 | 
            +
                  str = "HTTP/%s %s (%s)" % [@http_version, @status, @message]
         | 
| 76 | 
            +
                  str << $/ + headers.join($/)
         | 
| 77 | 
            +
                  str << $/ + $/ + body if body
         | 
| 78 | 
            +
                  str
         | 
| 79 | 
            +
                end
         | 
| 80 | 
            +
                
         | 
| 81 | 
            +
                def to_s
         | 
| 82 | 
            +
                  str = "%s: HTTP/%s %s (%s)" % [time.strftime(NICE_TIME_FORMAT), @http_version, @status, @message]
         | 
| 83 | 
            +
                  str 
         | 
| 84 | 
            +
                end
         | 
| 85 | 
            +
                
         | 
| 86 | 
            +
                
         | 
| 87 | 
            +
                def cookies
         | 
| 88 | 
            +
                  return [] unless header.is_a?(Array) && !header[:Set_Cookie].empty?
         | 
| 89 | 
            +
                  header[:Set_Cookie] 
         | 
| 90 | 
            +
                end
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
            end
         |