Azure Storage Blob Batch (#3776)
This commit is contained in:
parent
38b07749dc
commit
dc04b2410e
@ -4,6 +4,8 @@
|
||||
|
||||
### Features Added
|
||||
|
||||
- Added support for Blob Batch.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
### Bugs Fixed
|
||||
|
||||
@ -41,6 +41,7 @@ endif()
|
||||
set(
|
||||
AZURE_STORAGE_BLOBS_HEADER
|
||||
inc/azure/storage/blobs/append_blob_client.hpp
|
||||
inc/azure/storage/blobs/blob_batch.hpp
|
||||
inc/azure/storage/blobs/blob_client.hpp
|
||||
inc/azure/storage/blobs/blob_container_client.hpp
|
||||
inc/azure/storage/blobs/blob_lease_client.hpp
|
||||
@ -49,6 +50,7 @@ set(
|
||||
inc/azure/storage/blobs/blob_sas_builder.hpp
|
||||
inc/azure/storage/blobs/blob_service_client.hpp
|
||||
inc/azure/storage/blobs/block_blob_client.hpp
|
||||
inc/azure/storage/blobs/deferred_response.hpp
|
||||
inc/azure/storage/blobs/dll_import_export.hpp
|
||||
inc/azure/storage/blobs/page_blob_client.hpp
|
||||
inc/azure/storage/blobs/rest_client.hpp
|
||||
@ -61,6 +63,7 @@ set(
|
||||
set(
|
||||
AZURE_STORAGE_BLOBS_SOURCE
|
||||
src/append_blob_client.cpp
|
||||
src/blob_batch.cpp
|
||||
src/blob_client.cpp
|
||||
src/blob_container_client.cpp
|
||||
src/blob_lease_client.cpp
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
#include <azure/storage/common/storage_exception.hpp>
|
||||
|
||||
#include "azure/storage/blobs/append_blob_client.hpp"
|
||||
#include "azure/storage/blobs/blob_batch.hpp"
|
||||
#include "azure/storage/blobs/blob_client.hpp"
|
||||
#include "azure/storage/blobs/blob_container_client.hpp"
|
||||
#include "azure/storage/blobs/blob_lease_client.hpp"
|
||||
|
||||
@ -0,0 +1,154 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstring>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <azure/core/nullable.hpp>
|
||||
#include <azure/core/url.hpp>
|
||||
|
||||
#include "azure/storage/blobs/blob_client.hpp"
|
||||
#include "azure/storage/blobs/blob_container_client.hpp"
|
||||
#include "azure/storage/blobs/blob_service_client.hpp"
|
||||
#include "azure/storage/blobs/deferred_response.hpp"
|
||||
|
||||
namespace Azure { namespace Storage { namespace Blobs {
|
||||
|
||||
namespace _detail {
|
||||
extern const Core::Context::Key s_batchKey;
|
||||
|
||||
class StringBodyStream final : public Core::IO::BodyStream {
|
||||
public:
|
||||
explicit StringBodyStream(std::string content) : m_content(std::move(content)) {}
|
||||
StringBodyStream(const StringBodyStream&) = delete;
|
||||
StringBodyStream& operator=(const StringBodyStream&) = delete;
|
||||
StringBodyStream(StringBodyStream&& other) = default;
|
||||
StringBodyStream& operator=(StringBodyStream&& other) = default;
|
||||
~StringBodyStream() override {}
|
||||
int64_t Length() const override { return m_content.length(); }
|
||||
void Rewind() override { m_offset = 0; }
|
||||
|
||||
private:
|
||||
size_t OnRead(uint8_t* buffer, size_t count, Azure::Core::Context const& context) override;
|
||||
|
||||
private:
|
||||
std::string m_content;
|
||||
size_t m_offset = 0;
|
||||
};
|
||||
|
||||
enum class BatchSubrequestType
|
||||
{
|
||||
DeleteBlob,
|
||||
SetBlobAccessTier,
|
||||
};
|
||||
|
||||
struct BatchSubrequest
|
||||
{
|
||||
explicit BatchSubrequest(BatchSubrequestType type) : Type(type) {}
|
||||
virtual ~BatchSubrequest() = 0;
|
||||
|
||||
BatchSubrequestType Type;
|
||||
};
|
||||
|
||||
struct BlobBatchAccessHelper;
|
||||
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> ConstructBatchRequestPolicy(
|
||||
const std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&
|
||||
servicePerRetryPolicies,
|
||||
const std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&
|
||||
servicePerOperationPolicies,
|
||||
const BlobClientOptions& options);
|
||||
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> ConstructBatchSubrequestPolicy(
|
||||
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>&& tokenAuthPolicy,
|
||||
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>&& sharedKeyAuthPolicy,
|
||||
const BlobClientOptions& options);
|
||||
} // namespace _detail
|
||||
|
||||
/**
|
||||
* @brief A batch object allows you to batch multiple operations in a single request via
|
||||
* #Azure::Storage::Blobs::BlobServiceClient::SubmitBatch or
|
||||
* #Azure::Storage::Blobs::BlobContainerClient::SubmitBatch.
|
||||
*/
|
||||
class BlobBatch final {
|
||||
public:
|
||||
/**
|
||||
* @brief Adds a delete subrequest into batch object.
|
||||
*
|
||||
* @param blobContainerName Container name of the blob to delete.
|
||||
* @param blobName Blob name of the blob to delete.
|
||||
* @param options Optional parameters to execute the delete operation.
|
||||
* @return A deferred response which can produce a Response<DeleteBlobResult> after batch object
|
||||
* is submitted.
|
||||
*/
|
||||
DeferredResponse<Models::DeleteBlobResult> DeleteBlob(
|
||||
const std::string& blobContainerName,
|
||||
const std::string& blobName,
|
||||
const DeleteBlobOptions& options = DeleteBlobOptions());
|
||||
|
||||
/**
|
||||
* @brief Adds a delete subrequest into batch object.
|
||||
*
|
||||
* @param blobUrl Url of the blob to delete.
|
||||
* @param options Optional parameters to execute the delete operation.
|
||||
* @return A deferred response which can produce a Response<DeleteBlobResult> after batch object
|
||||
* is submitted.
|
||||
*/
|
||||
DeferredResponse<Models::DeleteBlobResult> DeleteBlob(
|
||||
const std::string& blobUrl,
|
||||
const DeleteBlobOptions& options = DeleteBlobOptions());
|
||||
|
||||
/**
|
||||
* @brief Adds a change tier subrequest into batch object.
|
||||
*
|
||||
* @param blobContainerName Container name of the blob to delete.
|
||||
* @param blobName Blob name of the blob to delete.
|
||||
* @param accessTier Indicates the tier to be set on the blob.
|
||||
* @param options Optional parameters to execute the delete operation.
|
||||
* @return A deferred response which can produce a Response<SetBlobAccessTierResult> after batch
|
||||
* object is submitted.
|
||||
*/
|
||||
DeferredResponse<Models::SetBlobAccessTierResult> SetBlobAccessTier(
|
||||
const std::string& blobContainerName,
|
||||
const std::string& blobName,
|
||||
Models::AccessTier accessTier,
|
||||
const SetBlobAccessTierOptions& options = SetBlobAccessTierOptions());
|
||||
|
||||
/**
|
||||
* @brief Adds a change tier subrequest into batch object.
|
||||
*
|
||||
* @param blobUrl Url of the blob to delete.
|
||||
* @param accessTier Indicates the tier to be set on the blob.
|
||||
* @param options Optional parameters to execute the delete operation.
|
||||
* @return A deferred response which can produce a Response<SetBlobAccessTierResult> after batch
|
||||
* object is submitted.
|
||||
*/
|
||||
DeferredResponse<Models::SetBlobAccessTierResult> SetBlobAccessTier(
|
||||
const std::string& blobUrl,
|
||||
Models::AccessTier accessTier,
|
||||
const SetBlobAccessTierOptions& options = SetBlobAccessTierOptions());
|
||||
|
||||
private:
|
||||
explicit BlobBatch(BlobServiceClient blobServiceClient);
|
||||
explicit BlobBatch(BlobContainerClient blobContainerClient);
|
||||
|
||||
BlobClient GetBlobClientForSubrequest(Core::Url url) const;
|
||||
|
||||
private:
|
||||
Core::Url m_url;
|
||||
Nullable<BlobServiceClient> m_blobServiceClient;
|
||||
Nullable<BlobContainerClient> m_blobContainerClient;
|
||||
|
||||
std::vector<std::shared_ptr<_detail::BatchSubrequest>> m_subrequests;
|
||||
|
||||
friend class BlobServiceClient;
|
||||
friend class BlobContainerClient;
|
||||
friend struct _detail::BlobBatchAccessHelper;
|
||||
};
|
||||
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
@ -431,5 +431,6 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
friend class Files::DataLake::DataLakeDirectoryClient;
|
||||
friend class Files::DataLake::DataLakeFileClient;
|
||||
friend class BlobLeaseClient;
|
||||
friend class BlobBatch;
|
||||
};
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
namespace Azure { namespace Storage { namespace Blobs {
|
||||
|
||||
class BlobLeaseClient;
|
||||
class BlobBatch;
|
||||
|
||||
/**
|
||||
* The BlobContainerClient allows you to manipulate Azure Storage containers and their
|
||||
@ -284,25 +285,41 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
const UploadBlockBlobOptions& options = UploadBlockBlobOptions(),
|
||||
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||
|
||||
/**
|
||||
* @brief Creates a new batch object to collect subrequests that can be submitted together via
|
||||
* SubmitBatch.
|
||||
*
|
||||
* @return A new batch object.
|
||||
*/
|
||||
BlobBatch CreateBatch();
|
||||
|
||||
/**
|
||||
* @brief Submits a batch of subrequests.
|
||||
*
|
||||
* @param batch The batch object containing subrequests.
|
||||
* @param options Optional parameters to execute this function.
|
||||
* @param context Context for cancelling long running operations.
|
||||
* @return A SubmitBlobBatchResult.
|
||||
* @remark This function will throw only if there's something wrong with the batch request
|
||||
* (parent request).
|
||||
*/
|
||||
Response<Models::SubmitBlobBatchResult> SubmitBatch(
|
||||
const BlobBatch& batch,
|
||||
const SubmitBlobBatchOptions& options = SubmitBlobBatchOptions(),
|
||||
const Core::Context& context = Core::Context()) const;
|
||||
|
||||
private:
|
||||
Azure::Core::Url m_blobContainerUrl;
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_pipeline;
|
||||
Azure::Nullable<EncryptionKey> m_customerProvidedKey;
|
||||
Azure::Nullable<std::string> m_encryptionScope;
|
||||
|
||||
explicit BlobContainerClient(
|
||||
Azure::Core::Url blobContainerUrl,
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> pipeline,
|
||||
Azure::Nullable<EncryptionKey> customerProvidedKey,
|
||||
Azure::Nullable<std::string> encryptionScope)
|
||||
: m_blobContainerUrl(std::move(blobContainerUrl)), m_pipeline(std::move(pipeline)),
|
||||
m_customerProvidedKey(std::move(customerProvidedKey)),
|
||||
m_encryptionScope(std::move(encryptionScope))
|
||||
{
|
||||
}
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_batchRequestPipeline;
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_batchSubrequestPipeline;
|
||||
|
||||
friend class BlobServiceClient;
|
||||
friend class BlobLeaseClient;
|
||||
friend class BlobBatch;
|
||||
};
|
||||
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
|
||||
@ -1484,6 +1484,13 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Optional parameters for #Azure::Storage::Blobs::BlobBatchClient::SubmitBatch.
|
||||
*/
|
||||
struct SubmitBlobBatchOptions final
|
||||
{
|
||||
};
|
||||
|
||||
namespace _detail {
|
||||
inline std::string TagsToString(const std::map<std::string, std::string>& tags)
|
||||
{
|
||||
|
||||
@ -163,6 +163,13 @@ namespace Azure { namespace Storage {
|
||||
std::string LeaseId;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Response type for #Azure::Storage::Blobs::BlobBatchClient::SubmitBatch.
|
||||
*/
|
||||
struct SubmitBlobBatchResult final
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace Models
|
||||
|
||||
/**
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
|
||||
namespace Azure { namespace Storage { namespace Blobs {
|
||||
|
||||
class BlobBatch;
|
||||
|
||||
/**
|
||||
* The BlobServiceClient allows you to manipulate Azure Storage service resources and blob
|
||||
* containers. The storage account provides the top-level namespace for the Blob service.
|
||||
@ -243,10 +245,38 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
const RenameBlobContainerOptions& options = RenameBlobContainerOptions(),
|
||||
const Azure::Core::Context& context = Azure::Core::Context()) const;
|
||||
|
||||
/**
|
||||
* @brief Creates a new batch object to collect subrequests that can be submitted together via
|
||||
* SubmitBatch.
|
||||
*
|
||||
* @return A new batch object.
|
||||
*/
|
||||
BlobBatch CreateBatch();
|
||||
|
||||
/**
|
||||
* @brief Submits a batch of subrequests.
|
||||
*
|
||||
* @param batch The batch object containing subrequests.
|
||||
* @param options Optional parameters to execute this function.
|
||||
* @param context Context for cancelling long running operations.
|
||||
* @return A SubmitBlobBatchResult.
|
||||
* @remark This function will throw only if there's something wrong with the batch request
|
||||
* (parent request).
|
||||
*/
|
||||
Response<Models::SubmitBlobBatchResult> SubmitBatch(
|
||||
const BlobBatch& batch,
|
||||
const SubmitBlobBatchOptions& options = SubmitBlobBatchOptions(),
|
||||
const Core::Context& context = Core::Context()) const;
|
||||
|
||||
private:
|
||||
Azure::Core::Url m_serviceUrl;
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_pipeline;
|
||||
Azure::Nullable<EncryptionKey> m_customerProvidedKey;
|
||||
Azure::Nullable<std::string> m_encryptionScope;
|
||||
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_batchRequestPipeline;
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> m_batchSubrequestPipeline;
|
||||
|
||||
friend class BlobBatch;
|
||||
};
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
|
||||
@ -0,0 +1,42 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include <azure/core/response.hpp>
|
||||
|
||||
namespace Azure { namespace Storage {
|
||||
namespace Blobs {
|
||||
class BlobBatch;
|
||||
}
|
||||
/**
|
||||
* @brief Base type for a deferred response.
|
||||
*/
|
||||
template <typename T> class DeferredResponse final {
|
||||
public:
|
||||
DeferredResponse(const DeferredResponse&) = delete;
|
||||
DeferredResponse(DeferredResponse&&) = default;
|
||||
DeferredResponse& operator=(const DeferredResponse&) = delete;
|
||||
DeferredResponse& operator=(DeferredResponse&&) = default;
|
||||
|
||||
/**
|
||||
* @brief Gets the deferred response.
|
||||
*
|
||||
* @remark It's undefined behavior to call this function before the response or exception is
|
||||
* available.
|
||||
*
|
||||
* @return The deferred response. An exception is thrown if error occurred.
|
||||
*/
|
||||
Response<T> GetResponse() const { return m_func(); }
|
||||
|
||||
private:
|
||||
DeferredResponse(std::function<Response<T>()> func) : m_func(std::move(func)) {}
|
||||
|
||||
private:
|
||||
std::function<Response<T>()> m_func;
|
||||
|
||||
friend class Blobs::BlobBatch;
|
||||
};
|
||||
}} // namespace Azure::Storage
|
||||
625
sdk/storage/azure-storage-blobs/src/blob_batch.cpp
Normal file
625
sdk/storage/azure-storage-blobs/src/blob_batch.cpp
Normal file
@ -0,0 +1,625 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include "azure/storage/blobs/blob_batch.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include <azure/core/azure_assert.hpp>
|
||||
#include <azure/core/http/policies/policy.hpp>
|
||||
#include <azure/core/internal/http/pipeline.hpp>
|
||||
#include <azure/core/io/body_stream.hpp>
|
||||
#include <azure/storage/common/crypt.hpp>
|
||||
#include <azure/storage/common/internal/constants.hpp>
|
||||
#include <azure/storage/common/internal/shared_key_policy.hpp>
|
||||
|
||||
#include "private/package_version.hpp"
|
||||
|
||||
namespace Azure { namespace Storage { namespace Blobs {
|
||||
|
||||
const Core::Context::Key _detail::s_batchKey;
|
||||
|
||||
namespace _detail {
|
||||
|
||||
struct BlobBatchAccessHelper
|
||||
{
|
||||
explicit BlobBatchAccessHelper(const BlobBatch& batch) : m_batch(&batch) {}
|
||||
|
||||
const std::vector<std::shared_ptr<BatchSubrequest>>& Subrequests() const
|
||||
{
|
||||
return m_batch->m_subrequests;
|
||||
}
|
||||
const BlobBatch* m_batch;
|
||||
};
|
||||
|
||||
} // namespace _detail
|
||||
|
||||
namespace {
|
||||
const std::string LineEnding = "\r\n";
|
||||
const std::string BatchContentTypePrefix = "multipart/mixed; boundary=";
|
||||
|
||||
static Core::Context::Key s_subrequestKey;
|
||||
static Core::Context::Key s_subresponseKey;
|
||||
|
||||
struct Parser final
|
||||
{
|
||||
explicit Parser(const std::string& str)
|
||||
: startPos(str.data()), currPos(startPos), endPos(startPos + str.length())
|
||||
{
|
||||
}
|
||||
explicit Parser(const std::vector<uint8_t>& str)
|
||||
: startPos(reinterpret_cast<const char*>(str.data())),
|
||||
currPos(reinterpret_cast<const char*>(startPos)),
|
||||
endPos(reinterpret_cast<const char*>(startPos) + str.size())
|
||||
{
|
||||
}
|
||||
const char* startPos;
|
||||
const char* currPos;
|
||||
const char* endPos;
|
||||
|
||||
bool IsEnd() const { return currPos == endPos; }
|
||||
|
||||
bool LookAhead(const std::string& expect) const
|
||||
{
|
||||
for (size_t i = 0; i < expect.length(); ++i)
|
||||
{
|
||||
if (currPos + i < endPos && currPos[i] == expect[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Consume(const std::string& expect)
|
||||
{
|
||||
// This moves currPos
|
||||
if (LookAhead(expect))
|
||||
{
|
||||
currPos += expect.length();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"failed to parse response body at " + std::to_string(currPos - startPos));
|
||||
}
|
||||
}
|
||||
|
||||
const char* FindNext(const std::string& expect) const
|
||||
{
|
||||
return std::search(currPos, endPos, expect.begin(), expect.end());
|
||||
}
|
||||
|
||||
const char* AfterNext(const std::string& expect) const
|
||||
{
|
||||
return std::min(endPos, FindNext(expect) + expect.length());
|
||||
}
|
||||
|
||||
std::string GetBeforeNextAndConsume(const std::string& expect)
|
||||
{
|
||||
// This moves currPos
|
||||
auto ePos = FindNext(expect);
|
||||
std::string ret(currPos, ePos);
|
||||
currPos = std::min(endPos, ePos + expect.length());
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<Core::Http::RawResponse> ParseRawResponse(const std::string& responseText)
|
||||
{
|
||||
Parser parser(responseText);
|
||||
|
||||
parser.Consume("HTTP/");
|
||||
int32_t httpMajorVersion = std::stoi(parser.GetBeforeNextAndConsume("."));
|
||||
int32_t httpMinorVersion = std::stoi(parser.GetBeforeNextAndConsume(" "));
|
||||
int32_t httpStatusCode = std::stoi(parser.GetBeforeNextAndConsume(" "));
|
||||
const std::string httpReasonPhrase = parser.GetBeforeNextAndConsume(LineEnding);
|
||||
|
||||
auto rawResponse = std::make_unique<Azure::Core::Http::RawResponse>(
|
||||
httpMajorVersion,
|
||||
httpMinorVersion,
|
||||
static_cast<Azure::Core::Http::HttpStatusCode>(httpStatusCode),
|
||||
httpReasonPhrase);
|
||||
|
||||
while (!parser.IsEnd())
|
||||
{
|
||||
if (parser.LookAhead(LineEnding))
|
||||
{
|
||||
break;
|
||||
}
|
||||
std::string headerName = parser.GetBeforeNextAndConsume(": ");
|
||||
std::string headerValue = parser.GetBeforeNextAndConsume(LineEnding);
|
||||
rawResponse->SetHeader(headerName, headerValue);
|
||||
}
|
||||
parser.Consume(LineEnding);
|
||||
rawResponse->SetBody(std::vector<uint8_t>(parser.currPos, parser.endPos));
|
||||
|
||||
return rawResponse;
|
||||
}
|
||||
|
||||
class RemoveXMsVersionPolicy final : public Core::Http::Policies::HttpPolicy {
|
||||
public:
|
||||
~RemoveXMsVersionPolicy() override {}
|
||||
|
||||
std::unique_ptr<HttpPolicy> Clone() const override
|
||||
{
|
||||
return std::make_unique<RemoveXMsVersionPolicy>(*this);
|
||||
}
|
||||
std::unique_ptr<Core::Http::RawResponse> Send(
|
||||
Core::Http::Request& request,
|
||||
Core::Http::Policies::NextHttpPolicy nextPolicy,
|
||||
const Core::Context& context) const override
|
||||
{
|
||||
request.RemoveHeader(_internal::HttpHeaderXMsVersion);
|
||||
return nextPolicy.Send(request, context);
|
||||
}
|
||||
};
|
||||
|
||||
class NoopTransportPolicy final : public Core::Http::Policies::HttpPolicy {
|
||||
public:
|
||||
~NoopTransportPolicy() override {}
|
||||
|
||||
std::unique_ptr<HttpPolicy> Clone() const override
|
||||
{
|
||||
return std::make_unique<NoopTransportPolicy>(*this);
|
||||
}
|
||||
|
||||
std::unique_ptr<Core::Http::RawResponse> Send(
|
||||
Core::Http::Request& request,
|
||||
Core::Http::Policies::NextHttpPolicy nextPolicy,
|
||||
const Core::Context& context) const override
|
||||
{
|
||||
(void)nextPolicy;
|
||||
|
||||
std::string* subrequestText = nullptr;
|
||||
context.TryGetValue(s_subrequestKey, subrequestText);
|
||||
|
||||
if (subrequestText)
|
||||
{
|
||||
std::string requestText = request.GetMethod().ToString() + " /"
|
||||
+ request.GetUrl().GetRelativeUrl() + " HTTP/1.1" + LineEnding;
|
||||
for (const auto& header : request.GetHeaders())
|
||||
{
|
||||
requestText += header.first + ": " + header.second + LineEnding;
|
||||
}
|
||||
requestText += LineEnding;
|
||||
*subrequestText = std::move(requestText);
|
||||
|
||||
auto rawResponse = std::make_unique<Core::Http::RawResponse>(
|
||||
1, 1, Core::Http::HttpStatusCode::Accepted, "Accepted");
|
||||
return rawResponse;
|
||||
}
|
||||
|
||||
std::string* subresponseText = nullptr;
|
||||
context.TryGetValue(s_subresponseKey, subresponseText);
|
||||
if (subresponseText)
|
||||
{
|
||||
return ParseRawResponse(*subresponseText);
|
||||
}
|
||||
AZURE_UNREACHABLE_CODE();
|
||||
}
|
||||
};
|
||||
|
||||
class ConstructBatchRequestBodyPolicy final : public Core::Http::Policies::HttpPolicy {
|
||||
public:
|
||||
ConstructBatchRequestBodyPolicy(
|
||||
std::function<void(Core::Http::Request&, const Core::Context&)> constructRequestFunction,
|
||||
std::function<void(std::unique_ptr<Core::Http::RawResponse>&, const Core::Context&)>
|
||||
parseResponseFunction)
|
||||
: m_constructRequestFunction(std::move(constructRequestFunction)),
|
||||
m_parseResponseFunction(std::move(parseResponseFunction))
|
||||
{
|
||||
}
|
||||
~ConstructBatchRequestBodyPolicy() override {}
|
||||
|
||||
std::unique_ptr<HttpPolicy> Clone() const override
|
||||
{
|
||||
return std::make_unique<ConstructBatchRequestBodyPolicy>(*this);
|
||||
}
|
||||
|
||||
std::unique_ptr<Core::Http::RawResponse> Send(
|
||||
Core::Http::Request& request,
|
||||
Core::Http::Policies::NextHttpPolicy nextPolicy,
|
||||
const Core::Context& context) const override
|
||||
{
|
||||
m_constructRequestFunction(request, context);
|
||||
auto rawResponse = nextPolicy.Send(request, context);
|
||||
m_parseResponseFunction(rawResponse, context);
|
||||
return rawResponse;
|
||||
}
|
||||
|
||||
private:
|
||||
std::function<void(Core::Http::Request&, const Core::Context&)> m_constructRequestFunction;
|
||||
std::function<void(std::unique_ptr<Core::Http::RawResponse>&, const Core::Context&)>
|
||||
m_parseResponseFunction;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
std::function<Response<T>()> CreateDeferredResponseFunc(
|
||||
std::promise<Nullable<Response<T>>>& promise)
|
||||
{
|
||||
return [&promise]() {
|
||||
try
|
||||
{
|
||||
auto f = promise.get_future();
|
||||
AZURE_ASSERT_MSG(
|
||||
f.wait_for(std::chrono::seconds(0)) == std::future_status::ready,
|
||||
"GetResponse() is called when response is not ready.");
|
||||
return f.get().Value();
|
||||
}
|
||||
catch (std::future_error&)
|
||||
{
|
||||
AZURE_ASSERT_MSG(false, "GetResponse() can only be called once.");
|
||||
}
|
||||
AZURE_UNREACHABLE_CODE();
|
||||
};
|
||||
}
|
||||
|
||||
struct DeleteBlobSubrequest final : public _detail::BatchSubrequest
|
||||
{
|
||||
DeleteBlobSubrequest() : BatchSubrequest(_detail::BatchSubrequestType::DeleteBlob) {}
|
||||
|
||||
Nullable<Blobs::BlobClient> Client;
|
||||
DeleteBlobOptions Options;
|
||||
std::promise<Nullable<Response<Models::DeleteBlobResult>>> Promise;
|
||||
};
|
||||
|
||||
struct SetBlobAccessTierSubrequest final : public _detail::BatchSubrequest
|
||||
{
|
||||
SetBlobAccessTierSubrequest()
|
||||
: BatchSubrequest(_detail::BatchSubrequestType::SetBlobAccessTier)
|
||||
{
|
||||
}
|
||||
|
||||
Nullable<Blobs::BlobClient> Client;
|
||||
Models::AccessTier Tier;
|
||||
SetBlobAccessTierOptions Options;
|
||||
std::promise<Nullable<Response<Models::SetBlobAccessTierResult>>> Promise;
|
||||
};
|
||||
|
||||
void ConstructSubrequests(Core::Http::Request& request, const Core::Context& context)
|
||||
{
|
||||
const std::string boundary = "batch_" + Azure::Core::Uuid::CreateUuid().ToString();
|
||||
|
||||
auto getBatchBoundary = [&boundary, subRequestCounter = 0]() mutable {
|
||||
std::string ret;
|
||||
ret += "--" + boundary + LineEnding;
|
||||
ret += "Content-Type: application/http" + LineEnding + "Content-Transfer-Encoding: binary"
|
||||
+ LineEnding + "Content-ID: " + std::to_string(subRequestCounter++) + LineEnding
|
||||
+ LineEnding;
|
||||
return ret;
|
||||
};
|
||||
|
||||
std::string requestBody;
|
||||
|
||||
const BlobBatch* batch = nullptr;
|
||||
context.TryGetValue(_detail::s_batchKey, batch);
|
||||
|
||||
for (const auto& subrequestPtr : _detail::BlobBatchAccessHelper(*batch).Subrequests())
|
||||
{
|
||||
if (subrequestPtr->Type == _detail::BatchSubrequestType::DeleteBlob)
|
||||
{
|
||||
auto& subrequest = *static_cast<DeleteBlobSubrequest*>(subrequestPtr.get());
|
||||
requestBody += getBatchBoundary();
|
||||
std::string subrequestText;
|
||||
subrequest.Client.Value().Delete(
|
||||
subrequest.Options, Core::Context().WithValue(s_subrequestKey, &subrequestText));
|
||||
requestBody += subrequestText;
|
||||
}
|
||||
else if (subrequestPtr->Type == _detail::BatchSubrequestType::SetBlobAccessTier)
|
||||
{
|
||||
auto& subrequest = *static_cast<SetBlobAccessTierSubrequest*>(subrequestPtr.get());
|
||||
requestBody += getBatchBoundary();
|
||||
|
||||
std::string subrequestText;
|
||||
subrequest.Client.Value().SetAccessTier(
|
||||
subrequest.Tier,
|
||||
subrequest.Options,
|
||||
Core::Context().WithValue(s_subrequestKey, &subrequestText));
|
||||
requestBody += subrequestText;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZURE_UNREACHABLE_CODE();
|
||||
}
|
||||
}
|
||||
requestBody += "--" + boundary + "--" + LineEnding;
|
||||
|
||||
request.SetHeader(_internal::HttpHeaderContentType, BatchContentTypePrefix + boundary);
|
||||
static_cast<_detail::StringBodyStream&>(*request.GetBodyStream())
|
||||
= _detail::StringBodyStream(std::move(requestBody));
|
||||
request.SetHeader(
|
||||
_internal::HttpHeaderContentLength, std::to_string(request.GetBodyStream()->Length()));
|
||||
}
|
||||
|
||||
void ParseSubresponses(
|
||||
std::unique_ptr<Core::Http::RawResponse>& rawResponse,
|
||||
const Core::Context& context)
|
||||
{
|
||||
if (rawResponse->GetStatusCode() != Core::Http::HttpStatusCode::Accepted
|
||||
|| rawResponse->GetHeaders().count(_internal::HttpHeaderContentType) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string boundary = rawResponse->GetHeaders()
|
||||
.at(std::string(_internal::HttpHeaderContentType))
|
||||
.substr(BatchContentTypePrefix.length());
|
||||
|
||||
const std::vector<uint8_t>& responseBody
|
||||
= rawResponse->ExtractBodyStream()->ReadToEnd(context);
|
||||
Parser parser(responseBody);
|
||||
|
||||
std::vector<std::string> subresponses;
|
||||
while (true)
|
||||
{
|
||||
parser.Consume("--" + boundary);
|
||||
if (parser.LookAhead("--"))
|
||||
{
|
||||
parser.Consume("--");
|
||||
}
|
||||
if (parser.IsEnd())
|
||||
{
|
||||
break;
|
||||
}
|
||||
auto contentIdPos = parser.AfterNext("Content-ID: ");
|
||||
auto responseStartPos = parser.AfterNext(LineEnding + LineEnding);
|
||||
auto responseEndPos = parser.FindNext("--" + boundary);
|
||||
if (contentIdPos != parser.endPos)
|
||||
{
|
||||
parser.currPos = contentIdPos;
|
||||
auto idEndPos = parser.FindNext(LineEnding);
|
||||
size_t id = static_cast<size_t>(std::stoi(std::string(parser.currPos, idEndPos)));
|
||||
if (subresponses.size() < id + 1)
|
||||
{
|
||||
subresponses.resize(id + 1);
|
||||
}
|
||||
subresponses[id] = std::string(responseStartPos, responseEndPos);
|
||||
parser.currPos = responseEndPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
rawResponse = ParseRawResponse(std::string(responseStartPos, responseEndPos));
|
||||
parser.currPos = responseEndPos;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const BlobBatch* batch = nullptr;
|
||||
context.TryGetValue(_detail::s_batchKey, batch);
|
||||
|
||||
size_t subresponseCounter = 0;
|
||||
for (const auto& subrequestPtr : _detail::BlobBatchAccessHelper(*batch).Subrequests())
|
||||
{
|
||||
if (subrequestPtr->Type == _detail::BatchSubrequestType::DeleteBlob)
|
||||
{
|
||||
auto& subrequest = *static_cast<DeleteBlobSubrequest*>(subrequestPtr.get());
|
||||
try
|
||||
{
|
||||
auto response = subrequest.Client.Value().Delete(
|
||||
subrequest.Options,
|
||||
Core::Context().WithValue(s_subresponseKey, &subresponses[subresponseCounter++]));
|
||||
subrequest.Promise.set_value(std::move(response));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
subrequest.Promise.set_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
else if (subrequestPtr->Type == _detail::BatchSubrequestType::SetBlobAccessTier)
|
||||
{
|
||||
auto& subrequest = *static_cast<SetBlobAccessTierSubrequest*>(subrequestPtr.get());
|
||||
try
|
||||
{
|
||||
auto response = subrequest.Client.Value().SetAccessTier(
|
||||
subrequest.Tier,
|
||||
subrequest.Options,
|
||||
Core::Context().WithValue(s_subresponseKey, &subresponses[subresponseCounter++]));
|
||||
subrequest.Promise.set_value(std::move(response));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
subrequest.Promise.set_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
AZURE_UNREACHABLE_CODE();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace _detail {
|
||||
|
||||
size_t StringBodyStream::OnRead(
|
||||
uint8_t* buffer,
|
||||
size_t count,
|
||||
Azure::Core::Context const& context)
|
||||
{
|
||||
(void)context;
|
||||
size_t copy_length = std::min(count, m_content.length() - m_offset);
|
||||
std::memcpy(buffer, &m_content[0] + m_offset, static_cast<size_t>(copy_length));
|
||||
m_offset += copy_length;
|
||||
return copy_length;
|
||||
}
|
||||
|
||||
BatchSubrequest::~BatchSubrequest() {}
|
||||
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> ConstructBatchRequestPolicy(
|
||||
const std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&
|
||||
servicePerRetryPolicies,
|
||||
const std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>>&
|
||||
servicePerOperationPolicies,
|
||||
const BlobClientOptions& options)
|
||||
{
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
|
||||
perRetryPolicies.push_back(std::make_unique<ConstructBatchRequestBodyPolicy>(
|
||||
[](Core::Http::Request& request, const Core::Context& context) {
|
||||
ConstructSubrequests(request, context);
|
||||
},
|
||||
[](std::unique_ptr<Core::Http::RawResponse>& rawResponse, const Core::Context& context) {
|
||||
ParseSubresponses(rawResponse, context);
|
||||
}));
|
||||
for (auto& policy : servicePerRetryPolicies)
|
||||
{
|
||||
perRetryPolicies.push_back(policy->Clone());
|
||||
}
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
|
||||
for (auto& policy : servicePerOperationPolicies)
|
||||
{
|
||||
perOperationPolicies.push_back(policy->Clone());
|
||||
}
|
||||
return std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
options,
|
||||
_internal::BlobServicePackageName,
|
||||
PackageVersion::ToString(),
|
||||
std::move(perRetryPolicies),
|
||||
std::move(perOperationPolicies));
|
||||
}
|
||||
|
||||
std::shared_ptr<Azure::Core::Http::_internal::HttpPipeline> ConstructBatchSubrequestPolicy(
|
||||
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>&& tokenAuthPolicy,
|
||||
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>&& sharedKeyAuthPolicy,
|
||||
const BlobClientOptions& options)
|
||||
{
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> policies;
|
||||
policies.emplace_back(
|
||||
std::make_unique<Azure::Core::Http::Policies::_internal::RequestIdPolicy>());
|
||||
policies.emplace_back(
|
||||
std::make_unique<Azure::Core::Http::Policies::_internal::TelemetryPolicy>(
|
||||
_internal::BlobServicePackageName, PackageVersion::ToString(), options.Telemetry));
|
||||
for (auto& policy : options.PerOperationPolicies)
|
||||
{
|
||||
policies.emplace_back(policy->Clone());
|
||||
}
|
||||
policies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
|
||||
if (tokenAuthPolicy)
|
||||
{
|
||||
policies.emplace_back(std::move(tokenAuthPolicy));
|
||||
}
|
||||
for (auto& policy : options.PerRetryPolicies)
|
||||
{
|
||||
policies.emplace_back(policy->Clone());
|
||||
}
|
||||
policies.emplace_back(std::make_unique<RemoveXMsVersionPolicy>());
|
||||
if (sharedKeyAuthPolicy)
|
||||
{
|
||||
policies.emplace_back(std::move(sharedKeyAuthPolicy));
|
||||
}
|
||||
policies.push_back(std::make_unique<NoopTransportPolicy>());
|
||||
return std::make_shared<Core::Http::_internal::HttpPipeline>(std::move(policies));
|
||||
}
|
||||
} // namespace _detail
|
||||
|
||||
BlobBatch::BlobBatch(BlobServiceClient blobServiceClient)
|
||||
: m_blobServiceClient(std::move(blobServiceClient))
|
||||
{
|
||||
m_url = m_blobServiceClient.Value().m_serviceUrl;
|
||||
}
|
||||
|
||||
BlobBatch::BlobBatch(BlobContainerClient blobContainerClient)
|
||||
: m_blobContainerClient(std::move(blobContainerClient))
|
||||
{
|
||||
auto serviceUrl = m_blobContainerClient.Value().m_blobContainerUrl;
|
||||
auto path = serviceUrl.GetPath();
|
||||
if (!path.empty() && path.back() == '/')
|
||||
{
|
||||
path.pop_back();
|
||||
}
|
||||
auto slash_pos = path.rfind('/');
|
||||
path = path.substr(0, slash_pos == std::string::npos ? 0 : (slash_pos + 1));
|
||||
serviceUrl.SetPath(path);
|
||||
m_url = std::move(serviceUrl);
|
||||
}
|
||||
|
||||
BlobClient BlobBatch::GetBlobClientForSubrequest(Core::Url url) const
|
||||
{
|
||||
if (m_blobServiceClient.HasValue())
|
||||
{
|
||||
auto blobClient = m_blobServiceClient.Value().GetBlobContainerClient("$").GetBlobClient("$");
|
||||
blobClient.m_blobUrl = std::move(url);
|
||||
blobClient.m_pipeline = m_blobServiceClient.Value().m_batchSubrequestPipeline;
|
||||
return blobClient;
|
||||
}
|
||||
else if (m_blobContainerClient.HasValue())
|
||||
{
|
||||
auto blobClient = m_blobContainerClient->GetBlobClient("$");
|
||||
blobClient.m_blobUrl = std::move(url);
|
||||
blobClient.m_pipeline = m_blobContainerClient.Value().m_batchSubrequestPipeline;
|
||||
return blobClient;
|
||||
}
|
||||
AZURE_UNREACHABLE_CODE();
|
||||
}
|
||||
|
||||
DeferredResponse<Models::DeleteBlobResult> BlobBatch::DeleteBlob(
|
||||
const std::string& blobContainerName,
|
||||
const std::string& blobName,
|
||||
const DeleteBlobOptions& options)
|
||||
{
|
||||
auto blobUrl = m_url;
|
||||
blobUrl.AppendPath(blobContainerName);
|
||||
blobUrl.AppendPath(blobName);
|
||||
auto op = std::make_shared<DeleteBlobSubrequest>();
|
||||
op->Client = GetBlobClientForSubrequest(std::move(blobUrl));
|
||||
op->Options = options;
|
||||
DeferredResponse<Models::DeleteBlobResult> deferredResponse(
|
||||
CreateDeferredResponseFunc(op->Promise));
|
||||
m_subrequests.push_back(std::move(op));
|
||||
return deferredResponse;
|
||||
}
|
||||
|
||||
DeferredResponse<Models::DeleteBlobResult> BlobBatch::DeleteBlob(
|
||||
const std::string& blobUrl,
|
||||
const DeleteBlobOptions& options)
|
||||
{
|
||||
auto op = std::make_shared<DeleteBlobSubrequest>();
|
||||
op->Client = GetBlobClientForSubrequest(Core::Url(blobUrl));
|
||||
op->Options = options;
|
||||
DeferredResponse<Models::DeleteBlobResult> deferredResponse(
|
||||
CreateDeferredResponseFunc(op->Promise));
|
||||
m_subrequests.push_back(std::move(op));
|
||||
return deferredResponse;
|
||||
}
|
||||
|
||||
DeferredResponse<Models::SetBlobAccessTierResult> BlobBatch::SetBlobAccessTier(
|
||||
const std::string& blobContainerName,
|
||||
const std::string& blobName,
|
||||
Models::AccessTier accessTier,
|
||||
const SetBlobAccessTierOptions& options)
|
||||
{
|
||||
auto blobUrl = m_url;
|
||||
blobUrl.AppendPath(blobContainerName);
|
||||
blobUrl.AppendPath(blobName);
|
||||
auto op = std::make_shared<SetBlobAccessTierSubrequest>();
|
||||
op->Client = GetBlobClientForSubrequest(std::move(blobUrl));
|
||||
op->Tier = accessTier;
|
||||
op->Options = options;
|
||||
DeferredResponse<Models::SetBlobAccessTierResult> deferredResponse(
|
||||
CreateDeferredResponseFunc(op->Promise));
|
||||
m_subrequests.push_back(std::move(op));
|
||||
return deferredResponse;
|
||||
}
|
||||
|
||||
DeferredResponse<Models::SetBlobAccessTierResult> BlobBatch::SetBlobAccessTier(
|
||||
const std::string& blobUrl,
|
||||
Models::AccessTier accessTier,
|
||||
const SetBlobAccessTierOptions& options)
|
||||
{
|
||||
auto op = std::make_shared<SetBlobAccessTierSubrequest>();
|
||||
op->Client = GetBlobClientForSubrequest(Core::Url(blobUrl));
|
||||
op->Tier = accessTier;
|
||||
op->Options = options;
|
||||
DeferredResponse<Models::SetBlobAccessTierResult> deferredResponse(
|
||||
CreateDeferredResponseFunc(op->Promise));
|
||||
m_subrequests.push_back(std::move(op));
|
||||
return deferredResponse;
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
@ -14,6 +14,7 @@
|
||||
#include <azure/storage/common/storage_exception.hpp>
|
||||
|
||||
#include "azure/storage/blobs/append_blob_client.hpp"
|
||||
#include "azure/storage/blobs/blob_batch.hpp"
|
||||
#include "azure/storage/blobs/block_blob_client.hpp"
|
||||
#include "azure/storage/blobs/page_blob_client.hpp"
|
||||
|
||||
@ -48,8 +49,8 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
: BlobContainerClient(blobContainerUrl, options)
|
||||
{
|
||||
BlobClientOptions newOptions = options;
|
||||
newOptions.PerRetryPolicies.emplace_back(
|
||||
std::make_unique<_internal::SharedKeyPolicy>(credential));
|
||||
auto sharedKeyAuthPolicy = std::make_unique<_internal::SharedKeyPolicy>(credential);
|
||||
newOptions.PerRetryPolicies.emplace_back(sharedKeyAuthPolicy->Clone());
|
||||
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
|
||||
@ -58,6 +59,13 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
|
||||
perOperationPolicies.emplace_back(
|
||||
std::make_unique<_internal::StorageServiceVersionPolicy>(newOptions.ApiVersion));
|
||||
|
||||
m_batchRequestPipeline
|
||||
= _detail::ConstructBatchRequestPolicy(perRetryPolicies, perOperationPolicies, newOptions);
|
||||
|
||||
m_batchSubrequestPipeline
|
||||
= _detail::ConstructBatchSubrequestPolicy(nullptr, std::move(sharedKeyAuthPolicy), options);
|
||||
|
||||
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
newOptions,
|
||||
_internal::BlobServicePackageName,
|
||||
@ -77,15 +85,24 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
perRetryPolicies.emplace_back(std::make_unique<_internal::StorageSwitchToSecondaryPolicy>(
|
||||
m_blobContainerUrl.GetHost(), options.SecondaryHostForRetryReads));
|
||||
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
|
||||
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy> tokenAuthPolicy;
|
||||
{
|
||||
Azure::Core::Credentials::TokenRequestContext tokenContext;
|
||||
tokenContext.Scopes.emplace_back(_internal::StorageScope);
|
||||
perRetryPolicies.emplace_back(
|
||||
std::make_unique<Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
|
||||
credential, tokenContext));
|
||||
tokenAuthPolicy = std::make_unique<
|
||||
Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
|
||||
credential, tokenContext);
|
||||
perRetryPolicies.emplace_back(tokenAuthPolicy->Clone());
|
||||
}
|
||||
perOperationPolicies.emplace_back(
|
||||
std::make_unique<_internal::StorageServiceVersionPolicy>(options.ApiVersion));
|
||||
|
||||
m_batchRequestPipeline
|
||||
= _detail::ConstructBatchRequestPolicy(perRetryPolicies, perOperationPolicies, options);
|
||||
|
||||
m_batchSubrequestPipeline
|
||||
= _detail::ConstructBatchSubrequestPolicy(std::move(tokenAuthPolicy), nullptr, options);
|
||||
|
||||
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
options,
|
||||
_internal::BlobServicePackageName,
|
||||
@ -107,6 +124,12 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
|
||||
perOperationPolicies.emplace_back(
|
||||
std::make_unique<_internal::StorageServiceVersionPolicy>(options.ApiVersion));
|
||||
|
||||
m_batchRequestPipeline
|
||||
= _detail::ConstructBatchRequestPolicy(perRetryPolicies, perOperationPolicies, options);
|
||||
|
||||
m_batchSubrequestPipeline = _detail::ConstructBatchSubrequestPolicy(nullptr, nullptr, options);
|
||||
|
||||
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
options,
|
||||
_internal::BlobServicePackageName,
|
||||
@ -444,4 +467,30 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
std::move(blockBlobClient), std::move(response.RawResponse));
|
||||
}
|
||||
|
||||
BlobBatch BlobContainerClient::CreateBatch() { return BlobBatch(*this); }
|
||||
|
||||
Response<Models::SubmitBlobBatchResult> BlobContainerClient::SubmitBatch(
|
||||
const BlobBatch& batch,
|
||||
const SubmitBlobBatchOptions& options,
|
||||
const Core::Context& context) const
|
||||
{
|
||||
(void)options;
|
||||
|
||||
if (!batch.m_blobContainerClient.HasValue())
|
||||
{
|
||||
throw std::runtime_error("Batch is not container-scoped.");
|
||||
}
|
||||
|
||||
_detail::BlobContainerClient::SubmitBlobContainerBatchOptions protocolLayerOptions;
|
||||
_detail::StringBodyStream bodyStream(std::string{});
|
||||
auto response = _detail::BlobContainerClient::SubmitBatch(
|
||||
*m_batchRequestPipeline,
|
||||
m_blobContainerUrl,
|
||||
bodyStream,
|
||||
protocolLayerOptions,
|
||||
context.WithValue(_detail::s_batchKey, &batch));
|
||||
return Azure::Response<Models::SubmitBlobBatchResult>(
|
||||
Models::SubmitBlobBatchResult(), std::move(response.RawResponse));
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
#include <azure/storage/common/internal/storage_switch_to_secondary_policy.hpp>
|
||||
#include <azure/storage/common/storage_common.hpp>
|
||||
|
||||
#include "azure/storage/blobs/blob_batch.hpp"
|
||||
#include "private/package_version.hpp"
|
||||
|
||||
namespace Azure { namespace Storage { namespace Blobs {
|
||||
@ -41,8 +42,8 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
: BlobServiceClient(serviceUrl, options)
|
||||
{
|
||||
BlobClientOptions newOptions = options;
|
||||
newOptions.PerRetryPolicies.emplace_back(
|
||||
std::make_unique<_internal::SharedKeyPolicy>(credential));
|
||||
auto sharedKeyPolicy = std::make_unique<_internal::SharedKeyPolicy>(credential);
|
||||
newOptions.PerRetryPolicies.emplace_back(sharedKeyPolicy->Clone());
|
||||
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perRetryPolicies;
|
||||
std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> perOperationPolicies;
|
||||
@ -51,6 +52,13 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
|
||||
perOperationPolicies.emplace_back(
|
||||
std::make_unique<_internal::StorageServiceVersionPolicy>(newOptions.ApiVersion));
|
||||
|
||||
m_batchRequestPipeline
|
||||
= _detail::ConstructBatchRequestPolicy(perRetryPolicies, perOperationPolicies, newOptions);
|
||||
|
||||
m_batchSubrequestPipeline
|
||||
= _detail::ConstructBatchSubrequestPolicy(nullptr, std::move(sharedKeyPolicy), options);
|
||||
|
||||
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
newOptions,
|
||||
_internal::BlobServicePackageName,
|
||||
@ -70,15 +78,24 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
perRetryPolicies.emplace_back(std::make_unique<_internal::StorageSwitchToSecondaryPolicy>(
|
||||
m_serviceUrl.GetHost(), options.SecondaryHostForRetryReads));
|
||||
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
|
||||
std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy> tokenAuthPolicy;
|
||||
{
|
||||
Azure::Core::Credentials::TokenRequestContext tokenContext;
|
||||
tokenContext.Scopes.emplace_back(_internal::StorageScope);
|
||||
perRetryPolicies.emplace_back(
|
||||
std::make_unique<Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
|
||||
credential, tokenContext));
|
||||
tokenAuthPolicy = std::make_unique<
|
||||
Azure::Core::Http::Policies::_internal::BearerTokenAuthenticationPolicy>(
|
||||
credential, tokenContext);
|
||||
perRetryPolicies.emplace_back(tokenAuthPolicy->Clone());
|
||||
}
|
||||
perOperationPolicies.emplace_back(
|
||||
std::make_unique<_internal::StorageServiceVersionPolicy>(options.ApiVersion));
|
||||
|
||||
m_batchRequestPipeline
|
||||
= _detail::ConstructBatchRequestPolicy(perRetryPolicies, perOperationPolicies, options);
|
||||
|
||||
m_batchSubrequestPipeline
|
||||
= _detail::ConstructBatchSubrequestPolicy(std::move(tokenAuthPolicy), nullptr, options);
|
||||
|
||||
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
options,
|
||||
_internal::BlobServicePackageName,
|
||||
@ -100,6 +117,12 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
perRetryPolicies.emplace_back(std::make_unique<_internal::StoragePerRetryPolicy>());
|
||||
perOperationPolicies.emplace_back(
|
||||
std::make_unique<_internal::StorageServiceVersionPolicy>(options.ApiVersion));
|
||||
|
||||
m_batchRequestPipeline
|
||||
= _detail::ConstructBatchRequestPolicy(perRetryPolicies, perOperationPolicies, options);
|
||||
|
||||
m_batchSubrequestPipeline = _detail::ConstructBatchSubrequestPolicy(nullptr, nullptr, options);
|
||||
|
||||
m_pipeline = std::make_shared<Azure::Core::Http::_internal::HttpPipeline>(
|
||||
options,
|
||||
_internal::BlobServicePackageName,
|
||||
@ -113,8 +136,14 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
{
|
||||
auto blobContainerUrl = m_serviceUrl;
|
||||
blobContainerUrl.AppendPath(_internal::UrlEncodePath(blobContainerName));
|
||||
return BlobContainerClient(
|
||||
std::move(blobContainerUrl), m_pipeline, m_customerProvidedKey, m_encryptionScope);
|
||||
|
||||
BlobContainerClient blobContainerClient(blobContainerUrl.GetAbsoluteUrl());
|
||||
blobContainerClient.m_pipeline = m_pipeline;
|
||||
blobContainerClient.m_customerProvidedKey = m_customerProvidedKey;
|
||||
blobContainerClient.m_encryptionScope = m_encryptionScope;
|
||||
blobContainerClient.m_batchRequestPipeline = m_batchRequestPipeline;
|
||||
blobContainerClient.m_batchSubrequestPipeline = m_batchSubrequestPipeline;
|
||||
return blobContainerClient;
|
||||
}
|
||||
|
||||
ListBlobContainersPagedResponse BlobServiceClient::ListBlobContainers(
|
||||
@ -281,4 +310,30 @@ namespace Azure { namespace Storage { namespace Blobs {
|
||||
std::move(blobContainerClient), std::move(response.RawResponse));
|
||||
}
|
||||
|
||||
BlobBatch BlobServiceClient::CreateBatch() { return BlobBatch(*this); }
|
||||
|
||||
Response<Models::SubmitBlobBatchResult> BlobServiceClient::SubmitBatch(
|
||||
const BlobBatch& batch,
|
||||
const SubmitBlobBatchOptions& options,
|
||||
const Core::Context& context) const
|
||||
{
|
||||
(void)options;
|
||||
|
||||
if (!batch.m_blobServiceClient.HasValue())
|
||||
{
|
||||
throw std::runtime_error("Batch is container-scoped.");
|
||||
}
|
||||
|
||||
_detail::ServiceClient::SubmitServiceBatchOptions protocolLayerOptions;
|
||||
_detail::StringBodyStream bodyStream(std::string{});
|
||||
auto response = _detail::ServiceClient::SubmitBatch(
|
||||
*m_batchRequestPipeline,
|
||||
m_serviceUrl,
|
||||
bodyStream,
|
||||
protocolLayerOptions,
|
||||
context.WithValue(_detail::s_batchKey, &batch));
|
||||
return Azure::Response<Models::SubmitBlobBatchResult>(
|
||||
Models::SubmitBlobBatchResult(), std::move(response.RawResponse));
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Blobs
|
||||
|
||||
@ -16,6 +16,7 @@ add_executable (
|
||||
azure-storage-blobs-test
|
||||
append_blob_client_test.cpp
|
||||
append_blob_client_test.hpp
|
||||
blob_batch_client_test.cpp
|
||||
blob_container_client_test.cpp
|
||||
blob_container_client_test.hpp
|
||||
blob_query_test.cpp
|
||||
|
||||
@ -0,0 +1,264 @@
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#include <azure/storage/blobs.hpp>
|
||||
|
||||
#include "test/ut/test_base.hpp"
|
||||
|
||||
namespace Azure { namespace Storage { namespace Test {
|
||||
|
||||
class BlobBatchClientTest : public StorageTest {
|
||||
private:
|
||||
std::unique_ptr<Azure::Storage::Blobs::BlobServiceClient> m_client;
|
||||
|
||||
protected:
|
||||
// Required to rename the test propertly once the test is started.
|
||||
// We can only know the test instance name until the test instance is run.
|
||||
Azure::Storage::Blobs::BlobServiceClient const& GetClientForTest(std::string const& testName)
|
||||
{
|
||||
// set the interceptor for the current test
|
||||
m_testContext.RenameTest(testName);
|
||||
return *m_client;
|
||||
}
|
||||
|
||||
void SetUp() override
|
||||
{
|
||||
StorageTest::SetUp();
|
||||
|
||||
auto options = InitClientOptions<Azure::Storage::Blobs::BlobClientOptions>();
|
||||
m_client = std::make_unique<Azure::Storage::Blobs::BlobServiceClient>(
|
||||
Azure::Storage::Blobs::BlobServiceClient::CreateFromConnectionString(
|
||||
StandardStorageConnectionString(), options));
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(BlobBatchClientTest, SubmitDeleteBatch)
|
||||
{
|
||||
const std::string testName = GetTestNameLowerCase();
|
||||
|
||||
const std::string containerName1 = testName + "1";
|
||||
const std::string blob1Name = "b1";
|
||||
const std::string blob2Name = "b2";
|
||||
const std::string containerName2 = testName + "2";
|
||||
const std::string blob3Name = "b3";
|
||||
|
||||
auto serviceClient = GetClientForTest(testName);
|
||||
auto container1Client = serviceClient.GetBlobContainerClient(containerName1);
|
||||
container1Client.CreateIfNotExists();
|
||||
auto container2Client = serviceClient.GetBlobContainerClient(containerName2);
|
||||
container2Client.CreateIfNotExists();
|
||||
auto blob1Client = container1Client.GetAppendBlobClient(blob1Name);
|
||||
blob1Client.Create();
|
||||
auto blob2Client = container1Client.GetAppendBlobClient(blob2Name);
|
||||
blob2Client.Create();
|
||||
auto blob3Client = container2Client.GetAppendBlobClient(blob3Name);
|
||||
blob3Client.Create();
|
||||
blob3Client.CreateSnapshot();
|
||||
|
||||
auto batch = serviceClient.CreateBatch();
|
||||
auto delete1Response = batch.DeleteBlob(blob1Client.GetUrl());
|
||||
auto delete2Response = batch.DeleteBlob(containerName1, blob2Name);
|
||||
Blobs::DeleteBlobOptions deleteOptions;
|
||||
deleteOptions.DeleteSnapshots = Blobs::Models::DeleteSnapshotsOption::OnlySnapshots;
|
||||
auto delete3Response = batch.DeleteBlob(blob3Client.GetUrl(), deleteOptions);
|
||||
auto submitBatchResponse = serviceClient.SubmitBatch(batch);
|
||||
|
||||
EXPECT_TRUE(delete1Response.GetResponse().Value.Deleted);
|
||||
EXPECT_TRUE(delete2Response.GetResponse().Value.Deleted);
|
||||
EXPECT_TRUE(delete3Response.GetResponse().Value.Deleted);
|
||||
EXPECT_THROW(blob1Client.GetProperties(), StorageException);
|
||||
EXPECT_THROW(blob2Client.GetProperties(), StorageException);
|
||||
EXPECT_NO_THROW(blob3Client.GetProperties());
|
||||
|
||||
container1Client.Delete();
|
||||
container2Client.Delete();
|
||||
}
|
||||
|
||||
TEST_F(BlobBatchClientTest, SubmitSetTierBatch_LIVEONLY_)
|
||||
{
|
||||
const std::string testName = GetTestNameLowerCase();
|
||||
|
||||
const std::string containerName = testName;
|
||||
const std::string blob1Name = "b1";
|
||||
const std::string blob2Name = "b2";
|
||||
|
||||
auto containerSasToken = [&]() {
|
||||
Sas::BlobSasBuilder sasBuilder;
|
||||
sasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
|
||||
sasBuilder.ExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(5);
|
||||
sasBuilder.BlobContainerName = containerName;
|
||||
sasBuilder.Resource = Sas::BlobSasResource::BlobContainer;
|
||||
sasBuilder.SetPermissions(Sas::BlobContainerSasPermissions::All);
|
||||
return sasBuilder.GenerateSasToken(
|
||||
*_internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential);
|
||||
}();
|
||||
|
||||
auto serviceClient = GetClientForTest(testName);
|
||||
serviceClient.GetBlobContainerClient(containerName).CreateIfNotExists();
|
||||
auto containerClient = Blobs::BlobContainerClient(
|
||||
serviceClient.GetBlobContainerClient(containerName).GetUrl() + containerSasToken,
|
||||
InitClientOptions<Blobs::BlobClientOptions>());
|
||||
auto blob1Client = containerClient.GetBlockBlobClient(blob1Name);
|
||||
blob1Client.UploadFrom(nullptr, 0);
|
||||
auto blob2Client = containerClient.GetBlockBlobClient(blob2Name);
|
||||
blob2Client.UploadFrom(nullptr, 0);
|
||||
|
||||
auto batch = containerClient.CreateBatch();
|
||||
auto setTier1Response
|
||||
= batch.SetBlobAccessTier(containerName, blob1Name, Blobs::Models::AccessTier::Cool);
|
||||
auto setTier2Response
|
||||
= batch.SetBlobAccessTier(blob2Client.GetUrl(), Blobs::Models::AccessTier::Archive);
|
||||
auto submitBatchResponse = containerClient.SubmitBatch(batch);
|
||||
|
||||
EXPECT_NO_THROW(setTier1Response.GetResponse());
|
||||
EXPECT_NO_THROW(setTier2Response.GetResponse());
|
||||
EXPECT_EQ(
|
||||
blob1Client.GetProperties().Value.AccessTier.Value(), Blobs::Models::AccessTier::Cool);
|
||||
EXPECT_EQ(
|
||||
blob2Client.GetProperties().Value.AccessTier.Value(), Blobs::Models::AccessTier::Archive);
|
||||
|
||||
serviceClient.DeleteBlobContainer(containerName);
|
||||
}
|
||||
|
||||
TEST_F(BlobBatchClientTest, TokenAuthorization)
|
||||
{
|
||||
const std::string testName = GetTestNameLowerCase();
|
||||
|
||||
std::shared_ptr<Azure::Core::Credentials::TokenCredential> credential
|
||||
= std::make_shared<Azure::Identity::ClientSecretCredential>(
|
||||
AadTenantId(), AadClientId(), AadClientSecret());
|
||||
Blobs::BlobClientOptions options;
|
||||
|
||||
auto serviceClient = InitTestClient<Blobs::BlobServiceClient, Blobs::BlobClientOptions>(
|
||||
GetClientForTest(testName).GetUrl(), credential, options);
|
||||
|
||||
const std::string containerName = testName;
|
||||
const std::string blobName = "b1";
|
||||
|
||||
auto containerClient = serviceClient->GetBlobContainerClient(containerName);
|
||||
containerClient.CreateIfNotExists();
|
||||
auto blobClient = containerClient.GetAppendBlobClient(blobName);
|
||||
blobClient.Create();
|
||||
|
||||
auto batch = containerClient.CreateBatch();
|
||||
auto delete1Response = batch.DeleteBlob(blobClient.GetUrl());
|
||||
auto submitBatchResponse = containerClient.SubmitBatch(batch);
|
||||
|
||||
EXPECT_TRUE(delete1Response.GetResponse().Value.Deleted);
|
||||
|
||||
containerClient.Delete();
|
||||
}
|
||||
|
||||
TEST_F(BlobBatchClientTest, Exceptions_LIVEONLY_)
|
||||
{
|
||||
const std::string testName = GetTestNameLowerCase();
|
||||
|
||||
const std::string containerName = testName;
|
||||
const std::string blobName = "b1";
|
||||
|
||||
auto serviceClient = GetClientForTest(testName);
|
||||
auto containerClient = serviceClient.GetBlobContainerClient(containerName);
|
||||
containerClient.CreateIfNotExists();
|
||||
auto blobClient = containerClient.GetBlockBlobClient(blobName);
|
||||
blobClient.UploadFrom(nullptr, 0);
|
||||
|
||||
// Empty batch
|
||||
auto batch = containerClient.CreateBatch();
|
||||
|
||||
try
|
||||
{
|
||||
containerClient.SubmitBatch(batch);
|
||||
FAIL();
|
||||
}
|
||||
catch (StorageException& e)
|
||||
{
|
||||
EXPECT_EQ(e.StatusCode, Azure::Core::Http::HttpStatusCode::BadRequest);
|
||||
EXPECT_FALSE(e.ReasonPhrase.empty());
|
||||
EXPECT_FALSE(e.RequestId.empty());
|
||||
EXPECT_FALSE(e.ClientRequestId.empty());
|
||||
EXPECT_EQ(e.ErrorCode, "InvalidInput");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL();
|
||||
}
|
||||
|
||||
// Partial failure
|
||||
{
|
||||
auto r1 = batch.SetBlobAccessTier(blobClient.GetUrl(), Blobs::Models::AccessTier::Hot);
|
||||
auto r2 = batch.SetBlobAccessTier(
|
||||
containerName, "BlobNameNotExists", Blobs::Models::AccessTier::Hot);
|
||||
EXPECT_NO_THROW(containerClient.SubmitBatch(batch));
|
||||
EXPECT_NO_THROW(r1.GetResponse());
|
||||
EXPECT_THROW(r2.GetResponse(), StorageException);
|
||||
}
|
||||
|
||||
// Mixed operations
|
||||
auto batch2 = containerClient.CreateBatch();
|
||||
batch2.SetBlobAccessTier(blobClient.GetUrl(), Blobs::Models::AccessTier::Cool);
|
||||
batch2.DeleteBlob(blobClient.GetUrl());
|
||||
|
||||
try
|
||||
{
|
||||
containerClient.SubmitBatch(batch2);
|
||||
FAIL();
|
||||
}
|
||||
catch (StorageException& e)
|
||||
{
|
||||
EXPECT_EQ(e.StatusCode, Azure::Core::Http::HttpStatusCode::BadRequest);
|
||||
EXPECT_FALSE(e.ReasonPhrase.empty());
|
||||
EXPECT_FALSE(e.RequestId.empty());
|
||||
EXPECT_FALSE(e.ClientRequestId.empty());
|
||||
EXPECT_EQ(e.ErrorCode, "AllBatchSubRequestsShouldBeSameApi");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL();
|
||||
}
|
||||
|
||||
auto containerExpiredSasToken = [&]() {
|
||||
Sas::BlobSasBuilder sasBuilder;
|
||||
sasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
|
||||
sasBuilder.ExpiresOn = std::chrono::system_clock::now() - std::chrono::minutes(5);
|
||||
sasBuilder.BlobContainerName = containerName;
|
||||
sasBuilder.Resource = Sas::BlobSasResource::BlobContainer;
|
||||
sasBuilder.SetPermissions(Sas::BlobContainerSasPermissions::All);
|
||||
return sasBuilder.GenerateSasToken(
|
||||
*_internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential);
|
||||
}();
|
||||
auto containerSasToken = [&]() {
|
||||
Sas::BlobSasBuilder sasBuilder;
|
||||
sasBuilder.Protocol = Sas::SasProtocol::HttpsAndHttp;
|
||||
sasBuilder.ExpiresOn = std::chrono::system_clock::now() + std::chrono::minutes(5);
|
||||
sasBuilder.BlobContainerName = containerName;
|
||||
sasBuilder.Resource = Sas::BlobSasResource::BlobContainer;
|
||||
sasBuilder.SetPermissions(Sas::BlobContainerSasPermissions::All);
|
||||
return sasBuilder.GenerateSasToken(
|
||||
*_internal::ParseConnectionString(StandardStorageConnectionString()).KeyCredential);
|
||||
}();
|
||||
auto containerSasClient = Blobs::BlobContainerClient(
|
||||
serviceClient.GetBlobContainerClient(containerName).GetUrl() + containerExpiredSasToken);
|
||||
auto batch3 = containerSasClient.CreateBatch();
|
||||
batch3.DeleteBlob(blobClient.GetUrl() + containerSasToken);
|
||||
try
|
||||
{
|
||||
containerSasClient.SubmitBatch(batch3);
|
||||
FAIL();
|
||||
}
|
||||
catch (StorageException& e)
|
||||
{
|
||||
EXPECT_EQ(e.StatusCode, Azure::Core::Http::HttpStatusCode::Forbidden);
|
||||
EXPECT_FALSE(e.ReasonPhrase.empty());
|
||||
EXPECT_FALSE(e.RequestId.empty());
|
||||
EXPECT_FALSE(e.ClientRequestId.empty());
|
||||
EXPECT_EQ(e.ErrorCode, "AuthenticationFailed");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FAIL();
|
||||
}
|
||||
|
||||
containerClient.Delete();
|
||||
}
|
||||
|
||||
}}} // namespace Azure::Storage::Test
|
||||
@ -0,0 +1,144 @@
|
||||
{
|
||||
"networkCallRecords": [
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "826b7081-9e8a-4486-4dbd-754bafda9877",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:58 GMT",
|
||||
"etag": "\"0x8DA58E4948E254B\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:59 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "826b7081-9e8a-4486-4dbd-754bafda9877",
|
||||
"x-ms-request-id": "4f35247c-201e-009a-5dcd-8a441b000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/exceptions?restype=container"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "805b8546-ca84-4de3-5cad-e9a4bf1d70ad",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"content-md5": "1B2M2Y8AsgTpgAmY7PhCfg==",
|
||||
"date": "Tue, 28 Jun 2022 08:59:58 GMT",
|
||||
"etag": "\"0x8DA58E494C38EF9\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:59 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "805b8546-ca84-4de3-5cad-e9a4bf1d70ad",
|
||||
"x-ms-content-crc64": "AAAAAAAAAAA=",
|
||||
"x-ms-request-id": "4f3524b7-201e-009a-10cd-8a441b000000",
|
||||
"x-ms-request-server-encrypted": "true",
|
||||
"x-ms-version": "2020-10-02",
|
||||
"x-ms-version-id": "2022-06-28T08:59:59.7978361Z"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/exceptions/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"content-type": "multipart/mixed; boundary=batch_ffe1c850-d4aa-4dd1-411c-120c5c215be2",
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "7391bd0f-94eb-4b07-5e9f-a3c79a7ae138",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "POST",
|
||||
"Response": {
|
||||
"BODY": "--batchresponse_706f8051-2324-4b39-8185-62332e77e61c\r\nContent-Type: application/http\r\n\r\nHTTP/1.1 400 One of the request inputs is not valid.\r\nx-ms-error-code: InvalidInput\r\nx-ms-request-id: 4f352503-201e-009a-56cd-8a441b000000\r\nx-ms-version: 2020-10-02\r\nx-ms-client-request-id: 7391bd0f-94eb-4b07-5e9f-a3c79a7ae138\r\nContent-Length: 221\r\nContent-Type: application/xml\r\nServer: Windows-Azure-Blob/1.0\r\n\r\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Error><Code>InvalidInput</Code><Message>One of the request inputs is not valid.\nRequestId:4f352503-201e-009a-56cd-8a441b000000\nTime:2022-06-28T09:00:00.2423323Z</Message></Error>\r\n--batchresponse_706f8051-2324-4b39-8185-62332e77e61c--",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"content-type": "multipart/mixed; boundary=batchresponse_706f8051-2324-4b39-8185-62332e77e61c",
|
||||
"date": "Tue, 28 Jun 2022 08:59:59 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"transfer-encoding": "chunked",
|
||||
"x-ms-client-request-id": "7391bd0f-94eb-4b07-5e9f-a3c79a7ae138",
|
||||
"x-ms-request-id": "4f352503-201e-009a-56cd-8a441b000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/exceptions?comp=batch&restype=container"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "e8577d2f-290e-450c-5780-c0b616d93dd6",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/exceptions/b1?comp=tier"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "aeacc093-2a2c-4650-7acf-71b99a83fee9",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/exceptions/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"content-type": "multipart/mixed; boundary=batch_44147b73-099b-48bf-593d-d5da18fc33a6",
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "0888f4dd-0214-4a2b-4cce-58018c2e88ea",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "POST",
|
||||
"Response": {
|
||||
"BODY": "--batchresponse_fab35cab-6d23-4c95-8dd1-edc59aafd881\r\nContent-Type: application/http\r\n\r\nHTTP/1.1 400 All batch subrequests should be the same api.\r\nx-ms-error-code: AllBatchSubRequestsShouldBeSameApi\r\nx-ms-request-id: 4f3525f8-201e-009a-28cd-8a441b000000\r\nx-ms-version: 2020-10-02\r\nx-ms-client-request-id: 0888f4dd-0214-4a2b-4cce-58018c2e88ea\r\nContent-Length: 249\r\nContent-Type: application/xml\r\nServer: Windows-Azure-Blob/1.0\r\n\r\n<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<Error><Code>AllBatchSubRequestsShouldBeSameApi</Code><Message>All batch subrequests should be the same api.\nRequestId:4f3525f8-201e-009a-28cd-8a441b000000\nTime:2022-06-28T09:00:01.1408151Z</Message></Error>\r\n--batchresponse_fab35cab-6d23-4c95-8dd1-edc59aafd881--",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"content-type": "multipart/mixed; boundary=batchresponse_fab35cab-6d23-4c95-8dd1-edc59aafd881",
|
||||
"date": "Tue, 28 Jun 2022 09:00:00 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"transfer-encoding": "chunked",
|
||||
"x-ms-client-request-id": "0888f4dd-0214-4a2b-4cce-58018c2e88ea",
|
||||
"x-ms-request-id": "4f3525f8-201e-009a-28cd-8a441b000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/exceptions?comp=batch&restype=container"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "f7d9ca6e-1362-4478-56c6-4588683fafa6",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 09:00:02 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "f7d9ca6e-1362-4478-56c6-4588683fafa6",
|
||||
"x-ms-request-id": "a432dc6f-d01e-0055-0fcd-8aca49000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/exceptions?restype=container"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,383 @@
|
||||
{
|
||||
"networkCallRecords": [
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "c4d555c2-46b5-4589-4208-ac374c2d53db",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:41 GMT",
|
||||
"etag": "\"0x8DA58E48A27F380\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:42 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "c4d555c2-46b5-4589-4208-ac374c2d53db",
|
||||
"x-ms-request-id": "1f791515-601e-008b-5ecd-8adeaf000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1?restype=container"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "ec0e9d4b-555b-435e-70d0-e4cd7e234b5e",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:41 GMT",
|
||||
"etag": "\"0x8DA58E48A5B09E0\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:42 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "ec0e9d4b-555b-435e-70d0-e4cd7e234b5e",
|
||||
"x-ms-request-id": "1f7915c0-601e-008b-72cd-8adeaf000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch2?restype=container"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "b22b2bb1-6f86-410c-66df-0e12f55b1be6",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:42 GMT",
|
||||
"etag": "\"0x8DA58E48A9467D5\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:42 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "b22b2bb1-6f86-410c-66df-0e12f55b1be6",
|
||||
"x-ms-request-id": "1f79163d-601e-008b-5ecd-8adeaf000000",
|
||||
"x-ms-request-server-encrypted": "true",
|
||||
"x-ms-version": "2020-10-02",
|
||||
"x-ms-version-id": "2022-06-28T08:59:42.7115989Z"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "70f62543-8fac-4d16-42aa-47fa382ee321",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:42 GMT",
|
||||
"etag": "\"0x8DA58E48AC50DA1\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:43 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "70f62543-8fac-4d16-42aa-47fa382ee321",
|
||||
"x-ms-request-id": "1f7916c7-601e-008b-57cd-8adeaf000000",
|
||||
"x-ms-request-server-encrypted": "true",
|
||||
"x-ms-version": "2020-10-02",
|
||||
"x-ms-version-id": "2022-06-28T08:59:43.0304161Z"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1/b2"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "1cfcb7ff-8964-44db-7c21-19604e60a773",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:42 GMT",
|
||||
"etag": "\"0x8DA58E48AF760E5\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:43 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "1cfcb7ff-8964-44db-7c21-19604e60a773",
|
||||
"x-ms-request-id": "1f791770-601e-008b-67cd-8adeaf000000",
|
||||
"x-ms-request-server-encrypted": "true",
|
||||
"x-ms-version": "2020-10-02",
|
||||
"x-ms-version-id": "2022-06-28T08:59:43.3612275Z"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch2/b3"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "4a05a52d-36df-4b3b-799a-b911c57f363a",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:43 GMT",
|
||||
"etag": "\"0x8DA58E48AF760E5\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:43 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "4a05a52d-36df-4b3b-799a-b911c57f363a",
|
||||
"x-ms-request-id": "1f791826-601e-008b-11cd-8adeaf000000",
|
||||
"x-ms-request-server-encrypted": "false",
|
||||
"x-ms-snapshot": "2022-06-28T08:59:43.6820453Z",
|
||||
"x-ms-version": "2020-10-02",
|
||||
"x-ms-version-id": "2022-06-28T08:59:43.6830453Z"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch2/b3?comp=snapshot"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "66705a78-b4b8-4785-5a18-2403a38abd57",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "89852895-6f69-4528-74f6-dddc86a96f88",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1/b2"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "1eaf9c79-0de1-40dc-781e-cc3c3cf5552f",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch2/b3"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"content-type": "multipart/mixed; boundary=batch_d3d5b5bc-7b2f-452a-7089-d737922908bd",
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "8d3bdb6a-9b67-48e2-4d4f-e00b3ab2853b",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "POST",
|
||||
"Response": {
|
||||
"BODY": "--batchresponse_3b60398a-cb61-4c44-8ddb-c3b37829cbb1\r\nContent-Type: application/http\r\nContent-ID: 0\r\n\r\nHTTP/1.1 202 Accepted\r\nx-ms-delete-type-permanent: true\r\nx-ms-request-id: 1f7918a1-601e-008b-73cd-8adeaf1e293f\r\nx-ms-version: 2020-10-02\r\nx-ms-client-request-id: 66705a78-b4b8-4785-5a18-2403a38abd57\r\nServer: Windows-Azure-Blob/1.0\r\n\r\n--batchresponse_3b60398a-cb61-4c44-8ddb-c3b37829cbb1\r\nContent-Type: application/http\r\nContent-ID: 1\r\n\r\nHTTP/1.1 202 Accepted\r\nx-ms-delete-type-permanent: true\r\nx-ms-request-id: 1f7918a1-601e-008b-73cd-8adeaf1e2941\r\nx-ms-version: 2020-10-02\r\nx-ms-client-request-id: 89852895-6f69-4528-74f6-dddc86a96f88\r\nServer: Windows-Azure-Blob/1.0\r\n\r\n--batchresponse_3b60398a-cb61-4c44-8ddb-c3b37829cbb1\r\nContent-Type: application/http\r\nContent-ID: 2\r\n\r\nHTTP/1.1 202 Accepted\r\nx-ms-delete-type-permanent: true\r\nx-ms-request-id: 1f7918a1-601e-008b-73cd-8adeaf1e2942\r\nx-ms-version: 2020-10-02\r\nx-ms-client-request-id: 1eaf9c79-0de1-40dc-781e-cc3c3cf5552f\r\nServer: Windows-Azure-Blob/1.0\r\n\r\n--batchresponse_3b60398a-cb61-4c44-8ddb-c3b37829cbb1--",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"content-type": "multipart/mixed; boundary=batchresponse_3b60398a-cb61-4c44-8ddb-c3b37829cbb1",
|
||||
"date": "Tue, 28 Jun 2022 08:59:43 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"transfer-encoding": "chunked",
|
||||
"x-ms-client-request-id": "8d3bdb6a-9b67-48e2-4d4f-e00b3ab2853b",
|
||||
"x-ms-request-id": "1f7918a1-601e-008b-73cd-8adeaf000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net?comp=batch"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "f95d874a-41d1-4f33-60f2-c4504cf88b47",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"Server": "Windows-Azure-Blob/1.0",
|
||||
"x-ms-client-request-id": "66705a78-b4b8-4785-5a18-2403a38abd57",
|
||||
"x-ms-delete-type-permanent": "true",
|
||||
"x-ms-request-id": "1f7918a1-601e-008b-73cd-8adeaf1e293f",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "faa2b291-3ca7-4212-47b8-7c3ff1b057b9",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"Server": "Windows-Azure-Blob/1.0",
|
||||
"x-ms-client-request-id": "89852895-6f69-4528-74f6-dddc86a96f88",
|
||||
"x-ms-delete-type-permanent": "true",
|
||||
"x-ms-request-id": "1f7918a1-601e-008b-73cd-8adeaf1e2941",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1/b2"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "f4a2acba-2d48-4b75-5d90-d48bbbddf5cd",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"Server": "Windows-Azure-Blob/1.0",
|
||||
"x-ms-client-request-id": "1eaf9c79-0de1-40dc-781e-cc3c3cf5552f",
|
||||
"x-ms-delete-type-permanent": "true",
|
||||
"x-ms-request-id": "1f7918a1-601e-008b-73cd-8adeaf1e2942",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch2/b3"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "6faf1d4d-d581-4919-6822-8202ee5d71cc",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "HEAD",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "The specified blob does not exist.",
|
||||
"STATUS_CODE": "404",
|
||||
"date": "Tue, 28 Jun 2022 08:59:44 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"transfer-encoding": "chunked",
|
||||
"vary": "Origin",
|
||||
"x-ms-client-request-id": "6faf1d4d-d581-4919-6822-8202ee5d71cc",
|
||||
"x-ms-error-code": "BlobNotFound",
|
||||
"x-ms-request-id": "1f7919bf-601e-008b-69cd-8adeaf000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "b6b68bfc-20fe-4738-6d4c-deb09f681a69",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "HEAD",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "The specified blob does not exist.",
|
||||
"STATUS_CODE": "404",
|
||||
"date": "Tue, 28 Jun 2022 08:59:44 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"transfer-encoding": "chunked",
|
||||
"vary": "Origin",
|
||||
"x-ms-client-request-id": "b6b68bfc-20fe-4738-6d4c-deb09f681a69",
|
||||
"x-ms-error-code": "BlobNotFound",
|
||||
"x-ms-request-id": "1f791a2a-601e-008b-49cd-8adeaf000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1/b2"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "6ad0e0bb-2072-4dc4-5f7d-e16fa12967c2",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "HEAD",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "OK",
|
||||
"STATUS_CODE": "200",
|
||||
"accept-ranges": "bytes",
|
||||
"content-length": "0",
|
||||
"content-type": "application/octet-stream",
|
||||
"date": "Tue, 28 Jun 2022 08:59:44 GMT",
|
||||
"etag": "\"0x8DA58E48AF760E5\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:43 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"vary": "Origin",
|
||||
"x-ms-blob-committed-block-count": "0",
|
||||
"x-ms-blob-type": "AppendBlob",
|
||||
"x-ms-client-request-id": "6ad0e0bb-2072-4dc4-5f7d-e16fa12967c2",
|
||||
"x-ms-creation-time": "Tue, 28 Jun 2022 08:59:43 GMT",
|
||||
"x-ms-is-current-version": "true",
|
||||
"x-ms-lease-state": "available",
|
||||
"x-ms-lease-status": "unlocked",
|
||||
"x-ms-request-id": "1f791a9e-601e-008b-32cd-8adeaf000000",
|
||||
"x-ms-server-encrypted": "true",
|
||||
"x-ms-version": "2020-10-02",
|
||||
"x-ms-version-id": "2022-06-28T08:59:43.6830453Z"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch2/b3"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "c9f892df-380a-4c35-7cc0-2fac5cf42322",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:45 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "c9f892df-380a-4c35-7cc0-2fac5cf42322",
|
||||
"x-ms-request-id": "1f791b04-601e-008b-07cd-8adeaf000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch1?restype=container"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "26d494dc-1836-4876-6054-4fee745bfe84",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:45 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "26d494dc-1836-4876-6054-4fee745bfe84",
|
||||
"x-ms-request-id": "1f791b6c-601e-008b-5ecd-8adeaf000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/submitdeletebatch2?restype=container"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
{
|
||||
"networkCallRecords": [
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "75b45834-afba-4e35-407f-f6a08f365fd4",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:54 GMT",
|
||||
"etag": "\"0x8DA58E491EF7D15\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:55 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "75b45834-afba-4e35-407f-f6a08f365fd4",
|
||||
"x-ms-request-id": "3ae4ef42-d01e-0018-7bcd-8a05a5000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/tokenauthorization?restype=container"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "0cdd77c6-688c-45df-69cd-7a578b609496",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "PUT",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Created",
|
||||
"STATUS_CODE": "201",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:55 GMT",
|
||||
"etag": "\"0x8DA58E4921FACD3\"",
|
||||
"last-modified": "Tue, 28 Jun 2022 08:59:55 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "0cdd77c6-688c-45df-69cd-7a578b609496",
|
||||
"x-ms-request-id": "3ae4efb9-d01e-0018-5ecd-8a05a5000000",
|
||||
"x-ms-request-server-encrypted": "true",
|
||||
"x-ms-version": "2020-10-02",
|
||||
"x-ms-version-id": "2022-06-28T08:59:55.3683667Z"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/tokenauthorization/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "030920c6-6578-4c1a-6f2c-de11eaf8814c",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/tokenauthorization/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"content-type": "multipart/mixed; boundary=batch_635fcca6-0ac8-447f-7677-e48bd5f17210",
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "93311fa2-726f-43a0-72fa-a59527bae4e3",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "POST",
|
||||
"Response": {
|
||||
"BODY": "--batchresponse_4fa0dc98-76e2-4af3-a696-c3f8b59d5778\r\nContent-Type: application/http\r\nContent-ID: 0\r\n\r\nHTTP/1.1 202 Accepted\r\nx-ms-delete-type-permanent: true\r\nx-ms-request-id: 3ae4f1f2-d01e-0018-51cd-8a05a51e08dc\r\nx-ms-version: 2020-10-02\r\nx-ms-client-request-id: 030920c6-6578-4c1a-6f2c-de11eaf8814c\r\nServer: Windows-Azure-Blob/1.0\r\n\r\n--batchresponse_4fa0dc98-76e2-4af3-a696-c3f8b59d5778--",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"content-type": "multipart/mixed; boundary=batchresponse_4fa0dc98-76e2-4af3-a696-c3f8b59d5778",
|
||||
"date": "Tue, 28 Jun 2022 08:59:57 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"transfer-encoding": "chunked",
|
||||
"x-ms-client-request-id": "93311fa2-726f-43a0-72fa-a59527bae4e3",
|
||||
"x-ms-request-id": "3ae4f1f2-d01e-0018-51cd-8a05a5000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/tokenauthorization?comp=batch&restype=container"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "67e38e19-f43e-4408-59bc-cddf6a734d50",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"Server": "Windows-Azure-Blob/1.0",
|
||||
"x-ms-client-request-id": "030920c6-6578-4c1a-6f2c-de11eaf8814c",
|
||||
"x-ms-delete-type-permanent": "true",
|
||||
"x-ms-request-id": "3ae4f1f2-d01e-0018-51cd-8a05a51e08dc",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/tokenauthorization/b1"
|
||||
},
|
||||
{
|
||||
"Headers": {
|
||||
"user-agent": "azsdk-cpp-storage-blobs/12.5.0-beta.2 (Windows 10 Pro 6.3 19044 19041.1.amd64fre.vb_release.191206-1406)",
|
||||
"x-ms-client-request-id": "13964705-9bc8-4a32-46f2-69b38ff0aac5",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Method": "DELETE",
|
||||
"Response": {
|
||||
"BODY": "",
|
||||
"REASON_PHRASE": "Accepted",
|
||||
"STATUS_CODE": "202",
|
||||
"content-length": "0",
|
||||
"date": "Tue, 28 Jun 2022 08:59:57 GMT",
|
||||
"server": "Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0",
|
||||
"x-ms-client-request-id": "13964705-9bc8-4a32-46f2-69b38ff0aac5",
|
||||
"x-ms-request-id": "3ae4f3d7-d01e-0018-7ecd-8a05a5000000",
|
||||
"x-ms-version": "2020-10-02"
|
||||
},
|
||||
"Url": "https://REDACTED.blob.core.windows.net/tokenauthorization?restype=container"
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user