Comparison

mod_rest/mod_rest.lua @ 3813:aa1ad69c7c10

mod_rest: Add JSON support
author Kim Alvefur <zash@zash.se>
date Wed, 01 Jan 2020 16:21:28 +0100
parent 3812:f027b8b1e794
child 3814:0dede5b0ab27
comparison
equal deleted inserted replaced
3812:f027b8b1e794 3813:aa1ad69c7c10
6 6
7 local errors = require "util.error"; 7 local errors = require "util.error";
8 local http = require "net.http"; 8 local http = require "net.http";
9 local id = require "util.id"; 9 local id = require "util.id";
10 local jid = require "util.jid"; 10 local jid = require "util.jid";
11 local json = require "util.json";
11 local st = require "util.stanza"; 12 local st = require "util.stanza";
12 local xml = require "util.xml"; 13 local xml = require "util.xml";
13 14
14 local allow_any_source = module:get_host_type() == "component"; 15 local allow_any_source = module:get_host_type() == "component";
15 local validate_from_addresses = module:get_option_boolean("validate_from_addresses", true); 16 local validate_from_addresses = module:get_option_boolean("validate_from_addresses", true);
16 local secret = assert(module:get_option_string("rest_credentials"), "rest_credentials is a required setting"); 17 local secret = assert(module:get_option_string("rest_credentials"), "rest_credentials is a required setting");
17 local auth_type = assert(secret:match("^%S+"), "Format of rest_credentials MUST be like 'Bearer secret'"); 18 local auth_type = assert(secret:match("^%S+"), "Format of rest_credentials MUST be like 'Bearer secret'");
18 assert(auth_type == "Bearer", "Only 'Bearer' is supported in rest_credentials"); 19 assert(auth_type == "Bearer", "Only 'Bearer' is supported in rest_credentials");
19 20
21 local jsonmap = module:require"jsonmap";
20 -- Bearer token 22 -- Bearer token
21 local function check_credentials(request) 23 local function check_credentials(request)
22 return request.headers.authorization == secret; 24 return request.headers.authorization == secret;
23 end 25 end
24 26
25 local function parse(mimetype, data) 27 local function parse(mimetype, data)
26 mimetype = mimetype:match("^[^; ]*"); 28 mimetype = mimetype:match("^[^; ]*");
27 if mimetype == "application/xmpp+xml" then 29 if mimetype == "application/xmpp+xml" then
28 return xml.parse(data); 30 return xml.parse(data);
31 elseif mimetype == "application/json" then
32 local parsed, err = json.decode(data);
33 if not parsed then
34 return parsed, err;
35 end
36 return jsonmap.json2st(parsed);
29 elseif mimetype == "text/plain" then 37 elseif mimetype == "text/plain" then
30 return st.message({ type = "chat" }, data); 38 return st.message({ type = "chat" }, data);
31 end 39 end
32 return nil, "unknown-payload-type"; 40 return nil, "unknown-payload-type";
33 end 41 end
34 42
35 local function decide_type() 43 local supported_types = { "application/xmpp+xml", "application/json" };
36 return "application/xmpp+xml"; 44
45 local function decide_type(accept)
46 -- assumes the accept header is sorted
47 local ret = supported_types[1];
48 if not accept then
49 return ret;
50 end
51 for i = 2, #supported_types do
52 if (accept:find(supported_types[i], 1, true) or 1000) < (accept:find(ret, 1, true) or 1000) then
53 ret = supported_types[i];
54 end
55 end
56 return ret;
37 end 57 end
38 58
39 local function encode(type, s) 59 local function encode(type, s)
60 if type == "application/json" then
61 return json.encode(jsonmap.st2json(s));
62 elseif type == "text/plain" then
63 return s:get_child_text("body") or "";
64 end
40 return tostring(s); 65 return tostring(s);
41 end 66 end
42 67
43 local function handle_post(event) 68 local function handle_post(event)
44 local request, response = event.request, event.response; 69 local request, response = event.request, event.response;
126 151
127 -- Forward stanzas from XMPP to HTTP and return any reply 152 -- Forward stanzas from XMPP to HTTP and return any reply
128 local rest_url = module:get_option_string("rest_callback_url", nil); 153 local rest_url = module:get_option_string("rest_callback_url", nil);
129 if rest_url then 154 if rest_url then
130 local send_type = module:get_option_string("rest_callback_content_type", "application/xmpp+xml"); 155 local send_type = module:get_option_string("rest_callback_content_type", "application/xmpp+xml");
156 if send_type == "json" then
157 send_type = "application/json";
158 end
131 159
132 local code2err = { 160 local code2err = {
133 [400] = { condition = "bad-request"; type = "modify" }; 161 [400] = { condition = "bad-request"; type = "modify" };
134 [401] = { condition = "not-authorized"; type = "auth" }; 162 [401] = { condition = "not-authorized"; type = "auth" };
135 [402] = { condition = "not-authorized"; type = "auth" }; 163 [402] = { condition = "not-authorized"; type = "auth" };
174 http.request(rest_url, { 202 http.request(rest_url, {
175 body = request_body, 203 body = request_body,
176 headers = { 204 headers = {
177 ["Content-Type"] = send_type, 205 ["Content-Type"] = send_type,
178 ["Content-Language"] = stanza.attr["xml:lang"], 206 ["Content-Language"] = stanza.attr["xml:lang"],
179 Accept = "application/xmpp+xml, text/plain", 207 Accept = table.concat(supported_types, ", ");
180 }, 208 },
181 }, function (body, code, response) 209 }, function (body, code, response)
182 if (code == 202 or code == 204) and not reply_needed then 210 if (code == 202 or code == 204) and not reply_needed then
183 -- Delivered, no reply 211 -- Delivered, no reply
184 return; 212 return;