Skip to content

Commit 8fbae82

Browse files
committed
Fix URI encoding for special characters in blob names
- Update generate_uri method to use CGI.escape with proper post-processing - Convert backslashes to forward slashes and handle URL encoding correctly - Fix HTML entity decoding in blob_list.rb for proper blob name retrieval - Add comprehensive tests for special character handling - Ensure compatibility with Azure Storage Ruby gem specifications Fixes #35
1 parent a660bc8 commit 8fbae82

File tree

4 files changed

+66
-5
lines changed

4 files changed

+66
-5
lines changed

lib/azure_blob/blob_list.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ def marker
6161
def current_page
6262
document
6363
.get_elements("//EnumerationResults/Blobs/Blob/Name")
64-
.map { |element| element.get_text.to_s }
64+
.map { |element| element.text }
6565
end
6666

6767
def fetch

lib/azure_blob/client.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
require_relative "entra_id_signer"
1111
require "time"
1212
require "base64"
13+
require "stringio"
1314

1415
module AzureBlob
1516
# AzureBlob Client class. You interact with the Azure Blob api
@@ -248,8 +249,12 @@ def delete_container(options = {})
248249
#
249250
# Example: +generate_uri("#{container}/#{key}")+
250251
def generate_uri(path)
251-
encoded = path.split("/").map { |segment| CGI.escape(segment) }.join("/")
252-
URI.parse([ host.chomp("/"), encoded ].join("/"))
252+
# https://github.com/Azure/azure-storage-ruby/blob/master/common/lib/azure/storage/common/service/storage_service.rb#L191-L201
253+
encoded_path = CGI.escape(path.encode("UTF-8"))
254+
encoded_path = encoded_path.gsub(/%2F/, "/")
255+
encoded_path = encoded_path.gsub(/%5C/, "/")
256+
encoded_path = encoded_path.gsub(/\+/, "%20")
257+
URI.parse(File.join(host, encoded_path))
253258
end
254259

255260
# Returns an SAS signed URI

test/client/test_client.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def setup
2323
host: @host,
2424
)
2525
@uid = SecureRandom.uuid
26-
@key = "test-client-?-#{name}-#{@uid}" # ? in key to test proper escaping
26+
@key = "test-client-#{name}-#{@uid} ?#&<>\"'%+/\\" # Special chars to test proper escaping
2727
@content = "Some random content #{Random.rand(200)}"
2828
end
2929

@@ -219,7 +219,9 @@ def test_list_prefix
219219

220220
blobs = client.list_blobs(prefix: prefix).to_a
221221

222-
assert_match_content [ key ], blobs
222+
# Account for backslash to forward slash conversion in our encoding
223+
expected_key = key.gsub(/\\/, "/")
224+
assert_match_content [ expected_key ], blobs
223225
end
224226

225227
def test_list_blobs_with_pages

test/client/test_generate_uri.rb

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require_relative "test_helper"
2+
3+
class TestGenerateUri < TestCase
4+
def setup
5+
@client = AzureBlob::Client.new(
6+
account_name: "testaccount",
7+
access_key: "ACCESS-KEY",
8+
container: "test-container"
9+
)
10+
end
11+
12+
def test_generate_uri_with_special_characters
13+
test_cases = {
14+
"container/simple.txt" => "https://testaccount.blob.core.windows.net/container/simple.txt",
15+
"container/path/to/file.txt" => "https://testaccount.blob.core.windows.net/container/path/to/file.txt",
16+
"container\\backslash\\path.txt" => "https://testaccount.blob.core.windows.net/container/backslash/path.txt",
17+
"container/file with spaces.txt" => "https://testaccount.blob.core.windows.net/container/file%20with%20spaces.txt",
18+
"container/file+with+plus.txt" => "https://testaccount.blob.core.windows.net/container/file%2Bwith%2Bplus.txt",
19+
"container/file?with?question.txt" => "https://testaccount.blob.core.windows.net/container/file%3Fwith%3Fquestion.txt",
20+
"container/file#with#hash.txt" => "https://testaccount.blob.core.windows.net/container/file%23with%23hash.txt",
21+
"container/file&with&ampersand.txt" => "https://testaccount.blob.core.windows.net/container/file%26with%26ampersand.txt",
22+
"container/file%with%percent.txt" => "https://testaccount.blob.core.windows.net/container/file%25with%25percent.txt",
23+
"container/file<with>brackets.txt" => "https://testaccount.blob.core.windows.net/container/file%3Cwith%3Ebrackets.txt",
24+
"container/file\"with\"quotes.txt" => "https://testaccount.blob.core.windows.net/container/file%22with%22quotes.txt",
25+
"container/file'with'apostrophe.txt" => "https://testaccount.blob.core.windows.net/container/file%27with%27apostrophe.txt",
26+
"container/test ?#&<>\"'%+/\\.txt" => "https://testaccount.blob.core.windows.net/container/test%20%3F%23%26%3C%3E%22%27%25%2B//.txt",
27+
}
28+
29+
test_cases.each do |input, expected|
30+
uri = @client.send(:generate_uri, input)
31+
assert_equal expected, uri.to_s, "Failed for input: #{input}"
32+
end
33+
end
34+
35+
def test_generate_uri_preserves_container_name
36+
containers = [
37+
"mycontainer",
38+
"my-container",
39+
"container123",
40+
"my-container-123",
41+
]
42+
43+
containers.each do |container|
44+
path = "#{container}/file.txt"
45+
uri = @client.send(:generate_uri, path)
46+
assert uri.to_s.include?("/#{container}/"), "Container name was incorrectly encoded: #{container}"
47+
end
48+
end
49+
50+
def test_generate_uri_with_utf8_characters
51+
uri = @client.send(:generate_uri, "test-container/文件名.txt")
52+
assert_equal "https://testaccount.blob.core.windows.net/test-container/%E6%96%87%E4%BB%B6%E5%90%8D.txt", uri.to_s
53+
end
54+
end

0 commit comments

Comments
 (0)