umegaya / lua-aws

pure-lua implementation of aws REST APIs
122 stars 35 forks source link

How to send params? #1

Closed CriztianiX closed 10 years ago

CriztianiX commented 10 years ago

When i try to send params for the request...

params = { QueueUrl = "DEV_email-one2one", MessageBody = "testing" }

res = AWS.SQS:api_by_version('2012-11-05'):sendMessage(params)

criztianix@blacker:~$ lua test-aws.lua
sqs:v[2012-11-05]: sendMessage:error:/usr/lib64/lua/5.1/lua-aws/class.lua:34: no such method: QueueUrl stack traceback: /usr/lib64/lua/5.1/lua-aws/class.lua:34: in function </usr/lib64/lua/5.1/lua-aws/class.lua:31> ...lua/5.1/lua-aws/requests/query_string_serializer.lua:16: in function 'serialize_struct' ...lua/5.1/lua-aws/requests/query_string_serializer.lua:11: in function 'serialize' /usr/lib64/lua/5.1/lua-aws/requests/query.lua:13: in function 'serialize_query' /usr/lib64/lua/5.1/lua-aws/requests/query.lua:27: in function 'build_request' /usr/lib64/lua/5.1/lua-aws/requests/base.lua:23: in function </usr/lib64/lua/5.1/lua-aws/requests/base.lua:21> (tail call): ? C: in function 'pcall' /usr/lib64/lua/5.1/lua-aws/api.lua:79: in function 'sendMessage' test-aws.lua:19: in main chunk

umegaya commented 10 years ago

hi, sorry for late reply, but thank you for trying to use lua-aws, also many improvement in your repo!! I checked my code and I think this is because my v2 encoding code contains some easy mistake. I made fix and confirmed that createQueue action is worked with parameter QueueName.

but with sendMessage/deleteQueue action, I encountered strange 403, Signature not matched error. I still keep on investigating problem. but I will provide diff (based on your master) for the first time.

diff --git a/lua-aws/api.lua b/lua-aws/api.lua
index b23130b..ea5d5c4 100644
--- a/lua-aws/api.lua
+++ b/lua-aws/api.lua
@@ -4,8 +4,10 @@ local Request = require ('lua-aws.request')

 local p
 do
-  local _obj_0 = require("moon")
-  p = _obj_0.p
+  local ok, _obj_0 = pcall(require, "moon")
+  if ok then
+   p = _obj_0.p
+  end
 end

 local get_endpoint_from_env = function ()
diff --git a/lua-aws/core.lua b/lua-aws/core.lua
index 86def98..148551b 100644
--- a/lua-aws/core.lua
+++ b/lua-aws/core.lua
@@ -10,7 +10,7 @@ local AWS = class.AWS {
        --> define service
        self.DynamoDB = require('lua-aws.services.dynamodb').new(self)
        self.EC2 = require('lua-aws.services.ec2').new(self)
-    self.SQS = require('lua-aws.services.sqs').new(self)
+       self.SQS = require('lua-aws.services.sqs').new(self)

        --[[
        require('./services/autoscaling')
diff --git a/lua-aws/requests/base.lua b/lua-aws/requests/base.lua
index 187002b..ac85d06 100644
--- a/lua-aws/requests/base.lua
+++ b/lua-aws/requests/base.lua
@@ -6,8 +6,10 @@ local EndPoint = require ('lua-aws.requests.endpoint')

 local p
 do
-  local _obj_0 = require("moon")
-  p = _obj_0.p
+  local ok, _obj_0 = pcall(require, "moon")
+  if ok then
+   p = _obj_0.p
+  end
 end

 return class.AWS_Request {
@@ -15,20 +17,21 @@ return class.AWS_Request {
        self._operation = operation
        self._params = params or {}
        self._api = api
-    s_version = api:signature_version()
+       local s_version = api:signature_version()
+       assert(Signer[s_version], "signer not implement:"..s_version)
        self._signer = Signer[s_version].new()
    end,
    send = function (self)
        local req = self:base_build_request()
        self:build_request(req)
        self:validate(req)
-       _api_timestamp = self._api:timestamp()
+       local _api_timestamp = self._api:timestamp()
        self._signer:sign(req, self._api:config(), _api_timestamp)
        local resp = self._api:http_request(req)
        if resp.status == 200 then
            return self:extract_data(resp)
        else
-           print("Something wrong in the request")
+           self._api:log("Something wrong in the request", resp.status, resp.body)
            return self:extract_error(resp)
        end
    end,
@@ -96,7 +99,7 @@ return class.AWS_Request {
            message = string,
        }
    ]]--
-   extract_data = function (self, resp)
+   extract_error = function (self, resp)
        assert(false)
    end,
 }
diff --git a/lua-aws/requests/query.lua b/lua-aws/requests/query.lua
index db23036..defeb0b 100644
--- a/lua-aws/requests/query.lua
+++ b/lua-aws/requests/query.lua
@@ -9,7 +9,7 @@ return class.AWS_QueryRequest.extends(Request) {
        if rules then 
            rules = rules.members 
        end
-       local builder = Serializer.new(rules, self._api)
+       local builder = Serializer.new(self._api, rules)
        builder:serialize(self._params, function(name, value)
            params[name] = value
        end)
diff --git a/lua-aws/requests/query_string_serializer.lua b/lua-aws/requests/query_string_serializer.lua
index 74320b5..b916ab1 100644
--- a/lua-aws/requests/query_string_serializer.lua
+++ b/lua-aws/requests/query_string_serializer.lua
@@ -8,7 +8,7 @@ return class.AWS_RequestSerializer {
    end,

    serialize = function (self, params, fn)
-       self:serialize_struct('', params, self._rules, fn)
+       self:serialize_struct(false, params, self._rules, fn)
    end,

    serialize_struct = function (self, prefix, struct, rules, fn)
@@ -59,9 +59,9 @@ return class.AWS_RequestSerializer {
            self:serialize_map(name, value, rules, fn)
        elseif rules.type == 'timestamp' then
            local timestamp_format = (rules.format or self._api:timestamp_format())
-           fn.call(self, name, util.date_format(value, timestamp_format))
+           fn(name, util.date_format(value, timestamp_format))
        else
-           fn.call(self, name, tostring(value))
+           fn(name, tostring(value))
        end
    end,
 }
diff --git a/lua-aws/util.lua b/lua-aws/util.lua
index ee1d15e..6c40630 100644
--- a/lua-aws/util.lua
+++ b/lua-aws/util.lua
@@ -432,6 +432,7 @@ local fill_header = function (req)
    req.headers["Connection"] = "Keep-Alive"
 end
 local http_print = function (...)
+   -- print(...)
 end
 if luasocket_ok then
    local ltn12 = require"ltn12"
@@ -445,6 +446,7 @@ if luasocket_ok then
            source = ltn12.source.string(req.body),
            sink = ltn12.sink.table(respbody)
        }
+       http_print('requestto:', req.protocol .. "://" .. req.host .. ":" .. req.port .. req.path)
        http_print('sentbody:', req.body)
        http_print('result of query:', result, respcode, respstatus)
        for k,v in pairs(respheaders) do
diff --git a/test/sqs.lua b/test/sqs.lua
new file mode 100644
index 0000000..f127b3f
--- /dev/null
+++ b/test/sqs.lua
@@ -0,0 +1,35 @@
+local AWS = require ('lua-aws.init')
+local aws = AWS.new({
+   accessKeyId = os.getenv('AWS_ACCESS_KEY'),
+   secretAccessKey = os.getenv('AWS_SECRET_KEY')
+})
+
+local function dump_res(tag, res)
+   for k,v in pairs(res) do
+       print(tag, k, v)
+       if type(v) == 'table' then
+           for kk,vv in pairs(v) do
+               print(tag, k, kk, vv)
+           end
+       end
+   end
+end
+
+local res
+res = aws.SQS:api_by_version('2012-11-05'):createQueue({
+   QueueName = "testQueue",
+})
+dump_res('create', res)
+
+local params = {
+   QueueUrl = "https://sqs.ap-northeast-1.amazonaws.com/871570535967/testQueue",
+   MessageBody = "testing"
+}
+res = aws.SQS:api_by_version('2012-11-05'):sendMessage(params)
+dump_res('message', res)
+
+
+res = aws.SQS:api_by_version('2012-11-05'):deleteQueue({
+   QueueUrl = "https://sqs.ap-northeast-1.amazonaws.com/871570535967/testQueue",
+})
+dump_res('delete', res)
diff --git a/lua-aws/api.lua b/lua-aws/api.lua
index b23130b..ea5d5c4 100644
--- a/lua-aws/api.lua
+++ b/lua-aws/api.lua
@@ -4,8 +4,10 @@ local Request = require ('lua-aws.request')

 local p
 do
-  local _obj_0 = require("moon")
-  p = _obj_0.p
+  local ok, _obj_0 = pcall(require, "moon")
+  if ok then
+   p = _obj_0.p
+  end
 end

 local get_endpoint_from_env = function ()
diff --git a/lua-aws/core.lua b/lua-aws/core.lua
index 86def98..148551b 100644
--- a/lua-aws/core.lua
+++ b/lua-aws/core.lua
@@ -10,7 +10,7 @@ local AWS = class.AWS {
        --> define service
        self.DynamoDB = require('lua-aws.services.dynamodb').new(self)
        self.EC2 = require('lua-aws.services.ec2').new(self)
-    self.SQS = require('lua-aws.services.sqs').new(self)
+       self.SQS = require('lua-aws.services.sqs').new(self)

        --[[
        require('./services/autoscaling')
diff --git a/lua-aws/requests/base.lua b/lua-aws/requests/base.lua
index 187002b..ac85d06 100644
--- a/lua-aws/requests/base.lua
+++ b/lua-aws/requests/base.lua
@@ -6,8 +6,10 @@ local EndPoint = require ('lua-aws.requests.endpoint')

 local p
 do
-  local _obj_0 = require("moon")
-  p = _obj_0.p
+  local ok, _obj_0 = pcall(require, "moon")
+  if ok then
+   p = _obj_0.p
+  end
 end

 return class.AWS_Request {
@@ -15,20 +17,21 @@ return class.AWS_Request {
        self._operation = operation
        self._params = params or {}
        self._api = api
-    s_version = api:signature_version()
+       local s_version = api:signature_version()
+       assert(Signer[s_version], "signer not implement:"..s_version)
        self._signer = Signer[s_version].new()
    end,
    send = function (self)
        local req = self:base_build_request()
        self:build_request(req)
        self:validate(req)
-       _api_timestamp = self._api:timestamp()
+       local _api_timestamp = self._api:timestamp()
        self._signer:sign(req, self._api:config(), _api_timestamp)
        local resp = self._api:http_request(req)
        if resp.status == 200 then
            return self:extract_data(resp)
        else
-           print("Something wrong in the request")
+           self._api:log("Something wrong in the request", resp.status, resp.body)
            return self:extract_error(resp)
        end
    end,
@@ -96,7 +99,7 @@ return class.AWS_Request {
            message = string,
        }
    ]]--
-   extract_data = function (self, resp)
+   extract_error = function (self, resp)
        assert(false)
    end,
 }
diff --git a/lua-aws/requests/query.lua b/lua-aws/requests/query.lua
index db23036..defeb0b 100644
--- a/lua-aws/requests/query.lua
+++ b/lua-aws/requests/query.lua
@@ -9,7 +9,7 @@ return class.AWS_QueryRequest.extends(Request) {
        if rules then 
            rules = rules.members 
        end
-       local builder = Serializer.new(rules, self._api)
+       local builder = Serializer.new(self._api, rules)
        builder:serialize(self._params, function(name, value)
            params[name] = value
        end)
diff --git a/lua-aws/requests/query_string_serializer.lua b/lua-aws/requests/query_string_serializer.lua
index 74320b5..b916ab1 100644
--- a/lua-aws/requests/query_string_serializer.lua
+++ b/lua-aws/requests/query_string_serializer.lua
@@ -8,7 +8,7 @@ return class.AWS_RequestSerializer {
    end,

    serialize = function (self, params, fn)
-       self:serialize_struct('', params, self._rules, fn)
+       self:serialize_struct(false, params, self._rules, fn)
    end,

    serialize_struct = function (self, prefix, struct, rules, fn)
@@ -59,9 +59,9 @@ return class.AWS_RequestSerializer {
            self:serialize_map(name, value, rules, fn)
        elseif rules.type == 'timestamp' then
            local timestamp_format = (rules.format or self._api:timestamp_format())
-           fn.call(self, name, util.date_format(value, timestamp_format))
+           fn(name, util.date_format(value, timestamp_format))
        else
-           fn.call(self, name, tostring(value))
+           fn(name, tostring(value))
        end
    end,
 }
diff --git a/lua-aws/util.lua b/lua-aws/util.lua
index ee1d15e..6c40630 100644
--- a/lua-aws/util.lua
+++ b/lua-aws/util.lua
@@ -432,6 +432,7 @@ local fill_header = function (req)
    req.headers["Connection"] = "Keep-Alive"
 end
 local http_print = function (...)
+   -- print(...)
 end
 if luasocket_ok then
    local ltn12 = require"ltn12"
@@ -445,6 +446,7 @@ if luasocket_ok then
            source = ltn12.source.string(req.body),
            sink = ltn12.sink.table(respbody)
        }
+       http_print('requestto:', req.protocol .. "://" .. req.host .. ":" .. req.port .. req.path)
        http_print('sentbody:', req.body)
        http_print('result of query:', result, respcode, respstatus)
        for k,v in pairs(respheaders) do
diff --git a/test/sqs.lua b/test/sqs.lua
new file mode 100644
index 0000000..f6c180f
--- /dev/null
+++ b/test/sqs.lua
@@ -0,0 +1,37 @@
+local AWS = require ('lua-aws.init')
+local aws = AWS.new({
+   accessKeyId = os.getenv('AWS_ACCESS_KEY'),
+   secretAccessKey = os.getenv('AWS_SECRET_KEY')
+})
+
+local function dump_res(tag, res)
+   for k,v in pairs(res) do
+       print(tag, k, v)
+       if type(v) == 'table' then
+           for kk,vv in pairs(v) do
+               print(tag, k, kk, vv)
+           end
+       end
+   end
+end
+
+local res
+res = aws.SQS:api_by_version('2012-11-05'):createQueue({
+   QueueName = "testQueue",
+})
+dump_res('create', res)
+
+--[[
+local params = {
+   QueueUrl = "https://sqs.$region.amazonaws.com/$id/$queueName",
+   MessageBody = "testing"
+}
+res = aws.SQS:api_by_version('2012-11-05'):sendMessage(params)
+dump_res('message', res)
+
+
+res = aws.SQS:api_by_version('2012-11-05'):deleteQueue({
+   QueueUrl = "https://sqs.$region.amazonaws.com/$id/$queueName",
+})
+dump_res('delete', res)
+]]
\ No newline at end of file
CriztianiX commented 10 years ago

oh thanks! I will apply this patch for testing!

CriztianiX commented 10 years ago

Yes.. With the patch applied i get a lot of errors with signature checks!

qs:v[2012-11-05]: Something wrong in the request 403 <?xml version="1.0"?>SenderSignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.c9db2b77-c4cc-5071-b3d9-67dbc6ba9fe6<?xml version="1.0"?>SenderSignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.9554d91e-c7ba-5428-a5e6-95b2c5cd4ff1<?xml version="1.0"?>SenderSignatureDoesNotMatchThe request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.5aec17a5-5e8a-5601-9f25-5b095cf2c19a message message false message code 403

umegaya commented 10 years ago

hi, thank you for testing. finally, it is because current URL escape code wrongly escapse "." character. (and QueueUrl likely to contain "." character). after fix it, sendMessage/deleteQueue also works. here is another diff from previous patch.

btw, with my investigation, QueueUrl format is https://sqs.$region.amazonaws.com/$id/$queueName but your example's QueueUrl has the value like QueueUrl = "DEV_email-one2one" it is just hiding actual url, that is ok. but if not, please review your actual Queue URL. thanks!

diff --git a/lua-aws/util.lua b/lua-aws/util.lua
index 6c40630..f3a52cc 100644
--- a/lua-aws/util.lua
+++ b/lua-aws/util.lua
@@ -323,7 +323,7 @@ end
 -- taken from Lua Socket and added underscore to ignore (MIT-License)
 -----------------------------------------------------------------------------
 function _M.escape(s)
-    return string.gsub(s, "([^A-Za-z0-9_%-])", function(c)
+    return string.gsub(s, "([^A-Za-z0-9_%-%.])", function(c)
         return string.format("%%%02X", string.byte(c))
     end)
 end
@@ -337,8 +337,8 @@ end
 -- taken from Lua Socket
 -----------------------------------------------------------------------------
 function _M.unescape(s)
-    return string.gsub(s, "%%(%X%X)", function(hex)
-        return string.char(base.tonumber(hex, 16))
+    return string.gsub(s, "%%(%x%x)", function(hex)
+        return string.char(tonumber(hex, 16))
     end)
 end

diff --git a/test/signer/v2.lua b/test/signer/v2.lua
new file mode 100644
index 0000000..ec360b8
--- /dev/null
+++ b/test/signer/v2.lua
@@ -0,0 +1,39 @@
+--[[
+AWSAccessKeyId=$AWS_ACCESS_KEY&
+Action=SendMessage&
+MessageBody=testing&
+QueueUrl=http%3A%2F%2Fsqs.ap-northeast-1.amazonaws.com%2F871570535967%2FtestQueue&
+Signature=hchgVw%2B5D5vwSSCgMVsOt5ycJjb6bDOpNmCe4EpLavo%3D&
+SignatureMethod=HmacSHA256&
+SignatureVersion=2&
+Timestamp=2014-10-06T03%3A46%3A55.305Z&
+Version=2012-11-05
+]]--
+
+local v2signer = require 'lua-aws.signers.v2'
+local endpoint = require 'lua-aws.requests.endpoint'
+local util = require 'lua-aws.util'
+
+local signer = v2signer.new()
+
+local params = {
+   Action = "SendMessage",
+   QueueUrl = "http://sqs.ap-northeast-1.amazonaws.com/871570535967/testQueue",
+   MessageBody = "testing",
+   Version = "2012-11-05"
+}
+
+signer:sign({
+   method = "POST",
+   headers = {},
+   host = "sqs.ap-northeast-1.amazonaws.com",
+   path = "/",
+   params = params,
+}, {
+   accessKeyId = os.getenv('AWS_ACCESS_KEY'),
+   secretAccessKey = os.getenv('AWS_SECRET_KEY'),
+}, "2014-10-06T03:46:55.305Z")
+
+-- TODO : can we provide universal test??? now its only for *ME*
+print('params:', params.Signature, util.unescape("hchgVw%2B5D5vwSSCgMVsOt5ycJjb6bDOpNmCe4EpLavo%3D"))
+assert(params.Signature == util.unescape("hchgVw%2B5D5vwSSCgMVsOt5ycJjb6bDOpNmCe4EpLavo%3D"))
diff --git a/test/sqs.lua b/test/sqs.lua
index f6c180f..9aab358 100644
--- a/test/sqs.lua
+++ b/test/sqs.lua
@@ -8,9 +8,7 @@ local function dump_res(tag, res)
    for k,v in pairs(res) do
        print(tag, k, v)
        if type(v) == 'table' then
-           for kk,vv in pairs(v) do
-               print(tag, k, kk, vv)
-           end
+           dump_res(tag.."."..k, v)
        end
    end
 end
@@ -21,9 +19,10 @@ res = aws.SQS:api_by_version('2012-11-05'):createQueue({
 })
 dump_res('create', res)

---[[
+local QueueUrl = res.value.CreateQueueResponse.value.CreateQueueResult.value.QueueUrl.value
+print("QueueUrl:", QueueUrl)
 local params = {
-   QueueUrl = "https://sqs.$region.amazonaws.com/$id/$queueName",
+   QueueUrl = QueueUrl,
    MessageBody = "testing"
 }
 res = aws.SQS:api_by_version('2012-11-05'):sendMessage(params)
@@ -31,7 +30,7 @@ dump_res('message', res)

 res = aws.SQS:api_by_version('2012-11-05'):deleteQueue({
-   QueueUrl = "https://sqs.$region.amazonaws.com/$id/$queueName",
+   QueueUrl = QueueUrl,
 })
 dump_res('delete', res)
-]]
\ No newline at end of file
+-- ]]
\ No newline at end of file
CriztianiX commented 10 years ago

Yes!!I It works! Only i have this issue...

When i configure AWS with

AWS = AWS.new({ accessKeyId = 'AAA', secretAccessKey = 'BBB' })

i get sqs:v[2012-11-05]: sendMessage:error:/usr/lib64/lua/5.1/lua-aws/api.lua:45: attempt to concatenate local 'endpoint' (a nil value)

So... i have to do AWS = AWS.new({ accessKeyId = 'AAA', secretAccessKey = 'BBB', endpoint = "us-east-1.amazonaws.com" })

umegaya commented 10 years ago

hi, I think it is most likely to occur when environment value EC2_URL is not set or unset as following. if you don't set EC2_URL env value, you should specify your ec2-url explicitly with constructor param for AWS object.

dokyougemusu-no-MacBook-Pro:lua-aws iyatomi$ unset EC2_URL
dokyougemusu-no-MacBook-Pro:lua-aws iyatomi$ echo $EC2_URL

dokyougemusu-no-MacBook-Pro:lua-aws iyatomi$ luajit test/sqs.lua 
sqs:v[2012-11-05]:  createQueue:error:./lua-aws/api.lua:53: attempt to concatenate local 'endpoint' (a nil value)

but I agree that more informative message is needed for this situation, so fix little bit.

diff --git a/lua-aws/api.lua b/lua-aws/api.lua
index ea5d5c4..8fce5a4 100644
--- a/lua-aws/api.lua
+++ b/lua-aws/api.lua
@@ -13,7 +13,7 @@ end
 local get_endpoint_from_env = function ()
        local ec2url = os.getenv('EC2_URL')
        if not ec2url then
-               return nil
+               error('neither config.endpoint given nor EC2_URL environment set.')
        else
                return ec2url:gsub('https://ec2%.', '')
        end
@@ -21,7 +21,7 @@ end
 local get_region_from_env = function ()
        local ec2url = os.getenv('EC2_URL')
        if not ec2url then
-               return nil
+               error('neither config.endpoint given nor EC2_URL environment set.')
        else
                local region = false
                ec2url:gsub('https://ec2%.(.*)%.amazonaws.com.*', function (s)

also, more document is required but sorry for my laziness :<

umegaya commented 10 years ago

if it is ok, can you open pull request to my repository?

umegaya commented 10 years ago

thank you. now it is merged to main branch.

umegaya commented 10 years ago

with #2