Software /
code /
prosody-modules
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 |
parent | 4477:8df6cc648963 |
child | 4480: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); |