-----------------------------------------------------------------------------
-- Defines Sputnik's interface to WSAPI.
--
-- (c) 2007, 2008 Yuri Takhteyev (yuri@freewisdom.org)
-- License: MIT/X, see http://sputnik.freewisdom.org/en/License
-----------------------------------------------------------------------------
module(..., package.seeall)
require("sputnik")
local util = require("sputnik.util")
require("coxpcall")
-----------------------------------------------------------------------------
-- An auxiliary functions to catch common errors and provide a better message
-- for them.
-----------------------------------------------------------------------------
local detect_common_errors = function(error_message, config)
local pattern = "Versium storage error: (.*) Can't open file: (.*) in mode w"
local dummy, path = string.match(error_message, pattern)
local dir = config.VERSIUM_PARAMS
if path and path:sub(1, dir:len()) == dir then
return string.format([[Versium's data directory (%s) is not writable.
Please fix directory permissions.]], dir)
end
end
-----------------------------------------------------------------------------
-- Templates for an error messages with a stack trace.
-----------------------------------------------------------------------------
local HTML_MESSAGE_WITH_STACK_TRACE = [[
%s
Error details:
%s
]]
-----------------------------------------------------------------------------
-- Templates for an error messages without a stack trace.
-----------------------------------------------------------------------------
local HTML_MESSAGE_WITHOUT_STACK_TRACE = [[
%s
(If you are the admin for this site, you can turn on stack trace display
by setting SHOW_STACK_TRACE parameter to true.)
]]
-----------------------------------------------------------------------------
-- Calls a function safely, returning an error message formatted as HTML
-- if something goes wrong.
--
-- @param fn the function to be called.
-- @param config a config table.
-- @param logger an optional lualogging logger.
-- @param request the request (optional)
-- @param ... parameters for fn()
-- @return status
-- @return the result of fn(...) or an error message.
-----------------------------------------------------------------------------
local function htmlized_pcall (fn, config, logger, request, ...)
local success, result_or_error = coxpcall(fn, function(e) return e end, ...)
if success then
return true, result_or_error
else
local error_message = result_or_error
local summary = detect_common_errors(error_message, config)
or "Sputnik ran but failed due to an unexpected error."
if logger then
logger:error(error_message)
end
return false, string.format(config.SHOW_STACK_TRACE
and HTML_MESSAGE_WITH_STACK_TRACE
or HTML_MESSAGE_WITHOUT_STACK_TRACE,
summary, util.escape(error_message))
end
end
-----------------------------------------------------------------------------
-- Creates a WSAPI app that always returns an error message.
--
-- @param error_message the error message to return for every request.
-- @param a WSAPI app function.
-----------------------------------------------------------------------------
local function new_error_app_function(error_message)
require("wsapi.response")
return function(wsapi_env)
response = wsapi.response.new()
response:write(error_message)
response.status = 500
return response:finish()
end
end
-----------------------------------------------------------------------------
-- Creates a WSAPI app function to handle requests based on a configuration
-- table.
--
-- @param config a bootstrap configuration for Sputnik.
-----------------------------------------------------------------------------
function new(config)
local ok, logger, my_sputnik, error_message
ok, logger = htmlized_pcall(util.make_logger, config, nil, nil,
config.LOGGER,
config.LOGGER_PARAMS, config.LOGGER_LEVEL)
if not ok then
error_message = logger
return new_error_app_function(error_message)
end
ok, my_sputnik = htmlized_pcall(sputnik.new, config, logger, nil,
config, logger)
if not ok then
error_message = my_sputnik
return new_error_app_function(error_message)
end
if ok and config.INIT_FUNCTION then
ok, error_message = htmlized_pcall(config.INIT_FUNCTION, config, logger, nil,
my_sputnik, config)
end
if not ok then
return new_error_app_function(error_message)
end
return function (wsapi_env)
_G.format = string.format -- to work around a bug in wsapi.response
require("wsapi.request")
local request = wsapi.request.new(wsapi_env)
request.wsapi_env = wsapi_env
require("wsapi.response")
local response = wsapi.response.new()
local ok, error_as_html = htmlized_pcall(my_sputnik.handle_request,
config, my_sputnik.logger,
request,
-- actual function params
my_sputnik, request, response)
if not ok then
response = wsapi.response.new()
response:write(error_as_html)
response.status = 500
end
-- Change the HTTP status code to 302 is a location header is set
if response.headers["Location"] then
if response.status < 300 then
response.status = 302
end
end
return response:finish()
end
end