baidubce-sdk 0.9.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.
@@ -0,0 +1,102 @@
1
+ # Copyright 2017 Baidu, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License") you may not use this file
4
+ # except in compliance with the License. You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the
9
+ # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10
+ # either express or implied. See the License for the specific language governing permissions
11
+ # and limitations under the License.
12
+
13
+ # This module defines string constants for HTTP
14
+
15
+ module Baidubce
16
+ module Http
17
+
18
+ # HTTP Content Types
19
+ JSON_TYPE = 'application/json; charset=utf-8'
20
+ OCTET_STREAM_TYPE = 'application/octet-stream'
21
+
22
+ # HTTP Methods
23
+ GET = 'GET'
24
+ PUT = 'PUT'
25
+ POST = 'POST'
26
+ DELETE = 'DELETE'
27
+ HEAD = 'HEAD'
28
+
29
+ # HTTP Headers
30
+ AUTHORIZATION = "Authorization"
31
+
32
+ CACHE_CONTROL = "Cache-Control"
33
+
34
+ CONTENT_DISPOSITION = "Content-Disposition"
35
+
36
+ CONTENT_ENCODING = "Content-Encoding"
37
+
38
+ CONTENT_LENGTH = "Content-Length"
39
+
40
+ CONTENT_MD5 = "Content-MD5"
41
+
42
+ CONTENT_RANGE = "Content-Range"
43
+
44
+ CONTENT_TYPE = "Content-Type"
45
+
46
+ DATE = "Date"
47
+
48
+ ETAG = "ETag"
49
+
50
+ EXPIRES = "Expires"
51
+
52
+ HOST = "Host"
53
+
54
+ LAST_MODIFIED = "Last-Modified"
55
+
56
+ RANGE = "Range"
57
+
58
+ SERVER = "Server"
59
+
60
+ USER_AGENT = "User-Agent"
61
+
62
+ # BCE Common HTTP Headers
63
+
64
+ BCE_PREFIX = "x-bce-"
65
+
66
+ BCE_ACL = "x-bce-acl"
67
+
68
+ BCE_CONTENT_SHA256 = "x-bce-content-sha256"
69
+
70
+ BCE_CONTENT_CRC32 = "x-bce-content-crc32"
71
+
72
+ BCE_COPY_METADATA_DIRECTIVE = "x-bce-metadata-directive"
73
+
74
+ BCE_COPY_SOURCE = "x-bce-copy-source"
75
+
76
+ BCE_COPY_SOURCE_IF_MATCH = "x-bce-copy-source-if-match"
77
+
78
+ BCE_COPY_SOURCE_IF_MODIFIED_SINCE = "x-bce-copy-source-if-modified-since"
79
+
80
+ BCE_COPY_SOURCE_IF_NONE_MATCH = "x-bce-copy-source-if-none-match"
81
+
82
+ BCE_COPY_SOURCE_IF_UNMODIFIED_SINCE = "x-bce-copy-source-if-unmodified-since"
83
+
84
+ BCE_COPY_SOURCE_RANGE = "x-bce-copy-source-range"
85
+
86
+ BCE_DATE = "x-bce-date"
87
+
88
+ BCE_USER_METADATA_PREFIX = "x-bce-meta-"
89
+
90
+ BCE_REQUEST_ID = "x-bce-request-id"
91
+
92
+ # BOS HTTP Headers
93
+
94
+ BOS_DEBUG_ID = "x-bce-bos-debug-id"
95
+
96
+ BOS_STORAGE_CLASS = "x-bce-storage-class"
97
+
98
+ # STS HTTP Headers
99
+
100
+ STS_SECURITY_TOKEN = "x-bce-security-token"
101
+ end
102
+ end
@@ -0,0 +1,87 @@
1
+ # Copyright 2017 Baidu, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
4
+ # except in compliance with the License. You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the
9
+ # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10
+ # either express or implied. See the License for the specific language governing permissions
11
+ # and limitations under the License.
12
+
13
+ # This module defines a retry policy for BCE.
14
+
15
+ require_relative 'utils/log'
16
+
17
+ module Baidubce
18
+
19
+ # A policy that never retries.
20
+ class NoRetryPolicy
21
+
22
+ # Always returns False.
23
+ def should_retry(http_code, retries_attempted)
24
+ false
25
+ end
26
+
27
+ # Always returns 0.
28
+ def get_delay_before_next_retry_in_millis(retries_attempted)
29
+ 0
30
+ end
31
+
32
+ end
33
+
34
+ # A policy that retries with exponential back-off strategy.
35
+ # This policy will keep retrying until the maximum number of retries is reached. The delay time
36
+ # will be a fixed interval for the first time then 2 * interval for the second, 4 * internal for
37
+ # the third, and so on. In general, the delay time will be 2^number_of_retries_attempted*interval.
38
+
39
+ # When a maximum of delay time is specified, the delay time will never exceed this limit.
40
+ class BackOffRetryPolicy
41
+
42
+ include Log
43
+
44
+ attr_accessor :max_error_retry, :max_delay_in_millis, :base_interval_in_millis
45
+
46
+ def initialize(max_error_retry=3,
47
+ max_delay_in_millis=20 * 1000,
48
+ base_interval_in_millis=300)
49
+
50
+ max_error_retry_msg = "max_error_retry should be a non-negative integer."
51
+ max_delay_in_millis_msg = "max_delay_in_millis should be a non-negative integer."
52
+ raise BceClientException.new(max_error_retry_msg) if max_error_retry < 0
53
+ raise BceClientException.new(max_delay_in_millis_msg) if max_delay_in_millis < 0
54
+ @max_error_retry = max_error_retry
55
+ @max_delay_in_millis = max_delay_in_millis
56
+ @base_interval_in_millis = base_interval_in_millis
57
+ end
58
+
59
+ # Return true if the http client should retry the request.
60
+ def should_retry(http_code, retries_attempted)
61
+
62
+ # stop retrying when the maximum number of retries is reached
63
+ return false if retries_attempted >= @max_error_retry
64
+ return true if http_code.nil?
65
+
66
+ # Only retry on a subset of service exceptions
67
+ if http_code == 408
68
+ logger.debug('Retry for request timeout.')
69
+ return true
70
+ end
71
+ if http_code >= 500 && http_code != 501
72
+ logger.debug('Retry for server error.')
73
+ return true
74
+ end
75
+ return false
76
+ end
77
+
78
+ # Returns the delay time in milliseconds before the next retry.
79
+ def get_delay_before_next_retry_in_millis(retries_attempted)
80
+ return 0 if retries_attempted < 0
81
+ delay_in_millis = (1 << retries_attempted) * @base_interval_in_millis
82
+ return @max_delay_in_millis if delay_in_millis > @max_delay_in_millis
83
+ return delay_in_millis
84
+ end
85
+ end
86
+
87
+ end
@@ -0,0 +1,461 @@
1
+ # Copyright 2017 Baidu, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
4
+ # except in compliance with the License. You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software distributed under the
9
+ # License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
10
+ # either express or implied. See the License for the specific language governing permissions
11
+ # and limitations under the License.
12
+
13
+ # This module provides a client class for BOS.
14
+
15
+ require 'mimemagic'
16
+
17
+ require_relative '../../bce_base_client'
18
+ require_relative 'bos_constants'
19
+
20
+ module Baidubce
21
+ module Services
22
+
23
+ class BosClient < BceBaseClient
24
+
25
+ # List buckets of user.
26
+ # returns all buckets owned by the user.
27
+ def list_buckets()
28
+ send_request(GET)
29
+ end
30
+
31
+ # Create bucket with specific name.
32
+ def create_bucket(bucket_name)
33
+ send_request(PUT, bucket_name)
34
+ end
35
+
36
+ # Delete bucket with specific name.
37
+ def delete_bucket(bucket_name)
38
+ send_request(DELETE, bucket_name)
39
+ end
40
+
41
+ # Check whether there is a bucket with specific name.
42
+ def does_bucket_exist(bucket_name)
43
+ begin
44
+ send_request(HEAD, bucket_name)
45
+ rescue BceServerException => e
46
+ return false if e.status_code == 404
47
+ return true if e.status_code == 403
48
+ end
49
+ true
50
+ end
51
+
52
+ # Get the region which the bucket located in.
53
+ # returns region of the bucket(bj/gz/sz).
54
+ def get_bucket_location(bucket_name)
55
+ params = { location: "" }
56
+ resp = send_request(GET, bucket_name, params)
57
+ resp['locationConstraint']
58
+ end
59
+
60
+ # Get Access Control Level of bucket.
61
+ def get_bucket_acl(bucket_name)
62
+ params = { acl: "" }
63
+ send_request(GET, bucket_name, params)
64
+ end
65
+
66
+ # Set Access Control Level of bucket by body.
67
+ def set_bucket_acl(bucket_name, acl)
68
+ params = { acl: "" }
69
+ headers = { CONTENT_TYPE => JSON_TYPE }
70
+ body = { accessControlList: acl }.to_json
71
+ send_request(PUT, bucket_name, params, "", headers, body)
72
+ end
73
+
74
+ # Set Access Control Level of bucket by headers.
75
+ def set_bucket_canned_acl(bucket_name, canned_acl)
76
+ params = { acl: "" }
77
+ headers = {BCE_ACL => canned_acl}
78
+ send_request(PUT, bucket_name, params, "", headers)
79
+ end
80
+
81
+ # Put Bucket Lifecycle.
82
+ def put_bucket_lifecycle(bucket_name, rules)
83
+ params = { lifecycle: "" }
84
+ headers = { CONTENT_TYPE => JSON_TYPE }
85
+ body = { rule: rules }.to_json
86
+ send_request(PUT, bucket_name, params, "", headers, body)
87
+ end
88
+
89
+ # Gut Bucket Lifecycle.
90
+ def get_bucket_lifecycle(bucket_name)
91
+ params = { lifecycle: "" }
92
+ send_request(GET, bucket_name, params)
93
+ end
94
+
95
+ # Delete Bucket Lifecycle.
96
+ def delete_bucket_lifecycle(bucket_name)
97
+ params = { lifecycle: "" }
98
+ send_request(DELETE, bucket_name, params)
99
+ end
100
+
101
+ # Put Bucket Storageclass.
102
+ def put_bucket_storageclass(bucket_name, storage_class)
103
+ params = { storageClass: "" }
104
+ headers = { CONTENT_TYPE => JSON_TYPE }
105
+ body = { storageClass: storage_class }.to_json
106
+ send_request(PUT, bucket_name, params, "", headers, body)
107
+ end
108
+
109
+ # Get Bucket Storageclass.
110
+ def get_bucket_storageclass(bucket_name)
111
+ params = { storageClass: "" }
112
+ resp = send_request(GET, bucket_name, params)
113
+ resp['storageClass']
114
+ end
115
+
116
+ # Put Bucket Cors.
117
+ def put_bucket_cors(bucket_name, cors_configuration)
118
+ params = { cors: "" }
119
+ headers = { CONTENT_TYPE => JSON_TYPE }
120
+ body = { corsConfiguration: cors_configuration }.to_json
121
+ send_request(PUT, bucket_name, params, "", headers, body)
122
+ end
123
+
124
+ # Get Bucket Cors.
125
+ def get_bucket_cors(bucket_name)
126
+ params = { cors: "" }
127
+ send_request(GET, bucket_name, params)
128
+ end
129
+
130
+ # Delete Bucket Cors.
131
+ def delete_bucket_cors(bucket_name)
132
+ params = { cors: "" }
133
+ send_request(DELETE, bucket_name, params)
134
+ end
135
+
136
+ # Put Bucket Logging.
137
+ def put_bucket_logging(source_bucket, target_bucket, target_prefix="")
138
+ params = { logging: "" }
139
+ headers = { CONTENT_TYPE => JSON_TYPE }
140
+ body = { targetBucket: target_bucket, targetPrefix: target_prefix }.to_json
141
+ send_request(PUT, source_bucket, params, "", headers, body)
142
+ end
143
+
144
+ # Get Bucket Logging.
145
+ def get_bucket_logging(bucket_name)
146
+ params = { logging: "" }
147
+ send_request(GET, bucket_name, params)
148
+ end
149
+
150
+ # Delete Bucket Logging.
151
+ def delete_bucket_logging(bucket_name)
152
+ params = { logging: "" }
153
+ send_request(DELETE, bucket_name, params)
154
+ end
155
+
156
+ # Get Object Information of bucket.
157
+ def list_objects(bucket_name, options={})
158
+ params = { maxKeys: 1000 }
159
+ params.merge! options
160
+ send_request(GET, bucket_name, params)
161
+ end
162
+
163
+ def get_object(bucket_name, key, range, save_path=nil, return_body=true)
164
+ headers = range.nil? ? {} : get_range_header_dict(range)
165
+ send_request(GET, bucket_name, {}, key, headers, "", save_path, return_body)
166
+ end
167
+
168
+ # Get Content of Object and Put Content to String.
169
+ def get_object_as_string(bucket_name, key, range=nil)
170
+ get_object(bucket_name, key, range)
171
+ end
172
+
173
+ # Get Content of Object and Put Content to File.
174
+ def get_object_to_file(bucket_name, key, save_path, range=nil)
175
+ get_object(bucket_name, key, range, save_path, false)
176
+ end
177
+
178
+ # Put an appendable object to BOS or add content to an appendable object.
179
+ def append_object(bucket_name, key, data, offset, content_md5, content_length, options={})
180
+ if content_length > MAX_APPEND_OBJECT_LENGTH
181
+ raise BceClientException.new("Object length should be less than #{MAX_APPEND_OBJECT_LENGTH}. Use multi-part upload instead.")
182
+ end
183
+ params = { append: "" }
184
+ params[:offset] = offset unless offset.nil?
185
+ headers = {
186
+ CONTENT_MD5 => content_md5,
187
+ CONTENT_LENGTH => content_length,
188
+ }
189
+ headers.merge! options
190
+ populate_headers_with_user_metadata(headers) unless headers['user-metadata'].nil?
191
+ send_request(POST, bucket_name, params, key, headers, data)
192
+ end
193
+
194
+ # Create an appendable object and put content of string to the object
195
+ # or add content of string to an appendable object.
196
+ def append_object_from_string(bucket_name, key, data, options={})
197
+ data_md5 = Digest::MD5.base64digest(data)
198
+ append_object(bucket_name, key, data, options['offset'], data_md5, data.bytesize, options)
199
+ end
200
+
201
+ # Put object to BOS.
202
+ def put_object(bucket_name, key, data, content_md5, content_length, options, &block)
203
+ if content_length > MAX_PUT_OBJECT_LENGTH
204
+ raise BceClientException.new("Object length should be less than #{MAX_PUT_OBJECT_LENGTH}. Use multi-part upload instead.")
205
+ end
206
+
207
+ headers = {
208
+ CONTENT_MD5 => content_md5,
209
+ CONTENT_LENGTH => content_length,
210
+ }
211
+ headers.merge! options
212
+ headers[CONTENT_TYPE] = OCTET_STREAM_TYPE if headers[CONTENT_TYPE].nil?
213
+ populate_headers_with_user_metadata(headers) unless headers['user-metadata'].nil?
214
+ send_request(PUT, bucket_name, {}, key, headers, data, &block)
215
+ end
216
+
217
+ # Create object and put content of string to the object.
218
+ def put_object_from_string(bucket_name, key, data, options={})
219
+ data_md5 = Digest::MD5.base64digest(data)
220
+ put_object(bucket_name, key, data, data_md5, data.length, options)
221
+ end
222
+
223
+ # Put object and put content of file to the object.
224
+ def put_object_from_file(bucket_name, key, file_name, options={})
225
+ mime = MimeMagic.by_path(file_name)
226
+ options[CONTENT_TYPE] = mime.type if options[CONTENT_TYPE].nil? && !mime.nil?
227
+ buf_size = @config.recv_buf_size
228
+ if options[CONTENT_LENGTH].nil?
229
+ data = File.open(file_name, "rb")
230
+ data_md5 = Utils.get_md5_from_file(file_name, data.size, buf_size)
231
+ put_object(bucket_name, key, data, data_md5, data.size, options)
232
+ else
233
+ left_size = options[CONTENT_LENGTH]
234
+ data_md5 = Utils.get_md5_from_file(file_name, left_size, buf_size)
235
+ put_object(bucket_name, key, "", data_md5, left_size, options) do |buf_writer|
236
+ File.open(file_name, "rb") do |part_fp|
237
+ bytes_to_read = left_size > buf_size ? buf_size : left_size
238
+ until left_size <= 0
239
+ buf_writer << part_fp.read(bytes_to_read)
240
+ left_size -= bytes_to_read
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
246
+
247
+ # Get an authorization url with expire time.
248
+ def generate_pre_signed_url(bucket_name, key, options={})
249
+ headers = options['headers'].nil? ? {} : options['headers']
250
+ params = options['params'].nil? ? {} : options['params']
251
+
252
+ path = Utils.append_uri("/", key)
253
+ url, headers[HOST] = Utils.parse_url_host(@config)
254
+ url.insert(url.index('/') + 2, bucket_name + '.')
255
+ headers[HOST] = bucket_name + '.' + headers[HOST]
256
+ params[AUTHORIZATION.downcase] = @signer.sign(@config.credentials,
257
+ GET,
258
+ path,
259
+ headers,
260
+ params,
261
+ options['timestamp'],
262
+ options['expiration_in_seconds'] || 1800,
263
+ options['headers_to_sign'])
264
+ url += Utils.url_encode_except_slash(path)
265
+ query_str = Utils.get_canonical_querystring(params, false)
266
+ url += "?#{query_str}" unless query_str.to_s.empty?
267
+ url
268
+ end
269
+
270
+ # Get meta of object.
271
+ def get_object_meta_data(bucket_name, key)
272
+ send_request(HEAD, bucket_name, {}, key)
273
+ end
274
+
275
+ # Copy one object to another object.
276
+ def copy_object(source_bucket_name, source_key, target_bucket_name, target_key, options={})
277
+ headers = options
278
+ headers[BCE_COPY_SOURCE_IF_MATCH] = headers['etag'] unless headers['etag'].nil?
279
+ if headers['user-metadata'].nil?
280
+ headers[BCE_COPY_METADATA_DIRECTIVE] = 'copy'
281
+ else
282
+ headers[BCE_COPY_METADATA_DIRECTIVE] = 'replace'
283
+ populate_headers_with_user_metadata(headers)
284
+ end
285
+
286
+ headers[BCE_COPY_SOURCE] =
287
+ Utils.url_encode_except_slash("/#{source_bucket_name}/#{source_key}")
288
+
289
+ send_request(PUT, target_bucket_name, {}, target_key, headers)
290
+ end
291
+
292
+ # Delete Object.
293
+ def delete_object(bucket_name, key)
294
+ send_request(DELETE, bucket_name, {}, key)
295
+ end
296
+
297
+ # Delete Multiple Objects.
298
+ def delete_multiple_objects(bucket_name, key_list)
299
+ params = { delete: "" }
300
+ key_arr = []
301
+ key_list.each { |item| key_arr << { key: item } }
302
+ body = { objects: key_arr }.to_json
303
+ ret = send_request(POST, bucket_name, params, "", {}, body, nil, true)
304
+ return ret.empty? ? {} : JSON.parse(ret)
305
+ end
306
+
307
+ # Initialize multi_upload_file.
308
+ def initiate_multipart_upload(bucket_name, key, options={})
309
+ params = { uploads: "" }
310
+ send_request(POST, bucket_name, params, key, options)
311
+ end
312
+
313
+ # Upload a part.
314
+ def upload_part(bucket_name, key, upload_id, part_number, part_size, options={}, &block)
315
+ headers = options
316
+ params={ partNumber: part_number, uploadId: upload_id }
317
+ if part_number < MIN_PART_NUMBER || part_number > MAX_PART_NUMBER
318
+ raise BceClientException.new(sprintf("Invalid part_number %d. The valid range is from %d to %d.",
319
+ part_number, MIN_PART_NUMBER, MAX_PART_NUMBER))
320
+ end
321
+ if part_size > MAX_PUT_OBJECT_LENGTH
322
+ raise BceClientException.new(sprintf("Single part length should be less than %d.",
323
+ MAX_PUT_OBJECT_LENGTH))
324
+ end
325
+
326
+ headers[CONTENT_LENGTH] = part_size
327
+ headers[CONTENT_TYPE] = OCTET_STREAM_TYPE
328
+
329
+ send_request(POST, bucket_name, params, key, headers, &block)
330
+ end
331
+
332
+ # Upload a part from file.
333
+ def upload_part_from_file(bucket_name, key, upload_id, part_number,
334
+ part_size, file_name, offset=0, options={})
335
+
336
+ left_size = part_size
337
+ buf_size = @config.send_buf_size
338
+ upload_part(bucket_name, key, upload_id, part_number, part_size, options) do |buf_writer|
339
+ File.open(file_name, "rb") do |part_fp|
340
+ part_fp.seek(offset)
341
+ bytes_to_read = left_size > buf_size ? buf_size : left_size
342
+ until left_size <= 0
343
+ buf_writer << part_fp.read(bytes_to_read)
344
+ left_size -= bytes_to_read
345
+ end
346
+ end
347
+ end
348
+ end
349
+
350
+ # Copy part.
351
+ def upload_part_copy(source_bucket_name, source_key, target_bucket_name, target_key, upload_id,
352
+ part_number, part_size, offset, options={})
353
+ headers = options
354
+ params={ partNumber: part_number, uploadId: upload_id }
355
+
356
+ populate_headers_with_user_metadata(headers) unless headers['user-metadata'].nil?
357
+ headers[BCE_COPY_SOURCE_IF_MATCH] = headers['etag'] unless headers['etag'].nil?
358
+ headers[BCE_COPY_SOURCE_RANGE] = sprintf("bytes=%d-%d", offset, offset + part_size - 1)
359
+ headers[BCE_COPY_SOURCE] =
360
+ Utils.url_encode_except_slash("/#{source_bucket_name}/#{source_key}")
361
+ send_request(PUT, target_bucket_name, params, target_key, headers)
362
+
363
+ end
364
+
365
+ # After finish all the task, complete multi_upload_file.
366
+ def complete_multipart_upload(bucket_name, key,upload_id, part_list, options={})
367
+ headers = options
368
+ params = { uploadId: upload_id }
369
+
370
+ populate_headers_with_user_metadata(headers) unless headers['user-metadata'].nil?
371
+ part_list.each { |part| part['eTag'].gsub!("\"", "") }
372
+ body = { parts: part_list }.to_json
373
+ send_request(POST, bucket_name, params, key, headers, body)
374
+ end
375
+
376
+ # Abort upload a part which is being uploading.
377
+ def abort_multipart_upload(bucket_name, key, upload_id)
378
+ params = { uploadId: upload_id }
379
+ send_request(DELETE, bucket_name, params, key)
380
+ end
381
+
382
+ # List all the parts that have been upload success.
383
+ def list_parts(bucket_name, key, upload_id, options={})
384
+ params = { uploadId: upload_id }
385
+ params.merge! options
386
+ send_request(GET, bucket_name, params, key)
387
+ end
388
+
389
+ # List all Multipart upload task which haven't been ended.(Completed Init_MultiPartUpload
390
+ # but not completed Complete_MultiPartUpload or Abort_MultiPartUpload).
391
+ def list_multipart_uploads(bucket_name, options={})
392
+ params = { uploads: "" }
393
+ params.merge! options
394
+ send_request(GET, bucket_name, params)
395
+ end
396
+
397
+ # Get object acl.
398
+ def get_object_acl(bucket_name, key)
399
+ params = { acl: "" }
400
+ send_request(GET, bucket_name, params, key)
401
+ end
402
+
403
+ # Set object acl by body.
404
+ def set_object_acl(bucket_name, key, acl)
405
+ params = { acl: "" }
406
+ headers = { CONTENT_TYPE => JSON_TYPE }
407
+ body = { accessControlList: acl }.to_json
408
+ send_request(PUT, bucket_name, params, key, headers, body)
409
+ end
410
+
411
+ # Set object acl by headers.
412
+ def set_object_canned_acl(bucket_name, key, canned_acl={})
413
+ params = { acl: "" }
414
+ send_request(PUT, bucket_name, params, key, canned_acl)
415
+ end
416
+
417
+ # Delete object acl.
418
+ def delete_object_acl(bucket_name, key)
419
+ params = { acl: "" }
420
+ send_request(DELETE, bucket_name, params, key)
421
+ end
422
+
423
+ def send_request(http_method, bucket_name="", params={}, key="", headers={}, body="", save_path=nil, return_body=false, &block)
424
+ path = Utils.append_uri("/", bucket_name, key)
425
+ body, headers = @http_client.send_request(@config, @signer, http_method, path, params, headers, body, save_path, &block)
426
+ # Generate result from headers and body
427
+ Utils.generate_response(headers, body, return_body)
428
+ end
429
+
430
+ def get_range_header_dict(range)
431
+ raise BceClientException.new("range type should be a array") unless range.is_a? Array
432
+ raise BceClientException.new("range should have length of 2") unless range.length == 2
433
+ raise BceClientException.new("range all element should be integer") unless range.all? { |i| i.is_a?(Integer) }
434
+ { RANGE => "bytes=#{range[0]}-#{range[1]}" }
435
+ end
436
+
437
+ def populate_headers_with_user_metadata(headers)
438
+ meta_size = 0
439
+ user_metadata = headers['user-metadata']
440
+ raise BceClientException.new("user_metadata should be of type hash.") unless user_metadata.is_a? Hash
441
+
442
+ user_metadata.each do |k, v|
443
+ k = k.encode(DEFAULT_ENCODING)
444
+ v = v.encode(DEFAULT_ENCODING)
445
+ normalized_key = BCE_USER_METADATA_PREFIX + k
446
+ headers[normalized_key] = v
447
+ meta_size += normalized_key.length
448
+ meta_size += v.length
449
+ end
450
+
451
+ if meta_size > MAX_USER_METADATA_SIZE
452
+ raise BceClientException.new("Metadata size should not be greater than #{MAX_USER_METADATA_SIZE}")
453
+ end
454
+ headers.delete('user-metadata')
455
+ end
456
+
457
+ end
458
+ end
459
+ end
460
+
461
+