better-riak-client 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/LICENSE +16 -0
  2. data/README.markdown +198 -0
  3. data/RELEASE_NOTES.md +211 -0
  4. data/better-riak-client.gemspec +61 -0
  5. data/erl_src/riak_kv_test014_backend.beam +0 -0
  6. data/erl_src/riak_kv_test014_backend.erl +189 -0
  7. data/erl_src/riak_kv_test_backend.beam +0 -0
  8. data/erl_src/riak_kv_test_backend.erl +697 -0
  9. data/erl_src/riak_search_test_backend.beam +0 -0
  10. data/erl_src/riak_search_test_backend.erl +175 -0
  11. data/lib/riak/bucket.rb +221 -0
  12. data/lib/riak/client/beefcake/messages.rb +213 -0
  13. data/lib/riak/client/beefcake/object_methods.rb +111 -0
  14. data/lib/riak/client/beefcake_protobuffs_backend.rb +226 -0
  15. data/lib/riak/client/decaying.rb +36 -0
  16. data/lib/riak/client/excon_backend.rb +162 -0
  17. data/lib/riak/client/feature_detection.rb +88 -0
  18. data/lib/riak/client/http_backend/configuration.rb +211 -0
  19. data/lib/riak/client/http_backend/key_streamer.rb +43 -0
  20. data/lib/riak/client/http_backend/object_methods.rb +106 -0
  21. data/lib/riak/client/http_backend/request_headers.rb +34 -0
  22. data/lib/riak/client/http_backend/transport_methods.rb +201 -0
  23. data/lib/riak/client/http_backend.rb +340 -0
  24. data/lib/riak/client/net_http_backend.rb +82 -0
  25. data/lib/riak/client/node.rb +115 -0
  26. data/lib/riak/client/protobuffs_backend.rb +173 -0
  27. data/lib/riak/client/search.rb +91 -0
  28. data/lib/riak/client.rb +540 -0
  29. data/lib/riak/cluster.rb +151 -0
  30. data/lib/riak/core_ext/blank.rb +53 -0
  31. data/lib/riak/core_ext/deep_dup.rb +13 -0
  32. data/lib/riak/core_ext/extract_options.rb +7 -0
  33. data/lib/riak/core_ext/json.rb +15 -0
  34. data/lib/riak/core_ext/slice.rb +18 -0
  35. data/lib/riak/core_ext/stringify_keys.rb +10 -0
  36. data/lib/riak/core_ext/symbolize_keys.rb +10 -0
  37. data/lib/riak/core_ext/to_param.rb +31 -0
  38. data/lib/riak/core_ext.rb +7 -0
  39. data/lib/riak/encoding.rb +6 -0
  40. data/lib/riak/failed_request.rb +81 -0
  41. data/lib/riak/i18n.rb +5 -0
  42. data/lib/riak/json.rb +52 -0
  43. data/lib/riak/link.rb +94 -0
  44. data/lib/riak/locale/en.yml +53 -0
  45. data/lib/riak/locale/fr.yml +52 -0
  46. data/lib/riak/map_reduce/filter_builder.rb +103 -0
  47. data/lib/riak/map_reduce/phase.rb +98 -0
  48. data/lib/riak/map_reduce.rb +225 -0
  49. data/lib/riak/map_reduce_error.rb +7 -0
  50. data/lib/riak/node/configuration.rb +293 -0
  51. data/lib/riak/node/console.rb +133 -0
  52. data/lib/riak/node/control.rb +207 -0
  53. data/lib/riak/node/defaults.rb +83 -0
  54. data/lib/riak/node/generation.rb +106 -0
  55. data/lib/riak/node/log.rb +34 -0
  56. data/lib/riak/node/version.rb +43 -0
  57. data/lib/riak/node.rb +38 -0
  58. data/lib/riak/robject.rb +318 -0
  59. data/lib/riak/search.rb +3 -0
  60. data/lib/riak/serializers.rb +74 -0
  61. data/lib/riak/stamp.rb +77 -0
  62. data/lib/riak/test_server.rb +89 -0
  63. data/lib/riak/util/escape.rb +76 -0
  64. data/lib/riak/util/headers.rb +53 -0
  65. data/lib/riak/util/multipart/stream_parser.rb +62 -0
  66. data/lib/riak/util/multipart.rb +52 -0
  67. data/lib/riak/util/tcp_socket_extensions.rb +58 -0
  68. data/lib/riak/util/translation.rb +19 -0
  69. data/lib/riak/version.rb +3 -0
  70. data/lib/riak/walk_spec.rb +105 -0
  71. data/lib/riak.rb +21 -0
  72. metadata +348 -0
@@ -0,0 +1,111 @@
1
+ require 'riak/robject'
2
+ require 'riak/link'
3
+ require 'riak/client/beefcake/messages'
4
+
5
+ module Riak
6
+ class Client
7
+ class BeefcakeProtobuffsBackend
8
+ module ObjectMethods
9
+ ENCODING = "Riak".respond_to?(:encoding)
10
+
11
+ # Returns RpbPutReq
12
+ def dump_object(robject, options={})
13
+ pbuf = RpbPutReq.new(options.merge(:bucket => maybe_encode(robject.bucket.name)))
14
+ pbuf.key = maybe_encode(robject.key) if robject.key # Put w/o key supported!
15
+ pbuf.vclock = maybe_encode(Base64.decode64(robject.vclock)) if robject.vclock
16
+ pbuf.content = RpbContent.new(:value => maybe_encode(robject.raw_data),
17
+ :content_type => maybe_encode(robject.content_type),
18
+ :links => robject.links.map {|l| encode_link(l) }.compact,
19
+ :indexes => robject.indexes.map {|k,s| encode_index(k,s) }.flatten)
20
+
21
+ pbuf.content.usermeta = robject.meta.map {|k,v| encode_meta(k,v)} if robject.meta.any?
22
+ pbuf.content.vtag = maybe_encode(robject.etag) if robject.etag.present?
23
+ if ENCODING # 1.9 support
24
+ pbuf.content.charset = maybe_encode(robject.raw_data.encoding.name)
25
+ end
26
+ pbuf
27
+ end
28
+
29
+ # Returns RObject
30
+ def load_object(pbuf, robject)
31
+ return robject if pbuf.respond_to?(:unchanged) && pbuf.unchanged # Reloading
32
+ robject.vclock = Base64.encode64(pbuf.vclock).chomp if pbuf.vclock
33
+ robject.key = maybe_unescape(pbuf.key) if pbuf.respond_to?(:key) && pbuf.key # Put w/o key
34
+ if pbuf.content.size > 1
35
+ robject.conflict = true
36
+ robject.siblings = pbuf.content.map do |c|
37
+ sibling = RObject.new(robject.bucket, robject.key)
38
+ sibling.vclock = robject.vclock
39
+ load_content(c, sibling)
40
+ end
41
+
42
+ return robject.attempt_conflict_resolution
43
+ else
44
+ load_content(pbuf.content.first, robject)
45
+ end
46
+ robject
47
+ end
48
+
49
+ private
50
+ def load_content(pbuf, robject)
51
+ if ENCODING && pbuf.charset.present?
52
+ pbuf.value.force_encoding(pbuf.charset) if Encoding.find(pbuf.charset)
53
+ end
54
+ robject.raw_data = pbuf.value
55
+ robject.etag = pbuf.vtag if pbuf.vtag.present?
56
+ robject.content_type = pbuf.content_type if pbuf.content_type.present?
57
+ robject.links = pbuf.links.map(&method(:decode_link)) if pbuf.links.present?
58
+ pbuf.usermeta.each {|pair| decode_meta(pair, robject.meta) } if pbuf.usermeta.present?
59
+ if pbuf.indexes.present?
60
+ robject.indexes.clear
61
+ pbuf.indexes.each {|pair| decode_index(pair, robject.indexes) }
62
+ end
63
+ if pbuf.last_mod.present?
64
+ robject.last_modified = Time.at(pbuf.last_mod)
65
+ robject.last_modified += pbuf.last_mod_usecs / 1000000 if pbuf.last_mod_usecs.present?
66
+ end
67
+ robject
68
+ end
69
+
70
+ def decode_link(pbuf)
71
+ Riak::Link.new(pbuf.bucket, pbuf.key, pbuf.tag)
72
+ end
73
+
74
+ def encode_link(link)
75
+ return nil unless link.key.present?
76
+ RpbLink.new(:bucket => maybe_encode(link.bucket.to_s),
77
+ :key => maybe_encode(link.key.to_s),
78
+ :tag => maybe_encode(link.tag.to_s))
79
+ end
80
+
81
+ def decode_meta(pbuf, hash)
82
+ hash[pbuf.key] = pbuf.value
83
+ end
84
+
85
+ def encode_meta(key,value)
86
+ return nil unless value.present?
87
+ RpbPair.new(:key => maybe_encode(key.to_s),
88
+ :value => maybe_encode(value.to_s))
89
+ end
90
+
91
+ def decode_index(pbuf, hash)
92
+ value = pbuf.key =~ /int$/ ? pbuf.value.to_i : pbuf.value
93
+ hash[pbuf.key] << value
94
+ end
95
+
96
+ def encode_index(key, set)
97
+ set.map do |v|
98
+ RpbPair.new(:key => maybe_encode(key),
99
+ :value => maybe_encode(v.to_s))
100
+ end
101
+ end
102
+
103
+ def maybe_encode(string)
104
+ ENCODING ? string.dup.force_encoding('BINARY') : string
105
+ end
106
+ end
107
+
108
+ include ObjectMethods
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,226 @@
1
+ require 'base64'
2
+ require 'riak/json'
3
+ require 'riak/client'
4
+ require 'riak/failed_request'
5
+ require 'riak/client/protobuffs_backend'
6
+
7
+ module Riak
8
+ class Client
9
+ class BeefcakeProtobuffsBackend < ProtobuffsBackend
10
+ def self.configured?
11
+ begin
12
+ require 'beefcake'
13
+ require 'riak/client/beefcake/messages'
14
+ require 'riak/client/beefcake/object_methods'
15
+ true
16
+ rescue LoadError, NameError
17
+ false
18
+ end
19
+ end
20
+
21
+ def set_client_id(id)
22
+ value = case id
23
+ when Integer
24
+ [id].pack("N")
25
+ else
26
+ id.to_s
27
+ end
28
+ req = RpbSetClientIdReq.new(:client_id => value)
29
+ write_protobuff(:SetClientIdReq, req)
30
+ decode_response
31
+ end
32
+
33
+ def fetch_object(bucket, key, options={})
34
+ options = prune_unsupported_options(:GetReq, normalize_quorums(options))
35
+ bucket = Bucket === bucket ? bucket.name : bucket
36
+ req = RpbGetReq.new(options.merge(:bucket => maybe_encode(bucket), :key => maybe_encode(key)))
37
+ write_protobuff(:GetReq, req)
38
+ decode_response(RObject.new(client.bucket(bucket), key))
39
+ end
40
+
41
+ def reload_object(robject, options={})
42
+ options = normalize_quorums(options)
43
+ options[:bucket] = maybe_encode(robject.bucket.name)
44
+ options[:key] = maybe_encode(robject.key)
45
+ options[:if_modified] = maybe_encode Base64.decode64(robject.vclock) if robject.vclock
46
+ req = RpbGetReq.new(prune_unsupported_options(:GetReq, options))
47
+ write_protobuff(:GetReq, req)
48
+ decode_response(robject)
49
+ end
50
+
51
+ def store_object(robject, options={})
52
+ options = normalize_quorums(options)
53
+ if robject.prevent_stale_writes
54
+ unless pb_conditionals?
55
+ other = fetch_object(robject.bucket, robject.key)
56
+ raise Riak::ProtobuffsFailedRequest.new(:stale_object, t("stale_write_prevented")) unless other.vclock == robject.vclock
57
+ end
58
+ if robject.vclock
59
+ options[:if_not_modified] = true
60
+ else
61
+ options[:if_none_match] = true
62
+ end
63
+ end
64
+ req = dump_object(robject, prune_unsupported_options(:PutReq, options))
65
+ write_protobuff(:PutReq, req)
66
+ decode_response(robject)
67
+ end
68
+
69
+ def delete_object(bucket, key, options={})
70
+ bucket = Bucket === bucket ? bucket.name : bucket
71
+ options = normalize_quorums(options)
72
+ options[:bucket] = maybe_encode(bucket)
73
+ options[:key] = maybe_encode(key)
74
+ options[:vclock] = Base64.decode64(options[:vclock]) if options[:vclock]
75
+ req = RpbDelReq.new(prune_unsupported_options(:DelReq, options))
76
+ write_protobuff(:DelReq, req)
77
+ decode_response
78
+ end
79
+
80
+ def get_bucket_props(bucket)
81
+ bucket = bucket.name if Bucket === bucket
82
+ req = RpbGetBucketReq.new(:bucket => maybe_encode(bucket))
83
+ write_protobuff(:GetBucketReq, req)
84
+ decode_response
85
+ end
86
+
87
+ def set_bucket_props(bucket, props)
88
+ bucket = bucket.name if Bucket === bucket
89
+ props = props.slice('n_val', 'allow_mult')
90
+ req = RpbSetBucketReq.new(:bucket => maybe_encode(bucket), :props => RpbBucketProps.new(props))
91
+ write_protobuff(:SetBucketReq, req)
92
+ decode_response
93
+ end
94
+
95
+ def list_keys(bucket, &block)
96
+ bucket = bucket.name if Bucket === bucket
97
+ req = RpbListKeysReq.new(:bucket => maybe_encode(bucket))
98
+ write_protobuff(:ListKeysReq, req)
99
+ keys = []
100
+ while msg = decode_response
101
+ break if msg.done
102
+ if block_given?
103
+ yield msg.keys
104
+ else
105
+ keys += msg.keys
106
+ end
107
+ end
108
+ block_given? || keys
109
+ end
110
+
111
+ def mapred(mr, &block)
112
+ raise MapReduceError.new(t("empty_map_reduce_query")) if mr.query.empty? && !mapred_phaseless?
113
+ req = RpbMapRedReq.new(:request => mr.to_json, :content_type => "application/json")
114
+ write_protobuff(:MapRedReq, req)
115
+ results = []
116
+ while msg = decode_response
117
+ break if msg.done
118
+ if block_given?
119
+ yield msg.phase, JSON.parse(msg.response)
120
+ else
121
+ results[msg.phase] ||= []
122
+ results[msg.phase] += JSON.parse(msg.response)
123
+ end
124
+ end
125
+ block_given? || results.compact.size == 1 ? results.last : results
126
+ end
127
+
128
+ def get_index(bucket, index, query)
129
+ return super unless pb_indexes?
130
+ if Range === query
131
+ options = {
132
+ :qtype => RpbIndexReq::IndexQueryType::RANGE,
133
+ :range_min => query.begin.to_s,
134
+ :range_max => query.end.to_s
135
+ }
136
+ else
137
+ options = {
138
+ :qtype => RpbIndexReq::IndexQueryType::EQ,
139
+ :key => query.to_s
140
+ }
141
+ end
142
+ req = RpbIndexReq.new(options.merge(:bucket => bucket, :index => index))
143
+ write_protobuff(:IndexReq, req)
144
+ decode_response
145
+ end
146
+
147
+ def search(index, query, options={})
148
+ return super unless pb_search?
149
+ options = options.symbolize_keys
150
+ options[:op] = options.delete(:'q.op') if options[:'q.op']
151
+ req = RpbSearchQueryReq.new(options.merge(:index => index || 'search', :q => query))
152
+ write_protobuff(:SearchQueryReq, req)
153
+ decode_response
154
+ end
155
+
156
+ private
157
+ def write_protobuff(code, message)
158
+ encoded = message.encode
159
+ header = [encoded.length+1, MESSAGE_CODES.index(code)].pack("NC")
160
+ socket.write(header + encoded)
161
+ end
162
+
163
+ def decode_response(*args)
164
+ header = socket.read(5)
165
+ raise SocketError, "Unexpected EOF on PBC socket" if header.nil?
166
+ msglen, msgcode = header.unpack("NC")
167
+ if msglen == 1
168
+ case MESSAGE_CODES[msgcode]
169
+ when :PingResp, :SetClientIdResp, :PutResp, :DelResp, :SetBucketResp
170
+ true
171
+ when :ListBucketsResp, :ListKeysResp
172
+ []
173
+ when :GetResp
174
+ raise Riak::ProtobuffsFailedRequest.new(:not_found, t('not_found'))
175
+ else
176
+ false
177
+ end
178
+ else
179
+ message = socket.read(msglen-1)
180
+ case MESSAGE_CODES[msgcode]
181
+ when :ErrorResp
182
+ res = RpbErrorResp.decode(message)
183
+ raise Riak::ProtobuffsFailedRequest.new(res.errcode, res.errmsg)
184
+ when :GetClientIdResp
185
+ res = RpbGetClientIdResp.decode(message)
186
+ res.client_id
187
+ when :GetServerInfoResp
188
+ res = RpbGetServerInfoResp.decode(message)
189
+ {:node => res.node, :server_version => res.server_version}
190
+ when :GetResp
191
+ res = RpbGetResp.decode(message)
192
+ load_object(res, args.first)
193
+ when :PutResp
194
+ res = RpbPutResp.decode(message)
195
+ load_object(res, args.first)
196
+ when :ListBucketsResp
197
+ res = RpbListBucketsResp.decode(message)
198
+ res.buckets
199
+ when :ListKeysResp
200
+ RpbListKeysResp.decode(message)
201
+ when :GetBucketResp
202
+ res = RpbGetBucketResp.decode(message)
203
+ {'n_val' => res.props.n_val, 'allow_mult' => res.props.allow_mult}
204
+ when :MapRedResp
205
+ RpbMapRedResp.decode(message)
206
+ when :IndexResp
207
+ res = RpbIndexResp.decode(message)
208
+ res.keys
209
+ when :SearchQueryResp
210
+ res = RpbSearchQueryResp.decode(message)
211
+ { 'docs' => res.docs.map {|d| decode_doc(d) },
212
+ 'max_score' => res.max_score,
213
+ 'num_found' => res.num_found }
214
+ end
215
+ end
216
+ rescue SystemCallError, SocketError => e
217
+ reset_socket
218
+ raise
219
+ end
220
+
221
+ def decode_doc(doc)
222
+ Hash[doc.properties.map {|p| [ p.key, p.value ] }]
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,36 @@
1
+ module Riak
2
+ class Client
3
+ # A float value which decays exponentially toward 0 over time.
4
+ # @private
5
+ class Decaying
6
+ attr_accessor :e
7
+ attr_accessor :p
8
+
9
+ # @param [Hash] opts options
10
+ # @option options [Float] :p (0.0) The initial value
11
+ # @option options [Float] :e (Math::E) Exponent base
12
+ # @option options [Float] :r (Math.log(0.5) / 10) Timescale
13
+ # factor - defaulting to decay 50% every 10 seconds
14
+ def initialize(opts = {})
15
+ @p = opts[:p] || 0.0
16
+ @e = opts[:e] || Math::E
17
+ @r = opts[:r] || Math.log(0.5) / 10
18
+ @t0 = Time.now
19
+ end
20
+
21
+ # Add to current value.
22
+ # @param [Float] d the value to add
23
+ def <<(d)
24
+ @p = value + d
25
+ end
26
+
27
+ # @return [Float] the current value (adjusted for the time decay)
28
+ def value
29
+ now = Time.now
30
+ dt = now - @t0
31
+ @t0 = now
32
+ @p = @p * (@e ** (@r * dt))
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,162 @@
1
+ require 'riak/failed_request'
2
+ require 'riak/client/http_backend'
3
+ require 'riak/client/http_backend/request_headers'
4
+
5
+ module Riak
6
+ class Client
7
+ # An HTTP backend for Riak::Client that uses Wesley Beary's Excon
8
+ # HTTP library. Conforms to the Riak::Client::HTTPBackend
9
+ # interface.
10
+ class ExconBackend < HTTPBackend
11
+ def self.configured?
12
+ begin
13
+ require 'excon'
14
+ minimum_version?("0.5.7") && register_exceptions && handle_deprecations && patch_sockets
15
+ rescue LoadError
16
+ false
17
+ end
18
+ end
19
+
20
+ # Adds Excon's relevant internal exceptions to the rescuable
21
+ # network-related errors.
22
+ def self.register_exceptions
23
+ unless Client::NETWORK_ERRORS.include?(Excon::Errors::SocketError)
24
+ Client::NETWORK_ERRORS << Excon::Errors::SocketError
25
+ Client::NETWORK_ERRORS << Excon::Errors::Timeout if defined? Excon::Errors::Timeout
26
+ end
27
+ true
28
+ end
29
+
30
+ # Adjusts Excon's connection collection to allow multiple
31
+ # connections to the same host from the same Thread. Instead we
32
+ # use the Riak::Client::Pool to segregate connections.
33
+ # @note This can be changed when Excon has a proper pool of its own.
34
+ def self.patch_sockets
35
+ unless defined? @@patched
36
+ ::Excon::Connection.class_eval do
37
+ def sockets
38
+ @sockets ||= {}
39
+ end
40
+ end
41
+ end
42
+ @@patched = true
43
+ end
44
+
45
+ # Defines instance methods that handle changes in the Excon API
46
+ # across different versions.
47
+ def self.handle_deprecations
48
+ # Define #make_request
49
+ unless method_defined?(:make_request)
50
+ if minimum_version?("0.10.2")
51
+ def make_request(params, block)
52
+ params[:response_block] = block if block
53
+ connection.request(params)
54
+ end
55
+ else
56
+ def make_request(params, block)
57
+ response = connection.request(params, &block)
58
+ end
59
+ end
60
+ private :make_request
61
+ end
62
+
63
+ # Define #configure_ssl
64
+ unless method_defined?(:configure_ssl)
65
+ if minimum_version?("0.9.6")
66
+ def configure_ssl
67
+ Excon.defaults[:ssl_verify_peer] = (@node.ssl_options[:verify_mode].to_s === "peer")
68
+ Excon.defaults[:ssl_ca_path] = @node.ssl_options[:ca_path] if @node.ssl_options[:ca_path]
69
+ end
70
+ else
71
+ def configure_ssl
72
+ Excon.ssl_verify_peer = (@node.ssl_options[:verify_mode].to_s === "peer")
73
+ Excon.ssl_ca_path = @node.ssl_options[:ca_path] if @node.ssl_options[:ca_path]
74
+ end
75
+ end
76
+ private :configure_ssl
77
+ end
78
+ true
79
+ end
80
+
81
+ # Returns true if the Excon library is at least the given
82
+ # version. This is used inside the backend to check how to
83
+ # provide certain request and configuration options.
84
+ def self.minimum_version?(version)
85
+ Gem::Version.new(Excon::VERSION) >= Gem::Version.new(version)
86
+ end
87
+
88
+ # Sets the connect timeout applied to the Excon connection
89
+ # Increase this if you have very long request times.
90
+ def self.connect_timeout=(timeout)
91
+ @connect_timeout = timeout
92
+ end
93
+
94
+ def self.connect_timeout
95
+ @connect_timeout ||= 4096
96
+ end
97
+
98
+ # Sets the read_timeout applied to the Excon connection
99
+ # Increase this if you have very long request times.
100
+ def self.read_timeout=(timeout)
101
+ @read_timeout = timeout
102
+ end
103
+
104
+ def self.read_timeout
105
+ @read_timeout ||= 4096
106
+ end
107
+
108
+ # Sets the write_timeout applied to the Excon connection
109
+ # Increase this if you have very long request times.
110
+ def self.write_timeout=(timeout)
111
+ @write_timeout = timeout
112
+ end
113
+
114
+ def self.write_timeout
115
+ @write_timeout ||= 4096
116
+ end
117
+
118
+ def teardown
119
+ connection.reset
120
+ end
121
+
122
+ private
123
+ def perform(method, uri, headers, expect, data=nil, &block)
124
+ configure_ssl if @node.ssl_enabled?
125
+
126
+ params = {
127
+ :method => method.to_s.upcase,
128
+ :headers => RequestHeaders.new(headers).to_hash,
129
+ :path => uri.path
130
+ }
131
+ params[:query] = uri.query if uri.query
132
+ params[:body] = data if [:put,:post].include?(method)
133
+ params[:idempotent] = (method != :post)
134
+
135
+ # Later versions of Excon pass multiple arguments to the block
136
+ block = lambda {|*args| yield args.first } if block_given?
137
+
138
+ response = make_request(params, block)
139
+ response_headers.initialize_http_header(response.headers)
140
+
141
+ if valid_response?(expect, response.status)
142
+ result = {:headers => response_headers.to_hash, :code => response.status}
143
+ if return_body?(method, response.status, block_given?)
144
+ result[:body] = response.body
145
+ end
146
+ result
147
+ else
148
+ raise HTTPFailedRequest.new(method, expect, response.status, response_headers.to_hash, response.body)
149
+ end
150
+ end
151
+
152
+ def connection
153
+ @connection ||= Excon::Connection.new(
154
+ root_uri.to_s,
155
+ :read_timeout => self.class.read_timeout,
156
+ :write_timeout => self.class.write_timeout,
157
+ :connect_timeout => self.class.connect_timeout
158
+ )
159
+ end
160
+ end
161
+ end
162
+ end