right_http_connection 1.2.1 → 1.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/History.txt +11 -1
  2. data/README.txt +1 -1
  3. data/lib/right_http_connection.rb +88 -36
  4. metadata +42 -35
@@ -39,4 +39,14 @@ Initial public release
39
39
  * "RightAws: incompatible Net::HTTP monkey-patch" exception is raised if our net_fix
40
40
  patch was overriden (by attachment_fu for example, to avoid this load attachment_fu
41
41
  before loading the right_http_connection gem).
42
-
42
+
43
+ == 1.2.2
44
+
45
+ * r3524, konstantin, 2008-04-17 11:35:42 +0400
46
+ * Fixed a problem with incorrect error handling (connection retries always failed).
47
+
48
+ == 1.2.3
49
+
50
+ - Added support for setting retry & timeout parameters in the constructor
51
+ - Improve handling of data streams during upload: if there is a failure and a retry, reset
52
+ the seek pointer for the subsequent re-request
data/README.txt CHANGED
@@ -28,7 +28,7 @@ algorithm for low-level network errors.
28
28
 
29
29
  == INSTALL:
30
30
 
31
- sudo gem install
31
+ sudo gem install right_http_connection
32
32
 
33
33
  == LICENSE:
34
34
 
@@ -34,7 +34,7 @@ module RightHttpConnection #:nodoc:
34
34
  module VERSION #:nodoc:
35
35
  MAJOR = 1
36
36
  MINOR = 2
37
- TINY = 1
37
+ TINY = 3
38
38
 
39
39
  STRING = [MAJOR, MINOR, TINY].join('.')
40
40
  end
@@ -82,22 +82,31 @@ them.
82
82
  # Throw a Timeout::Error if a connection isn't established within this number of seconds
83
83
  HTTP_CONNECTION_OPEN_TIMEOUT = 5
84
84
  # Throw a Timeout::Error if no data have been read on this connnection within this number of seconds
85
- HTTP_CONNECTION_READ_TIMEOUT = 30
85
+ HTTP_CONNECTION_READ_TIMEOUT = 120
86
86
  # Length of the post-error probationary period during which all requests will fail
87
87
  HTTP_CONNECTION_RETRY_DELAY = 15
88
88
 
89
89
  #--------------------
90
90
  # class methods
91
91
  #--------------------
92
- # Params hash
93
- # :user_agent => 'www.HostName.com' # String to report as HTTP User agent
94
- # :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.
95
- # :logger => Logger object # If omitted, HttpConnection logs to STDOUT
96
- # :exception => Exception to raise # The type of exception to raise
97
- # if a request repeatedly fails. RuntimeError is raised if this parameter is omitted.
92
+ #
98
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
99
98
 
100
- # Query the global (class-level) parameters
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
101
110
  def self.params
102
111
  @@params
103
112
  end
@@ -115,16 +124,22 @@ them.
115
124
  attr_accessor :params # see @@params
116
125
  attr_accessor :logger
117
126
 
118
- =begin rdoc
119
- Params hash:
120
- :user_agent => 'www.HostName.com' String to report as HTTP User agent
121
- :ca_file => 'path_to_file' A path of a CA certification file in PEM format. The file can contain several CA certificates.
122
- :logger => Logger object If omitted, HttpConnection logs to STDOUT
123
- :exception => Exception to raise The type of exception to raise if a request repeatedly fails. RuntimeError is raised if this parameter is omitted.
124
-
125
- =end
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
+ #
126
137
  def initialize(params={})
127
- @params = 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]
128
143
  @http = nil
129
144
  @server = nil
130
145
  @logger = get_param(:logger) ||
@@ -218,10 +233,10 @@ them.
218
233
  @@eof[@server] && @@eof[@server].last
219
234
  end
220
235
 
221
- # Returns true if we are receiving EOFs during last HTTP_CONNECTION_RETRY_DELAY seconds
236
+ # Returns true if we are receiving EOFs during last @params[:http_connection_retry_delay] seconds
222
237
  # and there were no successful response from server
223
238
  def raise_on_eof_exception?
224
- @@eof[@server].blank? ? false : ( (Time.now.to_i-HTTP_CONNECTION_RETRY_DELAY) > @@eof[@server].last.to_i )
239
+ @@eof[@server].blank? ? false : ( (Time.now.to_i-@params[:http_connection_retry_delay]) > @@eof[@server].last.to_i )
225
240
  end
226
241
 
227
242
  # Reset a list of EOFs for this server.
@@ -229,6 +244,36 @@ them.
229
244
  def eof_reset
230
245
  @@eof.delete(@server)
231
246
  end
247
+
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
257
+
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
265
+
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
270
+ rescue Exception => e
271
+ @logger.warn("Failed file pointer reset; aborting HTTP retries." +
272
+ " -- #{err_header} #{e.inspect}")
273
+ raise e
274
+ end
275
+ end
276
+ end
232
277
 
233
278
  # Start a fresh connection. The object closes any existing connection and
234
279
  # opens a new one.
@@ -242,8 +287,8 @@ them.
242
287
 
243
288
  @logger.info("Opening new #{@protocol.upcase} connection to #@server:#@port")
244
289
  @http = Net::HTTP.new(@server, @port)
245
- @http.open_timeout = HTTP_CONNECTION_OPEN_TIMEOUT
246
- @http.read_timeout = HTTP_CONNECTION_READ_TIMEOUT
290
+ @http.open_timeout = @params[:http_connection_open_timeout]
291
+ @http.read_timeout = @params[:http_connection_read_timeout]
247
292
 
248
293
  if @protocol == 'https'
249
294
  verifyCallbackProc = Proc.new{ |ok, x509_store_ctx|
@@ -271,25 +316,30 @@ them.
271
316
  Send HTTP request to server
272
317
 
273
318
  request_params hash:
274
- :server => 'www.HostName.com' Hostname or IP address of HTTP server
275
- :port => '80' Port of HTTP server
276
- :protocol => 'https' http and https are supported on any port
277
- :request => 'requeststring' Fully-formed HTTP request to make
319
+ :server => 'www.HostName.com' # Hostname or IP address of HTTP server
320
+ :port => '80' # Port of HTTP server
321
+ :protocol => 'https' # http and https are supported on any port
322
+ :request => 'requeststring' # Fully-formed HTTP request to make
278
323
 
279
324
  Raises RuntimeError, Interrupt, and params[:exception] (if specified in new).
280
325
 
281
326
  =end
282
327
  def request(request_params, &block)
328
+ # We save the offset here so that if we need to retry, we can return the file pointer to its initial position
329
+ mypos = get_fileptr_offset(request_params)
283
330
  loop do
284
331
  # if we are inside a delay between retries: no requests this time!
285
- if error_count > HTTP_CONNECTION_RETRY_COUNT \
286
- && error_time + HTTP_CONNECTION_RETRY_DELAY > Time.now
287
- @logger.warn("#{err_header} re-raising same error: #{banana_message} " +
332
+ if error_count > @params[:http_connection_retry_count] &&
333
+ error_time + @params[:http_connection_retry_delay] > Time.now
334
+ # store the message (otherwise it will be lost after error_reset and
335
+ # we will raise an exception with an empty text)
336
+ banana_message_text = banana_message
337
+ @logger.warn("#{err_header} re-raising same error: #{banana_message_text} " +
288
338
  "-- error count: #{error_count}, error age: #{Time.now.to_i - error_time.to_i}")
289
339
  exception = get_param(:exception) || RuntimeError
290
- raise exception.new(banana_message)
340
+ raise exception.new(banana_message_text)
291
341
  end
292
-
342
+
293
343
  # try to connect server(if connection does not exist) and get response data
294
344
  begin
295
345
  request_params[:protocol] ||= (request_params[:port] == 443 ? 'https' : 'http')
@@ -308,11 +358,7 @@ them.
308
358
 
309
359
  # Detect if the body is a streamable object like a file or socket. If so, stream that
310
360
  # bad boy.
311
- if(request.body && request.body.respond_to?(:read))
312
- body = request.body
313
- request.content_length = body.respond_to?(:lstat) ? body.lstat.size : body.size
314
- request.body_stream = request.body
315
- end
361
+ setup_streaming(request)
316
362
  response = @http.request(request, &block)
317
363
 
318
364
  error_reset
@@ -341,6 +387,8 @@ them.
341
387
  else
342
388
  # ... else just sleep a bit before new retry
343
389
  sleep(add_eof)
390
+ # We will be retrying the request, so reset the file pointer
391
+ reset_fileptr_offset(request, mypos)
344
392
  end
345
393
  rescue Exception => e # See comment at bottom for the list of errors seen...
346
394
  @http = nil
@@ -356,6 +404,10 @@ them.
356
404
  # oops - we got a banana: log it
357
405
  error_add(e.message)
358
406
  @logger.warn("#{err_header} request failure count: #{error_count}, exception: #{e.inspect}")
407
+
408
+ # We will be retrying the request, so reset the file pointer
409
+ reset_fileptr_offset(request, mypos)
410
+
359
411
  end
360
412
  end
361
413
  end
metadata CHANGED
@@ -1,33 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
3
- specification_version: 1
4
2
  name: right_http_connection
5
3
  version: !ruby/object:Gem::Version
6
- version: 1.2.1
7
- date: 2008-02-11 00:00:00 -08:00
8
- summary: RightScale's robust HTTP/S connection module
9
- require_paths:
10
- - lib
11
- email: support@rightscale.com
12
- homepage: http://rightaws.rubyforge.org
13
- rubyforge_project: rightaws
14
- description: RightScale's robust HTTP/S connection module
15
- autorequire:
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">"
22
- - !ruby/object:Gem::Version
23
- version: 0.0.0
24
- version:
4
+ version: 1.2.3
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - RightScale
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-06-27 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: RightScale's robust HTTP/S connection module
17
+ email: support@rightscale.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - History.txt
24
+ - Manifest.txt
25
+ - README.txt
31
26
  files:
32
27
  - History.txt
33
28
  - Manifest.txt
@@ -36,20 +31,32 @@ files:
36
31
  - lib/net_fix.rb
37
32
  - lib/right_http_connection.rb
38
33
  - setup.rb
39
- test_files: []
40
-
34
+ has_rdoc: true
35
+ homepage: http://rightaws.rubyforge.org
36
+ post_install_message:
41
37
  rdoc_options:
42
38
  - --main
43
39
  - README.txt
44
- extra_rdoc_files:
45
- - History.txt
46
- - Manifest.txt
47
- - README.txt
48
- executables: []
49
-
50
- extensions: []
51
-
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
52
54
  requirements: []
53
55
 
54
- dependencies: []
56
+ rubyforge_project: rightaws
57
+ rubygems_version: 1.0.1
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: RightScale's robust HTTP/S connection module
61
+ test_files: []
55
62