Mercurial > prosody-modules
comparison mod_http_prebind/mod_http_prebind.lua @ 4229:3943032533a7
mod_http_prebind: New module
| author | Emmanuel Gil Peyrot <linkmauve@linkmauve.fr> |
|---|---|
| date | Sun, 25 Oct 2020 17:58:02 +0100 |
| parents | |
| children | 495a23d61418 |
comparison
equal
deleted
inserted
replaced
| 4228:3eb595cf847f | 4229:3943032533a7 |
|---|---|
| 1 module:depends("http"); | |
| 2 | |
| 3 local http = require "net.http"; | |
| 4 local format = require "util.format".format; | |
| 5 local json_encode = require "util.json".encode; | |
| 6 local promise = require "util.promise"; | |
| 7 local xml = require "util.xml"; | |
| 8 local t_insert = table.insert; | |
| 9 | |
| 10 local function new_options(host) | |
| 11 return { | |
| 12 headers = { | |
| 13 ["Content-Type"] = "text/xml; charset=utf-8", | |
| 14 ["Host"] = host, | |
| 15 }, | |
| 16 method = "POST", | |
| 17 }; | |
| 18 end | |
| 19 | |
| 20 local function connect_to_bosh(url, hostname) | |
| 21 local rid = math.random(100000, 100000000) | |
| 22 local options = new_options(hostname); | |
| 23 options.body = format([[<body content='text/xml; charset=utf-8' | |
| 24 hold='1' | |
| 25 rid='%d' | |
| 26 to='%s' | |
| 27 wait='60' | |
| 28 xml:lang='en' | |
| 29 xmpp:version='1.0' | |
| 30 xmlns='http://jabber.org/protocol/httpbind' | |
| 31 xmlns:xmpp='urn:xmpp:xbosh'/>]], rid, hostname); | |
| 32 local rid = rid + 1; | |
| 33 return promise.new(function (on_fulfilled, on_error) | |
| 34 assert(http.request(url, options, function (body, code) | |
| 35 if code ~= 200 then | |
| 36 on_error("Failed to fetch, HTTP error code "..code); | |
| 37 return; | |
| 38 end | |
| 39 local body = xml.parse(body); | |
| 40 local sid = body.attr.sid; | |
| 41 local mechanisms = {}; | |
| 42 for mechanism in body:get_child("features", "http://etherx.jabber.org/streams") | |
| 43 :get_child("mechanisms", "urn:ietf:params:xml:ns:xmpp-sasl") | |
| 44 :childtags("mechanism", "urn:ietf:params:xml:ns:xmpp-sasl") do | |
| 45 mechanisms[mechanism:get_text()] = true; | |
| 46 end | |
| 47 on_fulfilled({ url = url, sid = sid, rid = rid, mechanisms = mechanisms }); | |
| 48 end)); | |
| 49 end); | |
| 50 end | |
| 51 | |
| 52 local function authenticate(data) | |
| 53 local options = new_options(); | |
| 54 options.body = format([[<body sid='%s' | |
| 55 rid='%d' | |
| 56 xmlns='http://jabber.org/protocol/httpbind'> | |
| 57 <auth xmlns='urn:ietf:params:xml:ns:xmpp-sasl' | |
| 58 mechanism='ANONYMOUS'/> | |
| 59 </body>]], data.sid, data.rid); | |
| 60 data.rid = data.rid + 1; | |
| 61 return promise.new(function (on_fulfilled, on_error) | |
| 62 if data.mechanisms["ANONYMOUS"] == nil then | |
| 63 on_error("No SASL ANONYMOUS mechanism supported on this host."); | |
| 64 return; | |
| 65 end | |
| 66 assert(http.request(data.url, options, function (body, code) | |
| 67 if code ~= 200 then | |
| 68 on_error("Failed to fetch, HTTP error code "..code); | |
| 69 return; | |
| 70 end | |
| 71 local body = xml.parse(body); | |
| 72 local success = body:get_child("success", "urn:ietf:params:xml:ns:xmpp-sasl"); | |
| 73 if success then | |
| 74 data.mechanisms = nil; | |
| 75 on_fulfilled(data); | |
| 76 else | |
| 77 on_error("Authentication failed."); | |
| 78 end | |
| 79 end)); | |
| 80 end); | |
| 81 end; | |
| 82 | |
| 83 local function restart_stream(data) | |
| 84 local options = new_options(); | |
| 85 options.body = format([[ | |
| 86 <body sid='%s' | |
| 87 rid='%d' | |
| 88 xml:lang='en' | |
| 89 xmlns='http://jabber.org/protocol/httpbind' | |
| 90 xmlns:xmpp='urn:xmpp:xbosh' | |
| 91 xmpp:restart='true'/>]], data.sid, data.rid); | |
| 92 data.rid = data.rid + 1; | |
| 93 return promise.new(function (on_fulfilled, on_error) | |
| 94 assert(http.request(data.url, options, function (body, code) | |
| 95 if code ~= 200 then | |
| 96 on_error("Failed to fetch, HTTP error code "..code); | |
| 97 return; | |
| 98 end | |
| 99 local body = xml.parse(body); | |
| 100 on_fulfilled(data); | |
| 101 end)); | |
| 102 end); | |
| 103 end; | |
| 104 | |
| 105 local function bind(data) | |
| 106 local options = new_options(); | |
| 107 options.body = format([[ | |
| 108 <body sid='%s' | |
| 109 rid='%d' | |
| 110 xmlns='http://jabber.org/protocol/httpbind'> | |
| 111 <iq xmlns='jabber:client' | |
| 112 type='set'> | |
| 113 <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'/> | |
| 114 </iq> | |
| 115 </body>]], data.sid, data.rid); | |
| 116 data.rid = data.rid + 1; | |
| 117 return promise.new(function (on_fulfilled, on_error) | |
| 118 assert(http.request(data.url, options, function (body, code) | |
| 119 if code ~= 200 then | |
| 120 on_error("Failed to fetch, HTTP error code "..code); | |
| 121 return; | |
| 122 end | |
| 123 local body = xml.parse(body); | |
| 124 local jid = body:get_child("iq", "jabber:client") | |
| 125 :get_child("bind", "urn:ietf:params:xml:ns:xmpp-bind") | |
| 126 :get_child_text("jid", "urn:ietf:params:xml:ns:xmpp-bind"); | |
| 127 on_fulfilled(json_encode({rid = data.rid, sid = data.sid, jid = jid})); | |
| 128 end)); | |
| 129 end); | |
| 130 end; | |
| 131 | |
| 132 module:provides("http", { | |
| 133 route = { | |
| 134 ["GET"] = function (event) | |
| 135 return connect_to_bosh("http://[::1]:5280/http-bind", "anon.localhost") | |
| 136 :next(authenticate) | |
| 137 :next(restart_stream) | |
| 138 :next(bind); | |
| 139 end; | |
| 140 }; | |
| 141 }); |
