aliyun-oss-ruby-sdk 0.4.1

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.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +95 -0
  3. data/README.md +423 -0
  4. data/examples/aliyun/oss/bucket.rb +144 -0
  5. data/examples/aliyun/oss/callback.rb +61 -0
  6. data/examples/aliyun/oss/object.rb +182 -0
  7. data/examples/aliyun/oss/resumable_download.rb +42 -0
  8. data/examples/aliyun/oss/resumable_upload.rb +49 -0
  9. data/examples/aliyun/oss/streaming.rb +124 -0
  10. data/examples/aliyun/oss/using_sts.rb +48 -0
  11. data/examples/aliyun/sts/assume_role.rb +59 -0
  12. data/lib/aliyun_sdk/common.rb +6 -0
  13. data/lib/aliyun_sdk/common/exception.rb +18 -0
  14. data/lib/aliyun_sdk/common/logging.rb +46 -0
  15. data/lib/aliyun_sdk/common/struct.rb +56 -0
  16. data/lib/aliyun_sdk/oss.rb +16 -0
  17. data/lib/aliyun_sdk/oss/bucket.rb +661 -0
  18. data/lib/aliyun_sdk/oss/client.rb +106 -0
  19. data/lib/aliyun_sdk/oss/config.rb +39 -0
  20. data/lib/aliyun_sdk/oss/download.rb +255 -0
  21. data/lib/aliyun_sdk/oss/exception.rb +108 -0
  22. data/lib/aliyun_sdk/oss/http.rb +338 -0
  23. data/lib/aliyun_sdk/oss/iterator.rb +92 -0
  24. data/lib/aliyun_sdk/oss/multipart.rb +74 -0
  25. data/lib/aliyun_sdk/oss/object.rb +15 -0
  26. data/lib/aliyun_sdk/oss/protocol.rb +1499 -0
  27. data/lib/aliyun_sdk/oss/struct.rb +208 -0
  28. data/lib/aliyun_sdk/oss/upload.rb +238 -0
  29. data/lib/aliyun_sdk/oss/util.rb +89 -0
  30. data/lib/aliyun_sdk/sts.rb +9 -0
  31. data/lib/aliyun_sdk/sts/client.rb +38 -0
  32. data/lib/aliyun_sdk/sts/config.rb +22 -0
  33. data/lib/aliyun_sdk/sts/exception.rb +53 -0
  34. data/lib/aliyun_sdk/sts/protocol.rb +130 -0
  35. data/lib/aliyun_sdk/sts/struct.rb +64 -0
  36. data/lib/aliyun_sdk/sts/util.rb +48 -0
  37. data/lib/aliyun_sdk/version.rb +7 -0
  38. data/spec/aliyun/oss/bucket_spec.rb +597 -0
  39. data/spec/aliyun/oss/client/bucket_spec.rb +554 -0
  40. data/spec/aliyun/oss/client/client_spec.rb +297 -0
  41. data/spec/aliyun/oss/client/resumable_download_spec.rb +220 -0
  42. data/spec/aliyun/oss/client/resumable_upload_spec.rb +413 -0
  43. data/spec/aliyun/oss/http_spec.rb +83 -0
  44. data/spec/aliyun/oss/multipart_spec.rb +686 -0
  45. data/spec/aliyun/oss/object_spec.rb +785 -0
  46. data/spec/aliyun/oss/service_spec.rb +142 -0
  47. data/spec/aliyun/oss/util_spec.rb +50 -0
  48. data/spec/aliyun/sts/client_spec.rb +150 -0
  49. data/spec/aliyun/sts/util_spec.rb +39 -0
  50. data/tests/config.rb +31 -0
  51. data/tests/test_content_encoding.rb +54 -0
  52. data/tests/test_content_type.rb +95 -0
  53. data/tests/test_custom_headers.rb +70 -0
  54. data/tests/test_encoding.rb +77 -0
  55. data/tests/test_large_file.rb +66 -0
  56. data/tests/test_multipart.rb +97 -0
  57. data/tests/test_object_acl.rb +49 -0
  58. data/tests/test_object_key.rb +68 -0
  59. data/tests/test_object_url.rb +69 -0
  60. data/tests/test_resumable.rb +40 -0
  61. metadata +240 -0
@@ -0,0 +1,785 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'yaml'
5
+ require 'nokogiri'
6
+
7
+ module AliyunSDK
8
+ module OSS
9
+
10
+ describe "Object" do
11
+
12
+ before :all do
13
+ @endpoint = 'oss.aliyuncs.com'
14
+ @protocol = Protocol.new(
15
+ Config.new(:endpoint => @endpoint,
16
+ :access_key_id => 'xxx', :access_key_secret => 'yyy'))
17
+ @bucket = 'rubysdk-bucket'
18
+ end
19
+
20
+ def get_request_path(object = nil)
21
+ p = "#{@bucket}.#{@endpoint}/"
22
+ p += CGI.escape(object) if object
23
+ p
24
+ end
25
+
26
+ def get_resource_path(object, bucket = nil)
27
+ "/#{bucket || @bucket}/#{object}"
28
+ end
29
+
30
+ def mock_copy_object(last_modified, etag)
31
+ builder = Nokogiri::XML::Builder.new do |xml|
32
+ xml.CopyObjectResult {
33
+ xml.LastModified last_modified.to_s
34
+ xml.ETag etag
35
+ }
36
+ end
37
+
38
+ builder.to_xml
39
+ end
40
+
41
+ def mock_acl(acl)
42
+ Nokogiri::XML::Builder.new do |xml|
43
+ xml.AccessControlPolicy {
44
+ xml.Owner {
45
+ xml.ID 'owner_id'
46
+ xml.DisplayName 'owner_name'
47
+ }
48
+
49
+ xml.AccessControlList {
50
+ xml.Grant acl
51
+ }
52
+ }
53
+ end.to_xml
54
+ end
55
+
56
+ def mock_delete(objects, opts = {})
57
+ # It may have invisible chars in object key which will corrupt
58
+ # libxml. So we're constructing xml body manually here.
59
+ body = '<?xml version="1.0"?>'
60
+ body << '<Delete>'
61
+ body << '<Quiet>' << (opts[:quiet]? true : false).to_s << '</Quiet>'
62
+ objects.each { |k|
63
+ body << '<Object><Key>' << k << '</Key></Object>'
64
+ }
65
+ body << '</Delete>'
66
+ end
67
+
68
+ def mock_delete_result(deleted, opts = {})
69
+ Nokogiri::XML::Builder.new do |xml|
70
+ xml.DeleteResult {
71
+ xml.EncodingType opts[:encoding] if opts[:encoding]
72
+ deleted.each do |o|
73
+ xml.Deleted {
74
+ xml.Key o
75
+ }
76
+ end
77
+ }
78
+ end.to_xml
79
+ end
80
+
81
+ def mock_error(code, message)
82
+ Nokogiri::XML::Builder.new do |xml|
83
+ xml.Error {
84
+ xml.Code code
85
+ xml.Message message
86
+ xml.RequestId '0000'
87
+ }
88
+ end.to_xml
89
+ end
90
+
91
+ def err(msg, reqid = '0000')
92
+ "#{msg} RequestId: #{reqid}"
93
+ end
94
+
95
+ context "Put object" do
96
+
97
+ it "should PUT to create object" do
98
+ object_name = 'ruby'
99
+ url = get_request_path(object_name)
100
+ stub_request(:put, url)
101
+
102
+ content = "hello world"
103
+ @protocol.put_object(@bucket, object_name) do |c|
104
+ c << content
105
+ end
106
+
107
+ expect(WebMock).to have_requested(:put, url)
108
+ .with(:body => content, :query => {})
109
+ end
110
+
111
+ it "should raise Exception on error" do
112
+ object_name = 'ruby'
113
+ url = get_request_path(object_name)
114
+
115
+ code = 'NoSuchBucket'
116
+ message = 'The bucket does not exist.'
117
+ stub_request(:put, url).to_return(
118
+ :status => 404, :body => mock_error(code, message))
119
+
120
+ content = "hello world"
121
+ expect {
122
+ @protocol.put_object(@bucket, object_name) do |c|
123
+ c << content
124
+ end
125
+ }.to raise_error(ServerError, err(message))
126
+
127
+ expect(WebMock).to have_requested(:put, url)
128
+ .with(:body => content, :query => {})
129
+ end
130
+
131
+ it "should use default content-type" do
132
+ object_name = 'ruby'
133
+ url = get_request_path(object_name)
134
+ stub_request(:put, url)
135
+
136
+ @protocol.put_object(@bucket, object_name) do |content|
137
+ content << 'hello world'
138
+ end
139
+
140
+ expect(WebMock).to have_requested(:put, url)
141
+ .with(:body => 'hello world',
142
+ :headers => {'Content-Type' => HTTP::DEFAULT_CONTENT_TYPE})
143
+ end
144
+
145
+ it "should use customized content-type" do
146
+ object_name = 'ruby'
147
+ url = get_request_path(object_name)
148
+ stub_request(:put, url)
149
+
150
+ @protocol.put_object(
151
+ @bucket, object_name, :content_type => 'application/ruby'
152
+ ) do |content|
153
+ content << 'hello world'
154
+ end
155
+
156
+ expect(WebMock).to have_requested(:put, url)
157
+ .with(:body => 'hello world',
158
+ :headers => {'Content-Type' => 'application/ruby'})
159
+ end
160
+
161
+ it "should support non-ascii object name" do
162
+ object_name = '中国のruby'
163
+ url = get_request_path(object_name)
164
+ stub_request(:put, url)
165
+
166
+ content = "hello world"
167
+ @protocol.put_object(@bucket, object_name) do |c|
168
+ c << content
169
+ end
170
+
171
+ expect(WebMock).to have_requested(:put, url)
172
+ .with(:body => content, :query => {})
173
+ end
174
+
175
+ it "should set user defined metas" do
176
+ object_name = 'ruby'
177
+ url = get_request_path(object_name)
178
+ stub_request(:put, url)
179
+
180
+ @protocol.put_object(
181
+ @bucket, object_name, :metas => {'year' => '2015', 'people' => 'mary'}
182
+ ) do |content|
183
+ content << 'hello world'
184
+ end
185
+
186
+ expect(WebMock).to have_requested(:put, url)
187
+ .with(:body => 'hello world',
188
+ :headers => {
189
+ 'x-oss-meta-year' => '2015',
190
+ 'x-oss-meta-people' => 'mary'})
191
+ end
192
+
193
+ end # put object
194
+
195
+ context "Append object" do
196
+
197
+ it "should POST to append object" do
198
+ object_name = 'ruby'
199
+ url = get_request_path(object_name)
200
+
201
+ query = {'append' => '', 'position' => 11}
202
+ return_headers = {'x-oss-next-append-position' => '101'}
203
+ stub_request(:post, url).with(:query => query)
204
+ .to_return(:headers => return_headers)
205
+
206
+ content = "hello world"
207
+ next_pos = @protocol.append_object(@bucket, object_name, 11) do |c|
208
+ c << content
209
+ end
210
+
211
+ expect(WebMock).to have_requested(:post, url)
212
+ .with(:body => content, :query => query)
213
+ expect(next_pos).to eq(101)
214
+ end
215
+
216
+ it "should raise Exception on error" do
217
+ object_name = 'ruby'
218
+ url = get_request_path(object_name)
219
+
220
+ query = {'append' => '', 'position' => 11}
221
+ code = 'ObjectNotAppendable'
222
+ message = 'Normal object cannot be appended.'
223
+ stub_request(:post, url).with(:query => query).
224
+ to_return(:status => 409, :body => mock_error(code, message))
225
+
226
+ content = "hello world"
227
+ expect {
228
+ @protocol.append_object(@bucket, object_name, 11) do |c|
229
+ c << content
230
+ end
231
+ }.to raise_error(Exception, err(message))
232
+
233
+ expect(WebMock).to have_requested(:post, url)
234
+ .with(:body => content, :query => query)
235
+ end
236
+
237
+ it "should use default content-type" do
238
+ object_name = 'ruby'
239
+ url = get_request_path(object_name)
240
+ query = {'append' => '', 'position' => 0}
241
+
242
+ stub_request(:post, url).with(:query => query)
243
+
244
+ @protocol.append_object(@bucket, object_name, 0) do |content|
245
+ content << 'hello world'
246
+ end
247
+
248
+ expect(WebMock).to have_requested(:post, url)
249
+ .with(:body => 'hello world',
250
+ :query => query,
251
+ :headers => {'Content-Type' => HTTP::DEFAULT_CONTENT_TYPE})
252
+ end
253
+
254
+ it "should use customized content-type" do
255
+ object_name = 'ruby'
256
+ url = get_request_path(object_name)
257
+ query = {'append' => '', 'position' => 0}
258
+
259
+ stub_request(:post, url).with(:query => query)
260
+
261
+ @protocol.append_object(
262
+ @bucket, object_name, 0, :content_type => 'application/ruby'
263
+ ) do |content|
264
+ content << 'hello world'
265
+ end
266
+
267
+ expect(WebMock).to have_requested(:post, url)
268
+ .with(:body => 'hello world',
269
+ :query => query,
270
+ :headers => {'Content-Type' => 'application/ruby'})
271
+ end
272
+
273
+ it "should set user defined metas" do
274
+ object_name = 'ruby'
275
+ url = get_request_path(object_name)
276
+ query = {'append' => '', 'position' => 0}
277
+
278
+ stub_request(:post, url).with(:query => query)
279
+
280
+ @protocol.append_object(
281
+ @bucket, object_name, 0, :metas => {'year' => '2015', 'people' => 'mary'}
282
+ ) do |content|
283
+ content << 'hello world'
284
+ end
285
+
286
+ expect(WebMock).to have_requested(:post, url)
287
+ .with(:query => query,
288
+ :body => 'hello world',
289
+ :headers => {
290
+ 'x-oss-meta-year' => '2015',
291
+ 'x-oss-meta-people' => 'mary'})
292
+ end
293
+ end # append object
294
+
295
+ context "Copy object" do
296
+
297
+ it "should copy object" do
298
+ src_object = 'ruby'
299
+ dst_object = 'rails'
300
+ url = get_request_path(dst_object)
301
+
302
+ last_modified = Time.parse(Time.now.rfc822)
303
+ etag = '0000'
304
+ stub_request(:put, url).to_return(
305
+ :body => mock_copy_object(last_modified, etag))
306
+
307
+ result = @protocol.copy_object(@bucket, src_object, dst_object)
308
+
309
+ expect(WebMock).to have_requested(:put, url)
310
+ .with(:body => nil, :headers => {
311
+ 'x-oss-copy-source' => get_resource_path(src_object)})
312
+
313
+ expect(result[:last_modified]).to eq(last_modified)
314
+ expect(result[:etag]).to eq(etag)
315
+ end
316
+
317
+ it "should copy object of different buckets" do
318
+ src_bucket = 'source-bucket'
319
+ src_object = 'ruby'
320
+ dst_object = 'rails'
321
+ url = get_request_path(dst_object)
322
+
323
+ last_modified = Time.parse(Time.now.rfc822)
324
+ etag = '0000'
325
+ stub_request(:put, url).to_return(
326
+ :body => mock_copy_object(last_modified, etag))
327
+
328
+ result = @protocol.copy_object(
329
+ @bucket, src_object, dst_object, :src_bucket => src_bucket)
330
+
331
+ expect(WebMock).to have_requested(:put, url)
332
+ .with(:body => nil, :headers => {
333
+ 'x-oss-copy-source' => get_resource_path(src_object, src_bucket)})
334
+
335
+ expect(result[:last_modified]).to eq(last_modified)
336
+ expect(result[:etag]).to eq(etag)
337
+ end
338
+
339
+ it "should set acl and conditions when copy object" do
340
+ src_object = 'ruby'
341
+ dst_object = 'rails'
342
+ url = get_request_path(dst_object)
343
+
344
+ modified_since = Time.now
345
+ unmodified_since = Time.now
346
+ last_modified = Time.parse(Time.now.rfc822)
347
+ etag = '0000'
348
+
349
+ headers = {
350
+ 'x-oss-copy-source' => get_resource_path(src_object),
351
+ 'x-oss-object-acl' => ACL::PRIVATE,
352
+ 'x-oss-metadata-directive' => MetaDirective::REPLACE,
353
+ 'x-oss-copy-source-if-modified-since' => modified_since.httpdate,
354
+ 'x-oss-copy-source-if-unmodified-since' => unmodified_since.httpdate,
355
+ 'x-oss-copy-source-if-match' => 'me',
356
+ 'x-oss-copy-source-if-none-match' => 'ume'
357
+ }
358
+ stub_request(:put, url).to_return(
359
+ :body => mock_copy_object(last_modified, etag))
360
+
361
+ result = @protocol.copy_object(
362
+ @bucket, src_object, dst_object,
363
+ {:acl => ACL::PRIVATE,
364
+ :meta_directive => MetaDirective::REPLACE,
365
+ :condition => {
366
+ :if_modified_since => modified_since,
367
+ :if_unmodified_since => unmodified_since,
368
+ :if_match_etag => 'me',
369
+ :if_unmatch_etag => 'ume'
370
+ }
371
+ })
372
+
373
+ expect(WebMock).to have_requested(:put, url)
374
+ .with(:body => nil, :headers => headers)
375
+
376
+ expect(result[:last_modified]).to eq(last_modified)
377
+ expect(result[:etag]).to eq(etag)
378
+ end
379
+
380
+ it "should set user defined metas" do
381
+ src_object = 'ruby'
382
+ dst_object = 'rails'
383
+ url = get_request_path(dst_object)
384
+
385
+ stub_request(:put, url)
386
+
387
+ @protocol.copy_object(@bucket, src_object, dst_object,
388
+ :metas => {
389
+ 'year' => '2015',
390
+ 'people' => 'mary'
391
+ })
392
+
393
+ expect(WebMock).to have_requested(:put, url)
394
+ .with(:body => nil,
395
+ :headers => {
396
+ 'x-oss-meta-year' => '2015',
397
+ 'x-oss-meta-people' => 'mary'})
398
+ end
399
+
400
+ it "should raise Exception on error" do
401
+ src_object = 'ruby'
402
+ dst_object = 'rails'
403
+ url = get_request_path(dst_object)
404
+
405
+ code = 'EntityTooLarge'
406
+ message = 'The object to copy is too large.'
407
+ stub_request(:put, url).to_return(
408
+ :status => 400,
409
+ :headers => {'x-oss-request-id' => '0000'},
410
+ :body => mock_error(code, message))
411
+
412
+ begin
413
+ @protocol.copy_object(@bucket, src_object, dst_object)
414
+ expect(false).to be true
415
+ rescue ServerError => e
416
+ expect(e.http_code).to eq(400)
417
+ expect(e.error_code).to eq(code)
418
+ expect(e.message).to eq(err(message))
419
+ expect(e.request_id).to eq('0000')
420
+ end
421
+
422
+ expect(WebMock).to have_requested(:put, url)
423
+ .with(:body => nil, :headers => {
424
+ 'x-oss-copy-source' => get_resource_path(src_object)})
425
+ end
426
+ end # copy object
427
+
428
+ context "Get object" do
429
+
430
+ it "should GET to get object" do
431
+ object_name = 'ruby'
432
+ url = get_request_path(object_name)
433
+
434
+ return_content = "hello world"
435
+ stub_request(:get, url).to_return(:body => return_content)
436
+
437
+ content = ""
438
+ @protocol.get_object(@bucket, object_name) {|c| content << c}
439
+
440
+ expect(WebMock).to have_requested(:get, url)
441
+ .with(:body => nil, :query => {})
442
+
443
+ expect(content).to eq(return_content)
444
+ end
445
+
446
+ it "should return object meta" do
447
+ object_name = 'ruby'
448
+ url = get_request_path(object_name)
449
+
450
+ last_modified = Time.now.rfc822
451
+ return_headers = {
452
+ 'x-oss-object-type' => 'Normal',
453
+ 'ETag' => 'xxxyyyzzz',
454
+ 'Content-Length' => 1024,
455
+ 'Last-Modified' => last_modified,
456
+ 'x-oss-meta-year' => '2015',
457
+ 'x-oss-meta-people' => 'mary'
458
+ }
459
+ return_content = "hello world"
460
+ stub_request(:get, url)
461
+ .to_return(:headers => return_headers, :body => return_content)
462
+
463
+ content = ""
464
+ obj = @protocol.get_object(@bucket, object_name) {|c| content << c}
465
+
466
+ expect(WebMock).to have_requested(:get, url)
467
+ .with(:body => nil, :query => {})
468
+
469
+ expect(content).to eq(return_content)
470
+ expect(obj.key).to eq(object_name)
471
+ expect(obj.type).to eq('Normal')
472
+ expect(obj.etag).to eq('xxxyyyzzz')
473
+ expect(obj.size).to eq(1024)
474
+ expect(obj.last_modified.rfc822).to eq(last_modified)
475
+ expect(obj.metas).to eq({'year' => '2015', 'people' => 'mary'})
476
+ end
477
+
478
+ it "should raise Exception on error" do
479
+ object_name = 'ruby'
480
+ url = get_request_path(object_name)
481
+
482
+ code = 'NoSuchKey'
483
+ message = 'The object does not exist'
484
+ stub_request(:get, url).to_return(
485
+ :status => 404, :body => mock_error(code, message))
486
+
487
+ expect {
488
+ @protocol.get_object(@bucket, object_name) {|c| true}
489
+ }.to raise_error(ServerError, err(message))
490
+
491
+ expect(WebMock).to have_requested(:get, url)
492
+ .with(:body => nil, :query => {})
493
+ end
494
+
495
+ it "should get object range" do
496
+ object_name = 'ruby'
497
+ url = get_request_path(object_name)
498
+
499
+ stub_request(:get, url)
500
+
501
+ @protocol.get_object(@bucket, object_name, {:range => [0, 10]}) {}
502
+
503
+ expect(WebMock).to have_requested(:get, url)
504
+ .with(:body => nil, :query => {},
505
+ :headers => {
506
+ 'Range' => 'bytes=0-9'
507
+ })
508
+ end
509
+
510
+ it "should match modify time and etag" do
511
+ object_name = 'ruby'
512
+ url = get_request_path(object_name)
513
+
514
+ stub_request(:get, url)
515
+
516
+ modified_since = Time.now
517
+ unmodified_since = Time.now
518
+ etag = 'xxxyyyzzz'
519
+ not_etag = 'aaabbbccc'
520
+ @protocol.get_object(
521
+ @bucket, object_name,
522
+ {:condition => {
523
+ :if_modified_since => modified_since,
524
+ :if_unmodified_since => unmodified_since,
525
+ :if_match_etag => etag,
526
+ :if_unmatch_etag => not_etag}}) {}
527
+
528
+ expect(WebMock).to have_requested(:get, url)
529
+ .with(:body => nil, :query => {},
530
+ :headers => {
531
+ 'If-Modified-Since' => modified_since.httpdate,
532
+ 'If-Unmodified-since' => unmodified_since.httpdate,
533
+ 'If-Match' => etag,
534
+ 'If-None-Match' => not_etag})
535
+ end
536
+
537
+ it "should rewrite response headers" do
538
+ object_name = 'ruby'
539
+ url = get_request_path(object_name)
540
+
541
+ expires = Time.now
542
+ rewrites = {
543
+ :content_type => 'ct',
544
+ :content_language => 'cl',
545
+ :expires => expires,
546
+ :cache_control => 'cc',
547
+ :content_disposition => 'cd',
548
+ :content_encoding => 'ce'
549
+ }
550
+ query = Hash[rewrites.map {|k, v| ["response-#{k.to_s.sub('_', '-')}", v]}]
551
+ query['response-expires'] = rewrites[:expires].httpdate
552
+
553
+ stub_request(:get, url).with(:query => query)
554
+
555
+ @protocol.get_object(@bucket, object_name, :rewrite => rewrites) {}
556
+
557
+ expect(WebMock).to have_requested(:get, url)
558
+ .with(:body => nil, :query => query)
559
+ end
560
+ end # Get object
561
+
562
+ context "Get object meta" do
563
+
564
+ it "should get object meta" do
565
+ object_name = 'ruby'
566
+ url = get_request_path(object_name)
567
+
568
+ last_modified = Time.now.rfc822
569
+ return_headers = {
570
+ 'x-oss-object-type' => 'Normal',
571
+ 'ETag' => 'xxxyyyzzz',
572
+ 'Content-Length' => 1024,
573
+ 'Last-Modified' => last_modified,
574
+ 'x-oss-meta-year' => '2015',
575
+ 'x-oss-meta-people' => 'mary'
576
+ }
577
+ stub_request(:head, url).to_return(:headers => return_headers)
578
+
579
+ obj = @protocol.get_object_meta(@bucket, object_name)
580
+
581
+ expect(WebMock).to have_requested(:head, url)
582
+ .with(:body => nil, :query => {})
583
+
584
+ expect(obj.key).to eq(object_name)
585
+ expect(obj.type).to eq('Normal')
586
+ expect(obj.etag).to eq('xxxyyyzzz')
587
+ expect(obj.size).to eq(1024)
588
+ expect(obj.last_modified.rfc822).to eq(last_modified)
589
+ expect(obj.metas).to eq({'year' => '2015', 'people' => 'mary'})
590
+ end
591
+
592
+ it "should set conditions" do
593
+ object_name = 'ruby'
594
+ url = get_request_path(object_name)
595
+
596
+ stub_request(:head, url)
597
+
598
+ modified_since = Time.now
599
+ unmodified_since = Time.now
600
+ etag = 'xxxyyyzzz'
601
+ not_etag = 'aaabbbccc'
602
+
603
+ @protocol.get_object_meta(
604
+ @bucket, object_name,
605
+ :condition => {
606
+ :if_modified_since => modified_since,
607
+ :if_unmodified_since => unmodified_since,
608
+ :if_match_etag => etag,
609
+ :if_unmatch_etag => not_etag})
610
+
611
+ expect(WebMock).to have_requested(:head, url)
612
+ .with(:body => nil, :query => {},
613
+ :headers => {
614
+ 'If-Modified-Since' => modified_since.httpdate,
615
+ 'If-Unmodified-since' => unmodified_since.httpdate,
616
+ 'If-Match' => etag,
617
+ 'If-None-Match' => not_etag})
618
+ end
619
+ end # Get object meta
620
+
621
+ context "Delete object" do
622
+
623
+ it "should DELETE to delete object" do
624
+ object_name = 'ruby'
625
+ url = get_request_path(object_name)
626
+
627
+ stub_request(:delete, url)
628
+
629
+ @protocol.delete_object(@bucket, object_name)
630
+
631
+ expect(WebMock).to have_requested(:delete, url)
632
+ .with(:body => nil, :query => {})
633
+ end
634
+
635
+ it "should raise Exception on error" do
636
+ object_name = 'ruby'
637
+ url = get_request_path(object_name)
638
+
639
+ code = 'NoSuchBucket'
640
+ message = 'The bucket does not exist.'
641
+ stub_request(:delete, url).to_return(
642
+ :status => 404, :body => mock_error(code, message))
643
+
644
+ expect {
645
+ @protocol.delete_object(@bucket, object_name)
646
+ }.to raise_error(ServerError, err(message))
647
+
648
+ expect(WebMock).to have_requested(:delete, url)
649
+ .with(:body => nil, :query => {})
650
+ end
651
+
652
+ it "should batch delete objects" do
653
+ url = get_request_path
654
+ query = {'delete' => '', 'encoding-type' => KeyEncoding::URL}
655
+
656
+ object_names = (1..5).map do |i|
657
+ "object-#{i}"
658
+ end
659
+
660
+ stub_request(:post, url)
661
+ .with(:query => query)
662
+ .to_return(:body => mock_delete_result(object_names))
663
+
664
+ opts = {:quiet => false, :encoding => KeyEncoding::URL}
665
+ deleted = @protocol.batch_delete_objects(@bucket, object_names, opts)
666
+
667
+ expect(WebMock).to have_requested(:post, url)
668
+ .with(:query => query, :body => mock_delete(object_names, opts))
669
+ expect(deleted).to match_array(object_names)
670
+ end
671
+
672
+ it "should decode object key in batch delete response" do
673
+ url = get_request_path
674
+ query = {'delete' => '', 'encoding-type' => KeyEncoding::URL}
675
+
676
+ object_names = (1..5).map do |i|
677
+ "对象-#{i}"
678
+ end
679
+ es_objects = (1..5).map do |i|
680
+ CGI.escape "对象-#{i}"
681
+ end
682
+ opts = {:quiet => false, :encoding => KeyEncoding::URL}
683
+
684
+ stub_request(:post, url)
685
+ .with(:query => query)
686
+ .to_return(:body => mock_delete_result(es_objects, opts))
687
+
688
+ deleted = @protocol.batch_delete_objects(@bucket, object_names, opts)
689
+
690
+ expect(WebMock).to have_requested(:post, url)
691
+ .with(:query => query, :body => mock_delete(object_names, opts))
692
+ expect(deleted).to match_array(object_names)
693
+ end
694
+ end # delete object
695
+
696
+ context "acl" do
697
+ it "should update acl" do
698
+ object_name = 'ruby'
699
+ url = get_request_path(object_name)
700
+
701
+ query = {'acl' => ''}
702
+ stub_request(:put, url).with(:query => query)
703
+
704
+ @protocol.put_object_acl(@bucket, object_name, ACL::PUBLIC_READ)
705
+
706
+ expect(WebMock).to have_requested(:put, url)
707
+ .with(:query => query,
708
+ :headers => {'x-oss-object-acl' => ACL::PUBLIC_READ},
709
+ :body => nil)
710
+ end
711
+
712
+ it "should get acl" do
713
+ object_name = 'ruby'
714
+ url = get_request_path(object_name)
715
+
716
+ query = {'acl' => ''}
717
+ return_acl = ACL::PUBLIC_READ
718
+
719
+ stub_request(:get, url)
720
+ .with(:query => query)
721
+ .to_return(:body => mock_acl(return_acl))
722
+
723
+ acl = @protocol.get_object_acl(@bucket, object_name)
724
+
725
+ expect(WebMock).to have_requested(:get, url)
726
+ .with(:body => nil, :query => query)
727
+ expect(acl).to eq(return_acl)
728
+ end
729
+ end # acl
730
+
731
+ context "cors" do
732
+ it "should get object cors" do
733
+ object_name = 'ruby'
734
+ url = get_request_path(object_name)
735
+
736
+ return_rule = CORSRule.new(
737
+ :allowed_origins => 'origin',
738
+ :allowed_methods => 'PUT',
739
+ :allowed_headers => 'Authorization',
740
+ :expose_headers => 'x-oss-test',
741
+ :max_age_seconds => 10
742
+ )
743
+ stub_request(:options, url).to_return(
744
+ :headers => {
745
+ 'Access-Control-Allow-Origin' => return_rule.allowed_origins,
746
+ 'Access-Control-Allow-Methods' => return_rule.allowed_methods,
747
+ 'Access-Control-Allow-Headers' => return_rule.allowed_headers,
748
+ 'Access-Control-Expose-Headers' => return_rule.expose_headers,
749
+ 'Access-Control-Max-Age' => return_rule.max_age_seconds
750
+ }
751
+ )
752
+
753
+ rule = @protocol.get_object_cors(
754
+ @bucket, object_name, 'origin', 'PUT', ['Authorization'])
755
+
756
+ expect(WebMock).to have_requested(:options, url)
757
+ .with(:body => nil, :query => {})
758
+ expect(rule.to_s).to eq(return_rule.to_s)
759
+ end
760
+ end # cors
761
+
762
+ context "callback" do
763
+ it "should encode callback" do
764
+ callback = Callback.new(
765
+ url: 'http://app.server.com/callback',
766
+ query: {'id' => 1, 'name' => '杭州'},
767
+ body: 'hello world',
768
+ host: 'server.com'
769
+ )
770
+
771
+ encoded = "eyJjYWxsYmFja1VybCI6Imh0dHA6Ly9hcHAuc2VydmVyLmNvbS9jYWxsYmFjaz9pZD0xJm5hbWU9JUU2JTlEJUFEJUU1JUI3JTlFIiwiY2FsbGJhY2tCb2R5IjoiaGVsbG8gd29ybGQiLCJjYWxsYmFja0JvZHlUeXBlIjoiYXBwbGljYXRpb24veC13d3ctZm9ybS11cmxlbmNvZGVkIiwiY2FsbGJhY2tIb3N0Ijoic2VydmVyLmNvbSJ9"
772
+ expect(callback.serialize).to eq(encoded)
773
+ end
774
+
775
+ it "should not accept url with query string" do
776
+ expect {
777
+ Callback.new(url: 'http://app.server.com/callback?id=1').serialize
778
+ }.to raise_error(ClientError, "Query parameters should not appear in URL.")
779
+ end
780
+
781
+ end
782
+ end # Object
783
+
784
+ end # OSS
785
+ end # Aliyun