Merge pull request #73 from seguler/dev2
Encode url to fix #51 Files named with spaces not supported + test
This commit is contained in:
Коммит
1066b0440d
|
@ -9,6 +9,8 @@
|
|||
namespace microsoft_azure {
|
||||
namespace storage {
|
||||
|
||||
std::string encode_url_path(const std::string& path);
|
||||
|
||||
class storage_url {
|
||||
public:
|
||||
storage_url &set_domain(const std::string &domain) {
|
||||
|
@ -29,6 +31,10 @@ namespace microsoft_azure {
|
|||
return m_path;
|
||||
}
|
||||
|
||||
std::string get_encoded_path() const {
|
||||
return encode_url_path(m_path);
|
||||
}
|
||||
|
||||
storage_url &add_query(const std::string &name, const std::string &value) {
|
||||
m_query[name].insert(value);
|
||||
return *this;
|
||||
|
|
|
@ -38,7 +38,7 @@ namespace microsoft_azure {
|
|||
}
|
||||
|
||||
// Canonicalized resource
|
||||
string_to_sign.append("/").append(m_account_name).append(url.get_path());
|
||||
string_to_sign.append("/").append(m_account_name).append(url.get_encoded_path());
|
||||
for (const auto &name : url.get_query()) {
|
||||
string_to_sign.append("\n").append(name.first);
|
||||
bool first_value = true;
|
||||
|
|
|
@ -2,10 +2,97 @@
|
|||
|
||||
namespace microsoft_azure {
|
||||
namespace storage {
|
||||
bool is_alnum(char ch)
|
||||
{
|
||||
return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
|
||||
}
|
||||
|
||||
bool is_unreserved(char ch)
|
||||
{
|
||||
return is_alnum(ch) || ch == '-' || ch == '.' || ch == '_' || ch == '~';
|
||||
}
|
||||
bool is_sub_delim(char ch)
|
||||
{
|
||||
switch(ch)
|
||||
{
|
||||
case '!':
|
||||
case '$':
|
||||
case '&':
|
||||
case '\'':
|
||||
case '(':
|
||||
case ')':
|
||||
case '*':
|
||||
case '+':
|
||||
case ',':
|
||||
case ';':
|
||||
case '=':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_path_character(char ch)
|
||||
{
|
||||
return is_unreserved(ch) || is_sub_delim(ch) || ch == '%' || ch == '/' || ch == ':' || ch == '@';
|
||||
}
|
||||
|
||||
bool is_query_character(char ch)
|
||||
{
|
||||
return is_path_character(ch) || ch == '?';
|
||||
}
|
||||
|
||||
std::string encode_url_path(const std::string& path)
|
||||
{
|
||||
const char* const hex = "0123456789ABCDEF";
|
||||
std::string encoded;
|
||||
for(size_t index = 0; index < path.size(); ++index)
|
||||
{
|
||||
char ch = path[index];
|
||||
if(!is_path_character(ch)
|
||||
|| ch == '%'
|
||||
|| ch == '+'
|
||||
|| ch == '&')
|
||||
{
|
||||
encoded.push_back('%');
|
||||
encoded.push_back(hex[ (ch >> 4) & 0xF]);
|
||||
encoded.push_back(hex[ ch & 0xF ]);
|
||||
}
|
||||
else
|
||||
{
|
||||
encoded.push_back(ch);
|
||||
}
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
std::string encode_url_query(const std::string& path)
|
||||
{
|
||||
const char* const hex = "0123456789ABCDEF";
|
||||
std::string encoded;
|
||||
for(size_t index = 0; index < path.size(); ++index)
|
||||
{
|
||||
char ch = path[index];
|
||||
if(!is_query_character(ch)
|
||||
|| ch == '%'
|
||||
|| ch == '+'
|
||||
|| ch == '&')
|
||||
{
|
||||
encoded.push_back('%');
|
||||
encoded.push_back(hex[ (ch >> 4) & 0xF]);
|
||||
encoded.push_back(hex[ ch & 0xF ]);
|
||||
}
|
||||
else
|
||||
{
|
||||
encoded.push_back(ch);
|
||||
}
|
||||
}
|
||||
return encoded;
|
||||
}
|
||||
|
||||
std::string storage_url::to_string() const {
|
||||
std::string url(m_domain);
|
||||
url.append(m_path);
|
||||
url.append(encode_url_path(m_path));
|
||||
|
||||
bool first_query = true;
|
||||
for (const auto &q : m_query) {
|
||||
|
@ -17,7 +104,7 @@ namespace microsoft_azure {
|
|||
url.append("&");
|
||||
}
|
||||
for (const auto &value : q.second) {
|
||||
url.append(q.first).append("=").append(value);
|
||||
url.append(encode_url_query(q.first)).append("=").append(encode_url_query(value));
|
||||
}
|
||||
}
|
||||
return url;
|
||||
|
|
|
@ -53,6 +53,18 @@ class TestFuse(unittest.TestCase):
|
|||
os.remove(filepath)
|
||||
self.assertEqual(False, os.path.exists(filepath))
|
||||
|
||||
def test_WriteReadSingleFileUnicode(self):
|
||||
file1txt = "你好,世界!"
|
||||
filepath = os.path.join(self.blobstage, "文本: hello?world&we^are%all~together1 .txt");
|
||||
with open(filepath, 'w') as file1blob:
|
||||
file1blob.write(file1txt)
|
||||
self.assertEqual(True, os.path.exists(filepath))
|
||||
with open(filepath, 'r') as file1blob:
|
||||
file1txtrt = file1blob.read()
|
||||
self.assertEqual(file1txt, file1txtrt)
|
||||
os.remove(filepath)
|
||||
self.assertEqual(False, os.path.exists(filepath))
|
||||
|
||||
def test_medium_files(self):
|
||||
mediumBlobsSourceDir = os.path.join(self.blobstage, "mediumblobs")
|
||||
if not os.path.exists(mediumBlobsSourceDir):
|
||||
|
@ -79,6 +91,8 @@ class TestFuse(unittest.TestCase):
|
|||
|
||||
def test_directory_operations(self):
|
||||
testDir = os.path.join(self.blobstage, "testDirectory")
|
||||
subdir1 = "subDir1"
|
||||
subdir2 = "Thİs!is-a%directory&name @1"
|
||||
self.assertFalse(os.path.exists(testDir))
|
||||
self.assertFalse(os.path.isdir(testDir))
|
||||
|
||||
|
@ -96,9 +110,9 @@ class TestFuse(unittest.TestCase):
|
|||
with open(os.path.join(testDir, "file3"), 'w') as fileblob:
|
||||
fileblob.write(filetxt)
|
||||
|
||||
testSubDir1 = os.path.join(testDir, "subdir1")
|
||||
testSubDir1 = os.path.join(testDir, subdir1)
|
||||
os.makedirs(testSubDir1)
|
||||
testSubDir2 = os.path.join(testDir, "subdir2")
|
||||
testSubDir2 = os.path.join(testDir, subdir2)
|
||||
os.makedirs(testSubDir2)
|
||||
|
||||
children = os.listdir(testDir);
|
||||
|
@ -106,8 +120,8 @@ class TestFuse(unittest.TestCase):
|
|||
self.assertTrue("file1" in children)
|
||||
self.assertTrue("file2" in children)
|
||||
self.assertTrue("file3" in children)
|
||||
self.assertTrue("subdir1" in children)
|
||||
self.assertTrue("subdir2" in children)
|
||||
self.assertTrue(subdir1 in children)
|
||||
self.assertTrue(subdir2 in children)
|
||||
|
||||
# Directory not empty should throw
|
||||
with self.assertRaises(OSError):
|
||||
|
@ -121,7 +135,7 @@ class TestFuse(unittest.TestCase):
|
|||
|
||||
self.assertTrue("file1" in children)
|
||||
self.assertTrue("file3" in children)
|
||||
self.assertTrue("subdir2" in children)
|
||||
self.assertTrue(subdir2 in children)
|
||||
|
||||
os.rmdir(testSubDir2)
|
||||
os.remove(os.path.join(testDir, "file1"))
|
||||
|
|
Загрузка…
Ссылка в новой задаче