protocol 'ec2' not implemented — EC2 and related services unusable; query protocol drops list params
- Dominant language
- Lua
- Stars
- 35
- Forks
- 28
- PR merge metrics
- No merged PRs in 30d
Description
## Bug: `protocol 'ec2' not implemented yet` + query protocol silently drops list parameters
### Environment
| | |
|---|---|
| lua-resty-aws | 1.7.2-1 |
| OpenResty | 1.29.2.3-1 |
| Runtime | ECS Fargate (OpenResty/LuaJIT) |
---
### Problem 1 — EC2 service is completely unusable
Any call to a service that uses the `"ec2"` wire protocol (EC2, EC2 Instance Connect, …)
throws a hard error before the request is sent:
/usr/local/share/lua/5.1/resty/aws/request/build.lua:241:
protocol 'ec2' not implemented yet
**Minimal reproduction:**
```lua
local aws_sdk = require("resty.aws")
local aws = aws_sdk({ region = "eu-central-1" })
local ec2 = aws:EC2()
local res, err = ec2:describeInstances({ InstanceIds = { "i-0abc123" } })
-- ↑ always errors, never reaches the network
The protocols table at the top of build.lua already lists ec2 = true (so the
validation guard passes), but the body-encoding section immediately below has only a
stub:
if config.protocol == "ec2" then
error("protocol 'ec2' not implemented yet") -- line 241
```
---
Problem 2 — query protocol silently corrupts list parameters
For services that use "query" protocol (AutoScaling, ELB, SQS, …), list-valued
parameters are stored as raw Lua tables in request.query:
```lua
-- build.lua ~line 196 (location == nil branch for query protocol):
request.query[name] = param_value -- param_value may be a table!
```
When resty.luasocket.http serialises the query table, a Lua array value either
errors or produces a single repeated key (InstanceIds=i-xxx&InstanceIds=i-yyy),
not the .member.N form that AWS query-protocol services require
(InstanceIds.member.1=i-xxx&InstanceIds.member.2=i-yyy).
Minimal reproduction:
```lua
local asg = aws:AutoScaling()
local res, err = asg:describeAutoScalingInstances({
InstanceIds = { "i-0abc1", "i-0abc2" }
})
-- AWS receives InstanceIds=i-0abc1&InstanceIds=i-0abc2 (wrong)
-- instead of InstanceIds.member.1=i-0abc1&InstanceIds.member.2=i-0abc2
This is only masked today because EC2 calls typically fail first (Problem 1), so the
AutoScaling call is never reached.
```
---
Expected behaviour
- aws:EC2():describeInstances({ InstanceIds = {...} }) sends a valid signed POST to
https://ec2..amazonaws.com/ with
Action=DescribeInstances&Version=2016-11-15&InstanceId.1=i-xxx&… in the body.
- aws:AutoScaling():describeAutoScalingInstances({ InstanceIds = {...} }) sends
…&InstanceIds.member.1=i-xxx&InstanceIds.member.2=i-yyy&….
---
Root cause & suggested fix
EC2 protocol — the body encoder needs to be implemented. The ec2 wire protocol
is structurally identical to query (POST + application/x-www-form-urlencoded),
with two differences:
1. Action and Version go in the body, not request.query.
2. List members use the member shape's locationName with a .N suffix (no .member.
infix). For example, DescribeInstances defines InstanceIds with
"locationName": "InstanceId", so the wire key is InstanceId.1, InstanceId.2, …
Suggested replacement for the stub in build.lua:
```lua
if config.protocol == "ec2" then
local body_parts = {
"Action=" .. operation.name,
"Version=" .. config.apiVersion,
}
for bname, bvalue in pairs(body_tbl) do
local mc = ((operation.input or {}).members or {})[bname] or {}
local lname = mc.locationName or bname
if type(bvalue) == "table" then
for i, v in ipairs(bvalue) do
body_parts[#body_parts + 1] = lname .. "." .. i .. "=" .. escape_uri(tostring(v))
end
else
body_parts[#body_parts + 1] = lname .. "=" .. escape_uri(tostring(bvalue))
end
end
table.sort(body_parts)
request.headers["Content-Type"] = "application/x-www-form-urlencoded"
request.body = table.concat(body_parts, "&")
Query protocol list params — serialize tables immediately rather than storing them
raw:
-- location == nil branch, query protocol:
if type(param_value) == "table" then
for i, v in ipairs(param_value) do
request.query[name .. ".member." .. i] = tostring(v)
end
else
request.query[name] = param_value
end
```
Both fixes use only locals already present in build.lua (escape_uri, operation,
body_tbl) and require no new dependencies.
Happy to open a PR with these changes if that would be helpful.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start in build.lua around the query-protocol handling near line 196 and the EC2 stub near line 241, using the two minimal reproductions to trace request construction. Confirm completion when EC2 produces the expected form-encoded Action, Version, and InstanceId.N fields, while query-protocol lists produce name.member.N keys without silently retaining raw tables.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- aws, lua
- Domain
- api, backend
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 48/100