http_connection 1.3.0 → 1.3.1

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.
Files changed (3) hide show
  1. data/lib/net_fix.rb +1 -1
  2. data/lib/right_http_connection.rb +315 -317
  3. metadata +18 -6
data/lib/net_fix.rb CHANGED
@@ -91,7 +91,7 @@ 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.bytesize
95
95
  delete 'Transfer-Encoding'
96
96
  supply_default_content_type
97
97
  write_header(sock, ver, path) unless send_only == :body
@@ -31,13 +31,7 @@ require "net_fix"
31
31
 
32
32
 
33
33
  module RightHttpConnection #:nodoc:
34
- module VERSION #:nodoc:
35
- MAJOR = 1
36
- MINOR = 2
37
- TINY = 4
38
34
 
39
- STRING = [MAJOR, MINOR, TINY].join('.')
40
- end
41
35
  end
42
36
 
43
37
 
@@ -75,243 +69,243 @@ the full number of potential reconnects and retries available to
75
69
  them.
76
70
  =end
77
71
 
78
- class HttpConnection
79
-
80
- # Number of times to retry the request after encountering the first error
81
- HTTP_CONNECTION_RETRY_COUNT = 3
82
- # Throw a Timeout::Error if a connection isn't established within this number of seconds
83
- HTTP_CONNECTION_OPEN_TIMEOUT = 5
84
- # Throw a Timeout::Error if no data have been read on this connnection within this number of seconds
85
- HTTP_CONNECTION_READ_TIMEOUT = 120
86
- # Length of the post-error probationary period during which all requests will fail
87
- HTTP_CONNECTION_RETRY_DELAY = 15
88
-
89
- #--------------------
90
- # class methods
91
- #--------------------
92
- #
93
- @@params = {}
94
- @@params[:http_connection_retry_count] = HTTP_CONNECTION_RETRY_COUNT
95
- @@params[:http_connection_open_timeout] = HTTP_CONNECTION_OPEN_TIMEOUT
96
- @@params[:http_connection_read_timeout] = HTTP_CONNECTION_READ_TIMEOUT
97
- @@params[:http_connection_retry_delay] = HTTP_CONNECTION_RETRY_DELAY
98
-
99
- # Query the global (class-level) parameters:
100
- #
101
- # :user_agent => 'www.HostName.com' # String to report as HTTP User agent
102
- # :ca_file => 'path_to_file' # Path to a CA certification file in PEM format. The file can contain several CA certificates. If this parameter isn't set, HTTPS certs won't be verified.
103
- # :logger => Logger object # If omitted, HttpConnection logs to STDOUT
104
- # :exception => Exception to raise # The type of exception to raise
105
- # # if a request repeatedly fails. RuntimeError is raised if this parameter is omitted.
106
- # :http_connection_retry_count # by default == Rightscale::HttpConnection::HTTP_CONNECTION_RETRY_COUNT
107
- # :http_connection_open_timeout # by default == Rightscale::HttpConnection::HTTP_CONNECTION_OPEN_TIMEOUT
108
- # :http_connection_read_timeout # by default == Rightscale::HttpConnection::HTTP_CONNECTION_READ_TIMEOUT
109
- # :http_connection_retry_delay # by default == Rightscale::HttpConnection::HTTP_CONNECTION_RETRY_DELAY
110
- def self.params
111
- @@params
112
- end
113
-
114
- # Set the global (class-level) parameters
115
- def self.params=(params)
116
- @@params = params
117
- end
72
+ class HttpConnection
73
+
74
+ # Number of times to retry the request after encountering the first error
75
+ HTTP_CONNECTION_RETRY_COUNT = 3
76
+ # Throw a Timeout::Error if a connection isn't established within this number of seconds
77
+ HTTP_CONNECTION_OPEN_TIMEOUT = 5
78
+ # Throw a Timeout::Error if no data have been read on this connnection within this number of seconds
79
+ HTTP_CONNECTION_READ_TIMEOUT = 120
80
+ # Length of the post-error probationary period during which all requests will fail
81
+ HTTP_CONNECTION_RETRY_DELAY = 15
82
+
83
+ #--------------------
84
+ # class methods
85
+ #--------------------
86
+ #
87
+ @@params = {}
88
+ @@params[:http_connection_retry_count] = HTTP_CONNECTION_RETRY_COUNT
89
+ @@params[:http_connection_open_timeout] = HTTP_CONNECTION_OPEN_TIMEOUT
90
+ @@params[:http_connection_read_timeout] = HTTP_CONNECTION_READ_TIMEOUT
91
+ @@params[:http_connection_retry_delay] = HTTP_CONNECTION_RETRY_DELAY
92
+
93
+ # Query the global (class-level) parameters:
94
+ #
95
+ # :user_agent => 'www.HostName.com' # String to report as HTTP User agent
96
+ # :ca_file => 'path_to_file' # Path to a CA certification file in PEM format. The file can contain several CA certificates. If this parameter isn't set, HTTPS certs won't be verified.
97
+ # :logger => Logger object # If omitted, HttpConnection logs to STDOUT
98
+ # :exception => Exception to raise # The type of exception to raise
99
+ # # if a request repeatedly fails. RuntimeError is raised if this parameter is omitted.
100
+ # :http_connection_retry_count # by default == Rightscale::HttpConnection::HTTP_CONNECTION_RETRY_COUNT
101
+ # :http_connection_open_timeout # by default == Rightscale::HttpConnection::HTTP_CONNECTION_OPEN_TIMEOUT
102
+ # :http_connection_read_timeout # by default == Rightscale::HttpConnection::HTTP_CONNECTION_READ_TIMEOUT
103
+ # :http_connection_retry_delay # by default == Rightscale::HttpConnection::HTTP_CONNECTION_RETRY_DELAY
104
+ def self.params
105
+ @@params
106
+ end
118
107
 
119
- #------------------
120
- # instance methods
121
- #------------------
122
- attr_accessor :http
123
- attr_accessor :server
124
- attr_accessor :params # see @@params
125
- attr_accessor :logger
126
-
127
- # Params hash:
128
- # :user_agent => 'www.HostName.com' # String to report as HTTP User agent
129
- # :ca_file => 'path_to_file' # A path of a CA certification file in PEM format. The file can contain several CA certificates.
130
- # :logger => Logger object # If omitted, HttpConnection logs to STDOUT
131
- # :exception => Exception to raise # The type of exception to raise if a request repeatedly fails. RuntimeError is raised if this parameter is omitted.
132
- # :http_connection_retry_count # by default == Rightscale::HttpConnection.params[:http_connection_retry_count]
133
- # :http_connection_open_timeout # by default == Rightscale::HttpConnection.params[:http_connection_open_timeout]
134
- # :http_connection_read_timeout # by default == Rightscale::HttpConnection.params[:http_connection_read_timeout]
135
- # :http_connection_retry_delay # by default == Rightscale::HttpConnection.params[:http_connection_retry_delay]
136
- #
137
- def initialize(params={})
138
- @params = params
139
- @params[:http_connection_retry_count] ||= @@params[:http_connection_retry_count]
140
- @params[:http_connection_open_timeout] ||= @@params[:http_connection_open_timeout]
141
- @params[:http_connection_read_timeout] ||= @@params[:http_connection_read_timeout]
142
- @params[:http_connection_retry_delay] ||= @@params[:http_connection_retry_delay]
143
- @http = nil
144
- @server = nil
145
- @logger = get_param(:logger) ||
146
- (RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)) ||
147
- Logger.new(STDOUT)
148
- end
108
+ # Set the global (class-level) parameters
109
+ def self.params=(params)
110
+ @@params = params
111
+ end
149
112
 
150
- def get_param(name)
151
- @params[name] || @@params[name]
152
- end
113
+ #------------------
114
+ # instance methods
115
+ #------------------
116
+ attr_accessor :http
117
+ attr_accessor :server
118
+ attr_accessor :params # see @@params
119
+ attr_accessor :logger
120
+
121
+ # Params hash:
122
+ # :user_agent => 'www.HostName.com' # String to report as HTTP User agent
123
+ # :ca_file => 'path_to_file' # A path of a CA certification file in PEM format. The file can contain several CA certificates.
124
+ # :logger => Logger object # If omitted, HttpConnection logs to STDOUT
125
+ # :exception => Exception to raise # The type of exception to raise if a request repeatedly fails. RuntimeError is raised if this parameter is omitted.
126
+ # :http_connection_retry_count # by default == Rightscale::HttpConnection.params[:http_connection_retry_count]
127
+ # :http_connection_open_timeout # by default == Rightscale::HttpConnection.params[:http_connection_open_timeout]
128
+ # :http_connection_read_timeout # by default == Rightscale::HttpConnection.params[:http_connection_read_timeout]
129
+ # :http_connection_retry_delay # by default == Rightscale::HttpConnection.params[:http_connection_retry_delay]
130
+ #
131
+ def initialize(params={})
132
+ @params = params
133
+ @params[:http_connection_retry_count] ||= @@params[:http_connection_retry_count]
134
+ @params[:http_connection_open_timeout] ||= @@params[:http_connection_open_timeout]
135
+ @params[:http_connection_read_timeout] ||= @@params[:http_connection_read_timeout]
136
+ @params[:http_connection_retry_delay] ||= @@params[:http_connection_retry_delay]
137
+ @http = nil
138
+ @server = nil
139
+ @logger = get_param(:logger) ||
140
+ (RAILS_DEFAULT_LOGGER if defined?(RAILS_DEFAULT_LOGGER)) ||
141
+ Logger.new(STDOUT)
142
+ end
153
143
 
154
- # Query for the maximum size (in bytes) of a single read from the underlying
155
- # socket. For bulk transfer, especially over fast links, this is value is
156
- # critical to performance.
157
- def socket_read_size?
158
- Net::BufferedIO.socket_read_size?
159
- end
144
+ def get_param(name)
145
+ @params[name] || @@params[name]
146
+ end
160
147
 
161
- # Set the maximum size (in bytes) of a single read from the underlying
162
- # socket. For bulk transfer, especially over fast links, this is value is
163
- # critical to performance.
164
- def socket_read_size=(newsize)
165
- Net::BufferedIO.socket_read_size=(newsize)
166
- end
148
+ # Query for the maximum size (in bytes) of a single read from the underlying
149
+ # socket. For bulk transfer, especially over fast links, this is value is
150
+ # critical to performance.
151
+ def socket_read_size?
152
+ Net::BufferedIO.socket_read_size?
153
+ end
167
154
 
168
- # Query for the maximum size (in bytes) of a single read from local data
169
- # sources like files. This is important, for example, in a streaming PUT of a
170
- # large buffer.
171
- def local_read_size?
172
- Net::HTTPGenericRequest.local_read_size?
173
- end
155
+ # Set the maximum size (in bytes) of a single read from the underlying
156
+ # socket. For bulk transfer, especially over fast links, this is value is
157
+ # critical to performance.
158
+ def socket_read_size=(newsize)
159
+ Net::BufferedIO.socket_read_size=(newsize)
160
+ end
174
161
 
175
- # Set the maximum size (in bytes) of a single read from local data
176
- # sources like files. This can be used to tune the performance of, for example, a streaming PUT of a
177
- # large buffer.
178
- def local_read_size=(newsize)
179
- Net::HTTPGenericRequest.local_read_size=(newsize)
180
- end
162
+ # Query for the maximum size (in bytes) of a single read from local data
163
+ # sources like files. This is important, for example, in a streaming PUT of a
164
+ # large buffer.
165
+ def local_read_size?
166
+ Net::HTTPGenericRequest.local_read_size?
167
+ end
181
168
 
182
- private
183
- #--------------
184
- # Retry state - Keep track of errors on a per-server basis
185
- #--------------
186
- @@state = {} # retry state indexed by server: consecutive error count, error time, and error
187
- @@eof = {}
169
+ # Set the maximum size (in bytes) of a single read from local data
170
+ # sources like files. This can be used to tune the performance of, for example, a streaming PUT of a
171
+ # large buffer.
172
+ def local_read_size=(newsize)
173
+ Net::HTTPGenericRequest.local_read_size=(newsize)
174
+ end
188
175
 
189
- # number of consecutive errors seen for server, 0 all is ok
190
- def error_count
191
- @@state[@server] ? @@state[@server][:count] : 0
192
- end
176
+ private
177
+ #--------------
178
+ # Retry state - Keep track of errors on a per-server basis
179
+ #--------------
180
+ @@state = {} # retry state indexed by server: consecutive error count, error time, and error
181
+ @@eof = {}
193
182
 
194
- # time of last error for server, nil if all is ok
195
- def error_time
196
- @@state[@server] && @@state[@server][:time]
197
- end
183
+ # number of consecutive errors seen for server, 0 all is ok
184
+ def error_count
185
+ @@state[@server] ? @@state[@server][:count] : 0
186
+ end
198
187
 
199
- # message for last error for server, "" if all is ok
200
- def error_message
201
- @@state[@server] ? @@state[@server][:message] : ""
202
- end
188
+ # time of last error for server, nil if all is ok
189
+ def error_time
190
+ @@state[@server] && @@state[@server][:time]
191
+ end
203
192
 
204
- # add an error for a server
205
- def error_add(message)
206
- @@state[@server] = { :count => error_count+1, :time => Time.now, :message => message }
207
- end
193
+ # message for last error for server, "" if all is ok
194
+ def error_message
195
+ @@state[@server] ? @@state[@server][:message] : ""
196
+ end
208
197
 
209
- # reset the error state for a server (i.e. a request succeeded)
210
- def error_reset
211
- @@state.delete(@server)
212
- end
198
+ # add an error for a server
199
+ def error_add(message)
200
+ @@state[@server] = { :count => error_count+1, :time => Time.now, :message => message }
201
+ end
213
202
 
214
- # Error message stuff...
215
- def banana_message
216
- return "#{@server} temporarily unavailable: (#{error_message})"
217
- end
203
+ # reset the error state for a server (i.e. a request succeeded)
204
+ def error_reset
205
+ @@state.delete(@server)
206
+ end
218
207
 
219
- def err_header
220
- return "#{self.class.name} :"
221
- end
208
+ # Error message stuff...
209
+ def banana_message
210
+ return "#{@server} temporarily unavailable: (#{error_message})"
211
+ end
222
212
 
223
- # Adds new EOF timestamp.
224
- # Returns the number of seconds to wait before new conection retry:
225
- # 0.5, 1, 2, 4, 8
226
- def add_eof
227
- (@@eof[@server] ||= []).unshift Time.now
228
- 0.25 * 2 ** @@eof[@server].size
229
- end
213
+ def err_header
214
+ return "#{self.class.name} :"
215
+ end
230
216
 
231
- # Returns first EOF timestamp or nul if have no EOFs being tracked.
232
- def eof_time
233
- @@eof[@server] && @@eof[@server].last
234
- end
217
+ # Adds new EOF timestamp.
218
+ # Returns the number of seconds to wait before new conection retry:
219
+ # 0.5, 1, 2, 4, 8
220
+ def add_eof
221
+ (@@eof[@server] ||= []).unshift Time.now
222
+ 0.25 * 2 ** @@eof[@server].size
223
+ end
235
224
 
236
- # Returns true if we are receiving EOFs during last @params[:http_connection_retry_delay] seconds
237
- # and there were no successful response from server
238
- def raise_on_eof_exception?
239
- @@eof[@server].blank? ? false : ( (Time.now.to_i-@params[:http_connection_retry_delay]) > @@eof[@server].last.to_i )
240
- end
225
+ # Returns first EOF timestamp or nul if have no EOFs being tracked.
226
+ def eof_time
227
+ @@eof[@server] && @@eof[@server].last
228
+ end
241
229
 
242
- # Reset a list of EOFs for this server.
243
- # This is being called when we have got an successful response from server.
244
- def eof_reset
245
- @@eof.delete(@server)
246
- end
230
+ # Returns true if we are receiving EOFs during last @params[:http_connection_retry_delay] seconds
231
+ # and there were no successful response from server
232
+ def raise_on_eof_exception?
233
+ @@eof[@server].blank? ? false : ( (Time.now.to_i-@params[:http_connection_retry_delay]) > @@eof[@server].last.to_i )
234
+ end
247
235
 
248
- # Detects if an object is 'streamable' - can we read from it, and can we know the size?
249
- def setup_streaming(request)
250
- if(request.body && request.body.respond_to?(:read))
251
- body = request.body
252
- request.content_length = body.respond_to?(:lstat) ? body.lstat.size : body.size
253
- request.body_stream = request.body
254
- true
255
- end
256
- end
236
+ # Reset a list of EOFs for this server.
237
+ # This is being called when we have got an successful response from server.
238
+ def eof_reset
239
+ @@eof.delete(@server)
240
+ end
257
241
 
258
- def get_fileptr_offset(request_params)
259
- request_params[:request].body.pos
260
- rescue Exception => e
261
- # Probably caught this because the body doesn't support the pos() method, like if it is a socket.
262
- # Just return 0 and get on with life.
263
- 0
264
- end
242
+ # Detects if an object is 'streamable' - can we read from it, and can we know the size?
243
+ def setup_streaming(request)
244
+ if (request.body && request.body.respond_to?(:read))
245
+ body = request.body
246
+ request.content_length = body.respond_to?(:lstat) ? body.lstat.size : body.size
247
+ request.body_stream = request.body
248
+ true
249
+ end
250
+ end
265
251
 
266
- def reset_fileptr_offset(request, offset = 0)
267
- if(request.body_stream && request.body_stream.respond_to?(:pos))
268
- begin
269
- request.body_stream.pos = offset
252
+ def get_fileptr_offset(request_params)
253
+ request_params[:request].body.pos
270
254
  rescue Exception => e
271
- @logger.warn("Failed file pointer reset; aborting HTTP retries." +
272
- " -- #{err_header} #{e.inspect}")
273
- raise e
255
+ # Probably caught this because the body doesn't support the pos() method, like if it is a socket.
256
+ # Just return 0 and get on with life.
257
+ 0
274
258
  end
275
- end
276
- end
277
259
 
278
- # Start a fresh connection. The object closes any existing connection and
279
- # opens a new one.
280
- def start(request_params)
281
- # close the previous if exists
282
- finish
283
- # create new connection
284
- @server = request_params[:server]
285
- @port = request_params[:port]
286
- @protocol = request_params[:protocol]
287
-
288
- @logger.info("Opening new #{@protocol.upcase} connection to #@server:#@port")
289
- @http = Net::HTTP.new(@server, @port)
290
- @http.open_timeout = @params[:http_connection_open_timeout]
291
- @http.read_timeout = @params[:http_connection_read_timeout]
292
-
293
- if @protocol == 'https'
294
- verifyCallbackProc = Proc.new{ |ok, x509_store_ctx|
295
- code = x509_store_ctx.error
296
- msg = x509_store_ctx.error_string
297
- #debugger
298
- @logger.warn("##### #{@server} certificate verify failed: #{msg}") unless code == 0
299
- true
300
- }
301
- @http.use_ssl = true
302
- @http.verify_mode = OpenSSL::SSL::VERIFY_NONE # Looks like Ruby 1.9 defaults to VERIFY_PEER which doesn't work well
303
- ca_file = get_param(:ca_file)
304
- if ca_file
305
- @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
306
- @http.verify_callback = verifyCallbackProc
307
- @http.ca_file = ca_file
260
+ def reset_fileptr_offset(request, offset = 0)
261
+ if (request.body_stream && request.body_stream.respond_to?(:pos))
262
+ begin
263
+ request.body_stream.pos = offset
264
+ rescue Exception => e
265
+ @logger.warn("Failed file pointer reset; aborting HTTP retries." +
266
+ " -- #{err_header} #{e.inspect}")
267
+ raise e
268
+ end
269
+ end
270
+ end
271
+
272
+ # Start a fresh connection. The object closes any existing connection and
273
+ # opens a new one.
274
+ def start(request_params)
275
+ # close the previous if exists
276
+ finish
277
+ # create new connection
278
+ @server = request_params[:server]
279
+ @port = request_params[:port]
280
+ @protocol = request_params[:protocol]
281
+
282
+ @logger.info("Opening new #{@protocol.upcase} connection to #@server:#@port")
283
+ @http = Net::HTTP.new(@server, @port)
284
+ @http.open_timeout = @params[:http_connection_open_timeout]
285
+ @http.read_timeout = @params[:http_connection_read_timeout]
286
+
287
+ if @protocol == 'https'
288
+ verifyCallbackProc = Proc.new{ |ok, x509_store_ctx|
289
+ code = x509_store_ctx.error
290
+ msg = x509_store_ctx.error_string
291
+ #debugger
292
+ @logger.warn("##### #{@server} certificate verify failed: #{msg}") unless code == 0
293
+ true
294
+ }
295
+ @http.use_ssl = true
296
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE # Looks like Ruby 1.9 defaults to VERIFY_PEER which doesn't work well
297
+ ca_file = get_param(:ca_file)
298
+ if ca_file
299
+ @http.verify_mode = OpenSSL::SSL::VERIFY_PEER
300
+ @http.verify_callback = verifyCallbackProc
301
+ @http.ca_file = ca_file
302
+ end
303
+ end
304
+ # open connection
305
+ @http.start
308
306
  end
309
- end
310
- # open connection
311
- @http.start
312
- end
313
307
 
314
- public
308
+ public
315
309
 
316
310
  =begin rdoc
317
311
  Send HTTP request to server
@@ -325,112 +319,116 @@ them.
325
319
  Raises RuntimeError, Interrupt, and params[:exception] (if specified in new).
326
320
 
327
321
  =end
328
- def request(request_params, &block)
329
- # We save the offset here so that if we need to retry, we can return the file pointer to its initial position
330
- mypos = get_fileptr_offset(request_params)
331
- loop do
332
- # if we are inside a delay between retries: no requests this time!
333
- if error_count > @params[:http_connection_retry_count] &&
334
- error_time + @params[:http_connection_retry_delay] > Time.now
335
- # store the message (otherwise it will be lost after error_reset and
336
- # we will raise an exception with an empty text)
337
- banana_message_text = banana_message
338
- @logger.warn("#{err_header} re-raising same error: #{banana_message_text} " +
339
- "-- error count: #{error_count}, error age: #{Time.now.to_i - error_time.to_i}")
340
- exception = get_param(:exception) || RuntimeError
341
- raise exception.new(banana_message_text)
322
+ def request(request_params, &block)
323
+ # We save the offset here so that if we need to retry, we can return the file pointer to its initial position
324
+ mypos = get_fileptr_offset(request_params)
325
+ loop do
326
+ # if we are inside a delay between retries: no requests this time!
327
+ if error_count > @params[:http_connection_retry_count] &&
328
+ error_time + @params[:http_connection_retry_delay] > Time.now
329
+ # store the message (otherwise it will be lost after error_reset and
330
+ # we will raise an exception with an empty text)
331
+ banana_message_text = banana_message
332
+ @logger.warn("#{err_header} re-raising same error: #{banana_message_text} " +
333
+ "-- error count: #{error_count}, error age: #{Time.now.to_i - error_time.to_i}")
334
+ exception = get_param(:exception) || RuntimeError
335
+ raise exception.new(banana_message_text)
336
+ end
337
+
338
+ # try to connect server(if connection does not exist) and get response data
339
+ begin
340
+ request_params[:protocol] ||= (request_params[:port] == 443 ? 'https' : 'http')
341
+
342
+ request = request_params[:request]
343
+ request['User-Agent'] = get_param(:user_agent) || ''
344
+
345
+ # (re)open connection to server if none exists or params has changed
346
+ unless @http &&
347
+ @http.started? &&
348
+ @server == request_params[:server] &&
349
+ @port == request_params[:port] &&
350
+ @protocol == request_params[:protocol]
351
+ start(request_params)
352
+ end
353
+
354
+ # Detect if the body is a streamable object like a file or socket. If so, stream that
355
+ # bad boy.
356
+ setup_streaming(request)
357
+ response = @http.request(request, &block)
358
+
359
+ error_reset
360
+ eof_reset
361
+ return response
362
+
363
+ # We treat EOF errors and the timeout/network errors differently. Both
364
+ # are tracked in different statistics blocks. Note below that EOF
365
+ # errors will sleep for a certain (exponentially increasing) period.
366
+ # Other errors don't sleep because there is already an inherent delay
367
+ # in them; connect and read timeouts (for example) have already
368
+ # 'slept'. It is still not clear which way we should treat errors
369
+ # like RST and resolution failures. For now, there is no additional
370
+ # delay for these errors although this may change in the future.
371
+
372
+ # EOFError means the server closed the connection on us.
373
+ rescue EOFError => e
374
+ @logger.debug("#{err_header} server #{@server} closed connection")
375
+ @http = nil
376
+
377
+ # if we have waited long enough - raise an exception...
378
+ if raise_on_eof_exception?
379
+ exception = get_param(:exception) || RuntimeError
380
+ @logger.warn("#{err_header} raising #{exception} due to permanent EOF being received from #{@server}, error age: #{Time.now.to_i - eof_time.to_i}")
381
+ raise exception.new("Permanent EOF is being received from #{@server}.")
382
+ else
383
+ # ... else just sleep a bit before new retry
384
+ sleep(add_eof)
385
+ # We will be retrying the request, so reset the file pointer
386
+ reset_fileptr_offset(request, mypos)
387
+ end
388
+ rescue Exception => e # See comment at bottom for the list of errors seen...
389
+ @http = nil
390
+ # if ctrl+c is pressed - we have to reraise exception to terminate proggy
391
+ if e.is_a?(Interrupt) && !( e.is_a?(Errno::ETIMEDOUT) || e.is_a?(Timeout::Error))
392
+ @logger.debug( "#{err_header} request to server #{@server} interrupted by ctrl-c")
393
+ raise
394
+ elsif e.is_a?(ArgumentError) && e.message.include?('wrong number of arguments (5 for 4)')
395
+ # seems our net_fix patch was overriden...
396
+ exception = get_param(:exception) || RuntimeError
397
+ raise exception.new('incompatible Net::HTTP monkey-patch')
398
+ end
399
+ # oops - we got a banana: log it
400
+ error_add(e.message)
401
+ @logger.warn("#{err_header} request failure count: #{error_count}, exception: #{e.inspect}")
402
+
403
+ # We will be retrying the request, so reset the file pointer
404
+ reset_fileptr_offset(request, mypos)
405
+
406
+ end
407
+ end
342
408
  end
343
409
 
344
- # try to connect server(if connection does not exist) and get response data
345
- begin
346
- request_params[:protocol] ||= (request_params[:port] == 443 ? 'https' : 'http')
347
-
348
- request = request_params[:request]
349
- request['User-Agent'] = get_param(:user_agent) || ''
350
-
351
- # (re)open connection to server if none exists or params has changed
352
- unless @http &&
353
- @http.started? &&
354
- @server == request_params[:server] &&
355
- @port == request_params[:port] &&
356
- @protocol == request_params[:protocol]
357
- start(request_params)
358
- end
359
-
360
- # Detect if the body is a streamable object like a file or socket. If so, stream that
361
- # bad boy.
362
- setup_streaming(request)
363
- response = @http.request(request, &block)
364
-
365
- error_reset
366
- eof_reset
367
- return response
368
-
369
- # We treat EOF errors and the timeout/network errors differently. Both
370
- # are tracked in different statistics blocks. Note below that EOF
371
- # errors will sleep for a certain (exponentially increasing) period.
372
- # Other errors don't sleep because there is already an inherent delay
373
- # in them; connect and read timeouts (for example) have already
374
- # 'slept'. It is still not clear which way we should treat errors
375
- # like RST and resolution failures. For now, there is no additional
376
- # delay for these errors although this may change in the future.
377
-
378
- # EOFError means the server closed the connection on us.
379
- rescue EOFError => e
380
- @logger.debug("#{err_header} server #{@server} closed connection")
381
- @http = nil
382
-
383
- # if we have waited long enough - raise an exception...
384
- if raise_on_eof_exception?
385
- exception = get_param(:exception) || RuntimeError
386
- @logger.warn("#{err_header} raising #{exception} due to permanent EOF being received from #{@server}, error age: #{Time.now.to_i - eof_time.to_i}")
387
- raise exception.new("Permanent EOF is being received from #{@server}.")
388
- else
389
- # ... else just sleep a bit before new retry
390
- sleep(add_eof)
391
- # We will be retrying the request, so reset the file pointer
392
- reset_fileptr_offset(request, mypos)
393
- end
394
- rescue Exception => e # See comment at bottom for the list of errors seen...
395
- @http = nil
396
- # if ctrl+c is pressed - we have to reraise exception to terminate proggy
397
- if e.is_a?(Interrupt) && !( e.is_a?(Errno::ETIMEDOUT) || e.is_a?(Timeout::Error))
398
- @logger.debug( "#{err_header} request to server #{@server} interrupted by ctrl-c")
399
- raise
400
- elsif e.is_a?(ArgumentError) && e.message.include?('wrong number of arguments (5 for 4)')
401
- # seems our net_fix patch was overriden...
402
- exception = get_param(:exception) || RuntimeError
403
- raise exception.new('incompatible Net::HTTP monkey-patch')
404
- end
405
- # oops - we got a banana: log it
406
- error_add(e.message)
407
- @logger.warn("#{err_header} request failure count: #{error_count}, exception: #{e.inspect}")
408
-
409
- # We will be retrying the request, so reset the file pointer
410
- reset_fileptr_offset(request, mypos)
410
+ def finish(reason = '')
411
+ if @http && @http.started?
412
+ reason = ", reason: '#{reason}'" unless reason.blank?
413
+ @logger.info("Closing #{@http.use_ssl? ? 'HTTPS' : 'HTTP'} connection to #{@http.address}:#{@http.port}#{reason}")
414
+ @http.finish
415
+ end
416
+ end
411
417
 
418
+ def close(reason='')
419
+ finish
412
420
  end
413
- end
414
- end
415
421
 
416
- def finish(reason = '')
417
- if @http && @http.started?
418
- reason = ", reason: '#{reason}'" unless reason.blank?
419
- @logger.info("Closing #{@http.use_ssl? ? 'HTTPS' : 'HTTP'} connection to #{@http.address}:#{@http.port}#{reason}")
420
- @http.finish
421
- end
422
+ # Errors received during testing:
423
+ #
424
+ # #<Timeout::Error: execution expired>
425
+ # #<Errno::ETIMEDOUT: Connection timed out - connect(2)>
426
+ # #<SocketError: getaddrinfo: Name or service not known>
427
+ # #<SocketError: getaddrinfo: Temporary failure in name resolution>
428
+ # #<EOFError: end of file reached>
429
+ # #<Errno::ECONNRESET: Connection reset by peer>
430
+ # #<OpenSSL::SSL::SSLError: SSL_write:: bad write retry>
422
431
  end
423
432
 
424
- # Errors received during testing:
425
- #
426
- # #<Timeout::Error: execution expired>
427
- # #<Errno::ETIMEDOUT: Connection timed out - connect(2)>
428
- # #<SocketError: getaddrinfo: Name or service not known>
429
- # #<SocketError: getaddrinfo: Temporary failure in name resolution>
430
- # #<EOFError: end of file reached>
431
- # #<Errno::ECONNRESET: Connection reset by peer>
432
- # #<OpenSSL::SSL::SSLError: SSL_write:: bad write retry>
433
- end
434
-
435
433
  end
436
434
 
metadata CHANGED
@@ -1,7 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: http_connection
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ hash: 25
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 3
9
+ - 1
10
+ version: 1.3.1
5
11
  platform: ruby
6
12
  authors:
7
13
  - Travis Reeder
@@ -10,7 +16,7 @@ autorequire:
10
16
  bindir: bin
11
17
  cert_chain: []
12
18
 
13
- date: 2009-07-23 00:00:00 -07:00
19
+ date: 2010-09-13 00:00:00 -07:00
14
20
  default_executable:
15
21
  dependencies: []
16
22
 
@@ -36,23 +42,29 @@ rdoc_options:
36
42
  require_paths:
37
43
  - lib
38
44
  required_ruby_version: !ruby/object:Gem::Requirement
45
+ none: false
39
46
  requirements:
40
47
  - - ">="
41
48
  - !ruby/object:Gem::Version
49
+ hash: 3
50
+ segments:
51
+ - 0
42
52
  version: "0"
43
- version:
44
53
  required_rubygems_version: !ruby/object:Gem::Requirement
54
+ none: false
45
55
  requirements:
46
56
  - - ">="
47
57
  - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
48
61
  version: "0"
49
- version:
50
62
  requirements: []
51
63
 
52
64
  rubyforge_project:
53
- rubygems_version: 1.3.5
65
+ rubygems_version: 1.3.7
54
66
  signing_key:
55
- specification_version: 2
67
+ specification_version: 3
56
68
  summary: HTTP helper library
57
69
  test_files: []
58
70