keplerproject / wsapi

WSAPI is an API that abstracts the web server from Lua web applications.
http://keplerproject.github.io/wsapi
74 stars 33 forks source link

wsapi.request.qs_encode yields invalid querystrings #13

Closed ignacio closed 11 years ago

ignacio commented 11 years ago

According to RFC 3875, the question mark is not part of the querystring.

This code fails because the first key-value pair is missing:

local mock = require "wsapi.mock"
local ws_request = require "wsapi.request"

local test_app = function(wsapi_env)
    local qs = wsapi_env.QUERY_STRING
    print("QUERY_STRING: " .. qs)

    local req = ws_request.new(wsapi_env)

    return 200, {}, coroutine.wrap(function()
        coroutine.yield(tostring(req.params.key1) .. tostring(req.params.key2))
    end)
end

local app = mock.make_handler(test_app)

local response, request = app:get("test", { key1="1", key2="2"})
assert(response.code == 200)
assert(response.body == "12")

I could fix that in wsapi.mock, just stripping the question mark, but it seems wrong to me. If _wsapi.request.qsencode does not include it, methods like _request.routelink and request.link should be fixed. That seems like the proper thing to do. What do you think?

ignacio commented 11 years ago

While this commit makes qs_encode behave properly according to the spec, it makes it cumbersome when using that to build urls. So, an alternative way is proposed here. It trades correctness for ease of use:

function methods:qs_encode(query, url)
  local parts = {}
  for k, v in pairs(query or {}) do
    parts[#parts+1] = k .. "=" .. util.url_encode(v)
  end
  if #parts > 0 then
    return (url and (url .. "?") or "") .. table.concat(parts, "&")
  else
    return (url and url or "")
  end
end

function methods:route_link(route, query, ...)
  local builder = self.mk_app["link_" .. route]
  if builder then
    local uri = builder(self.mk_app, self.env, ...)
    return self:qs_encode(query, uri)
  else
    error("there is no route named " .. route)
  end
end

function methods:link(url, query)
  local prefix = (self.mk_app and self.mk_app.prefix) or self.script_name
  local uri = prefix .. url
  return self:qs_encode(query, uri)
end

function methods:absolute_link(url, query)
  return self:qs_encode(query, url)
end

Maybe there is some middle ground, like _qsencode having a flag to control whether '?' is prepended or not.