azure-storage-blob 1.1.0 → 2.0.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.
@@ -1,679 +1,720 @@
1
- # frozen_string_literal: true
2
-
3
- #-------------------------------------------------------------------------
4
- # # Copyright (c) Microsoft and contributors. All rights reserved.
5
- #
6
- # The MIT License(MIT)
7
-
8
- # Permission is hereby granted, free of charge, to any person obtaining a copy
9
- # of this software and associated documentation files(the "Software"), to deal
10
- # in the Software without restriction, including without limitation the rights
11
- # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12
- # copies of the Software, and to permit persons to whom the Software is
13
- # furnished to do so, subject to the following conditions :
14
-
15
- # The above copyright notice and this permission notice shall be included in
16
- # all copies or substantial portions of the Software.
17
-
18
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
- # THE SOFTWARE.
25
- #--------------------------------------------------------------------------
26
- require "azure/storage/blob/page"
27
- require "azure/storage/blob/block"
28
- require "azure/storage/blob/append"
29
- require "azure/storage/blob/blob"
30
-
31
- module Azure::Storage
32
- include Azure::Storage::Common::Service
33
- StorageService = Azure::Storage::Common::Service::StorageService
34
-
35
- module Blob
36
- class BlobService < StorageService
37
- include Azure::Storage::Common::Core::Utility
38
- include Azure::Storage::Blob
39
- include Azure::Storage::Blob::Container
40
-
41
- class << self
42
- # Public: Creates an instance of [Azure::Storage::Blob::BlobService]
43
- #
44
- # ==== Attributes
45
- #
46
- # * +options+ - Hash. Optional parameters.
47
- #
48
- # ==== Options
49
- #
50
- # Accepted key/value pairs in options parameter are:
51
- #
52
- # * +:use_development_storage+ - TrueClass|FalseClass. Whether to use storage emulator.
53
- # * +:development_storage_proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
54
- # * +:storage_account_name+ - String. The name of the storage account.
55
- # * +:storage_access_key+ - Base64 String. The access key of the storage account.
56
- # * +:storage_sas_token+ - String. The signed access signature for the storage account or one of its service.
57
- # * +:storage_blob_host+ - String. Specified Blob service endpoint or hostname
58
- # * +:storage_dns_suffix+ - String. The suffix of a regional Storage Service, to
59
- # * +:default_endpoints_protocol+ - String. http or https
60
- # * +:use_path_style_uri+ - String. Whether use path style URI for specified endpoints
61
- # * +:ca_file+ - String. File path of the CA file if having issue with SSL
62
- # * +:user_agent_prefix+ - String. The user agent prefix that can identify the application calls the library
63
- #
64
- # The valid set of options include:
65
- # * Storage Emulator: +:use_development_storage+ required, +:development_storage_proxy_uri+ optionally
66
- # * Storage account name and key: +:storage_account_name+ and +:storage_access_key+ required, set +:storage_dns_suffix+ necessarily
67
- # * Storage account name and SAS token: +:storage_account_name+ and +:storage_sas_token+ required, set +:storage_dns_suffix+ necessarily
68
- # * Specified hosts and SAS token: At least one of the service host and SAS token. It's up to user to ensure the SAS token is suitable for the serivce
69
- # * Anonymous Blob: only +:storage_blob_host+, if it is to only access blobs within a container
70
- #
71
- # Additional notes:
72
- # * Specified hosts can be set when use account name with access key or sas token
73
- # * +:default_endpoints_protocol+ can be set if the scheme is not specified in hosts
74
- # * Storage emulator always use path style URI
75
- # * +:ca_file+ is independent.
76
- #
77
- # When empty options are given, it will try to read settings from Environment Variables. Refer to [Azure::Storage::Common::ClientOptions.env_vars_mapping] for the mapping relationship
78
- #
79
- # @return [Azure::Storage::Blob::BlobService]
80
- def create(options = {}, &block)
81
- service_options = { client: Azure::Storage::Common::Client::create(options, &block), api_version: Azure::Storage::Blob::Default::STG_VERSION }
82
- service_options[:user_agent_prefix] = options[:user_agent_prefix] if options[:user_agent_prefix]
83
- Azure::Storage::Blob::BlobService.new(service_options, &block)
84
- end
85
-
86
- # Public: Creates an instance of [Azure::Storage::Blob::BlobService] with Storage Emulator
87
- #
88
- # ==== Attributes
89
- #
90
- # * +proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
91
- #
92
- # @return [Azure::Storage::Blob::BlobService]
93
- def create_development(proxy_uri = nil, &block)
94
- service_options = { client: Azure::Storage::Common::Client::create_development(proxy_uri, &block), api_version: Azure::Storage::Blob::Default::STG_VERSION }
95
- Azure::Storage::Blob::BlobService.new(service_options, &block)
96
- end
97
-
98
- # Public: Creates an instance of [Azure::Storage::Blob::BlobService] from Environment Variables
99
- #
100
- # @return [Azure::Storage::Blob::BlobService]
101
- def create_from_env(&block)
102
- service_options = { client: Azure::Storage::Common::Client::create_from_env(&block), api_version: Azure::Storage::Blob::Default::STG_VERSION }
103
- Azure::Storage::Blob::BlobService.new(service_options, &block)
104
- end
105
-
106
- # Public: Creates an instance of [Azure::Storage::Blob::BlobService] from Environment Variables
107
- #
108
- # ==== Attributes
109
- #
110
- # * +connection_string+ - String. Please refer to https://azure.microsoft.com/en-us/documentation/articles/storage-configure-connection-string/.
111
- #
112
- # @return [Azure::Storage::Blob::BlobService]
113
- def create_from_connection_string(connection_string, &block)
114
- service_options = { client: Azure::Storage::Common::Client::create_from_connection_string(connection_string, &block), api_version: Azure::Storage::Blob::Default::STG_VERSION }
115
- Azure::Storage::Blob::BlobService.new(service_options, &block)
116
- end
117
- end
118
-
119
- # Public: Initializes an instance of [Azure::Storage::Blob::BlobService]
120
- #
121
- # ==== Attributes
122
- #
123
- # * +options+ - Hash. Optional parameters.
124
- #
125
- # ==== Options
126
- #
127
- # Accepted key/value pairs in options parameter are:
128
- #
129
- # * +:use_development_storage+ - TrueClass|FalseClass. Whether to use storage emulator.
130
- # * +:development_storage_proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
131
- # * +:storage_connection_string+ - String. The storage connection string.
132
- # * +:storage_account_name+ - String. The name of the storage account.
133
- # * +:storage_access_key+ - Base64 String. The access key of the storage account.
134
- # * +:storage_sas_token+ - String. The signed access signature for the storage account or one of its service.
135
- # * +:storage_blob_host+ - String. Specified Blob serivce endpoint or hostname
136
- # * +:storage_dns_suffix+ - String. The suffix of a regional Storage Serivce, to
137
- # * +:default_endpoints_protocol+ - String. http or https
138
- # * +:use_path_style_uri+ - String. Whether use path style URI for specified endpoints
139
- # * +:ca_file+ - String. File path of the CA file if having issue with SSL
140
- # * +:ssl_version+ - Symbol. The ssl version to be used, sample: :TLSv1_1, :TLSv1_2, for the details, see https://github.com/ruby/openssl/blob/master/lib/openssl/ssl.rb
141
- # * +:ssl_min_version+ - Symbol. The min ssl version supported, only supported in Ruby 2.5+
142
- # * +:ssl_max_version+ - Symbol. The max ssl version supported, only supported in Ruby 2.5+
143
- # * +:user_agent_prefix+ - String. The user agent prefix that can identify the application calls the library
144
- # * +:client+ - Azure::Storage::Common::Client. The common client used to initalize the service.
145
- #
146
- # The valid set of options include:
147
- # * Storage Emulator: +:use_development_storage+ required, +:development_storage_proxy_uri+ optionally
148
- # * Storage account name and key: +:storage_account_name+ and +:storage_access_key+ required, set +:storage_dns_suffix+ necessarily
149
- # * Storage account name and SAS token: +:storage_account_name+ and +:storage_sas_token+ required, set +:storage_dns_suffix+ necessarily
150
- # * Specified hosts and SAS token: At least one of the service host and SAS token. It's up to user to ensure the SAS token is suitable for the serivce
151
- # * Azure::Storage::Common::Client: The common client used to initalize the service. This client can be initalized and used repeatedly.
152
- # * Anonymous Blob: only +:storage_blob_host+, if it is to only access blobs within a container
153
- #
154
- # Additional notes:
155
- # * Specified hosts can be set when use account name with access key or sas token
156
- # * +:default_endpoints_protocol+ can be set if the scheme is not specified in hosts
157
- # * Storage emulator always use path style URI
158
- # * +:ca_file+ is independent.
159
- #
160
- # When empty options are given, it will try to read settings from Environment Variables. Refer to [Azure::Storage::Common::ClientOptions.env_vars_mapping] for the mapping relationship
161
- def initialize(options = {}, &block)
162
- service_options = options.clone
163
- client_config = service_options[:client] ||= Azure::Storage::Common::Client::create(service_options, &block)
164
- @user_agent_prefix = service_options[:user_agent_prefix] if service_options[:user_agent_prefix]
165
- @api_version = service_options[:api_version] || Azure::Storage::Blob::Default::STG_VERSION
166
- signer = service_options[:signer] || client_config.signer || Azure::Storage::Common::Core::Auth::SharedKey.new(client_config.storage_account_name, client_config.storage_access_key)
167
- signer.api_ver = @api_version if signer.is_a? Azure::Storage::Common::Core::Auth::SharedAccessSignatureSigner
168
- super(signer, client_config.storage_account_name, service_options, &block)
169
- @storage_service_host[:primary] = client.storage_blob_host
170
- @storage_service_host[:secondary] = client.storage_blob_host true
171
- end
172
-
173
- def call(method, uri, body = nil, headers = {}, options = {})
174
- content_type = get_or_apply_content_type(body, headers[Azure::Storage::Common::HeaderConstants::BLOB_CONTENT_TYPE])
175
- headers[Azure::Storage::Common::HeaderConstants::BLOB_CONTENT_TYPE] = content_type if content_type
176
-
177
- headers["x-ms-version"] = @api_version ? @api_version : Default::STG_VERSION
178
- headers["User-Agent"] = @user_agent_prefix ? "#{@user_agent_prefix}; #{Default::USER_AGENT}" : Default::USER_AGENT
179
- response = super
180
-
181
- # Force the response.body to the content charset of specified in the header.
182
- # Content-Type is echo'd back for the blob and is used to store the encoding of the octet stream
183
- if !response.nil? && !response.body.nil? && response.headers["Content-Type"]
184
- charset = parse_charset_from_content_type(response.headers["Content-Type"])
185
- response.body.force_encoding(charset) if charset && charset.length > 0
186
- end
187
-
188
- response
189
- end
190
-
191
- # Public: Get a list of Containers from the server.
192
- #
193
- # ==== Attributes
194
- #
195
- # * +options+ - Hash. Optional parameters.
196
- #
197
- # ==== Options
198
- #
199
- # Accepted key/value pairs in options parameter are:
200
- # * +:prefix+ - String. Filters the results to return only containers
201
- # whose name begins with the specified prefix. (optional)
202
- #
203
- # * +:marker+ - String. An identifier the specifies the portion of the
204
- # list to be returned. This value comes from the property
205
- # Azure::Storage::Common::EnumerationResults.continuation_token when there
206
- # are more containers available than were returned. The
207
- # marker value may then be used here to request the next set
208
- # of list items. (optional)
209
- #
210
- # * +:max_results+ - Integer. Specifies the maximum number of containers to return.
211
- # If max_results is not specified, or is a value greater than
212
- # 5,000, the server will return up to 5,000 items. If it is set
213
- # to a value less than or equal to zero, the server will return
214
- # status code 400 (Bad Request). (optional)
215
- #
216
- # * +:metadata+ - Boolean. Specifies whether or not to return the container metadata.
217
- # (optional, Default=false)
218
- #
219
- # * +:timeout+ - Integer. A timeout in seconds.
220
- #
221
- # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
222
- # in the analytics logs when storage analytics logging is enabled.
223
- #
224
- # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
225
- # which location the request should be sent to.
226
- #
227
- # See: https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx
228
- #
229
- # NOTE: Metadata requested with the :metadata parameter must have been stored in
230
- # accordance with the naming restrictions imposed by the 2009-09-19 version of the Blob
231
- # service. Beginning with that version, all metadata names must adhere to the naming
232
- # conventions for C# identifiers. See: https://msdn.microsoft.com/en-us/library/aa664670(VS.71).aspx
233
- #
234
- # Any metadata with invalid names which were previously stored, will be returned with the
235
- # key "x-ms-invalid-name" in the metadata hash. This may contain multiple values and be an
236
- # Array (vs a String if it only contains a single value).
237
- #
238
- # Returns an Azure::Storage::Common::EnumerationResults
239
- #
240
- def list_containers(options = {})
241
- query = {}
242
- if options
243
- StorageService.with_query query, "prefix", options[:prefix]
244
- StorageService.with_query query, "marker", options[:marker]
245
- StorageService.with_query query, "maxresults", options[:max_results].to_s if options[:max_results]
246
- StorageService.with_query query, "include", "metadata" if options[:metadata] == true
247
- StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
248
- end
249
-
250
- options[:request_location_mode] = Azure::Storage::Common::RequestLocationMode::PRIMARY_OR_SECONDARY
251
- uri = containers_uri(query, options)
252
- response = call(:get, uri, nil, {}, options)
253
-
254
- Serialization.container_enumeration_results_from_xml(response.body)
255
- end
256
-
257
- # Protected: Establishes an exclusive write lock on a container or a blob. The lock duration can be 15 to 60 seconds, or can be infinite.
258
- # To write to a locked container or blob, a client must provide a lease ID.
259
- #
260
- # ==== Attributes
261
- #
262
- # * +container+ - String. The container name.
263
- # * +blob+ - String. The blob name.
264
- # * +options+ - Hash. Optional parameters.
265
- #
266
- # ==== Options
267
- #
268
- # Accepted key/value pairs in options parameter are:
269
- # * +:duration+ - Integer. Default -1. Specifies the duration of the lease, in seconds, or negative one (-1)
270
- # for a lease that never expires. A non-infinite lease can be between 15 and 60 seconds. (optional)
271
- # * +:proposed_lease_id+ - String. Proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request)
272
- # if the proposed lease ID is not in the correct format. (optional)
273
- # * +:timeout+ - Integer. A timeout in seconds.
274
- # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
275
- # in the analytics logs when storage analytics logging is enabled.
276
- # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to acquire the lease
277
- # only if the blob has been modified since the specified date/time. If the blob has not been modified,
278
- # the Blob service returns status code 412 (Precondition Failed).
279
- # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to acquire the lease
280
- # only if the blob has not been modified since the specified date/time. If the blob has been modified,
281
- # the Blob service returns status code 412 (Precondition Failed).
282
- # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to acquire the lease
283
- # only if the blob's ETag value matches the value specified. If the values do not match,
284
- # the Blob service returns status code 412 (Precondition Failed).
285
- # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to acquire the lease
286
- # only if the blob's ETag value does not match the value specified. If the values are identical,
287
- # the Blob service returns status code 412 (Precondition Failed).
288
- # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
289
- # in cross-origin resource sharing headers on the response.
290
- #
291
- # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
292
- #
293
- # Returns a String of the new unique lease id. While the lease is active, you must include the lease ID with any request
294
- # to write, or to renew, change, or release the lease.
295
- #
296
- protected
297
- def acquire_lease(container, blob, options = {})
298
- query = { "comp" => "lease" }
299
- StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
300
-
301
- if blob
302
- uri = blob_uri(container, blob, query)
303
- else
304
- uri = container_uri(container, query)
305
- end
306
-
307
- duration = -1
308
- duration = options[:duration] if options[:duration]
309
-
310
- headers = {}
311
- StorageService.with_header headers, "x-ms-lease-action", "acquire"
312
- StorageService.with_header headers, "x-ms-lease-duration", duration.to_s if duration
313
- StorageService.with_header headers, "x-ms-proposed-lease-id", options[:proposed_lease_id]
314
- StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
315
- add_blob_conditional_headers options, headers
316
-
317
- response = call(:put, uri, nil, headers, options)
318
- response.headers["x-ms-lease-id"]
319
- end
320
-
321
- # Protected: Renews the lease. The lease can be renewed if the lease ID specified on the request matches that
322
- # associated with the blob. Note that the lease may be renewed even if it has expired as long as the container or blob
323
- # has not been modified or leased again since the expiration of that lease. When you renew a lease, the
324
- # lease duration clock resets.
325
- #
326
- # ==== Attributes
327
- #
328
- # * +container+ - String. The container name.
329
- # * +blob+ - String. The blob name.
330
- # * +lease+ - String. The lease id
331
- # * +options+ - Hash. Optional parameters.
332
- #
333
- # ==== Options
334
- #
335
- # Accepted key/value pairs in options parameter are:
336
- # * +:timeout+ - Integer. A timeout in seconds.
337
- # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
338
- # in the analytics logs when storage analytics logging is enabled.
339
- # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to renew the lease
340
- # only if the blob has been modified since the specified date/time. If the blob has not been modified,
341
- # the Blob service returns status code 412 (Precondition Failed).
342
- # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to renew the lease
343
- # only if the blob has not been modified since the specified date/time. If the blob has been modified,
344
- # the Blob service returns status code 412 (Precondition Failed).
345
- # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to renew the lease
346
- # only if the blob's ETag value matches the value specified. If the values do not match,
347
- # the Blob service returns status code 412 (Precondition Failed).
348
- # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to renew the lease
349
- # only if the blob's ETag value does not match the value specified. If the values are identical,
350
- # the Blob service returns status code 412 (Precondition Failed).
351
- # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
352
- # in cross-origin resource sharing headers on the response.
353
- #
354
- # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
355
- #
356
- # Returns the renewed lease id
357
- #
358
- protected
359
- def renew_lease(container, blob, lease, options = {})
360
- query = { "comp" => "lease" }
361
- StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
362
-
363
- if blob
364
- uri = blob_uri(container, blob, query)
365
- else
366
- uri = container_uri(container, query)
367
- end
368
-
369
- headers = {}
370
- StorageService.with_header headers, "x-ms-lease-action", "renew"
371
- StorageService.with_header headers, "x-ms-lease-id", lease
372
- StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
373
- add_blob_conditional_headers options, headers
374
-
375
- response = call(:put, uri, nil, headers, options)
376
- response.headers["x-ms-lease-id"]
377
- end
378
-
379
- # Protected: Change the ID of an existing lease.
380
- #
381
- # ==== Attributes
382
- #
383
- # * +container+ - String. The container name.
384
- # * +blob+ - String. The blob name.
385
- # * +lease+ - String. The existing lease id.
386
- # * +proposed_lease+ - String. Proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request)
387
- # if the proposed lease ID is not in the correct format. (optional).
388
- # * +options+ - Hash. Optional parameters.
389
- #
390
- # ==== Options
391
- #
392
- # Accepted key/value pairs in options parameter are:
393
- # * +:timeout+ - Integer. A timeout in seconds.
394
- # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
395
- # in the analytics logs when storage analytics logging is enabled.
396
- # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to change the lease
397
- # only if the blob has been modified since the specified date/time. If the blob has not been modified,
398
- # the Blob service returns status code 412 (Precondition Failed).
399
- # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to change the lease
400
- # only if the blob has not been modified since the specified date/time. If the blob has been modified,
401
- # the Blob service returns status code 412 (Precondition Failed).
402
- # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to change the lease
403
- # only if the blob's ETag value matches the value specified. If the values do not match,
404
- # the Blob service returns status code 412 (Precondition Failed).
405
- # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to change the lease
406
- # only if the blob's ETag value does not match the value specified. If the values are identical,
407
- # the Blob service returns status code 412 (Precondition Failed).
408
- # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
409
- # in cross-origin resource sharing headers on the response.
410
- #
411
- # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
412
- #
413
- # Returns a String of the new unique lease id. While the lease is active, you must include the lease ID with any request
414
- # to write, or to renew, change, or release the lease.
415
- #
416
- protected
417
- def change_lease(container, blob, lease, proposed_lease, options = {})
418
- query = { "comp" => "lease" }
419
- StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
420
-
421
- if blob
422
- uri = blob_uri(container, blob, query)
423
- else
424
- uri = container_uri(container, query)
425
- end
426
-
427
- headers = {}
428
- StorageService.with_header headers, "x-ms-lease-action", "change"
429
- StorageService.with_header headers, "x-ms-lease-id", lease
430
- StorageService.with_header headers, "x-ms-proposed-lease-id", proposed_lease
431
- StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
432
- add_blob_conditional_headers options, headers
433
-
434
- response = call(:put, uri, nil, headers, options)
435
- response.headers["x-ms-lease-id"]
436
- end
437
-
438
- # Protected: Releases the lease. The lease may be released if the lease ID specified on the request matches that
439
- # associated with the container or blob. Releasing the lease allows another client to immediately acquire the lease for
440
- # the container or blob as soon as the release is complete.
441
- #
442
- # ==== Attributes
443
- #
444
- # * +container+ - String. The container name.
445
- # * +blob+ - String. The blob name.
446
- # * +lease+ - String. The lease id.
447
- # * +options+ - Hash. Optional parameters.
448
- #
449
- # ==== Options
450
- #
451
- # Accepted key/value pairs in options parameter are:
452
- # * +:timeout+ - Integer. A timeout in seconds.
453
- # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
454
- # in the analytics logs when storage analytics logging is enabled.
455
- # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to release the lease
456
- # only if the blob has been modified since the specified date/time. If the blob has not been modified,
457
- # the Blob service returns status code 412 (Precondition Failed).
458
- # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to release the lease
459
- # only if the blob has not been modified since the specified date/time. If the blob has been modified,
460
- # the Blob service returns status code 412 (Precondition Failed).
461
- # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to release the lease
462
- # only if the blob's ETag value matches the value specified. If the values do not match,
463
- # the Blob service returns status code 412 (Precondition Failed).
464
- # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to release the lease
465
- # only if the blob's ETag value does not match the value specified. If the values are identical,
466
- # the Blob service returns status code 412 (Precondition Failed).
467
- # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
468
- # in cross-origin resource sharing headers on the response.
469
- #
470
- # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
471
- #
472
- # Returns nil on success
473
- #
474
- protected
475
- def release_lease(container, blob, lease, options = {})
476
- query = { "comp" => "lease" }
477
- StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
478
-
479
- if blob
480
- uri = blob_uri(container, blob, query)
481
- else
482
- uri = container_uri(container, query)
483
- end
484
-
485
- headers = {}
486
- StorageService.with_header headers, "x-ms-lease-action", "release"
487
- StorageService.with_header headers, "x-ms-lease-id", lease
488
- StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
489
- add_blob_conditional_headers options, headers
490
-
491
- call(:put, uri, nil, headers, options)
492
- nil
493
- end
494
-
495
- # Protected: Breaks the lease, if the container or blob has an active lease. Once a lease is broken, it cannot be renewed. Any
496
- # authorized request can break the lease; the request is not required to specify a matching lease ID. When a
497
- # lease is broken, the lease break period is allowed to elapse, during which time no lease operation except
498
- # break and release can be performed on the container or blob. When a lease is successfully broken, the response indicates
499
- # the interval in seconds until a new lease can be acquired.
500
- #
501
- # A lease that has been broken can also be released, in which case another client may immediately acquire the
502
- # lease on the container or blob.
503
- #
504
- # ==== Attributes
505
- #
506
- # * +container+ - String. The container name.
507
- # * +blob+ - String. The blob name.
508
- # * +options+ - Hash. Optional parameters.
509
- #
510
- # ==== Options
511
- #
512
- # Accepted key/value pairs in options parameter are:
513
- # * +:break_period+ - Integer. The proposed duration of seconds that the lease should continue before it is
514
- # broken, between 0 and 60 seconds. This break period is only used if it is shorter than
515
- # the time remaining on the lease. If longer, the time remaining on the lease is used. A
516
- # new lease will not be available before the break period has expired, but the lease may
517
- # be held for longer than the break period.
518
- #
519
- # If this option is not used, a fixed-duration lease breaks after the remaining lease
520
- # period elapses, and an infinite lease breaks immediately.
521
- # * +:timeout+ - Integer. A timeout in seconds.
522
- # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
523
- # in the analytics logs when storage analytics logging is enabled.
524
- # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to acquire the lease
525
- # only if the blob has been modified since the specified date/time. If the blob has not been modified,
526
- # the Blob service returns status code 412 (Precondition Failed).
527
- # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to acquire the lease
528
- # only if the blob has not been modified since the specified date/time. If the blob has been modified,
529
- # the Blob service returns status code 412 (Precondition Failed).
530
- # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to acquire the lease
531
- # only if the blob's ETag value matches the value specified. If the values do not match,
532
- # the Blob service returns status code 412 (Precondition Failed).
533
- # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to acquire the lease
534
- # only if the blob's ETag value does not match the value specified. If the values are identical,
535
- # the Blob service returns status code 412 (Precondition Failed).
536
- # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
537
- # in cross-origin resource sharing headers on the response.
538
- #
539
- # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
540
- #
541
- # Returns an Integer of the remaining lease time. This value is the approximate time remaining in the lease
542
- # period, in seconds. This header is returned only for a successful request to break the lease. If the break
543
- # is immediate, 0 is returned.
544
- #
545
- protected
546
- def break_lease(container, blob, options = {})
547
- query = { "comp" => "lease" }
548
- StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
549
-
550
- if blob
551
- uri = blob_uri(container, blob, query)
552
- else
553
- uri = container_uri(container, query)
554
- end
555
-
556
- headers = {}
557
- StorageService.with_header headers, "x-ms-lease-action", "break"
558
- StorageService.with_header headers, "x-ms-lease-break-period", options[:break_period].to_s if options[:break_period]
559
- StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
560
- add_blob_conditional_headers options, headers
561
-
562
- response = call(:put, uri, nil, headers, options)
563
- response.headers["x-ms-lease-time"].to_i
564
- end
565
-
566
- # Protected: Generate the URI for the collection of containers.
567
- #
568
- # ==== Attributes
569
- #
570
- # * +query+ - A Hash of key => value query parameters.
571
- #
572
- # Returns a URI.
573
- #
574
- protected
575
- def containers_uri(query = {}, options = {})
576
- query = { "comp" => "list" }.merge(query)
577
- generate_uri("", query, options)
578
- end
579
-
580
- # Protected: Generate the URI for a specific container.
581
- #
582
- # ==== Attributes
583
- #
584
- # * +name+ - The container name. If this is a URI, we just return this.
585
- # * +query+ - A Hash of key => value query parameters.
586
- #
587
- # Returns a URI.
588
- #
589
- protected
590
- def container_uri(name, query = {}, options = {})
591
- return name if name.kind_of? ::URI
592
- query = { "restype" => "container" }.merge(query)
593
- generate_uri(name, query, options)
594
- end
595
-
596
- # Protected: Generate the URI for a specific Blob.
597
- #
598
- # ==== Attributes
599
- #
600
- # * +container_name+ - String representing the name of the container.
601
- # * +blob_name+ - String representing the name of the blob.
602
- # * +query+ - A Hash of key => value query parameters.
603
- #
604
- # Returns a URI.
605
- #
606
- protected
607
- def blob_uri(container_name, blob_name, query = {}, options = {})
608
- if container_name.nil? || container_name.empty?
609
- path = blob_name
610
- else
611
- path = ::File.join(container_name, blob_name)
612
- end
613
- options = { encode: true }.merge(options)
614
- generate_uri(path, query, options)
615
- end
616
-
617
- # Adds conditional header with required condition
618
- #
619
- # headers - A Hash of HTTP headers
620
- # options - A Hash of condition name/value pairs
621
- #
622
- protected
623
- def add_blob_conditional_headers(options, headers)
624
- return unless options
625
-
626
- # Common conditional headers for blobs: https://msdn.microsoft.com/en-us/library/azure/dd179371.aspx
627
- StorageService.with_header headers, "If-Modified-Since", options[:if_modified_since]
628
- StorageService.with_header headers, "If-Unmodified-Since", options[:if_unmodified_since]
629
- StorageService.with_header headers, "If-Match", options[:if_match]
630
- StorageService.with_header headers, "If-None-Match", options[:if_none_match]
631
-
632
- # Conditional headers for copying blob
633
- StorageService.with_header headers, "If-Modified-Since", options[:dest_if_modified_since]
634
- StorageService.with_header headers, "If-Unmodified-Since", options[:dest_if_unmodified_since]
635
- StorageService.with_header headers, "If-Match", options[:dest_if_match]
636
- StorageService.with_header headers, "If-None-Match", options[:dest_if_none_match]
637
- StorageService.with_header headers, "x-ms-source-if-modified-since", options[:source_if_modified_since]
638
- StorageService.with_header headers, "x-ms-source-if-unmodified-since", options[:source_if_unmodified_since]
639
- StorageService.with_header headers, "x-ms-source-if-match", options[:source_if_match]
640
- StorageService.with_header headers, "x-ms-source-if-none-match", options[:source_if_none_match]
641
-
642
- # Conditional headers for page blob
643
- StorageService.with_header headers, "x-ms-if-sequence-number-le", options[:if_sequence_number_le] if options[:if_sequence_number_le]
644
- StorageService.with_header headers, "x-ms-if-sequence-number-lt", options[:if_sequence_number_lt] if options[:if_sequence_number_lt]
645
- StorageService.with_header headers, "x-ms-if-sequence-number-eq", options[:if_sequence_number_eq] if options[:if_sequence_number_eq]
646
-
647
- # Conditional headers for append blob
648
- StorageService.with_header headers, "x-ms-blob-condition-maxsize", options[:max_size]
649
- StorageService.with_header headers, "x-ms-blob-condition-appendpos", options[:append_position]
650
- end
651
-
652
- # Get the content type according to the blob content type header and request body.
653
- #
654
- # headers - The request body
655
- # content_type - The request content type
656
- protected
657
- def get_or_apply_content_type(body, content_type = nil)
658
- unless body.nil?
659
- if (body.is_a? String) && body.encoding.to_s != "ASCII_8BIT" && !body.empty?
660
- if content_type.nil?
661
- content_type = "text/plain; charset=#{body.encoding}"
662
- else
663
- # Force the request.body to the content encoding of specified in the header
664
- charset = parse_charset_from_content_type(content_type)
665
- body.force_encoding(charset) if charset
666
- end
667
- else
668
- # It is either that the body is not a string, or that the body's encoding is ASCII_8BIT, which is a binary
669
- # In this case, set the content type to be default content-type
670
- content_type = Default::CONTENT_TYPE_VALUE unless content_type
671
- end
672
- end
673
- content_type
674
- end
675
- end
676
- end
677
- end
678
-
679
- Azure::Storage::BlobService = Azure::Storage::Blob::BlobService
1
+ # frozen_string_literal: true
2
+
3
+ #-------------------------------------------------------------------------
4
+ # # Copyright (c) Microsoft and contributors. All rights reserved.
5
+ #
6
+ # The MIT License(MIT)
7
+
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files(the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions :
14
+
15
+ # The above copyright notice and this permission notice shall be included in
16
+ # all copies or substantial portions of the Software.
17
+
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24
+ # THE SOFTWARE.
25
+ #--------------------------------------------------------------------------
26
+ require "azure/storage/blob/page"
27
+ require "azure/storage/blob/block"
28
+ require "azure/storage/blob/append"
29
+ require "azure/storage/blob/blob"
30
+
31
+ module Azure::Storage
32
+ include Azure::Storage::Common::Service
33
+ StorageService = Azure::Storage::Common::Service::StorageService
34
+
35
+ module Blob
36
+ class BlobService < StorageService
37
+ include Azure::Storage::Common::Core::Utility
38
+ include Azure::Storage::Blob
39
+ include Azure::Storage::Blob::Container
40
+
41
+ class << self
42
+ # Public: Creates an instance of [Azure::Storage::Blob::BlobService]
43
+ #
44
+ # ==== Attributes
45
+ #
46
+ # * +options+ - Hash. Optional parameters.
47
+ #
48
+ # ==== Options
49
+ #
50
+ # Accepted key/value pairs in options parameter are:
51
+ #
52
+ # * +:use_development_storage+ - TrueClass|FalseClass. Whether to use storage emulator.
53
+ # * +:development_storage_proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
54
+ # * +:storage_account_name+ - String. The name of the storage account.
55
+ # * +:storage_access_key+ - Base64 String. The access key of the storage account.
56
+ # * +:storage_sas_token+ - String. The signed access signature for the storage account or one of its service.
57
+ # * +:storage_blob_host+ - String. Specified Blob service endpoint or hostname
58
+ # * +:storage_dns_suffix+ - String. The suffix of a regional Storage Service, to
59
+ # * +:default_endpoints_protocol+ - String. http or https
60
+ # * +:use_path_style_uri+ - String. Whether use path style URI for specified endpoints
61
+ # * +:ca_file+ - String. File path of the CA file if having issue with SSL
62
+ # * +:user_agent_prefix+ - String. The user agent prefix that can identify the application calls the library
63
+ #
64
+ # The valid set of options include:
65
+ # * Storage Emulator: +:use_development_storage+ required, +:development_storage_proxy_uri+ optionally
66
+ # * Storage account name and key: +:storage_account_name+ and +:storage_access_key+ required, set +:storage_dns_suffix+ necessarily
67
+ # * Storage account name and SAS token: +:storage_account_name+ and +:storage_sas_token+ required, set +:storage_dns_suffix+ necessarily
68
+ # * Specified hosts and SAS token: At least one of the service host and SAS token. It's up to user to ensure the SAS token is suitable for the serivce
69
+ # * Anonymous Blob: only +:storage_blob_host+, if it is to only access blobs within a container
70
+ #
71
+ # Additional notes:
72
+ # * Specified hosts can be set when use account name with access key or sas token
73
+ # * +:default_endpoints_protocol+ can be set if the scheme is not specified in hosts
74
+ # * Storage emulator always use path style URI
75
+ # * +:ca_file+ is independent.
76
+ #
77
+ # When empty options are given, it will try to read settings from Environment Variables. Refer to [Azure::Storage::Common::ClientOptions.env_vars_mapping] for the mapping relationship
78
+ #
79
+ # @return [Azure::Storage::Blob::BlobService]
80
+ def create(options = {}, &block)
81
+ service_options = { client: Azure::Storage::Common::Client::create(options, &block), api_version: Azure::Storage::Blob::Default::STG_VERSION }
82
+ service_options[:user_agent_prefix] = options[:user_agent_prefix] if options[:user_agent_prefix]
83
+ Azure::Storage::Blob::BlobService.new(service_options, &block)
84
+ end
85
+
86
+ # Public: Creates an instance of [Azure::Storage::Blob::BlobService] with Storage Emulator
87
+ #
88
+ # ==== Attributes
89
+ #
90
+ # * +proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
91
+ #
92
+ # @return [Azure::Storage::Blob::BlobService]
93
+ def create_development(proxy_uri = nil, &block)
94
+ service_options = { client: Azure::Storage::Common::Client::create_development(proxy_uri, &block), api_version: Azure::Storage::Blob::Default::STG_VERSION }
95
+ Azure::Storage::Blob::BlobService.new(service_options, &block)
96
+ end
97
+
98
+ # Public: Creates an instance of [Azure::Storage::Blob::BlobService] from Environment Variables
99
+ #
100
+ # @return [Azure::Storage::Blob::BlobService]
101
+ def create_from_env(&block)
102
+ service_options = { client: Azure::Storage::Common::Client::create_from_env(&block), api_version: Azure::Storage::Blob::Default::STG_VERSION }
103
+ Azure::Storage::Blob::BlobService.new(service_options, &block)
104
+ end
105
+
106
+ # Public: Creates an instance of [Azure::Storage::Blob::BlobService] from Environment Variables
107
+ #
108
+ # ==== Attributes
109
+ #
110
+ # * +connection_string+ - String. Please refer to https://azure.microsoft.com/en-us/documentation/articles/storage-configure-connection-string/.
111
+ #
112
+ # @return [Azure::Storage::Blob::BlobService]
113
+ def create_from_connection_string(connection_string, &block)
114
+ service_options = { client: Azure::Storage::Common::Client::create_from_connection_string(connection_string, &block), api_version: Azure::Storage::Blob::Default::STG_VERSION }
115
+ Azure::Storage::Blob::BlobService.new(service_options, &block)
116
+ end
117
+ end
118
+
119
+ # Public: Initializes an instance of [Azure::Storage::Blob::BlobService]
120
+ #
121
+ # ==== Attributes
122
+ #
123
+ # * +options+ - Hash. Optional parameters.
124
+ #
125
+ # ==== Options
126
+ #
127
+ # Accepted key/value pairs in options parameter are:
128
+ #
129
+ # * +:use_development_storage+ - TrueClass|FalseClass. Whether to use storage emulator.
130
+ # * +:development_storage_proxy_uri+ - String. Used with +:use_development_storage+ if emulator is hosted other than localhost.
131
+ # * +:storage_connection_string+ - String. The storage connection string.
132
+ # * +:storage_account_name+ - String. The name of the storage account.
133
+ # * +:storage_access_key+ - Base64 String. The access key of the storage account.
134
+ # * +:storage_sas_token+ - String. The signed access signature for the storage account or one of its service.
135
+ # * +:storage_blob_host+ - String. Specified Blob serivce endpoint or hostname
136
+ # * +:storage_dns_suffix+ - String. The suffix of a regional Storage Serivce, to
137
+ # * +:default_endpoints_protocol+ - String. http or https
138
+ # * +:use_path_style_uri+ - String. Whether use path style URI for specified endpoints
139
+ # * +:ca_file+ - String. File path of the CA file if having issue with SSL
140
+ # * +:ssl_version+ - Symbol. The ssl version to be used, sample: :TLSv1_1, :TLSv1_2, for the details, see https://github.com/ruby/openssl/blob/master/lib/openssl/ssl.rb
141
+ # * +:ssl_min_version+ - Symbol. The min ssl version supported, only supported in Ruby 2.5+
142
+ # * +:ssl_max_version+ - Symbol. The max ssl version supported, only supported in Ruby 2.5+
143
+ # * +:user_agent_prefix+ - String. The user agent prefix that can identify the application calls the library
144
+ # * +:client+ - Azure::Storage::Common::Client. The common client used to initalize the service.
145
+ #
146
+ # The valid set of options include:
147
+ # * Storage Emulator: +:use_development_storage+ required, +:development_storage_proxy_uri+ optionally
148
+ # * Storage account name and key: +:storage_account_name+ and +:storage_access_key+ required, set +:storage_dns_suffix+ necessarily
149
+ # * Storage account name and SAS token: +:storage_account_name+ and +:storage_sas_token+ required, set +:storage_dns_suffix+ necessarily
150
+ # * Specified hosts and SAS token: At least one of the service host and SAS token. It's up to user to ensure the SAS token is suitable for the serivce
151
+ # * Azure::Storage::Common::Client: The common client used to initalize the service. This client can be initalized and used repeatedly.
152
+ # * Anonymous Blob: only +:storage_blob_host+, if it is to only access blobs within a container
153
+ #
154
+ # Additional notes:
155
+ # * Specified hosts can be set when use account name with access key or sas token
156
+ # * +:default_endpoints_protocol+ can be set if the scheme is not specified in hosts
157
+ # * Storage emulator always use path style URI
158
+ # * +:ca_file+ is independent.
159
+ #
160
+ # When empty options are given, it will try to read settings from Environment Variables. Refer to [Azure::Storage::Common::ClientOptions.env_vars_mapping] for the mapping relationship
161
+ def initialize(options = {}, &block)
162
+ service_options = options.clone
163
+ client_config = service_options[:client] ||= Azure::Storage::Common::Client::create(service_options, &block)
164
+ @user_agent_prefix = service_options[:user_agent_prefix] if service_options[:user_agent_prefix]
165
+ @api_version = service_options[:api_version] || Azure::Storage::Blob::Default::STG_VERSION
166
+ signer = service_options[:signer] || client_config.signer || Azure::Storage::Common::Core::Auth::SharedKey.new(client_config.storage_account_name, client_config.storage_access_key)
167
+ signer.api_ver = @api_version if signer.is_a? Azure::Storage::Common::Core::Auth::SharedAccessSignatureSigner
168
+ super(signer, client_config.storage_account_name, service_options, &block)
169
+ @storage_service_host[:primary] = client.storage_blob_host
170
+ @storage_service_host[:secondary] = client.storage_blob_host true
171
+ end
172
+
173
+ def call(method, uri, body = nil, headers = {}, options = {})
174
+ content_type = get_or_apply_content_type(body, headers[Azure::Storage::Common::HeaderConstants::BLOB_CONTENT_TYPE])
175
+ headers[Azure::Storage::Common::HeaderConstants::BLOB_CONTENT_TYPE] = content_type if content_type
176
+
177
+ headers["x-ms-version"] = @api_version ? @api_version : Default::STG_VERSION
178
+ headers["User-Agent"] = @user_agent_prefix ? "#{@user_agent_prefix}; #{Default::USER_AGENT}" : Default::USER_AGENT
179
+ response = super
180
+
181
+ # Force the response.body to the content charset of specified in the header.
182
+ # Content-Type is echo'd back for the blob and is used to store the encoding of the octet stream
183
+ if !response.nil? && !response.body.nil? && response.headers["Content-Type"]
184
+ charset = parse_charset_from_content_type(response.headers["Content-Type"])
185
+ response.body.force_encoding(charset) if charset && charset.length > 0
186
+ end
187
+
188
+ response
189
+ end
190
+
191
+ # Public: Get a list of Containers from the server.
192
+ #
193
+ # ==== Attributes
194
+ #
195
+ # * +options+ - Hash. Optional parameters.
196
+ #
197
+ # ==== Options
198
+ #
199
+ # Accepted key/value pairs in options parameter are:
200
+ # * +:prefix+ - String. Filters the results to return only containers
201
+ # whose name begins with the specified prefix. (optional)
202
+ #
203
+ # * +:marker+ - String. An identifier the specifies the portion of the
204
+ # list to be returned. This value comes from the property
205
+ # Azure::Storage::Common::EnumerationResults.continuation_token when there
206
+ # are more containers available than were returned. The
207
+ # marker value may then be used here to request the next set
208
+ # of list items. (optional)
209
+ #
210
+ # * +:max_results+ - Integer. Specifies the maximum number of containers to return.
211
+ # If max_results is not specified, or is a value greater than
212
+ # 5,000, the server will return up to 5,000 items. If it is set
213
+ # to a value less than or equal to zero, the server will return
214
+ # status code 400 (Bad Request). (optional)
215
+ #
216
+ # * +:metadata+ - Boolean. Specifies whether or not to return the container metadata.
217
+ # (optional, Default=false)
218
+ #
219
+ # * +:timeout+ - Integer. A timeout in seconds.
220
+ #
221
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
222
+ # in the analytics logs when storage analytics logging is enabled.
223
+ #
224
+ # * +:location_mode+ - LocationMode. Specifies the location mode used to decide
225
+ # which location the request should be sent to.
226
+ #
227
+ # See: https://msdn.microsoft.com/en-us/library/azure/dd179352.aspx
228
+ #
229
+ # NOTE: Metadata requested with the :metadata parameter must have been stored in
230
+ # accordance with the naming restrictions imposed by the 2009-09-19 version of the Blob
231
+ # service. Beginning with that version, all metadata names must adhere to the naming
232
+ # conventions for C# identifiers. See: https://msdn.microsoft.com/en-us/library/aa664670(VS.71).aspx
233
+ #
234
+ # Any metadata with invalid names which were previously stored, will be returned with the
235
+ # key "x-ms-invalid-name" in the metadata hash. This may contain multiple values and be an
236
+ # Array (vs a String if it only contains a single value).
237
+ #
238
+ # Returns an Azure::Storage::Common::EnumerationResults
239
+ #
240
+ def list_containers(options = {})
241
+ query = {}
242
+ if options
243
+ StorageService.with_query query, "prefix", options[:prefix]
244
+ StorageService.with_query query, "marker", options[:marker]
245
+ StorageService.with_query query, "maxresults", options[:max_results].to_s if options[:max_results]
246
+ StorageService.with_query query, "include", "metadata" if options[:metadata] == true
247
+ StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
248
+ end
249
+
250
+ options[:request_location_mode] = Azure::Storage::Common::RequestLocationMode::PRIMARY_OR_SECONDARY
251
+ uri = containers_uri(query, options)
252
+ response = call(:get, uri, nil, {}, options)
253
+
254
+ Serialization.container_enumeration_results_from_xml(response.body)
255
+ end
256
+
257
+ # Public: Obtain a user delegation key for the purpose of signing SAS tokens.
258
+ #
259
+ # ==== Attributes
260
+ #
261
+ # * +start+ - Time. The start time for the user delegation SAS.
262
+ # * +expiry+ - Time. The expiry time of user delegation SAS.
263
+ #
264
+ # See: https://docs.microsoft.com/en-us/rest/api/storageservices/get-user-delegation-key
265
+ #
266
+ # NOTE: A token credential must be present on the service object for this request to succeed.
267
+ # The start and expiry times must be within 7 days of the current time.
268
+ #
269
+ # Returns an Azure::Storage::Common::UserDelegationKey
270
+ #
271
+ def get_user_delegation_key(start, expiry)
272
+ max_delegation_time = Time.now + BlobConstants::MAX_USER_DELEGATION_KEY_SECONDS
273
+ raise ArgumentError, "Start time must be before #{max_delegation_time}" if start > max_delegation_time
274
+ raise ArgumentError, "Expiry time must be before #{max_delegation_time}" if expiry > max_delegation_time
275
+ raise ArgumentError, "Start time must be before expiry time" if start >= expiry
276
+
277
+ body = Serialization.key_info_to_xml(start, expiry)
278
+
279
+ response = call(:post, user_delegation_key_uri, body)
280
+
281
+ Serialization.user_delegation_key_from_xml(response.body)
282
+ end
283
+
284
+ # Protected: Establishes an exclusive write lock on a container or a blob. The lock duration can be 15 to 60 seconds, or can be infinite.
285
+ # To write to a locked container or blob, a client must provide a lease ID.
286
+ #
287
+ # ==== Attributes
288
+ #
289
+ # * +container+ - String. The container name.
290
+ # * +blob+ - String. The blob name.
291
+ # * +options+ - Hash. Optional parameters.
292
+ #
293
+ # ==== Options
294
+ #
295
+ # Accepted key/value pairs in options parameter are:
296
+ # * +:duration+ - Integer. Default -1. Specifies the duration of the lease, in seconds, or negative one (-1)
297
+ # for a lease that never expires. A non-infinite lease can be between 15 and 60 seconds. (optional)
298
+ # * +:proposed_lease_id+ - String. Proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request)
299
+ # if the proposed lease ID is not in the correct format. (optional)
300
+ # * +:timeout+ - Integer. A timeout in seconds.
301
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
302
+ # in the analytics logs when storage analytics logging is enabled.
303
+ # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to acquire the lease
304
+ # only if the blob has been modified since the specified date/time. If the blob has not been modified,
305
+ # the Blob service returns status code 412 (Precondition Failed).
306
+ # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to acquire the lease
307
+ # only if the blob has not been modified since the specified date/time. If the blob has been modified,
308
+ # the Blob service returns status code 412 (Precondition Failed).
309
+ # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to acquire the lease
310
+ # only if the blob's ETag value matches the value specified. If the values do not match,
311
+ # the Blob service returns status code 412 (Precondition Failed).
312
+ # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to acquire the lease
313
+ # only if the blob's ETag value does not match the value specified. If the values are identical,
314
+ # the Blob service returns status code 412 (Precondition Failed).
315
+ # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
316
+ # in cross-origin resource sharing headers on the response.
317
+ #
318
+ # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
319
+ #
320
+ # Returns a String of the new unique lease id. While the lease is active, you must include the lease ID with any request
321
+ # to write, or to renew, change, or release the lease.
322
+ #
323
+ protected
324
+ def acquire_lease(container, blob, options = {})
325
+ query = { "comp" => "lease" }
326
+ StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
327
+
328
+ if blob
329
+ uri = blob_uri(container, blob, query)
330
+ else
331
+ uri = container_uri(container, query)
332
+ end
333
+
334
+ duration = -1
335
+ duration = options[:duration] if options[:duration]
336
+
337
+ headers = {}
338
+ StorageService.with_header headers, "x-ms-lease-action", "acquire"
339
+ StorageService.with_header headers, "x-ms-lease-duration", duration.to_s if duration
340
+ StorageService.with_header headers, "x-ms-proposed-lease-id", options[:proposed_lease_id]
341
+ StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
342
+ add_blob_conditional_headers options, headers
343
+
344
+ response = call(:put, uri, nil, headers, options)
345
+ response.headers["x-ms-lease-id"]
346
+ end
347
+
348
+ # Protected: Renews the lease. The lease can be renewed if the lease ID specified on the request matches that
349
+ # associated with the blob. Note that the lease may be renewed even if it has expired as long as the container or blob
350
+ # has not been modified or leased again since the expiration of that lease. When you renew a lease, the
351
+ # lease duration clock resets.
352
+ #
353
+ # ==== Attributes
354
+ #
355
+ # * +container+ - String. The container name.
356
+ # * +blob+ - String. The blob name.
357
+ # * +lease+ - String. The lease id
358
+ # * +options+ - Hash. Optional parameters.
359
+ #
360
+ # ==== Options
361
+ #
362
+ # Accepted key/value pairs in options parameter are:
363
+ # * +:timeout+ - Integer. A timeout in seconds.
364
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
365
+ # in the analytics logs when storage analytics logging is enabled.
366
+ # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to renew the lease
367
+ # only if the blob has been modified since the specified date/time. If the blob has not been modified,
368
+ # the Blob service returns status code 412 (Precondition Failed).
369
+ # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to renew the lease
370
+ # only if the blob has not been modified since the specified date/time. If the blob has been modified,
371
+ # the Blob service returns status code 412 (Precondition Failed).
372
+ # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to renew the lease
373
+ # only if the blob's ETag value matches the value specified. If the values do not match,
374
+ # the Blob service returns status code 412 (Precondition Failed).
375
+ # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to renew the lease
376
+ # only if the blob's ETag value does not match the value specified. If the values are identical,
377
+ # the Blob service returns status code 412 (Precondition Failed).
378
+ # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
379
+ # in cross-origin resource sharing headers on the response.
380
+ #
381
+ # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
382
+ #
383
+ # Returns the renewed lease id
384
+ #
385
+ protected
386
+ def renew_lease(container, blob, lease, options = {})
387
+ query = { "comp" => "lease" }
388
+ StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
389
+
390
+ if blob
391
+ uri = blob_uri(container, blob, query)
392
+ else
393
+ uri = container_uri(container, query)
394
+ end
395
+
396
+ headers = {}
397
+ StorageService.with_header headers, "x-ms-lease-action", "renew"
398
+ StorageService.with_header headers, "x-ms-lease-id", lease
399
+ StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
400
+ add_blob_conditional_headers options, headers
401
+
402
+ response = call(:put, uri, nil, headers, options)
403
+ response.headers["x-ms-lease-id"]
404
+ end
405
+
406
+ # Protected: Change the ID of an existing lease.
407
+ #
408
+ # ==== Attributes
409
+ #
410
+ # * +container+ - String. The container name.
411
+ # * +blob+ - String. The blob name.
412
+ # * +lease+ - String. The existing lease id.
413
+ # * +proposed_lease+ - String. Proposed lease ID, in a GUID string format. The Blob service returns 400 (Invalid request)
414
+ # if the proposed lease ID is not in the correct format. (optional).
415
+ # * +options+ - Hash. Optional parameters.
416
+ #
417
+ # ==== Options
418
+ #
419
+ # Accepted key/value pairs in options parameter are:
420
+ # * +:timeout+ - Integer. A timeout in seconds.
421
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
422
+ # in the analytics logs when storage analytics logging is enabled.
423
+ # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to change the lease
424
+ # only if the blob has been modified since the specified date/time. If the blob has not been modified,
425
+ # the Blob service returns status code 412 (Precondition Failed).
426
+ # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to change the lease
427
+ # only if the blob has not been modified since the specified date/time. If the blob has been modified,
428
+ # the Blob service returns status code 412 (Precondition Failed).
429
+ # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to change the lease
430
+ # only if the blob's ETag value matches the value specified. If the values do not match,
431
+ # the Blob service returns status code 412 (Precondition Failed).
432
+ # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to change the lease
433
+ # only if the blob's ETag value does not match the value specified. If the values are identical,
434
+ # the Blob service returns status code 412 (Precondition Failed).
435
+ # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
436
+ # in cross-origin resource sharing headers on the response.
437
+ #
438
+ # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
439
+ #
440
+ # Returns a String of the new unique lease id. While the lease is active, you must include the lease ID with any request
441
+ # to write, or to renew, change, or release the lease.
442
+ #
443
+ protected
444
+ def change_lease(container, blob, lease, proposed_lease, options = {})
445
+ query = { "comp" => "lease" }
446
+ StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
447
+
448
+ if blob
449
+ uri = blob_uri(container, blob, query)
450
+ else
451
+ uri = container_uri(container, query)
452
+ end
453
+
454
+ headers = {}
455
+ StorageService.with_header headers, "x-ms-lease-action", "change"
456
+ StorageService.with_header headers, "x-ms-lease-id", lease
457
+ StorageService.with_header headers, "x-ms-proposed-lease-id", proposed_lease
458
+ StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
459
+ add_blob_conditional_headers options, headers
460
+
461
+ response = call(:put, uri, nil, headers, options)
462
+ response.headers["x-ms-lease-id"]
463
+ end
464
+
465
+ # Protected: Releases the lease. The lease may be released if the lease ID specified on the request matches that
466
+ # associated with the container or blob. Releasing the lease allows another client to immediately acquire the lease for
467
+ # the container or blob as soon as the release is complete.
468
+ #
469
+ # ==== Attributes
470
+ #
471
+ # * +container+ - String. The container name.
472
+ # * +blob+ - String. The blob name.
473
+ # * +lease+ - String. The lease id.
474
+ # * +options+ - Hash. Optional parameters.
475
+ #
476
+ # ==== Options
477
+ #
478
+ # Accepted key/value pairs in options parameter are:
479
+ # * +:timeout+ - Integer. A timeout in seconds.
480
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
481
+ # in the analytics logs when storage analytics logging is enabled.
482
+ # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to release the lease
483
+ # only if the blob has been modified since the specified date/time. If the blob has not been modified,
484
+ # the Blob service returns status code 412 (Precondition Failed).
485
+ # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to release the lease
486
+ # only if the blob has not been modified since the specified date/time. If the blob has been modified,
487
+ # the Blob service returns status code 412 (Precondition Failed).
488
+ # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to release the lease
489
+ # only if the blob's ETag value matches the value specified. If the values do not match,
490
+ # the Blob service returns status code 412 (Precondition Failed).
491
+ # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to release the lease
492
+ # only if the blob's ETag value does not match the value specified. If the values are identical,
493
+ # the Blob service returns status code 412 (Precondition Failed).
494
+ # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
495
+ # in cross-origin resource sharing headers on the response.
496
+ #
497
+ # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
498
+ #
499
+ # Returns nil on success
500
+ #
501
+ protected
502
+ def release_lease(container, blob, lease, options = {})
503
+ query = { "comp" => "lease" }
504
+ StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
505
+
506
+ if blob
507
+ uri = blob_uri(container, blob, query)
508
+ else
509
+ uri = container_uri(container, query)
510
+ end
511
+
512
+ headers = {}
513
+ StorageService.with_header headers, "x-ms-lease-action", "release"
514
+ StorageService.with_header headers, "x-ms-lease-id", lease
515
+ StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
516
+ add_blob_conditional_headers options, headers
517
+
518
+ call(:put, uri, nil, headers, options)
519
+ nil
520
+ end
521
+
522
+ # Protected: Breaks the lease, if the container or blob has an active lease. Once a lease is broken, it cannot be renewed. Any
523
+ # authorized request can break the lease; the request is not required to specify a matching lease ID. When a
524
+ # lease is broken, the lease break period is allowed to elapse, during which time no lease operation except
525
+ # break and release can be performed on the container or blob. When a lease is successfully broken, the response indicates
526
+ # the interval in seconds until a new lease can be acquired.
527
+ #
528
+ # A lease that has been broken can also be released, in which case another client may immediately acquire the
529
+ # lease on the container or blob.
530
+ #
531
+ # ==== Attributes
532
+ #
533
+ # * +container+ - String. The container name.
534
+ # * +blob+ - String. The blob name.
535
+ # * +options+ - Hash. Optional parameters.
536
+ #
537
+ # ==== Options
538
+ #
539
+ # Accepted key/value pairs in options parameter are:
540
+ # * +:break_period+ - Integer. The proposed duration of seconds that the lease should continue before it is
541
+ # broken, between 0 and 60 seconds. This break period is only used if it is shorter than
542
+ # the time remaining on the lease. If longer, the time remaining on the lease is used. A
543
+ # new lease will not be available before the break period has expired, but the lease may
544
+ # be held for longer than the break period.
545
+ #
546
+ # If this option is not used, a fixed-duration lease breaks after the remaining lease
547
+ # period elapses, and an infinite lease breaks immediately.
548
+ # * +:timeout+ - Integer. A timeout in seconds.
549
+ # * +:request_id+ - String. Provides a client-generated, opaque value with a 1 KB character limit that is recorded
550
+ # in the analytics logs when storage analytics logging is enabled.
551
+ # * +:if_modified_since+ - String. A DateTime value. Specify this conditional header to acquire the lease
552
+ # only if the blob has been modified since the specified date/time. If the blob has not been modified,
553
+ # the Blob service returns status code 412 (Precondition Failed).
554
+ # * +:if_unmodified_since+ - String. A DateTime value. Specify this conditional header to acquire the lease
555
+ # only if the blob has not been modified since the specified date/time. If the blob has been modified,
556
+ # the Blob service returns status code 412 (Precondition Failed).
557
+ # * +:if_match+ - String. An ETag value. Specify an ETag value for this conditional header to acquire the lease
558
+ # only if the blob's ETag value matches the value specified. If the values do not match,
559
+ # the Blob service returns status code 412 (Precondition Failed).
560
+ # * +:if_none_match+ - String. An ETag value. Specify an ETag value for this conditional header to acquire the lease
561
+ # only if the blob's ETag value does not match the value specified. If the values are identical,
562
+ # the Blob service returns status code 412 (Precondition Failed).
563
+ # * +:origin+ - String. Optional. Specifies the origin from which the request is issued. The presence of this header results
564
+ # in cross-origin resource sharing headers on the response.
565
+ #
566
+ # See http://msdn.microsoft.com/en-us/library/azure/ee691972.aspx
567
+ #
568
+ # Returns an Integer of the remaining lease time. This value is the approximate time remaining in the lease
569
+ # period, in seconds. This header is returned only for a successful request to break the lease. If the break
570
+ # is immediate, 0 is returned.
571
+ #
572
+ protected
573
+ def break_lease(container, blob, options = {})
574
+ query = { "comp" => "lease" }
575
+ StorageService.with_query query, "timeout", options[:timeout].to_s if options[:timeout]
576
+
577
+ if blob
578
+ uri = blob_uri(container, blob, query)
579
+ else
580
+ uri = container_uri(container, query)
581
+ end
582
+
583
+ headers = {}
584
+ StorageService.with_header headers, "x-ms-lease-action", "break"
585
+ StorageService.with_header headers, "x-ms-lease-break-period", options[:break_period].to_s if options[:break_period]
586
+ StorageService.with_header headers, "Origin", options[:origin].to_s if options[:origin]
587
+ add_blob_conditional_headers options, headers
588
+
589
+ response = call(:put, uri, nil, headers, options)
590
+ response.headers["x-ms-lease-time"].to_i
591
+ end
592
+
593
+ # Protected: Generate the URI for the collection of containers.
594
+ #
595
+ # ==== Attributes
596
+ #
597
+ # * +query+ - A Hash of key => value query parameters.
598
+ #
599
+ # Returns a URI.
600
+ #
601
+ protected
602
+ def containers_uri(query = {}, options = {})
603
+ query = { "comp" => "list" }.merge(query)
604
+ generate_uri("", query, options)
605
+ end
606
+
607
+ # Protected: Generate the URI for the user delegation key.
608
+ #
609
+ # ==== Attributes
610
+ #
611
+ # * +query+ - A Hash of key => value query parameters.
612
+ #
613
+ # Returns a URI.
614
+ #
615
+ protected
616
+ def user_delegation_key_uri(query = {}, options = {})
617
+ query = { :restype => "service", :comp => "userdelegationkey" }.merge(query)
618
+ generate_uri("", query, options)
619
+ end
620
+
621
+ # Protected: Generate the URI for a specific container.
622
+ #
623
+ # ==== Attributes
624
+ #
625
+ # * +name+ - The container name. If this is a URI, we just return this.
626
+ # * +query+ - A Hash of key => value query parameters.
627
+ #
628
+ # Returns a URI.
629
+ #
630
+ protected
631
+ def container_uri(name, query = {}, options = {})
632
+ return name if name.kind_of? ::URI
633
+ query = { "restype" => "container" }.merge(query)
634
+ generate_uri(name, query, options)
635
+ end
636
+
637
+ # Protected: Generate the URI for a specific Blob.
638
+ #
639
+ # ==== Attributes
640
+ #
641
+ # * +container_name+ - String representing the name of the container.
642
+ # * +blob_name+ - String representing the name of the blob.
643
+ # * +query+ - A Hash of key => value query parameters.
644
+ #
645
+ # Returns a URI.
646
+ #
647
+ protected
648
+ def blob_uri(container_name, blob_name, query = {}, options = {})
649
+ if container_name.nil? || container_name.empty?
650
+ path = blob_name
651
+ else
652
+ path = ::File.join(container_name, blob_name)
653
+ end
654
+ options = { encode: true }.merge(options)
655
+ generate_uri(path, query, options)
656
+ end
657
+
658
+ # Adds conditional header with required condition
659
+ #
660
+ # headers - A Hash of HTTP headers
661
+ # options - A Hash of condition name/value pairs
662
+ #
663
+ protected
664
+ def add_blob_conditional_headers(options, headers)
665
+ return unless options
666
+
667
+ # Common conditional headers for blobs: https://msdn.microsoft.com/en-us/library/azure/dd179371.aspx
668
+ StorageService.with_header headers, "If-Modified-Since", options[:if_modified_since]
669
+ StorageService.with_header headers, "If-Unmodified-Since", options[:if_unmodified_since]
670
+ StorageService.with_header headers, "If-Match", options[:if_match]
671
+ StorageService.with_header headers, "If-None-Match", options[:if_none_match]
672
+
673
+ # Conditional headers for copying blob
674
+ StorageService.with_header headers, "If-Modified-Since", options[:dest_if_modified_since]
675
+ StorageService.with_header headers, "If-Unmodified-Since", options[:dest_if_unmodified_since]
676
+ StorageService.with_header headers, "If-Match", options[:dest_if_match]
677
+ StorageService.with_header headers, "If-None-Match", options[:dest_if_none_match]
678
+ StorageService.with_header headers, "x-ms-source-if-modified-since", options[:source_if_modified_since]
679
+ StorageService.with_header headers, "x-ms-source-if-unmodified-since", options[:source_if_unmodified_since]
680
+ StorageService.with_header headers, "x-ms-source-if-match", options[:source_if_match]
681
+ StorageService.with_header headers, "x-ms-source-if-none-match", options[:source_if_none_match]
682
+
683
+ # Conditional headers for page blob
684
+ StorageService.with_header headers, "x-ms-if-sequence-number-le", options[:if_sequence_number_le] if options[:if_sequence_number_le]
685
+ StorageService.with_header headers, "x-ms-if-sequence-number-lt", options[:if_sequence_number_lt] if options[:if_sequence_number_lt]
686
+ StorageService.with_header headers, "x-ms-if-sequence-number-eq", options[:if_sequence_number_eq] if options[:if_sequence_number_eq]
687
+
688
+ # Conditional headers for append blob
689
+ StorageService.with_header headers, "x-ms-blob-condition-maxsize", options[:max_size]
690
+ StorageService.with_header headers, "x-ms-blob-condition-appendpos", options[:append_position]
691
+ end
692
+
693
+ # Get the content type according to the blob content type header and request body.
694
+ #
695
+ # headers - The request body
696
+ # content_type - The request content type
697
+ protected
698
+ def get_or_apply_content_type(body, content_type = nil)
699
+ unless body.nil?
700
+ if (body.is_a? String) && body.encoding.to_s != "ASCII_8BIT" && !body.empty?
701
+ if content_type.nil?
702
+ content_type = "text/plain; charset=#{body.encoding}"
703
+ else
704
+ # Force the request.body to the content encoding of specified in the header
705
+ charset = parse_charset_from_content_type(content_type)
706
+ body.force_encoding(charset) if charset
707
+ end
708
+ else
709
+ # It is either that the body is not a string, or that the body's encoding is ASCII_8BIT, which is a binary
710
+ # In this case, set the content type to be default content-type
711
+ content_type = Default::CONTENT_TYPE_VALUE unless content_type
712
+ end
713
+ end
714
+ content_type
715
+ end
716
+ end
717
+ end
718
+ end
719
+
720
+ Azure::Storage::BlobService = Azure::Storage::Blob::BlobService