Skip to content

Commit 5bad554

Browse files
authored
fix path encoding issue (#794)
1 parent fda4398 commit 5bad554

File tree

5 files changed

+88
-18
lines changed

5 files changed

+88
-18
lines changed

sdk/storage/azure-storage-blobs/src/blob_batch_client.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ namespace Azure { namespace Storage { namespace Blobs {
239239
requestBody += getBatchBoundary();
240240

241241
auto blobUrl = m_serviceUrl;
242-
blobUrl.AppendPath(subrequest.ContainerName);
243-
blobUrl.AppendPath(subrequest.BlobName);
242+
blobUrl.AppendPath(Details::UrlEncodePath(subrequest.ContainerName));
243+
blobUrl.AppendPath(Details::UrlEncodePath(subrequest.BlobName));
244244
BlobRestClient::Blob::DeleteBlobOptions protocolLayerOptions;
245245
protocolLayerOptions.DeleteSnapshots = subrequest.Options.DeleteSnapshots;
246246
protocolLayerOptions.IfModifiedSince = subrequest.Options.AccessConditions.IfModifiedSince;
@@ -261,8 +261,8 @@ namespace Azure { namespace Storage { namespace Blobs {
261261
requestBody += getBatchBoundary();
262262

263263
auto blobUrl = m_serviceUrl;
264-
blobUrl.AppendPath(subrequest.ContainerName);
265-
blobUrl.AppendPath(subrequest.BlobName);
264+
blobUrl.AppendPath(Details::UrlEncodePath(subrequest.ContainerName));
265+
blobUrl.AppendPath(Details::UrlEncodePath(subrequest.BlobName));
266266
BlobRestClient::Blob::SetBlobAccessTierOptions protocolLayerOptions;
267267
protocolLayerOptions.Tier = subrequest.Tier;
268268
protocolLayerOptions.RehydratePriority = subrequest.Options.RehydratePriority;

sdk/storage/azure-storage-blobs/src/blob_container_client.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ namespace Azure { namespace Storage { namespace Blobs {
2323
{
2424
auto parsedConnectionString = Details::ParseConnectionString(connectionString);
2525
auto containerUri = std::move(parsedConnectionString.BlobServiceUri);
26-
containerUri.AppendPath(containerName);
26+
containerUri.AppendPath(Details::UrlEncodePath(containerName));
2727

2828
if (parsedConnectionString.KeyCredential)
2929
{
@@ -117,7 +117,7 @@ namespace Azure { namespace Storage { namespace Blobs {
117117
BlobClient BlobContainerClient::GetBlobClient(const std::string& blobName) const
118118
{
119119
auto blobUri = m_containerUrl;
120-
blobUri.AppendPath(blobName);
120+
blobUri.AppendPath(Details::UrlEncodePath(blobName));
121121
return BlobClient(std::move(blobUri), m_pipeline, m_customerProvidedKey, m_encryptionScope);
122122
}
123123

sdk/storage/azure-storage-blobs/test/blob_container_client_test.cpp

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// SPDX-License-Identifier: MIT
33

4-
#include "blob_container_client_test.hpp"
5-
#include "azure/storage/blobs/blob_sas_builder.hpp"
6-
#include "azure/storage/common/crypt.hpp"
7-
84
#include <chrono>
95
#include <thread>
106

7+
#include "azure/storage/blobs/blob_sas_builder.hpp"
8+
#include "azure/storage/common/crypt.hpp"
9+
#include "blob_container_client_test.hpp"
10+
1111
namespace Azure { namespace Storage { namespace Blobs {
1212

1313
bool operator==(
@@ -1048,4 +1048,39 @@ namespace Azure { namespace Storage { namespace Test {
10481048
}
10491049
}
10501050

1051+
TEST_F(BlobContainerClientTest, SpecialBlobName)
1052+
{
1053+
const std::string non_ascii_word = "\xE6\xB5\x8B\xE8\xAF\x95";
1054+
const std::string encoded_non_ascii_word = "%E6%B5%8B%E8%AF%95";
1055+
std::string baseBlobName = "a b c / !@#$%^&*() def" + non_ascii_word;
1056+
1057+
{
1058+
std::string blobName = baseBlobName + RandomString();
1059+
auto blobClient = m_blobContainerClient->GetAppendBlobClient(blobName);
1060+
EXPECT_NO_THROW(blobClient.Create());
1061+
auto blobUrl = blobClient.GetUri();
1062+
EXPECT_EQ(
1063+
blobUrl,
1064+
m_blobContainerClient->GetUri() + "/" + Storage::Details::UrlEncodePath(blobName));
1065+
}
1066+
{
1067+
std::string blobName = baseBlobName + RandomString();
1068+
auto blobClient = m_blobContainerClient->GetPageBlobClient(blobName);
1069+
EXPECT_NO_THROW(blobClient.Create(1024));
1070+
auto blobUrl = blobClient.GetUri();
1071+
EXPECT_EQ(
1072+
blobUrl,
1073+
m_blobContainerClient->GetUri() + "/" + Storage::Details::UrlEncodePath(blobName));
1074+
}
1075+
{
1076+
std::string blobName = baseBlobName + RandomString();
1077+
auto blobClient = m_blobContainerClient->GetBlockBlobClient(blobName);
1078+
EXPECT_NO_THROW(blobClient.UploadFrom(nullptr, 0));
1079+
auto blobUrl = blobClient.GetUri();
1080+
EXPECT_EQ(
1081+
blobUrl,
1082+
m_blobContainerClient->GetUri() + "/" + Storage::Details::UrlEncodePath(blobName));
1083+
}
1084+
}
1085+
10511086
}}} // namespace Azure::Storage::Test

sdk/storage/azure-storage-common/inc/azure/storage/common/crypt.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,6 @@ namespace Azure { namespace Storage {
6464
std::string Sha256(const std::string& text);
6565
std::string HmacSha256(const std::string& text, const std::string& key);
6666
std::string UrlEncodeQueryParameter(const std::string& value);
67+
std::string UrlEncodePath(const std::string& value);
6768
} // namespace Details
6869
}} // namespace Azure::Storage

sdk/storage/azure-storage-common/src/crypt.cpp

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,16 @@
2323
namespace Azure { namespace Storage {
2424

2525
namespace Details {
26+
static const char* c_unreserved
27+
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
28+
static const char* c_subdelimiters = "!$&'()*+,;=";
29+
const char* c_hex = "0123456789ABCDEF";
30+
2631
std::string UrlEncodeQueryParameter(const std::string& value)
2732
{
28-
static const char* unreserved
29-
= "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
30-
static const char* subdelimiters = "!$&'()*+,;=";
31-
3233
const static std::vector<bool> shouldEncodeTable = []() {
3334
std::string queryCharacters
34-
= std::string(unreserved) + std::string(subdelimiters) + "%/:@?";
35+
= std::string(c_unreserved) + std::string(c_subdelimiters) + "%/:@?";
3536

3637
std::vector<bool> ret(256, true);
3738
for (char c : queryCharacters)
@@ -48,7 +49,40 @@ namespace Azure { namespace Storage {
4849
return ret;
4950
}();
5051

51-
const char* hex = "0123456789ABCDEF";
52+
std::string encoded;
53+
for (char c : value)
54+
{
55+
unsigned char uc = c;
56+
if (shouldEncodeTable[uc])
57+
{
58+
encoded += '%';
59+
encoded += c_hex[(uc >> 4) & 0x0f];
60+
encoded += c_hex[uc & 0x0f];
61+
}
62+
else
63+
{
64+
encoded += c;
65+
}
66+
}
67+
return encoded;
68+
}
69+
70+
std::string UrlEncodePath(const std::string& value)
71+
{
72+
const static std::vector<bool> shouldEncodeTable = []() {
73+
std::string pathCharacters
74+
= std::string(c_unreserved) + std::string(c_subdelimiters) + "%/:@";
75+
76+
std::vector<bool> ret(256, true);
77+
for (char c : pathCharacters)
78+
{
79+
ret[c] = false;
80+
}
81+
// we also encode % and +
82+
ret['%'] = true;
83+
ret['+'] = true;
84+
return ret;
85+
}();
5286

5387
std::string encoded;
5488
for (char c : value)
@@ -57,8 +91,8 @@ namespace Azure { namespace Storage {
5791
if (shouldEncodeTable[uc])
5892
{
5993
encoded += '%';
60-
encoded += hex[(uc >> 4) & 0x0f];
61-
encoded += hex[uc & 0x0f];
94+
encoded += c_hex[(uc >> 4) & 0x0f];
95+
encoded += c_hex[uc & 0x0f];
6296
}
6397
else
6498
{

0 commit comments

Comments
 (0)