@dra2020/baseclient 1.0.13 → 1.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +37 -0
  3. package/dist/all/all.d.ts +36 -0
  4. package/dist/all/allclient.d.ts +18 -0
  5. package/dist/base.js +33010 -0
  6. package/dist/base.js.map +1 -0
  7. package/dist/baseclient.js +8991 -0
  8. package/dist/baseclient.js.map +1 -0
  9. package/dist/context/all.d.ts +1 -0
  10. package/dist/context/context.d.ts +13 -0
  11. package/dist/dbabstract/all.d.ts +1 -0
  12. package/dist/dbabstract/db.d.ts +83 -0
  13. package/dist/dbdynamo/all.d.ts +1 -0
  14. package/dist/dbdynamo/dbdynamo.d.ts +190 -0
  15. package/dist/filterexpr/all.d.ts +1 -0
  16. package/dist/filterexpr/filterexpr.d.ts +64 -0
  17. package/dist/fsm/all.d.ts +1 -0
  18. package/dist/fsm/fsm.d.ts +118 -0
  19. package/dist/fsmfile/all.d.ts +1 -0
  20. package/dist/fsmfile/fsmfile.d.ts +47 -0
  21. package/dist/jsonstream/all.d.ts +1 -0
  22. package/dist/jsonstream/jsonstream.d.ts +130 -0
  23. package/dist/lambda/all.d.ts +1 -0
  24. package/dist/lambda/env.d.ts +10 -0
  25. package/dist/lambda/lambda.d.ts +18 -0
  26. package/dist/logabstract/all.d.ts +1 -0
  27. package/dist/logabstract/log.d.ts +26 -0
  28. package/dist/logclient/all.d.ts +1 -0
  29. package/dist/logclient/log.d.ts +6 -0
  30. package/dist/logserver/all.d.ts +5 -0
  31. package/dist/logserver/log.d.ts +11 -0
  32. package/dist/logserver/logaccum.d.ts +154 -0
  33. package/dist/logserver/logblob.d.ts +24 -0
  34. package/dist/logserver/logconcat.d.ts +55 -0
  35. package/dist/logserver/logkey.d.ts +28 -0
  36. package/dist/memsqs/all.d.ts +4 -0
  37. package/dist/memsqs/client.d.ts +13 -0
  38. package/dist/memsqs/loopback.d.ts +11 -0
  39. package/dist/memsqs/orderedlist.d.ts +19 -0
  40. package/dist/memsqs/queue.d.ts +84 -0
  41. package/dist/memsqs/server.d.ts +37 -0
  42. package/dist/ot-editutil/all.d.ts +2 -0
  43. package/dist/ot-editutil/oteditutil.d.ts +14 -0
  44. package/dist/ot-editutil/otmaputil.d.ts +21 -0
  45. package/dist/ot-js/all.d.ts +9 -0
  46. package/dist/ot-js/otarray.d.ts +111 -0
  47. package/dist/ot-js/otclientengine.d.ts +38 -0
  48. package/dist/ot-js/otcomposite.d.ts +37 -0
  49. package/dist/ot-js/otcounter.d.ts +17 -0
  50. package/dist/ot-js/otengine.d.ts +22 -0
  51. package/dist/ot-js/otmap.d.ts +19 -0
  52. package/dist/ot-js/otserverengine.d.ts +38 -0
  53. package/dist/ot-js/otsession.d.ts +111 -0
  54. package/dist/ot-js/ottypes.d.ts +29 -0
  55. package/dist/poly/all.d.ts +15 -0
  56. package/dist/poly/blend.d.ts +1 -0
  57. package/dist/poly/boundbox.d.ts +16 -0
  58. package/dist/poly/cartesian.d.ts +5 -0
  59. package/dist/poly/graham-scan.d.ts +8 -0
  60. package/dist/poly/hash.d.ts +1 -0
  61. package/dist/poly/matrix.d.ts +24 -0
  62. package/dist/poly/minbound.d.ts +1 -0
  63. package/dist/poly/poly.d.ts +52 -0
  64. package/dist/poly/polybin.d.ts +5 -0
  65. package/dist/poly/polylabel.d.ts +7 -0
  66. package/dist/poly/polypack.d.ts +30 -0
  67. package/dist/poly/polyround.d.ts +1 -0
  68. package/dist/poly/polysimplify.d.ts +1 -0
  69. package/dist/poly/quad.d.ts +48 -0
  70. package/dist/poly/selfintersect.d.ts +1 -0
  71. package/dist/poly/shamos.d.ts +1 -0
  72. package/dist/poly/simplify.d.ts +2 -0
  73. package/dist/poly/topo.d.ts +46 -0
  74. package/dist/poly/union.d.ts +48 -0
  75. package/dist/storage/all.d.ts +4 -0
  76. package/dist/storage/datablob.d.ts +9 -0
  77. package/dist/storage/env.d.ts +10 -0
  78. package/dist/storage/splitsblob.d.ts +13 -0
  79. package/dist/storage/storage.d.ts +166 -0
  80. package/dist/storages3/all.d.ts +1 -0
  81. package/dist/storages3/s3.d.ts +62 -0
  82. package/dist/util/all.d.ts +5 -0
  83. package/dist/util/bintrie.d.ts +93 -0
  84. package/dist/util/countedhash.d.ts +19 -0
  85. package/dist/util/gradient.d.ts +15 -0
  86. package/dist/util/indexedarray.d.ts +15 -0
  87. package/dist/util/util.d.ts +68 -0
  88. package/docs/context.md +2 -0
  89. package/docs/dbabstract.md +2 -0
  90. package/docs/dbdynamo.md +2 -0
  91. package/docs/fsm.md +243 -0
  92. package/docs/fsmfile.md +2 -0
  93. package/docs/jsonstream.md +44 -0
  94. package/docs/lambda.md +2 -0
  95. package/docs/logabstract.md +2 -0
  96. package/docs/logclient.md +2 -0
  97. package/docs/logserver.md +2 -0
  98. package/docs/ot-editutil.md +2 -0
  99. package/docs/ot-js.md +95 -0
  100. package/docs/poly.md +103 -0
  101. package/docs/storage.md +2 -0
  102. package/docs/storages3.md +2 -0
  103. package/docs/util.md +2 -0
  104. package/lib/all/all.ts +41 -0
  105. package/lib/all/allclient.ts +19 -0
  106. package/lib/context/all.ts +1 -0
  107. package/lib/context/context.ts +82 -0
  108. package/lib/dbabstract/all.ts +1 -0
  109. package/lib/dbabstract/db.ts +246 -0
  110. package/lib/dbdynamo/all.ts +1 -0
  111. package/lib/dbdynamo/dbdynamo.ts +1551 -0
  112. package/lib/filterexpr/all.ts +1 -0
  113. package/lib/filterexpr/filterexpr.ts +625 -0
  114. package/lib/fsm/all.ts +1 -0
  115. package/lib/fsm/fsm.ts +549 -0
  116. package/lib/fsmfile/all.ts +1 -0
  117. package/lib/fsmfile/fsmfile.ts +236 -0
  118. package/lib/jsonstream/all.ts +1 -0
  119. package/lib/jsonstream/jsonstream.ts +940 -0
  120. package/lib/lambda/all.ts +1 -0
  121. package/lib/lambda/env.ts +13 -0
  122. package/lib/lambda/lambda.ts +120 -0
  123. package/lib/logabstract/all.ts +1 -0
  124. package/lib/logabstract/log.ts +55 -0
  125. package/lib/logclient/all.ts +1 -0
  126. package/lib/logclient/log.ts +105 -0
  127. package/lib/logserver/all.ts +5 -0
  128. package/lib/logserver/log.ts +565 -0
  129. package/lib/logserver/logaccum.ts +1445 -0
  130. package/lib/logserver/logblob.ts +84 -0
  131. package/lib/logserver/logconcat.ts +313 -0
  132. package/lib/logserver/logkey.ts +125 -0
  133. package/lib/memsqs/all.ts +4 -0
  134. package/lib/memsqs/client.ts +268 -0
  135. package/lib/memsqs/loopback.ts +64 -0
  136. package/lib/memsqs/orderedlist.ts +74 -0
  137. package/lib/memsqs/queue.ts +395 -0
  138. package/lib/memsqs/server.ts +262 -0
  139. package/lib/ot-editutil/all.ts +2 -0
  140. package/lib/ot-editutil/oteditutil.ts +180 -0
  141. package/lib/ot-editutil/otmaputil.ts +209 -0
  142. package/lib/ot-js/all.ts +9 -0
  143. package/lib/ot-js/otarray.ts +1168 -0
  144. package/lib/ot-js/otclientengine.ts +327 -0
  145. package/lib/ot-js/otcomposite.ts +247 -0
  146. package/lib/ot-js/otcounter.ts +145 -0
  147. package/lib/ot-js/otengine.ts +71 -0
  148. package/lib/ot-js/otmap.ts +144 -0
  149. package/lib/ot-js/otserverengine.ts +329 -0
  150. package/lib/ot-js/otsession.ts +199 -0
  151. package/lib/ot-js/ottypes.ts +98 -0
  152. package/lib/poly/all.ts +15 -0
  153. package/lib/poly/blend.ts +27 -0
  154. package/lib/poly/boundbox.ts +102 -0
  155. package/lib/poly/cartesian.ts +130 -0
  156. package/lib/poly/graham-scan.ts +401 -0
  157. package/lib/poly/hash.ts +15 -0
  158. package/lib/poly/matrix.ts +309 -0
  159. package/lib/poly/minbound.ts +211 -0
  160. package/lib/poly/poly.ts +767 -0
  161. package/lib/poly/polybin.ts +218 -0
  162. package/lib/poly/polylabel.ts +204 -0
  163. package/lib/poly/polypack.ts +458 -0
  164. package/lib/poly/polyround.ts +30 -0
  165. package/lib/poly/polysimplify.ts +24 -0
  166. package/lib/poly/quad.ts +272 -0
  167. package/lib/poly/selfintersect.ts +87 -0
  168. package/lib/poly/shamos.ts +297 -0
  169. package/lib/poly/simplify.ts +119 -0
  170. package/lib/poly/topo.ts +525 -0
  171. package/lib/poly/union.ts +371 -0
  172. package/lib/storage/all.ts +4 -0
  173. package/lib/storage/datablob.ts +36 -0
  174. package/lib/storage/env.ts +14 -0
  175. package/lib/storage/splitsblob.ts +63 -0
  176. package/lib/storage/storage.ts +604 -0
  177. package/lib/storages3/all.ts +1 -0
  178. package/lib/storages3/s3.ts +576 -0
  179. package/lib/util/all.ts +5 -0
  180. package/lib/util/bintrie.ts +603 -0
  181. package/lib/util/countedhash.ts +83 -0
  182. package/lib/util/gradient.ts +108 -0
  183. package/lib/util/indexedarray.ts +80 -0
  184. package/lib/util/util.ts +695 -0
  185. package/package.json +8 -8
@@ -0,0 +1,395 @@
1
+ // Project libraries
2
+ import * as Util from '../util/all';
3
+
4
+ // Local libraries
5
+ import * as OL from './orderedlist';
6
+
7
+ export const DefaultPort: number = (process.env['MEMSQS_PORT'] ? Number(process.env['MEMSQS_PORT']) : 0) || 80;
8
+ export const DefaultServerUrl: string = `http://localhost:${DefaultPort}`;
9
+
10
+ export interface QMessage
11
+ {
12
+ messageid: string;
13
+ groupid: string;
14
+ seqno?: number;
15
+ contents?: any;
16
+ blobid?: string;
17
+ }
18
+ export type QMessages = QMessage[];
19
+
20
+ interface QMessageHolder
21
+ {
22
+ m: QMessage;
23
+ pending: boolean;
24
+ deadline: Util.Deadline;
25
+ }
26
+
27
+ type QMessageList = OL.OrderedList<QMessageHolder>;
28
+ type QMessageIndex = { [key: string]: boolean };
29
+
30
+ interface QVisibility
31
+ {
32
+ owner: string;
33
+ deadline: Util.Deadline;
34
+ index: QMessageIndex;
35
+ }
36
+
37
+ const DefaultVisibility: QVisibility = { owner: '', deadline: null, index: null };
38
+
39
+ interface QGroup
40
+ {
41
+ id: string;
42
+ messages: QMessageList;
43
+ visibility: QVisibility;
44
+ }
45
+
46
+ type QGroupIndex = { [id: string]: QGroup };
47
+
48
+ export interface QQueueOptions
49
+ {
50
+ timeoutVisibility: number,
51
+ timeoutDead: number,
52
+ timeoutQueueDead: number,
53
+ receiveLimit: number,
54
+ longpoll: boolean,
55
+ }
56
+
57
+ const DefaultQueueOptions = {
58
+ timeoutVisibility: 1000 * 30, // 30 seconds - if server goes down, another server will grab req in 30 sec
59
+ timeoutDead: 1000 * 60 * 30, // 30 minutes - just cleaning up dead server queue
60
+ timeoutQueueDead: 1000 * 60 * 30, // 30 minutes - just cleaning up dead server queue
61
+ receiveLimit: 10, // don't get too greedy, but some batching for efficiency
62
+ longpoll: true,
63
+ };
64
+
65
+ class QQueue
66
+ {
67
+ id: string;
68
+ groups: QGroupIndex;
69
+ seqno: number;
70
+ options: QQueueOptions;
71
+ nHeld: number;
72
+ lastActive: Util.Elapsed;
73
+
74
+ constructor(id: string, options?: QQueueOptions)
75
+ {
76
+ this.id = id;
77
+ this.options = options ? options : Util.shallowCopy(DefaultQueueOptions);
78
+ this.groups = {};
79
+ this.nHeld = 0;
80
+ this.seqno = 0;
81
+ this.lastActive = new Util.Elapsed();
82
+ }
83
+
84
+ dumpLog(): void
85
+ {
86
+ console.log(`memsqs: queue: ${this.id}: ${this.nHeld} messages held`);
87
+ this.forEachGroup((g: QGroup) => {
88
+ if (! g.messages.isempty())
89
+ console.log(`memsqs: queue ${this.id}: group: ${g.id}: non-empty`);
90
+ if (g.visibility.owner != '')
91
+ console.log(`memsqs: queue ${this.id}: group: ${g.id}: owned by ${g.visibility.owner}`);
92
+ return true;
93
+ });
94
+ }
95
+
96
+ setOptions(options: QQueueOptions)
97
+ {
98
+ this.options = Util.shallowCopy(options);
99
+ }
100
+
101
+ _group(id: string): QGroup
102
+ {
103
+ let g = this.groups[id];
104
+ if (g === undefined)
105
+ {
106
+ g = { id: id, messages: new OL.OrderedList<QMessageHolder>(), visibility: Util.shallowCopy(DefaultVisibility) };
107
+ this.groups[g.id] = g;
108
+ }
109
+ return g;
110
+ }
111
+
112
+ forEachGroup(cb: (g: QGroup) => boolean): void
113
+ {
114
+ for (let id in this.groups) if (this.groups.hasOwnProperty(id))
115
+ if (! cb(this.groups[id]))
116
+ break;
117
+ }
118
+
119
+ forEachMessageHolder(g: QGroup, cb: (h: QMessageHolder) => boolean): void
120
+ {
121
+ g.messages.forEach(cb);
122
+ }
123
+
124
+ get isActive(): boolean
125
+ {
126
+ if (this.nHeld > 0) return true;
127
+
128
+ this.lastActive.end();
129
+ return this.lastActive.ms() < this.options.timeoutQueueDead;
130
+ }
131
+
132
+ checkTimeout(): void
133
+ {
134
+ // Clear visibility and cull empty groups
135
+ this.forEachGroup((g: QGroup) => {
136
+ // Clear deadline if passed
137
+ if (g.visibility.deadline && g.visibility.deadline.done())
138
+ {
139
+ g.messages.forEach((h: QMessageHolder) => {
140
+ h.pending = false;
141
+ return true; // continue
142
+ });
143
+ g.visibility = Util.shallowCopy(DefaultVisibility);
144
+ }
145
+
146
+ // Cull group if no messages and no deadline.
147
+ // Note that not culling group means that we can build up list of groups (during deadline)
148
+ // if many groups are used (like one-per-message which is standard if no grouping is needed).
149
+ if (g.visibility.owner === '' && g.messages.isempty())
150
+ delete this.groups[g.id];
151
+ return true; // continue
152
+ });
153
+
154
+ // Cull messages past deadline
155
+ this.forEachGroup((g: QGroup) => {
156
+ g.messages.forEach((h: QMessageHolder) => {
157
+ if (h.deadline.done())
158
+ this.removeFromGroup(g, h.m);
159
+ return true; // continue
160
+ });
161
+
162
+ // Cull group if no messages and no deadline
163
+ if (g.visibility.owner === '' && g.messages.isempty())
164
+ delete this.groups[g.id];
165
+ return true; // continue
166
+ });
167
+
168
+ }
169
+
170
+ send(m: QMessage): string
171
+ {
172
+ this.lastActive.start();
173
+ if (m.groupid === '*')
174
+ {
175
+ let e: string = null;
176
+ this.forEachGroup((g: QGroup) => {
177
+ if (g.visibility.owner !== '')
178
+ {
179
+ this.nHeld++;
180
+ let o = g.visibility.owner;
181
+ let gm: QMessage = { messageid: m.messageid + o, groupid: o, contents: m.contents, seqno: this.seqno++ };
182
+ e = g.messages.insert(gm.messageid, { m: gm, pending: false, deadline: new Util.Deadline(this.options.timeoutDead) } );
183
+ return e == null;
184
+ }
185
+ else
186
+ return true;
187
+ });
188
+ return e;
189
+ }
190
+ else
191
+ {
192
+ m.seqno = this.seqno++; // inject sequence number
193
+ this.nHeld++;
194
+ let g = this._group(m.groupid);
195
+ return g.messages.insert(m.messageid, { m: m, pending: false, deadline: new Util.Deadline(this.options.timeoutDead) } );
196
+ }
197
+ }
198
+
199
+ claim(owner: string, groupid: string): string
200
+ {
201
+ this.lastActive.start();
202
+ let g = this._group(groupid);
203
+ let deadline = new Util.Deadline(this.options.timeoutVisibility);
204
+ if (g.visibility.owner === '')
205
+ {
206
+ g.visibility = { owner: owner, deadline: deadline, index: {} };
207
+ return null;
208
+ }
209
+ else if (g.visibility.owner === owner)
210
+ {
211
+ g.visibility.deadline = deadline;
212
+ return null;
213
+ }
214
+ return `memsqs: claim: group ${groupid} already owned by ${g.visibility.owner}, ${owner} cannot claim.`;
215
+ }
216
+
217
+ receive(owner: string, result: QMessages): void
218
+ {
219
+ this.lastActive.start();
220
+ let deadline = new Util.Deadline(this.options.timeoutVisibility);
221
+
222
+ // First prioritize returning non-pending messages for same owner
223
+ this.forEachGroup((g: QGroup) => {
224
+ if (g.visibility.owner === owner)
225
+ {
226
+ g.messages.forEach((h: QMessageHolder) => {
227
+ if (! h.pending)
228
+ {
229
+ h.pending = true;
230
+ g.visibility.index[h.m.messageid] = true;
231
+ g.visibility.deadline = deadline;
232
+ result.push(h.m);
233
+ if (result.length >= this.options.receiveLimit)
234
+ return false;
235
+ }
236
+ return true;
237
+ });
238
+ }
239
+ return true;
240
+ });
241
+
242
+ // Now go through and add messages for any unclaimed message groups
243
+ if (result.length >= this.options.receiveLimit) return;
244
+
245
+ this.forEachGroup((g: QGroup) => {
246
+ if (g.visibility.owner === '')
247
+ {
248
+ g.messages.forEach((h: QMessageHolder) => {
249
+ if (! h.pending)
250
+ {
251
+ if (g.visibility.owner === '')
252
+ g.visibility = { owner: owner, deadline: deadline, index: {} };
253
+ h.pending = true;
254
+ g.visibility.index[h.m.messageid] = true;
255
+ result.push(h.m);
256
+ if (result.length >= this.options.receiveLimit)
257
+ return false;
258
+ }
259
+ return true;
260
+ });
261
+ }
262
+ return true;
263
+ });
264
+ }
265
+
266
+ removeFromGroup(g: QGroup, m: QMessage): string
267
+ {
268
+ this.lastActive.start();
269
+ let result = g.messages.remove(m.messageid);
270
+ if (g.visibility.index)
271
+ delete g.visibility.index[m.messageid];
272
+ if (result === null) this.nHeld--;
273
+ return result;
274
+ }
275
+
276
+ remove(m: QMessage): string
277
+ {
278
+ let g = this._group(m.groupid);
279
+ if (g)
280
+ {
281
+ let result = this.removeFromGroup(g, m);
282
+
283
+ // Comment these lines out to leave visiblity constrained through deadline.
284
+ // (Not completely sure what AWS semantics for SQS is on this point.)
285
+ // The alternative is to force client to send/receive/remove keep-alive messages.
286
+ // if (Util.isEmpty(g.visibility.index))
287
+ // g.visibility = Util.shallowCopy(DefaultVisibility);
288
+
289
+ return result;
290
+ }
291
+ else
292
+ return `memsqs: remove: queue ${this.id} has no existing group ${m.groupid}`;;
293
+ }
294
+ }
295
+
296
+ type QQueueIndex = { [id: string]: QQueue };
297
+
298
+ export class QQueueManager
299
+ {
300
+ queues: QQueueIndex;
301
+
302
+ nSent: number;
303
+ nReceived: number;
304
+ nRemoved: number;
305
+ nCulled: number;
306
+
307
+ constructor()
308
+ {
309
+ this.queues = {};
310
+ this.nSent = 0;
311
+ this.nReceived = 0;
312
+ this.nRemoved = 0;
313
+ this.nCulled = 0;
314
+ this.checkTimeout = this.checkTimeout.bind(this);
315
+ setTimeout(this.checkTimeout, 5000);
316
+ }
317
+
318
+ _queue(queueid: string): QQueue
319
+ {
320
+ let q = this.queues[queueid];
321
+ if (q === undefined)
322
+ {
323
+ q = new QQueue(queueid);
324
+ this.queues[queueid] = q;
325
+ }
326
+ return q;
327
+ }
328
+
329
+ checkTimeout(): void
330
+ {
331
+ this.forEach((q: QQueue) => {
332
+ let nHeld = q.nHeld;
333
+ q.checkTimeout();
334
+ this.nCulled += nHeld - q.nHeld;
335
+ if (! q.isActive)
336
+ {
337
+ console.log(`memsqs:checkTimeout: deleting inactive queue ${q.id}`);
338
+ delete this.queues[q.id];
339
+ }
340
+ });
341
+
342
+ setTimeout(this.checkTimeout, 5000);
343
+ }
344
+
345
+ forEach(cb: (q: QQueue) => void): void
346
+ {
347
+ for (let qid in this.queues) if (this.queues.hasOwnProperty(qid))
348
+ cb(this.queues[qid]);
349
+ }
350
+
351
+ dumpLog(): void
352
+ {
353
+ this.forEach((q: QQueue) => {
354
+ q.dumpLog();
355
+ return true;
356
+ });
357
+ }
358
+
359
+ setOptions(queueid: string, options: QQueueOptions): void
360
+ {
361
+ this._queue(queueid).setOptions(options);
362
+ }
363
+
364
+ isLongpoll(queueid: string): boolean
365
+ {
366
+ return this._queue(queueid).options.longpoll;
367
+ }
368
+
369
+ claim(queueid: string, owner: string, groupid: string): string
370
+ {
371
+ return this._queue(queueid).claim(owner, groupid);
372
+ }
373
+
374
+ send(queueid: string, m: QMessage): string
375
+ {
376
+ let result = this._queue(queueid).send(m);
377
+ if (! result)
378
+ this.nSent++;
379
+ return result;
380
+ }
381
+
382
+ receive(queueid: string, owner: string, result: QMessages): void
383
+ {
384
+ this._queue(queueid).receive(owner, result);
385
+ this.nReceived += result.length;
386
+ }
387
+
388
+ remove(queueid: string, m: QMessage): string
389
+ {
390
+ let result = this._queue(queueid).remove(m);
391
+ if (! result)
392
+ this.nRemoved++;
393
+ return result;
394
+ }
395
+ }
@@ -0,0 +1,262 @@
1
+ // Node libraries
2
+ import * as http from 'http';
3
+ import * as url from 'url';
4
+
5
+ // Shared libraries
6
+ import * as JS from '../jsonstream/all';
7
+
8
+ // Local libraries
9
+ import * as Q from './queue';
10
+
11
+ const LongPollTimeout: number = 5000;
12
+
13
+ export class SQSServer
14
+ {
15
+ private port: number;
16
+ private server: any;
17
+ queuemanager: Q.QQueueManager;
18
+ longpoll: LongPoll;
19
+
20
+ constructor(port: number = Q.DefaultPort)
21
+ {
22
+ this.queuemanager = new Q.QQueueManager();
23
+ this.longpoll = new LongPoll();
24
+ this.port = port;
25
+
26
+ this.server = http.createServer();
27
+ this.server.keepAliveTimeout = 61 * 1000; // Don't interfere with longpoll timeout
28
+ this.server.headersTimeout = 65 * 1000; // Longer than keepAliveTimeout
29
+
30
+ this.server.on('request', (req: any, res: any) => {
31
+ new OneRequest(this, req, res);
32
+ });
33
+
34
+ this.server.on('close', () => {
35
+ this.server = null;
36
+ });
37
+
38
+ this.server.on('error', (err: any) => {
39
+ if (err && err.message) err = err.message;
40
+ console.log(`memsqs: server: unexpected error ${err}`);
41
+ this.server = null;
42
+ });
43
+
44
+ try
45
+ {
46
+ this.server.listen(this.port);
47
+ }
48
+ catch (err)
49
+ {
50
+ console.log(`memsqs: server: unexpected exception on listen: ${JSON.stringify(err)}`);
51
+ this.server = null;
52
+ }
53
+ }
54
+
55
+ get running(): boolean
56
+ {
57
+ return this.server != null;
58
+ }
59
+
60
+ close(): void
61
+ {
62
+ if (this.server)
63
+ this.server.close();
64
+ }
65
+ }
66
+
67
+ interface LongPollRequest
68
+ {
69
+ deadline: number;
70
+ request: OneRequest;
71
+ }
72
+
73
+ class LongPoll
74
+ {
75
+ requests: LongPollRequest[];
76
+
77
+ constructor()
78
+ {
79
+ this.requests = [];
80
+ this.tick = this.tick.bind(this);
81
+ setTimeout(this.tick, 1000);
82
+ }
83
+
84
+ check(): void
85
+ {
86
+ let deadline: number = (new Date()).getTime();
87
+ for (let i: number = 0; i < this.requests.length; )
88
+ {
89
+ let r = this.requests[i];
90
+ if (! r.request.onRetry())
91
+ if (r.deadline < deadline)
92
+ r.request.onFinish();
93
+ if (r.request.isDone())
94
+ this.requests.splice(i, 1);
95
+ else
96
+ i++;
97
+ }
98
+ }
99
+
100
+ tick(): void
101
+ {
102
+ this.check();
103
+ setTimeout(this.tick, 1000);
104
+ }
105
+
106
+ add(one: OneRequest): void
107
+ {
108
+ //console.log('memsqs: queuing longpoll');
109
+ this.requests.push({ deadline: (new Date()).getTime() + LongPollTimeout, request: one });
110
+ }
111
+ }
112
+
113
+ class OneRequest
114
+ {
115
+ server: SQSServer;
116
+ req: any;
117
+ res: any;
118
+ jsonstream: JS.JSONStreamReader;
119
+ json: any;
120
+ body: any;
121
+
122
+ constructor(server: SQSServer, req: any, res: any)
123
+ {
124
+ this.server = server;
125
+ this.req = req;
126
+ this.res = res;
127
+ this.jsonstream = new JS.JSONStreamReader();
128
+ this.json = null;
129
+ this.body = { statuscode: 0 };
130
+ this.jsonstream.on('end', (json: any) => { this.json = json; this.onDone(); });
131
+ this.jsonstream.on('error', (err: any) => {
132
+ this.onError(`memsqs: error parsing request body: ${err}`);
133
+ });
134
+ this.req.on('error', (err: any) => {
135
+ this.onError(`memsqs: error reading request: ${err}`);
136
+ });
137
+ this.res.on('error', (err: any) => {
138
+ this.onError(`memsqs: error writing response: ${err}`);
139
+ });
140
+ this.jsonstream.start(req);
141
+ }
142
+
143
+ isDone(): boolean
144
+ {
145
+ return this.res == null;
146
+ }
147
+
148
+ onRetry(): boolean
149
+ {
150
+ let result: Q.QMessages = [];
151
+ let qm = this.server.queuemanager;
152
+ qm.receive(this.json.queueid, this.json.owner, result);
153
+ this.body.result = result;
154
+ if (result.length != 0)
155
+ {
156
+ this.onFinish();
157
+ return true;
158
+ }
159
+ else
160
+ return false;
161
+ }
162
+
163
+ onDone(): void
164
+ {
165
+ try
166
+ {
167
+ if (this.json.queueid === undefined || this.json.queueid == '' || this.json.api === undefined)
168
+ {
169
+ this.onError(`memsqs: badly formed request: ${JSON.stringify(this.json)}`);
170
+ return;
171
+ }
172
+
173
+ let qm = this.server.queuemanager;
174
+
175
+ switch (this.json.api)
176
+ {
177
+ case 'setoptions':
178
+ if (this.json.data === undefined)
179
+ this.onError('memsqs: setoptions: payload is empty');
180
+ else
181
+ qm.setOptions(this.json.queueid, this.json.data as Q.QQueueOptions);
182
+ break;
183
+
184
+ case 'claim':
185
+ if (this.json.data === undefined || this.json.data.owner === undefined || this.json.data.groupid === undefined)
186
+ this.onError('memsqs: claim: badly formed request');
187
+ else
188
+ {
189
+ let err = qm.claim(this.json.queueid, this.json.data.owner, this.json.data.groupid);
190
+ if (err)
191
+ this.onError(err);
192
+ }
193
+ break;
194
+
195
+ case 'send':
196
+ if (this.json.data === undefined)
197
+ this.onError('memsqs: send: payload is empty');
198
+ else
199
+ {
200
+ let err = qm.send(this.json.queueid, this.json.data as Q.QMessage);
201
+ if (err)
202
+ this.onError(err);
203
+ else
204
+ this.server.longpoll.check();
205
+ }
206
+ break;
207
+
208
+ case 'receive':
209
+ if (this.json.owner === undefined)
210
+ this.onError('memsqs: receive: no owner specified');
211
+ else
212
+ {
213
+ let result: Q.QMessages = [];
214
+ qm.receive(this.json.queueid, this.json.owner, result);
215
+ this.body.result = result;
216
+ if (result.length == 0 && qm.isLongpoll(this.json.queueid))
217
+ {
218
+ this.server.longpoll.add(this);
219
+ return;
220
+ }
221
+ }
222
+ break;
223
+
224
+ case 'remove':
225
+ if (this.json.data === undefined)
226
+ this.onError('memsqs: remove: payload is empty');
227
+ else
228
+ {
229
+ let err = qm.remove(this.json.queueid, this.json.data as Q.QMessage);
230
+ if (err)
231
+ this.onError(err);
232
+ }
233
+ break;
234
+ }
235
+
236
+ this.onFinish();
237
+ }
238
+ catch (err)
239
+ {
240
+ console.log(`memsqs: server: unexpected exception in onDone: ${JSON.stringify(err)}`);
241
+ this.onError((err && err.message) ? err.message : err);
242
+ }
243
+ }
244
+
245
+ onError(s: string): void
246
+ {
247
+ console.log(`${s}: reporting failure`);
248
+ this.body.statuscode = 1;
249
+ this.body.error = 'failure';
250
+ this.onFinish();
251
+ }
252
+
253
+ onFinish(): void
254
+ {
255
+ if (this.res)
256
+ {
257
+ this.res.writeHead(200, { 'Content-Type': 'application/json; charset=UTF-8' });
258
+ this.res.end(JSON.stringify(this.body));
259
+ this.res = null;
260
+ }
261
+ }
262
+ }
@@ -0,0 +1,2 @@
1
+ export * from "./oteditutil";
2
+ export * from "./otmaputil";