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.
- data/History.txt +11 -1
- data/README.txt +1 -1
- data/lib/right_http_connection.rb +88 -36
- metadata +42 -35
data/History.txt
CHANGED
@@ -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
@@ -34,7 +34,7 @@ module RightHttpConnection #:nodoc:
|
|
34
34
|
module VERSION #:nodoc:
|
35
35
|
MAJOR = 1
|
36
36
|
MINOR = 2
|
37
|
-
TINY =
|
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 =
|
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
|
-
|
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
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
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
|
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
|
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 =
|
246
|
-
@http.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 >
|
286
|
-
|
287
|
-
|
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(
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
45
|
-
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
|