azure-storage-blob 1.1.0 → 2.0.0

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