comparison mod_rest/mod_rest.lua @ 4478:7ab0c423688a

mod_rest: Support GET for certain IQ queries Example: GET /rest/version/example.com 200 OK { version: { name: "thing", version: "1.0.0" } }
author Kim Alvefur <zash@zash.se>
date Sun, 28 Feb 2021 19:33:09 +0100
parents 8df6cc648963
children dad0367d33e8
comparison
equal deleted inserted replaced
4477:8df6cc648963 4478:7ab0c423688a
61 61
62 -- (table, string) -> table 62 -- (table, string) -> table
63 local function amend_from_path(data, path) 63 local function amend_from_path(data, path)
64 local st_kind, st_type, st_to = path:match("^([mpi]%w+)/(%w+)/(.*)$"); 64 local st_kind, st_type, st_to = path:match("^([mpi]%w+)/(%w+)/(.*)$");
65 if not st_kind then return; end 65 if not st_kind then return; end
66 data.kind = st_kind; 66 if st_kind == "iq" and st_type ~= "get" and st_type ~= "set" then
67 data.type = st_type; 67 -- GET /iq/disco/jid
68 data = {
69 kind = "iq";
70 type = "get";
71 [st_type] = data;
72 }
73 else
74 data.kind = st_kind;
75 data.type = st_type;
76 end
68 if st_to and st_to ~= "" then 77 if st_to and st_to ~= "" then
69 data.to = st_to; 78 data.to = st_to;
70 end 79 end
71 return data; 80 return data;
72 end 81 end
109 elseif parsed.kind == "presence" then 118 elseif parsed.kind == "presence" then
110 parsed.show = data; 119 parsed.show = data;
111 else 120 else
112 return nil, "invalid-path"; 121 return nil, "invalid-path";
113 end 122 end
123 return jsonmap.json2st(parsed);
124 elseif not mimetype and path then
125 local parsed = amend_from_path({}, path);
126 if not parsed then return nil, "invalid-path"; end
114 return jsonmap.json2st(parsed); 127 return jsonmap.json2st(parsed);
115 end 128 end
116 return nil, "unknown-payload-type"; 129 return nil, "unknown-payload-type";
117 end 130 end
118 131
186 iq_type = { code = 422, condition = "invalid-xml", text = "'iq' stanza must be of type 'get' or 'set'", }, 199 iq_type = { code = 422, condition = "invalid-xml", text = "'iq' stanza must be of type 'get' or 'set'", },
187 iq_tags = { code = 422, condition = "bad-format", text = "'iq' stanza must have exactly one child tag", }, 200 iq_tags = { code = 422, condition = "bad-format", text = "'iq' stanza must have exactly one child tag", },
188 mediatype = { code = 415, condition = "bad-format", text = "Unsupported media type" }, 201 mediatype = { code = 415, condition = "bad-format", text = "Unsupported media type" },
189 }); 202 });
190 203
191 local function handle_post(event, path) 204 -- GET → iq-get
205 local function parse_request(request, path)
206 if path and request.method == "GET" then
207 -- e.g. /verison/{to}
208 return parse(nil, nil, "iq/"..path);
209 else
210 return parse(request.headers.content_type, request.body, path);
211 end
212 end
213
214 local function handle_request(event, path)
192 local request, response = event.request, event.response; 215 local request, response = event.request, event.response;
193 local from; 216 local from;
194 local origin; 217 local origin;
195 218
196 if not request.headers.authorization then 219 if not request.headers.authorization then
201 if not origin then 224 if not origin then
202 return post_errors.new("unauthz"); 225 return post_errors.new("unauthz");
203 end 226 end
204 from = jid.join(origin.username, origin.host, origin.resource); 227 from = jid.join(origin.username, origin.host, origin.resource);
205 end 228 end
206 local payload, err = parse(request.headers.content_type, request.body, path); 229 local payload, err = parse_request(request, path);
207 if not payload then 230 if not payload then
208 -- parse fail 231 -- parse fail
209 local ctx = { error = err, type = request.headers.content_type, data = request.body, }; 232 local ctx = { error = err, type = request.headers.content_type, data = request.body, };
210 if err == "unknown-payload-type" then 233 if err == "unknown-payload-type" then
211 return post_errors.new("mediatype", ctx); 234 return post_errors.new("mediatype", ctx);
243 type = payload.attr.type, 266 type = payload.attr.type,
244 ["xml:lang"] = payload.attr["xml:lang"], 267 ["xml:lang"] = payload.attr["xml:lang"],
245 }; 268 };
246 269
247 module:log("debug", "Received[rest]: %s", payload:top_tag()); 270 module:log("debug", "Received[rest]: %s", payload:top_tag());
248 local send_type = decide_type((request.headers.accept or "") ..",".. request.headers.content_type, supported_outputs) 271 local send_type = decide_type((request.headers.accept or "") ..",".. (request.headers.content_type or ""), supported_outputs)
249 if payload.name == "iq" then 272 if payload.name == "iq" then
250 function origin.send(stanza) 273 function origin.send(stanza)
251 module:send(stanza); 274 module:send(stanza);
252 end 275 end
253 276
290 313
291 -- Handle stanzas submitted via HTTP 314 -- Handle stanzas submitted via HTTP
292 module:depends("http"); 315 module:depends("http");
293 module:provides("http", { 316 module:provides("http", {
294 route = { 317 route = {
295 POST = handle_post; 318 POST = handle_request;
296 ["POST /*"] = handle_post; 319 ["POST /*"] = handle_request;
320 ["GET /*"] = handle_request;
297 }; 321 };
298 }); 322 });
299 323
300 -- Forward stanzas from XMPP to HTTP and return any reply 324 -- Forward stanzas from XMPP to HTTP and return any reply
301 local rest_url = module:get_option_string("rest_callback_url", nil); 325 local rest_url = module:get_option_string("rest_callback_url", nil);