Class: RightScale::CloudApi::ConnectionProxy::RightHttpConnectionProxy

Inherits:
Object
  • Object
show all
Defined in:
lib/base/routines/connection_proxies/right_http_connection_proxy.rb

Defined Under Namespace

Classes: Error

Constant Summary collapse

INACTIVE_LIFETIME_LIMIT =

seconds

900
@@storage =
{}

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.clean_storageObject

Remove dead threads/fibers from the storage



36
37
38
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 36

def self.clean_storage
  Utils::remove_dead_fibers_and_threads_from_storage(storage)
end

.storageObject



31
32
33
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 31

def self.storage
  @@storage
end

Instance Method Details

#clean_outdated_connectionsObject

Delete out-of-dated connections for current Thread/Fiber



192
193
194
195
196
197
198
199
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 192

def clean_outdated_connections
  life_time_scratch = Time::now - INACTIVE_LIFETIME_LIMIT
  storage.each do |endpoint, connection|
    if connection[:last_used_at] < life_time_scratch
      close_connection(endpoint, 'out-of-date')
    end
  end
end

#close_connection(endpoint, reason = '') ⇒ Object

:nodoc:



178
179
180
181
182
183
184
185
186
187
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 178

def close_connection(endpoint, reason='') # :nodoc:
  return nil unless storage[endpoint]

  log "Destroying RightHttpConnection to #{endpoint}, reason: #{reason}"
  storage[endpoint][:connection].finish(reason)
rescue => e
  log "Exception in close_connection: #{e.class.name}: #{e.message}"
ensure
  storage.delete(endpoint) if endpoint
end

#current_connectionObject

Expire the connection if it has expired.



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 202

def current_connection # :nodoc:
  # Remove dead threads/fibers from the storage
  self.class::clean_storage
  # Delete out-of-dated connections
  clean_outdated_connections
  # Get current_connection
  endpoint = current_endpoint
  unless storage[endpoint]
    storage[endpoint] = {}
    storage[endpoint][:connection] = Rightscale::HttpConnection.new( :exception => CloudError, 
                                                                     :logger    => @data[:options][:cloud_api_logger].logger )
    log "Creating RightHttpConection to #{endpoint.inspect}"
  else
    log "Reusing RightHttpConection to #{endpoint.inspect}"
  end
  storage[endpoint][:last_used_at] = Time::now
  storage[endpoint][:connection]
end

#current_endpointObject

:nodoc:



174
175
176
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 174

def current_endpoint # :nodoc:
  "#{@uri.scheme}://#{@uri.host}:#{@uri.port}"
end

#log(message) ⇒ Object



162
163
164
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 162

def log(message)     
  @data[:options][:cloud_api_logger].log(message, :connection_proxy)
end

#request(data) ⇒ Object

Performs an HTTP request.

Parameters:

  • data (Hash)

    The API request data storage. See ApiManager.initialize_api_request_options code for its explanation.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 48

def request(data)
  require "right_http_connection"

  @data = data
  @data[:response] = {}
  # Create a connection
  @uri = @data[:connection][:uri].dup

  # Create/Get RightHttpConnection instance
  remote_endpoint       = current_endpoint
  right_http_connection = current_connection

  # Register a callback to close current connection
  @data[:callbacks][:close_current_connection] = Proc::new{|reason| close_connection(remote_endpoint, reason); log "Current connection closed: #{reason}" }

  # Create a real HTTP request
  fake = @data[:request][:instance]
  http_request = "Net::HTTP::#{fake.verb._camelize}"._constantize::new(fake.path)
  if fake.is_io?
    http_request.body_stream = fake.body
  else
    http_request.body = fake.body
  end
  fake.headers.each{|header, value| http_request[header] = value }
  fake.raw = http_request

  # Set all the options are suported by RightHttpConnection (if they are)
  http_connection_data = {
    :server    => @uri.host,
    :port      => @uri.port,
    :protocol  => @uri.scheme,
    :request   => http_request,
    :exception => ConnectionError
  }

  # Set all required options
  http_connection_data[:logger]                       = @data[:options][:cloud_api_logger].logger
  http_connection_data[:user_agent]                   = @data[:options][:connection_user_agent]   if @data[:options].has_key?(:connection_user_agent)
  http_connection_data[:ca_file]                      = @data[:options][:connection_ca_file]      if @data[:options].has_key?(:connection_ca_file)
  http_connection_data[:http_connection_retry_count]  = @data[:options][:connection_retry_count]  if @data[:options].has_key?(:connection_retry_count)
  http_connection_data[:http_connection_read_timeout] = @data[:options][:connection_read_timeout] if @data[:options].has_key?(:connection_read_timeout)
  http_connection_data[:http_connection_open_timeout] = @data[:options][:connection_open_timeout] if @data[:options].has_key?(:connection_open_timeout)
  http_connection_data[:http_connection_retry_delay]  = @data[:options][:connection_retry_delay]  if @data[:options].has_key?(:connection_retry_delay)
  http_connection_data[:raise_on_timeout]             = @data[:options][:abort_on_timeout]        if @data[:options][:abort_on_timeout]
  http_connection_data[:cert]                         = @data[:credentials][:cert]                if @data[:credentials].has_key?(:cert)
  http_connection_data[:key]                          = @data[:credentials][:key]                 if @data[:credentials].has_key?(:key)
    
  #log "HttpConnection request: #{http_connection_data.inspect}"

  # Make a request:
  block = @data[:vars][:system][:block]
  if block
    # If block is given - pass there all the chunks of a response and stop
    # (dont do any parsing, analysing etc)
    # 
    # TRB 9/17/07 Careful - because we are passing in blocks, we get a situation where
    # an exception may get thrown in the block body (which is high-level
    # code either here or in the application) but gets caught in the
    # low-level code of HttpConnection.  The solution is not to let any
    # exception escape the block that we pass to HttpConnection::request.
    # Exceptions can originate from code directly in the block, or from user
    # code called in the other block which is passed to response.read_body.
    # 
    # TODO: the suggested fix for RightHttpConnection if to catch 
    # Interrupt and SystemCallError instead of Exception in line 402
    response = nil
    begin
      block_exception = nil
      # Response.body will be a Net::ReadAdapter instance here - it cant be read as a string.
      # WEB: On its own, Net::HTTP causes response.body to be a Net::ReadAdapter when you make a request with a block 
      # that calls read_body on the response.
      response = right_http_connection.request(http_connection_data) do |res|
        begin
          # Update temp response
          @data[:response][:instance] = HTTPResponse::new( res.code,
                                                           nil,
                                                           res.to_hash,
                                                           res )
          res.read_body(&block) if res.is_a?(Net::HTTPSuccess)
        rescue Exception => e
          block_exception = e
          break
        end
      end
      raise block_exception if block_exception
    rescue Exception => e
      right_http_connection.finish(e.message)
      raise e
    end
  else
    # Things are simple if there is no any block
    response = right_http_connection.request(http_connection_data)
  end

  @data[:response][:instance] = HTTPResponse::new( response.code,
                                                   response.body,
                                                   response.to_hash,
                                                   response )

  #        # HACK: KD 
  #        # 
  #        # When one uploads a file with pos > 0 and 'content-length' != File.size - pos
  #        # then the next request through this connection fails with 400 or 505...
  #        # It seems that it expects the file to be read until EOF.
  #        # 
  #        # KIlling the current connection seems to help but it is not good...
  #        #
  #        if @data[:request][:instance].body_stream #&& !@data[:request][:instance].body_stream.eof
  #          pp @data[:request][:instance].body_stream.pos
  #          log "closing current connection because of an issue when an IO object is not read until EOF"
  #          @connection.finish
  #        end
end

#storageObject


HTTP Connections handling




170
171
172
# File 'lib/base/routines/connection_proxies/right_http_connection_proxy.rb', line 170

def storage # :nodoc:
  @@storage[Utils::current_thread_and_fiber] ||= {}
end