Software /
code /
prosody-modules
Diff
mod_json_streams/mod_json_streams.lua @ 353:8ef36af30181
merge with upstream
author | Phil Stewart <phil.stewart@lichp.co.uk> |
---|---|
date | Sun, 03 Apr 2011 22:49:36 +0100 |
parent | 350:98569ec25ac2 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_json_streams/mod_json_streams.lua Sun Apr 03 22:49:36 2011 +0100 @@ -0,0 +1,154 @@ +-- +-- XEP-0295: JSON Encodings for XMPP +-- + +module.host = "*" + +local httpserver = require "net.httpserver"; +local filters = require "util.filters" +local json = require "util.json" + +local json_escapes = { + ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", + ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"}; + +local s_char = string.char; +for i=0,31 do + local ch = s_char(i); + if not json_escapes[ch] then json_escapes[ch] = ("\\u%.4X"):format(i); end +end + +local state_out = 0; +local state_key_before = 1; +local state_key_in = 2; +local state_key_escape = 3; +local state_key_after = 4; +local state_val_before = 5; +local state_val_in = 6; +local state_val_escape = 7; +local state_val_after = 8; + +local whitespace = { [" "] = true, ["\n"] = true, ["\r"] = true, ["\t"] = true }; +function json_decoder() + local state = state_out; + local quote; + local output = ""; + local buffer = ""; + return function(input) + for ch in input:gmatch(".") do + module:log("debug", "%s | %d", ch, state) + local final = false; + if state == state_out then + if whitespace[ch] then + elseif ch ~= "{" then return nil, "{ expected"; + else state = state_key_before end + elseif state == state_key_before then + if whitespace[ch] then + elseif ch ~= "'" and ch ~= "\"" then return nil, "\" expected"; + else quote = ch; state = state_key_in; end + elseif state == state_key_in then + if ch == quote then state = state_key_after; + elseif ch ~= "s" then return nil, "invalid key, 's' expected"; -- only s as key allowed + else end -- ignore key + elseif state == state_key_after then + if whitespace[ch] then + elseif ch ~= ":" then return nil, ": expected"; + else state = state_val_before; end + elseif state == state_val_before then + if whitespace[ch] then + elseif ch ~= "'" and ch ~= "\"" then return nil, "\" expected"; + else quote = ch; state = state_val_in; end + elseif state == state_val_in then + if ch == quote then state = state_val_after; + elseif ch == "\\" then state = state_val_escape; + else end + elseif state == state_val_after then + if whitespace[ch] then + elseif ch ~= "}" then return nil, "} expected"; + else state = state_out; + final = true; + end + elseif state == state_val_escape then + state = state_val_in; + else + module:log("error", "Unhandled state: "..state); + return nil, "Unhandled state in parser" + end + buffer = buffer..ch; + if final then + module:log("debug", "%s", buffer) + local tmp; + pcall(function() tmp = json.decode(buffer); end); + if not tmp then return nil, "Invalid JSON"; end + output, buffer = output..tmp.s, ""; + end + end + local _ = output; output = ""; + return _; + end; +end + +function filter_hook(session) + local determined = false; + local is_json = false; + local function in_filter(t) + if not determined then + is_json = (t:sub(1,1) == "{") and json_decoder(); + determined = true; + end + if is_json then + local s, err = is_json(t); + if not err then return s; end + session:close("not-well-formed"); + return; + end + return t; + end + local function out_filter(t) + if is_json then + return '{"s":"' .. t:gsub(".", json_escapes) .. '"}'; -- encode + end + return t; + end + filters.add_filter(session, "bytes/in", in_filter, 100); + filters.add_filter(session, "bytes/out", out_filter, 100); +end + +function module.load() + filters.add_filter_hook(filter_hook); +end +function module.unload() + filters.remove_filter_hook(filter_hook); +end + +function encode(data) + if type(data) == "string" then + data = json.encode({ s = data }); + elseif type(data) == "table" and data.body then + data.body = json.encode({ s = data.body }); + data.headers["Content-Type"] = "application/json"; + end + return data; +end +function handle_request(method, body, request) + local mod_bosh = modulemanager.get_module("*", "bosh") + if mod_bosh then + if body and method == "POST" then + pcall(function() body = json.decode(body).s; end); + end + local _send = request.send; + function request:send(data) return _send(self, encode(data)); end + return encode(mod_bosh.handle_request(method, body, request)); + end + return "<html><body>mod_bosh not loaded</body></html>"; +end + +local function setup() + local ports = module:get_option("jsonstreams_ports") or { 5280 }; + httpserver.new_from_config(ports, handle_request, { base = "jsonstreams" }); +end +if prosody.start_time then -- already started + setup(); +else + prosody.events.add_handler("server-started", setup); +end