alchemymvc 1.1.9 → 1.2.1

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.
@@ -1,621 +1,621 @@
1
- var iostream = alchemy.use('socket.io-stream'),
2
- libstream = require('stream');
3
-
4
- /**
5
- * The Socket Conduit Class
6
- *
7
- * @author Jelle De Loecker <jelle@develry.be>
8
- * @since 0.2.0
9
- * @version 1.1.3
10
- *
11
- * @param {Socker} socket
12
- * @param {Object} announcement
13
- */
14
- var SocketConduit = Function.inherits('Alchemy.Conduit', function Socket(socket, announcement) {
15
-
16
- var that = this;
17
-
18
- // Indicate this is a websocket
19
- this.websocket = socket;
20
- this.socket = socket;
21
-
22
- // Store the headers
23
- this.headers = socket.handshake.headers;
24
-
25
- // Store the announcement data
26
- this.announcement = announcement;
27
-
28
- // Detect node clients
29
- if (this.headers['user-agent'] == 'node-XMLHttpRequest') {
30
- this.isNodeClient = true;
31
- }
32
-
33
- // Cookies to send to the client
34
- this.new_cookies = {};
35
- this.new_cookie_header = [];
36
-
37
- // The amount of submissions
38
- this.counter = 0;
39
-
40
- // Collection of callbacks
41
- this.callbacks = {};
42
-
43
- // Create a stream instance
44
- this.stream = iostream(socket);
45
-
46
- // Linkup storage
47
- this.linkups = {};
48
-
49
- this.parseAnnouncement();
50
- if (!this.isNodeClient) this.parseRequest();
51
-
52
- // Listen for payloads
53
- socket.on('payload', function onPayload(packet) {
54
- that.onPayload(packet);
55
- });
56
-
57
- // Listen for responses
58
- socket.on('response', function onResponse(packet) {
59
- that.onResponse(packet);
60
- });
61
-
62
- // Listen for linkups
63
- socket.on('linkup', function onLinkup(packet) {
64
- that.onLinkup(packet);
65
- });
66
-
67
- // Listen for disconnects
68
- socket.on('disconnect', function onDisconnect() {
69
-
70
- var key;
71
-
72
- that.emit('disconnect');
73
-
74
- for (key in that.linkups) {
75
- that.linkups[key].emit('disconnect');
76
- }
77
- });
78
-
79
- // Listen for payloads on the stream socket
80
- this.stream.on('payload', function onStreamPayload(stream, packet) {
81
- packet.stream = stream;
82
- that.onPayload(packet);
83
- });
84
-
85
- // Listen for responses on the stream socket
86
- this.stream.on('response', function onStreamResponse(stream, data) {
87
- packet.stream = stream;
88
- that.onPayload(packet);
89
- });
90
-
91
- // Listen to data subscriptions
92
- this.on('subscribe-data', function gotSubscriptionRequest(arr) {
93
-
94
- if (!Array.isArray(arr)) {
95
- return;
96
- }
97
-
98
- that.registerBindings(arr);
99
- });
100
- });
101
-
102
- /**
103
- * Return the client IP address
104
- *
105
- * @author Jelle De Loecker <jelle@develry.be>
106
- * @since 0.2.1
107
- * @version 0.2.1
108
- */
109
- SocketConduit.setProperty(function ip() {
110
-
111
- var sock = this.socket;
112
-
113
- if (!sock) {
114
- return null;
115
- }
116
-
117
- return sock.conn.remoteAddress || null;
118
- });
119
-
120
- /**
121
- * Parse the request, get information from the url
122
- *
123
- * @author Jelle De Loecker <jelle@develry.be>
124
- * @since 0.2.0
125
- * @version 0.2.0
126
- */
127
- SocketConduit.setMethod(function parseRequest() {
128
- this.parseShortcuts();
129
- this.parseLanguages();
130
- });
131
-
132
- /**
133
- * Handle a client announcement
134
- *
135
- * @author Jelle De Loecker <jelle@develry.be>
136
- * @since 0.2.0
137
- * @version 1.1.0
138
- *
139
- * @param {Object} data
140
- */
141
- SocketConduit.setMethod(function parseAnnouncement() {
142
-
143
- var connections,
144
- data = this.announcement;
145
-
146
- // Store the scene information
147
- this.connection_type = data.connection_type;
148
- this.last_update = data.last_update || Date.now();
149
- this.scene_id = data.scene;
150
-
151
- // Register the connection in the user's session
152
- this.getSession().registerConnection(this);
153
-
154
- // Tell the client we're ready
155
- this.websocket.emit('ready');
156
-
157
- if (alchemy.settings.debug) {
158
- log.info('Established websocket connection to', this.ip, 'using', this.useragent.family, this.useragent.major);
159
- }
160
- });
161
-
162
- /**
163
- * Handle an incoming linkup
164
- *
165
- * @author Jelle De Loecker <jelle@develry.be>
166
- * @since 0.2.0
167
- * @version 0.4.0
168
- *
169
- * @param {Object} packet
170
- */
171
- SocketConduit.setMethod(function onLinkup(packet) {
172
-
173
- var controller,
174
- instance,
175
- section,
176
- linkup,
177
- router,
178
- action,
179
- split,
180
- type,
181
- temp,
182
- fnc,
183
- id;
184
-
185
- temp = packet.type;
186
-
187
- // Make sure a type is defined
188
- if (!temp) {
189
- return;
190
- }
191
-
192
- id = packet.id;
193
-
194
- // Split by at symbol to get the optional sub section
195
- temp = temp.split('@');
196
-
197
- if (temp.length == 2) {
198
- section = temp[0];
199
- type = temp[1];
200
-
201
- router = Router.subSections[section];
202
-
203
- if (!router) {
204
- return;
205
- }
206
-
207
- } else {
208
- type = temp[0];
209
- router = Router;
210
- }
211
-
212
- // See if any socket routes have been set
213
- if (router.linkupRoutes[type]) {
214
- fnc = router.linkupRoutes[type];
215
-
216
- // Create the new linkup instance
217
- linkup = new Linkup(this, type, id, packet.data);
218
-
219
- if (typeof fnc === 'function') {
220
- fnc.call(this, this, linkup);
221
- } else if (typeof fnc === 'string') { // Strings like 'StaticController#index'
222
-
223
- split = fnc.split('#');
224
- controller = split[0];
225
- action = split[1];
226
-
227
- instance = Controller.get(controller, this);
228
- instance.packetType = packet.type;
229
- instance.doAction(action, [this, linkup, packet.data]);
230
- }
231
- }
232
-
233
- // Emit it on the socket itself
234
- this.emit(packet.type, linkup, null);
235
- });
236
-
237
- /**
238
- * Handle a client packet
239
- *
240
- * @author Jelle De Loecker <jelle@develry.be>
241
- * @since 0.2.0
242
- * @version 1.1.4
243
- *
244
- * @param {Object} packet
245
- */
246
- SocketConduit.setMethod(function onPayload(packet) {
247
-
248
- var that = this,
249
- controller,
250
- instance,
251
- respond,
252
- action,
253
- linkup,
254
- route,
255
- split,
256
- args,
257
- fnc;
258
-
259
- if (packet.respond) {
260
- respond = function respondToPayload(err, data, stream) {
261
-
262
- var responsePacket = {
263
- err : null,
264
- respond_to : packet.id,
265
- noData : false,
266
- data : null
267
- };
268
-
269
- if (err) {
270
- err = JSON.clone(err, 'toHawkejs');
271
- responsePacket.err = JSON.toDryObject(err);
272
- }
273
-
274
- if (data && data.constructor.name == 'IOStream') {
275
- responsePacket.noData = true;
276
- stream = data;
277
- } else {
278
- // Make sure the data is converted to client-side friendly code
279
- data = JSON.clone(data, 'toHawkejs');
280
-
281
- // Now create a DRY object
282
- responsePacket.data = JSON.toDryObject(data);
283
- }
284
-
285
- if (stream && stream.constructor.name == 'IOStream') {
286
- return that.stream.emit('response', stream, responsePacket);
287
- }
288
-
289
- that.socket.emit('response', responsePacket);
290
- };
291
- }
292
-
293
- // See if this is for a specific linkup
294
- if (packet.link) {
295
- linkup = this.linkups[packet.link];
296
-
297
- // Do NOT allow packets of the 'linkup_packet' type,
298
- // we use that internally for the entire packet
299
- if (packet.type == 'linkup_packet') {
300
- return;
301
- }
302
-
303
- if (linkup) {
304
-
305
- let test = linkup.simpleListeners.get(packet.type);
306
-
307
- if (!test) {
308
- console.error('Linkup', linkup, 'has no listener for', packet.type);
309
- }
310
-
311
- if (packet.stream) {
312
- linkup.emit(packet.type, packet.data, packet.stream, respond, null);
313
- } else {
314
- linkup.emit(packet.type, packet.data, respond, null);
315
- }
316
-
317
- // Also emit as a linkup_packet
318
- linkup.emit('linkup_packet', packet, respond, null);
319
- }
320
-
321
- return;
322
- }
323
-
324
- // Normalize data-server-events
325
- if (packet.type == 'data-server-event') {
326
-
327
- // See if data is valid, otherwise do nothing
328
- if (!packet.data || !packet.data[0]) {
329
- return;
330
- }
331
-
332
- // Get the real type
333
- packet.type = packet.data[0];
334
-
335
- // Store the rest as arguments
336
- packet.args = packet.data.slice(1);
337
- }
338
-
339
- // See if any socket routes have been set
340
- if (Router.socketRoutes[packet.type]) {
341
- route = Router.socketRoutes[packet.type];
342
-
343
- if (packet.args) {
344
- args = packet.args;
345
-
346
- // Add "respond" callback to the bottom
347
- args.push(respond);
348
- } else if (packet.stream) {
349
- args = [packet.data, packet.stream, respond];
350
- } else {
351
- args = [packet.data, respond];
352
- }
353
-
354
- route.callHandler(this, args);
355
- }
356
-
357
- // Emit it on the socket itself
358
- if (packet.stream) {
359
- that.emit(packet.type, packet.data, packet.stream, respond, null);
360
- } else {
361
- that.emit(packet.type, packet.data, respond, null);
362
- }
363
- });
364
-
365
- /**
366
- * Handle a client response
367
- *
368
- * @author Jelle De Loecker <jelle@develry.be>
369
- * @since 0.2.0
370
- * @version 0.2.0
371
- *
372
- * @param {Object} packet
373
- */
374
- SocketConduit.setMethod(function onResponse(packet) {
375
-
376
- if (typeof this.callbacks[packet.respond_to] === 'function') {
377
- this.callbacks[packet.respond_to](packet.err, packet.data);
378
- }
379
-
380
- delete this.callbacks[packet.respond_to];
381
- });
382
-
383
- /**
384
- * Submit a message to the client
385
- *
386
- * @author Jelle De Loecker <jelle@develry.be>
387
- * @since 0.0.1
388
- * @version 1.0.5
389
- *
390
- * @param {String} type
391
- * @param {Object} data
392
- * @param {IOStream} stream
393
- * @param {Function} callback
394
- */
395
- SocketConduit.setMethod(function submit(type, data, stream, callback) {
396
-
397
- var packet = {},
398
- regular_stream;
399
-
400
- if (alchemy.isStream(data)) {
401
- callback = stream;
402
- stream = data;
403
- data = undefined;
404
- } else if (typeof data === 'function') {
405
- callback = data;
406
- data = undefined;
407
- }
408
-
409
- if (!stream || typeof stream == 'function') {
410
- callback = stream;
411
- stream = undefined;
412
- } else if (stream && stream.constructor.name != 'IOStream') {
413
-
414
- // Keep the regular stream
415
- regular_stream = stream;
416
-
417
- // Create an IOStream
418
- stream = this.createStream();
419
-
420
- // Pipe the regular stream into the IOStream
421
- regular_stream.pipe(stream);
422
- }
423
-
424
- if (Array.isArray(type)) {
425
- packet.link = type[0];
426
- packet.type = type[1];
427
- } else {
428
- packet.type = type;
429
- }
430
-
431
- if (data && data.constructor.name == 'IOStream') {
432
- stream = data;
433
- packet.noData = true;
434
- } else {
435
- // Make sure the data is converted to client-side friendly code
436
- data = JSON.clone(data, 'toHawkejs');
437
-
438
- // Now create a DRY object
439
- packet.data = JSON.toDryObject(data);
440
- }
441
-
442
- packet.id = 's' + (++this.counter);
443
-
444
- if (typeof callback == 'function') {
445
- this.callbacks[packet.id] = callback;
446
- packet.respond = true;
447
- }
448
-
449
- if (stream) {
450
- // Emit on the IOStream socket
451
- return this.stream.emit('payload', stream, packet);
452
- }
453
-
454
- this.socket.emit('payload', packet);
455
- });
456
-
457
- /**
458
- * Create a stream we can send through a websocket connection
459
- *
460
- * @author Jelle De Loecker <jelle@develry.be>
461
- * @since 0.2.0
462
- * @version 0.2.0
463
- */
464
- SocketConduit.setMethod(function createStream() {
465
- return iostream.createStream();
466
- });
467
-
468
- /**
469
- * Create a linkup to the client
470
- *
471
- * @author Jelle De Loecker <jelle@develry.be>
472
- * @since 0.2.0
473
- * @version 0.2.0
474
- */
475
- SocketConduit.setMethod(function linkup(type, data, cb) {
476
-
477
- var client,
478
- id;
479
-
480
- if (typeof data == 'function') {
481
- cb = data;
482
- data = {};
483
- }
484
-
485
- id = type + '-' + Crypto.pseudoHex();
486
- client = new Linkup(this, type, id, data);
487
-
488
- this.socket.emit('linkup', {type: type, id: id, data: data});
489
-
490
- if (cb) {
491
- client.once('ready', function whenReady() {
492
- cb.call(client, client);
493
- });
494
- }
495
-
496
- return client;
497
- });
498
-
499
- /**
500
- * The Linkup class
501
- *
502
- * @author Jelle De Loecker <jelle@develry.be>
503
- * @since 0.2.0
504
- * @version 0.2.0
505
- *
506
- * @param {SocketConduit} conduit
507
- * @param {String} type
508
- * @param {String} id
509
- * @param {Object} data
510
- */
511
- var Linkup = Function.inherits('Informer', function Linkup(conduit, type, id, data) {
512
-
513
- // The socket conduit
514
- this.conduit = conduit;
515
-
516
- // The typename
517
- this.type = type;
518
-
519
- // The unique identifier, as created by the client
520
- this.id = id;
521
-
522
- // The initial data
523
- this.initialData = data;
524
-
525
- // Make it store itself
526
- conduit.linkups[id] = this;
527
-
528
- // Tell the client it's ready
529
- this.submit('ready');
530
- });
531
-
532
- /**
533
- * Destroy this link
534
- *
535
- * @author Jelle De Loecker <jelle@develry.be>
536
- * @since 0.2.0
537
- * @version 1.1.4
538
- */
539
- Linkup.setMethod(function destroy() {
540
-
541
- // Remove all listeners
542
- this.removeAllListeners();
543
-
544
- // Remove if from the linkups list
545
- delete this.conduit.linkups[this.id];
546
- });
547
-
548
- /**
549
- * Submit a message to the client
550
- *
551
- * @author Jelle De Loecker <jelle@develry.be>
552
- * @since 0.2.0
553
- * @version 0.2.0
554
- *
555
- * @param {String} type
556
- * @param {Object} data
557
- * @param {IOStream} stream
558
- * @param {Function} callback
559
- */
560
- Linkup.setMethod(function submit(type, data, stream, callback) {
561
- this.conduit.submit([this.id, type], data, stream, callback);
562
- });
563
-
564
- /**
565
- * Submit a message to the server on this link and return a promise
566
- *
567
- * @author Jelle De Loecker <jelle@elevenways.be>
568
- * @since 1.1.2
569
- * @version 1.1.2
570
- *
571
- * @param {String} type
572
- * @param {Object} data
573
- */
574
- Linkup.setMethod(function demand(type, data, stream) {
575
-
576
- const that = this;
577
-
578
- let pledge = new Classes.Pledge(),
579
- args = [type, data];
580
-
581
- if (stream) {
582
- args.push(stream);
583
- }
584
-
585
- this.submit(...args, function done(err, result) {
586
-
587
- if (err) {
588
- return pledge.reject(err);
589
- }
590
-
591
- pledge.resolve(result);
592
- });
593
-
594
- return pledge;
595
- });
596
-
597
- /**
598
- * Create a stream
599
- *
600
- * @author Jelle De Loecker <jelle@develry.be>
601
- * @since 0.2.0
602
- * @version 0.2.0
603
- */
604
- Linkup.setMethod(function createStream() {
605
- return this.conduit.createStream();
606
- });
607
-
608
- /**
609
- * Send an error to the client
610
- *
611
- * @author Jelle De Loecker <jelle@develry.be>
612
- * @since 0.2.0
613
- * @version 0.2.0
614
- *
615
- * @param {Error} err
616
- * @param {Function} callback
617
- */
618
- Linkup.setMethod(function error(err, callback) {
619
- console.log('Error:', err);
620
- this.submit('error', {stack: err.stack, message: err.message}, callback);
1
+ var iostream = alchemy.use('socket.io-stream'),
2
+ libstream = require('stream');
3
+
4
+ /**
5
+ * The Socket Conduit Class
6
+ *
7
+ * @author Jelle De Loecker <jelle@develry.be>
8
+ * @since 0.2.0
9
+ * @version 1.1.3
10
+ *
11
+ * @param {Socker} socket
12
+ * @param {Object} announcement
13
+ */
14
+ var SocketConduit = Function.inherits('Alchemy.Conduit', function Socket(socket, announcement) {
15
+
16
+ var that = this;
17
+
18
+ // Indicate this is a websocket
19
+ this.websocket = socket;
20
+ this.socket = socket;
21
+
22
+ // Store the headers
23
+ this.headers = socket.handshake.headers;
24
+
25
+ // Store the announcement data
26
+ this.announcement = announcement;
27
+
28
+ // Detect node clients
29
+ if (this.headers['user-agent'] == 'node-XMLHttpRequest') {
30
+ this.isNodeClient = true;
31
+ }
32
+
33
+ // Cookies to send to the client
34
+ this.new_cookies = {};
35
+ this.new_cookie_header = [];
36
+
37
+ // The amount of submissions
38
+ this.counter = 0;
39
+
40
+ // Collection of callbacks
41
+ this.callbacks = {};
42
+
43
+ // Create a stream instance
44
+ this.stream = iostream(socket);
45
+
46
+ // Linkup storage
47
+ this.linkups = {};
48
+
49
+ this.parseAnnouncement();
50
+ if (!this.isNodeClient) this.parseRequest();
51
+
52
+ // Listen for payloads
53
+ socket.on('payload', function onPayload(packet) {
54
+ that.onPayload(packet);
55
+ });
56
+
57
+ // Listen for responses
58
+ socket.on('response', function onResponse(packet) {
59
+ that.onResponse(packet);
60
+ });
61
+
62
+ // Listen for linkups
63
+ socket.on('linkup', function onLinkup(packet) {
64
+ that.onLinkup(packet);
65
+ });
66
+
67
+ // Listen for disconnects
68
+ socket.on('disconnect', function onDisconnect() {
69
+
70
+ var key;
71
+
72
+ that.emit('disconnect');
73
+
74
+ for (key in that.linkups) {
75
+ that.linkups[key].emit('disconnect');
76
+ }
77
+ });
78
+
79
+ // Listen for payloads on the stream socket
80
+ this.stream.on('payload', function onStreamPayload(stream, packet) {
81
+ packet.stream = stream;
82
+ that.onPayload(packet);
83
+ });
84
+
85
+ // Listen for responses on the stream socket
86
+ this.stream.on('response', function onStreamResponse(stream, data) {
87
+ packet.stream = stream;
88
+ that.onPayload(packet);
89
+ });
90
+
91
+ // Listen to data subscriptions
92
+ this.on('subscribe-data', function gotSubscriptionRequest(arr) {
93
+
94
+ if (!Array.isArray(arr)) {
95
+ return;
96
+ }
97
+
98
+ that.registerBindings(arr);
99
+ });
100
+ });
101
+
102
+ /**
103
+ * Return the client IP address
104
+ *
105
+ * @author Jelle De Loecker <jelle@develry.be>
106
+ * @since 0.2.1
107
+ * @version 0.2.1
108
+ */
109
+ SocketConduit.setProperty(function ip() {
110
+
111
+ var sock = this.socket;
112
+
113
+ if (!sock) {
114
+ return null;
115
+ }
116
+
117
+ return sock.conn.remoteAddress || null;
118
+ });
119
+
120
+ /**
121
+ * Parse the request, get information from the url
122
+ *
123
+ * @author Jelle De Loecker <jelle@develry.be>
124
+ * @since 0.2.0
125
+ * @version 0.2.0
126
+ */
127
+ SocketConduit.setMethod(function parseRequest() {
128
+ this.parseShortcuts();
129
+ this.parseLanguages();
130
+ });
131
+
132
+ /**
133
+ * Handle a client announcement
134
+ *
135
+ * @author Jelle De Loecker <jelle@develry.be>
136
+ * @since 0.2.0
137
+ * @version 1.1.0
138
+ *
139
+ * @param {Object} data
140
+ */
141
+ SocketConduit.setMethod(function parseAnnouncement() {
142
+
143
+ var connections,
144
+ data = this.announcement;
145
+
146
+ // Store the scene information
147
+ this.connection_type = data.connection_type;
148
+ this.last_update = data.last_update || Date.now();
149
+ this.scene_id = data.scene;
150
+
151
+ // Register the connection in the user's session
152
+ this.getSession().registerConnection(this);
153
+
154
+ // Tell the client we're ready
155
+ this.websocket.emit('ready');
156
+
157
+ if (alchemy.settings.debug) {
158
+ log.info('Established websocket connection to', this.ip, 'using', this.useragent.family, this.useragent.major);
159
+ }
160
+ });
161
+
162
+ /**
163
+ * Handle an incoming linkup
164
+ *
165
+ * @author Jelle De Loecker <jelle@develry.be>
166
+ * @since 0.2.0
167
+ * @version 0.4.0
168
+ *
169
+ * @param {Object} packet
170
+ */
171
+ SocketConduit.setMethod(function onLinkup(packet) {
172
+
173
+ var controller,
174
+ instance,
175
+ section,
176
+ linkup,
177
+ router,
178
+ action,
179
+ split,
180
+ type,
181
+ temp,
182
+ fnc,
183
+ id;
184
+
185
+ temp = packet.type;
186
+
187
+ // Make sure a type is defined
188
+ if (!temp) {
189
+ return;
190
+ }
191
+
192
+ id = packet.id;
193
+
194
+ // Split by at symbol to get the optional sub section
195
+ temp = temp.split('@');
196
+
197
+ if (temp.length == 2) {
198
+ section = temp[0];
199
+ type = temp[1];
200
+
201
+ router = Router.subSections[section];
202
+
203
+ if (!router) {
204
+ return;
205
+ }
206
+
207
+ } else {
208
+ type = temp[0];
209
+ router = Router;
210
+ }
211
+
212
+ // See if any socket routes have been set
213
+ if (router.linkupRoutes[type]) {
214
+ fnc = router.linkupRoutes[type];
215
+
216
+ // Create the new linkup instance
217
+ linkup = new Linkup(this, type, id, packet.data);
218
+
219
+ if (typeof fnc === 'function') {
220
+ fnc.call(this, this, linkup);
221
+ } else if (typeof fnc === 'string') { // Strings like 'StaticController#index'
222
+
223
+ split = fnc.split('#');
224
+ controller = split[0];
225
+ action = split[1];
226
+
227
+ instance = Controller.get(controller, this);
228
+ instance.packetType = packet.type;
229
+ instance.doAction(action, [this, linkup, packet.data]);
230
+ }
231
+ }
232
+
233
+ // Emit it on the socket itself
234
+ this.emit(packet.type, linkup, null);
235
+ });
236
+
237
+ /**
238
+ * Handle a client packet
239
+ *
240
+ * @author Jelle De Loecker <jelle@develry.be>
241
+ * @since 0.2.0
242
+ * @version 1.1.4
243
+ *
244
+ * @param {Object} packet
245
+ */
246
+ SocketConduit.setMethod(function onPayload(packet) {
247
+
248
+ var that = this,
249
+ controller,
250
+ instance,
251
+ respond,
252
+ action,
253
+ linkup,
254
+ route,
255
+ split,
256
+ args,
257
+ fnc;
258
+
259
+ if (packet.respond) {
260
+ respond = function respondToPayload(err, data, stream) {
261
+
262
+ var responsePacket = {
263
+ err : null,
264
+ respond_to : packet.id,
265
+ noData : false,
266
+ data : null
267
+ };
268
+
269
+ if (err) {
270
+ err = JSON.clone(err, 'toHawkejs');
271
+ responsePacket.err = JSON.toDryObject(err);
272
+ }
273
+
274
+ if (data && data.constructor.name == 'IOStream') {
275
+ responsePacket.noData = true;
276
+ stream = data;
277
+ } else {
278
+ // Make sure the data is converted to client-side friendly code
279
+ data = JSON.clone(data, 'toHawkejs');
280
+
281
+ // Now create a DRY object
282
+ responsePacket.data = JSON.toDryObject(data);
283
+ }
284
+
285
+ if (stream && stream.constructor.name == 'IOStream') {
286
+ return that.stream.emit('response', stream, responsePacket);
287
+ }
288
+
289
+ that.socket.emit('response', responsePacket);
290
+ };
291
+ }
292
+
293
+ // See if this is for a specific linkup
294
+ if (packet.link) {
295
+ linkup = this.linkups[packet.link];
296
+
297
+ // Do NOT allow packets of the 'linkup_packet' type,
298
+ // we use that internally for the entire packet
299
+ if (packet.type == 'linkup_packet') {
300
+ return;
301
+ }
302
+
303
+ if (linkup) {
304
+
305
+ let test = linkup.simpleListeners.get(packet.type);
306
+
307
+ if (!test) {
308
+ console.error('Linkup', linkup, 'has no listener for', packet.type);
309
+ }
310
+
311
+ if (packet.stream) {
312
+ linkup.emit(packet.type, packet.data, packet.stream, respond, null);
313
+ } else {
314
+ linkup.emit(packet.type, packet.data, respond, null);
315
+ }
316
+
317
+ // Also emit as a linkup_packet
318
+ linkup.emit('linkup_packet', packet, respond, null);
319
+ }
320
+
321
+ return;
322
+ }
323
+
324
+ // Normalize data-server-events
325
+ if (packet.type == 'data-server-event') {
326
+
327
+ // See if data is valid, otherwise do nothing
328
+ if (!packet.data || !packet.data[0]) {
329
+ return;
330
+ }
331
+
332
+ // Get the real type
333
+ packet.type = packet.data[0];
334
+
335
+ // Store the rest as arguments
336
+ packet.args = packet.data.slice(1);
337
+ }
338
+
339
+ // See if any socket routes have been set
340
+ if (Router.socketRoutes[packet.type]) {
341
+ route = Router.socketRoutes[packet.type];
342
+
343
+ if (packet.args) {
344
+ args = packet.args;
345
+
346
+ // Add "respond" callback to the bottom
347
+ args.push(respond);
348
+ } else if (packet.stream) {
349
+ args = [packet.data, packet.stream, respond];
350
+ } else {
351
+ args = [packet.data, respond];
352
+ }
353
+
354
+ route.callHandler(this, args);
355
+ }
356
+
357
+ // Emit it on the socket itself
358
+ if (packet.stream) {
359
+ that.emit(packet.type, packet.data, packet.stream, respond, null);
360
+ } else {
361
+ that.emit(packet.type, packet.data, respond, null);
362
+ }
363
+ });
364
+
365
+ /**
366
+ * Handle a client response
367
+ *
368
+ * @author Jelle De Loecker <jelle@develry.be>
369
+ * @since 0.2.0
370
+ * @version 0.2.0
371
+ *
372
+ * @param {Object} packet
373
+ */
374
+ SocketConduit.setMethod(function onResponse(packet) {
375
+
376
+ if (typeof this.callbacks[packet.respond_to] === 'function') {
377
+ this.callbacks[packet.respond_to](packet.err, packet.data);
378
+ }
379
+
380
+ delete this.callbacks[packet.respond_to];
381
+ });
382
+
383
+ /**
384
+ * Submit a message to the client
385
+ *
386
+ * @author Jelle De Loecker <jelle@develry.be>
387
+ * @since 0.0.1
388
+ * @version 1.0.5
389
+ *
390
+ * @param {String} type
391
+ * @param {Object} data
392
+ * @param {IOStream} stream
393
+ * @param {Function} callback
394
+ */
395
+ SocketConduit.setMethod(function submit(type, data, stream, callback) {
396
+
397
+ var packet = {},
398
+ regular_stream;
399
+
400
+ if (alchemy.isStream(data)) {
401
+ callback = stream;
402
+ stream = data;
403
+ data = undefined;
404
+ } else if (typeof data === 'function') {
405
+ callback = data;
406
+ data = undefined;
407
+ }
408
+
409
+ if (!stream || typeof stream == 'function') {
410
+ callback = stream;
411
+ stream = undefined;
412
+ } else if (stream && stream.constructor.name != 'IOStream') {
413
+
414
+ // Keep the regular stream
415
+ regular_stream = stream;
416
+
417
+ // Create an IOStream
418
+ stream = this.createStream();
419
+
420
+ // Pipe the regular stream into the IOStream
421
+ regular_stream.pipe(stream);
422
+ }
423
+
424
+ if (Array.isArray(type)) {
425
+ packet.link = type[0];
426
+ packet.type = type[1];
427
+ } else {
428
+ packet.type = type;
429
+ }
430
+
431
+ if (data && data.constructor.name == 'IOStream') {
432
+ stream = data;
433
+ packet.noData = true;
434
+ } else {
435
+ // Make sure the data is converted to client-side friendly code
436
+ data = JSON.clone(data, 'toHawkejs');
437
+
438
+ // Now create a DRY object
439
+ packet.data = JSON.toDryObject(data);
440
+ }
441
+
442
+ packet.id = 's' + (++this.counter);
443
+
444
+ if (typeof callback == 'function') {
445
+ this.callbacks[packet.id] = callback;
446
+ packet.respond = true;
447
+ }
448
+
449
+ if (stream) {
450
+ // Emit on the IOStream socket
451
+ return this.stream.emit('payload', stream, packet);
452
+ }
453
+
454
+ this.socket.emit('payload', packet);
455
+ });
456
+
457
+ /**
458
+ * Create a stream we can send through a websocket connection
459
+ *
460
+ * @author Jelle De Loecker <jelle@develry.be>
461
+ * @since 0.2.0
462
+ * @version 0.2.0
463
+ */
464
+ SocketConduit.setMethod(function createStream() {
465
+ return iostream.createStream();
466
+ });
467
+
468
+ /**
469
+ * Create a linkup to the client
470
+ *
471
+ * @author Jelle De Loecker <jelle@develry.be>
472
+ * @since 0.2.0
473
+ * @version 0.2.0
474
+ */
475
+ SocketConduit.setMethod(function linkup(type, data, cb) {
476
+
477
+ var client,
478
+ id;
479
+
480
+ if (typeof data == 'function') {
481
+ cb = data;
482
+ data = {};
483
+ }
484
+
485
+ id = type + '-' + Crypto.pseudoHex();
486
+ client = new Linkup(this, type, id, data);
487
+
488
+ this.socket.emit('linkup', {type: type, id: id, data: data});
489
+
490
+ if (cb) {
491
+ client.once('ready', function whenReady() {
492
+ cb.call(client, client);
493
+ });
494
+ }
495
+
496
+ return client;
497
+ });
498
+
499
+ /**
500
+ * The Linkup class
501
+ *
502
+ * @author Jelle De Loecker <jelle@develry.be>
503
+ * @since 0.2.0
504
+ * @version 0.2.0
505
+ *
506
+ * @param {SocketConduit} conduit
507
+ * @param {String} type
508
+ * @param {String} id
509
+ * @param {Object} data
510
+ */
511
+ var Linkup = Function.inherits('Informer', function Linkup(conduit, type, id, data) {
512
+
513
+ // The socket conduit
514
+ this.conduit = conduit;
515
+
516
+ // The typename
517
+ this.type = type;
518
+
519
+ // The unique identifier, as created by the client
520
+ this.id = id;
521
+
522
+ // The initial data
523
+ this.initialData = data;
524
+
525
+ // Make it store itself
526
+ conduit.linkups[id] = this;
527
+
528
+ // Tell the client it's ready
529
+ this.submit('ready');
530
+ });
531
+
532
+ /**
533
+ * Destroy this link
534
+ *
535
+ * @author Jelle De Loecker <jelle@develry.be>
536
+ * @since 0.2.0
537
+ * @version 1.1.4
538
+ */
539
+ Linkup.setMethod(function destroy() {
540
+
541
+ // Remove all listeners
542
+ this.removeAllListeners();
543
+
544
+ // Remove if from the linkups list
545
+ delete this.conduit.linkups[this.id];
546
+ });
547
+
548
+ /**
549
+ * Submit a message to the client
550
+ *
551
+ * @author Jelle De Loecker <jelle@develry.be>
552
+ * @since 0.2.0
553
+ * @version 0.2.0
554
+ *
555
+ * @param {String} type
556
+ * @param {Object} data
557
+ * @param {IOStream} stream
558
+ * @param {Function} callback
559
+ */
560
+ Linkup.setMethod(function submit(type, data, stream, callback) {
561
+ this.conduit.submit([this.id, type], data, stream, callback);
562
+ });
563
+
564
+ /**
565
+ * Submit a message to the server on this link and return a promise
566
+ *
567
+ * @author Jelle De Loecker <jelle@elevenways.be>
568
+ * @since 1.1.2
569
+ * @version 1.1.2
570
+ *
571
+ * @param {String} type
572
+ * @param {Object} data
573
+ */
574
+ Linkup.setMethod(function demand(type, data, stream) {
575
+
576
+ const that = this;
577
+
578
+ let pledge = new Classes.Pledge(),
579
+ args = [type, data];
580
+
581
+ if (stream) {
582
+ args.push(stream);
583
+ }
584
+
585
+ this.submit(...args, function done(err, result) {
586
+
587
+ if (err) {
588
+ return pledge.reject(err);
589
+ }
590
+
591
+ pledge.resolve(result);
592
+ });
593
+
594
+ return pledge;
595
+ });
596
+
597
+ /**
598
+ * Create a stream
599
+ *
600
+ * @author Jelle De Loecker <jelle@develry.be>
601
+ * @since 0.2.0
602
+ * @version 0.2.0
603
+ */
604
+ Linkup.setMethod(function createStream() {
605
+ return this.conduit.createStream();
606
+ });
607
+
608
+ /**
609
+ * Send an error to the client
610
+ *
611
+ * @author Jelle De Loecker <jelle@develry.be>
612
+ * @since 0.2.0
613
+ * @version 0.2.0
614
+ *
615
+ * @param {Error} err
616
+ * @param {Function} callback
617
+ */
618
+ Linkup.setMethod(function error(err, callback) {
619
+ console.log('Error:', err);
620
+ this.submit('error', {stack: err.stack, message: err.message}, callback);
621
621
  });