Mercurial > prosody-modules
comparison mod_rest/mod_rest.lua @ 4477:8df6cc648963
mod_rest: Add more REST-looking way to send stanzas
Example:
POST /rest/message/chat/juliet@example.net
{ body: "Hello" }
Becomes equivalent to
POST /rest
{ kind: "message", type: "chat", to: "juliet@example.net", body: "Hello" }
Sending messages as plain/text also becomes more convenient.
IQ stazas are still weird, but we'll do something special for those.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sun, 28 Feb 2021 19:25:45 +0100 |
| parents | 8b489203e4d3 |
| children | 7ab0c423688a |
comparison
equal
deleted
inserted
replaced
| 4476:125279f4a5b8 | 4477:8df6cc648963 |
|---|---|
| 57 return token_info.session; | 57 return token_info.session; |
| 58 end | 58 end |
| 59 return nil; | 59 return nil; |
| 60 end | 60 end |
| 61 | 61 |
| 62 local function parse(mimetype, data) | 62 -- (table, string) -> table |
| 63 local function amend_from_path(data, path) | |
| 64 local st_kind, st_type, st_to = path:match("^([mpi]%w+)/(%w+)/(.*)$"); | |
| 65 if not st_kind then return; end | |
| 66 data.kind = st_kind; | |
| 67 data.type = st_type; | |
| 68 if st_to and st_to ~= "" then | |
| 69 data.to = st_to; | |
| 70 end | |
| 71 return data; | |
| 72 end | |
| 73 | |
| 74 local function parse(mimetype, data, path) --> Stanza, error enum | |
| 63 mimetype = mimetype and mimetype:match("^[^; ]*"); | 75 mimetype = mimetype and mimetype:match("^[^; ]*"); |
| 64 if mimetype == "application/xmpp+xml" then | 76 if mimetype == "application/xmpp+xml" then |
| 65 return xml.parse(data); | 77 return xml.parse(data); |
| 66 elseif mimetype == "application/json" then | 78 elseif mimetype == "application/json" then |
| 67 local parsed, err = json.decode(data); | 79 local parsed, err = json.decode(data); |
| 68 if not parsed then | 80 if not parsed then |
| 69 return parsed, err; | 81 return parsed, err; |
| 70 end | 82 end |
| 83 if path and not amend_from_path(parsed, path) then return nil, "invalid-path"; end | |
| 71 return jsonmap.json2st(parsed); | 84 return jsonmap.json2st(parsed); |
| 72 elseif mimetype == "application/cbor" and have_cbor then | 85 elseif mimetype == "application/cbor" and have_cbor then |
| 73 local parsed, err = cbor.decode(data); | 86 local parsed, err = cbor.decode(data); |
| 74 if not parsed then | 87 if not parsed then |
| 75 return parsed, err; | 88 return parsed, err; |
| 81 return parse("text/plain", parsed); | 94 return parse("text/plain", parsed); |
| 82 end | 95 end |
| 83 for i = #parsed, 1, -1 do | 96 for i = #parsed, 1, -1 do |
| 84 parsed[i] = nil; | 97 parsed[i] = nil; |
| 85 end | 98 end |
| 99 if path and not amend_from_path(parsed, path) then return nil, "invalid-path"; end | |
| 86 return jsonmap.json2st(parsed); | 100 return jsonmap.json2st(parsed); |
| 87 elseif mimetype == "text/plain" then | 101 elseif mimetype == "text/plain" then |
| 88 return st.message({ type = "chat" }, data); | 102 if not path then |
| 103 return st.message({ type = "chat" }, data); | |
| 104 end | |
| 105 local parsed = {}; | |
| 106 if not amend_from_path(parsed, path) then return nil, "invalid-path"; end | |
| 107 if parsed.kind == "message" then | |
| 108 parsed.body = data; | |
| 109 elseif parsed.kind == "presence" then | |
| 110 parsed.show = data; | |
| 111 else | |
| 112 return nil, "invalid-path"; | |
| 113 end | |
| 114 return jsonmap.json2st(parsed); | |
| 89 end | 115 end |
| 90 return nil, "unknown-payload-type"; | 116 return nil, "unknown-payload-type"; |
| 91 end | 117 end |
| 92 | 118 |
| 93 local function decide_type(accept, supported_types) | 119 local function decide_type(accept, supported_types) |
| 160 iq_type = { code = 422, condition = "invalid-xml", text = "'iq' stanza must be of type 'get' or 'set'", }, | 186 iq_type = { code = 422, condition = "invalid-xml", text = "'iq' stanza must be of type 'get' or 'set'", }, |
| 161 iq_tags = { code = 422, condition = "bad-format", text = "'iq' stanza must have exactly one child tag", }, | 187 iq_tags = { code = 422, condition = "bad-format", text = "'iq' stanza must have exactly one child tag", }, |
| 162 mediatype = { code = 415, condition = "bad-format", text = "Unsupported media type" }, | 188 mediatype = { code = 415, condition = "bad-format", text = "Unsupported media type" }, |
| 163 }); | 189 }); |
| 164 | 190 |
| 165 local function handle_post(event) | 191 local function handle_post(event, path) |
| 166 local request, response = event.request, event.response; | 192 local request, response = event.request, event.response; |
| 167 local from; | 193 local from; |
| 168 local origin; | 194 local origin; |
| 169 | 195 |
| 170 if not request.headers.authorization then | 196 if not request.headers.authorization then |
| 175 if not origin then | 201 if not origin then |
| 176 return post_errors.new("unauthz"); | 202 return post_errors.new("unauthz"); |
| 177 end | 203 end |
| 178 from = jid.join(origin.username, origin.host, origin.resource); | 204 from = jid.join(origin.username, origin.host, origin.resource); |
| 179 end | 205 end |
| 180 local payload, err = parse(request.headers.content_type, request.body); | 206 local payload, err = parse(request.headers.content_type, request.body, path); |
| 181 if not payload then | 207 if not payload then |
| 182 -- parse fail | 208 -- parse fail |
| 183 local ctx = { error = err, type = request.headers.content_type, data = request.body, }; | 209 local ctx = { error = err, type = request.headers.content_type, data = request.body, }; |
| 184 if err == "unknown-payload-type" then | 210 if err == "unknown-payload-type" then |
| 185 return post_errors.new("mediatype", ctx); | 211 return post_errors.new("mediatype", ctx); |
| 265 -- Handle stanzas submitted via HTTP | 291 -- Handle stanzas submitted via HTTP |
| 266 module:depends("http"); | 292 module:depends("http"); |
| 267 module:provides("http", { | 293 module:provides("http", { |
| 268 route = { | 294 route = { |
| 269 POST = handle_post; | 295 POST = handle_post; |
| 296 ["POST /*"] = handle_post; | |
| 270 }; | 297 }; |
| 271 }); | 298 }); |
| 272 | 299 |
| 273 -- Forward stanzas from XMPP to HTTP and return any reply | 300 -- Forward stanzas from XMPP to HTTP and return any reply |
| 274 local rest_url = module:get_option_string("rest_callback_url", nil); | 301 local rest_url = module:get_option_string("rest_callback_url", nil); |
