right_http_connection 1.3.0 → 1.4.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.
- data/History.txt +25 -1
- data/lib/{net_fix.rb → base/net_fix.rb} +21 -3
- data/lib/{support.rb → base/support.rb} +0 -0
- data/lib/{version.rb → base/version.rb} +1 -1
- data/lib/right_http_connection.rb +112 -38
- data/right_http_connection.gemspec +1 -1
- data/spec/client/cacert.cer +0 -0
- data/spec/client/cacert.pem +17 -0
- data/spec/client/cert.pem +18 -0
- data/spec/client/key.pem +27 -0
- data/spec/really_dumb_webserver.rb +13 -4
- metadata +13 -11
    
        data/History.txt
    CHANGED
    
    | @@ -57,6 +57,30 @@ Initial public release | |
| 57 57 | 
             
              * fixed a bug: <NoMethodError: You have a nil object when you didn't expect it!
         | 
| 58 58 | 
             
                              The error occurred while evaluating nil.body_stream>
         | 
| 59 59 |  | 
| 60 | 
            -
            == 1.2.5 | 
| 60 | 
            +
            == 1.2.5
         | 
| 61 61 |  | 
| 62 62 | 
             
            - ActiveSupport dependency removal 
         | 
| 63 | 
            +
             | 
| 64 | 
            +
             | 
| 65 | 
            +
            == 1.3.0
         | 
| 66 | 
            +
             - Added:
         | 
| 67 | 
            +
               - support for using through proxies
         | 
| 68 | 
            +
               - functional tests
         | 
| 69 | 
            +
             | 
| 70 | 
            +
            == 1.3.1
         | 
| 71 | 
            +
             - Added:
         | 
| 72 | 
            +
               - SSL certificate handshake support
         | 
| 73 | 
            +
               - more specs
         | 
| 74 | 
            +
               - ability to give client side key and certificate inline
         | 
| 75 | 
            +
             - Fixed: some minor glitches
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            == 1.4.0
         | 
| 78 | 
            +
              - Added
         | 
| 79 | 
            +
                - license
         | 
| 80 | 
            +
                - HTTP_PROXY env variable support
         | 
| 81 | 
            +
              - Fixed
         | 
| 82 | 
            +
                - context-length issue (thx kristianm)
         | 
| 83 | 
            +
                - exception handling
         | 
| 84 | 
            +
                - connection is now closed on any exception
         | 
| 85 | 
            +
                - connection is reestablished when credentials change
         | 
| 86 | 
            +
                - some other minor bugs
         | 
| @@ -91,18 +91,30 @@ module Net | |
| 91 91 | 
             
                private
         | 
| 92 92 |  | 
| 93 93 | 
             
                def send_request_with_body(sock, ver, path, body, send_only=nil)
         | 
| 94 | 
            -
                  self.content_length = body.length
         | 
| 94 | 
            +
                  self.content_length = body.respond_to?(:bytesize) ? body.bytesize : body.length
         | 
| 95 95 | 
             
                  delete 'Transfer-Encoding'
         | 
| 96 96 | 
             
                  supply_default_content_type
         | 
| 97 97 | 
             
                  write_header(sock, ver, path) unless send_only == :body
         | 
| 98 | 
            -
                  sock.write(body) | 
| 98 | 
            +
                  sock.write(body && body.to_s) unless send_only == :header
         | 
| 99 99 | 
             
                end
         | 
| 100 100 |  | 
| 101 101 | 
             
                def send_request_with_body_stream(sock, ver, path, f, send_only=nil)
         | 
| 102 | 
            +
                  # KD: Fix 'content-length': it must not be greater than a piece of file left to be read.
         | 
| 103 | 
            +
                  # Otherwise the connection may behave like crazy causing 4xx or 5xx responses
         | 
| 104 | 
            +
                  #
         | 
| 105 | 
            +
                  # Only do this helpful thing if the stream responds to :pos (it may be something
         | 
| 106 | 
            +
                  # that responds to :read and :size but not :pos).
         | 
| 107 | 
            +
                  if f.respond_to?(:pos)
         | 
| 108 | 
            +
                    file_size           = f.respond_to?(:lstat) ? f.lstat.size : f.size
         | 
| 109 | 
            +
                    bytes_to_read       = [ file_size - f.pos, self.content_length.to_i ].sort.first
         | 
| 110 | 
            +
                    self.content_length = bytes_to_read
         | 
| 111 | 
            +
                  end
         | 
| 112 | 
            +
             | 
| 102 113 | 
             
                  unless content_length() or chunked?
         | 
| 103 114 | 
             
                    raise ArgumentError,
         | 
| 104 115 | 
             
                        "Content-Length not given and Transfer-Encoding is not `chunked'"
         | 
| 105 116 | 
             
                  end
         | 
| 117 | 
            +
                  bytes_to_read ||= content_length()
         | 
| 106 118 | 
             
                  supply_default_content_type
         | 
| 107 119 | 
             
                  write_header(sock, ver, path) unless send_only == :body
         | 
| 108 120 | 
             
                  unless send_only == :header
         | 
| @@ -112,8 +124,14 @@ module Net | |
| 112 124 | 
             
                      end
         | 
| 113 125 | 
             
                      sock.write "0\r\n\r\n"
         | 
| 114 126 | 
             
                    else
         | 
| 115 | 
            -
                       | 
| 127 | 
            +
                      # KD: When we read/write over file EOF it sometimes make the connection unstable
         | 
| 128 | 
            +
                      read_size = [ @@local_read_size, bytes_to_read ].sort.first
         | 
| 129 | 
            +
                      while s = f.read(read_size)
         | 
| 116 130 | 
             
                        sock.write s
         | 
| 131 | 
            +
                        # Make sure we do not read over EOF or more than expected content-length
         | 
| 132 | 
            +
                        bytes_to_read -= read_size
         | 
| 133 | 
            +
                        break if bytes_to_read <= 0
         | 
| 134 | 
            +
                        read_size = bytes_to_read if bytes_to_read < read_size
         | 
| 117 135 | 
             
                      end
         | 
| 118 136 | 
             
                    end
         | 
| 119 137 | 
             
                  end
         | 
| 
            File without changes
         | 
| @@ -24,7 +24,7 @@ | |
| 24 24 | 
             
            module RightHttpConnection #:nodoc:
         | 
| 25 25 | 
             
              module VERSION #:nodoc:
         | 
| 26 26 | 
             
                MAJOR = 1  unless defined?(MAJOR)
         | 
| 27 | 
            -
                MINOR =  | 
| 27 | 
            +
                MINOR = 4  unless defined?(MINOR)
         | 
| 28 28 | 
             
                TINY  = 0  unless defined?(TINY)
         | 
| 29 29 |  | 
| 30 30 | 
             
                STRING = [MAJOR, MINOR, TINY].join('.') unless defined?(STRING)
         | 
| @@ -1,5 +1,5 @@ | |
| 1 1 | 
             
            #
         | 
| 2 | 
            -
            # Copyright (c) 2007- | 
| 2 | 
            +
            # Copyright (c) 2007-2011 RightScale Inc
         | 
| 3 3 | 
             
            #
         | 
| 4 4 | 
             
            # Permission is hereby granted, free of charge, to any person obtaining
         | 
| 5 5 | 
             
            # a copy of this software and associated documentation files (the
         | 
| @@ -27,9 +27,9 @@ require "time" | |
| 27 27 | 
             
            require "logger"
         | 
| 28 28 |  | 
| 29 29 | 
             
            $:.unshift(File.dirname(__FILE__))
         | 
| 30 | 
            -
            require 'version'
         | 
| 31 | 
            -
            require 'support'
         | 
| 32 | 
            -
            require  | 
| 30 | 
            +
            require 'base/version'
         | 
| 31 | 
            +
            require 'base/support'
         | 
| 32 | 
            +
            require 'base/net_fix'
         | 
| 33 33 |  | 
| 34 34 | 
             
            module Rightscale
         | 
| 35 35 |  | 
| @@ -137,26 +137,47 @@ them. | |
| 137 137 | 
             
                 #  :raise_on_timeout                    # do not perform a retry if timeout is received (false by default)
         | 
| 138 138 | 
             
                def initialize(params={})
         | 
| 139 139 | 
             
                  @params = params
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                  #set up logging first
         | 
| 142 | 
            +
                  @logger = get_param(:logger) ||
         | 
| 143 | 
            +
                            (RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)) ||
         | 
| 144 | 
            +
                            Logger.new(STDOUT)
         | 
| 145 | 
            +
             | 
| 146 | 
            +
                  env_proxy_host, env_proxy_port, env_proxy_username, env_proxy_password = get_proxy_info_for_env if ENV['HTTP_PROXY']
         | 
| 147 | 
            +
             | 
| 140 148 | 
             
                  @params[:http_connection_retry_count]  ||= @@params[:http_connection_retry_count]
         | 
| 141 149 | 
             
                  @params[:http_connection_open_timeout] ||= @@params[:http_connection_open_timeout]
         | 
| 142 150 | 
             
                  @params[:http_connection_read_timeout] ||= @@params[:http_connection_read_timeout]
         | 
| 143 151 | 
             
                  @params[:http_connection_retry_delay]  ||= @@params[:http_connection_retry_delay]
         | 
| 144 | 
            -
                  @params[:proxy_host] ||= @@params[:proxy_host]
         | 
| 145 | 
            -
                  @params[:proxy_port] ||= @@params[:proxy_port]
         | 
| 146 | 
            -
                  @params[:proxy_username] ||= @@params[:proxy_username]
         | 
| 147 | 
            -
                  @params[:proxy_password] ||= @@params[:proxy_password]
         | 
| 152 | 
            +
                  @params[:proxy_host] ||= @@params[:proxy_host] || env_proxy_host
         | 
| 153 | 
            +
                  @params[:proxy_port] ||= @@params[:proxy_port] || env_proxy_port
         | 
| 154 | 
            +
                  @params[:proxy_username] ||= @@params[:proxy_username] || env_proxy_username
         | 
| 155 | 
            +
                  @params[:proxy_password] ||= @@params[:proxy_password] || env_proxy_password
         | 
| 156 | 
            +
             | 
| 148 157 | 
             
                  @http   = nil
         | 
| 149 158 | 
             
                  @server = nil
         | 
| 150 | 
            -
                  @logger = get_param(:logger) ||
         | 
| 151 | 
            -
                            (RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)) ||
         | 
| 152 | 
            -
                            Logger.new(STDOUT)
         | 
| 153 159 | 
             
                  #--------------
         | 
| 154 160 | 
             
                  # Retry state - Keep track of errors on a per-server basis
         | 
| 155 161 | 
             
                  #--------------
         | 
| 156 162 | 
             
                  @state = {}  # retry state indexed by server: consecutive error count, error time, and error
         | 
| 163 | 
            +
                                                                                                                                                                                                                      
         | 
| 157 164 | 
             
                  @eof   = {}
         | 
| 158 165 | 
             
                end
         | 
| 159 166 |  | 
| 167 | 
            +
                def get_proxy_info_for_env
         | 
| 168 | 
            +
                  parsed_uri = URI.parse(ENV['HTTP_PROXY'])
         | 
| 169 | 
            +
                  if parsed_uri.scheme.to_s.downcase == 'http'
         | 
| 170 | 
            +
                    return parsed_uri.host, parsed_uri.port, parsed_uri.user, parsed_uri.password
         | 
| 171 | 
            +
                  else
         | 
| 172 | 
            +
                    @logger.warn "Invalid protocol in ENV['HTTP_PROXY'] URI = #{ENV['HTTP_PROXY'].inspect} expecting 'http' got #{parsed_uri.scheme.inspect}"
         | 
| 173 | 
            +
                    return
         | 
| 174 | 
            +
                  end
         | 
| 175 | 
            +
                rescue Exception => e
         | 
| 176 | 
            +
                  @logger.warn "Error parsing ENV['HTTP_PROXY'] with exception: #{e.message}"
         | 
| 177 | 
            +
                  return
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
                private :get_proxy_info_for_env
         | 
| 180 | 
            +
             | 
| 160 181 | 
             
                def get_param(name, custom_options={})
         | 
| 161 182 | 
             
                  custom_options [name] || @params[name] || @@params[name]
         | 
| 162 183 | 
             
                end
         | 
| @@ -207,7 +228,9 @@ them. | |
| 207 228 | 
             
                end
         | 
| 208 229 |  | 
| 209 230 | 
             
                # add an error for a server
         | 
| 210 | 
            -
                def error_add( | 
| 231 | 
            +
                def error_add(error)
         | 
| 232 | 
            +
                  message = error
         | 
| 233 | 
            +
                  message = "#{error.class.name}: #{error.message}" if error.is_a?(Exception)
         | 
| 211 234 | 
             
                  @state[@server] = { :count => error_count+1, :time => Time.now, :message => message }
         | 
| 212 235 | 
             
                end
         | 
| 213 236 |  | 
| @@ -218,7 +241,7 @@ them. | |
| 218 241 |  | 
| 219 242 | 
             
                # Error message stuff...
         | 
| 220 243 | 
             
                def banana_message
         | 
| 221 | 
            -
                  return "#{@server} temporarily unavailable: (#{error_message})"
         | 
| 244 | 
            +
                  return "#{@protocol}://#{@server}:#{@port} temporarily unavailable: (#{error_message})"
         | 
| 222 245 | 
             
                end
         | 
| 223 246 |  | 
| 224 247 | 
             
                def err_header
         | 
| @@ -280,6 +303,8 @@ them. | |
| 280 303 | 
             
                  end
         | 
| 281 304 | 
             
                end
         | 
| 282 305 |  | 
| 306 | 
            +
                SECURITY_PARAMS = [:cert, :key, :cert_file, :key_file, :ca_file]
         | 
| 307 | 
            +
             | 
| 283 308 | 
             
                # Start a fresh connection. The object closes any existing connection and
         | 
| 284 309 | 
             
                # opens a new one.
         | 
| 285 310 | 
             
                def start(request_params)
         | 
| @@ -293,10 +318,15 @@ them. | |
| 293 318 | 
             
                  @proxy_port     = request_params[:proxy_port]
         | 
| 294 319 | 
             
                  @proxy_username = request_params[:proxy_username]
         | 
| 295 320 | 
             
                  @proxy_password = request_params[:proxy_password]
         | 
| 321 | 
            +
                  
         | 
| 322 | 
            +
                  SECURITY_PARAMS.each do |param_name|
         | 
| 323 | 
            +
                    @params[param_name] = request_params[param_name]
         | 
| 324 | 
            +
                  end
         | 
| 296 325 |  | 
| 297 326 | 
             
                  @logger.info("Opening new #{@protocol.upcase} connection to #@server:#@port")
         | 
| 327 | 
            +
             | 
| 298 328 | 
             
                  @logger.info("Connecting to proxy #{@proxy_host}:#{@proxy_port} with username" +
         | 
| 299 | 
            -
                               " #{@proxy_username}") unless @proxy_host.nil?
         | 
| 329 | 
            +
                               " #{@proxy_username.inspect}") unless @proxy_host.nil?
         | 
| 300 330 |  | 
| 301 331 | 
             
                  @http = Net::HTTP.new(@server, @port, @proxy_host, @proxy_port, @proxy_username,
         | 
| 302 332 | 
             
                                        @proxy_password)
         | 
| @@ -305,10 +335,9 @@ them. | |
| 305 335 |  | 
| 306 336 | 
             
                  if @protocol == 'https'
         | 
| 307 337 | 
             
                    verifyCallbackProc = Proc.new{ |ok, x509_store_ctx|
         | 
| 338 | 
            +
                      # List of error codes: http://www.openssl.org/docs/apps/verify.html
         | 
| 308 339 | 
             
                      code = x509_store_ctx.error
         | 
| 309 340 | 
             
                      msg = x509_store_ctx.error_string
         | 
| 310 | 
            -
                        #debugger
         | 
| 311 | 
            -
                      @logger.warn("##### #{@server} certificate verify failed: #{msg}") unless code == 0
         | 
| 312 341 | 
             
                      if request_params[:fail_if_ca_mismatch] && code != 0
         | 
| 313 342 | 
             
                        false
         | 
| 314 343 | 
             
                      else
         | 
| @@ -316,14 +345,44 @@ them. | |
| 316 345 | 
             
                      end
         | 
| 317 346 | 
             
                    }
         | 
| 318 347 | 
             
                    @http.use_ssl = true
         | 
| 348 | 
            +
             | 
| 319 349 | 
             
                    ca_file = get_param(:ca_file)
         | 
| 320 | 
            -
                    if ca_file
         | 
| 321 | 
            -
                       | 
| 350 | 
            +
                    if ca_file && File.exists?(ca_file)
         | 
| 351 | 
            +
                      # Documentation for 'http.rb':
         | 
| 352 | 
            +
                      # : verify_mode, verify_mode=((|mode|))
         | 
| 353 | 
            +
                      #    Sets the flags for server the certification verification at
         | 
| 354 | 
            +
                      #    beginning of SSL/TLS session.
         | 
| 355 | 
            +
                      #    OpenSSL::SSL::VERIFY_NONE or OpenSSL::SSL::VERIFY_PEER is acceptable.
         | 
| 356 | 
            +
                      #
         | 
| 357 | 
            +
                      # KHRVI: looks like the constant VERIFY_FAIL_IF_NO_PEER_CERT is not acceptable
         | 
| 322 358 | 
             
                      @http.verify_callback = verifyCallbackProc
         | 
| 323 | 
            -
                      @http.ca_file | 
| 359 | 
            +
                      @http.ca_file= ca_file
         | 
| 360 | 
            +
                      @http.verify_mode = get_param(:use_server_auth) ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
         | 
| 361 | 
            +
                      # The depth count is 'level 0:peer certificate', 'level 1: CA certificate', 'level 2: higher level CA certificate', and so on.
         | 
| 362 | 
            +
                      # Setting the maximum depth to 2 allows the levels 0, 1, and 2. The default depth limit is 9, allowing for the peer certificate and additional 9 CA certificates.
         | 
| 363 | 
            +
                      @http.verify_depth    = 9
         | 
| 324 364 | 
             
                    else
         | 
| 325 365 | 
             
                      @http.verify_mode = OpenSSL::SSL::VERIFY_NONE
         | 
| 326 366 | 
             
                    end
         | 
| 367 | 
            +
                    
         | 
| 368 | 
            +
                    # CERT
         | 
| 369 | 
            +
                    cert_file = get_param(:cert_file, request_params)
         | 
| 370 | 
            +
                    cert      = File.read(cert_file) if cert_file && File.exists?(cert_file)
         | 
| 371 | 
            +
                    cert    ||= get_param(:cert, request_params)
         | 
| 372 | 
            +
                    # KEY
         | 
| 373 | 
            +
                    key_file  = get_param(:key_file,  request_params)
         | 
| 374 | 
            +
                    key       = File.read(key_file) if key_file && File.exists?(key_file)
         | 
| 375 | 
            +
                    key     ||= get_param(:key,  request_params)
         | 
| 376 | 
            +
                    if cert && key
         | 
| 377 | 
            +
                      begin
         | 
| 378 | 
            +
                        @http.verify_callback = verifyCallbackProc
         | 
| 379 | 
            +
                        @http.cert = OpenSSL::X509::Certificate.new(cert)
         | 
| 380 | 
            +
                        @http.key  = OpenSSL::PKey::RSA.new(key)
         | 
| 381 | 
            +
                      rescue OpenSSL::PKey::RSAError, OpenSSL::X509::CertificateError => e
         | 
| 382 | 
            +
                        @logger.error "##### Error loading SSL client cert or key: #{e.message} :: backtrace #{e.backtrace}"
         | 
| 383 | 
            +
                        raise e
         | 
| 384 | 
            +
                      end
         | 
| 385 | 
            +
                    end
         | 
| 327 386 | 
             
                  end
         | 
| 328 387 | 
             
                  # open connection
         | 
| 329 388 | 
             
                  @http.start
         | 
| @@ -359,14 +418,21 @@ them. | |
| 359 418 | 
             
                  current_params = @params.merge(request_params)
         | 
| 360 419 | 
             
                  exception = get_param(:exception, current_params) || RuntimeError
         | 
| 361 420 |  | 
| 421 | 
            +
                  # Re-establish the connection if any of auth params has changed
         | 
| 422 | 
            +
                  same_auth_params_as_before = SECURITY_PARAMS.select do |param|
         | 
| 423 | 
            +
                    request_params[param] != get_param(param)
         | 
| 424 | 
            +
                  end.empty?
         | 
| 425 | 
            +
             | 
| 362 426 | 
             
                  # We save the offset here so that if we need to retry, we can return the file pointer to its initial position
         | 
| 363 427 | 
             
                  mypos = get_fileptr_offset(current_params)
         | 
| 364 428 | 
             
                  loop do
         | 
| 429 | 
            +
             | 
| 365 430 | 
             
                    current_params[:protocol] ||= (current_params[:port] == 443 ? 'https' : 'http')
         | 
| 366 431 | 
             
                    # (re)open connection to server if none exists or params has changed
         | 
| 367 | 
            -
                    same_server_as_before = @server   == current_params[:server] | 
| 368 | 
            -
                                            @port     == current_params[:port] | 
| 369 | 
            -
                                            @protocol == current_params[:protocol]
         | 
| 432 | 
            +
                    same_server_as_before = @server   == current_params[:server]   &&
         | 
| 433 | 
            +
                                            @port     == current_params[:port]     &&
         | 
| 434 | 
            +
                                            @protocol == current_params[:protocol] &&
         | 
| 435 | 
            +
                                            same_auth_params_as_before
         | 
| 370 436 |  | 
| 371 437 | 
             
                    # if we are inside a delay between retries: no requests this time!
         | 
| 372 438 | 
             
                    # (skip this step if the endpoint has changed)
         | 
| @@ -389,6 +455,7 @@ them. | |
| 389 455 | 
             
                      unless @http          &&
         | 
| 390 456 | 
             
                             @http.started? &&
         | 
| 391 457 | 
             
                             same_server_as_before
         | 
| 458 | 
            +
                        same_auth_params_as_before = true
         | 
| 392 459 | 
             
                        start(current_params)
         | 
| 393 460 | 
             
                      end
         | 
| 394 461 |  | 
| @@ -414,8 +481,9 @@ them. | |
| 414 481 |  | 
| 415 482 | 
             
                    # EOFError means the server closed the connection on us.
         | 
| 416 483 | 
             
                    rescue EOFError => e
         | 
| 484 | 
            +
                      finish(e.message)
         | 
| 485 | 
            +
                      
         | 
| 417 486 | 
             
                      @logger.debug("#{err_header} server #{@server} closed connection")
         | 
| 418 | 
            -
                      @http = nil
         | 
| 419 487 |  | 
| 420 488 | 
             
                        # if we have waited long enough - raise an exception...
         | 
| 421 489 | 
             
                      if raise_on_eof_exception?
         | 
| @@ -427,31 +495,35 @@ them. | |
| 427 495 | 
             
                        # We will be retrying the request, so reset the file pointer
         | 
| 428 496 | 
             
                        reset_fileptr_offset(request, mypos)
         | 
| 429 497 | 
             
                      end
         | 
| 430 | 
            -
                    rescue  | 
| 431 | 
            -
                       | 
| 432 | 
            -
                       | 
| 433 | 
            -
                       | 
| 434 | 
            -
             | 
| 498 | 
            +
                    rescue ArgumentError => e
         | 
| 499 | 
            +
                      finish(e.message)
         | 
| 500 | 
            +
                      
         | 
| 501 | 
            +
                      if e.message.include?('wrong number of arguments (5 for 4)')
         | 
| 502 | 
            +
                        # seems our net_fix patch was overriden...
         | 
| 503 | 
            +
                        raise exception.new('incompatible Net::HTTP monkey-patch')
         | 
| 504 | 
            +
                      else
         | 
| 505 | 
            +
                        raise e
         | 
| 506 | 
            +
                      end
         | 
| 507 | 
            +
             | 
| 508 | 
            +
                    rescue Timeout::Error, SocketError, SystemCallError, Interrupt => e  # See comment at bottom for the list of errors seen...
         | 
| 509 | 
            +
                      finish(e.message)
         | 
| 510 | 
            +
                      if e.is_a?(Errno::ETIMEDOUT) || e.is_a?(Timeout::Error)
         | 
| 511 | 
            +
                        # Omit retries if it was explicitly requested
         | 
| 435 512 | 
             
                        # #6481:
         | 
| 436 513 | 
             
                        # ... When creating a resource in EC2 (instance, volume, snapshot, etc) it is undetermined what happened if the call times out.
         | 
| 437 514 | 
             
                        # The resource may or may not have been created in EC2. Retrying the call may cause multiple resources to be created...
         | 
| 438 | 
            -
                        raise e
         | 
| 439 | 
            -
                       | 
| 440 | 
            -
             | 
| 441 | 
            -
                      if e.is_a?(Interrupt) && !timeout_exception
         | 
| 515 | 
            +
                        raise exception.new("#{e.class.name}: #{e.message}") if current_params[:raise_on_timeout]
         | 
| 516 | 
            +
                      elsif e.is_a?(Interrupt)
         | 
| 517 | 
            +
                        # if ctrl+c is pressed - we have to reraise exception to terminate proggy
         | 
| 442 518 | 
             
                        @logger.debug( "#{err_header} request to server #{@server} interrupted by ctrl-c")
         | 
| 443 | 
            -
                        raise
         | 
| 444 | 
            -
                      elsif e.is_a?(ArgumentError) && e.message.include?('wrong number of arguments (5 for 4)')
         | 
| 445 | 
            -
                        # seems our net_fix patch was overriden...
         | 
| 446 | 
            -
                        raise exception.new('incompatible Net::HTTP monkey-patch')
         | 
| 519 | 
            +
                        raise e
         | 
| 447 520 | 
             
                      end
         | 
| 448 521 | 
             
                      # oops - we got a banana: log it
         | 
| 449 | 
            -
                      error_add(e | 
| 522 | 
            +
                      error_add(e)
         | 
| 450 523 | 
             
                      @logger.warn("#{err_header} request failure count: #{error_count}, exception: #{e.inspect}")
         | 
| 451 524 |  | 
| 452 525 | 
             
                      # We will be retrying the request, so reset the file pointer
         | 
| 453 526 | 
             
                      reset_fileptr_offset(request, mypos)
         | 
| 454 | 
            -
             | 
| 455 527 | 
             
                    end
         | 
| 456 528 | 
             
                  end
         | 
| 457 529 | 
             
                end
         | 
| @@ -462,6 +534,8 @@ them. | |
| 462 534 | 
             
                    @logger.info("Closing #{@http.use_ssl? ? 'HTTPS' : 'HTTP'} connection to #{@http.address}:#{@http.port}#{reason}")
         | 
| 463 535 | 
             
                    @http.finish
         | 
| 464 536 | 
             
                  end
         | 
| 537 | 
            +
                ensure
         | 
| 538 | 
            +
                  @http = nil
         | 
| 465 539 | 
             
                end
         | 
| 466 540 |  | 
| 467 541 | 
             
              # Errors received during testing:
         | 
| @@ -22,7 +22,7 @@ | |
| 22 22 | 
             
            #++
         | 
| 23 23 |  | 
| 24 24 | 
             
            require 'rubygems'
         | 
| 25 | 
            -
            require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'version'))
         | 
| 25 | 
            +
            require File.expand_path(File.join(File.dirname(__FILE__), 'lib', 'base', 'version'))
         | 
| 26 26 |  | 
| 27 27 | 
             
            Gem::Specification.new do |spec|
         | 
| 28 28 | 
             
              spec.name = 'right_http_connection'
         | 
| Binary file | 
| @@ -0,0 +1,17 @@ | |
| 1 | 
            +
            -----BEGIN CERTIFICATE-----
         | 
| 2 | 
            +
            MIICxjCCAa6gAwIBAgIJAJYV+DprCQ1CMA0GCSqGSIb3DQEBBQUAMBMxETAPBgNV
         | 
| 3 | 
            +
            BAMTCE15VGVzdENBMB4XDTExMDkxMzIyNTMxMVoXDTEyMDkxMjIyNTMxMVowEzER
         | 
| 4 | 
            +
            MA8GA1UEAxMITXlUZXN0Q0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
         | 
| 5 | 
            +
            AQCwmvBNrd7t/Z7ZVo1YfimpGgerOn1vXZY+OGJtqo+pN11Ei7dhVQfWBd2dAkYH
         | 
| 6 | 
            +
            B8NlPr5QyxmIT88JIRKEzk7ZZ+nRdfyoocg63FeLn+b6OeR5hwyK38aMRbhqY1Gq
         | 
| 7 | 
            +
            aIKMYyEpv0YNbuwoomv5Atl8mwvuUFr2XKndyzsrP1TrTCHH4lA5P0UUzIjVyyz9
         | 
| 8 | 
            +
            F4YAjGLjjoVO5R02LmZ/h/LqT6bJQ+cu/2JeIWGVnjKoFvyWHd0TOaOGDHlQc5h8
         | 
| 9 | 
            +
            RxgdOFrjsZGpQ5sKlhcI+9p0LOXqVfoC2J2ZWtAjFo0d54E/OarnBPFB6VNtoSmj
         | 
| 10 | 
            +
            l0z+OLGMKuDGaLflXNE0STVdAgMBAAGjHTAbMAwGA1UdEwQFMAMBAf8wCwYDVR0P
         | 
| 11 | 
            +
            BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQAAbVkvPzS59uhX6Ox1ZT92cJXW8yjP
         | 
| 12 | 
            +
            IyXrZjcWlaKQSKcn8v5RpebtVA+pL6mCActBE8fMac5ixlwTTnF5LHb9v80XuXMe
         | 
| 13 | 
            +
            MXooQZBliyim5lVCp9gjKZYXEeVDphsuwDr5M4qO7tdZTB1ezCULObVF1N7qMwpO
         | 
| 14 | 
            +
            yWI6zifRtLsgWmnRyaeVyv2uNRYoAEsAd2Dj4oJjvuyc9U5QUhtsXwD3jvSPsdi6
         | 
| 15 | 
            +
            Mbr5tVIcZSpT4W9PSiZw2ZUZXIEbxX+w+FsuehhvoFJCi05R1ashCPxQA13bOJK0
         | 
| 16 | 
            +
            BmbHqeLDzJCK0+kQs8CRIGWGTGng84AyJ5MygGzd0WN9jtZslWTPDtbz
         | 
| 17 | 
            +
            -----END CERTIFICATE-----
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            -----BEGIN CERTIFICATE-----
         | 
| 2 | 
            +
            MIIC9TCCAd2gAwIBAgIBAjANBgkqhkiG9w0BAQUFADATMREwDwYDVQQDEwhNeVRl
         | 
| 3 | 
            +
            c3RDQTAeFw0xMTA5MTMyMzAxMDVaFw0xMjA5MTIyMzAxMDVaMDgxJTAjBgNVBAMM
         | 
| 4 | 
            +
            HE1haGVuZHJhLUt1dGFyZXMtTWFjQm9vay1Qcm8xDzANBgNVBAoMBmNsaWVudDCC
         | 
| 5 | 
            +
            ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMcEhrVTKKgWVqTUBCGBWJlD
         | 
| 6 | 
            +
            u7RIk7kPcGAARy7Ctx+4VReEYlgVRqECzt4itglNdrQkUVCHXE0rwJMOozE8Hsgh
         | 
| 7 | 
            +
            rAOQvxzPJhG3hPUJf/VkfB+Dn0xRsPvrE90HpRlSqdT8X6iuryPEmp5RyMaY122P
         | 
| 8 | 
            +
            r/+Xs+lHhRlKQPdRpYXHlOwWX/U56Wy7jjGU9lONBEIEV8tD5ExzkCG23nbCvrFr
         | 
| 9 | 
            +
            /2c4VjrAwXR2RyYfSDRyc/obky49ydKZ8/HKbS3VdJYAWBI4Wnj2hayCcZggEFB0
         | 
| 10 | 
            +
            zg/IDXpOjnr6zV5UEfdaMIH4/K44ISX7xmZWGmQ3464NTmykj5xUMmfy3rVNREsC
         | 
| 11 | 
            +
            AwEAAaMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCB4AwEwYDVR0lBAwwCgYIKwYB
         | 
| 12 | 
            +
            BQUHAwIwDQYJKoZIhvcNAQEFBQADggEBACmNyOoCvNsz8N3LN47VZK7aev54tjtd
         | 
| 13 | 
            +
            zJilLgAxEGBeaIvHX9LDkgi3sQAvHMHc3VIq4BoEd9TNtyxIrUdc2EG1TCJvHINP
         | 
| 14 | 
            +
            7YoHtbajvT3bhVLlnWjB7jHp9jNfZtHL7aEDp+5eqPT6wzaVeiu1nABs7gudCQq1
         | 
| 15 | 
            +
            CJw0Mfz1U3mG0sTb5JlRt7toce9dW0R6jfYTmTj6Yzu3kcgYjQKy2k2BCInLOIhz
         | 
| 16 | 
            +
            6tyOH51mCGAy1zgcWMvuyKYCeJQxRd46GrR2peyE2wYY6SfSlrK16pjaz48S3uhI
         | 
| 17 | 
            +
            01jd+HA1LARcImMhkMa/QFTo4uI7lx9Q+Y06Ny+rMuTNSnBSIgCUPQk=
         | 
| 18 | 
            +
            -----END CERTIFICATE-----
         | 
    
        data/spec/client/key.pem
    ADDED
    
    | @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            -----BEGIN RSA PRIVATE KEY-----
         | 
| 2 | 
            +
            MIIEpAIBAAKCAQEAxwSGtVMoqBZWpNQEIYFYmUO7tEiTuQ9wYABHLsK3H7hVF4Ri
         | 
| 3 | 
            +
            WBVGoQLO3iK2CU12tCRRUIdcTSvAkw6jMTweyCGsA5C/HM8mEbeE9Ql/9WR8H4Of
         | 
| 4 | 
            +
            TFGw++sT3QelGVKp1PxfqK6vI8SanlHIxpjXbY+v/5ez6UeFGUpA91GlhceU7BZf
         | 
| 5 | 
            +
            9TnpbLuOMZT2U40EQgRXy0PkTHOQIbbedsK+sWv/ZzhWOsDBdHZHJh9INHJz+huT
         | 
| 6 | 
            +
            Lj3J0pnz8cptLdV0lgBYEjhaePaFrIJxmCAQUHTOD8gNek6OevrNXlQR91owgfj8
         | 
| 7 | 
            +
            rjghJfvGZlYaZDfjrg1ObKSPnFQyZ/LetU1ESwIDAQABAoIBAB23pU3KHxYKT+HI
         | 
| 8 | 
            +
            7tz57XrlTE/9TmGh1ovfPsHSvXl1Eu+yCuVQN/2u56jv0fLNqF351lKKA9RaJiVP
         | 
| 9 | 
            +
            WDrv2UDVFlRp9r+chvi6SJY2Vu8TlB04kD7bK+xSC+NDUvnXCBkPnlEX1HsozlW5
         | 
| 10 | 
            +
            rJtLE0/+1q75vhmlXlCKb+z+OhMhmFnaWTf/xLNbkItO5tOf+mv/CoqBUSEk+i9t
         | 
| 11 | 
            +
            O6Zjzh02jbpW7xH3jJ/UexKMYOuqxoOMfC/MI6q3Qcu2OeZgl8cEIi94sjafq9ob
         | 
| 12 | 
            +
            WcFTrZY+YG5b1SE8ILg69Fkqve5d2Mn1sN8mYZxLeM0C/ATNghM5uSWhdze06bNu
         | 
| 13 | 
            +
            fpcgvOECgYEA/L+J/xVgUySUByELEBosY8q0HYG5Msq+GT7L5GMIoEmEp4j6MPRu
         | 
| 14 | 
            +
            kF/DihxefcvDyVRLhJh7o/kwR7Vwe4wP9145e3MOe9b7IH6pEwV0nAsBO6ldVToX
         | 
| 15 | 
            +
            gvrHOIoySNt/XtRurrbtZ08OtUDCLIRQATTnY9ieh8sxTyl1G9GehoMCgYEAyZQE
         | 
| 16 | 
            +
            r4ByBzXjTiuaODH6tPndbKFxRo2iis9CyxqYXAMDkjvF4NEpQyW5ucRxpRqTt51P
         | 
| 17 | 
            +
            kR13jdadnOF4t82M0qqEH3G6H4biKisY1jXRNH7mPSbyPbC4vxrQhnAEF3RiqbXz
         | 
| 18 | 
            +
            f2LUC4uOtLzW7HeyjEiZy2mg7UKdOfsmmJ//oJkCgYEAxZF/8GqoQjW8lJoKyMp8
         | 
| 19 | 
            +
            2oDQLKSDvSVoVdmVjfCwBIOTc1aKpAveBXMmKealIlZOtCj1Yy/CrlmSmOtGgvzo
         | 
| 20 | 
            +
            WihIbKxyrPFOmocH6PuBvJyJmTZ5464mRNd9NUApsHQL63fJET+i8feFer+lSSEg
         | 
| 21 | 
            +
            XOEa4xyoR2PZJpU0mstPzLsCgYBvcS3F+TURV3F7Xg+80aTROPJ5hCej4dni9ALx
         | 
| 22 | 
            +
            Vpq1A9WNmw4i5H/zZ3/ue/R4WuEfuhCrIade+y/X869RrooUTcENwUos891Fgt4Q
         | 
| 23 | 
            +
            T2CBrUaMuGNkR7dbr+9o47TfYrDJMpaT7odceqNCuMP5p5NGizy7gII/qXxS+c60
         | 
| 24 | 
            +
            woAIwQKBgQCiIfXZtAgYTPL23CQrxIMFwnlO0TiOe0ha0et7hjCh/CStG7NET7KK
         | 
| 25 | 
            +
            U1L1kfyl1YDgoJbLXTsG2WwGZRnK1oyEEFj2iY5EvwoMPr0Sv8/CiOIyEfC62s3V
         | 
| 26 | 
            +
            MoHemunnFhAj+JAy2HTKV0VYiNNNAxz3CBG8yMLK7YAMgPw1/HQQLQ==
         | 
| 27 | 
            +
            -----END RSA PRIVATE KEY-----
         | 
| @@ -23,7 +23,7 @@ | |
| 23 23 |  | 
| 24 24 | 
             
            require 'webrick'
         | 
| 25 25 |  | 
| 26 | 
            -
            ssl_cert, ssl_key = ARGV[0], ARGV[1]
         | 
| 26 | 
            +
            ssl_cert, ssl_key, ca_cert = ARGV[0], ARGV[1], ARGV[2]
         | 
| 27 27 |  | 
| 28 28 | 
             
            # Monkey patch bad User-Agent parsing
         | 
| 29 29 | 
             
            module WEBrick::AccessLog
         | 
| @@ -52,7 +52,7 @@ module WEBrick::AccessLog | |
| 52 52 | 
             
              end
         | 
| 53 53 | 
             
            end
         | 
| 54 54 |  | 
| 55 | 
            -
            logger = WEBrick::Log.new($stderr, WEBrick::Log::WARN)
         | 
| 55 | 
            +
            logger = WEBrick::Log.new($stderr, WEBrick::Log::WARN)#WEBrick::Log::DEBUG
         | 
| 56 56 | 
             
            config = {}
         | 
| 57 57 | 
             
            config[:Port] = 7890
         | 
| 58 58 | 
             
            config[:Logger] = logger
         | 
| @@ -60,10 +60,19 @@ config[:AccessLog] = [[$stdout, WEBrick::AccessLog::COMBINED_LOG_FORMAT]] | |
| 60 60 | 
             
            unless ssl_cert.nil? || ssl_key.nil?
         | 
| 61 61 | 
             
              require 'webrick/https'
         | 
| 62 62 | 
             
              config[:SSLEnable] = true
         | 
| 63 | 
            -
               | 
| 63 | 
            +
              # http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html#
         | 
| 64 | 
            +
              # SSL_VERIFY_FAIL_IF_NO_PEER_CERT
         | 
| 65 | 
            +
              # => Server mode: if the client did not return a certificate, the TLS/SSL handshake is immediately terminated with a 'handshake failure' alert.
         | 
| 66 | 
            +
              # => This flag must be used together with SSL_VERIFY_PEER.
         | 
| 67 | 
            +
              config[:SSLVerifyClient] = OpenSSL::SSL::VERIFY_PEER
         | 
| 68 | 
            +
              config[:SSLVerifyClient] |= OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT if ca_cert
         | 
| 64 69 | 
             
              config[:SSLPrivateKey] = OpenSSL::PKey::RSA.new(File.open(ssl_key).read)
         | 
| 65 70 | 
             
              config[:SSLCertificate] = OpenSSL::X509::Certificate.new(File.open(ssl_cert).read)
         | 
| 66 | 
            -
              config[:SSLCertName]  | 
| 71 | 
            +
              # KHRVI: option config[:SSLCertName] does make sense only when config[:SSLCertificate] isn't specified
         | 
| 72 | 
            +
              # see: webrick/ssl.rb method :setup_ssl_context
         | 
| 73 | 
            +
              # config[:SSLCertName] = [["CN", "Graham Hughes"]]
         | 
| 74 | 
            +
              config[:SSLVerifyDepth] = 9
         | 
| 75 | 
            +
              config[:SSLCACertificateFile] = ca_cert if ca_cert
         | 
| 67 76 | 
             
            end
         | 
| 68 77 | 
             
            $stdout.sync = true
         | 
| 69 78 | 
             
            server = WEBrick::HTTPServer.new(config)
         | 
    
        metadata
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification 
         | 
| 2 2 | 
             
            name: right_http_connection
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version 
         | 
| 4 | 
            -
              hash:  | 
| 5 | 
            -
              prerelease:  | 
| 4 | 
            +
              hash: 7
         | 
| 5 | 
            +
              prerelease: 
         | 
| 6 6 | 
             
              segments: 
         | 
| 7 7 | 
             
              - 1
         | 
| 8 | 
            -
              -  | 
| 8 | 
            +
              - 4
         | 
| 9 9 | 
             
              - 0
         | 
| 10 | 
            -
              version: 1. | 
| 10 | 
            +
              version: 1.4.0
         | 
| 11 11 | 
             
            platform: ruby
         | 
| 12 12 | 
             
            authors: 
         | 
| 13 13 | 
             
            - RightScale, Inc.
         | 
| @@ -15,8 +15,7 @@ autorequire: | |
| 15 15 | 
             
            bindir: bin
         | 
| 16 16 | 
             
            cert_chain: []
         | 
| 17 17 |  | 
| 18 | 
            -
            date:  | 
| 19 | 
            -
            default_executable: 
         | 
| 18 | 
            +
            date: 2013-06-13 00:00:00 Z
         | 
| 20 19 | 
             
            dependencies: 
         | 
| 21 20 | 
             
            - !ruby/object:Gem::Dependency 
         | 
| 22 21 | 
             
              name: rake
         | 
| @@ -115,10 +114,10 @@ files: | |
| 115 114 | 
             
            - Manifest.txt
         | 
| 116 115 | 
             
            - README.txt
         | 
| 117 116 | 
             
            - Rakefile
         | 
| 118 | 
            -
            - lib/net_fix.rb
         | 
| 117 | 
            +
            - lib/base/net_fix.rb
         | 
| 118 | 
            +
            - lib/base/support.rb
         | 
| 119 | 
            +
            - lib/base/version.rb
         | 
| 119 120 | 
             
            - lib/right_http_connection.rb
         | 
| 120 | 
            -
            - lib/support.rb
         | 
| 121 | 
            -
            - lib/version.rb
         | 
| 122 121 | 
             
            - right_http_connection.gemspec
         | 
| 123 122 | 
             
            - spec/bad.ca
         | 
| 124 123 | 
             
            - spec/ca/Rakefile
         | 
| @@ -128,12 +127,15 @@ files: | |
| 128 127 | 
             
            - spec/ca/demoCA/serial
         | 
| 129 128 | 
             
            - spec/ca/passphrase.txt
         | 
| 130 129 | 
             
            - spec/ca/server.csr
         | 
| 130 | 
            +
            - spec/client/cacert.cer
         | 
| 131 | 
            +
            - spec/client/cacert.pem
         | 
| 132 | 
            +
            - spec/client/cert.pem
         | 
| 133 | 
            +
            - spec/client/key.pem
         | 
| 131 134 | 
             
            - spec/good.ca
         | 
| 132 135 | 
             
            - spec/proxy_server.rb
         | 
| 133 136 | 
             
            - spec/really_dumb_webserver.rb
         | 
| 134 137 | 
             
            - spec/server.crt
         | 
| 135 138 | 
             
            - spec/server.key
         | 
| 136 | 
            -
            has_rdoc: true
         | 
| 137 139 | 
             
            homepage: http://rightscale.rubyforge.org/
         | 
| 138 140 | 
             
            licenses: []
         | 
| 139 141 |  | 
| @@ -173,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 173 175 | 
             
            requirements: []
         | 
| 174 176 |  | 
| 175 177 | 
             
            rubyforge_project: rightscale
         | 
| 176 | 
            -
            rubygems_version: 1. | 
| 178 | 
            +
            rubygems_version: 1.8.5
         | 
| 177 179 | 
             
            signing_key: 
         | 
| 178 180 | 
             
            specification_version: 3
         | 
| 179 181 | 
             
            summary: RightScale's robust HTTP/S connection module
         |