0
0
mirror of https://github.com/Doodle3D/doodle3d-firmware.git synced 2024-06-26 11:31:23 +02:00
doodle3d-firmware/src/rest/response.lua
Wouter R c3756f5348 Fix return values in printer api.
Pass along chunk metadata in printer api.
Move wifi station mode check from main.lua to signin.lua so it is always run past.
Only log postresponsequeue if it is not empty.

Conflicts:
	src/rest/api/api_printer.lua
2016-01-08 22:26:36 +01:00

202 lines
7.0 KiB
Lua

--
-- This file is part of the Doodle3D project (http://doodle3d.com).
--
-- @copyright 2013, Doodle3D
-- @license This software is licensed under the terms of the GNU GPL v2 or later.
-- See file LICENSE.txt or visit http://www.gnu.org/licenses/gpl.html for full license details.
---
-- The REST response object handles all operations necessary to generate a HTTP response.
-- It knows about the request, ensures the correct HTTP headers are present and
-- allows to set a status (one of `success`, `fail` or `error`) and addtional data.
local JSON = require('util/JSON')
local settings = require('util.settings')
local defaults = require('conf_defaults')
local utils = require('util.utils')
local log = require('util.logger')
local M = {}
M.__index = M
local REQUEST_ID_ARGUMENT = 'rq_id'
local MOD_ABBR = "HRSP"
M.httpStatusCode, M.httpStatusText, M.contentType = nil, nil, nil
M.binaryData, M.binarySavename = nil, nil
--- Print a HTTP header line with the given type and value.
-- @string headerType
-- @string headerValue
local function printHeaderLine(headerType, headerValue)
io.write(headerType .. ": " .. headerValue .. "\r\n")
end
setmetatable(M, {
__call = function(cls, ...)
return cls.new(...)
end
})
--- Instantiates a new response object initialized with the given @{request} object.
-- The request object should always be passed, except on init failure, when it is not yet available.
-- @tparam request requestObject The object representing the HTTP request.
-- @treturn response A newly instantiated response object.
function M.new(requestObject)
local self = setmetatable({}, M)
self.body = { status = nil, data = {} }
self:setHttpStatus(200, 'OK')
self:setContentType('application/json;charset=UTF-8')
--self:setContentType('application/json;charset=UTF-8')
-- A queue for functions to be executed when the response has been given.
-- Needed for api calls like network/associate, which requires a restart of the webserver.
self.postResponseQueue = {}
if requestObject ~= nil then
local rqId = requestObject:get(REQUEST_ID_ARGUMENT)
if rqId ~= nil then self.body[REQUEST_ID_ARGUMENT] = rqId end
if settings.API_INCLUDE_ENDPOINT_INFO == true then
self.body['module'] = requestObject:getRequestedApiModule()
self.body['function'] = requestObject:getRealApiFunctionName() or ''
end
end
return self
end
--- Set HTTP status (the default is `200 OK`).
-- @number code The status code.
-- @string text The status text, this should of course correspond to the given code.
function M:setHttpStatus(code, text)
if code ~= nil then self.httpStatusCode = code end
if text ~= nil then self.httpStatusText = text end
end
--- Set the HTTP content type (the default is `text/plain;charset=UTF-8`).
-- @string contentType The content type to set.
function M:setContentType(contentType)
if contentType ~= nil then self.contentType = contentType end
end
--- Set REST status to success.
-- @string[opt] msg An optional human-readable message to include with the status.
function M:setSuccess(msg)
self.body.status = 'success'
if msg ~= '' then self.body.msg = msg end
end
--- Set REST status to failure.
-- @string[opt] msg An optional human-readable message to include with the status.
function M:setFail(msg)
self.body.status = 'fail'
if msg ~= '' then self.body.msg = msg end
end
--- Set REST status to error.
-- A reference to the API documentation will also be included.
-- @string[opt] msg An optional human-readable message to include with the status.
function M:setError(msg)
self.body.status = 'error'
if msg ~= '' then self.body.msg = msg end
self:addData('more_info', 'http://' .. defaults.API_BASE_URL_PATH .. '/api')
end
--- Adds a data item to the response, this will be included under the `data` item of the json text.
--
-- NOTE: To add nested data with this method, it is necessary to precreate the table
-- and then add that with its root key (see usage).
--
-- After calling this, any binary data set by @{M:setBinaryFileData} will not be sent anymore.
--
-- @string k The key of the item to set.
-- @param v The value to set.
-- @usage response:addData('f_values', {f1=3, f2='x'})
function M:addData(k, v)
self.body.data[k] = v
self.binaryData = nil
end
--- Queue a function for execution after the response has been passed back to the webserver.
--
-- Note that this is not useful in many cases since the webserver will not actually send
-- the response until this script finishes. So for instance if the queue contains code
-- to restart the webserver, the response will never be sent out.
-- @func fn The function to queue.
function M:addPostResponseFunction(fn)
table.insert(self.postResponseQueue, fn)
end
--- Call all functions on the post-response queue, see @{M:addPostResponseFunction} for details and a side-note.
function M:executePostResponseQueue()
if #self.postResponseQueue > 0 then log:verbose(MOD_ABBR, "Response:executePostResponseQueue: " .. utils.dump(self.postResponseQueue)) end
for i,fn in ipairs(self.postResponseQueue) do fn() end
end
--- Returns an API url pointing to @{conf_defaults.API_BASE_URL_PATH}, which is quite useless.
-- @string mod
-- @string func
-- @treturn string A not-so-useful URL.
function M:apiURL(mod, func)
if not mod then return nil end
if func then func = '/' .. func else func = "" end
return 'http://' .. defaults.API_BASE_URL_PATH .. '/cgi-bin/d3dapi/' .. mod .. func
end
--- Returns the body data contained in this object as [JSON](http://www.json.org/).
-- @treturn string The JSON data.
function M:serializeAsJson()
return JSON:encode(self.body)
end
--- Writes HTTP headers to stdout, followed by an HTTP body containing either JSON data or a file attachment.
function M:send()
printHeaderLine("Status", self.httpStatusCode .. " " .. self.httpStatusText)
printHeaderLine("Content-Type", self.contentType)
printHeaderLine("Access-Control-Allow-Origin", "*")
printHeaderLine("Expires", "-1")
if self.binaryData == nil then
io.write("\r\n")
print(self:serializeAsJson())
else
printHeaderLine("Content-Disposition", "attachment; filename=" .. self.binarySavename)
io.write("\r\n")
io.write(self.binaryData)
end
if self.body.status ~= "success" then
log:warning(MOD_ABBR, "Response status: "..utils.dump(self.body.status).." ("..utils.dump(self.body.msg)..")")
end
end
--- Sets the response object to return binary data instead of JSON as its body.
--
-- After calling this, REST data and status will not be sent anymore.
-- @string rFile The file on the local file system to read the data from.
-- @string saveName The file name to suggest the user to save the data in.
-- @string contentType The content type of the data.
-- @treturn bool|nil True, or nil in which case the second argument will be set.
-- @treturn ?string An error message if the first argument is nil.
function M:setBinaryFileData(rFile, saveName, contentType)
if type(rFile) ~= 'string' or rFile:len() == 0 then return false end
local f,msg = io.open(rFile, "rb")
if not f then return nil,msg end
self.binaryData = f:read("*all")
f:close()
self.binarySavename = saveName
self:setContentType(contentType)
return true
end
return M