aliyun-oss-ruby-sdk 0.4.1

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