message_bus 3.3.6 → 3.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +10 -2
  3. data/.github/workflows/ci.yml +36 -19
  4. data/CHANGELOG +15 -8
  5. data/DEV.md +0 -2
  6. data/Gemfile +0 -27
  7. data/LICENSE +1 -1
  8. data/README.md +10 -10
  9. data/Rakefile +12 -7
  10. data/assets/message-bus-ajax.js +3 -3
  11. data/bench/codecs/marshal.rb +1 -1
  12. data/bench/codecs/packed_string.rb +1 -1
  13. data/examples/bench/bench.lua +2 -2
  14. data/lib/message_bus/backends/base.rb +3 -3
  15. data/lib/message_bus/backends/postgres.rb +7 -3
  16. data/lib/message_bus/backends/redis.rb +1 -1
  17. data/lib/message_bus/client.rb +3 -7
  18. data/lib/message_bus/connection_manager.rb +1 -1
  19. data/lib/message_bus/distributed_cache.rb +2 -1
  20. data/lib/message_bus/http_client.rb +2 -2
  21. data/lib/message_bus/rack/diagnostics.rb +30 -8
  22. data/lib/message_bus/rack/middleware.rb +6 -0
  23. data/lib/message_bus/rack/thin_ext.rb +1 -1
  24. data/lib/message_bus/version.rb +1 -1
  25. data/lib/message_bus.rb +6 -6
  26. data/message_bus.gemspec +20 -5
  27. data/package-lock.json +1575 -23
  28. data/package.json +7 -2
  29. data/spec/assets/SpecHelper.js +6 -5
  30. data/spec/assets/message-bus.spec.js +9 -6
  31. data/spec/helpers.rb +17 -6
  32. data/spec/integration/http_client_spec.rb +1 -1
  33. data/spec/lib/message_bus/backend_spec.rb +12 -44
  34. data/spec/lib/message_bus/client_spec.rb +6 -6
  35. data/spec/lib/message_bus/distributed_cache_spec.rb +5 -7
  36. data/spec/lib/message_bus/multi_process_spec.rb +1 -1
  37. data/spec/lib/message_bus/rack/middleware_spec.rb +16 -5
  38. data/spec/lib/message_bus_spec.rb +10 -6
  39. data/spec/spec_helper.rb +8 -9
  40. data/spec/support/jasmine-browser.json +16 -0
  41. metadata +220 -14
  42. data/spec/assets/support/jasmine.yml +0 -126
  43. data/spec/assets/support/jasmine_helper.rb +0 -11
  44. data/vendor/assets/javascripts/message-bus-ajax.js +0 -38
  45. data/vendor/assets/javascripts/message-bus.js +0 -549
@@ -1,549 +0,0 @@
1
- /*global define, jQuery*/
2
-
3
- (function (root, factory) {
4
- if (typeof define === "function" && define.amd) {
5
- // AMD. Register as an anonymous module.
6
- define([], function () {
7
- // Also create a global in case some scripts
8
- // that are loaded still are looking for
9
- // a global even when an AMD loader is in use.
10
- return (root.MessageBus = factory());
11
- });
12
- } else {
13
- // Browser globals
14
- root.MessageBus = factory();
15
- }
16
- })(typeof self !== "undefined" ? self : this, function () {
17
- "use strict";
18
-
19
- // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
20
- var uniqueId = function () {
21
- return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
22
- var r = (Math.random() * 16) | 0;
23
- var v = c === "x" ? r : (r & 0x3) | 0x8;
24
- return v.toString(16);
25
- });
26
- };
27
-
28
- var me;
29
- var delayPollTimeout;
30
- var ajaxInProgress = false;
31
- var started = false;
32
- var clientId = uniqueId();
33
- var callbacks = [];
34
- var failCount = 0;
35
- var baseUrl = "/";
36
- var paused = false;
37
- var later = [];
38
- var chunkedBackoff = 0;
39
- var stopped;
40
- var pollTimeout = null;
41
- var totalAjaxFailures = 0;
42
- var totalAjaxCalls = 0;
43
- var lastAjax;
44
-
45
- var isHidden = (function () {
46
- var prefixes = ["", "webkit", "ms", "moz"];
47
- var hiddenProperty;
48
- for (var i = 0; i < prefixes.length; i++) {
49
- var prefix = prefixes[i];
50
- var check = prefix + (prefix === "" ? "hidden" : "Hidden");
51
- if (document[check] !== undefined) {
52
- hiddenProperty = check;
53
- }
54
- }
55
-
56
- return function () {
57
- if (hiddenProperty !== undefined) {
58
- return document[hiddenProperty];
59
- } else {
60
- return !document.hasFocus;
61
- }
62
- };
63
- })();
64
-
65
- var hasLocalStorage = (function () {
66
- try {
67
- localStorage.setItem("mbTestLocalStorage", Date.now());
68
- localStorage.removeItem("mbTestLocalStorage");
69
- return true;
70
- } catch (e) {
71
- return false;
72
- }
73
- })();
74
-
75
- var updateLastAjax = function () {
76
- if (hasLocalStorage) {
77
- localStorage.setItem("__mbLastAjax", Date.now());
78
- }
79
- };
80
-
81
- var hiddenTabShouldWait = function () {
82
- if (hasLocalStorage && isHidden()) {
83
- var lastAjaxCall = parseInt(localStorage.getItem("__mbLastAjax"), 10);
84
- var deltaAjax = Date.now() - lastAjaxCall;
85
-
86
- return deltaAjax >= 0 && deltaAjax < me.minHiddenPollInterval;
87
- }
88
- return false;
89
- };
90
-
91
- var hasonprogress = new XMLHttpRequest().onprogress === null;
92
- var allowChunked = function () {
93
- return me.enableChunkedEncoding && hasonprogress;
94
- };
95
-
96
- var shouldLongPoll = function () {
97
- return (
98
- me.alwaysLongPoll ||
99
- (me.shouldLongPollCallback ? me.shouldLongPollCallback() : !isHidden())
100
- );
101
- };
102
-
103
- var processMessages = function (messages) {
104
- if (!messages || messages.length === 0) {
105
- return false;
106
- }
107
-
108
- for (var i = 0; i < messages.length; i++) {
109
- var message = messages[i];
110
- for (var j = 0; j < callbacks.length; j++) {
111
- var callback = callbacks[j];
112
- if (callback.channel === message.channel) {
113
- callback.last_id = message.message_id;
114
- try {
115
- callback.func(message.data, message.global_id, message.message_id);
116
- } catch (e) {
117
- if (console.log) {
118
- console.log(
119
- "MESSAGE BUS FAIL: callback " +
120
- callback.channel +
121
- " caused exception " +
122
- e.stack
123
- );
124
- }
125
- }
126
- }
127
- if (message.channel === "/__status") {
128
- if (message.data[callback.channel] !== undefined) {
129
- callback.last_id = message.data[callback.channel];
130
- }
131
- }
132
- }
133
- }
134
-
135
- return true;
136
- };
137
-
138
- var reqSuccess = function (messages) {
139
- failCount = 0;
140
- if (paused) {
141
- if (messages) {
142
- for (var i = 0; i < messages.length; i++) {
143
- later.push(messages[i]);
144
- }
145
- }
146
- } else {
147
- return processMessages(messages);
148
- }
149
- return false;
150
- };
151
-
152
- var longPoller = function (poll, data) {
153
- if (ajaxInProgress) {
154
- // never allow concurrent ajax reqs
155
- return;
156
- }
157
-
158
- var gotData = false;
159
- var aborted = false;
160
- var rateLimited = false;
161
- var rateLimitedSeconds;
162
-
163
- lastAjax = new Date();
164
- totalAjaxCalls += 1;
165
- data.__seq = totalAjaxCalls;
166
-
167
- var longPoll = shouldLongPoll() && me.enableLongPolling;
168
- var chunked = longPoll && allowChunked();
169
- if (chunkedBackoff > 0) {
170
- chunkedBackoff--;
171
- chunked = false;
172
- }
173
-
174
- var headers = { "X-SILENCE-LOGGER": "true" };
175
- for (var name in me.headers) {
176
- headers[name] = me.headers[name];
177
- }
178
-
179
- if (!chunked) {
180
- headers["Dont-Chunk"] = "true";
181
- }
182
-
183
- var dataType = chunked ? "text" : "json";
184
-
185
- var handle_progress = function (payload, position) {
186
- var separator = "\r\n|\r\n";
187
- var endChunk = payload.indexOf(separator, position);
188
-
189
- if (endChunk === -1) {
190
- return position;
191
- }
192
-
193
- var chunk = payload.substring(position, endChunk);
194
- chunk = chunk.replace(/\r\n\|\|\r\n/g, separator);
195
-
196
- try {
197
- reqSuccess(JSON.parse(chunk));
198
- } catch (e) {
199
- if (console.log) {
200
- console.log("FAILED TO PARSE CHUNKED REPLY");
201
- console.log(data);
202
- }
203
- }
204
-
205
- return handle_progress(payload, endChunk + separator.length);
206
- };
207
-
208
- var disableChunked = function () {
209
- if (me.longPoll) {
210
- me.longPoll.abort();
211
- chunkedBackoff = 30;
212
- }
213
- };
214
-
215
- if (!me.ajax) {
216
- throw new Error("Either jQuery or the ajax adapter must be loaded");
217
- }
218
-
219
- updateLastAjax();
220
-
221
- ajaxInProgress = true;
222
- var req = me.ajax({
223
- url:
224
- me.baseUrl +
225
- "message-bus/" +
226
- me.clientId +
227
- "/poll" +
228
- (!longPoll ? "?dlp=t" : ""),
229
- data: data,
230
- async: true,
231
- dataType: dataType,
232
- type: "POST",
233
- headers: headers,
234
- messageBus: {
235
- chunked: chunked,
236
- onProgressListener: function (xhr) {
237
- var position = 0;
238
- // if it takes longer than 3000 ms to get first chunk, we have some proxy
239
- // this is messing with us, so just backoff from using chunked for now
240
- var chunkedTimeout = setTimeout(disableChunked, 3000);
241
- return (xhr.onprogress = function () {
242
- clearTimeout(chunkedTimeout);
243
- if (
244
- xhr.getResponseHeader("Content-Type") ===
245
- "application/json; charset=utf-8"
246
- ) {
247
- chunked = false; // not chunked, we are sending json back
248
- } else {
249
- position = handle_progress(xhr.responseText, position);
250
- }
251
- });
252
- },
253
- },
254
- xhr: function () {
255
- var xhr = jQuery.ajaxSettings.xhr();
256
- if (!chunked) {
257
- return xhr;
258
- }
259
- this.messageBus.onProgressListener(xhr);
260
- return xhr;
261
- },
262
- success: function (messages) {
263
- if (!chunked) {
264
- // we may have requested text so jQuery will not parse
265
- if (typeof messages === "string") {
266
- messages = JSON.parse(messages);
267
- }
268
- gotData = reqSuccess(messages);
269
- }
270
- },
271
- error: function (xhr, textStatus) {
272
- if (xhr.status === 429) {
273
- var tryAfter =
274
- parseInt(
275
- xhr.getResponseHeader && xhr.getResponseHeader("Retry-After")
276
- ) || 0;
277
- tryAfter = tryAfter || 0;
278
- if (tryAfter < 15) {
279
- tryAfter = 15;
280
- }
281
- rateLimitedSeconds = tryAfter;
282
- rateLimited = true;
283
- } else if (textStatus === "abort") {
284
- aborted = true;
285
- } else {
286
- failCount += 1;
287
- totalAjaxFailures += 1;
288
- }
289
- },
290
- complete: function () {
291
- ajaxInProgress = false;
292
-
293
- var interval;
294
- try {
295
- if (rateLimited) {
296
- interval = Math.max(me.minPollInterval, rateLimitedSeconds * 1000);
297
- } else if (gotData || aborted) {
298
- interval = me.minPollInterval;
299
- } else {
300
- interval = me.callbackInterval;
301
- if (failCount > 2) {
302
- interval = interval * failCount;
303
- } else if (!shouldLongPoll()) {
304
- interval = me.backgroundCallbackInterval;
305
- }
306
- if (interval > me.maxPollInterval) {
307
- interval = me.maxPollInterval;
308
- }
309
-
310
- interval -= new Date() - lastAjax;
311
-
312
- if (interval < 100) {
313
- interval = 100;
314
- }
315
- }
316
- } catch (e) {
317
- if (console.log && e.message) {
318
- console.log("MESSAGE BUS FAIL: " + e.message);
319
- }
320
- }
321
-
322
- if (pollTimeout) {
323
- clearTimeout(pollTimeout);
324
- pollTimeout = null;
325
- }
326
-
327
- if (started) {
328
- pollTimeout = setTimeout(function () {
329
- pollTimeout = null;
330
- poll();
331
- }, interval);
332
- }
333
-
334
- me.longPoll = null;
335
- },
336
- });
337
-
338
- return req;
339
- };
340
-
341
- me = {
342
- /* shared between all tabs */
343
- minHiddenPollInterval: 1500,
344
- enableChunkedEncoding: true,
345
- enableLongPolling: true,
346
- callbackInterval: 15000,
347
- backgroundCallbackInterval: 60000,
348
- minPollInterval: 100,
349
- maxPollInterval: 3 * 60 * 1000,
350
- callbacks: callbacks,
351
- clientId: clientId,
352
- alwaysLongPoll: false,
353
- shouldLongPollCallback: undefined,
354
- baseUrl: baseUrl,
355
- headers: {},
356
- ajax: typeof jQuery !== "undefined" && jQuery.ajax,
357
- diagnostics: function () {
358
- console.log("Stopped: " + stopped + " Started: " + started);
359
- console.log("Current callbacks");
360
- console.log(callbacks);
361
- console.log(
362
- "Total ajax calls: " +
363
- totalAjaxCalls +
364
- " Recent failure count: " +
365
- failCount +
366
- " Total failures: " +
367
- totalAjaxFailures
368
- );
369
- console.log(
370
- "Last ajax call: " + (new Date() - lastAjax) / 1000 + " seconds ago"
371
- );
372
- },
373
-
374
- pause: function () {
375
- paused = true;
376
- },
377
-
378
- resume: function () {
379
- paused = false;
380
- processMessages(later);
381
- later = [];
382
- },
383
-
384
- stop: function () {
385
- stopped = true;
386
- started = false;
387
- if (delayPollTimeout) {
388
- clearTimeout(delayPollTimeout);
389
- delayPollTimeout = null;
390
- }
391
- if (pollTimeout) {
392
- clearTimeout(pollTimeout);
393
- pollTimeout = null;
394
- }
395
- if (me.longPoll) {
396
- me.longPoll.abort();
397
- }
398
- if (me.onVisibilityChange) {
399
- document.removeEventListener("visibilitychange", me.onVisibilityChange);
400
- me.onVisibilityChange = null;
401
- }
402
- },
403
-
404
- // Start polling
405
- start: function () {
406
- if (started) return;
407
- started = true;
408
- stopped = false;
409
-
410
- var poll = function () {
411
- if (stopped) {
412
- return;
413
- }
414
-
415
- if (callbacks.length === 0 || hiddenTabShouldWait()) {
416
- if (!delayPollTimeout) {
417
- delayPollTimeout = setTimeout(function () {
418
- delayPollTimeout = null;
419
- poll();
420
- }, parseInt(500 + Math.random() * 500));
421
- }
422
- return;
423
- }
424
-
425
- var data = {};
426
- for (var i = 0; i < callbacks.length; i++) {
427
- data[callbacks[i].channel] = callbacks[i].last_id;
428
- }
429
-
430
- // could possibly already be started
431
- // notice the delay timeout above
432
- if (!me.longPoll) {
433
- me.longPoll = longPoller(poll, data);
434
- }
435
- };
436
-
437
- // monitor visibility, issue a new long poll when the page shows
438
- if (document.addEventListener && "hidden" in document) {
439
- me.onVisibilityChange = function () {
440
- if (
441
- !document.hidden &&
442
- !me.longPoll &&
443
- (pollTimeout || delayPollTimeout)
444
- ) {
445
- clearTimeout(pollTimeout);
446
- clearTimeout(delayPollTimeout);
447
-
448
- delayPollTimeout = null;
449
- pollTimeout = null;
450
- poll();
451
- }
452
- };
453
-
454
- document.addEventListener("visibilitychange", me.onVisibilityChange);
455
- }
456
-
457
- poll();
458
- },
459
-
460
- status: function () {
461
- if (paused) {
462
- return "paused";
463
- } else if (started) {
464
- return "started";
465
- } else if (stopped) {
466
- return "stopped";
467
- } else {
468
- throw "Cannot determine current status";
469
- }
470
- },
471
-
472
- // Subscribe to a channel
473
- // if lastId is 0 or larger, it will recieve messages AFTER that id
474
- // if lastId is negative it will perform lookbehind
475
- // -1 will subscribe to all new messages
476
- // -2 will recieve last message + all new messages
477
- // -3 will recieve last 2 messages + all new messages
478
- // if undefined will default to -1
479
- subscribe: function (channel, func, lastId) {
480
- if (!started && !stopped) {
481
- me.start();
482
- }
483
-
484
- if (lastId === null || typeof lastId === "undefined") {
485
- lastId = -1;
486
- } else if (typeof lastId !== "number") {
487
- throw (
488
- "lastId has type " + typeof lastId + " but a number was expected."
489
- );
490
- }
491
-
492
- if (typeof channel !== "string") {
493
- throw "Channel name must be a string!";
494
- }
495
-
496
- callbacks.push({
497
- channel: channel,
498
- func: func,
499
- last_id: lastId,
500
- });
501
- if (me.longPoll) {
502
- me.longPoll.abort();
503
- }
504
-
505
- return func;
506
- },
507
-
508
- // Unsubscribe from a channel
509
- unsubscribe: function (channel, func) {
510
- // TODO allow for globbing in the middle of a channel name
511
- // like /something/*/something
512
- // at the moment we only support globbing /something/*
513
- var glob = false;
514
- if (channel.indexOf("*", channel.length - 1) !== -1) {
515
- channel = channel.substr(0, channel.length - 1);
516
- glob = true;
517
- }
518
-
519
- var removed = false;
520
-
521
- for (var i = callbacks.length - 1; i >= 0; i--) {
522
- var callback = callbacks[i];
523
- var keep;
524
-
525
- if (glob) {
526
- keep = callback.channel.substr(0, channel.length) !== channel;
527
- } else {
528
- keep = callback.channel !== channel;
529
- }
530
-
531
- if (!keep && func && callback.func !== func) {
532
- keep = true;
533
- }
534
-
535
- if (!keep) {
536
- callbacks.splice(i, 1);
537
- removed = true;
538
- }
539
- }
540
-
541
- if (removed && me.longPoll) {
542
- me.longPoll.abort();
543
- }
544
-
545
- return removed;
546
- },
547
- };
548
- return me;
549
- });