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); |