mod_rest: Support GET for certain IQ queries
Example:
GET /rest/version/example.com
200 OK
{ version: { name: "thing", version: "1.0.0" } }
--- a/mod_rest/README.markdown Sun Feb 28 19:25:45 2021 +0100
+++ b/mod_rest/README.markdown Sun Feb 28 19:33:09 2021 +0100
@@ -101,6 +101,23 @@
entities (connected clients or remote servers) will not be returned, but
can be forwarded via the callback API described in the next section.
+### Simple info queries
+
+A subset of IQ stanzas can be sent as simple GET requests
+
+```
+curl https://prosody.example:5281/rest/version/example.com \
+ --oauth2-bearer dmVyeSBzZWNyZXQgdG9rZW4K \
+ -H 'Accept: application/json'
+```
+
+The supported queries are
+
+- `disco`
+- `items`
+- `version`
+- `ping`
+
## Receiving stanzas
TL;DR: Set this webhook callback URL, get XML `POST`-ed there.
--- a/mod_rest/mod_rest.lua Sun Feb 28 19:25:45 2021 +0100
+++ b/mod_rest/mod_rest.lua Sun Feb 28 19:33:09 2021 +0100
@@ -63,8 +63,17 @@
local function amend_from_path(data, path)
local st_kind, st_type, st_to = path:match("^([mpi]%w+)/(%w+)/(.*)$");
if not st_kind then return; end
- data.kind = st_kind;
- data.type = st_type;
+ if st_kind == "iq" and st_type ~= "get" and st_type ~= "set" then
+ -- GET /iq/disco/jid
+ data = {
+ kind = "iq";
+ type = "get";
+ [st_type] = data;
+ }
+ else
+ data.kind = st_kind;
+ data.type = st_type;
+ end
if st_to and st_to ~= "" then
data.to = st_to;
end
@@ -112,6 +121,10 @@
return nil, "invalid-path";
end
return jsonmap.json2st(parsed);
+ elseif not mimetype and path then
+ local parsed = amend_from_path({}, path);
+ if not parsed then return nil, "invalid-path"; end
+ return jsonmap.json2st(parsed);
end
return nil, "unknown-payload-type";
end
@@ -188,7 +201,17 @@
mediatype = { code = 415, condition = "bad-format", text = "Unsupported media type" },
});
-local function handle_post(event, path)
+-- GET → iq-get
+local function parse_request(request, path)
+ if path and request.method == "GET" then
+ -- e.g. /verison/{to}
+ return parse(nil, nil, "iq/"..path);
+ else
+ return parse(request.headers.content_type, request.body, path);
+ end
+end
+
+local function handle_request(event, path)
local request, response = event.request, event.response;
local from;
local origin;
@@ -203,7 +226,7 @@
end
from = jid.join(origin.username, origin.host, origin.resource);
end
- local payload, err = parse(request.headers.content_type, request.body, path);
+ local payload, err = parse_request(request, path);
if not payload then
-- parse fail
local ctx = { error = err, type = request.headers.content_type, data = request.body, };
@@ -245,7 +268,7 @@
};
module:log("debug", "Received[rest]: %s", payload:top_tag());
- local send_type = decide_type((request.headers.accept or "") ..",".. request.headers.content_type, supported_outputs)
+ local send_type = decide_type((request.headers.accept or "") ..",".. (request.headers.content_type or ""), supported_outputs)
if payload.name == "iq" then
function origin.send(stanza)
module:send(stanza);
@@ -292,8 +315,9 @@
module:depends("http");
module:provides("http", {
route = {
- POST = handle_post;
- ["POST /*"] = handle_post;
+ POST = handle_request;
+ ["POST /*"] = handle_request;
+ ["GET /*"] = handle_request;
};
});
--- a/mod_rest/openapi.yaml Sun Feb 28 19:25:45 2021 +0100
+++ b/mod_rest/openapi.yaml Sun Feb 28 19:33:09 2021 +0100
@@ -62,6 +62,109 @@
payload.
415:
description: Unsupported mediatype.
+ /rest/ping/{to}:
+ get:
+ security:
+ - basic: []
+ - token: []
+ summary: Ping a local or remote server or other entity
+ responses:
+ 200:
+ content:
+ application/json:
+ schema:
+ type: object
+ application/xmpp+xml:
+ schema:
+ description: Single XMPP stanza in XML format.
+ example: |
+ <iq type="result"/>
+ description: OK
+ parameters:
+ - name: to
+ in: path
+ required: true
+ schema:
+ $ref: '#/components/schemas/to'
+ /rest/disco/{to}:
+ get:
+ security:
+ - basic: []
+ - token: []
+ summary: Query a remote entity for supported features
+ responses:
+ 200:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ disco:
+ $ref: '#/components/schemas/disco'
+ application/xmpp+xml:
+ schema:
+ description: See XEP-0030
+ description: OK
+ parameters:
+ - name: to
+ in: path
+ required: true
+ schema:
+ $ref: '#/components/schemas/to'
+ /rest/items/{to}:
+ get:
+ security:
+ - basic: []
+ - token: []
+ summary: Query an entity for related services, chat rooms or other items
+ responses:
+ 200:
+ content:
+ application/json:
+ schema:
+ type: object
+ properties:
+ disco:
+ $ref: '#/components/schemas/items'
+ application/xmpp+xml:
+ schema:
+ description: See XEP-0030
+ description: OK
+ parameters:
+ - name: to
+ in: path
+ required: true
+ schema:
+ $ref: '#/components/schemas/to'
+ /rest/version/{to}:
+ get:
+ security:
+ - basic: []
+ - token: []
+ summary: Ask what software version is used
+ responses:
+ 200:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/stanza'
+ application/xmpp+xml:
+ schema:
+ description: Single XMPP stanza in XML format.
+ example: |
+ <iq type="result">
+ <query xmlns="jabber:iq:version">
+ <name>Exodus</name>
+ <version>0.7.0.4</version>
+ </query>
+ </iq>
+ description: OK
+ parameters:
+ - name: to
+ in: path
+ required: true
+ schema:
+ $ref: '#/components/schemas/to'
/rest/{kind}/{type}/{to}:
post:
responses: