mirror of
https://github.com/Doodle3D/doodle3d-firmware.git
synced 2024-12-22 11:03:48 +01:00
Merge branch 'master' into printerdriver
Conflicts: src/conf_defaults.lua
This commit is contained in:
commit
ba47733dad
@ -14,12 +14,13 @@
|
|||||||
|
|
||||||
WIFIBOX_IP=192.168.5.1
|
WIFIBOX_IP=192.168.5.1
|
||||||
#WIFIBOX_IP=192.168.10.1
|
#WIFIBOX_IP=192.168.10.1
|
||||||
API_BASE=$WIFIBOX_IP/d3dapi
|
#API_BASE=$WIFIBOX_IP/d3dapi
|
||||||
|
API_BASE=$WIFIBOX_IP/cgi-bin/d3dapi
|
||||||
WGET=wget
|
WGET=wget
|
||||||
#REQUEST_PATH=network/status
|
#REQUEST_PATH=network/status
|
||||||
REQUEST_PATH=printer/print
|
REQUEST_PATH=printer/print
|
||||||
#POST_PARMS=--post-data=xyzzy
|
#POST_PARMS=--post-data=xyzzy
|
||||||
POST_PARMS=--post-file=lorem.txt
|
POST_PARMS=--post-file=200k.gcode
|
||||||
|
|
||||||
RETRIES=1
|
RETRIES=1
|
||||||
|
|
||||||
@ -30,14 +31,14 @@ while true; do
|
|||||||
$WGET -q -O - $POST_PARMS -t $RETRIES $API_BASE/$REQUEST_PATH 2>&1 >/dev/null
|
$WGET -q -O - $POST_PARMS -t $RETRIES $API_BASE/$REQUEST_PATH 2>&1 >/dev/null
|
||||||
#check $? (and time spent?)
|
#check $? (and time spent?)
|
||||||
#print line every 100 counts or when a timeout/error occurs?
|
#print line every 100 counts or when a timeout/error occurs?
|
||||||
|
|
||||||
if [ $? -gt 0 ]; then
|
if [ $? -gt 0 ]; then
|
||||||
echo "response error at counter: $counter"
|
echo "response error at counter: $counter"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ `expr $counter % 25` -eq 0 ]; then
|
if [ `expr $counter % 25` -eq 0 ]; then
|
||||||
echo "counter: $counter"
|
echo "counter: $counter"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
counter=`expr $counter + 1`
|
counter=`expr $counter + 1`
|
||||||
done
|
done
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
- type: used for basic type checking, one of bool, int, float or string
|
- type: used for basic type checking, one of bool, int, float or string
|
||||||
- description: A descriptive text usable by API clients
|
- description: A descriptive text usable by API clients
|
||||||
- min, max, regex: optional constraints (min and max constrain value for numbers, or length for strings)
|
- min, max, regex: optional constraints (min and max constrain value for numbers, or length for strings)
|
||||||
|
|
||||||
NOTE that the all-caps definitions will be changed into configuration keys, or moved to a different location
|
NOTE that the all-caps definitions will be changed into configuration keys, or moved to a different location
|
||||||
]]--
|
]]--
|
||||||
local printer = require('util.printer')
|
local printer = require('util.printer')
|
||||||
@ -29,9 +29,9 @@ M.API_INCLUDE_ENDPOINT_INFO = false
|
|||||||
M.API_BASE_URL_PATH = 'doodle3d.com' -- includes any base path if necessary (e.g. 'localhost/~user')
|
M.API_BASE_URL_PATH = 'doodle3d.com' -- includes any base path if necessary (e.g. 'localhost/~user')
|
||||||
|
|
||||||
M.network_ap_ssid = {
|
M.network_ap_ssid = {
|
||||||
default = 'd3d-ap-%%MAC_ADDR_TAIL%%',
|
default = 'Doodle3D-%%MAC_ADDR_TAIL%%',
|
||||||
type = 'string',
|
type = 'string',
|
||||||
description = 'Access Point mode SSID',
|
description = 'Access Point mode SSID (name)',
|
||||||
min = 1,
|
min = 1,
|
||||||
max = 32
|
max = 32
|
||||||
}
|
}
|
||||||
@ -54,9 +54,9 @@ M.printer_type = {
|
|||||||
default = 'ultimaker',
|
default = 'ultimaker',
|
||||||
type = 'string',
|
type = 'string',
|
||||||
description = '',
|
description = '',
|
||||||
isValid = function(value)
|
isValid = function(value)
|
||||||
local printers = printer.supportedPrinters()
|
local printers = printer.supportedPrinters()
|
||||||
return printers[value] ~= nil
|
return printers[value] ~= nil
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,9 +64,9 @@ M.printer_baudrate = {
|
|||||||
default = '115200',
|
default = '115200',
|
||||||
type = 'int',
|
type = 'int',
|
||||||
description = '',
|
description = '',
|
||||||
isValid = function(value)
|
isValid = function(value)
|
||||||
local baudrates = printer.supportedBaudRates()
|
local baudrates = printer.supportedBaudRates()
|
||||||
return baudrates[tostring(value)] ~= nil
|
return baudrates[tostring(value)] ~= nil
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -124,10 +124,10 @@ M.printer_firstLayerSlow = {
|
|||||||
description = 'Print the first layer slowly to get a more stable start'
|
description = 'Print the first layer slowly to get a more stable start'
|
||||||
}
|
}
|
||||||
|
|
||||||
M.printer_autoWarmUp = {
|
M.printer_heatupTemperature = {
|
||||||
default = true,
|
default = 180,
|
||||||
type = 'bool',
|
type = 'int',
|
||||||
description = '',
|
description = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
M.printer_retraction_enabled = {
|
M.printer_retraction_enabled = {
|
||||||
@ -157,31 +157,43 @@ M.printer_retraction_amount = {
|
|||||||
min = 0
|
min = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
M.printer_heatupTemperature = {
|
M.printer_enableTraveling = {
|
||||||
default = 180,
|
default = false,
|
||||||
type = 'int',
|
type = 'bool',
|
||||||
|
description = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
M.printer_autoWarmUpCommand = {
|
||||||
|
default = 'M104 S180',
|
||||||
|
type = 'string',
|
||||||
description = ''
|
description = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
M.printer_startgcode = {
|
M.printer_startgcode = {
|
||||||
default = ';Generated with Doodle3D\nG21 ;metric values\nG91 ;relative positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E10 ;extrude 10mm of feed stock\nG92 E0 ;zero the extruded length again\nG92 X-100 Y-100 E0 ;zero the extruded length again and make center the start position\nG1 F9000\nG90 ;absolute positioning\nM117 Printing Doodle... ;display message (20 characters to clear whole screen)',
|
default = ';Generated with Doodle3D\nG21 ;metric values\nG91 ;relative positioning\nM107 ;start with the fan off\nG28 X0 Y0 ;move X/Y to min endstops\nG28 Z0 ;move Z to min endstops\nG1 Z15 F9000 ;move the platform down 15mm\nG92 E0 ;zero the extruded length\nG1 F200 E10 ;extrude 10mm of feed stock\nG92 E0 ;zero the extruded length again\nG92 E0 ;zero the extruded length again\nG1 F9000\nG90 ;absolute positioning\nM117 Printing Doodle... ;display message (20 characters to clear whole screen)',
|
||||||
type = 'string',
|
type = 'string',
|
||||||
description = ''
|
description = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
M.printer_endgcode = {
|
M.printer_endgcode = {
|
||||||
default = 'M107 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;disable axes / steppers\nG90 ;absolute positioning\nM117 Done ;display message (20 characters to clear whole screen)',
|
default = 'M107 ;fan off\nG91 ;relative positioning\nG1 E-1 F300 ;retract the filament a bit before lifting the nozzle, to release some of the pressure\nG1 Z+0.5 E-5 X-20 Y-20 F9000 ;move Z up a bit and retract filament even more\nG28 X0 Y0 ;move X/Y to min endstops, so the head is out of the way\nM84 ;disable axes / steppers\nG90 ;absolute positioning\nM104 S180\nM117 Done ;display message (20 characters to clear whole screen)',
|
||||||
type = 'string',
|
type = 'string',
|
||||||
description = ''
|
description = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
M.doodle3d_objectHeight = {
|
M.printer_maxObjectHeight = {
|
||||||
default = 20,
|
default = 150,
|
||||||
type = 'int',
|
type = 'int',
|
||||||
description = 'Maximum height that will be printed',
|
description = 'Maximum height that will be printed',
|
||||||
min = 0
|
min = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
M.printer_screenToMillimeterScale = {
|
||||||
|
default = 0.3,
|
||||||
|
type = 'float',
|
||||||
|
description = '',
|
||||||
|
}
|
||||||
|
|
||||||
M.doodle3d_simplify_minDistance = {
|
M.doodle3d_simplify_minDistance = {
|
||||||
default = 3,
|
default = 3,
|
||||||
type = 'int',
|
type = 'int',
|
||||||
|
@ -160,7 +160,7 @@ local function init(environment)
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
local function main(environment)
|
local function main(environment)
|
||||||
local rq = RequestClass.new(environment, postData, confDefaults.DEBUG_API)
|
local rq = RequestClass.new(environment, postData, confDefaults.DEBUG_API)
|
||||||
|
|
||||||
if rq:getRequestMethod() == 'CMDLINE' and rq:get('autowifi') ~= nil then
|
if rq:getRequestMethod() == 'CMDLINE' and rq:get('autowifi') ~= nil then
|
||||||
|
@ -23,7 +23,7 @@ M.resolutionError = nil --non-nil means function could not be resolved
|
|||||||
local function kvTableFromUrlEncodedString(encodedText)
|
local function kvTableFromUrlEncodedString(encodedText)
|
||||||
local args = {}
|
local args = {}
|
||||||
if (encodedText ~= nil) then
|
if (encodedText ~= nil) then
|
||||||
urlcode.parsequery(encodedText, args)
|
urlcode.parsequeryNoRegex(encodedText, args)
|
||||||
end
|
end
|
||||||
return args
|
return args
|
||||||
|
|
||||||
@ -31,9 +31,9 @@ end
|
|||||||
|
|
||||||
local function kvTableFromArray(argArray)
|
local function kvTableFromArray(argArray)
|
||||||
local args = {}
|
local args = {}
|
||||||
|
|
||||||
if not argArray then return args end
|
if not argArray then return args end
|
||||||
|
|
||||||
for _, v in ipairs(argArray) do
|
for _, v in ipairs(argArray) do
|
||||||
local split = v:find("=")
|
local split = v:find("=")
|
||||||
if split ~= nil then
|
if split ~= nil then
|
||||||
@ -42,7 +42,7 @@ local function kvTableFromArray(argArray)
|
|||||||
args[v] = true
|
args[v] = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return args
|
return args
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -65,18 +65,18 @@ end
|
|||||||
local function resolveApiModule(modname)
|
local function resolveApiModule(modname)
|
||||||
if modname == nil then return nil, "missing module name" end
|
if modname == nil then return nil, "missing module name" end
|
||||||
if string.find(modname, '_') == 1 then return nil, "module names starting with '_' are preserved for internal use" end
|
if string.find(modname, '_') == 1 then return nil, "module names starting with '_' are preserved for internal use" end
|
||||||
|
|
||||||
local reqModName = 'rest.api.api_' .. modname
|
local reqModName = 'rest.api.api_' .. modname
|
||||||
local ok, modObj
|
local ok, modObj
|
||||||
|
|
||||||
if confDefaults.DEBUG_PCALLS then ok, modObj = true, require(reqModName)
|
if confDefaults.DEBUG_PCALLS then ok, modObj = true, require(reqModName)
|
||||||
else ok, modObj = pcall(require, reqModName)
|
else ok, modObj = pcall(require, reqModName)
|
||||||
end
|
end
|
||||||
|
|
||||||
if ok == false then return nil, "API module does not exist" end
|
if ok == false then return nil, "API module does not exist" end
|
||||||
if modObj == nil then return nil, "API module could not be found" end
|
if modObj == nil then return nil, "API module could not be found" end
|
||||||
if modObj.isApi ~= true then return nil, "module is not part of the CGI API" end
|
if modObj.isApi ~= true then return nil, "module is not part of the CGI API" end
|
||||||
|
|
||||||
return modObj
|
return modObj
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -95,43 +95,43 @@ end
|
|||||||
-- @see resolveApiModule
|
-- @see resolveApiModule
|
||||||
local function resolveApiFunction(modname, funcname, requestMethod)
|
local function resolveApiFunction(modname, funcname, requestMethod)
|
||||||
local resultData = {}
|
local resultData = {}
|
||||||
|
|
||||||
if funcname and string.find(funcname, "_") == 1 then return nil, "function names starting with '_' are preserved for internal use" end
|
if funcname and string.find(funcname, "_") == 1 then return nil, "function names starting with '_' are preserved for internal use" end
|
||||||
|
|
||||||
local mod, msg = resolveApiModule(modname)
|
local mod, msg = resolveApiModule(modname)
|
||||||
|
|
||||||
if mod == nil then
|
if mod == nil then
|
||||||
-- error is indicated by leaving out 'func' key and adding 'notfound'=true
|
-- error is indicated by leaving out 'func' key and adding 'notfound'=true
|
||||||
resultData.notfound = true
|
resultData.notfound = true
|
||||||
resultData.msg = msg
|
resultData.msg = msg
|
||||||
return resultData
|
return resultData
|
||||||
end
|
end
|
||||||
|
|
||||||
if (funcname == nil or funcname == '') then funcname = GLOBAL_API_FUNCTION_NAME end --treat empty function name as nil
|
if (funcname == nil or funcname == '') then funcname = GLOBAL_API_FUNCTION_NAME end --treat empty function name as nil
|
||||||
local rqType = requestMethod == 'POST' and 'POST' or 'GET'
|
local rqType = requestMethod == 'POST' and 'POST' or 'GET'
|
||||||
local fGeneric = mod[funcname]
|
local fGeneric = mod[funcname]
|
||||||
local fWithMethod = mod[funcname .. '_' .. rqType]
|
local fWithMethod = mod[funcname .. '_' .. rqType]
|
||||||
local funcNumber = tonumber(funcname)
|
local funcNumber = tonumber(funcname)
|
||||||
|
|
||||||
if (type(fWithMethod) == 'function') then
|
if (type(fWithMethod) == 'function') then
|
||||||
resultData.func = fWithMethod
|
resultData.func = fWithMethod
|
||||||
resultData.accessType = rqType
|
resultData.accessType = rqType
|
||||||
|
|
||||||
elseif (type(fGeneric) == 'function') then
|
elseif (type(fGeneric) == 'function') then
|
||||||
resultData.func = fGeneric
|
resultData.func = fGeneric
|
||||||
resultData.accessType = 'ANY'
|
resultData.accessType = 'ANY'
|
||||||
|
|
||||||
elseif funcNumber ~= nil then
|
elseif funcNumber ~= nil then
|
||||||
resultData.func = mod[GLOBAL_API_FUNCTION_NAME .. '_' .. rqType]
|
resultData.func = mod[GLOBAL_API_FUNCTION_NAME .. '_' .. rqType]
|
||||||
resultData.accessType = rqType
|
resultData.accessType = rqType
|
||||||
|
|
||||||
if not resultData.func then
|
if not resultData.func then
|
||||||
resultData.func = mod[GLOBAL_API_FUNCTION_NAME]
|
resultData.func = mod[GLOBAL_API_FUNCTION_NAME]
|
||||||
resultData.accessType = 'ANY'
|
resultData.accessType = 'ANY'
|
||||||
end
|
end
|
||||||
|
|
||||||
resultData.blankArg = funcNumber
|
resultData.blankArg = funcNumber
|
||||||
|
|
||||||
else
|
else
|
||||||
local otherRqType = rqType == 'POST' and 'GET' or 'POST'
|
local otherRqType = rqType == 'POST' and 'GET' or 'POST'
|
||||||
local fWithOtherMethod = mod[funcname .. '_' .. otherRqType]
|
local fWithOtherMethod = mod[funcname .. '_' .. otherRqType]
|
||||||
@ -143,7 +143,7 @@ local function resolveApiFunction(modname, funcname, requestMethod)
|
|||||||
resultData.notfound = true
|
resultData.notfound = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return resultData
|
return resultData
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -158,7 +158,7 @@ setmetatable(M, {
|
|||||||
--NOTE: if debugging is enabled, commandline arguments 'm' and 'f' override requested module and function
|
--NOTE: if debugging is enabled, commandline arguments 'm' and 'f' override requested module and function
|
||||||
function M.new(environment, postData, debugEnabled)
|
function M.new(environment, postData, debugEnabled)
|
||||||
local self = setmetatable({}, M)
|
local self = setmetatable({}, M)
|
||||||
|
|
||||||
--NOTE: is it correct to assume that absence of REQUEST_METHOD indicates command line invocation?
|
--NOTE: is it correct to assume that absence of REQUEST_METHOD indicates command line invocation?
|
||||||
self.requestMethod = environment['REQUEST_METHOD']
|
self.requestMethod = environment['REQUEST_METHOD']
|
||||||
if type(self.requestMethod) == 'string' and self.requestMethod:len() > 0 then
|
if type(self.requestMethod) == 'string' and self.requestMethod:len() > 0 then
|
||||||
@ -168,16 +168,16 @@ function M.new(environment, postData, debugEnabled)
|
|||||||
else
|
else
|
||||||
self.requestMethod = 'CMDLINE'
|
self.requestMethod = 'CMDLINE'
|
||||||
end
|
end
|
||||||
|
|
||||||
self.cmdLineArgs = kvTableFromArray(arg)
|
self.cmdLineArgs = kvTableFromArray(arg)
|
||||||
self.getArgs = kvTableFromUrlEncodedString(environment['QUERY_STRING'])
|
self.getArgs = kvTableFromUrlEncodedString(environment['QUERY_STRING'])
|
||||||
self.postArgs = kvTableFromUrlEncodedString(postData)
|
self.postArgs = kvTableFromUrlEncodedString(postData)
|
||||||
self.pathArgs = arrayFromPath(environment['PATH_INFO'])
|
self.pathArgs = arrayFromPath(environment['PATH_INFO'])
|
||||||
|
|
||||||
-- override path arguments with command line parameter and allow to emulate GET/POST if debugging is enabled *and* if the autowifi special command wasn't mentioned
|
-- override path arguments with command line parameter and allow to emulate GET/POST if debugging is enabled *and* if the autowifi special command wasn't mentioned
|
||||||
if debugEnabled and self.requestMethod == 'CMDLINE' and self:get('autowifi') == nil then
|
if debugEnabled and self.requestMethod == 'CMDLINE' and self:get('autowifi') == nil then
|
||||||
self.pathArgs = arrayFromPath(self.cmdLineArgs['p'])
|
self.pathArgs = arrayFromPath(self.cmdLineArgs['p'])
|
||||||
|
|
||||||
if self.cmdLineArgs['r'] == 'GET' or self.cmdLineArgs['r'] == nil then
|
if self.cmdLineArgs['r'] == 'GET' or self.cmdLineArgs['r'] == nil then
|
||||||
self.requestMethod = 'GET'
|
self.requestMethod = 'GET'
|
||||||
self.getArgs = self.cmdLineArgs
|
self.getArgs = self.cmdLineArgs
|
||||||
@ -189,19 +189,19 @@ function M.new(environment, postData, debugEnabled)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
table.remove(self.pathArgs, 1) --drop the first 'empty' field caused by the opening slash of the query string
|
table.remove(self.pathArgs, 1) --drop the first 'empty' field caused by the opening slash of the query string
|
||||||
|
|
||||||
|
|
||||||
if #self.pathArgs >= 1 then self.requestedApiModule = self.pathArgs[1] end
|
if #self.pathArgs >= 1 then self.requestedApiModule = self.pathArgs[1] end
|
||||||
if #self.pathArgs >= 2 then self.requestedApiFunction = self.pathArgs[2] end
|
if #self.pathArgs >= 2 then self.requestedApiFunction = self.pathArgs[2] end
|
||||||
|
|
||||||
if self.requestedApiModule == '' then self.requestedApiModule = nil end
|
if self.requestedApiModule == '' then self.requestedApiModule = nil end
|
||||||
if self.requestedApiFunction == '' then self.requestedApiFunction = nil end
|
if self.requestedApiFunction == '' then self.requestedApiFunction = nil end
|
||||||
|
|
||||||
|
|
||||||
-- Perform module/function resolution
|
-- Perform module/function resolution
|
||||||
local rData = resolveApiFunction(self:getRequestedApiModule(), self:getRequestedApiFunction(), self.requestMethod)
|
local rData = resolveApiFunction(self:getRequestedApiModule(), self:getRequestedApiFunction(), self.requestMethod)
|
||||||
local modFuncInfo = (self:getRequestedApiModule() or "<>") .. "/" .. (self:getRequestedApiFunction() or "<>")
|
local modFuncInfo = (self:getRequestedApiModule() or "<>") .. "/" .. (self:getRequestedApiFunction() or "<>")
|
||||||
|
|
||||||
if rData.func ~= nil then --function (possibly the global one) could be resolved
|
if rData.func ~= nil then --function (possibly the global one) could be resolved
|
||||||
self.resolvedApiFunction = rData.func
|
self.resolvedApiFunction = rData.func
|
||||||
if rData.blankArg ~= nil then --apparently it was the global one, and we received a 'blank argument'
|
if rData.blankArg ~= nil then --apparently it was the global one, and we received a 'blank argument'
|
||||||
@ -220,7 +220,7 @@ function M.new(environment, postData, debugEnabled)
|
|||||||
else
|
else
|
||||||
self.resolutionError = "module/function '" .. modFuncInfo .. "' can only be accessed with the " .. rData.accessType .. " method"
|
self.resolutionError = "module/function '" .. modFuncInfo .. "' can only be accessed with the " .. rData.accessType .. " method"
|
||||||
end
|
end
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -266,14 +266,14 @@ end
|
|||||||
function M:handle()
|
function M:handle()
|
||||||
local modname = self:getRequestedApiModule()
|
local modname = self:getRequestedApiModule()
|
||||||
local resp = ResponseClass.new(self)
|
local resp = ResponseClass.new(self)
|
||||||
|
|
||||||
if (self.resolvedApiFunction ~= nil) then --we found a function (possible the global function)
|
if (self.resolvedApiFunction ~= nil) then --we found a function (possible the global function)
|
||||||
--invoke the function
|
--invoke the function
|
||||||
local ok, r
|
local ok, r
|
||||||
if confDefaults.DEBUG_PCALLS then ok, r = true, self.resolvedApiFunction(self, resp)
|
if confDefaults.DEBUG_PCALLS then ok, r = true, self.resolvedApiFunction(self, resp)
|
||||||
else ok, r = pcall(self.resolvedApiFunction, self, resp)
|
else ok, r = pcall(self.resolvedApiFunction, self, resp)
|
||||||
end
|
end
|
||||||
|
|
||||||
--handle the result
|
--handle the result
|
||||||
if ok == true then
|
if ok == true then
|
||||||
return resp, nil
|
return resp, nil
|
||||||
@ -285,7 +285,7 @@ function M:handle()
|
|||||||
resp:setError("cannot call function or module '" .. (modname or "<empty>") .. "/" .. (self:getRequestedApiFunction() or "<empty>") .. "' ('" .. self.resolutionError .. "')")
|
resp:setError("cannot call function or module '" .. (modname or "<empty>") .. "/" .. (self:getRequestedApiFunction() or "<empty>") .. "' ('" .. self.resolutionError .. "')")
|
||||||
return resp, ("cannot call requested API function ('" .. self.resolutionError .. "')")
|
return resp, ("cannot call requested API function ('" .. self.resolutionError .. "')")
|
||||||
end
|
end
|
||||||
|
|
||||||
return resp
|
return resp
|
||||||
end
|
end
|
||||||
|
|
||||||
|
70
src/test/test_urlcode.lua
Normal file
70
src/test/test_urlcode.lua
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
-- TODO: also test malformed query strings
|
||||||
|
local urlcode = require("util.urlcode")
|
||||||
|
|
||||||
|
local M = {
|
||||||
|
_is_test = true,
|
||||||
|
_skip = {},
|
||||||
|
_wifibox_only = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- NOTE: the previous approach using #t1 and #t2 was too naive and only worked for tables with contiguous ranges of numeric keys.
|
||||||
|
local function compareTables(t1, t2)
|
||||||
|
local len = 0
|
||||||
|
|
||||||
|
for k1,v1 in pairs(t1) do
|
||||||
|
len = len + 1
|
||||||
|
if t2[k1] ~= v1 then return false end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _ in pairs(t2) do len = len - 1 end
|
||||||
|
|
||||||
|
return len == 0 and true or false
|
||||||
|
end
|
||||||
|
|
||||||
|
local queryTexts = {
|
||||||
|
[1] = "k1=v1&k2=v2x&k3yy=v3",
|
||||||
|
[2] = "k1=v1&k2=v2x&k3yy=v3&",
|
||||||
|
[3] = "k1=v1&k2=v2x&k3yy=v3&=",
|
||||||
|
[4] = "k1=v1&k2=v2x&k3yy=v3& =",
|
||||||
|
[5] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
local queryTables = {
|
||||||
|
[1] = { ["k1"] = "v1", ["k2"] = "v2x", ["k3yy"] = "v3" },
|
||||||
|
[2] = { ["k1"] = "v1", ["k2"] = "v2x", ["k3yy"] = "v3" },
|
||||||
|
[3] = { ["k1"] = "v1", ["k2"] = "v2x", ["k3yy"] = "v3" },
|
||||||
|
[4] = { ["k1"] = "v1", ["k2"] = "v2x", ["k3yy"] = "v3", [" "] = "" },
|
||||||
|
[5] = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
function M:_setup()
|
||||||
|
local longValue = ""
|
||||||
|
for i=1,5000 do
|
||||||
|
longValue = longValue .. i .. ": abcdefghijklmnopqrstuvwxyz\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(queryTexts, "shortkey=&longkey=" .. longValue)
|
||||||
|
table.insert(queryTables, { ["shortkey"] = "", ["longkey"] = longValue })
|
||||||
|
end
|
||||||
|
|
||||||
|
function M:_teardown()
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function M:test_parsequery()
|
||||||
|
for i=1,#queryTexts do
|
||||||
|
local args = {}
|
||||||
|
urlcode.parsequery(queryTexts[i], args)
|
||||||
|
assert(compareTables(queryTables[i], args))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function M:test_parsequeryNoRegex()
|
||||||
|
for i=1,#queryTexts do
|
||||||
|
local args = {}
|
||||||
|
urlcode.parsequeryNoRegex(queryTexts[i], args)
|
||||||
|
assert(compareTables(queryTables[i], args))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -15,32 +15,32 @@ local _M = {}
|
|||||||
|
|
||||||
-- Converts an hexadecimal code in the form %XX to a character
|
-- Converts an hexadecimal code in the form %XX to a character
|
||||||
local function hexcode2char (h)
|
local function hexcode2char (h)
|
||||||
return strchar(tonumber(h,16))
|
return strchar(tonumber(h,16))
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
-- Decode an URL-encoded string (see RFC 2396)
|
-- Decode an URL-encoded string (see RFC 2396)
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
function _M.unescape (str)
|
function _M.unescape (str)
|
||||||
str = gsub (str, "+", " ")
|
str = gsub (str, "+", " ")
|
||||||
str = gsub (str, "%%(%x%x)", hexcode2char)
|
str = gsub (str, "%%(%x%x)", hexcode2char)
|
||||||
str = gsub (str, "\r\n", "\n")
|
str = gsub (str, "\r\n", "\n")
|
||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Converts a character to an hexadecimal code in the form %XX
|
-- Converts a character to an hexadecimal code in the form %XX
|
||||||
local function char2hexcode (c)
|
local function char2hexcode (c)
|
||||||
return strformat ("%%%02X", strbyte(c))
|
return strformat ("%%%02X", strbyte(c))
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
-- URL-encode a string (see RFC 2396)
|
-- URL-encode a string (see RFC 2396)
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
function _M.escape (str)
|
function _M.escape (str)
|
||||||
str = gsub (str, "\n", "\r\n")
|
str = gsub (str, "\n", "\r\n")
|
||||||
str = gsub (str, "([^0-9a-zA-Z ])", char2hexcode) -- locale independent
|
str = gsub (str, "([^0-9a-zA-Z ])", char2hexcode) -- locale independent
|
||||||
str = gsub (str, " ", "+")
|
str = gsub (str, " ", "+")
|
||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
@ -52,21 +52,21 @@ end
|
|||||||
-- (in the order they came).
|
-- (in the order they came).
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
function _M.insertfield (args, name, value)
|
function _M.insertfield (args, name, value)
|
||||||
if not args[name] then
|
if not args[name] then
|
||||||
args[name] = value
|
args[name] = value
|
||||||
else
|
else
|
||||||
local t = type (args[name])
|
local t = type (args[name])
|
||||||
if t == "string" then
|
if t == "string" then
|
||||||
args[name] = {
|
args[name] = {
|
||||||
args[name],
|
args[name],
|
||||||
value,
|
value,
|
||||||
}
|
}
|
||||||
elseif t == "table" then
|
elseif t == "table" then
|
||||||
tinsert (args[name], value)
|
tinsert (args[name], value)
|
||||||
else
|
else
|
||||||
error ("CGILua fatal error (invalid args table)!")
|
error ("CGILua fatal error (invalid args table)!")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
@ -78,13 +78,46 @@ end
|
|||||||
-- @param args Table where to store the pairs.
|
-- @param args Table where to store the pairs.
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
function _M.parsequery (query, args)
|
function _M.parsequery (query, args)
|
||||||
if type(query) == "string" then
|
if type(query) == "string" then
|
||||||
local insertfield, unescape = _M.insertfield, _M.unescape
|
local insertfield, unescape = _M.insertfield, _M.unescape
|
||||||
gsub (query, "([^&=]+)=([^&=]*)&?",
|
gsub (query, "([^&=]+)=([^&=]*)&?",
|
||||||
function (key, val)
|
function (key, val)
|
||||||
_M.insertfield (args, unescape(key), unescape(val))
|
_M.insertfield (args, unescape(key), unescape(val))
|
||||||
end)
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
-- Parse url-encoded request data without using regular expressions
|
||||||
|
-- (the query part of the script URL or url-encoded post data)
|
||||||
|
--
|
||||||
|
-- Each decoded (name=value) pair is inserted into table [[args]]
|
||||||
|
-- @param query String to be parsed.
|
||||||
|
-- @param args Table where to store the pairs.
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
function _M.parsequeryNoRegex (query, args)
|
||||||
|
if type(query) == "string" then
|
||||||
|
local insertfield, unescape = _M.insertfield, _M.unescape
|
||||||
|
|
||||||
|
local k = 1
|
||||||
|
while true do
|
||||||
|
local v = query:find('=', k+1, true) -- look for '=', assuming a key of at least 1 character and do not perform pattern matching
|
||||||
|
if not v then break end -- no k/v pairs left
|
||||||
|
|
||||||
|
local key = query:sub(k, v-1)
|
||||||
|
v = v + 1
|
||||||
|
local ampersand = query:find('&', v, true)
|
||||||
|
if not ampersand then ampersand = 0 end -- 0 will become -1 in the substring call below...meaning end of string
|
||||||
|
|
||||||
|
|
||||||
|
local value = query:sub(v, ampersand - 1)
|
||||||
|
insertfield (args, unescape(key), unescape(value))
|
||||||
|
|
||||||
|
if ampersand == 0 then break end -- we couldn't find any ampersands anymore so this was the last k/v
|
||||||
|
|
||||||
|
k = ampersand + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
@ -94,21 +127,21 @@ end
|
|||||||
-- @return String with the resulting encoding.
|
-- @return String with the resulting encoding.
|
||||||
----------------------------------------------------------------------------
|
----------------------------------------------------------------------------
|
||||||
function _M.encodetable (args)
|
function _M.encodetable (args)
|
||||||
if args == nil or next(args) == nil then -- no args or empty args?
|
if args == nil or next(args) == nil then -- no args or empty args?
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
local escape = _M.escape
|
local escape = _M.escape
|
||||||
local strp = ""
|
local strp = ""
|
||||||
for key, vals in pairs(args) do
|
for key, vals in pairs(args) do
|
||||||
if type(vals) ~= "table" then
|
if type(vals) ~= "table" then
|
||||||
vals = {vals}
|
vals = {vals}
|
||||||
end
|
end
|
||||||
for i,val in ipairs(vals) do
|
for i,val in ipairs(vals) do
|
||||||
strp = strp.."&"..escape(key).."="..escape(val)
|
strp = strp.."&"..escape(key).."="..escape(val)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
-- remove first &
|
-- remove first &
|
||||||
return strsub(strp,2)
|
return strsub(strp,2)
|
||||||
end
|
end
|
||||||
|
|
||||||
return _M
|
return _M
|
||||||
|
2
src/www
2
src/www
@ -1 +1 @@
|
|||||||
Subproject commit a57a36f420bb6ed4e1ded56a2b65ac2cb6385c87
|
Subproject commit 1842ec01b2d455142b8f654323ec92a4e2395753
|
Loading…
Reference in New Issue
Block a user