http_connection 1.3.0 → 1.3.1

Sign up to get free protection for your applications and to get access to all the features.
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