cloudfiles 1.4.18 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +6 -0
- data/CONTRIBUTORS +1 -0
- data/cloudfiles.gemspec +5 -3
- data/lib/client.rb +620 -0
- data/lib/cloudfiles.rb +1 -1
- data/lib/cloudfiles/authentication.rb +10 -25
- data/lib/cloudfiles/connection.rb +55 -123
- data/lib/cloudfiles/container.rb +89 -73
- data/lib/cloudfiles/storage_object.rb +52 -36
- data/lib/cloudfiles/version.rb +1 -1
- data/test/cloudfiles_authentication_test.rb +17 -22
- data/test/cloudfiles_client_test.rb +797 -0
- data/test/cloudfiles_connection_test.rb +53 -194
- data/test/cloudfiles_container_test.rb +253 -121
- data/test/cloudfiles_storage_object_test.rb +51 -65
- data/test/test_helper.rb +1 -0
- metadata +12 -10
data/CHANGELOG
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
================================================================================
|
2
|
+
1.5.0 (2011/10/31
|
3
|
+
================================================================================
|
4
|
+
o The underlying http wrapper now uses client.rb a simple abstraction to manage
|
5
|
+
each ReST call in its own function
|
6
|
+
|
1
7
|
================================================================================
|
2
8
|
1.4.18 (2011/09/07)
|
3
9
|
================================================================================
|
data/CONTRIBUTORS
CHANGED
data/cloudfiles.gemspec
CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
"TODO",
|
24
24
|
"cloudfiles.gemspec",
|
25
25
|
"lib/cloudfiles.rb",
|
26
|
+
"lib/client.rb",
|
26
27
|
"lib/cloudfiles/authentication.rb",
|
27
28
|
"lib/cloudfiles/connection.rb",
|
28
29
|
"lib/cloudfiles/container.rb",
|
@@ -34,6 +35,7 @@ Gem::Specification.new do |s|
|
|
34
35
|
"test/cloudfiles_connection_test.rb",
|
35
36
|
"test/cloudfiles_container_test.rb",
|
36
37
|
"test/cloudfiles_storage_object_test.rb",
|
38
|
+
"test/cloudfiles_client_test.rb",
|
37
39
|
"test/test_helper.rb"
|
38
40
|
]
|
39
41
|
s.homepage = %q{http://www.rackspacecloud.com/cloud_hosting_products/files}
|
@@ -47,21 +49,21 @@ Gem::Specification.new do |s|
|
|
47
49
|
"test/cloudfiles_connection_test.rb",
|
48
50
|
"test/cloudfiles_container_test.rb",
|
49
51
|
"test/cloudfiles_storage_object_test.rb",
|
52
|
+
"test/cloudfiles_client_test.rb",
|
50
53
|
"test/test_helper.rb"
|
51
54
|
]
|
52
55
|
|
56
|
+
s.add_dependency('json')
|
57
|
+
|
53
58
|
if s.respond_to? :specification_version then
|
54
59
|
s.specification_version = 3
|
55
60
|
|
56
61
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
57
|
-
s.add_runtime_dependency(%q<mime-types>, [">= 1.16"])
|
58
62
|
s.add_development_dependency(%q<mocha>, ["~> 0.9.8"])
|
59
63
|
else
|
60
|
-
s.add_dependency(%q<mime-types>, [">= 1.16"])
|
61
64
|
s.add_dependency(%q<mocha>, ["~> 0.9.8"])
|
62
65
|
end
|
63
66
|
else
|
64
|
-
s.add_dependency(%q<mime-types>, [">= 1.16"])
|
65
67
|
s.add_dependency(%q<mocha>, ["~> 0.9.8"])
|
66
68
|
end
|
67
69
|
end
|
data/lib/client.rb
ADDED
@@ -0,0 +1,620 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'uri'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
class ClientException < StandardError
|
6
|
+
attr_reader :scheme, :host, :port, :path, :query, :status, :reason, :devices
|
7
|
+
def initialize(msg, params={})
|
8
|
+
@msg = msg
|
9
|
+
@scheme = params[:http_scheme]
|
10
|
+
@host = params[:http_host]
|
11
|
+
@port = params[:http_port]
|
12
|
+
@path = params[:http_path]
|
13
|
+
@query = params[:http_query]
|
14
|
+
@status = params[:http_status]
|
15
|
+
@reason = params[:http_reason]
|
16
|
+
@device = params[:http_device]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
a = @msg
|
21
|
+
b = ''
|
22
|
+
b += "#{@scheme}://" if @scheme
|
23
|
+
b += @host if @host
|
24
|
+
b += ":#{@port}" if @port
|
25
|
+
b += @path if @path
|
26
|
+
b += "?#{@query}" if @query
|
27
|
+
b ? b = "#{b} #{@status}" : b = @status.to_s if @status
|
28
|
+
b ? b = "#{b} #{@reason}" : b = "- #{@reason}" if @reason
|
29
|
+
b ? b = "#{b}: device #{@device}" : b = "device #{@device}" if @device
|
30
|
+
b ? "#{a} #{b}" : a
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ChunkedConnectionWrapper
|
35
|
+
def initialize(data, chunk_size)
|
36
|
+
@size = chunk_size
|
37
|
+
if data.respond_to? :read
|
38
|
+
@file = data
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def read(foo)
|
43
|
+
if @file
|
44
|
+
@file.read(@size)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
def eof!
|
48
|
+
@file.eof!
|
49
|
+
end
|
50
|
+
def eof?
|
51
|
+
@file.eof?
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def quote(value)
|
56
|
+
URI.encode(value)
|
57
|
+
end
|
58
|
+
|
59
|
+
class Query
|
60
|
+
def initialize(url_params)
|
61
|
+
if url_params
|
62
|
+
@params = Query.from_url_params(url_params)
|
63
|
+
else
|
64
|
+
@params = {}
|
65
|
+
end
|
66
|
+
end
|
67
|
+
def to_s
|
68
|
+
to_url_params
|
69
|
+
end
|
70
|
+
def to_url_params
|
71
|
+
elements = []
|
72
|
+
@params.each_pair {|k,v| elements << "#{k}=#{v}"}
|
73
|
+
elements.join('&')
|
74
|
+
end
|
75
|
+
def self.from_url_params(url_params)
|
76
|
+
result = {}
|
77
|
+
url_params.split('&').each do |element|
|
78
|
+
element = element.split('=')
|
79
|
+
result[element[0]] = element[1]
|
80
|
+
end
|
81
|
+
result
|
82
|
+
end
|
83
|
+
def has_key?(key)
|
84
|
+
@params.has_key? key
|
85
|
+
end
|
86
|
+
def add(key, value)
|
87
|
+
@params[key] = value
|
88
|
+
end
|
89
|
+
def delete(key)
|
90
|
+
@params.delete(key)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class SwiftClient
|
95
|
+
def initialize(authurl, user, key, retries=5, preauthurl=nil, preauthtoken=nil, snet=false, starting_backoff=1)
|
96
|
+
@authurl = authurl
|
97
|
+
@user = user
|
98
|
+
@key = key
|
99
|
+
@retries = retries
|
100
|
+
@http_conn = nil
|
101
|
+
@url = preauthurl
|
102
|
+
@token = preauthtoken
|
103
|
+
@attempts = 0
|
104
|
+
@snet = snet
|
105
|
+
@starting_backoff = starting_backoff
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
def _retry(reset, func, args=nil)
|
110
|
+
@attempts = 0
|
111
|
+
backoff = @starting_backoff
|
112
|
+
|
113
|
+
while @attempts < @retries
|
114
|
+
@attempts += 1
|
115
|
+
begin
|
116
|
+
if !@url or !@token
|
117
|
+
@url, @token = self.get_auth()
|
118
|
+
@http_conn = nil
|
119
|
+
end
|
120
|
+
@http_conn = self.http_connection() if !@http_conn
|
121
|
+
return SwiftClient.method(func).call(@url, @token, *args)
|
122
|
+
rescue Net::HTTPExceptions
|
123
|
+
if @attempts > @retries
|
124
|
+
raise
|
125
|
+
end
|
126
|
+
@http_conn = nil
|
127
|
+
rescue ClientException => err
|
128
|
+
if @attempts > @retries
|
129
|
+
raise
|
130
|
+
end
|
131
|
+
if err.status.to_i == 401
|
132
|
+
@url = @token = nil
|
133
|
+
if @attempts > 1
|
134
|
+
raise
|
135
|
+
end
|
136
|
+
elsif err.status.to_i == 408
|
137
|
+
@http_conn = nil
|
138
|
+
elsif err.status.to_i >= 500 and err.status.to_i <= 599
|
139
|
+
return nil
|
140
|
+
else
|
141
|
+
raise
|
142
|
+
end
|
143
|
+
end
|
144
|
+
sleep(backoff)
|
145
|
+
backoff *= 2
|
146
|
+
if reset
|
147
|
+
reset.call(args)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
public
|
153
|
+
def self.http_connection(url, proxy_host=nil, proxy_port=nil)
|
154
|
+
parsed = URI::parse(url)
|
155
|
+
|
156
|
+
if parsed.scheme == 'http'
|
157
|
+
require 'net/http'
|
158
|
+
conn = Net::HTTP::Proxy(proxy_host, proxy_port).new(parsed.host, parsed.port)
|
159
|
+
[parsed, conn]
|
160
|
+
elsif parsed.scheme == 'https'
|
161
|
+
require 'net/https'
|
162
|
+
conn = Net::HTTP::Proxy(proxy_host, proxy_port).new(parsed.host, parsed.port)
|
163
|
+
conn.use_ssl = true
|
164
|
+
conn.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
165
|
+
[parsed, conn]
|
166
|
+
else
|
167
|
+
raise ClientException.new(
|
168
|
+
"Cannot handle protocol scheme #{parsed.scheme} for #{url} %s")
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def http_connection
|
173
|
+
if !@http_conn
|
174
|
+
@http_conn = SwiftClient.http_connection(@url)
|
175
|
+
else
|
176
|
+
@http_conn
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.get_auth(url, user, key, snet=false)
|
181
|
+
parsed, conn = http_connection(url)
|
182
|
+
conn.start if not conn.started?
|
183
|
+
resp = conn.get(URI.encode(parsed.request_uri), {"x-auth-user" => user, "x-auth-key" => key })
|
184
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
185
|
+
raise ClientException.new('Account GET failed', :http_scheme=>parsed.scheme,
|
186
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
187
|
+
:http_path=>parsed.path, :http_query=>parsed.query, :http_status=>resp.code,
|
188
|
+
:http_reason=>resp.message)
|
189
|
+
end
|
190
|
+
url = URI::parse(resp.header['x-storage-url'])
|
191
|
+
if snet
|
192
|
+
url.host = "snet-#{url.host}"
|
193
|
+
end
|
194
|
+
[url.to_s, resp.header['x-auth-token'], resp.header]
|
195
|
+
end
|
196
|
+
|
197
|
+
def get_auth
|
198
|
+
@url, @token = SwiftClient.get_auth(@authurl, @user, @key, @snet)
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.get_account(url, token, marker=nil, limit=nil, prefix=nil,
|
202
|
+
http_conn=nil, full_listing=false)
|
203
|
+
#todo: add in rest of functionality
|
204
|
+
if not http_conn
|
205
|
+
http_conn = http_connection(url)
|
206
|
+
end
|
207
|
+
parsed = http_conn[0].clone
|
208
|
+
conn = http_conn[1]
|
209
|
+
if full_listing
|
210
|
+
rv = get_account(url, token, marker, limit, prefix, http_conn)
|
211
|
+
listing = rv[1]
|
212
|
+
while listing.length > 0
|
213
|
+
marker = listing[-1]['name']
|
214
|
+
listing = get_account(url, token, marker, limit, prefix, http_conn)[1]
|
215
|
+
if listing.length > 0
|
216
|
+
rv[1] += listing
|
217
|
+
end
|
218
|
+
end
|
219
|
+
return rv
|
220
|
+
end
|
221
|
+
query = Query.new(parsed.query)
|
222
|
+
query.add('format', 'json')
|
223
|
+
query.add('marker', quote(marker.to_s)) if marker
|
224
|
+
query.add('limit', quote(limit.to_s)) if limit
|
225
|
+
query.add('prefix', quote(prefix.to_s)) if prefix
|
226
|
+
parsed.query = query.to_url_params
|
227
|
+
conn.start if !conn.started?
|
228
|
+
resp = conn.get(parsed.request_uri, {'x-auth-token' => token})
|
229
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
230
|
+
raise ClientException.new('Account GET failed', :http_scheme=>parsed.scheme,
|
231
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
232
|
+
:http_path=>parsed.path, :http_query=>parsed.query, :http_status=>resp.code,
|
233
|
+
:http_reason=>resp.message)
|
234
|
+
end
|
235
|
+
resp_headers = {}
|
236
|
+
resp.header.each do |k,v|
|
237
|
+
resp_headers[k.downcase] = v
|
238
|
+
end
|
239
|
+
if resp.code.to_i == 204
|
240
|
+
[resp_headers, []]
|
241
|
+
else
|
242
|
+
[resp_headers, JSON.parse(resp.body)]
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def get_account(marker=nil, limit=nil, prefix=nil, full_listing=false)
|
247
|
+
_retry(nil, :get_account, [marker, limit, prefix, @http_conn, full_listing])
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.head_account(url, token, http_conn=nil)
|
251
|
+
if not http_conn
|
252
|
+
http_conn = http_connection(url)
|
253
|
+
end
|
254
|
+
parsed = http_conn[0].clone
|
255
|
+
conn = http_conn[1]
|
256
|
+
conn.start if !conn.started?
|
257
|
+
resp = conn.head(parsed.request_uri, {'x-auth-token' => token})
|
258
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
259
|
+
raise ClientException.new('Account HEAD failed', :http_scheme=>parsed.scheme,
|
260
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
261
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
262
|
+
:http_reason=>resp.message)
|
263
|
+
end
|
264
|
+
resp_headers = {}
|
265
|
+
resp.header.each do |k,v|
|
266
|
+
resp_headers[k.downcase] = v
|
267
|
+
end
|
268
|
+
resp_headers
|
269
|
+
end
|
270
|
+
|
271
|
+
def head_account
|
272
|
+
_retry(nil, :head_account, [@http_conn])
|
273
|
+
end
|
274
|
+
|
275
|
+
def self.post_account(url, token, headers, http_conn=nil)
|
276
|
+
if not http_conn
|
277
|
+
http_conn = http_connection(url)
|
278
|
+
end
|
279
|
+
parsed = http_conn[0].clone
|
280
|
+
conn = http_conn[1]
|
281
|
+
headers['x-auth-token'] = token
|
282
|
+
conn.start if !conn.started?
|
283
|
+
resp = conn.post(parsed.request_uri, nil, headers)
|
284
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
285
|
+
raise ClientException.new('Account POST failed', :http_scheme=>parsed.scheme,
|
286
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
287
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
288
|
+
:http_reason=>resp.message)
|
289
|
+
end
|
290
|
+
resp.body
|
291
|
+
end
|
292
|
+
def post_account(headers=nil)
|
293
|
+
_retry(nil, :post_account, [headers, @http_conn])
|
294
|
+
end
|
295
|
+
|
296
|
+
def self.get_container(url, token, container, marker=nil, limit=nil,
|
297
|
+
prefix=nil, delimiter=nil, http_conn=nil, full_listing=nil)
|
298
|
+
#todo: add in rest of functionality
|
299
|
+
if not http_conn
|
300
|
+
http_conn = http_connection(url)
|
301
|
+
end
|
302
|
+
parsed = http_conn[0].clone
|
303
|
+
conn = http_conn[1]
|
304
|
+
|
305
|
+
if full_listing
|
306
|
+
rv = get_account(url, token, marker, limit, prefix, http_conn)
|
307
|
+
listing = rv[1]
|
308
|
+
while listing.length > 0
|
309
|
+
marker = listing[-1]['name']
|
310
|
+
listing = get_account(url, token, marker, limit, prefix, http_conn)[1]
|
311
|
+
if listing.length > 0
|
312
|
+
rv[1] += listing
|
313
|
+
end
|
314
|
+
end
|
315
|
+
return rv
|
316
|
+
end
|
317
|
+
query = Query.new(parsed.query)
|
318
|
+
query.add('format', 'json')
|
319
|
+
query.add('marker', quote(marker.to_s)) if marker
|
320
|
+
query.add('limit', quote(limit.to_s)) if limit
|
321
|
+
query.add('prefix', quote(prefix.to_s)) if prefix
|
322
|
+
parsed.query = query.to_url_params
|
323
|
+
conn.start if !conn.started?
|
324
|
+
parsed.path += "/#{quote(container)}"
|
325
|
+
resp = conn.get(parsed.request_uri, {'x-auth-token' => token})
|
326
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
327
|
+
raise ClientException.new('Container GET failed', :http_scheme=>parsed.scheme,
|
328
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
329
|
+
:http_path=>parsed.path, :http_query=>parsed.query, :http_status=>resp.code,
|
330
|
+
:http_reason=>resp.message)
|
331
|
+
end
|
332
|
+
resp_headers = {}
|
333
|
+
resp.header.each do |k,v|
|
334
|
+
resp_headers[k.downcase] = v
|
335
|
+
end
|
336
|
+
if resp.code.to_i == 204
|
337
|
+
[resp_headers, []]
|
338
|
+
else
|
339
|
+
[resp_headers, JSON.parse(resp.body())]
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def get_container(container, marker=nil, limit=nil, prefix=nil, delimiter=nil, full_listing=nil)
|
344
|
+
_retry(nil, :get_container, [container, marker, limit, prefix, delimiter, full_listing])
|
345
|
+
end
|
346
|
+
|
347
|
+
def self.head_container(url, token, container, http_conn=nil)
|
348
|
+
if not http_conn
|
349
|
+
http_conn = http_connection(url)
|
350
|
+
end
|
351
|
+
parsed = http_conn[0].clone
|
352
|
+
conn = http_conn[1]
|
353
|
+
|
354
|
+
conn.start if !conn.started?
|
355
|
+
parsed.path += "/#{quote(container)}"
|
356
|
+
resp = conn.head(parsed.request_uri, {'x-auth-token' => token})
|
357
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
358
|
+
raise ClientException.new('Container HEAD failed', :http_scheme=>parsed.scheme,
|
359
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
360
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
361
|
+
:http_reason=>resp.message)
|
362
|
+
end
|
363
|
+
resp_headers = {}
|
364
|
+
resp.header.each do |k,v|
|
365
|
+
resp_headers[k.downcase] = v
|
366
|
+
end
|
367
|
+
resp_headers
|
368
|
+
end
|
369
|
+
|
370
|
+
def head_container(container)
|
371
|
+
_retry(nil, :head_container, [container, @http_conn])
|
372
|
+
end
|
373
|
+
|
374
|
+
def self.put_container(url, token, container, headers={}, http_conn=nil)
|
375
|
+
if not http_conn
|
376
|
+
http_conn = http_connection(url)
|
377
|
+
end
|
378
|
+
parsed = http_conn[0].clone
|
379
|
+
conn = http_conn[1]
|
380
|
+
|
381
|
+
conn.start if !conn.started?
|
382
|
+
parsed.path += "/#{quote(container)}"
|
383
|
+
headers['x-auth-token'] = token
|
384
|
+
# headers['content-length'] = 0
|
385
|
+
resp = conn.put(parsed.request_uri, nil, headers)
|
386
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
387
|
+
raise ClientException.new('Container PUT failed', :http_scheme=>parsed.scheme,
|
388
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
389
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
390
|
+
:http_reason=>resp.message)
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
def put_container(container, headers={})
|
395
|
+
_retry(nil, :put_container, [container, headers, @http_conn])
|
396
|
+
end
|
397
|
+
|
398
|
+
def self.post_container(url, token, container, headers={}, http_conn=nil)
|
399
|
+
if not http_conn
|
400
|
+
http_conn = http_connection(url)
|
401
|
+
end
|
402
|
+
parsed = http_conn[0].clone
|
403
|
+
conn = http_conn[1]
|
404
|
+
|
405
|
+
conn.start if !conn.started?
|
406
|
+
parsed.path += "/#{quote(container)}"
|
407
|
+
headers['x-auth-token'] = token
|
408
|
+
resp = conn.post(parsed.request_uri, nil, headers)
|
409
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
410
|
+
raise ClientException.new('Container POST failed', :http_scheme=>parsed.scheme,
|
411
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
412
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
413
|
+
:http_reason=>resp.message)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
def post_container(container, headers={})
|
418
|
+
_retry(nil, :post_container, [container, headers])
|
419
|
+
end
|
420
|
+
|
421
|
+
def self.delete_container(url, token, container, headers={}, http_conn=nil)
|
422
|
+
if not http_conn
|
423
|
+
http_conn = http_connection(url)
|
424
|
+
end
|
425
|
+
parsed = http_conn[0].clone
|
426
|
+
conn = http_conn[1]
|
427
|
+
|
428
|
+
conn.start if !conn.started?
|
429
|
+
parsed.path += "/#{quote(container)}"
|
430
|
+
headers['x-auth-token'] = token
|
431
|
+
resp = conn.delete(parsed.request_uri, headers)
|
432
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
433
|
+
raise ClientException.new('Container DELETE failed', :http_scheme=>parsed.scheme,
|
434
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
435
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
436
|
+
:http_reason=>resp.message)
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def delete_container(container)
|
441
|
+
_retry(nil, :delete_container, [container])
|
442
|
+
end
|
443
|
+
|
444
|
+
def self.get_object(url, token, container, name, http_conn=nil, resp_chunk_size=nil, &block)
|
445
|
+
if not http_conn
|
446
|
+
http_conn = http_connection(url)
|
447
|
+
end
|
448
|
+
parsed = http_conn[0].clone
|
449
|
+
conn = http_conn[1]
|
450
|
+
|
451
|
+
|
452
|
+
parsed.path += "/#{quote(container)}/#{quote(name)}"
|
453
|
+
conn.start if not conn.started?
|
454
|
+
headers = {'x-auth-token' => token}
|
455
|
+
if block_given?
|
456
|
+
resp = conn.request_get(parsed.request_uri, headers) do |r|
|
457
|
+
r.read_body do |b|
|
458
|
+
yield b
|
459
|
+
end
|
460
|
+
end
|
461
|
+
object_body = nil
|
462
|
+
else
|
463
|
+
resp = conn.request_get(parsed.request_uri, headers)
|
464
|
+
object_body = resp.body
|
465
|
+
end
|
466
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
467
|
+
raise ClientException.new('Object GET failed', :http_scheme=>parsed.scheme,
|
468
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
469
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
470
|
+
:http_reason=>resp.message)
|
471
|
+
end
|
472
|
+
resp_headers = {}
|
473
|
+
resp.header.each do |k,v|
|
474
|
+
resp_headers[k.downcase] = v
|
475
|
+
end
|
476
|
+
[resp_headers, object_body]
|
477
|
+
end
|
478
|
+
|
479
|
+
def get_object(container, name, resp_chunk_size=nil)
|
480
|
+
_retry(nil, :get_object, [container, name, resp_chunk_size])
|
481
|
+
end
|
482
|
+
|
483
|
+
def self.head_object(url, token, container, name, http_conn=nil)
|
484
|
+
if not http_conn
|
485
|
+
http_conn = http_connection(url)
|
486
|
+
end
|
487
|
+
parsed = http_conn[0].clone
|
488
|
+
conn = http_conn[1]
|
489
|
+
|
490
|
+
|
491
|
+
parsed.path += "/#{quote(container)}/#{quote(name)}"
|
492
|
+
conn.start if not conn.started?
|
493
|
+
resp = conn.head(parsed.request_uri, {'x-auth-token' => token})
|
494
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
495
|
+
raise ClientException.new('Object HEAD failed', :http_scheme=>parsed.scheme,
|
496
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
497
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
498
|
+
:http_reason=>resp.message)
|
499
|
+
end
|
500
|
+
resp_headers = {}
|
501
|
+
resp.header.each do |k,v|
|
502
|
+
resp_headers[k.downcase] = v
|
503
|
+
end
|
504
|
+
resp_headers
|
505
|
+
end
|
506
|
+
|
507
|
+
def head_object(container, name)
|
508
|
+
_retry(nil, :head_object, [container, name])
|
509
|
+
end
|
510
|
+
|
511
|
+
def self.put_object(url, token=nil, container=nil, name=nil, contents=nil,
|
512
|
+
content_length=nil, etag=nil, chunk_size=65536,
|
513
|
+
content_type=nil, headers={}, http_conn=nil, proxy=nil)
|
514
|
+
if not http_conn
|
515
|
+
http_conn = http_connection(url)
|
516
|
+
end
|
517
|
+
parsed = http_conn[0].clone
|
518
|
+
conn = http_conn[1]
|
519
|
+
|
520
|
+
parsed.path += "/#{quote(container)}" if container
|
521
|
+
parsed.path += "/#{quote(name)}" if name
|
522
|
+
headers['x-auth-token'] = token if token
|
523
|
+
headers['etag'] = etag if etag
|
524
|
+
if content_length != nil
|
525
|
+
headers['content-length'] = content_length.to_s
|
526
|
+
else
|
527
|
+
headers.each do |k,v|
|
528
|
+
if k.downcase == 'content-length'
|
529
|
+
content_length = v.to_i
|
530
|
+
end
|
531
|
+
end
|
532
|
+
end
|
533
|
+
headers['content-type'] = content_type if content_type
|
534
|
+
headers['content-length'] = '0' if not contents
|
535
|
+
if contents.respond_to? :read
|
536
|
+
request = Net::HTTP::Put.new(parsed.request_uri, headers)
|
537
|
+
chunked = ChunkedConnectionWrapper.new(contents, chunk_size)
|
538
|
+
if content_length == nil
|
539
|
+
request['Transfer-Encoding'] = 'chunked'
|
540
|
+
request.delete('content-length')
|
541
|
+
end
|
542
|
+
request.body_stream = chunked
|
543
|
+
resp = conn.start do |http|
|
544
|
+
http.request(request)
|
545
|
+
end
|
546
|
+
else
|
547
|
+
conn.start if not conn.started?
|
548
|
+
resp = conn.put(parsed.request_uri, contents, headers)
|
549
|
+
end
|
550
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
551
|
+
raise ClientException.new('Object PUT failed', :http_scheme=>parsed.scheme,
|
552
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
553
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
554
|
+
:http_reason=>resp.message)
|
555
|
+
end
|
556
|
+
resp.header['etag']
|
557
|
+
end
|
558
|
+
|
559
|
+
def put_object(container, obj, contents, content_length=nil, etag=nil, chunk_size=65536, content_type=nil, headers={})
|
560
|
+
|
561
|
+
_default_reset = Proc.new do |args|
|
562
|
+
raise ClientException("put_object(#{container}, #{obj}, ...) failure and no ability to reset contents for reupload.")
|
563
|
+
end
|
564
|
+
reset_func = _default_reset
|
565
|
+
if (contents.respond_to? :seek) and (contents.respond_to? :tell)
|
566
|
+
orig_pos = contents.tell
|
567
|
+
reset_func = Proc.new {|a| contents.seek(orig_pos)}
|
568
|
+
elsif !contents
|
569
|
+
reset_func = Proc.new {|a| nil }
|
570
|
+
end
|
571
|
+
_retry(reset_func, :put_object, [container, obj, contents, content_length, etag, chunk_size, content_type, headers])
|
572
|
+
end
|
573
|
+
|
574
|
+
def self.post_object(url, token=nil, container=nil, name=nil, headers={}, http_conn=nil)
|
575
|
+
if not http_conn
|
576
|
+
http_conn = http_connection(url)
|
577
|
+
end
|
578
|
+
parsed = http_conn[0].clone
|
579
|
+
conn = http_conn[1]
|
580
|
+
|
581
|
+
parsed.path += "/#{quote(container)}" if container
|
582
|
+
parsed.path += "/#{quote(name)}" if name
|
583
|
+
headers['x-auth-token'] = token if token
|
584
|
+
resp = conn.post(parsed.request_uri, nil, headers)
|
585
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
586
|
+
raise ClientException.new('Object POST failed', :http_scheme=>parsed.scheme,
|
587
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
588
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
589
|
+
:http_reason=>resp.message)
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
def post_object(container, name, headers={})
|
594
|
+
_retry(nil, :post_object, [container, name, headers])
|
595
|
+
end
|
596
|
+
|
597
|
+
def self.delete_object(url, token=nil, container=nil, name=nil, http_conn=nil, headers={}, proxy=nil)
|
598
|
+
if not http_conn
|
599
|
+
http_conn = http_connection(url)
|
600
|
+
end
|
601
|
+
parsed = http_conn[0].clone
|
602
|
+
conn = http_conn[1]
|
603
|
+
|
604
|
+
conn.start if !conn.started?
|
605
|
+
parsed.path += "/#{quote(container)}" if container
|
606
|
+
parsed.path += "/#{quote(name)}" if name
|
607
|
+
headers['x-auth-token'] = token if token
|
608
|
+
resp = conn.delete(parsed.request_uri, headers)
|
609
|
+
if resp.code.to_i < 200 or resp.code.to_i > 300
|
610
|
+
raise ClientException.new('Object DELETE failed', :http_scheme=>parsed.scheme,
|
611
|
+
:http_host=>conn.address, :http_port=>conn.port,
|
612
|
+
:http_path=>parsed.path, :http_status=>resp.code,
|
613
|
+
:http_reason=>resp.message)
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
617
|
+
def delete_object(container, name, headers={})
|
618
|
+
_retry(nil, :delete_object, [container, name, headers])
|
619
|
+
end
|
620
|
+
end
|