Mercurial > prosody-modules
changeset 3794:4b258329e6e4
mod_rest: Initial commit of another RESTful API module
author | Kim Alvefur <zash@zash.se> |
---|---|
date | Mon, 30 Dec 2019 04:04:34 +0100 |
parents | 0d3926e49b55 |
children | f51308fcba83 |
files | .luacheckrc mod_rest/README.markdown mod_rest/mod_rest.lua |
diffstat | 3 files changed, 135 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/.luacheckrc Wed Jan 01 10:11:08 2020 +0100 +++ b/.luacheckrc Mon Dec 30 04:04:34 2019 +0100 @@ -1,6 +1,6 @@ cache = true allow_defined_top = true -unused_secondaries = false +--unused_secondaries = false max_line_length = 150 codes = true ignore = { "411/err", "421/err", "411/ok", "421/ok", "211/_ENV" };
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_rest/README.markdown Mon Dec 30 04:04:34 2019 +0100 @@ -0,0 +1,54 @@ +--- +labels: +- 'Stage-Alpha' +summary: RESTful XMPP API +--- + +# Introduction + +This is yet another RESTful API for sending stanzas via Prosody. + +# Usage + +Note that there is currently **no authentication**, so be careful with +exposing the API endpoint to the Internet. + +## Enabling + +``` {.lua} +Component "rest.example.net" "rest" +``` + +## Sending stanzas + +The API endpoint becomes available at the path `/rest`, so the full URL +will be something like `https://your-prosody.example:5281/rest`. + +To try it, simply `curl` an XML stanza payload: + +``` {.sh} +curl https://prosody.example:5281/rest \ + -H 'Content-Type: application/xmpp+xml' \ + --data-binary '<message type="chat" to="user@example.org"> + <body>Hello!</body> + </body>' +``` + +The `Content-Type` **MUST** be `application/xmpp+xml`. + +### Replies + +A POST containing an `<iq>` stanza automatically wait for the reply, +long-polling style. + +``` {.sh} +curl https://prosody.example:5281/rest \ + -H 'Content-Type: application/xmpp+xml' \ + --data-binary '<iq type="get" to="example.net"> + <ping xmlns="urn:xmpp:ping"/> + </iq>' +``` + +# Compatibility + +Requires Prosody trunk / 0.12
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_rest/mod_rest.lua Mon Dec 30 04:04:34 2019 +0100 @@ -0,0 +1,80 @@ +-- RESTful API +-- +-- Copyright (c) 2019 Kim Alvefur +-- +-- This file is MIT/X11 licensed. + +local errors = require "util.error"; +local id = require "util.id"; +local jid = require "util.jid"; +local xml = require "util.xml"; + +local allow_any_source = module:get_host_type() == "component"; +local validate_from_addresses = module:get_option_boolean("validate_from_addresses", true); + +local function handle_post(event) + local request, response = event.request, event.response; + if request.headers.content_type ~= "application/xmpp+xml" then + return errors.new({ code = 415, text = "'application/xmpp+xml' expected" }); + end + local payload, err = xml.parse(request.body); + if not payload then + -- parse fail + return errors.new({ code = 400, text = err }); + end + local to = jid.prep(payload.attr.to); + if not to then + return errors.new({ code = 400, text = "Invalid destination JID" }); + end + local from = module.host; + if allow_any_source and payload.attr.from then + from = jid.prep(payload.attr.from); + if not from then + return errors.new({ code = 400, text = "Invalid source JID" }); + end + if validate_from_addresses and not jid.compare(from, module.host) then + return errors.new({ code = 403, text = "Source JID must belong to current host" }); + end + end + payload.attr = { + from = from, + to = to, + id = payload.attr.id or id.medium(), + type = payload.attr.type, + ["xml:lang"] = payload.attr["xml:lang"], + }; + if payload.name == "iq" then + if payload.attr.type ~= "get" and payload.attr.type ~= "set" then + return errors.new({ code = 400, text = "'iq' stanza must be of type 'get' or 'set'" }); + end + return module:send_iq(payload):next( + function (result) + response.headers.content_type = "application/xmpp+xml"; + return tostring(result.stanza); + end, + function (error) + if error.context.stanza then + response.headers.content_type = "application/xmpp+xml"; + return tostring(error.context.stanza); + else + return error; + end + end); + elseif payload.name == "message" or payload.name == "presence" then + if module:send(payload) then + return 202; + else + return 500; + end + else + return errors.new({ code = 400, text = "Invalid stanza, must be 'message', 'presence' or 'iq'." }); + end +end + +-- Handle stanzas submitted via HTTP +module:depends("http"); +module:provides("http", { + route = { + POST = handle_post; + }; + });