Skip to content

Commit 89df1f8

Browse files
fix: compatibility for S3
Put bucket into host instead of path. Add option "s3_bucket_in_path" to use deprecated APIs. I wonder why the offical SDK do not update api definition. They instead do it with monkey patch code?!
1 parent 8ffdb73 commit 89df1f8

File tree

5 files changed

+160
-65
lines changed

5 files changed

+160
-65
lines changed

src/resty/aws/init.lua

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,23 @@ local unsigned = {
273273
}
274274

275275

276+
local function s3_patch(request, bucket)
277+
if not bucket then
278+
return
279+
end
280+
281+
request.headers.Host = bucket .. "." .. request.headers.Host
282+
283+
local path = request.path
284+
if bucket and path then
285+
path = path:sub(#bucket + 2)
286+
if path == "/" then
287+
path = ""
288+
end
289+
290+
request.path = path
291+
end
292+
end
276293

277294
-- Generate a function for each operation in the service api "operations" table
278295
local function generate_service_methods(service)
@@ -317,6 +334,10 @@ local function generate_service_methods(service)
317334
self.config.signatureVersion = "none"
318335
end
319336

337+
if not self.config.s3_bucket_in_path then
338+
s3_patch(request, params.Bucket)
339+
end
340+
320341
-- sign the request according to the signature version required
321342
local signed_request, err = sign_request(self.config, request)
322343
if old_sig then

src/resty/aws/request/build.lua

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,39 @@ local function parse_query(q)
7272
return query_tbl
7373
end
7474

75+
local function get_host_port(config)
76+
local host = config.endpoint
77+
do
78+
local s, e = host:find("://")
79+
if s then
80+
-- the "globalSSL" one from the region_config_data file
81+
local scheme = host:sub(1, s-1):lower()
82+
host = host:sub(e+1, -1)
83+
if config.tls == nil then
84+
config.tls = scheme == "https"
85+
end
86+
end
87+
end
88+
89+
local tls = config.tls
90+
local port = config.port or (tls and 443 or 80)
91+
92+
local host_header do -- If the "standard" port is not in use, the port should be added to the Host header
93+
local with_port
94+
if tls then
95+
with_port = port ~= 443
96+
else
97+
with_port = port ~= 80
98+
end
99+
if with_port then
100+
host_header = string.format("%s:%d", host, port)
101+
else
102+
host_header = host
103+
end
104+
end
105+
106+
return host_header, port
107+
end
75108

76109
-- implement AWS api protocols.
77110
-- returns a request table;
@@ -95,12 +128,15 @@ local function build_request(operation, config, params)
95128
local http = operation.http or {}
96129
local uri = http.requestUri or ""
97130

131+
local host, port = get_host_port(config)
98132

99133
local request = {
100134
path = uri,
101135
method = http.method,
102136
query = {},
103137
headers = {},
138+
host = host,
139+
port = port,
104140
}
105141

106142
local body_tbl = {}

src/resty/aws/request/signatures/none.lua

Lines changed: 3 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -22,44 +22,14 @@
2222
-- hostname template: see https://github.com/aws/aws-sdk-js/blob/ae07e498e77000e55da70b20996dc8fd2f8b3051/lib/region_config_data.json
2323
local function prepare_request(config, request_data)
2424
local tls = config.tls
25-
local host = config.endpoint
26-
do
27-
local s, e = host:find("://")
28-
if s then
29-
-- the "globalSSL" one from the region_config_data file
30-
local scheme = host:sub(1, s-1):lower()
31-
host = host:sub(e+1, -1)
32-
if config.tls == nil then
33-
config.tls = scheme == "https"
34-
end
35-
end
36-
end
37-
38-
if tls == nil then
39-
tls = true
40-
end
41-
42-
local port = config.port or (tls and 443 or 80)
25+
local host = request_data.host
26+
local port = request_data.port
4327
local timestamp = ngx.time()
4428
local req_date = os.date("!%Y%m%dT%H%M%SZ", timestamp)
4529

46-
local host_header do -- If the "standard" port is not in use, the port should be added to the Host header
47-
local with_port
48-
if tls then
49-
with_port = port ~= 443
50-
else
51-
with_port = port ~= 80
52-
end
53-
if with_port then
54-
host_header = string.format("%s:%d", host, port)
55-
else
56-
host_header = host
57-
end
58-
end
59-
6030
local headers = {
6131
["X-Amz-Date"] = req_date,
62-
["Host"] = host_header,
32+
["Host"] = host,
6333
}
6434
for k, v in pairs(request_data.headers or {}) do
6535
headers[k] = v

src/resty/aws/request/signatures/v4.lua

Lines changed: 4 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ local function prepare_awsv4_request(config, request_data)
141141
canonical_querystring = canonicalise_query_string(query)
142142
end
143143

144-
local req_headers = request_data.headers or {}
144+
local req_headers = request_data.headers
145145
local req_payload = request_data.body
146146

147147
-- get credentials
@@ -157,44 +157,16 @@ local function prepare_awsv4_request(config, request_data)
157157
end
158158

159159
local tls = config.tls
160-
local host = config.endpoint
161-
do
162-
local s, e = host:find("://")
163-
if s then
164-
-- the "globalSSL" one from the region_config_data file
165-
local scheme = host:sub(1, s-1):lower()
166-
host = host:sub(e+1, -1)
167-
if config.tls == nil then
168-
config.tls = scheme == "https"
169-
end
170-
end
171-
end
172160

173-
if tls == nil then
174-
tls = true
175-
end
176-
local port = config.port or (tls and 443 or 80)
161+
local host = request_data.host
162+
local port = request_data.port
177163
local timestamp = ngx.time()
178164
local req_date = os.date("!%Y%m%dT%H%M%SZ", timestamp)
179165
local date = os.date("!%Y%m%d", timestamp)
180166

181-
local host_header do -- If the "standard" port is not in use, the port should be added to the Host header
182-
local with_port
183-
if tls then
184-
with_port = port ~= 443
185-
else
186-
with_port = port ~= 80
187-
end
188-
if with_port then
189-
host_header = string.format("%s:%d", host, port)
190-
else
191-
host_header = host
192-
end
193-
end
194-
195167
local headers = {
196168
["X-Amz-Date"] = req_date,
197-
["Host"] = host_header,
169+
["Host"] = host,
198170
["X-Amz-Security-Token"] = session_token,
199171
}
200172

src/resty/aws/s3.lua

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
local AWS = require("resty.aws")
2+
local normalize = require("kong.tools.uri").normalize
3+
4+
-- this happens at init phase because the kong.clustering.new() is called by kong.init at init phase
5+
-- please make sure this is required at init phase if that changes
6+
local AWS_config = require("resty.aws.config")
7+
local third_party_env = os.getenv("AWS_THIRD_PARTY_ENDPOINT")
8+
9+
local xml = require("pl.xml")
10+
local xml_parse = xml.parse
11+
local s3_instance
12+
local re_match = ngx.re.match
13+
14+
local _M = {}
15+
16+
17+
local empty = {}
18+
local function endpoint_settings(config)
19+
local endpoint = config.third_party_endpoint or third_party_env
20+
if endpoint then
21+
local m = re_match(endpoint, [[^(?:(https?):\/\/)?([^:]+)(?::(\d+))?$]], "jo")
22+
assert(m and m[2], "invalid endpoint")
23+
local scheme = m[1] or "https"
24+
local tls = scheme == "https"
25+
return {
26+
scheme = scheme,
27+
tls = tls,
28+
endpoint = m[2],
29+
port = tonumber(m[3]) or (tls and 443 or 80),
30+
}
31+
else
32+
return empty
33+
end
34+
end
35+
36+
function _M.init_worker()
37+
-- TODO: avoid http request to get the region
38+
local global_config = AWS_config.global
39+
global_config.s3_update = true
40+
local aws_instance = assert(AWS(global_config))
41+
s3_instance = assert(aws_instance:S3(endpoint_settings(global_config)))
42+
end
43+
44+
function _M.new(gateway_version, url)
45+
local self = {
46+
url = url,
47+
gateway_version = gateway_version,
48+
}
49+
local m = assert(re_match(url, [[^s3:\/\/([^\/]+)\/(.+)$]], "jo"), "invalid S3 URL")
50+
self.bucket = m[1]
51+
self.key = normalize(m[2] .. "/" .. gateway_version .. ".json", true)
52+
53+
return setmetatable(self, { __index = _M })
54+
end
55+
56+
function _M:backup_config(config, config_hash)
57+
local res, err = s3_instance:putObject{
58+
Bucket = self.bucket,
59+
Key = self.key,
60+
Body = config,
61+
-- optional, to help DP recalculate the hash
62+
Metadata = config_hash and {
63+
["config-hash"] = config_hash,
64+
},
65+
ContentType = "application/json",
66+
}
67+
68+
if not res then
69+
return nil, err
70+
end
71+
72+
if not (res.status == 200) then
73+
return nil, xml_parse(res.body, nil, true):child_with_name("Message")[1]
74+
end
75+
76+
return true
77+
end
78+
79+
function _M:fetch_config()
80+
local res, err = s3_instance:getObject{
81+
Bucket = self.bucket,
82+
Key = self.key,
83+
}
84+
85+
if not res then
86+
return nil, err
87+
end
88+
89+
if not (res.status == 200) then
90+
return nil, xml_parse(res.body, nil, true):child_with_name("Message")[1]
91+
end
92+
93+
return res.body, res.headers["x-amz-meta-config-hash"]
94+
end
95+
96+
return _M

0 commit comments

Comments
 (0)