Skip to content

Commit 315ead6

Browse files
committed
Query parameters for S3 are already URI encoded, so don't encode/sanitize them again.
1 parent 6a54c3c commit 315ead6

File tree

2 files changed

+82
-45
lines changed

2 files changed

+82
-45
lines changed

lib/ex_aws/request/url.ex

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,9 @@ defmodule ExAws.Request.Url do
6363
url
6464
|> URI.parse()
6565
|> Map.put(:fragment, nil)
66-
|> Map.put(:path, "/" <> new_path)
67-
|> Map.put(:query, query)
66+
|> Map.put(:path, "/" <> String.replace(new_path, "+", "%2B"))
67+
|> Map.put(:query, query_replace_spaces(query))
6868
|> URI.to_string()
69-
|> String.replace("+", "%2B")
7069
end
7170

7271
def sanitize(url, _), do: String.replace(url, "+", "%20")
@@ -106,6 +105,9 @@ defmodule ExAws.Request.Url do
106105

107106
def uri_encode(url), do: URI.encode(url, &valid_path_char?/1)
108107

108+
defp query_replace_spaces(nil), do: nil
109+
defp query_replace_spaces(query), do: String.replace(query, "+", "%20")
110+
109111
# Space character
110112
defp valid_path_char?(?\ ), do: false
111113
defp valid_path_char?(?/), do: true

test/ex_aws/request/url_test.exs

Lines changed: 77 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -18,58 +18,74 @@ defmodule ExAws.Request.UrlTest do
1818
{:ok, %{query: query, config: config}}
1919
end
2020

21-
test "it build urls for query operation", %{query: query, config: config} do
22-
assert Url.build(query, config) == "https://example.com/path?foo=bar"
23-
end
21+
describe "build" do
22+
test "it build urls for query operation", %{query: query, config: config} do
23+
assert Url.build(query, config) == "https://example.com/path?foo=bar"
24+
end
2425

25-
test "it allows setting custom port", %{query: query, config: config} do
26-
config = config |> Map.put(:port, 4430)
27-
assert Url.build(query, config) == "https://example.com:4430/path?foo=bar"
28-
end
26+
test "it allows setting custom port", %{query: query, config: config} do
27+
config = config |> Map.put(:port, 4430)
28+
assert Url.build(query, config) == "https://example.com:4430/path?foo=bar"
29+
end
2930

30-
test "it converts the port to an integer if it is a string", %{query: query, config: config} do
31-
config = config |> Map.put(:port, "4430")
32-
assert Url.build(query, config) == "https://example.com:4430/path?foo=bar"
33-
end
31+
test "it converts the port to an integer if it is a string", %{query: query, config: config} do
32+
config = config |> Map.put(:port, "4430")
33+
assert Url.build(query, config) == "https://example.com:4430/path?foo=bar"
34+
end
3435

35-
test "it allows passing scheme with trailing ://", %{query: query, config: config} do
36-
config = config |> Map.put(:scheme, "https://")
37-
assert Url.build(query, config) == "https://example.com/path?foo=bar"
38-
end
36+
test "it allows passing scheme with trailing ://", %{query: query, config: config} do
37+
config = config |> Map.put(:scheme, "https://")
38+
assert Url.build(query, config) == "https://example.com/path?foo=bar"
39+
end
3940

40-
test "it accepts params as a list of keywords", %{query: query, config: config} do
41-
query = query |> Map.put(:params, foo: :bar)
42-
assert Url.build(query, config) == "https://example.com/path?foo=bar"
43-
end
41+
test "it accepts params as a list of keywords", %{query: query, config: config} do
42+
query = query |> Map.put(:params, foo: :bar)
43+
assert Url.build(query, config) == "https://example.com/path?foo=bar"
44+
end
4445

45-
test "it does not have trailing ? when params is an empty map", %{query: query, config: config} do
46-
query = query |> Map.put(:params, %{})
47-
assert Url.build(query, config) == "https://example.com/path"
48-
end
46+
test "it does not have trailing ? when params is an empty map", %{
47+
query: query,
48+
config: config
49+
} do
50+
query = query |> Map.put(:params, %{})
51+
assert Url.build(query, config) == "https://example.com/path"
52+
end
4953

50-
test "it does not have trailing ? when params is an empty list", %{query: query, config: config} do
51-
query = query |> Map.put(:params, [])
52-
assert Url.build(query, config) == "https://example.com/path"
53-
end
54+
test "it does not have trailing ? when params is an empty list", %{
55+
query: query,
56+
config: config
57+
} do
58+
query = query |> Map.put(:params, [])
59+
assert Url.build(query, config) == "https://example.com/path"
60+
end
5461

55-
test "it accepts query without params key", %{query: query, config: config} do
56-
query = query |> Map.delete(:params)
57-
assert Url.build(query, config) == "https://example.com/path"
58-
end
62+
test "it accepts query without params key", %{query: query, config: config} do
63+
query = query |> Map.delete(:params)
64+
assert Url.build(query, config) == "https://example.com/path"
65+
end
5966

60-
test "it cleans up excessive slashes in the path", %{query: query, config: config} do
61-
query = query |> Map.put(:path, "//path///with/too/many//slashes//")
62-
assert Url.build(query, config) == "https://example.com/path/with/too/many/slashes/?foo=bar"
63-
end
67+
test "it cleans up excessive slashes in the path", %{query: query, config: config} do
68+
query = query |> Map.put(:path, "//path///with/too/many//slashes//")
69+
assert Url.build(query, config) == "https://example.com/path/with/too/many/slashes/?foo=bar"
70+
end
6471

65-
test "it ignores empty parameter key", %{query: query, config: config} do
66-
query = query |> Map.put(:params, %{"foo" => "bar", "" => 1})
67-
assert Url.build(query, config) == "https://example.com/path?foo=bar"
68-
end
72+
test "it ignores empty parameter key", %{query: query, config: config} do
73+
query = query |> Map.put(:params, %{"foo" => "bar", "" => 1})
74+
assert Url.build(query, config) == "https://example.com/path?foo=bar"
75+
end
6976

70-
test "it ignores nil parameter key", %{query: query, config: config} do
71-
query = query |> Map.put(:params, %{"foo" => "bar", nil => 1})
72-
assert Url.build(query, config) == "https://example.com/path?foo=bar"
77+
test "it ignores nil parameter key", %{query: query, config: config} do
78+
query = query |> Map.put(:params, %{"foo" => "bar", nil => 1})
79+
assert Url.build(query, config) == "https://example.com/path?foo=bar"
80+
end
81+
82+
test "it URI encodes spaces, + and unicode characters in query parameters", %{
83+
query: query,
84+
config: config
85+
} do
86+
query = query |> Map.put(:params, %{"foo" => "put: it+й"})
87+
assert Url.build(query, config) == "https://example.com/path?foo=put%3A+it%2B%D0%B9"
88+
end
7389
end
7490

7591
describe "get_path" do
@@ -83,4 +99,23 @@ defmodule ExAws.Request.UrlTest do
8399
assert Url.get_path(url) == "/uploads/invalid path but+valid//for"
84100
end
85101
end
102+
103+
describe "sanitize" do
104+
test "it URI encodes spaces, + and unicode characters in the path" do
105+
url = "s3://my-bucket/uploads/a key with ++"
106+
assert Url.sanitize(url, :s3) == "s3://my-bucket/uploads/a%20key%20with%20%2B%2B"
107+
end
108+
109+
test "it URI encodes unicode characters in the path" do
110+
url = "s3://my-bucket/uploads/a key with й"
111+
assert Url.sanitize(url, :s3) == "s3://my-bucket/uploads/a%20key%20with%20%D0%B9"
112+
end
113+
114+
test "it doesn't re-encode query parameters" do
115+
url = "s3://my-bucket/uploads/a key with й?foo=put%3A+it%2B%D0%B9"
116+
117+
assert Url.sanitize(url, :s3) ==
118+
"s3://my-bucket/uploads/a%20key%20with%20%D0%B9?foo=put%3A%20it%2B%D0%B9"
119+
end
120+
end
86121
end

0 commit comments

Comments
 (0)