right_cloud_api_base 0.1.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/HISTORY +5 -1
- data/lib/base/api_manager.rb +4 -0
- data/lib/base/helpers/http_request.rb +15 -20
- data/lib/base/helpers/utils.rb +10 -9
- data/lib/base/routines/connection_proxies/net_http_persistent_proxy.rb +89 -35
- data/lib/base/routines/connection_proxies/right_http_connection_proxy.rb +4 -1
- data/lib/base/routines/connection_proxy.rb +3 -3
- data/lib/right_cloud_api_base_version.rb +2 -2
- metadata +9 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 93876f7645c63a599b1cdde9e5752490efcb6b9e
|
4
|
+
data.tar.gz: cf0a599994e10d1c17c2cf014e03ab303a18deed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76b486fbb4c8392dff74a77e953c051b7eabeb8a2e723f795d0efb1bf66c3cebbcb6c543bc36e4674e41adee214e8bac4955fda97cd69b8a79e11ad7a4752e9c
|
7
|
+
data.tar.gz: ba3b90502877bbe162ceae9bfba958bb4682cc5ee2083fc1cd71c36b435c1ca8bb078cd522861683fb91fa30d3bf82855758936e803c2b04275e654a06e8b44a
|
data/HISTORY
CHANGED
data/lib/base/api_manager.rb
CHANGED
@@ -179,6 +179,10 @@ module RightScale
|
|
179
179
|
# @option options [Integer] :connection_retry_delay
|
180
180
|
# Defines how long we wait on a low level connection error (in seconds)
|
181
181
|
#
|
182
|
+
# @option options [Integer] :connection_verify_mode
|
183
|
+
# SSL connection cert check: either OpenSSL::SSL::VERIFY_PEER (default) or
|
184
|
+
# OpenSSL::SSL::VERIFY_NONE
|
185
|
+
#
|
182
186
|
# @option options [Hash] :creds
|
183
187
|
# A set of optional extra creds a cloud may require
|
184
188
|
# (see right_cloud_stack_api gem which supports :tenant_name and :tenant_id)
|
@@ -116,26 +116,21 @@ module RightScale
|
|
116
116
|
#
|
117
117
|
def body=(new_body)
|
118
118
|
# Set a request body
|
119
|
-
if new_body.
|
120
|
-
@body =
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
file_size = file.respond_to?(:lstat) ? file.lstat.size : file.size
|
131
|
-
bytes_to_read = [ file_size - file.pos, self['content-length'].first ].compact.map{|v| v.to_i }.sort.first # remove nils then make values Integers
|
132
|
-
if self['content-length'].first._blank? || self['content-length'].first.to_i > bytes_to_read
|
133
|
-
self['content-length'] = bytes_to_read
|
134
|
-
end
|
135
|
-
else
|
136
|
-
@body = new_body
|
137
|
-
self['content-length'] = body.size if self['content-length'].first.to_i > body.size
|
119
|
+
if new_body.is_a?(IO)
|
120
|
+
@body = file = new_body
|
121
|
+
# Make sure the file is openned in binmode
|
122
|
+
file.binmode if file.respond_to?(:binmode)
|
123
|
+
# Fix 'content-length': it must not be bigger than a piece of a File left to be read or a String body size.
|
124
|
+
# Otherwise the connection may behave like crazy causing 4xx or 5xx responses
|
125
|
+
# KD: Make sure this code is used with the patched RightHttpConnection gem (see net_fix.rb)
|
126
|
+
file_size = file.respond_to?(:lstat) ? file.lstat.size : file.size
|
127
|
+
bytes_to_read = [ file_size - file.pos, self['content-length'].first ].compact.map{|v| v.to_i }.sort.first # remove nils then make values Integers
|
128
|
+
if self['content-length'].first._blank? || self['content-length'].first.to_i > bytes_to_read
|
129
|
+
self['content-length'] = bytes_to_read
|
138
130
|
end
|
131
|
+
else
|
132
|
+
@body = new_body.to_s
|
133
|
+
self['content-length'] = @body.bytesize if self['content-length'].first.to_i > @body.bytesize
|
139
134
|
end
|
140
135
|
end
|
141
136
|
|
@@ -164,7 +159,7 @@ module RightScale
|
|
164
159
|
if is_io?
|
165
160
|
"#{body.class.name}, size: #{body.respond_to?(:lstat) ? body.lstat.size : body.size}, pos: #{body.pos}"
|
166
161
|
else
|
167
|
-
"size: #{body.to_s.
|
162
|
+
"size: #{body.to_s.bytesize}, first #{BODY_BYTES_TO_LOG} bytes:\n#{body.to_s[0...BODY_BYTES_TO_LOG]}"
|
168
163
|
end
|
169
164
|
end
|
170
165
|
end
|
data/lib/base/helpers/utils.rb
CHANGED
@@ -49,7 +49,7 @@ module RightScale
|
|
49
49
|
def self.base64en(string)
|
50
50
|
Base64::encode64(string.to_s).strip
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
# Makes a URL params string from a given hash. If block is given the it invokes the block on
|
54
54
|
# every value so that one could escape it in his own way. If there is no block it url_encode
|
55
55
|
# values automatically.
|
@@ -169,9 +169,9 @@ module RightScale
|
|
169
169
|
# :code! => Condition, # Response code must not match Condition
|
170
170
|
# :response! => Condition, # Response body must not match Condition
|
171
171
|
# :if => Proc::new{ |opts| do something } # Extra condition: should return true | false
|
172
|
-
#
|
172
|
+
#
|
173
173
|
# (Condition above is /RegExp/ or String or Symbol)
|
174
|
-
#
|
174
|
+
#
|
175
175
|
# Opts is a Hash:
|
176
176
|
# :request => Object, # HTTP request instance
|
177
177
|
# :response => Object, # HTTP response instance
|
@@ -210,7 +210,7 @@ module RightScale
|
|
210
210
|
return nil if pattern[:if] && !pattern[:if].call(opts)
|
211
211
|
true
|
212
212
|
end
|
213
|
-
|
213
|
+
|
214
214
|
# Returns an Array with the current Thread and Fiber (if exists) instances.
|
215
215
|
#
|
216
216
|
# @return [Array] The first item is the current Thread instance and the second item
|
@@ -233,7 +233,7 @@ module RightScale
|
|
233
233
|
end
|
234
234
|
end
|
235
235
|
end
|
236
|
-
|
236
|
+
|
237
237
|
# Transforms body (when it is a Hash) into String
|
238
238
|
#
|
239
239
|
# @param [Hash] body The request body as a Hash instance.
|
@@ -254,13 +254,14 @@ module RightScale
|
|
254
254
|
def self.contentify_body(body, content_type)
|
255
255
|
return body unless body.is_a?(Hash)
|
256
256
|
# Transform
|
257
|
+
ct = dearrayify(content_type).to_s
|
257
258
|
case dearrayify(content_type).to_s
|
258
259
|
when /json/ then body.to_json
|
259
260
|
when /xml/ then body._to_xml!
|
260
|
-
else fail Error::new("
|
261
|
+
else fail Error::new("Cannot transform Hash body into #{ct.inspect} content type")
|
261
262
|
end
|
262
263
|
end
|
263
|
-
|
264
|
+
|
264
265
|
# Generates a unique token (Uses UUID when possible)
|
265
266
|
#
|
266
267
|
# @return [String] A random 28-symbols string.
|
@@ -275,7 +276,7 @@ module RightScale
|
|
275
276
|
# Use UUID gem if it is
|
276
277
|
if defined?(UUID) && UUID::respond_to?(:new)
|
277
278
|
uuid = UUID::new
|
278
|
-
return uuid.generate if uuid.respond_to?(:generate)
|
279
|
+
return uuid.generate if uuid.respond_to?(:generate)
|
279
280
|
end
|
280
281
|
# Otherwise generate a random token
|
281
282
|
time = Time::now.utc
|
@@ -374,7 +375,7 @@ module RightScale
|
|
374
375
|
end
|
375
376
|
chain
|
376
377
|
end
|
377
|
-
|
378
|
+
|
378
379
|
end
|
379
380
|
end
|
380
381
|
end
|
@@ -24,7 +24,7 @@
|
|
24
24
|
module RightScale
|
25
25
|
module CloudApi
|
26
26
|
class ConnectionProxy
|
27
|
-
|
27
|
+
|
28
28
|
class NetHttpPersistentProxy
|
29
29
|
class Error < CloudApi::Error
|
30
30
|
end
|
@@ -40,7 +40,8 @@ module RightScale
|
|
40
40
|
def log(message)
|
41
41
|
@data[:options][:cloud_api_logger].log(message, :connection_proxy, :warn)
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
|
+
|
44
45
|
# Performs an HTTP request.
|
45
46
|
#
|
46
47
|
# @param [Hash] data The API request +data+ storage.
|
@@ -51,48 +52,101 @@ module RightScale
|
|
51
52
|
#
|
52
53
|
def request(data)
|
53
54
|
require "net/http/persistent"
|
54
|
-
|
55
|
-
@data
|
55
|
+
# Initialize things:
|
56
|
+
@data = data
|
56
57
|
@data[:response] = {}
|
57
|
-
|
58
|
-
|
59
|
-
# Create
|
60
|
-
connection
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
http_request.body = fake.body
|
58
|
+
# Create a new HTTP request instance
|
59
|
+
http_request = create_new_http_request
|
60
|
+
# Create and tweak Net::HTTP::Persistent instance
|
61
|
+
connection = create_new_persistent_connection
|
62
|
+
# Make a request
|
63
|
+
begin
|
64
|
+
make_request_with_retries(connection, @data[:connection][:uri], http_request)
|
65
|
+
rescue => e
|
66
|
+
fail(ConnectionError, e.message)
|
67
|
+
ensure
|
68
|
+
connection.shutdown
|
69
69
|
end
|
70
|
-
|
71
|
-
fake.raw = http_request
|
70
|
+
end
|
72
71
|
|
72
|
+
|
73
|
+
# Creates a new connection.
|
74
|
+
#
|
75
|
+
# There is a bug in Net::HTTP::Persistent where it allows you to reuse an SSL connection
|
76
|
+
# created by another instance of Net::HTTP::Persistent, if they share the same app name.
|
77
|
+
# To avoid this, every instance of Net::HTTP::Persistent should have its own 'name'.
|
78
|
+
#
|
79
|
+
# If your app does not care about SSL certs and keys (like AWS does) then it is safe to
|
80
|
+
# reuse connections.
|
81
|
+
#
|
82
|
+
# see https://github.com/drbrain/net-http-persistent/issues/45
|
83
|
+
#
|
84
|
+
def create_new_persistent_connection
|
85
|
+
app_name = if @data[:options][:connection_ca_file] ||
|
86
|
+
@data[:credentials][:cert] ||
|
87
|
+
@data[:credentials][:key]
|
88
|
+
'right_cloud_api_gem_%s' % Utils::generate_token
|
89
|
+
else
|
90
|
+
'right_cloud_api_gem'
|
91
|
+
end
|
92
|
+
connection = Net::HTTP::Persistent.new(app_name)
|
93
|
+
set_persistent_connection_options!(connection)
|
73
94
|
# Register a callback to close current connection
|
74
95
|
@data[:callbacks][:close_current_connection] = Proc::new do |reason|
|
75
96
|
connection.shutdown
|
76
97
|
log "Current connection closed: #{reason}"
|
77
98
|
end
|
78
|
-
|
79
|
-
|
80
|
-
# P.S. :connection_retry_count, :http_connection_retry_delay are not supported by this proxy
|
81
|
-
#
|
82
|
-
http_request['user-agent'] ||= @data[:options][:connection_user_agent] if @data[:options].has_key?(:connection_user_agent)
|
83
|
-
connection.ca_file = @data[:options][:connection_ca_file] if @data[:options].has_key?(:connection_ca_file)
|
84
|
-
connection.read_timeout = @data[:options][:connection_read_timeout] if @data[:options].has_key?(:connection_read_timeout)
|
85
|
-
connection.open_timeout = @data[:options][:connection_open_timeout] if @data[:options].has_key?(:connection_open_timeout)
|
86
|
-
connection.cert = OpenSSL::X509::Certificate.new(@data[:credentials][:cert]) if @data[:credentials].has_key?(:cert)
|
87
|
-
connection.key = OpenSSL::PKey::RSA.new(@data[:credentials][:key]) if @data[:credentials].has_key?(:key)
|
99
|
+
connection
|
100
|
+
end
|
88
101
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
102
|
+
|
103
|
+
# Sets connection_ca_file, connection_read_timeout, connection_open_timeout,
|
104
|
+
# connection_verify_mode and SSL cert and key
|
105
|
+
#
|
106
|
+
# @param [Net::HTTP::Persistent] connection
|
107
|
+
#
|
108
|
+
# @return [Net::HTTP::Persistent]
|
109
|
+
#
|
110
|
+
def set_persistent_connection_options!(connection)
|
111
|
+
[:ca_file, :read_timeout, :open_timeout, :verify_mode].each do |connection_method|
|
112
|
+
connection_option_name = "connection_#{connection_method}".to_sym
|
113
|
+
next unless @data[:options].has_key?(connection_option_name)
|
114
|
+
connection.__send__("#{connection_method}=", @data[:options][connection_option_name])
|
115
|
+
end
|
116
|
+
if @data[:credentials].has_key?(:cert)
|
117
|
+
connection.cert = OpenSSL::X509::Certificate.new(@data[:credentials][:cert])
|
118
|
+
end
|
119
|
+
if @data[:credentials].has_key?(:key)
|
120
|
+
connection.key = OpenSSL::PKey::RSA.new(@data[:credentials][:key])
|
121
|
+
end
|
122
|
+
connection
|
123
|
+
end
|
124
|
+
|
125
|
+
|
126
|
+
# Creates and configures a new HTTP request object
|
127
|
+
#
|
128
|
+
# @return [Net::HTTPRequest]
|
129
|
+
#
|
130
|
+
def create_new_http_request
|
131
|
+
# Create a new HTTP request instance
|
132
|
+
request_spec = @data[:request][:instance]
|
133
|
+
http_class = "Net::HTTP::#{request_spec.verb._camelize}"
|
134
|
+
http_request = http_class._constantize::new(request_spec.path)
|
135
|
+
# Set the request body
|
136
|
+
if request_spec.is_io?
|
137
|
+
http_request.body_stream = request_spec.body
|
138
|
+
else
|
139
|
+
http_request.body = request_spec.body
|
140
|
+
end
|
141
|
+
# Copy headers
|
142
|
+
request_spec.headers.each { |header, value| http_request[header] = value }
|
143
|
+
# Save the new request
|
144
|
+
request_spec.raw = http_request
|
145
|
+
# Set user-agent
|
146
|
+
if @data[:options].has_key?(:connection_user_agent)
|
147
|
+
http_request['user-agent'] ||= @data[:options][:connection_user_agent]
|
95
148
|
end
|
149
|
+
http_request
|
96
150
|
end
|
97
151
|
|
98
152
|
|
@@ -188,7 +242,7 @@ module RightScale
|
|
188
242
|
nil
|
189
243
|
end
|
190
244
|
|
191
|
-
end
|
245
|
+
end
|
192
246
|
end
|
193
247
|
end
|
194
248
|
end
|
@@ -91,7 +91,10 @@ module RightScale
|
|
91
91
|
http_connection_data[:raise_on_timeout] = @data[:options][:abort_on_timeout] if @data[:options][:abort_on_timeout]
|
92
92
|
http_connection_data[:cert] = @data[:credentials][:cert] if @data[:credentials].has_key?(:cert)
|
93
93
|
http_connection_data[:key] = @data[:credentials][:key] if @data[:credentials].has_key?(:key)
|
94
|
-
|
94
|
+
if @data[:options].has_key?(:connection_verify_mode)
|
95
|
+
http_connection_data[:use_server_auth] = (@data[:options][:connection_verify_mode] != OpenSSL::SSL::VERIFY_NONE)
|
96
|
+
end
|
97
|
+
|
95
98
|
#log "HttpConnection request: #{http_connection_data.inspect}"
|
96
99
|
|
97
100
|
# Make a request:
|
@@ -47,8 +47,8 @@ module RightScale
|
|
47
47
|
connection_proxy_class = RightScale::CloudApi::ConnectionProxy::NetHttpPersistentProxy
|
48
48
|
end
|
49
49
|
@connection_proxy = connection_proxy_class.new
|
50
|
-
end
|
51
|
-
|
50
|
+
end
|
51
|
+
|
52
52
|
# Register a call back to close current connection
|
53
53
|
data[:callbacks][:close_current_connection] = Proc::new do |reason|
|
54
54
|
@connection_proxy.close_connection(nil, reason)
|
@@ -60,7 +60,7 @@ module RightScale
|
|
60
60
|
@connection_proxy.request(data)
|
61
61
|
end
|
62
62
|
end
|
63
|
-
|
63
|
+
|
64
64
|
end
|
65
65
|
end
|
66
66
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: right_cloud_api_base
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- RightScale, Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-01-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: json
|
@@ -193,23 +193,23 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
193
193
|
version: '0'
|
194
194
|
requirements: []
|
195
195
|
rubyforge_project:
|
196
|
-
rubygems_version: 2.
|
196
|
+
rubygems_version: 2.4.3
|
197
197
|
signing_key:
|
198
198
|
specification_version: 4
|
199
199
|
summary: The gem provides base Query and REST API management functionalities for Amazon,
|
200
200
|
OpenStack, Rackspace, CloudStack, etc cloud services
|
201
201
|
test_files:
|
202
|
+
- spec/spec_helper.rb
|
202
203
|
- spec/routines/test_connection_proxy_spec.rb
|
203
|
-
- spec/routines/
|
204
|
-
- spec/routines/
|
204
|
+
- spec/routines/test_retry_manager_spec.rb
|
205
|
+
- spec/routines/test_response_parser_spec.rb
|
205
206
|
- spec/routines/connection_proxies/test_net_http_persistent_proxy_spec.rb
|
207
|
+
- spec/routines/test_cache_validator_spec.rb
|
206
208
|
- spec/routines/test_response_analyzer_spec.rb
|
207
209
|
- spec/routines/test_request_analyzer_spec.rb
|
208
|
-
- spec/routines/
|
209
|
-
- spec/routines/test_retry_manager_spec.rb
|
210
|
-
- spec/spec_helper.rb
|
211
|
-
- spec/helpers/query_api_pattern_spec.rb
|
210
|
+
- spec/routines/test_result_wrapper_spec.rb
|
212
211
|
- spec/helpers/support_xml_spec.rb
|
213
212
|
- spec/helpers/support_spec.rb
|
214
213
|
- spec/helpers/utils_spec.rb
|
214
|
+
- spec/helpers/query_api_pattern_spec.rb
|
215
215
|
has_rdoc:
|