Auto-generated protocol layer of Container and BlockBlob (#144)

* add blob service auto generated protocol layer

* Add storage uri builder

* integrate with pipeline

* Add libxml2 dependency

* Fix variables name

* Rename variable and function name

* Move xxx_options.hpp out of internal directory

* Rename FromConnectionString -> CreateFromConnectionString
This commit is contained in:
JinmingHu 2020-06-04 13:21:08 +08:00 committed by GitHub
parent 502d3f15a9
commit 5fc6fea9f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 3959 additions and 8 deletions

View File

@ -38,3 +38,4 @@ include(global_compile_options)
# sub-projects
add_subdirectory(sdk/core/azure-core)
add_subdirectory(sdk/samples/http_client/curl) # will work only if BUILD_CURL_TRANSPORT=ON
add_subdirectory(sdk/storage)

View File

@ -9,48 +9,48 @@ jobs:
matrix:
Linux_x64:
vm.image: 'ubuntu-18.04'
vcpkg.deps: 'curl[ssl]'
vcpkg.deps: 'curl[ssl] libxml2'
VCPKG_DEFAULT_TRIPLET: 'x64-linux'
Win_x86:
vm.image: 'windows-2019'
vcpkg.deps: 'curl[winssl]'
vcpkg.deps: 'curl[winssl] libxml2'
VCPKG_DEFAULT_TRIPLET: 'x86-windows-static'
CMAKE_GENERATOR: 'Visual Studio 16 2019'
CMAKE_GENERATOR_PLATFORM: Win32
Win_x64:
vm.image: 'windows-2019'
vcpkg.deps: 'curl[winssl]'
vcpkg.deps: 'curl[winssl] libxml2'
VCPKG_DEFAULT_TRIPLET: 'x64-windows-static'
CMAKE_GENERATOR: 'Visual Studio 16 2019'
CMAKE_GENERATOR_PLATFORM: x64
MacOS_x64:
vm.image: 'macOS-10.14'
vcpkg.deps: 'curl[ssl]'
vcpkg.deps: 'curl[ssl] libxml2'
VCPKG_DEFAULT_TRIPLET: 'x64-osx'
# Unit testing ON
Linux_x64_with_unit_test:
vm.image: 'ubuntu-18.04'
vcpkg.deps: 'curl[ssl]'
vcpkg.deps: 'curl[ssl] libxml2'
VCPKG_DEFAULT_TRIPLET: 'x64-linux'
build.args: ' -DBUILD_TESTING=ON'
Win_x86_with_unit_test:
vm.image: 'windows-2019'
vcpkg.deps: 'curl[winssl]'
vcpkg.deps: 'curl[winssl] libxml2'
VCPKG_DEFAULT_TRIPLET: 'x86-windows-static'
CMAKE_GENERATOR: 'Visual Studio 16 2019'
CMAKE_GENERATOR_PLATFORM: Win32
build.args: ' -DBUILD_TESTING=ON'
Win_x64_with_unit_test:
vm.image: 'windows-2019'
vcpkg.deps: 'curl[winssl]'
vcpkg.deps: 'curl[winssl] libxml2'
VCPKG_DEFAULT_TRIPLET: 'x64-windows-static'
CMAKE_GENERATOR: 'Visual Studio 16 2019'
CMAKE_GENERATOR_PLATFORM: x64
build.args: ' -DBUILD_TESTING=ON'
MacOS_x64_with_unit_test:
vm.image: 'macOS-10.14'
vcpkg.deps: 'curl[ssl]'
vcpkg.deps: 'curl[ssl] libxml2'
VCPKG_DEFAULT_TRIPLET: 'x64-osx'
build.args: ' -DBUILD_TESTING=ON'
pool:

View File

@ -0,0 +1,65 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: MIT
cmake_minimum_required (VERSION 3.12)
project (azure-storage LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED True)
option(BUILD_STORAGE_SAMPLES "Build sample codes" ON)
set(AZURE_STORAGE_HEADER
inc/common/storage_common.hpp
inc/common/storage_credential.hpp
inc/common/storage_url_builder.hpp
inc/blobs/blob.hpp
inc/blobs/blob_client.hpp
inc/blobs/block_blob_client.hpp
inc/blobs/blob_container_client.hpp
inc/blobs/blob_client_options.hpp
inc/blobs/blob_container_client_options.hpp
inc/blobs/internal/protocol/blob_rest_client.hpp
)
set(AZURE_STORAGE_SOURCE
src/blobs/blob_client.cpp
src/blobs/block_blob_client.cpp
src/blobs/blob_container_client.cpp
src/common/storage_credential.cpp
src/common/storage_url_builder.cpp
)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
add_library(azure-storage ${AZURE_STORAGE_HEADER} ${AZURE_STORAGE_SOURCE})
find_package(LibXml2 REQUIRED)
target_include_directories(azure-storage PUBLIC ${LIBXML2_INCLUDE_DIR} $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc> $<INSTALL_INTERFACE:include/azure_storage>)
target_link_libraries(azure-storage azure-core ${LIBXML2_LIBRARIES})
if(MSVC)
target_compile_definitions(azure-storage PRIVATE NOMINMAX)
endif()
# Set version numbers centralized
set(AZURE_STORAGE_VERSION_MAJOR 0)
set(AZURE_STORAGE_VERSION_MINOR 1)
set(AZURE_STORAGE_VERSION_REVISION 0)
#install(DIRECTORY include/ DESTINATION include)
#install(TARGETS azure-storage
# ARCHIVE DESTINATION lib
# LIBRARY DESTINATION lib
# RUNTIME DESTINATION bin)
if(BUILD_STORAGE_SAMPLES)
add_subdirectory(sample)
endif()
# make sure that users can consume the project as a library.
add_library (azure::storage ALIAS azure-storage)

View File

@ -0,0 +1,8 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include "blobs/blob_container_client.hpp"
#include "blobs/blob_client.hpp"
#include "blobs/block_blob_client.hpp"

View File

@ -0,0 +1,63 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <memory>
#include <string>
#include <map>
#include "common/storage_url_builder.hpp"
#include "common/storage_credential.hpp"
#include "blob_client_options.hpp"
#include "internal/protocol/blob_rest_client.hpp"
namespace Azure { namespace Storage { namespace Blobs {
class BlobClient {
public:
// connection string
static BlobClient CreateFromConnectionString(
const std::string& connectionString,
const std::string& containerName,
const std::string& blobName,
const BlobClientOptions& options = BlobClientOptions());
// shared key auth
explicit BlobClient(
const std::string& blobUri,
std::shared_ptr<SharedKeyCredential> credential,
const BlobClientOptions& options = BlobClientOptions());
// token auth
explicit BlobClient(
const std::string& blobUri,
std::shared_ptr<TokenCredential> credential,
const BlobClientOptions& options = BlobClientOptions());
// anonymous/SAS/customized pipeline auth
explicit BlobClient(
const std::string& blobUri,
const BlobClientOptions& options = BlobClientOptions());
BlobClient WithSnapshot(const std::string& snapshot);
BlobProperties GetProperties(
const GetBlobPropertiesOptions& options = GetBlobPropertiesOptions());
BlobInfo SetHttpHeaders(const SetBlobHttpHeadersOptions& options = SetBlobHttpHeadersOptions());
BlobInfo SetMetadata(
std::map<std::string, std::string> metadata,
const SetBlobMetadataOptions& options = SetBlobMetadataOptions());
FlattenedDownloadProperties Download(
const DownloadBlobOptions& options = DownloadBlobOptions());
BasicResponse Delete(const DeleteBlobOptions& options = DeleteBlobOptions());
protected:
UrlBuilder m_blobUrl;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;
};
}}} // namespace Azure::Storage::Blobs

View File

@ -0,0 +1,89 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <string>
#include <limits>
#include <utility>
#include "internal/protocol/blob_rest_client.hpp"
namespace Azure { namespace Storage { namespace Blobs {
struct BlobClientOptions
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
};
struct BlockBlobClientOptions : public BlobClientOptions
{
};
struct GetBlobPropertiesOptions
{
Azure::Core::Context Context;
};
struct SetBlobHttpHeadersOptions
{
Azure::Core::Context Context;
std::string ContentType;
std::string ContentEncoding;
std::string ContentLanguage;
std::string ContentMD5;
std::string CacheControl;
std::string ContentDisposition;
};
struct SetBlobMetadataOptions
{
Azure::Core::Context Context;
};
struct DownloadBlobOptions
{
Azure::Core::Context Context;
uint64_t Offset = std::numeric_limits<uint64_t>::max();
uint64_t Length = 0;
};
struct DeleteBlobOptions
{
Azure::Core::Context Context;
DeleteSnapshotsOption DeleteSnapshots = DeleteSnapshotsOption::None;
};
struct UploadBlobOptions
{
Azure::Core::Context Context;
std::string ContentMD5;
std::string ContentCRC64;
BlobHttpHeaders Properties;
std::map<std::string, std::string> Metadata;
AccessTier Tier = AccessTier::Unknown;
};
struct StageBlockOptions
{
Azure::Core::Context Context;
std::string ContentMD5;
std::string ContentCRC64;
};
struct CommitBlockListOptions
{
Azure::Core::Context Context;
BlobHttpHeaders Properties;
std::map<std::string, std::string> Metadata;
AccessTier Tier = AccessTier::Unknown;
};
struct GetBlockListOptions
{
Azure::Core::Context Context;
BlockListTypeOption ListType = BlockListTypeOption::All;
};
}}} // namespace Azure::Storage::Blobs

View File

@ -0,0 +1,64 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <string>
#include <map>
#include <memory>
#include "blobs/blob_client.hpp"
#include "common/storage_url_builder.hpp"
#include "common/storage_credential.hpp"
#include "blob_container_client_options.hpp"
#include "internal/protocol/blob_rest_client.hpp"
namespace Azure { namespace Storage { namespace Blobs {
class BlobContainerClient {
public:
// connection string
static BlobContainerClient CreateFromConnectionString(
const std::string& connectionString,
const std::string& containerName,
const BlobContainerClientOptions& options = BlobContainerClientOptions());
// shared key auth
explicit BlobContainerClient(
const std::string& containerUri,
std::shared_ptr<SharedKeyCredential> credential,
const BlobContainerClientOptions& options = BlobContainerClientOptions());
// token auth
explicit BlobContainerClient(
const std::string& containerUri,
std::shared_ptr<TokenCredential> credential,
const BlobContainerClientOptions& options = BlobContainerClientOptions());
// anonymous/SAS/customized pipeline auth
explicit BlobContainerClient(
const std::string& containerUri,
const BlobContainerClientOptions& options = BlobContainerClientOptions());
BlobClient GetBlobClient(const std::string& blobName);
BlobContainerInfo Create(
const CreateBlobContainerOptions& options = CreateBlobContainerOptions());
BasicResponse Delete(const DeleteBlobContainerOptions& options = DeleteBlobContainerOptions());
BlobContainerProperties GetProperties(
const GetBlobContainerPropertiesOptions& options = GetBlobContainerPropertiesOptions());
BlobContainerInfo SetMetadata(
std::map<std::string, std::string> metadata,
SetBlobContainerMetadataOptions options = SetBlobContainerMetadataOptions());
BlobsFlatSegment ListBlobs(const ListBlobsOptions& options = ListBlobsOptions());
private:
UrlBuilder m_ContainerUri;
std::shared_ptr<Azure::Core::Http::HttpPipeline> m_pipeline;
};
}}} // namespace Azure::Storage::Blobs

View File

@ -0,0 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <string>
#include <vector>
#include <map>
#include "internal/protocol/blob_rest_client.hpp"
namespace Azure { namespace Storage { namespace Blobs {
struct BlobContainerClientOptions
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
};
struct CreateBlobContainerOptions
{
Azure::Core::Context Context;
PublicAccessType AccessType = PublicAccessType::Private;
std::map<std::string, std::string> Metadata;
};
struct DeleteBlobContainerOptions
{
Azure::Core::Context Context;
};
struct GetBlobContainerPropertiesOptions
{
Azure::Core::Context Context;
};
struct SetBlobContainerMetadataOptions
{
Azure::Core::Context Context;
};
struct ListBlobsOptions
{
Azure::Core::Context Context;
std::string Prefix;
std::string Delimiter;
std::string Marker;
int MaxResults = 0;
std::vector<ListBlobsIncludeItem> Include;
};
}}} // namespace Azure::Storage::Blobs

View File

@ -0,0 +1,65 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <map>
#include <string>
#include "blobs/blob_client.hpp"
#include "common/storage_credential.hpp"
#include "blob_client_options.hpp"
#include "internal/protocol/blob_rest_client.hpp"
namespace Azure { namespace Storage { namespace Blobs {
class BlockBlobClient : public BlobClient {
public:
// connection string
static BlockBlobClient CreateFromConnectionString(
const std::string& connectionString,
const std::string& containerName,
const std::string& blobName,
const BlockBlobClientOptions& options = BlockBlobClientOptions());
// shared key auth
explicit BlockBlobClient(
const std::string& blobUri,
std::shared_ptr<SharedKeyCredential> credential,
const BlockBlobClientOptions& options = BlockBlobClientOptions());
// token auth
explicit BlockBlobClient(
const std::string& blobUri,
std::shared_ptr<TokenCredential> credential,
const BlockBlobClientOptions& options = BlockBlobClientOptions());
// anonymous/SAS/customized pipeline auth
explicit BlockBlobClient(
const std::string& blobUri,
const BlockBlobClientOptions& options = BlockBlobClientOptions());
BlockBlobClient WithSnapshot(const std::string& snapshot);
BlobContentInfo Upload(
// TODO: We don't have BodyStream for now.
std::vector<uint8_t> content,
const UploadBlobOptions& options = UploadBlobOptions());
BlockInfo StageBlock(
const std::string& blockId,
// TODO: We don't have BodyStream for now.
std::vector<uint8_t> content,
const StageBlockOptions& options = StageBlockOptions());
BlobContentInfo CommitBlockList(
const std::vector<std::pair<BlockType, std::string>>& blockIds,
const CommitBlockListOptions& options = CommitBlockListOptions());
BlobBlockListInfo GetBlockList(const GetBlockListOptions& options = GetBlockListOptions());
private:
explicit BlockBlobClient(BlobClient blobClient);
};
}}} // namespace Azure::Storage::Blobs

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
namespace Azure { namespace Storage {
template <class... T> void unused(T&&...)
{
}
}} // namespace Azure::Storage

View File

@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <map>
#include <mutex>
#include <string>
namespace Azure { namespace Storage {
struct TokenCredential
{
explicit TokenCredential(std::string token) : Token(std::move(token))
{
}
void SetToken(std::string token)
{
std::lock_guard<std::mutex> guard(Mutex);
Token = std::move(token);
}
private:
std::string GetToken()
{
std::lock_guard<std::mutex> guard(Mutex);
return Token;
}
std::mutex Mutex;
std::string Token;
};
struct SharedKeyCredential
{
explicit SharedKeyCredential(std::string accountName, std::string accountKey)
: AccountName(std::move(accountName)), AccountKey(std::move(accountKey))
{
}
void SetAccountKey(std::string accountKey)
{
std::lock_guard<std::mutex> guard(Mutex);
AccountKey = std::move(accountKey);
}
std::string AccountName;
private:
std::string GetAccountKey()
{
std::lock_guard<std::mutex> guard(Mutex);
return AccountKey;
}
std::mutex Mutex;
std::string AccountKey;
};
std::map<std::string, std::string> ParseConnectionString(const std::string& connectionString);
}} // namespace Azure::Storage

View File

@ -0,0 +1,92 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <map>
#include <string>
#include <functional>
namespace Azure { namespace Storage {
class UrlBuilder {
public:
UrlBuilder()
{
}
// url must be url-encoded
explicit UrlBuilder(const std::string& url);
void SetScheme(const std::string& scheme)
{
m_scheme = scheme;
}
void SetHost(const std::string& host, bool do_encoding = false)
{
m_host = do_encoding ? EncodeHost(host) : host;
}
void SetPort(uint16_t port)
{
m_port = port;
}
void SetPath(const std::string& path, bool do_encoding = false)
{
m_path = do_encoding ? EncodePath(path) : path;
}
void AppendPath(const std::string& path, bool do_encoding = false)
{
if (!m_path.empty() && m_path.back() != '/')
{
m_path += '/';
}
m_path += do_encoding ? EncodePath(path) : path;
}
// query must be encoded
void SetQuery(const std::string& query);
void AppendQuery(const std::string& key, const std::string& value, bool do_encoding = false)
{
if (do_encoding)
{
m_query[EncodeQuery(key)] = EncodeQuery(value);
}
else
{
m_query[key] = value;
}
}
void RemoveQuery(const std::string& key)
{
m_query.erase(key);
}
void SetFragment(const std::string& fragment, bool do_encoding = false)
{
m_fragment = do_encoding ? EncodeFragment(fragment) : fragment;
}
std::string to_string() const;
private:
static std::string EncodeHost(const std::string& host);
static std::string EncodePath(const std::string& path);
static std::string EncodeQuery(const std::string& query);
static std::string EncodeFragment(const std::string& fragment);
static std::string EncodeImpl(const std::string& source, const std::function<bool(int)>& should_encode);
std::string m_scheme;
std::string m_host;
int m_port{-1};
std::string m_path; // encoded
std::map<std::string, std::string> m_query; // encoded
std::string m_fragment;
};
}} // namespace Azure::Storage

View File

@ -0,0 +1,15 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# SPDX-License-Identifier: MIT
cmake_minimum_required (VERSION 3.12)
if (BUILD_CURL_TRANSPORT)
add_executable (
azure-storage-sample
blob_getting_started.cpp
)
target_link_libraries(azure-storage-sample PRIVATE azure-storage)
endif()

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "blobs/blob.hpp"
#include <fstream>
#include <iostream>
int main()
{
using namespace Azure::Storage::Blobs;
BlobClient blob_client("https://targettest.blob.core.windows.net/container1/file_8M");
auto properties = blob_client.GetProperties();
std::cout << properties.ContentLength << std::endl;
auto downloaded_data = blob_client.Download();
std::cout << downloaded_data.BodyBuffer.size() << std::endl;
return 0;
}

View File

@ -0,0 +1,179 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "blobs/blob_client.hpp"
#include "common/storage_common.hpp"
#include "http/curl/curl.hpp"
namespace Azure { namespace Storage { namespace Blobs {
BlobClient BlobClient::CreateFromConnectionString(
const std::string& connectionString,
const std::string& containerName,
const std::string& blobName,
const BlobClientOptions& options)
{
auto parsedConnectionString = ParseConnectionString(connectionString);
std::string accountName;
std::string accountKey;
std::string blobEndpoint;
std::string EndpointSuffix;
std::string defaultEndpointsProtocol = ".core.windows.net";
auto ite = parsedConnectionString.find("AccountName");
if (ite != parsedConnectionString.end())
{
accountName = ite->second;
}
ite = parsedConnectionString.find("AccountKey");
if (ite != parsedConnectionString.end())
{
accountKey = ite->second;
}
ite = parsedConnectionString.find("BlobEndpoint");
if (ite != parsedConnectionString.end())
{
blobEndpoint = ite->second;
}
ite = parsedConnectionString.find("EndpointSuffix");
if (ite != parsedConnectionString.end())
{
EndpointSuffix = ite->second;
}
ite = parsedConnectionString.find("DefaultEndpointsProtocol");
if (ite != parsedConnectionString.end())
{
defaultEndpointsProtocol = ite->second;
}
UrlBuilder builder;
builder.SetScheme(defaultEndpointsProtocol);
if (!blobEndpoint.empty())
{
builder = UrlBuilder(blobEndpoint);
}
else if (!accountName.empty())
{
builder.SetHost(accountName + ".blob" + defaultEndpointsProtocol);
}
else
{
throw std::runtime_error("invalid connection string");
}
builder.AppendPath(containerName, true);
builder.AppendPath(blobName, true);
auto credential = std::make_shared<SharedKeyCredential>(accountName, accountKey);
return BlobClient(builder.to_string(), credential, options);
}
BlobClient::BlobClient(
const std::string& blobUri,
std::shared_ptr<SharedKeyCredential> credential,
const BlobClientOptions& options)
: BlobClient(blobUri, options)
{
// not implemented yet
unused(credential, options);
}
BlobClient::BlobClient(
const std::string& blobUri,
std::shared_ptr<TokenCredential> credential,
const BlobClientOptions& options)
: BlobClient(blobUri, options)
{
// not implemented yet
unused(credential);
}
BlobClient::BlobClient(const std::string& blobUri, const BlobClientOptions& options)
: m_blobUrl(blobUri)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
for (const auto& p : options.policies)
{
policies.emplace_back(std::unique_ptr<Azure::Core::Http::HttpPolicy>(p->Clone()));
}
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
BlobClient BlobClient::WithSnapshot(const std::string& snapshot)
{
BlobClient newClient(*this);
if (snapshot.empty())
{
m_blobUrl.RemoveQuery("snapshot");
}
else
{
m_blobUrl.AppendQuery("snapshot", snapshot);
}
return newClient;
}
FlattenedDownloadProperties BlobClient::Download(const DownloadBlobOptions& options)
{
BlobRestClient::Blob::DownloadOptions protocolLayerOptions;
if (options.Offset != std::numeric_limits<decltype(options.Offset)>::max())
{
protocolLayerOptions.Range
= std::make_pair(options.Offset, options.Offset + options.Length - 1);
}
else
{
protocolLayerOptions.Range
= std::make_pair(std::numeric_limits<uint64_t>::max(), uint64_t(0));
}
return BlobRestClient::Blob::Download(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
BlobProperties BlobClient::GetProperties(const GetBlobPropertiesOptions& options)
{
unused(options);
BlobRestClient::Blob::GetPropertiesOptions protocolLayerOptions;
return BlobRestClient::Blob::GetProperties(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
BlobInfo BlobClient::SetHttpHeaders(const SetBlobHttpHeadersOptions& options)
{
BlobRestClient::Blob::SetHttpHeadersOptions protocolLayerOptions;
protocolLayerOptions.ContentType = options.ContentType;
protocolLayerOptions.ContentEncoding = options.ContentEncoding;
protocolLayerOptions.ContentLanguage = options.ContentLanguage;
protocolLayerOptions.ContentMD5 = options.ContentMD5;
protocolLayerOptions.CacheControl = options.CacheControl;
protocolLayerOptions.ContentDisposition = options.ContentDisposition;
return BlobRestClient::Blob::SetHttpHeaders(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
BlobInfo BlobClient::SetMetadata(
std::map<std::string, std::string> metadata,
const SetBlobMetadataOptions& options)
{
unused(options);
BlobRestClient::Blob::SetMetadataOptions protocolLayerOptions;
protocolLayerOptions.Metadata = std::move(metadata);
return BlobRestClient::Blob::SetMetadata(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
BasicResponse BlobClient::Delete(const DeleteBlobOptions& options)
{
BlobRestClient::Blob::DeleteOptions protocolLayerOptions;
protocolLayerOptions.DeleteSnapshots = options.DeleteSnapshots;
return BlobRestClient::Blob::Delete(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
}}} // namespace Azure::Storage::Blobs

View File

@ -0,0 +1,155 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "blobs/blob_container_client.hpp"
#include "common/storage_common.hpp"
#include "http/curl/curl.hpp"
namespace Azure { namespace Storage { namespace Blobs {
BlobContainerClient BlobContainerClient::CreateFromConnectionString(
const std::string& connectionString,
const std::string& containerName,
const BlobContainerClientOptions& options)
{
auto parsedConnectionString = ParseConnectionString(connectionString);
std::string accountName;
std::string accountKey;
std::string blobEndpoint;
std::string EndpointSuffix;
std::string defaultEndpointsProtocol = ".core.windows.net";
auto ite = parsedConnectionString.find("AccountName");
if (ite != parsedConnectionString.end())
{
accountName = ite->second;
}
ite = parsedConnectionString.find("AccountKey");
if (ite != parsedConnectionString.end())
{
accountKey = ite->second;
}
ite = parsedConnectionString.find("BlobEndpoint");
if (ite != parsedConnectionString.end())
{
blobEndpoint = ite->second;
}
ite = parsedConnectionString.find("EndpointSuffix");
if (ite != parsedConnectionString.end())
{
EndpointSuffix = ite->second;
}
ite = parsedConnectionString.find("DefaultEndpointsProtocol");
if (ite != parsedConnectionString.end())
{
defaultEndpointsProtocol = ite->second;
}
UrlBuilder builder;
builder.SetScheme(defaultEndpointsProtocol);
if (!blobEndpoint.empty())
{
builder = UrlBuilder(blobEndpoint);
}
else if (!accountName.empty())
{
builder.SetHost(accountName + ".blob" + defaultEndpointsProtocol);
}
else
{
throw std::runtime_error("invalid connection string");
}
builder.AppendPath(containerName, true);
auto credential = std::make_shared<SharedKeyCredential>(accountName, accountKey);
return BlobContainerClient(builder.to_string(), credential, options);
}
BlobContainerClient::BlobContainerClient(
const std::string& containerUri,
std::shared_ptr<SharedKeyCredential> credential,
const BlobContainerClientOptions& options)
: BlobContainerClient(containerUri, options)
{
// not implemented yet
unused(credential);
}
BlobContainerClient::BlobContainerClient(
const std::string& containerUri,
std::shared_ptr<TokenCredential> credential,
const BlobContainerClientOptions& options)
: BlobContainerClient(containerUri, options)
{
// not implemented yet
unused(credential);
}
BlobContainerClient::BlobContainerClient(
const std::string& containerUri,
const BlobContainerClientOptions& options)
: m_ContainerUri(containerUri)
{
std::vector<std::unique_ptr<Azure::Core::Http::HttpPolicy>> policies;
for (const auto& p : options.policies)
{
policies.emplace_back(std::unique_ptr<Azure::Core::Http::HttpPolicy>(p->Clone()));
}
policies.emplace_back(std::make_unique<Azure::Core::Http::TransportPolicy>(
std::make_shared<Azure::Core::Http::CurlTransport>()));
m_pipeline = std::make_shared<Azure::Core::Http::HttpPipeline>(policies);
}
BlobContainerInfo BlobContainerClient::Create(const CreateBlobContainerOptions& options)
{
BlobRestClient::Container::CreateOptions protocolLayerOptions;
protocolLayerOptions.AccessType = options.AccessType;
protocolLayerOptions.Metadata = options.Metadata;
return BlobRestClient::Container::Create(
options.Context, *m_pipeline, m_ContainerUri.to_string(), protocolLayerOptions);
}
BasicResponse BlobContainerClient::Delete(const DeleteBlobContainerOptions& options)
{
unused(options);
BlobRestClient::Container::DeleteOptions protocolLayerOptions;
return BlobRestClient::Container::Delete(
options.Context, *m_pipeline, m_ContainerUri.to_string(), protocolLayerOptions);
}
BlobContainerProperties BlobContainerClient::GetProperties(
const GetBlobContainerPropertiesOptions& options)
{
unused(options);
BlobRestClient::Container::GetPropertiesOptions protocolLayerOptions;
return BlobRestClient::Container::GetProperties(
options.Context, *m_pipeline, m_ContainerUri.to_string(), protocolLayerOptions);
}
BlobContainerInfo BlobContainerClient::SetMetadata(
std::map<std::string, std::string> metadata,
SetBlobContainerMetadataOptions options)
{
unused(options);
BlobRestClient::Container::SetMetadataOptions protocolLayerOptions;
protocolLayerOptions.Metadata = metadata;
return BlobRestClient::Container::SetMetadata(
options.Context, *m_pipeline, m_ContainerUri.to_string(), protocolLayerOptions);
}
BlobsFlatSegment BlobContainerClient::ListBlobs(const ListBlobsOptions& options)
{
BlobRestClient::Container::ListBlobsOptions protocolLayerOptions;
protocolLayerOptions.Prefix = options.Prefix;
protocolLayerOptions.Delimiter = options.Delimiter;
protocolLayerOptions.Marker = options.Marker;
protocolLayerOptions.MaxResults = options.MaxResults;
protocolLayerOptions.Include = options.Include;
return BlobRestClient::Container::ListBlobs(
options.Context, *m_pipeline, m_ContainerUri.to_string(), protocolLayerOptions);
}
}}} // namespace Azure::Storage::Blobs

View File

@ -0,0 +1,115 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "blobs/block_blob_client.hpp"
#include "common/storage_common.hpp"
namespace Azure { namespace Storage { namespace Blobs {
BlockBlobClient BlockBlobClient::CreateFromConnectionString(
const std::string& connectionString,
const std::string& containerName,
const std::string& blobName,
const BlockBlobClientOptions& options)
{
BlockBlobClient newClient(
BlobClient::CreateFromConnectionString(connectionString, containerName, blobName, options));
return newClient;
}
BlockBlobClient::BlockBlobClient(
const std::string& blobUri,
std::shared_ptr<SharedKeyCredential> credential,
const BlockBlobClientOptions& options)
: BlobClient(blobUri, std::move(credential), options)
{
}
BlockBlobClient::BlockBlobClient(
const std::string& blobUri,
std::shared_ptr<TokenCredential> credential,
const BlockBlobClientOptions& options)
: BlobClient(blobUri, std::move(credential), options)
{
}
BlockBlobClient::BlockBlobClient(
const std::string& blobUri,
const BlockBlobClientOptions& options)
: BlobClient(blobUri, options)
{
}
BlockBlobClient::BlockBlobClient(BlobClient blobClient) : BlobClient(std::move(blobClient))
{
}
BlockBlobClient BlockBlobClient::WithSnapshot(const std::string& snapshot)
{
BlockBlobClient newClient(*this);
if (snapshot.empty())
{
newClient.m_blobUrl.RemoveQuery("snapshot");
}
else
{
newClient.m_blobUrl.AppendQuery("snapshot", snapshot);
}
return newClient;
}
BlobContentInfo BlockBlobClient::Upload(
// TODO: We don't have BodyStream for now.
std::vector<uint8_t> content,
const UploadBlobOptions& options)
{
BlobRestClient::BlockBlob::UploadOptions protocolLayerOptions;
protocolLayerOptions.BodyBuffer = &content;
protocolLayerOptions.ContentMD5 = options.ContentMD5;
protocolLayerOptions.ContentCRC64 = options.ContentCRC64;
protocolLayerOptions.BlobType = BlobType::BlockBlob;
protocolLayerOptions.Properties = options.Properties;
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.Tier = options.Tier;
return BlobRestClient::BlockBlob::Upload(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
BlockInfo BlockBlobClient::StageBlock(
const std::string& blockId,
// TODO: We don't have BodyStream for now.
std::vector<uint8_t> content,
const StageBlockOptions& options)
{
BlobRestClient::BlockBlob::StageBlockOptions protocolLayerOptions;
protocolLayerOptions.BodyBuffer = &content;
protocolLayerOptions.BlockId = blockId;
protocolLayerOptions.ContentMD5 = options.ContentMD5;
protocolLayerOptions.ContentCRC64 = options.ContentCRC64;
return BlobRestClient::BlockBlob::StageBlock(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
BlobContentInfo BlockBlobClient::CommitBlockList(
const std::vector<std::pair<BlockType, std::string>>& blockIds,
const CommitBlockListOptions& options)
{
BlobRestClient::BlockBlob::CommitBlockListOptions protocolLayerOptions;
protocolLayerOptions.BlockList = blockIds;
protocolLayerOptions.Properties = options.Properties;
protocolLayerOptions.Metadata = options.Metadata;
protocolLayerOptions.Tier = options.Tier;
return BlobRestClient::BlockBlob::CommitBlockList(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
BlobBlockListInfo BlockBlobClient::GetBlockList(const GetBlockListOptions& options)
{
BlobRestClient::BlockBlob::GetBlockListOptions protocolLayerOptions;
protocolLayerOptions.ListType = options.ListType;
return BlobRestClient::BlockBlob::GetBlockList(
options.Context, *m_pipeline, m_blobUrl.to_string(), protocolLayerOptions);
}
}}} // namespace Azure::Storage::Blobs

View File

@ -0,0 +1,45 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <algorithm>
#include "common/storage_credential.hpp"
namespace Azure { namespace Storage {
std::map<std::string, std::string> ParseConnectionString(const std::string& connectionString)
{
std::map<std::string, std::string> result;
std::string::const_iterator cur = connectionString.begin();
while (cur != connectionString.end())
{
auto key_begin = cur;
auto key_end = std::find(cur, connectionString.end(), '=');
std::string key = std::string(key_begin, key_end);
cur = key_end;
if (cur != connectionString.end())
{
++cur;
}
auto value_begin = cur;
auto value_end = std::find(cur, connectionString.end(), ';');
std::string value = std::string(value_begin, value_end);
cur = value_end;
if (cur != connectionString.end())
{
++cur;
}
if (!key.empty() || !value.empty())
{
result[std::move(key)] = std::move(value);
}
}
return result;
}
}} // namespace Azure::Storage

View File

@ -0,0 +1,220 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "common/storage_url_builder.hpp"
#include <locale>
#include <vector>
#include <algorithm>
#include <cctype>
namespace Azure { namespace Storage {
UrlBuilder::UrlBuilder(const std::string& url)
{
std::string::const_iterator pos = url.begin();
const std::string schemeEnd = "://";
auto schemeIter = url.find(schemeEnd);
if (schemeIter != std::string::npos)
{
std::transform(url.begin(), url.begin() + schemeIter, std::back_inserter(m_scheme), [](char c) {
return static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
});
pos = url.begin() + schemeIter + schemeEnd.length();
}
auto hostIter = std::find_if(pos, url.end(), [](char c) { return c == '/' || c == '?' || c == ':'; });
m_host = std::string(pos, hostIter);
pos = hostIter;
if (pos != url.end() && *pos == ':')
{
auto port_ite = std::find_if_not(pos + 1, url.end(), [](char c) { return std::isdigit(static_cast<unsigned char>(c)); });
m_port = std::stoi(std::string(pos + 1, port_ite));
pos = port_ite;
}
if (pos != url.end() && *pos == '/')
{
auto pathIter = std::find(pos + 1, url.end(), '?');
m_path = std::string(pos + 1, pathIter);
pos = pathIter;
}
if (pos != url.end() && *pos == '?')
{
auto queryIter = std::find(pos + 1, url.end(), '#');
SetQuery(std::string(pos + 1, queryIter));
pos = queryIter;
}
if (pos != url.end() && *pos == '#')
{
m_fragment = std::string(pos + 1, url.end());
}
}
static const char* unreserved
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
static const char* subdelimiters = "!$&'()*+,;=";
std::string UrlBuilder::EncodeHost(const std::string& host)
{
return EncodeImpl(host, [](int c) { return c > 127; });
}
std::string UrlBuilder::EncodePath(const std::string& path)
{
const static std::vector<bool> shouldEncodeTable = []() {
const std::string pathCharacters
= std::string(unreserved) + std::string(subdelimiters) + "%/:@";
std::vector<bool> ret(256, true);
for (char c : pathCharacters)
{
ret[c] = false;
}
// we also encode % and +
ret['%'] = true;
ret['+'] = true;
return ret;
}();
return EncodeImpl(path, [](int c) { return shouldEncodeTable[c]; });
}
std::string UrlBuilder::EncodeQuery(const std::string& query)
{
const static std::vector<bool> shouldEncodeTable = []() {
std::string queryCharacters = std::string(unreserved) + std::string(subdelimiters) + "%/:@?";
std::vector<bool> ret(256, true);
for (char c : queryCharacters)
{
ret[c] = false;
}
// we also encode % and +
ret['%'] = true;
ret['+'] = true;
// Surprisingly, '=' also needs to be encoded because Azure Storage server side is so strict.
// We are applying this function to query key and value respectively, so this won't affect
// that = used to separate key and query.
ret['='] = true;
return ret;
}();
return EncodeImpl(query, [](int c) { return shouldEncodeTable[c]; });
}
std::string UrlBuilder::EncodeFragment(const std::string& fragment)
{
const static std::vector<bool> shouldEncodeTable = []() {
std::string queryCharacters = std::string(unreserved) + std::string(subdelimiters) + "%/:@?";
std::vector<bool> ret(256, true);
for (char c : queryCharacters)
{
ret[c] = false;
}
// we also encode % and +
ret['%'] = true;
ret['+'] = true;
return ret;
}();
return EncodeImpl(fragment, [](int c) { return shouldEncodeTable[c]; });
}
std::string UrlBuilder::EncodeImpl(
const std::string& source,
const std::function<bool(int)>& shouldEncode)
{
const char* hex = "0123456789ABCDEF";
std::string encoded;
for (char c : source)
{
unsigned char uc = c;
if (shouldEncode(uc))
{
encoded += '%';
encoded += hex[(uc >> 4) & 0x0f];
encoded += hex[uc & 0x0f];
}
else
{
encoded += c;
}
}
return encoded;
}
void UrlBuilder::SetQuery(const std::string& query)
{
m_query.clear();
std::string::const_iterator cur = query.begin();
if (cur != query.end() && *cur == '?')
{
++cur;
}
while (cur != query.end())
{
auto key_end = std::find(cur, query.end(), '=');
std::string query_key = std::string(cur, key_end);
cur = key_end;
if (cur != query.end())
{
++cur;
}
auto value_end = std::find(cur, query.end(), '&');
std::string query_value = std::string(cur, value_end);
cur = value_end;
if (cur != query.end())
{
++cur;
}
m_query[std::move(query_key)] = std::move(query_value);
}
}
std::string UrlBuilder::to_string() const
{
std::string full_url;
if (!m_scheme.empty())
{
full_url += m_scheme + "://";
}
full_url += m_host;
if (m_port != -1)
{
full_url += ":" + std::to_string(m_port);
}
if (!m_path.empty())
{
full_url += "/" + m_path;
}
if (!m_query.empty())
{
bool first_query = true;
for (const auto& q : m_query)
{
full_url += first_query ? "?" : "&";
first_query = false;
full_url += q.first + "=" + q.second;
}
}
if (!m_fragment.empty())
{
full_url += "#" + m_fragment;
}
return full_url;
}
}} // namespace Azure::Storage