Comparison

spec/mod_bosh_spec.lua @ 9374:a1a39d395260

mod_bosh: Add tests (run with 'busted -r bosh')
author Matthew Wild <mwild1@gmail.com>
date Sun, 23 Sep 2018 17:12:21 +0100
comparison
equal deleted inserted replaced
9373:1a69803d5d5d 9374:a1a39d395260
1
2 -- Requires a host 'localhost' with SASL ANONYMOUS
3
4 local bosh_url = "http://localhost:5280/http-bind"
5
6 local logger = require "util.logger";
7
8 local debug = false;
9
10 local print = print;
11 if debug then
12 logger.add_simple_sink(print, {
13 --"debug";
14 "info";
15 "warn";
16 "error";
17 });
18 else
19 print = function () end
20 end
21
22 describe("#mod_bosh", function ()
23 local server = require "net.server_select";
24 package.loaded["net.server"] = server;
25 local async = require "util.async";
26 local timer = require "util.timer";
27 local http = require "net.http".new({ suppress_errors = false });
28
29 local function sleep(n)
30 local wait, done = async.waiter();
31 timer.add_task(n, function () done() end);
32 wait();
33 end
34
35 local st = require "util.stanza";
36 local xml = require "util.xml";
37
38 local function request(url, opt, cb, auto_wait)
39 local wait, done = async.waiter();
40 local ok, err;
41 http:request(url, opt, function (...)
42 ok, err = pcall(cb, ...);
43 if not ok then print("CAUGHT", err) end
44 done();
45 end);
46 local function err_wait(throw)
47 wait();
48 if throw ~= false and not ok then
49 error(err);
50 end
51 return ok, err;
52 end
53 if auto_wait == false then
54 return err_wait;
55 else
56 err_wait();
57 end
58 end
59
60 local function run_async(f)
61 local err;
62 local r = async.runner();
63 r:onerror(function (_, err_)
64 print("EER", err_)
65 err = err_;
66 server.setquitting("once");
67 end)
68 :onwaiting(function ()
69 --server.loop();
70 end)
71 :run(function ()
72 f()
73 server.setquitting("once");
74 end);
75 server.loop();
76 if err then
77 error(err);
78 end
79 if r.state ~= "ready" then
80 error("Runner in unexpected state: "..r.state);
81 end
82 end
83
84 it("test endpoint should be reachable", function ()
85 -- This is partly just to ensure the other tests have a chance to succeed
86 -- (i.e. the BOSH endpoint is up and functioning)
87 local function test()
88 request(bosh_url, nil, function (resp, code)
89 if code ~= 200 then
90 error("Unable to reach BOSH endpoint "..bosh_url);
91 end
92 assert.is_string(resp);
93 end);
94 end
95 run_async(test);
96 end);
97
98 it("should respond to past rids with past responses", function ()
99 local resp_1000_1, resp_1000_2 = "1", "2";
100
101 local function test_bosh()
102 local sid;
103
104 -- Set up BOSH session
105 request(bosh_url, {
106 body = tostring(st.stanza("body", {
107 to = "localhost";
108 from = "test@localhost";
109 content = "text/xml; charset=utf-8";
110 hold = "1";
111 rid = "998";
112 wait = "10";
113 ["xml:lang"] = "en";
114 ["xmpp:version"] = "1.0";
115 xmlns = "http://jabber.org/protocol/httpbind";
116 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
117 })
118 :tag("auth", { xmlns = "urn:ietf:params:xml:ns:xmpp-sasl", mechanism = "ANONYMOUS" }):up()
119 :tag("iq", { xmlns = "jabber:client", type = "set", id = "bind1" })
120 :tag("bind", { xmlns = "urn:ietf:params:xml:ns:xmpp-bind" })
121 :tag("resource"):text("bosh-test1"):up()
122 :up()
123 :up()
124 );
125 }, function (response_body)
126 local resp = xml.parse(response_body);
127 if not response_body:find("<jid>", 1, true) then
128 print("ERR", resp:pretty_print());
129 error("Failed to set up BOSH session");
130 end
131 sid = assert(resp.attr.sid);
132 print("SID", sid);
133 end);
134
135 -- Receive some additional post-login stuff
136 request(bosh_url, {
137 body = tostring(st.stanza("body", {
138 sid = sid;
139 rid = "999";
140 content = "text/xml; charset=utf-8";
141 ["xml:lang"] = "en";
142 xmlns = "http://jabber.org/protocol/httpbind";
143 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
144 })
145 )
146 }, function (response_body)
147 local resp = xml.parse(response_body);
148 print("RESP 999", resp:pretty_print());
149 end);
150
151 -- Send first long poll
152 print "SEND 1000#1"
153 local wait1000 = request(bosh_url, {
154 body = tostring(st.stanza("body", {
155 sid = sid;
156 rid = "1000";
157 content = "text/xml; charset=utf-8";
158 ["xml:lang"] = "en";
159 xmlns = "http://jabber.org/protocol/httpbind";
160 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
161 }))
162 }, function (response_body)
163 local resp = xml.parse(response_body);
164 resp_1000_1 = resp;
165 print("RESP 1000#1", resp:pretty_print());
166 end, false);
167
168 -- Wait a couple of seconds
169 sleep(2)
170
171 -- Send an early request, causing rid 1000 to return early
172 print "SEND 1001"
173 local wait1001 = request(bosh_url, {
174 body = tostring(st.stanza("body", {
175 sid = sid;
176 rid = "1001";
177 content = "text/xml; charset=utf-8";
178 ["xml:lang"] = "en";
179 xmlns = "http://jabber.org/protocol/httpbind";
180 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
181 }))
182 }, function (response_body)
183 local resp = xml.parse(response_body);
184 print("RESP 1001", resp:pretty_print());
185 end, false);
186 -- Ensure we've received the response for rid 1000
187 wait1000();
188
189 -- Sleep a couple of seconds
190 print "...pause..."
191 sleep(2);
192
193 -- Re-send rid 1000, we should get the same response
194 print "SEND 1000#2"
195 request(bosh_url, {
196 body = tostring(st.stanza("body", {
197 sid = sid;
198 rid = "1000";
199 content = "text/xml; charset=utf-8";
200 ["xml:lang"] = "en";
201 xmlns = "http://jabber.org/protocol/httpbind";
202 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
203 }))
204 }, function (response_body)
205 local resp = xml.parse(response_body);
206 resp_1000_2 = resp;
207 print("RESP 1000#2", resp:pretty_print());
208 end);
209
210 local wait_final = request(bosh_url, {
211 body = tostring(st.stanza("body", {
212 sid = sid;
213 rid = "1002";
214 type = "terminate";
215 content = "text/xml; charset=utf-8";
216 ["xml:lang"] = "en";
217 xmlns = "http://jabber.org/protocol/httpbind";
218 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
219 }))
220 }, function ()
221 end, false);
222
223 print "WAIT 1001"
224 wait1001();
225 wait_final();
226 print "DONE ALL"
227 end
228 run_async(test_bosh);
229 assert.truthy(resp_1000_1);
230 assert.same(resp_1000_1, resp_1000_2);
231 end);
232
233 it("should handle out-of-order requests", function ()
234 local function test()
235 local sid;
236 -- Set up BOSH session
237 local wait, done = async.waiter();
238 http:request(bosh_url, {
239 body = tostring(st.stanza("body", {
240 to = "localhost";
241 from = "test@localhost";
242 content = "text/xml; charset=utf-8";
243 hold = "1";
244 rid = "1";
245 wait = "10";
246 ["xml:lang"] = "en";
247 ["xmpp:version"] = "1.0";
248 xmlns = "http://jabber.org/protocol/httpbind";
249 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
250 }));
251 }, function (response_body)
252 local resp = xml.parse(response_body);
253 sid = assert(resp.attr.sid, "Failed to set up BOSH session");
254 print("SID", sid);
255 done();
256 end);
257 print "WAIT 1"
258 wait();
259 print "DONE 1"
260
261 local rid2_response_received = false;
262
263 -- Temporarily skip rid 2, to simulate missed request
264 local wait3, done3 = async.waiter();
265 http:request(bosh_url, {
266 body = tostring(st.stanza("body", {
267 sid = sid;
268 rid = "3";
269 content = "text/xml; charset=utf-8";
270 ["xml:lang"] = "en";
271 xmlns = "http://jabber.org/protocol/httpbind";
272 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
273 }):tag("iq", { xmlns = "jabber:client", type = "set", id = "bind" })
274 :tag("bind", { xmlns = "urn:ietf:params:xml:ns:xmpp-bind" }):up()
275 :up()
276 )
277 }, function (response_body)
278 local resp = xml.parse(response_body);
279 print("RESP 3", resp:pretty_print());
280 done3();
281 -- The server should not respond to this request until
282 -- it has responded to rid 2
283 assert.is_true(rid2_response_received);
284 end);
285
286 print "SLEEPING"
287 sleep(2);
288 print "SLEPT"
289
290 -- Send the "missed" rid 2
291 local wait2, done2 = async.waiter();
292 http:request(bosh_url, {
293 body = tostring(st.stanza("body", {
294 sid = sid;
295 rid = "2";
296 content = "text/xml; charset=utf-8";
297 ["xml:lang"] = "en";
298 xmlns = "http://jabber.org/protocol/httpbind";
299 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
300 }):tag("auth", { xmlns = "urn:ietf:params:xml:ns:xmpp-sasl", mechanism = "ANONYMOUS" }):up()
301 )
302 }, function (response_body)
303 local resp = xml.parse(response_body);
304 print("RESP 2", resp:pretty_print());
305 rid2_response_received = true;
306 done2();
307 end);
308 print "WAIT 2"
309 wait2();
310 print "WAIT 3"
311 wait3();
312 print "QUIT"
313 end
314 run_async(test);
315 end);
316
317 it("should work", function ()
318 local function test()
319 local sid;
320 -- Set up BOSH session
321 local wait, done = async.waiter();
322 http:request(bosh_url, {
323 body = tostring(st.stanza("body", {
324 to = "localhost";
325 from = "test@localhost";
326 content = "text/xml; charset=utf-8";
327 hold = "1";
328 rid = "1";
329 wait = "10";
330 ["xml:lang"] = "en";
331 ["xmpp:version"] = "1.0";
332 xmlns = "http://jabber.org/protocol/httpbind";
333 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
334 }));
335 }, function (response_body)
336 local resp = xml.parse(response_body);
337 sid = assert(resp.attr.sid, "Failed to set up BOSH session");
338 print("SID", sid);
339 done();
340 end);
341 print "WAIT 1"
342 wait();
343 print "DONE 1"
344
345 local rid2_response_received = false;
346
347 -- Send the "missed" rid 2
348 local wait2, done2 = async.waiter();
349 http:request(bosh_url, {
350 body = tostring(st.stanza("body", {
351 sid = sid;
352 rid = "2";
353 content = "text/xml; charset=utf-8";
354 ["xml:lang"] = "en";
355 xmlns = "http://jabber.org/protocol/httpbind";
356 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
357 }):tag("auth", { xmlns = "urn:ietf:params:xml:ns:xmpp-sasl", mechanism = "ANONYMOUS" }):up()
358 )
359 }, function (response_body)
360 local resp = xml.parse(response_body);
361 print("RESP 2", resp:pretty_print());
362 rid2_response_received = true;
363 done2();
364 end);
365
366 local wait3, done3 = async.waiter();
367 http:request(bosh_url, {
368 body = tostring(st.stanza("body", {
369 sid = sid;
370 rid = "3";
371 content = "text/xml; charset=utf-8";
372 ["xml:lang"] = "en";
373 xmlns = "http://jabber.org/protocol/httpbind";
374 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
375 }):tag("iq", { xmlns = "jabber:client", type = "set", id = "bind" })
376 :tag("bind", { xmlns = "urn:ietf:params:xml:ns:xmpp-bind" }):up()
377 :up()
378 )
379 }, function (response_body)
380 local resp = xml.parse(response_body);
381 print("RESP 3", resp:pretty_print());
382 done3();
383 -- The server should not respond to this request until
384 -- it has responded to rid 2
385 assert.is_true(rid2_response_received);
386 end);
387
388 print "SLEEPING"
389 sleep(2);
390 print "SLEPT"
391
392 print "WAIT 2"
393 wait2();
394 print "WAIT 3"
395 wait3();
396 print "QUIT"
397 end
398 run_async(test);
399 end);
400
401 it("should handle aborted pending requests", function ()
402 local resp_1000_1, resp_1000_2 = "1", "2";
403
404 local function test_bosh()
405 local sid;
406
407 -- Set up BOSH session
408 request(bosh_url, {
409 body = tostring(st.stanza("body", {
410 to = "localhost";
411 from = "test@localhost";
412 content = "text/xml; charset=utf-8";
413 hold = "1";
414 rid = "998";
415 wait = "10";
416 ["xml:lang"] = "en";
417 ["xmpp:version"] = "1.0";
418 xmlns = "http://jabber.org/protocol/httpbind";
419 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
420 })
421 :tag("auth", { xmlns = "urn:ietf:params:xml:ns:xmpp-sasl", mechanism = "ANONYMOUS" }):up()
422 :tag("iq", { xmlns = "jabber:client", type = "set", id = "bind1" })
423 :tag("bind", { xmlns = "urn:ietf:params:xml:ns:xmpp-bind" })
424 :tag("resource"):text("bosh-test1"):up()
425 :up()
426 :up()
427 );
428 }, function (response_body)
429 local resp = xml.parse(response_body);
430 if not response_body:find("<jid>", 1, true) then
431 print("ERR", resp:pretty_print());
432 error("Failed to set up BOSH session");
433 end
434 sid = assert(resp.attr.sid);
435 print("SID", sid);
436 end);
437
438 -- Receive some additional post-login stuff
439 request(bosh_url, {
440 body = tostring(st.stanza("body", {
441 sid = sid;
442 rid = "999";
443 content = "text/xml; charset=utf-8";
444 ["xml:lang"] = "en";
445 xmlns = "http://jabber.org/protocol/httpbind";
446 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
447 })
448 )
449 }, function (response_body)
450 local resp = xml.parse(response_body);
451 print("RESP 999", resp:pretty_print());
452 end);
453
454 -- Send first long poll
455 print "SEND 1000#1"
456 local wait1000_1 = request(bosh_url, {
457 body = tostring(st.stanza("body", {
458 sid = sid;
459 rid = "1000";
460 content = "text/xml; charset=utf-8";
461 ["xml:lang"] = "en";
462 xmlns = "http://jabber.org/protocol/httpbind";
463 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
464 }))
465 }, function (response_body)
466 local resp = xml.parse(response_body);
467 resp_1000_1 = resp;
468 assert.is_nil(resp.attr.type);
469 print("RESP 1000#1", resp:pretty_print());
470 end, false);
471
472 -- Wait a couple of seconds
473 sleep(2)
474
475 -- Re-send rid 1000, we should eventually get a normal response (with no stanzas)
476 print "SEND 1000#2"
477 request(bosh_url, {
478 body = tostring(st.stanza("body", {
479 sid = sid;
480 rid = "1000";
481 content = "text/xml; charset=utf-8";
482 ["xml:lang"] = "en";
483 xmlns = "http://jabber.org/protocol/httpbind";
484 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
485 }))
486 }, function (response_body)
487 local resp = xml.parse(response_body);
488 resp_1000_2 = resp;
489 assert.is_nil(resp.attr.type);
490 print("RESP 1000#2", resp:pretty_print());
491 end);
492
493 wait1000_1();
494 print "DONE ALL"
495 end
496 run_async(test_bosh);
497 assert.truthy(resp_1000_1);
498 assert.same(resp_1000_1, resp_1000_2);
499 end);
500
501 it("should fail on requests beyond rid window", function ()
502 local function test_bosh()
503 local sid;
504
505 -- Set up BOSH session
506 request(bosh_url, {
507 body = tostring(st.stanza("body", {
508 to = "localhost";
509 from = "test@localhost";
510 content = "text/xml; charset=utf-8";
511 hold = "1";
512 rid = "998";
513 wait = "10";
514 ["xml:lang"] = "en";
515 ["xmpp:version"] = "1.0";
516 xmlns = "http://jabber.org/protocol/httpbind";
517 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
518 })
519 :tag("auth", { xmlns = "urn:ietf:params:xml:ns:xmpp-sasl", mechanism = "ANONYMOUS" }):up()
520 :tag("iq", { xmlns = "jabber:client", type = "set", id = "bind1" })
521 :tag("bind", { xmlns = "urn:ietf:params:xml:ns:xmpp-bind" })
522 :tag("resource"):text("bosh-test1"):up()
523 :up()
524 :up()
525 );
526 }, function (response_body)
527 local resp = xml.parse(response_body);
528 if not response_body:find("<jid>", 1, true) then
529 print("ERR", resp:pretty_print());
530 error("Failed to set up BOSH session");
531 end
532 sid = assert(resp.attr.sid);
533 print("SID", sid);
534 end);
535
536 -- Receive some additional post-login stuff
537 request(bosh_url, {
538 body = tostring(st.stanza("body", {
539 sid = sid;
540 rid = "999";
541 content = "text/xml; charset=utf-8";
542 ["xml:lang"] = "en";
543 xmlns = "http://jabber.org/protocol/httpbind";
544 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
545 })
546 )
547 }, function (response_body)
548 local resp = xml.parse(response_body);
549 print("RESP 999", resp:pretty_print());
550 end);
551
552 -- Send poll with a rid that's too high (current + 2, where only current + 1 is allowed)
553 print "SEND 1002(!)"
554 request(bosh_url, {
555 body = tostring(st.stanza("body", {
556 sid = sid;
557 rid = "1002";
558 content = "text/xml; charset=utf-8";
559 ["xml:lang"] = "en";
560 xmlns = "http://jabber.org/protocol/httpbind";
561 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
562 }))
563 }, function (response_body)
564 local resp = xml.parse(response_body);
565 assert.equal("terminate", resp.attr.type);
566 print("RESP 1002(!)", resp:pretty_print());
567 end);
568
569 print "DONE ALL"
570 end
571 run_async(test_bosh);
572 end);
573
574 it("should always succeed for requests within the rid window", function ()
575 local function test()
576 local sid;
577 -- Set up BOSH session
578 request(bosh_url, {
579 body = tostring(st.stanza("body", {
580 to = "localhost";
581 from = "test@localhost";
582 content = "text/xml; charset=utf-8";
583 hold = "1";
584 rid = "1";
585 wait = "10";
586 ["xml:lang"] = "en";
587 ["xmpp:version"] = "1.0";
588 xmlns = "http://jabber.org/protocol/httpbind";
589 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
590 }));
591 }, function (response_body)
592 local resp = xml.parse(response_body);
593 sid = assert(resp.attr.sid, "Failed to set up BOSH session");
594 print("SID", sid);
595 end);
596 print "DONE 1"
597
598 request(bosh_url, {
599 body = tostring(st.stanza("body", {
600 sid = sid;
601 rid = "2";
602 content = "text/xml; charset=utf-8";
603 ["xml:lang"] = "en";
604 xmlns = "http://jabber.org/protocol/httpbind";
605 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
606 }):tag("auth", { xmlns = "urn:ietf:params:xml:ns:xmpp-sasl", mechanism = "ANONYMOUS" }):up()
607 )
608 }, function (response_body)
609 local resp = xml.parse(response_body);
610 print("RESP 2", resp:pretty_print());
611 end);
612
613 local resp3;
614 request(bosh_url, {
615 body = tostring(st.stanza("body", {
616 sid = sid;
617 rid = "3";
618 content = "text/xml; charset=utf-8";
619 ["xml:lang"] = "en";
620 xmlns = "http://jabber.org/protocol/httpbind";
621 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
622 }):tag("iq", { xmlns = "jabber:client", type = "set", id = "bind" })
623 :tag("bind", { xmlns = "urn:ietf:params:xml:ns:xmpp-bind" }):up()
624 :up()
625 )
626 }, function (response_body)
627 local resp = xml.parse(response_body);
628 print("RESP 3#1", resp:pretty_print());
629 resp3 = resp;
630 end);
631
632
633 request(bosh_url, {
634 body = tostring(st.stanza("body", {
635 sid = sid;
636 rid = "4";
637 content = "text/xml; charset=utf-8";
638 ["xml:lang"] = "en";
639 xmlns = "http://jabber.org/protocol/httpbind";
640 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
641 }):tag("iq", { xmlns = "jabber:client", type = "get", id = "ping1" })
642 :tag("ping", { xmlns = "urn:xmpp:ping" }):up()
643 :up()
644 )
645 }, function (response_body)
646 local resp = xml.parse(response_body);
647 print("RESP 4", resp:pretty_print());
648 end);
649
650 request(bosh_url, {
651 body = tostring(st.stanza("body", {
652 sid = sid;
653 rid = "3";
654 content = "text/xml; charset=utf-8";
655 ["xml:lang"] = "en";
656 xmlns = "http://jabber.org/protocol/httpbind";
657 ["xmlns:xmpp"] = "urn:xmpp:xbosh";
658 }):tag("iq", { xmlns = "jabber:client", type = "set", id = "bind" })
659 :tag("bind", { xmlns = "urn:ietf:params:xml:ns:xmpp-bind" }):up()
660 :up()
661 )
662 }, function (response_body)
663 local resp = xml.parse(response_body);
664 print("RESP 3#2", resp:pretty_print());
665 assert.not_equal("terminate", resp.attr.type);
666 assert.same(resp3, resp);
667 end);
668
669
670 print "QUIT"
671 end
672 run_async(test);
673 end);
674 end);