cloudfiles 1.4.18 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|