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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8f33d6964b838a9bb131d41d06239ff7dbeb2c4f
4
- data.tar.gz: c099b7d9948e970eb00035cf9888d53973238d10
3
+ metadata.gz: 93876f7645c63a599b1cdde9e5752490efcb6b9e
4
+ data.tar.gz: cf0a599994e10d1c17c2cf014e03ab303a18deed
5
5
  SHA512:
6
- metadata.gz: e2a7cbfac9fef859a80fef19ed779085762db032ee19475ea74f5d33de230d5905bd157be7d5804fc833a011b249766ca62327c5777a7d8c75b1e089a5a18b97
7
- data.tar.gz: 04a958fe2e8f940286a3fb0c0dfa2712393ad3bc61aeff17c7998553dd4ece5abc734131e360a0fe2cec375b2b9edcd6734882a2a5fcaf16ba25b4fef0868354
6
+ metadata.gz: 76b486fbb4c8392dff74a77e953c051b7eabeb8a2e723f795d0efb1bf66c3cebbcb6c543bc36e4674e41adee214e8bac4955fda97cd69b8a79e11ad7a4752e9c
7
+ data.tar.gz: ba3b90502877bbe162ceae9bfba958bb4682cc5ee2083fc1cd71c36b435c1ca8bb078cd522861683fb91fa30d3bf82855758936e803c2b04275e654a06e8b44a
data/HISTORY CHANGED
@@ -1,2 +1,6 @@
1
1
  == 2013-06-28
2
- - pre-release candidate created
2
+ - pre-release candidate created
3
+
4
+ == 2014-11-03
5
+ - Fix object._blank? issue for IO objects.
6
+ - version bumped to 0.1.1
@@ -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._blank?
120
- @body = nil
121
- self['content-length'] = 0
122
- else
123
- if new_body.is_a?(IO)
124
- @body = file = new_body
125
- # Make sure the file is openned in binmode
126
- file.binmode if file.respond_to?(:binmode)
127
- # Fix 'content-length': it must not be bigger than a piece of a File left to be read or a String body size.
128
- # Otherwise the connection may behave like crazy causing 4xx or 5xx responses
129
- # KD: Make sure this code is used with the patched RightHttpConnection gem (see net_fix.rb)
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.size}, first #{BODY_BYTES_TO_LOG} bytes:\n#{body.to_s[0...BODY_BYTES_TO_LOG]}"
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
@@ -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("Can't transform body from Hash into #{content_type.inspect} type String")
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 = data
55
+ # Initialize things:
56
+ @data = data
56
57
  @data[:response] = {}
57
- uri = @data[:connection][:uri]
58
-
59
- # Create a connection
60
- connection = Net::HTTP::Persistent.new('right_cloud_api_gem')
61
-
62
- # Create a fake HTTP request
63
- fake = @data[:request][:instance]
64
- http_request = "Net::HTTP::#{fake.verb._camelize}"._constantize::new(fake.path)
65
- if fake.is_io?
66
- http_request.body_stream = fake.body
67
- else
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
- fake.headers.each{|header, value| http_request[header] = value }
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
- # Set all required options
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
- # Make a request
90
- begin
91
- make_request_with_retries(connection, uri, http_request)
92
- rescue => e
93
- connection.shutdown
94
- fail(ConnectionError, e.message)
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
@@ -31,7 +31,7 @@ module RightScale
31
31
  # CloudApi gem version namespace
32
32
  module VERSION
33
33
  # The gem version
34
- STRING = '0.1.0'
34
+ STRING = '0.2.1'
35
35
  end
36
36
  end
37
- end
37
+ 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.0
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: 2014-06-19 00:00:00.000000000 Z
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.1.11
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/test_result_wrapper_spec.rb
204
- - spec/routines/test_cache_validator_spec.rb
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/test_response_parser_spec.rb
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: