From e4361842fcdec369fbd4466f2528b2815f504ff9 Mon Sep 17 00:00:00 2001 From: Kaarle Ritvanen Date: Sun, 16 Dec 2012 19:10:38 +0200 Subject: initial version --- server.lua | 115 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 server.lua (limited to 'server.lua') diff --git a/server.lua b/server.lua new file mode 100644 index 0000000..11bfe55 --- /dev/null +++ b/server.lua @@ -0,0 +1,115 @@ +--[[ +Copyright (c) 2012 Kaarle Ritvanen +See LICENSE file for license details +--]] + +require 'acf' +local isinstance = acf.object.isinstance + +require 'json' +require 'stringy' + + +local function handle(env, txn, path) + local method = env.REQUEST_METHOD + local data + if env.CONTENT_LENGTH then + data = json.decode(env.input:read(env.CONTENT_LENGTH)) + end + + local parent, name + if path ~= '/' then + parent = txn:search(acf.path.parent(path)) + name = acf.path.name(path) + end + + if method == 'GET' then + local obj = txn:search(path) + if obj == nil then return 404 end + + local res + + if isinstance(obj, acf.model.node.TreeNode) then + local node = {} + for _, k in ipairs(acf.model.node.members(obj)) do + local v = obj[k] + if isinstance(v, acf.model.node.TreeNode) then + v = acf.model.node.path(v) + end + node[k] = v + end + res = {data=node, meta=acf.model.node.meta(obj)} + + else res = {data=obj, meta=acf.model.node.mmeta(parent, name)} end + + local function f() coroutine.yield(json.encode(res)) end + return 200, {['Content-Type']='application/json'}, coroutine.wrap(f) + end + + if method == 'DELETE' then + parent[name] = nil + + elseif method == 'PUT' then + parent[name] = data + + -- TODO implement POST for invoking object-specific actions + + else return 405 end + + return 200 +end + + +-- TODO implement transactions as threads or store their state in +-- shared storage +local last_id = 0 +local txns = {} + + +return function(env) + local method = env.REQUEST_METHOD + local path = env.REQUEST_URI + + -- TODO login session management + + local txn_id = tonumber(env.HTTP_X_ACF_TRANSACTION_ID) + local txn + if txn_id then + txn = txns[txn_id] + if not txn then return 404 end + else txn = acf.transaction.start() end + + if stringy.startswith(path, '/config/') then + -- TODO catch and forward relevant errors to the client + local code, hdr, body = handle(env, + txn, + string.sub(path, 8, -1)) + if not txn_id and method ~= 'GET' and code == 200 then + txn:commit() + end + return code, hdr, body + end + + if path == '/' then + if method == 'GET' then return 301, {['Location']='/browser/'} end + + if not ({DELETE=true, POST=true})[method] then + return 405 + end + + if txn_id then + if method == 'POST' then txn:commit() end + txns[txn_id] = nil + return 200 + end + + if method == 'DELETE' then return 405 end + + last_id = last_id + 1 + local txn_id = last_id + txns[txn_id] = txn + return 200, {['X-ACF-Transaction-ID']=txn_id} + end + + return 404 + end -- cgit v1.2.3