changeset 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 356b5ad521a5
files mod_rest/README.markdown mod_rest/mod_rest.lua mod_rest/openapi.yaml
diffstat 3 files changed, 151 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- 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: