subduino 0.3.1 → 0.5.0
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.
- data/README.md +66 -0
- data/Rakefile +25 -39
- data/bin/subduino +16 -28
- data/lib/subduino.rb +10 -2
- data/lib/subduino/ard_io.rb +34 -37
- data/lib/subduino/scaffold/semantic.cache +13 -0
- data/lib/subduino/serial.rb +44 -0
- data/lib/subduino/version.rb +4 -0
- data/spec/subduino/#cubduino_spec.rb# +12 -0
- data/spec/subduino/ard_io_spec.rb +2 -2
- metadata +79 -100
- data/.document +0 -5
- data/Readme.textile +0 -92
- data/VERSION +0 -1
- data/duino/.gitignore +0 -2
- data/duino/Makefile +0 -403
- data/duino/duino.pde +0 -323
- data/duino/duino.rb +0 -16
- data/duino/methods.pde +0 -5
- data/examples/duin +0 -45
- data/ext/subduino/Makefile +0 -187
- data/ext/subduino/cubduino.c +0 -242
- data/ext/subduino/cubduino.h +0 -12
- data/ext/subduino/cubduino.o +0 -0
- data/ext/subduino/cubduino.so +0 -0
- data/lib/subduino/arduino.rb +0 -30
- data/node/arduinode.js +0 -68
- data/node/server.js +0 -45
- data/node/vendor/faye.js +0 -1925
- data/subduino.gemspec +0 -92
data/ext/subduino/cubduino.c
DELETED
@@ -1,242 +0,0 @@
|
|
1
|
-
/*
|
2
|
-
* Arduino-serial
|
3
|
-
* --------------
|
4
|
-
*
|
5
|
-
* A simple command-line example program showing how a computer can
|
6
|
-
* communicate with an Arduino board. Works on any POSIX system (Mac/Unix/PC)
|
7
|
-
*
|
8
|
-
*
|
9
|
-
* Compile with something like:
|
10
|
-
* gcc -o arduino-serial arduino-serial.c
|
11
|
-
*
|
12
|
-
* Created 5 December 2006
|
13
|
-
* Copyleft (c) 2006, Tod E. Kurt, tod@todbot.com
|
14
|
-
* http://todbot.com/blog/
|
15
|
-
*
|
16
|
-
*
|
17
|
-
* Updated 8 December 2006:
|
18
|
-
* Justin McBride discoevered B14400 & B28800 aren't in Linux's termios.h.
|
19
|
-
* I've included his patch, but commented out for now. One really needs a
|
20
|
-
* real make system when doing cross-platform C and I wanted to avoid that
|
21
|
-
* for this little program. Those baudrates aren't used much anyway. :)
|
22
|
-
*
|
23
|
-
* Updated 26 December 2007:
|
24
|
-
* Added ability to specify a delay (so you can wait for Arduino Diecimila)
|
25
|
-
* Added ability to send a binary byte number
|
26
|
-
*
|
27
|
-
* Update 31 August 2008:
|
28
|
-
* Added patch to clean up odd baudrates from Andy at hexapodia.org
|
29
|
-
*
|
30
|
-
*/
|
31
|
-
|
32
|
-
#include "cubduino.h"
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
// Ruby 1.x compatibility
|
37
|
-
#ifndef RSTRING_PTR
|
38
|
-
#define RSTRING_PTR(s) (RSTRING(s)->ptr)
|
39
|
-
#endif
|
40
|
-
#ifndef RSTRING_LEN
|
41
|
-
#define RSTRING_LEN(s) (RSTRING(s)->len)
|
42
|
-
#endif
|
43
|
-
|
44
|
-
VALUE Cubduino;
|
45
|
-
|
46
|
-
VALUE sBaud, sDataBits, sStopBits, sParity; /* strings */
|
47
|
-
VALUE sRts, sDtr, sCts, sDsr, sDcd, sRi;
|
48
|
-
|
49
|
-
|
50
|
-
void Init_cubduino();
|
51
|
-
//VALUE method_cwrite(VALUE self);
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
void usage(void);
|
56
|
-
int serialport_init(const char* serialport, int baud);
|
57
|
-
int serialport_writebyte(int fd, uint8_t b);
|
58
|
-
int serialport_write(int fd, const char* str);
|
59
|
-
int serialport_read_until(int fd, char* buf, char until);
|
60
|
-
|
61
|
-
|
62
|
-
int main(int argc, char *argv[])
|
63
|
-
{
|
64
|
-
int rc,n;
|
65
|
-
int fd = 0;
|
66
|
-
int baudrate = B9600; // default
|
67
|
-
char serialport[256];
|
68
|
-
char buf[256];
|
69
|
-
|
70
|
-
/* parse options */
|
71
|
-
int option_index = 0, opt;
|
72
|
-
static struct option loptions[] = {
|
73
|
-
{"help", no_argument, 0, 'h'},
|
74
|
-
{"port", required_argument, 0, 'p'},
|
75
|
-
{"baud", required_argument, 0, 'b'},
|
76
|
-
{"send", required_argument, 0, 's'},
|
77
|
-
{"receive", no_argument, 0, 'r'},
|
78
|
-
{"num", required_argument, 0, 'n'},
|
79
|
-
{"delay", required_argument, 0, 'd'}
|
80
|
-
};
|
81
|
-
|
82
|
-
while(1) {
|
83
|
-
opt = getopt_long (argc, argv, "hp:b:s:rn:d:",
|
84
|
-
loptions, &option_index);
|
85
|
-
if (opt==-1) break;
|
86
|
-
switch (opt) {
|
87
|
-
case '0': break;
|
88
|
-
case 'd':
|
89
|
-
n = strtol(optarg,NULL,10);
|
90
|
-
usleep(n * 1000 ); // sleep milliseconds
|
91
|
-
break;
|
92
|
-
case 'h':
|
93
|
-
usage();
|
94
|
-
break;
|
95
|
-
case 'b':
|
96
|
-
baudrate = strtol(optarg,NULL,10);
|
97
|
-
break;
|
98
|
-
case 'p':
|
99
|
-
strcpy(serialport,optarg);
|
100
|
-
fd = serialport_init(optarg, baudrate);
|
101
|
-
if(fd==-1) return -1;
|
102
|
-
break;
|
103
|
-
case 'n':
|
104
|
-
n = strtol(optarg, NULL, 10); // convert string to number
|
105
|
-
rc = serialport_writebyte(fd, (uint8_t)n);
|
106
|
-
if(rc==-1) return -1;
|
107
|
-
break;
|
108
|
-
case 's':
|
109
|
-
strcpy(buf,optarg);
|
110
|
-
rc = serialport_write(fd, buf);
|
111
|
-
if(rc==-1) return -1;
|
112
|
-
break;
|
113
|
-
case 'r':
|
114
|
-
serialport_read_until(fd, buf, '\n');
|
115
|
-
printf("read: %s\n",buf);
|
116
|
-
break;
|
117
|
-
}
|
118
|
-
}
|
119
|
-
|
120
|
-
exit(EXIT_SUCCESS);
|
121
|
-
} // end main
|
122
|
-
|
123
|
-
int serialport_writebyte( int fd, uint8_t b)
|
124
|
-
{
|
125
|
-
int n = write(fd,&b,1);
|
126
|
-
if( n!=1)
|
127
|
-
return -1;
|
128
|
-
return 0;
|
129
|
-
}
|
130
|
-
|
131
|
-
int serialport_write(int fd, const char* str)
|
132
|
-
{
|
133
|
-
int len = strlen(str);
|
134
|
-
int n = write(fd, str, len);
|
135
|
-
if( n!=len )
|
136
|
-
return -1;
|
137
|
-
return 0;
|
138
|
-
}
|
139
|
-
|
140
|
-
int serialport_read_until(int fd, char* buf, char until)
|
141
|
-
{
|
142
|
-
char b[1];
|
143
|
-
int i=0;
|
144
|
-
do {
|
145
|
-
int n = read(fd, b, 1); // read a char at a time
|
146
|
-
if( n==-1) return -1; // couldn't read
|
147
|
-
if( n==0 ) {
|
148
|
-
usleep( 10 * 1000 ); // wait 10 msec try again
|
149
|
-
continue;
|
150
|
-
}
|
151
|
-
buf[i] = b[0]; i++;
|
152
|
-
} while( b[0] != until );
|
153
|
-
|
154
|
-
buf[i] = 0; // null terminate the string
|
155
|
-
return 0;
|
156
|
-
}
|
157
|
-
|
158
|
-
// takes the string name of the serial port (e.g. "/dev/tty.usbserial","COM1")
|
159
|
-
// and a baud rate (bps) and connects to that port at that speed and 8N1.
|
160
|
-
// opens the port in fully raw mode so you can send binary data.
|
161
|
-
// returns valid fd, or -1 on error
|
162
|
-
int serialport_init(const char* serialport, int baud)
|
163
|
-
{
|
164
|
-
struct termios toptions;
|
165
|
-
int fd;
|
166
|
-
|
167
|
-
//fprintf(stderr,"init_serialport: opening port %s @ %d bps\n",
|
168
|
-
// serialport,baud);
|
169
|
-
|
170
|
-
fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
|
171
|
-
if (fd == -1) {
|
172
|
-
perror("init_serialport: Unable to open port ");
|
173
|
-
return -1;
|
174
|
-
}
|
175
|
-
|
176
|
-
if (tcgetattr(fd, &toptions) < 0) {
|
177
|
-
perror("init_serialport: Couldn't get term attributes");
|
178
|
-
return -1;
|
179
|
-
}
|
180
|
-
speed_t brate = baud; // let you override switch below if needed
|
181
|
-
switch(baud) {
|
182
|
-
case 4800: brate=B4800; break;
|
183
|
-
case 9600: brate=B9600; break;
|
184
|
-
#ifdef B14400
|
185
|
-
case 14400: brate=B14400; break;
|
186
|
-
#endif
|
187
|
-
case 19200: brate=B19200; break;
|
188
|
-
#ifdef B28800
|
189
|
-
case 28800: brate=B28800; break;
|
190
|
-
#endif
|
191
|
-
case 38400: brate=B38400; break;
|
192
|
-
case 57600: brate=B57600; break;
|
193
|
-
case 115200: brate=B115200; break;
|
194
|
-
}
|
195
|
-
cfsetispeed(&toptions, brate);
|
196
|
-
cfsetospeed(&toptions, brate);
|
197
|
-
|
198
|
-
// 8N1
|
199
|
-
toptions.c_cflag &= ~PARENB;
|
200
|
-
toptions.c_cflag &= ~CSTOPB;
|
201
|
-
toptions.c_cflag &= ~CSIZE;
|
202
|
-
toptions.c_cflag |= CS8;
|
203
|
-
// no flow control
|
204
|
-
toptions.c_cflag &= ~CRTSCTS;
|
205
|
-
|
206
|
-
toptions.c_cflag |= CREAD | CLOCAL; // turn on READ & ignore ctrl lines
|
207
|
-
toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl
|
208
|
-
|
209
|
-
toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
|
210
|
-
toptions.c_oflag &= ~OPOST; // make raw
|
211
|
-
|
212
|
-
// see: http://unixwiz.net/techtips/termios-vmin-vtime.html
|
213
|
-
toptions.c_cc[VMIN] = 0;
|
214
|
-
toptions.c_cc[VTIME] = 20;
|
215
|
-
|
216
|
-
if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
|
217
|
-
perror("init_serialport: Couldn't set term attributes");
|
218
|
-
return -1;
|
219
|
-
}
|
220
|
-
|
221
|
-
return fd;
|
222
|
-
}
|
223
|
-
|
224
|
-
static VALUE method_write(VALUE class, VALUE arg) {
|
225
|
-
printf("%s", RSTRING_PTR(arg));
|
226
|
-
return Qnil;
|
227
|
-
}
|
228
|
-
|
229
|
-
static VALUE method_read() {
|
230
|
-
int fd = 0;
|
231
|
-
int baudrate = 9600;
|
232
|
-
fd = serialport_init(optarg, baudrate);
|
233
|
-
char buf[256];
|
234
|
-
serialport_read_until(fd, buf, '\n');
|
235
|
-
return rb_str_new2(buf);
|
236
|
-
}
|
237
|
-
|
238
|
-
void Init_cubduino() {
|
239
|
-
Cubduino = rb_define_module("Cubduino");
|
240
|
-
rb_define_singleton_method(Cubduino, "read", method_write, 0);
|
241
|
-
rb_define_singleton_method(Cubduino, "write", method_write, 1);
|
242
|
-
}
|
data/ext/subduino/cubduino.h
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
#include <ruby.h>
|
2
|
-
|
3
|
-
#include <stdio.h> /* Standard input/output definitions */
|
4
|
-
#include <stdlib.h>
|
5
|
-
#include <stdint.h> /* Standard types */
|
6
|
-
#include <string.h> /* String function definitions */
|
7
|
-
#include <unistd.h> /* UNIX standard function definitions */
|
8
|
-
#include <fcntl.h> /* File control definitions */
|
9
|
-
#include <errno.h> /* Error number definitions */
|
10
|
-
#include <termios.h> /* POSIX terminal control definitions */
|
11
|
-
#include <sys/ioctl.h>
|
12
|
-
#include <getopt.h>
|
data/ext/subduino/cubduino.o
DELETED
Binary file
|
data/ext/subduino/cubduino.so
DELETED
Binary file
|
data/lib/subduino/arduino.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
module Subduino
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
class Arduino
|
6
|
-
|
7
|
-
def self.find_usb
|
8
|
-
Dir['/dev/ttyUSB*'].first
|
9
|
-
end
|
10
|
-
|
11
|
-
|
12
|
-
def initialize
|
13
|
-
end
|
14
|
-
|
15
|
-
def send_to_arduino(string)
|
16
|
-
string.to_s.chomp.split("").each do |char|
|
17
|
-
@sp.putc char
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
# read << case comm
|
24
|
-
# when "Temp" then "%0.2f C" % (value.to_i * 0.04)
|
25
|
-
# when "Light" then "#{value.to_i * 2} Lux"
|
26
|
-
# else
|
27
|
-
# "#{comm}: #{value}"
|
28
|
-
# end
|
29
|
-
|
30
|
-
end
|
data/node/arduinode.js
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
// Arduino Node.js
|
2
|
-
|
3
|
-
|
4
|
-
var sys = require('sys'),
|
5
|
-
fs = require('fs'),
|
6
|
-
faye = require('./vendor/faye');
|
7
|
-
|
8
|
-
var dpath = "/dev/ttyUSB1";
|
9
|
-
|
10
|
-
var client = new faye.Client('http://localhost:8000/faye');
|
11
|
-
|
12
|
-
var subscription = client.subscribe('/foo', function(message) {
|
13
|
-
sys.puts("SUB!!!"); // handle message
|
14
|
-
});
|
15
|
-
|
16
|
-
var log = function(txt) {
|
17
|
-
sys.puts(txt)
|
18
|
-
}
|
19
|
-
|
20
|
-
var find_arduino = function(err, stats) {
|
21
|
-
if(err)
|
22
|
-
return log("Arduino not found...");
|
23
|
-
|
24
|
-
log("DUIN!");
|
25
|
-
log(sys.inspect(stats));
|
26
|
-
}
|
27
|
-
|
28
|
-
fs.stat("/dev/ttyUSB1", find_arduino);
|
29
|
-
|
30
|
-
|
31
|
-
var spawn = require('child_process').spawn,
|
32
|
-
stty = spawn('stty -F /dev/ttyUSB1 cs8 115200 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts'); //, ['-lh', '/usr']);
|
33
|
-
|
34
|
-
stty.stdout.on('data', function (data) {
|
35
|
-
sys.print('stdout: ' + data);
|
36
|
-
});
|
37
|
-
|
38
|
-
stty.stderr.on('data', function (data) {
|
39
|
-
log("Could not stty " + dpath);
|
40
|
-
log('stderr: ' + data);
|
41
|
-
});
|
42
|
-
|
43
|
-
stty.on('exit', function (code) {
|
44
|
-
if (code == 0)
|
45
|
-
console.log('Done modding stty ' + dpath);
|
46
|
-
});
|
47
|
-
|
48
|
-
|
49
|
-
// stty -F /dev/ttyUSB1 cs8 115200 ignbrk -brkint -icrnl -imaxbel -opost -onlcr -isig -icanon -iexten -echo -echoe -echok -echoctl -echoke noflsh -ixon -crtscts
|
50
|
-
var rr = fs.createReadStream(dpath, {
|
51
|
-
'flags': 'r',
|
52
|
-
'encoding': 'ascii', // binary, utf8
|
53
|
-
// 'mode': 0666,
|
54
|
-
// 'bufferSize': 4 * 1024
|
55
|
-
});
|
56
|
-
|
57
|
-
|
58
|
-
rr.addListener('data', function (chunk) {
|
59
|
-
log('data: ' + chunk);
|
60
|
-
log(sys.inspect(chunk));
|
61
|
-
});
|
62
|
-
|
63
|
-
rr.on('end', function () {
|
64
|
-
log('end, closed fs');
|
65
|
-
});
|
66
|
-
|
67
|
-
|
68
|
-
|
data/node/server.js
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
// Arduino <> Subduino/Node <> WWW
|
2
|
-
|
3
|
-
var sys = require('sys'),
|
4
|
-
http = require('http'),
|
5
|
-
faye = require('./vendor/faye');
|
6
|
-
|
7
|
-
var bayeux = new faye.NodeAdapter({
|
8
|
-
mount: '/faye',
|
9
|
-
timeout: 45
|
10
|
-
});
|
11
|
-
|
12
|
-
sys.puts("Starting server...");
|
13
|
-
|
14
|
-
// process.on('SIGHUP', function () {
|
15
|
-
// sys.puts('Got SIGHUP signal.');
|
16
|
-
// });
|
17
|
-
// process.on('SIGKILL', function () {
|
18
|
-
// sys.puts('Got SIGHUP signal.');
|
19
|
-
// });
|
20
|
-
|
21
|
-
// Handle non-Bayeux requests
|
22
|
-
var server = http.createServer(function(request, response) {
|
23
|
-
response.writeHead(200, {'Content-Type': 'text/plain'});
|
24
|
-
response.write('Nothing to see here...');
|
25
|
-
response.end();
|
26
|
-
});
|
27
|
-
|
28
|
-
server.on('stream', function(stream) {
|
29
|
-
sys.puts("Stream!");
|
30
|
-
sys.puts(stream);
|
31
|
-
})
|
32
|
-
|
33
|
-
var listen_callback = function() {
|
34
|
-
sys.log("Server started...");
|
35
|
-
}
|
36
|
-
|
37
|
-
bayeux.attach(server);
|
38
|
-
server.listen(8000, listen_callback);
|
39
|
-
|
40
|
-
|
41
|
-
// var client = new faye.Client('http://localhost:8000/faye');
|
42
|
-
// bayeux.getClient().publish('/email/new', {
|
43
|
-
// text: 'New email has arrived!',
|
44
|
-
// inboxSize: 34
|
45
|
-
// });
|
data/node/vendor/faye.js
DELETED
@@ -1,1925 +0,0 @@
|
|
1
|
-
if (!this.Faye) Faye = {};
|
2
|
-
|
3
|
-
Faye.extend = function(dest, source, overwrite) {
|
4
|
-
if (!source) return dest;
|
5
|
-
for (var key in source) {
|
6
|
-
if (!source.hasOwnProperty(key)) continue;
|
7
|
-
if (dest.hasOwnProperty(key) && overwrite === false) continue;
|
8
|
-
if (dest[key] !== source[key])
|
9
|
-
dest[key] = source[key];
|
10
|
-
}
|
11
|
-
return dest;
|
12
|
-
};
|
13
|
-
|
14
|
-
Faye.extend(Faye, {
|
15
|
-
VERSION: '0.5.2',
|
16
|
-
|
17
|
-
BAYEUX_VERSION: '1.0',
|
18
|
-
ID_LENGTH: 128,
|
19
|
-
JSONP_CALLBACK: 'jsonpcallback',
|
20
|
-
CONNECTION_TYPES: ['long-polling', 'callback-polling', 'websocket'],
|
21
|
-
|
22
|
-
MANDATORY_CONNECTION_TYPES: ['long-polling', 'callback-polling', 'in-process'],
|
23
|
-
|
24
|
-
ENV: (function() { return this })(),
|
25
|
-
|
26
|
-
random: function(bitlength) {
|
27
|
-
bitlength = bitlength || this.ID_LENGTH;
|
28
|
-
if (bitlength > 32) {
|
29
|
-
var parts = Math.ceil(bitlength / 32),
|
30
|
-
string = '';
|
31
|
-
while (parts--) string += this.random(32);
|
32
|
-
return string;
|
33
|
-
}
|
34
|
-
var field = Math.pow(2, bitlength);
|
35
|
-
return Math.floor(Math.random() * field).toString(36);
|
36
|
-
},
|
37
|
-
|
38
|
-
commonElement: function(lista, listb) {
|
39
|
-
for (var i = 0, n = lista.length; i < n; i++) {
|
40
|
-
if (this.indexOf(listb, lista[i]) !== -1)
|
41
|
-
return lista[i];
|
42
|
-
}
|
43
|
-
return null;
|
44
|
-
},
|
45
|
-
|
46
|
-
indexOf: function(list, needle) {
|
47
|
-
for (var i = 0, n = list.length; i < n; i++) {
|
48
|
-
if (list[i] === needle) return i;
|
49
|
-
}
|
50
|
-
return -1;
|
51
|
-
},
|
52
|
-
|
53
|
-
each: function(object, callback, scope) {
|
54
|
-
if (object instanceof Array) {
|
55
|
-
for (var i = 0, n = object.length; i < n; i++) {
|
56
|
-
if (object[i] !== undefined)
|
57
|
-
callback.call(scope || null, object[i], i);
|
58
|
-
}
|
59
|
-
} else {
|
60
|
-
for (var key in object) {
|
61
|
-
if (object.hasOwnProperty(key))
|
62
|
-
callback.call(scope || null, key, object[key]);
|
63
|
-
}
|
64
|
-
}
|
65
|
-
},
|
66
|
-
|
67
|
-
map: function(object, callback, scope) {
|
68
|
-
var result = [];
|
69
|
-
this.each(object, function() {
|
70
|
-
result.push(callback.apply(scope || null, arguments));
|
71
|
-
});
|
72
|
-
return result;
|
73
|
-
},
|
74
|
-
|
75
|
-
filter: function(array, callback, scope) {
|
76
|
-
var result = [];
|
77
|
-
this.each(array, function() {
|
78
|
-
if (callback.apply(scope, arguments))
|
79
|
-
result.push(arguments[0]);
|
80
|
-
});
|
81
|
-
return result;
|
82
|
-
},
|
83
|
-
|
84
|
-
size: function(object) {
|
85
|
-
var size = 0;
|
86
|
-
this.each(object, function() { size += 1 });
|
87
|
-
return size;
|
88
|
-
},
|
89
|
-
|
90
|
-
enumEqual: function(actual, expected) {
|
91
|
-
if (expected instanceof Array) {
|
92
|
-
if (!(actual instanceof Array)) return false;
|
93
|
-
var i = actual.length;
|
94
|
-
if (i !== expected.length) return false;
|
95
|
-
while (i--) {
|
96
|
-
if (actual[i] !== expected[i]) return false;
|
97
|
-
}
|
98
|
-
return true;
|
99
|
-
} else {
|
100
|
-
if (!(actual instanceof Object)) return false;
|
101
|
-
if (this.size(expected) !== this.size(actual)) return false;
|
102
|
-
var result = true;
|
103
|
-
this.each(actual, function(key, value) {
|
104
|
-
result = result && (expected[key] === value);
|
105
|
-
});
|
106
|
-
return result;
|
107
|
-
}
|
108
|
-
},
|
109
|
-
|
110
|
-
// http://assanka.net/content/tech/2009/09/02/json2-js-vs-prototype/
|
111
|
-
toJSON: function(object) {
|
112
|
-
if (this.stringify)
|
113
|
-
return this.stringify(object, function(key, value) {
|
114
|
-
return (this[key] instanceof Array)
|
115
|
-
? this[key]
|
116
|
-
: value;
|
117
|
-
});
|
118
|
-
|
119
|
-
return JSON.stringify(object);
|
120
|
-
},
|
121
|
-
|
122
|
-
timestamp: function() {
|
123
|
-
var date = new Date(),
|
124
|
-
year = date.getFullYear(),
|
125
|
-
month = date.getMonth() + 1,
|
126
|
-
day = date.getDate(),
|
127
|
-
hour = date.getHours(),
|
128
|
-
minute = date.getMinutes(),
|
129
|
-
second = date.getSeconds();
|
130
|
-
|
131
|
-
var pad = function(n) {
|
132
|
-
return n < 10 ? '0' + n : String(n);
|
133
|
-
};
|
134
|
-
|
135
|
-
return pad(year) + '-' + pad(month) + '-' + pad(day) + ' ' +
|
136
|
-
pad(hour) + ':' + pad(minute) + ':' + pad(second);
|
137
|
-
}
|
138
|
-
});
|
139
|
-
|
140
|
-
|
141
|
-
Faye.Class = function(parent, methods) {
|
142
|
-
if (typeof parent !== 'function') {
|
143
|
-
methods = parent;
|
144
|
-
parent = Object;
|
145
|
-
}
|
146
|
-
|
147
|
-
var klass = function() {
|
148
|
-
if (!this.initialize) return this;
|
149
|
-
return this.initialize.apply(this, arguments) || this;
|
150
|
-
};
|
151
|
-
|
152
|
-
var bridge = function() {};
|
153
|
-
bridge.prototype = parent.prototype;
|
154
|
-
|
155
|
-
klass.prototype = new bridge();
|
156
|
-
Faye.extend(klass.prototype, methods);
|
157
|
-
|
158
|
-
return klass;
|
159
|
-
};
|
160
|
-
|
161
|
-
|
162
|
-
Faye.Namespace = Faye.Class({
|
163
|
-
initialize: function() {
|
164
|
-
this._used = {};
|
165
|
-
},
|
166
|
-
|
167
|
-
generate: function() {
|
168
|
-
var name = Faye.random();
|
169
|
-
while (this._used.hasOwnProperty(name))
|
170
|
-
name = Faye.random();
|
171
|
-
return this._used[name] = name;
|
172
|
-
}
|
173
|
-
});
|
174
|
-
|
175
|
-
|
176
|
-
Faye.Deferrable = {
|
177
|
-
callback: function(callback, scope) {
|
178
|
-
if (!callback) return;
|
179
|
-
|
180
|
-
if (this._deferredStatus === 'succeeded')
|
181
|
-
return callback.apply(scope, this._deferredArgs);
|
182
|
-
|
183
|
-
this._callbacks = this._callbacks || [];
|
184
|
-
this._callbacks.push([callback, scope]);
|
185
|
-
},
|
186
|
-
|
187
|
-
setDeferredStatus: function() {
|
188
|
-
var args = Array.prototype.slice.call(arguments),
|
189
|
-
status = args.shift();
|
190
|
-
|
191
|
-
this._deferredStatus = status;
|
192
|
-
this._deferredArgs = args;
|
193
|
-
|
194
|
-
if (status !== 'succeeded') return;
|
195
|
-
if (!this._callbacks) return;
|
196
|
-
|
197
|
-
Faye.each(this._callbacks, function(callback) {
|
198
|
-
callback[0].apply(callback[1], this._deferredArgs);
|
199
|
-
}, this);
|
200
|
-
|
201
|
-
this._callbacks = [];
|
202
|
-
}
|
203
|
-
};
|
204
|
-
|
205
|
-
|
206
|
-
Faye.Publisher = {
|
207
|
-
countSubscribers: function(eventType) {
|
208
|
-
if (!this._subscribers || !this._subscribers[eventType]) return 0;
|
209
|
-
return this._subscribers[eventType].length;
|
210
|
-
},
|
211
|
-
|
212
|
-
addSubscriber: function(eventType, listener, context) {
|
213
|
-
this._subscribers = this._subscribers || {};
|
214
|
-
var list = this._subscribers[eventType] = this._subscribers[eventType] || [];
|
215
|
-
list.push([listener, context]);
|
216
|
-
},
|
217
|
-
|
218
|
-
removeSubscriber: function(eventType, listener, context) {
|
219
|
-
if (!this._subscribers || !this._subscribers[eventType]) return;
|
220
|
-
|
221
|
-
var list = this._subscribers[eventType],
|
222
|
-
i = list.length;
|
223
|
-
|
224
|
-
while (i--) {
|
225
|
-
if (listener && list[i][0] !== listener) continue;
|
226
|
-
if (context && list[i][1] !== context) continue;
|
227
|
-
list.splice(i,1);
|
228
|
-
}
|
229
|
-
},
|
230
|
-
|
231
|
-
publishEvent: function() {
|
232
|
-
var args = Array.prototype.slice.call(arguments),
|
233
|
-
eventType = args.shift();
|
234
|
-
|
235
|
-
if (!this._subscribers || !this._subscribers[eventType]) return;
|
236
|
-
|
237
|
-
Faye.each(this._subscribers[eventType], function(listener) {
|
238
|
-
listener[0].apply(listener[1], args);
|
239
|
-
});
|
240
|
-
}
|
241
|
-
};
|
242
|
-
|
243
|
-
|
244
|
-
Faye.Timeouts = {
|
245
|
-
addTimeout: function(name, delay, callback, scope) {
|
246
|
-
this._timeouts = this._timeouts || {};
|
247
|
-
if (this._timeouts.hasOwnProperty(name)) return;
|
248
|
-
var self = this;
|
249
|
-
this._timeouts[name] = setTimeout(function() {
|
250
|
-
delete self._timeouts[name];
|
251
|
-
callback.call(scope);
|
252
|
-
}, 1000 * delay);
|
253
|
-
},
|
254
|
-
|
255
|
-
removeTimeout: function(name) {
|
256
|
-
this._timeouts = this._timeouts || {};
|
257
|
-
var timeout = this._timeouts[name];
|
258
|
-
if (!timeout) return;
|
259
|
-
clearTimeout(timeout);
|
260
|
-
delete this._timeouts[name];
|
261
|
-
}
|
262
|
-
};
|
263
|
-
|
264
|
-
|
265
|
-
Faye.Logging = {
|
266
|
-
LOG_LEVELS: {
|
267
|
-
error: 3,
|
268
|
-
warn: 2,
|
269
|
-
info: 1,
|
270
|
-
debug: 0
|
271
|
-
},
|
272
|
-
|
273
|
-
logLevel: 'error',
|
274
|
-
|
275
|
-
log: function(messageArgs, level) {
|
276
|
-
if (!Faye.logger) return;
|
277
|
-
|
278
|
-
var levels = Faye.Logging.LOG_LEVELS;
|
279
|
-
if (levels[Faye.Logging.logLevel] > levels[level]) return;
|
280
|
-
|
281
|
-
var messageArgs = Array.prototype.slice.apply(messageArgs),
|
282
|
-
banner = ' [' + level.toUpperCase() + '] [Faye',
|
283
|
-
klass = null,
|
284
|
-
|
285
|
-
message = messageArgs.shift().replace(/\?/g, function() {
|
286
|
-
try {
|
287
|
-
return Faye.toJSON(messageArgs.shift());
|
288
|
-
} catch (e) {
|
289
|
-
return '[Object]';
|
290
|
-
}
|
291
|
-
});
|
292
|
-
|
293
|
-
for (var key in Faye) {
|
294
|
-
if (klass) continue;
|
295
|
-
if (typeof Faye[key] !== 'function') continue;
|
296
|
-
if (this instanceof Faye[key]) klass = key;
|
297
|
-
}
|
298
|
-
if (klass) banner += '.' + klass;
|
299
|
-
banner += '] ';
|
300
|
-
|
301
|
-
Faye.logger(Faye.timestamp() + banner + message);
|
302
|
-
}
|
303
|
-
};
|
304
|
-
|
305
|
-
Faye.each(Faye.Logging.LOG_LEVELS, function(level, value) {
|
306
|
-
Faye.Logging[level] = function() {
|
307
|
-
this.log(arguments, level);
|
308
|
-
};
|
309
|
-
});
|
310
|
-
|
311
|
-
|
312
|
-
Faye.Grammar = {
|
313
|
-
|
314
|
-
LOWALPHA: /^[a-z]$/,
|
315
|
-
|
316
|
-
UPALPHA: /^[A-Z]$/,
|
317
|
-
|
318
|
-
ALPHA: /^([a-z]|[A-Z])$/,
|
319
|
-
|
320
|
-
DIGIT: /^[0-9]$/,
|
321
|
-
|
322
|
-
ALPHANUM: /^(([a-z]|[A-Z])|[0-9])$/,
|
323
|
-
|
324
|
-
MARK: /^(\-|\_|\!|\~|\(|\)|\$|\@)$/,
|
325
|
-
|
326
|
-
STRING: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,
|
327
|
-
|
328
|
-
TOKEN: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,
|
329
|
-
|
330
|
-
INTEGER: /^([0-9])+$/,
|
331
|
-
|
332
|
-
CHANNEL_SEGMENT: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+$/,
|
333
|
-
|
334
|
-
CHANNEL_SEGMENTS: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
|
335
|
-
|
336
|
-
CHANNEL_NAME: /^\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*$/,
|
337
|
-
|
338
|
-
WILD_CARD: /^\*{1,2}$/,
|
339
|
-
|
340
|
-
CHANNEL_PATTERN: /^(\/(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)))+)*\/\*{1,2}$/,
|
341
|
-
|
342
|
-
VERSION_ELEMENT: /^(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*$/,
|
343
|
-
|
344
|
-
VERSION: /^([0-9])+(\.(([a-z]|[A-Z])|[0-9])(((([a-z]|[A-Z])|[0-9])|\-|\_))*)*$/,
|
345
|
-
|
346
|
-
CLIENT_ID: /^((([a-z]|[A-Z])|[0-9]))+$/,
|
347
|
-
|
348
|
-
ID: /^((([a-z]|[A-Z])|[0-9]))+$/,
|
349
|
-
|
350
|
-
ERROR_MESSAGE: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*$/,
|
351
|
-
|
352
|
-
ERROR_ARGS: /^(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*$/,
|
353
|
-
|
354
|
-
ERROR_CODE: /^[0-9][0-9][0-9]$/,
|
355
|
-
|
356
|
-
ERROR: /^([0-9][0-9][0-9]:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*(,(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)*:(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*|[0-9][0-9][0-9]::(((([a-z]|[A-Z])|[0-9])|(\-|\_|\!|\~|\(|\)|\$|\@)| |\/|\*|\.))*)$/
|
357
|
-
|
358
|
-
};
|
359
|
-
|
360
|
-
|
361
|
-
Faye.Extensible = {
|
362
|
-
addExtension: function(extension) {
|
363
|
-
this._extensions = this._extensions || [];
|
364
|
-
this._extensions.push(extension);
|
365
|
-
if (extension.added) extension.added();
|
366
|
-
},
|
367
|
-
|
368
|
-
removeExtension: function(extension) {
|
369
|
-
if (!this._extensions) return;
|
370
|
-
var i = this._extensions.length;
|
371
|
-
while (i--) {
|
372
|
-
if (this._extensions[i] !== extension) continue;
|
373
|
-
this._extensions.splice(i,1);
|
374
|
-
if (extension.removed) extension.removed();
|
375
|
-
}
|
376
|
-
},
|
377
|
-
|
378
|
-
pipeThroughExtensions: function(stage, message, callback, scope) {
|
379
|
-
if (!this._extensions) return callback.call(scope, message);
|
380
|
-
var extensions = this._extensions.slice();
|
381
|
-
|
382
|
-
var pipe = function(message) {
|
383
|
-
if (!message) return callback.call(scope, message);
|
384
|
-
|
385
|
-
var extension = extensions.shift();
|
386
|
-
if (!extension) return callback.call(scope, message);
|
387
|
-
|
388
|
-
if (extension[stage]) extension[stage](message, pipe);
|
389
|
-
else pipe(message);
|
390
|
-
};
|
391
|
-
pipe(message);
|
392
|
-
}
|
393
|
-
};
|
394
|
-
|
395
|
-
|
396
|
-
Faye.Channel = Faye.Class({
|
397
|
-
initialize: function(name) {
|
398
|
-
this.id = this.name = name;
|
399
|
-
},
|
400
|
-
|
401
|
-
push: function(message) {
|
402
|
-
this.publishEvent('message', message);
|
403
|
-
}
|
404
|
-
});
|
405
|
-
|
406
|
-
Faye.extend(Faye.Channel.prototype, Faye.Publisher);
|
407
|
-
|
408
|
-
Faye.extend(Faye.Channel, {
|
409
|
-
HANDSHAKE: '/meta/handshake',
|
410
|
-
CONNECT: '/meta/connect',
|
411
|
-
SUBSCRIBE: '/meta/subscribe',
|
412
|
-
UNSUBSCRIBE: '/meta/unsubscribe',
|
413
|
-
DISCONNECT: '/meta/disconnect',
|
414
|
-
|
415
|
-
META: 'meta',
|
416
|
-
SERVICE: 'service',
|
417
|
-
|
418
|
-
isValid: function(name) {
|
419
|
-
return Faye.Grammar.CHANNEL_NAME.test(name) ||
|
420
|
-
Faye.Grammar.CHANNEL_PATTERN.test(name);
|
421
|
-
},
|
422
|
-
|
423
|
-
parse: function(name) {
|
424
|
-
if (!this.isValid(name)) return null;
|
425
|
-
return name.split('/').slice(1);
|
426
|
-
},
|
427
|
-
|
428
|
-
isMeta: function(name) {
|
429
|
-
var segments = this.parse(name);
|
430
|
-
return segments ? (segments[0] === this.META) : null;
|
431
|
-
},
|
432
|
-
|
433
|
-
isService: function(name) {
|
434
|
-
var segments = this.parse(name);
|
435
|
-
return segments ? (segments[0] === this.SERVICE) : null;
|
436
|
-
},
|
437
|
-
|
438
|
-
isSubscribable: function(name) {
|
439
|
-
if (!this.isValid(name)) return null;
|
440
|
-
return !this.isMeta(name) && !this.isService(name);
|
441
|
-
},
|
442
|
-
|
443
|
-
Tree: Faye.Class({
|
444
|
-
initialize: function(value) {
|
445
|
-
this._value = value;
|
446
|
-
this._children = {};
|
447
|
-
},
|
448
|
-
|
449
|
-
eachChild: function(block, context) {
|
450
|
-
Faye.each(this._children, function(key, subtree) {
|
451
|
-
block.call(context, key, subtree);
|
452
|
-
});
|
453
|
-
},
|
454
|
-
|
455
|
-
each: function(prefix, block, context) {
|
456
|
-
this.eachChild(function(path, subtree) {
|
457
|
-
path = prefix.concat(path);
|
458
|
-
subtree.each(path, block, context);
|
459
|
-
});
|
460
|
-
if (this._value !== undefined) block.call(context, prefix, this._value);
|
461
|
-
},
|
462
|
-
|
463
|
-
getKeys: function() {
|
464
|
-
return this.map(function(key, value) { return '/' + key.join('/') });
|
465
|
-
},
|
466
|
-
|
467
|
-
map: function(block, context) {
|
468
|
-
var result = [];
|
469
|
-
this.each([], function(path, value) {
|
470
|
-
result.push(block.call(context, path, value));
|
471
|
-
});
|
472
|
-
return result;
|
473
|
-
},
|
474
|
-
|
475
|
-
get: function(name) {
|
476
|
-
var tree = this.traverse(name);
|
477
|
-
return tree ? tree._value : null;
|
478
|
-
},
|
479
|
-
|
480
|
-
set: function(name, value) {
|
481
|
-
var subtree = this.traverse(name, true);
|
482
|
-
if (subtree) subtree._value = value;
|
483
|
-
},
|
484
|
-
|
485
|
-
traverse: function(path, createIfAbsent) {
|
486
|
-
if (typeof path === 'string') path = Faye.Channel.parse(path);
|
487
|
-
|
488
|
-
if (path === null) return null;
|
489
|
-
if (path.length === 0) return this;
|
490
|
-
|
491
|
-
var subtree = this._children[path[0]];
|
492
|
-
if (!subtree && !createIfAbsent) return null;
|
493
|
-
if (!subtree) subtree = this._children[path[0]] = new Faye.Channel.Tree();
|
494
|
-
|
495
|
-
return subtree.traverse(path.slice(1), createIfAbsent);
|
496
|
-
},
|
497
|
-
|
498
|
-
findOrCreate: function(channel) {
|
499
|
-
var existing = this.get(channel);
|
500
|
-
if (existing) return existing;
|
501
|
-
existing = new Faye.Channel(channel);
|
502
|
-
this.set(channel, existing);
|
503
|
-
return existing;
|
504
|
-
},
|
505
|
-
|
506
|
-
glob: function(path) {
|
507
|
-
if (typeof path === 'string') path = Faye.Channel.parse(path);
|
508
|
-
|
509
|
-
if (path === null) return [];
|
510
|
-
if (path.length === 0) return (this._value === undefined) ? [] : [this._value];
|
511
|
-
|
512
|
-
var list = [];
|
513
|
-
|
514
|
-
if (Faye.enumEqual(path, ['*'])) {
|
515
|
-
Faye.each(this._children, function(key, subtree) {
|
516
|
-
if (subtree._value !== undefined) list.push(subtree._value);
|
517
|
-
});
|
518
|
-
return list;
|
519
|
-
}
|
520
|
-
|
521
|
-
if (Faye.enumEqual(path, ['**'])) {
|
522
|
-
list = this.map(function(key, value) { return value });
|
523
|
-
if (this._value !== undefined) list.pop();
|
524
|
-
return list;
|
525
|
-
}
|
526
|
-
|
527
|
-
Faye.each(this._children, function(key, subtree) {
|
528
|
-
if (key !== path[0] && key !== '*') return;
|
529
|
-
var sublist = subtree.glob(path.slice(1));
|
530
|
-
Faye.each(sublist, function(channel) { list.push(channel) });
|
531
|
-
});
|
532
|
-
|
533
|
-
if (this._children['**']) list.push(this._children['**']._value);
|
534
|
-
return list;
|
535
|
-
},
|
536
|
-
|
537
|
-
subscribe: function(names, callback, scope) {
|
538
|
-
if (!callback) return;
|
539
|
-
Faye.each(names, function(name) {
|
540
|
-
var channel = this.findOrCreate(name);
|
541
|
-
channel.addSubscriber('message', callback, scope);
|
542
|
-
}, this);
|
543
|
-
},
|
544
|
-
|
545
|
-
unsubscribe: function(name, callback, scope) {
|
546
|
-
var channel = this.get(name);
|
547
|
-
if (!channel) return false;
|
548
|
-
channel.removeSubscriber('message', callback, scope);
|
549
|
-
return channel.countSubscribers('message') === 0;
|
550
|
-
},
|
551
|
-
|
552
|
-
distributeMessage: function(message) {
|
553
|
-
var channels = this.glob(message.channel);
|
554
|
-
Faye.each(channels, function(channel) {
|
555
|
-
channel.publishEvent('message', message.data);
|
556
|
-
});
|
557
|
-
}
|
558
|
-
})
|
559
|
-
});
|
560
|
-
|
561
|
-
|
562
|
-
Faye.Subscription = Faye.Class({
|
563
|
-
initialize: function(client, channels, callback, scope) {
|
564
|
-
this._client = client;
|
565
|
-
this._channels = channels;
|
566
|
-
this._callback = callback;
|
567
|
-
this._scope = scope;
|
568
|
-
this._cancelled = false;
|
569
|
-
},
|
570
|
-
|
571
|
-
cancel: function() {
|
572
|
-
if (this._cancelled) return;
|
573
|
-
this._client.unsubscribe(this._channels, this._callback, this._scope);
|
574
|
-
this._cancelled = true;
|
575
|
-
},
|
576
|
-
|
577
|
-
unsubscribe: function() {
|
578
|
-
this.cancel();
|
579
|
-
}
|
580
|
-
});
|
581
|
-
|
582
|
-
|
583
|
-
Faye.Client = Faye.Class({
|
584
|
-
UNCONNECTED: 1,
|
585
|
-
CONNECTING: 2,
|
586
|
-
CONNECTED: 3,
|
587
|
-
DISCONNECTED: 4,
|
588
|
-
|
589
|
-
HANDSHAKE: 'handshake',
|
590
|
-
RETRY: 'retry',
|
591
|
-
NONE: 'none',
|
592
|
-
|
593
|
-
CONNECTION_TIMEOUT: 60.0,
|
594
|
-
|
595
|
-
DEFAULT_ENDPOINT: '/bayeux',
|
596
|
-
MAX_DELAY: 0.001,
|
597
|
-
INTERVAL: 0.0,
|
598
|
-
|
599
|
-
initialize: function(endpoint, options) {
|
600
|
-
this.info('New client created for ?', endpoint);
|
601
|
-
|
602
|
-
this._endpoint = endpoint || this.DEFAULT_ENDPOINT;
|
603
|
-
this._options = options || {};
|
604
|
-
|
605
|
-
this._transport = Faye.Transport.get(this, Faye.MANDATORY_CONNECTION_TYPES);
|
606
|
-
this._state = this.UNCONNECTED;
|
607
|
-
this._outbox = [];
|
608
|
-
this._channels = new Faye.Channel.Tree();
|
609
|
-
|
610
|
-
this._namespace = new Faye.Namespace();
|
611
|
-
this._responseCallbacks = {};
|
612
|
-
|
613
|
-
this._advice = {
|
614
|
-
reconnect: this.RETRY,
|
615
|
-
interval: 1000 * (this._options.interval || this.INTERVAL),
|
616
|
-
timeout: 1000 * (this._options.timeout || this.CONNECTION_TIMEOUT)
|
617
|
-
};
|
618
|
-
|
619
|
-
if (Faye.Event) Faye.Event.on(Faye.ENV, 'beforeunload',
|
620
|
-
this.disconnect, this);
|
621
|
-
},
|
622
|
-
|
623
|
-
// Request
|
624
|
-
// MUST include: * channel
|
625
|
-
// * version
|
626
|
-
// * supportedConnectionTypes
|
627
|
-
// MAY include: * minimumVersion
|
628
|
-
// * ext
|
629
|
-
// * id
|
630
|
-
//
|
631
|
-
// Success Response Failed Response
|
632
|
-
// MUST include: * channel MUST include: * channel
|
633
|
-
// * version * successful
|
634
|
-
// * supportedConnectionTypes * error
|
635
|
-
// * clientId MAY include: * supportedConnectionTypes
|
636
|
-
// * successful * advice
|
637
|
-
// MAY include: * minimumVersion * version
|
638
|
-
// * advice * minimumVersion
|
639
|
-
// * ext * ext
|
640
|
-
// * id * id
|
641
|
-
// * authSuccessful
|
642
|
-
handshake: function(callback, scope) {
|
643
|
-
if (this._advice.reconnect === this.NONE) return;
|
644
|
-
if (this._state !== this.UNCONNECTED) return;
|
645
|
-
|
646
|
-
this._state = this.CONNECTING;
|
647
|
-
var self = this;
|
648
|
-
|
649
|
-
this.info('Initiating handshake with ?', this._endpoint);
|
650
|
-
|
651
|
-
this._send({
|
652
|
-
channel: Faye.Channel.HANDSHAKE,
|
653
|
-
version: Faye.BAYEUX_VERSION,
|
654
|
-
supportedConnectionTypes: [this._transport.connectionType]
|
655
|
-
|
656
|
-
}, function(response) {
|
657
|
-
|
658
|
-
if (response.successful) {
|
659
|
-
this._state = this.CONNECTED;
|
660
|
-
this._clientId = response.clientId;
|
661
|
-
this._transport = Faye.Transport.get(this, response.supportedConnectionTypes);
|
662
|
-
|
663
|
-
this.info('Handshake successful: ?', this._clientId);
|
664
|
-
|
665
|
-
this.subscribe(this._channels.getKeys());
|
666
|
-
if (callback) callback.call(scope);
|
667
|
-
|
668
|
-
} else {
|
669
|
-
this.info('Handshake unsuccessful');
|
670
|
-
setTimeout(function() { self.handshake(callback, scope) }, this._advice.interval);
|
671
|
-
this._state = this.UNCONNECTED;
|
672
|
-
}
|
673
|
-
}, this);
|
674
|
-
},
|
675
|
-
|
676
|
-
// Request Response
|
677
|
-
// MUST include: * channel MUST include: * channel
|
678
|
-
// * clientId * successful
|
679
|
-
// * connectionType * clientId
|
680
|
-
// MAY include: * ext MAY include: * error
|
681
|
-
// * id * advice
|
682
|
-
// * ext
|
683
|
-
// * id
|
684
|
-
// * timestamp
|
685
|
-
connect: function(callback, scope) {
|
686
|
-
if (this._advice.reconnect === this.NONE) return;
|
687
|
-
if (this._state === this.DISCONNECTED) return;
|
688
|
-
|
689
|
-
if (this._state === this.UNCONNECTED)
|
690
|
-
return this.handshake(function() { this.connect(callback, scope) }, this);
|
691
|
-
|
692
|
-
this.callback(callback, scope);
|
693
|
-
if (this._state !== this.CONNECTED) return;
|
694
|
-
|
695
|
-
this.info('Calling deferred actions for ?', this._clientId);
|
696
|
-
this.setDeferredStatus('succeeded');
|
697
|
-
this.setDeferredStatus('deferred');
|
698
|
-
|
699
|
-
if (this._connectRequest) return;
|
700
|
-
this._connectRequest = true;
|
701
|
-
|
702
|
-
this.info('Initiating connection for ?', this._clientId);
|
703
|
-
|
704
|
-
this._send({
|
705
|
-
channel: Faye.Channel.CONNECT,
|
706
|
-
clientId: this._clientId,
|
707
|
-
connectionType: this._transport.connectionType
|
708
|
-
|
709
|
-
}, this._cycleConnection, this);
|
710
|
-
},
|
711
|
-
|
712
|
-
// Request Response
|
713
|
-
// MUST include: * channel MUST include: * channel
|
714
|
-
// * clientId * successful
|
715
|
-
// MAY include: * ext * clientId
|
716
|
-
// * id MAY include: * error
|
717
|
-
// * ext
|
718
|
-
// * id
|
719
|
-
disconnect: function() {
|
720
|
-
if (this._state !== this.CONNECTED) return;
|
721
|
-
this._state = this.DISCONNECTED;
|
722
|
-
|
723
|
-
this.info('Disconnecting ?', this._clientId);
|
724
|
-
|
725
|
-
this._send({
|
726
|
-
channel: Faye.Channel.DISCONNECT,
|
727
|
-
clientId: this._clientId
|
728
|
-
});
|
729
|
-
|
730
|
-
this.info('Clearing channel listeners for ?', this._clientId);
|
731
|
-
this._channels = new Faye.Channel.Tree();
|
732
|
-
},
|
733
|
-
|
734
|
-
// Request Response
|
735
|
-
// MUST include: * channel MUST include: * channel
|
736
|
-
// * clientId * successful
|
737
|
-
// * subscription * clientId
|
738
|
-
// MAY include: * ext * subscription
|
739
|
-
// * id MAY include: * error
|
740
|
-
// * advice
|
741
|
-
// * ext
|
742
|
-
// * id
|
743
|
-
// * timestamp
|
744
|
-
subscribe: function(channels, callback, scope) {
|
745
|
-
if (channels instanceof Array)
|
746
|
-
return Faye.each(channels, function(channel) {
|
747
|
-
this.subscribe(channel, callback, scope);
|
748
|
-
}, this);
|
749
|
-
|
750
|
-
this._validateChannel(channels);
|
751
|
-
|
752
|
-
this.connect(function() {
|
753
|
-
this.info('Client ? attempting to subscribe to ?', this._clientId, channels);
|
754
|
-
|
755
|
-
this._send({
|
756
|
-
channel: Faye.Channel.SUBSCRIBE,
|
757
|
-
clientId: this._clientId,
|
758
|
-
subscription: channels
|
759
|
-
|
760
|
-
}, function(response) {
|
761
|
-
if (!response.successful) return;
|
762
|
-
|
763
|
-
var channels = [].concat(response.subscription);
|
764
|
-
this.info('Subscription acknowledged for ? to ?', this._clientId, channels);
|
765
|
-
this._channels.subscribe(channels, callback, scope);
|
766
|
-
}, this);
|
767
|
-
|
768
|
-
}, this);
|
769
|
-
|
770
|
-
return new Faye.Subscription(this, channels, callback, scope);
|
771
|
-
},
|
772
|
-
|
773
|
-
// Request Response
|
774
|
-
// MUST include: * channel MUST include: * channel
|
775
|
-
// * clientId * successful
|
776
|
-
// * subscription * clientId
|
777
|
-
// MAY include: * ext * subscription
|
778
|
-
// * id MAY include: * error
|
779
|
-
// * advice
|
780
|
-
// * ext
|
781
|
-
// * id
|
782
|
-
// * timestamp
|
783
|
-
unsubscribe: function(channels, callback, scope) {
|
784
|
-
if (channels instanceof Array)
|
785
|
-
return Faye.each(channels, function(channel) {
|
786
|
-
this.unsubscribe(channel, callback, scope);
|
787
|
-
}, this);
|
788
|
-
|
789
|
-
this._validateChannel(channels);
|
790
|
-
|
791
|
-
var dead = this._channels.unsubscribe(channels, callback, scope);
|
792
|
-
if (!dead) return;
|
793
|
-
|
794
|
-
this.connect(function() {
|
795
|
-
this.info('Client ? attempting to unsubscribe from ?', this._clientId, channels);
|
796
|
-
|
797
|
-
this._send({
|
798
|
-
channel: Faye.Channel.UNSUBSCRIBE,
|
799
|
-
clientId: this._clientId,
|
800
|
-
subscription: channels
|
801
|
-
|
802
|
-
}, function(response) {
|
803
|
-
if (!response.successful) return;
|
804
|
-
|
805
|
-
var channels = [].concat(response.subscription);
|
806
|
-
this.info('Unsubscription acknowledged for ? from ?', this._clientId, channels);
|
807
|
-
}, this);
|
808
|
-
|
809
|
-
}, this);
|
810
|
-
},
|
811
|
-
|
812
|
-
// Request Response
|
813
|
-
// MUST include: * channel MUST include: * channel
|
814
|
-
// * data * successful
|
815
|
-
// MAY include: * clientId MAY include: * id
|
816
|
-
// * id * error
|
817
|
-
// * ext * ext
|
818
|
-
publish: function(channel, data) {
|
819
|
-
this._validateChannel(channel);
|
820
|
-
|
821
|
-
this.connect(function() {
|
822
|
-
this.info('Client ? queueing published message to ?: ?', this._clientId, channel, data);
|
823
|
-
|
824
|
-
this._send({
|
825
|
-
channel: channel,
|
826
|
-
data: data,
|
827
|
-
clientId: this._clientId
|
828
|
-
});
|
829
|
-
}, this);
|
830
|
-
},
|
831
|
-
|
832
|
-
receiveMessage: function(message) {
|
833
|
-
this.pipeThroughExtensions('incoming', message, function(message) {
|
834
|
-
if (!message) return;
|
835
|
-
|
836
|
-
if (message.advice) this._handleAdvice(message.advice);
|
837
|
-
|
838
|
-
var callback = this._responseCallbacks[message.id];
|
839
|
-
if (callback) {
|
840
|
-
delete this._responseCallbacks[message.id];
|
841
|
-
callback[0].call(callback[1], message);
|
842
|
-
}
|
843
|
-
|
844
|
-
this._deliverMessage(message);
|
845
|
-
}, this);
|
846
|
-
},
|
847
|
-
|
848
|
-
_handleAdvice: function(advice) {
|
849
|
-
Faye.extend(this._advice, advice);
|
850
|
-
|
851
|
-
if (this._advice.reconnect === this.HANDSHAKE && this._state !== this.DISCONNECTED) {
|
852
|
-
this._state = this.UNCONNECTED;
|
853
|
-
this._clientId = null;
|
854
|
-
this._cycleConnection();
|
855
|
-
}
|
856
|
-
},
|
857
|
-
|
858
|
-
_deliverMessage: function(message) {
|
859
|
-
if (!message.channel || !message.data) return;
|
860
|
-
this.info('Client ? calling listeners for ? with ?', this._clientId, message.channel, message.data);
|
861
|
-
this._channels.distributeMessage(message);
|
862
|
-
},
|
863
|
-
|
864
|
-
_teardownConnection: function() {
|
865
|
-
if (!this._connectRequest) return;
|
866
|
-
this._connectRequest = null;
|
867
|
-
this.info('Closed connection for ?', this._clientId);
|
868
|
-
},
|
869
|
-
|
870
|
-
_cycleConnection: function() {
|
871
|
-
this._teardownConnection();
|
872
|
-
var self = this;
|
873
|
-
setTimeout(function() { self.connect() }, this._advice.interval);
|
874
|
-
},
|
875
|
-
|
876
|
-
_send: function(message, callback, scope) {
|
877
|
-
message.id = this._namespace.generate();
|
878
|
-
if (callback) this._responseCallbacks[message.id] = [callback, scope];
|
879
|
-
|
880
|
-
this.pipeThroughExtensions('outgoing', message, function(message) {
|
881
|
-
if (!message) return;
|
882
|
-
|
883
|
-
if (message.channel === Faye.Channel.HANDSHAKE)
|
884
|
-
return this._transport.send(message, this._advice.timeout / 1000);
|
885
|
-
|
886
|
-
this._outbox.push(message);
|
887
|
-
|
888
|
-
if (message.channel === Faye.Channel.CONNECT)
|
889
|
-
this._connectMessage = message;
|
890
|
-
|
891
|
-
this.addTimeout('publish', this.MAX_DELAY, this._flush, this);
|
892
|
-
}, this);
|
893
|
-
},
|
894
|
-
|
895
|
-
_flush: function() {
|
896
|
-
this.removeTimeout('publish');
|
897
|
-
|
898
|
-
if (this._outbox.length > 1 && this._connectMessage)
|
899
|
-
this._connectMessage.advice = {timeout: 0};
|
900
|
-
|
901
|
-
this._connectMessage = null;
|
902
|
-
|
903
|
-
this._transport.send(this._outbox, this._advice.timeout / 1000);
|
904
|
-
this._outbox = [];
|
905
|
-
},
|
906
|
-
|
907
|
-
_validateChannel: function(channel) {
|
908
|
-
if (!Faye.Channel.isValid(channel))
|
909
|
-
throw '"' + channel + '" is not a valid channel name';
|
910
|
-
if (!Faye.Channel.isSubscribable(channel))
|
911
|
-
throw 'Clients may not subscribe to channel "' + channel + '"';
|
912
|
-
}
|
913
|
-
});
|
914
|
-
|
915
|
-
Faye.extend(Faye.Client.prototype, Faye.Deferrable);
|
916
|
-
Faye.extend(Faye.Client.prototype, Faye.Timeouts);
|
917
|
-
Faye.extend(Faye.Client.prototype, Faye.Logging);
|
918
|
-
Faye.extend(Faye.Client.prototype, Faye.Extensible);
|
919
|
-
|
920
|
-
|
921
|
-
Faye.Transport = Faye.extend(Faye.Class({
|
922
|
-
initialize: function(client, endpoint) {
|
923
|
-
this.debug('Created new ? transport for ?', this.connectionType, endpoint);
|
924
|
-
this._client = client;
|
925
|
-
this._endpoint = endpoint;
|
926
|
-
},
|
927
|
-
|
928
|
-
send: function(messages, timeout) {
|
929
|
-
messages = [].concat(messages);
|
930
|
-
|
931
|
-
this.debug('Client ? sending message to ?: ?',
|
932
|
-
this._client._clientId, this._endpoint, messages);
|
933
|
-
|
934
|
-
return this.request(messages, timeout);
|
935
|
-
},
|
936
|
-
|
937
|
-
receive: function(responses) {
|
938
|
-
this.debug('Client ? received from ?: ?',
|
939
|
-
this._client._clientId, this._endpoint, responses);
|
940
|
-
|
941
|
-
Faye.each(responses, this._client.receiveMessage, this._client);
|
942
|
-
},
|
943
|
-
|
944
|
-
retry: function(message, timeout) {
|
945
|
-
var self = this;
|
946
|
-
return function() {
|
947
|
-
setTimeout(function() { self.request(message, 2 * timeout) }, 1000 * timeout);
|
948
|
-
};
|
949
|
-
}
|
950
|
-
|
951
|
-
}), {
|
952
|
-
get: function(client, connectionTypes) {
|
953
|
-
var endpoint = client._endpoint;
|
954
|
-
if (connectionTypes === undefined) connectionTypes = this.supportedConnectionTypes();
|
955
|
-
|
956
|
-
var candidateClass = null;
|
957
|
-
Faye.each(this._transports, function(pair) {
|
958
|
-
var connType = pair[0], klass = pair[1];
|
959
|
-
if (Faye.indexOf(connectionTypes, connType) < 0) return;
|
960
|
-
if (candidateClass) return;
|
961
|
-
if (klass.isUsable(endpoint)) candidateClass = klass;
|
962
|
-
});
|
963
|
-
|
964
|
-
if (!candidateClass) throw 'Could not find a usable connection type for ' + endpoint;
|
965
|
-
|
966
|
-
return new candidateClass(client, endpoint);
|
967
|
-
},
|
968
|
-
|
969
|
-
register: function(type, klass) {
|
970
|
-
this._transports.push([type, klass]);
|
971
|
-
klass.prototype.connectionType = type;
|
972
|
-
},
|
973
|
-
|
974
|
-
_transports: [],
|
975
|
-
|
976
|
-
supportedConnectionTypes: function() {
|
977
|
-
return Faye.map(this._transports, function(pair) { return pair[0] });
|
978
|
-
}
|
979
|
-
});
|
980
|
-
|
981
|
-
Faye.extend(Faye.Transport.prototype, Faye.Logging);
|
982
|
-
|
983
|
-
|
984
|
-
Faye.Set = Faye.Class({
|
985
|
-
initialize: function() {
|
986
|
-
this._index = {};
|
987
|
-
},
|
988
|
-
|
989
|
-
add: function(item) {
|
990
|
-
var key = (item.id !== undefined) ? item.id : item;
|
991
|
-
if (this._index.hasOwnProperty(key)) return false;
|
992
|
-
this._index[key] = item;
|
993
|
-
return true;
|
994
|
-
},
|
995
|
-
|
996
|
-
forEach: function(block, scope) {
|
997
|
-
for (var key in this._index) {
|
998
|
-
if (this._index.hasOwnProperty(key))
|
999
|
-
block.call(scope, this._index[key]);
|
1000
|
-
}
|
1001
|
-
},
|
1002
|
-
|
1003
|
-
isEmpty: function() {
|
1004
|
-
for (var key in this._index) {
|
1005
|
-
if (this._index.hasOwnProperty(key)) return false;
|
1006
|
-
}
|
1007
|
-
return true;
|
1008
|
-
},
|
1009
|
-
|
1010
|
-
member: function(item) {
|
1011
|
-
for (var key in this._index) {
|
1012
|
-
if (this._index[key] === item) return true;
|
1013
|
-
}
|
1014
|
-
return false;
|
1015
|
-
},
|
1016
|
-
|
1017
|
-
remove: function(item) {
|
1018
|
-
var key = (item.id !== undefined) ? item.id : item;
|
1019
|
-
delete this._index[key];
|
1020
|
-
},
|
1021
|
-
|
1022
|
-
toArray: function() {
|
1023
|
-
var array = [];
|
1024
|
-
this.forEach(function(item) { array.push(item) });
|
1025
|
-
return array;
|
1026
|
-
}
|
1027
|
-
});
|
1028
|
-
|
1029
|
-
|
1030
|
-
/**
|
1031
|
-
* Generic WebSocket implementation for Node
|
1032
|
-
* -----------------------------------------
|
1033
|
-
*
|
1034
|
-
* Though primarily here to support WebSockets as a network
|
1035
|
-
* transport in Faye, it would be nice for this class to
|
1036
|
-
* implement the same interface as the client-side WebSocket
|
1037
|
-
* for ease of use.
|
1038
|
-
*
|
1039
|
-
* For implementation reference:
|
1040
|
-
* http://dev.w3.org/html5/websockets/
|
1041
|
-
* http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-75
|
1042
|
-
* http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
|
1043
|
-
* http://www.w3.org/TR/DOM-Level-2-Events/events.html
|
1044
|
-
**/
|
1045
|
-
|
1046
|
-
var Buffer = require('buffer').Buffer,
|
1047
|
-
crypto = require('crypto');
|
1048
|
-
|
1049
|
-
Faye.WebSocket = Faye.Class({
|
1050
|
-
onopen: null,
|
1051
|
-
onmessage: null,
|
1052
|
-
onerror: null,
|
1053
|
-
onclose: null,
|
1054
|
-
|
1055
|
-
initialize: function(request, head) {
|
1056
|
-
this._request = request;
|
1057
|
-
this._head = head;
|
1058
|
-
this._stream = request.socket;
|
1059
|
-
|
1060
|
-
this.url = 'ws://' + request.headers.host + request.url;
|
1061
|
-
this.readyState = Faye.WebSocket.CONNECTING;
|
1062
|
-
this.bufferedAmount = 0;
|
1063
|
-
|
1064
|
-
this._handler = Faye.WebSocket.getHandler(request);
|
1065
|
-
this._handler.handshake(this.url, this._request, this._head, this._stream);
|
1066
|
-
this.readyState = Faye.WebSocket.OPEN;
|
1067
|
-
|
1068
|
-
var event = new Faye.WebSocket.Event();
|
1069
|
-
event.initEvent('open', false, false);
|
1070
|
-
this.dispatchEvent(event);
|
1071
|
-
|
1072
|
-
this._buffer = [];
|
1073
|
-
this._buffering = false;
|
1074
|
-
|
1075
|
-
var self = this;
|
1076
|
-
|
1077
|
-
this._stream.addListener('data', function(data) {
|
1078
|
-
for (var i = 0, n = data.length; i < n; i++)
|
1079
|
-
self._handleChar(data[i]);
|
1080
|
-
});
|
1081
|
-
},
|
1082
|
-
|
1083
|
-
send: function(data) {
|
1084
|
-
this._handler.send(this._stream, data);
|
1085
|
-
return true;
|
1086
|
-
},
|
1087
|
-
|
1088
|
-
close: function() {},
|
1089
|
-
|
1090
|
-
addEventListener: function(type, listener, useCapture) {
|
1091
|
-
this.addSubscriber(type, listener);
|
1092
|
-
},
|
1093
|
-
|
1094
|
-
removeEventListener: function(type, listener, useCapture) {
|
1095
|
-
this.removeSubscriber(type, listener);
|
1096
|
-
},
|
1097
|
-
|
1098
|
-
dispatchEvent: function(event) {
|
1099
|
-
event.target = event.currentTarget = this;
|
1100
|
-
event.eventPhase = Faye.WebSocket.Event.AT_TARGET;
|
1101
|
-
|
1102
|
-
this.publishEvent(event.type, event);
|
1103
|
-
if (this['on' + event.type])
|
1104
|
-
this['on' + event.type](event);
|
1105
|
-
},
|
1106
|
-
|
1107
|
-
_handleChar: function(data) {
|
1108
|
-
switch (data) {
|
1109
|
-
case 0x00:
|
1110
|
-
this._buffering = true;
|
1111
|
-
break;
|
1112
|
-
|
1113
|
-
case 0xFF:
|
1114
|
-
this._buffer = new Buffer(this._buffer);
|
1115
|
-
|
1116
|
-
var event = new Faye.WebSocket.Event();
|
1117
|
-
event.initEvent('message', false, false);
|
1118
|
-
event.data = this._buffer.toString('utf8', 0, this._buffer.length);
|
1119
|
-
|
1120
|
-
this.dispatchEvent(event);
|
1121
|
-
|
1122
|
-
this._buffer = [];
|
1123
|
-
this._buffering = false;
|
1124
|
-
break;
|
1125
|
-
|
1126
|
-
default:
|
1127
|
-
if (this._buffering) this._buffer.push(data);
|
1128
|
-
}
|
1129
|
-
}
|
1130
|
-
});
|
1131
|
-
|
1132
|
-
Faye.extend(Faye.WebSocket.prototype, Faye.Publisher);
|
1133
|
-
|
1134
|
-
Faye.extend(Faye.WebSocket, {
|
1135
|
-
CONNECTING: 0,
|
1136
|
-
OPEN: 1,
|
1137
|
-
CLOSING: 2,
|
1138
|
-
CLOSED: 3,
|
1139
|
-
|
1140
|
-
Event: Faye.extend(Faye.Class({
|
1141
|
-
initEvent: function(eventType, canBubble, cancelable) {
|
1142
|
-
this.type = eventType;
|
1143
|
-
this.bubbles = canBubble;
|
1144
|
-
this.cancelable = cancelable;
|
1145
|
-
},
|
1146
|
-
|
1147
|
-
stopPropagation: function() {},
|
1148
|
-
preventDefault: function() {}
|
1149
|
-
|
1150
|
-
}), {
|
1151
|
-
CAPTURING_PHASE: 1,
|
1152
|
-
AT_TARGET: 2,
|
1153
|
-
BUBBLING_PHASE: 3
|
1154
|
-
}),
|
1155
|
-
|
1156
|
-
getHandler: function(request) {
|
1157
|
-
var headers = request.headers;
|
1158
|
-
return (headers['sec-websocket-key1'] && headers['sec-websocket-key2'])
|
1159
|
-
? this.Protocol76
|
1160
|
-
: this.Protocol75;
|
1161
|
-
}
|
1162
|
-
});
|
1163
|
-
|
1164
|
-
(function() {
|
1165
|
-
var byteToChar = function(value) {
|
1166
|
-
if (typeof value === 'string') value = parseInt(value, 16);
|
1167
|
-
return String.fromCharCode(value);
|
1168
|
-
};
|
1169
|
-
|
1170
|
-
var numberFromKey = function(key) {
|
1171
|
-
return parseInt(key.match(/[0-9]/g).join(''), 10);
|
1172
|
-
};
|
1173
|
-
|
1174
|
-
var spacesInKey = function(key) {
|
1175
|
-
return key.match(/ /g).length;
|
1176
|
-
};
|
1177
|
-
|
1178
|
-
var bigEndian = function(number) {
|
1179
|
-
var string = '';
|
1180
|
-
Faye.each([24,16,8,0], function(offset) {
|
1181
|
-
string += String.fromCharCode(number >> offset & 0xFF);
|
1182
|
-
});
|
1183
|
-
return string;
|
1184
|
-
};
|
1185
|
-
|
1186
|
-
var writeToSocket = function(socket, message) {
|
1187
|
-
try {
|
1188
|
-
socket.write(FRAME_START, 'binary');
|
1189
|
-
socket.write(message, 'utf8');
|
1190
|
-
socket.write(FRAME_END, 'binary');
|
1191
|
-
} catch (e) {
|
1192
|
-
// socket closed while writing
|
1193
|
-
}
|
1194
|
-
};
|
1195
|
-
|
1196
|
-
var FRAME_START = byteToChar('00'),
|
1197
|
-
FRAME_END = byteToChar('FF');
|
1198
|
-
|
1199
|
-
Faye.WebSocket.Protocol75 = {
|
1200
|
-
handshake: function(url, request, head, socket) {
|
1201
|
-
socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n');
|
1202
|
-
socket.write('Upgrade: WebSocket\r\n');
|
1203
|
-
socket.write('Connection: Upgrade\r\n');
|
1204
|
-
socket.write('WebSocket-Origin: ' + request.headers.origin + '\r\n');
|
1205
|
-
socket.write('WebSocket-Location: ' + url + '\r\n');
|
1206
|
-
socket.write('\r\n');
|
1207
|
-
},
|
1208
|
-
|
1209
|
-
send: function(socket, message) {
|
1210
|
-
writeToSocket(socket, message);
|
1211
|
-
}
|
1212
|
-
};
|
1213
|
-
|
1214
|
-
Faye.WebSocket.Protocol76 = {
|
1215
|
-
handshake: function(url, request, head, socket) {
|
1216
|
-
var key1 = request.headers['sec-websocket-key1'],
|
1217
|
-
value1 = numberFromKey(key1) / spacesInKey(key1),
|
1218
|
-
|
1219
|
-
key2 = request.headers['sec-websocket-key2'],
|
1220
|
-
value2 = numberFromKey(key2) / spacesInKey(key2),
|
1221
|
-
|
1222
|
-
MD5 = crypto.createHash('md5');
|
1223
|
-
|
1224
|
-
MD5.update(bigEndian(value1));
|
1225
|
-
MD5.update(bigEndian(value2));
|
1226
|
-
MD5.update(head.toString('binary'));
|
1227
|
-
|
1228
|
-
socket.write('HTTP/1.1 101 Web Socket Protocol Handshake\r\n', 'binary');
|
1229
|
-
socket.write('Upgrade: WebSocket\r\n', 'binary');
|
1230
|
-
socket.write('Connection: Upgrade\r\n', 'binary');
|
1231
|
-
socket.write('Sec-WebSocket-Origin: ' + request.headers.origin + '\r\n', 'binary');
|
1232
|
-
socket.write('Sec-WebSocket-Location: ' + url + '\r\n', 'binary');
|
1233
|
-
socket.write('\r\n', 'binary');
|
1234
|
-
socket.write(MD5.digest('binary'), 'binary');
|
1235
|
-
},
|
1236
|
-
|
1237
|
-
send: function(socket, message) {
|
1238
|
-
writeToSocket(socket, message);
|
1239
|
-
}
|
1240
|
-
}
|
1241
|
-
})();
|
1242
|
-
|
1243
|
-
|
1244
|
-
Faye.Error = Faye.Class({
|
1245
|
-
initialize: function(code, args, message) {
|
1246
|
-
this.code = code;
|
1247
|
-
this.args = Array.prototype.slice.call(args);
|
1248
|
-
this.message = message;
|
1249
|
-
},
|
1250
|
-
|
1251
|
-
toString: function() {
|
1252
|
-
return this.code + ':' +
|
1253
|
-
this.args.join(',') + ':' +
|
1254
|
-
this.message;
|
1255
|
-
}
|
1256
|
-
});
|
1257
|
-
|
1258
|
-
|
1259
|
-
Faye.Error.versionMismatch = function() {
|
1260
|
-
return new this(300, arguments, "Version mismatch").toString();
|
1261
|
-
};
|
1262
|
-
|
1263
|
-
Faye.Error.conntypeMismatch = function() {
|
1264
|
-
return new this(301, arguments, "Connection types not supported").toString();
|
1265
|
-
};
|
1266
|
-
|
1267
|
-
Faye.Error.extMismatch = function() {
|
1268
|
-
return new this(302, arguments, "Extension mismatch").toString();
|
1269
|
-
};
|
1270
|
-
|
1271
|
-
Faye.Error.badRequest = function() {
|
1272
|
-
return new this(400, arguments, "Bad request").toString();
|
1273
|
-
};
|
1274
|
-
|
1275
|
-
Faye.Error.clientUnknown = function() {
|
1276
|
-
return new this(401, arguments, "Unknown client").toString();
|
1277
|
-
};
|
1278
|
-
|
1279
|
-
Faye.Error.parameterMissing = function() {
|
1280
|
-
return new this(402, arguments, "Missing required parameter").toString();
|
1281
|
-
};
|
1282
|
-
|
1283
|
-
Faye.Error.channelForbidden = function() {
|
1284
|
-
return new this(403, arguments, "Forbidden channel").toString();
|
1285
|
-
};
|
1286
|
-
|
1287
|
-
Faye.Error.channelUnknown = function() {
|
1288
|
-
return new this(404, arguments, "Unknown channel").toString();
|
1289
|
-
};
|
1290
|
-
|
1291
|
-
Faye.Error.channelInvalid = function() {
|
1292
|
-
return new this(405, arguments, "Invalid channel").toString();
|
1293
|
-
};
|
1294
|
-
|
1295
|
-
Faye.Error.extUnknown = function() {
|
1296
|
-
return new this(406, arguments, "Unknown extension").toString();
|
1297
|
-
};
|
1298
|
-
|
1299
|
-
Faye.Error.publishFailed = function() {
|
1300
|
-
return new this(407, arguments, "Failed to publish").toString();
|
1301
|
-
};
|
1302
|
-
|
1303
|
-
Faye.Error.serverError = function() {
|
1304
|
-
return new this(500, arguments, "Internal server error").toString();
|
1305
|
-
};
|
1306
|
-
|
1307
|
-
|
1308
|
-
|
1309
|
-
Faye.Server = Faye.Class({
|
1310
|
-
initialize: function(options) {
|
1311
|
-
this.info('New server created');
|
1312
|
-
this._options = options || {};
|
1313
|
-
this._channels = new Faye.Channel.Tree();
|
1314
|
-
this._connections = {};
|
1315
|
-
this._namespace = new Faye.Namespace();
|
1316
|
-
},
|
1317
|
-
|
1318
|
-
clientIds: function() {
|
1319
|
-
return Faye.map(this._connections, function(key, value) { return key });
|
1320
|
-
},
|
1321
|
-
|
1322
|
-
process: function(messages, localOrRemote, callback, scope) {
|
1323
|
-
var socket = (localOrRemote instanceof Faye.WebSocket) ? localOrRemote : null,
|
1324
|
-
local = (localOrRemote === true);
|
1325
|
-
|
1326
|
-
this.debug('Processing messages from ? client', local ? 'LOCAL' : 'REMOTE');
|
1327
|
-
|
1328
|
-
messages = [].concat(messages);
|
1329
|
-
var processed = 0, responses = [];
|
1330
|
-
|
1331
|
-
var gatherReplies = function(replies) {
|
1332
|
-
responses = responses.concat(replies);
|
1333
|
-
processed += 1;
|
1334
|
-
if (processed < messages.length) return;
|
1335
|
-
|
1336
|
-
var n = responses.length;
|
1337
|
-
while (n--) {
|
1338
|
-
if (!responses[n]) responses.splice(n,1);
|
1339
|
-
}
|
1340
|
-
callback.call(scope, responses);
|
1341
|
-
};
|
1342
|
-
|
1343
|
-
var handleReply = function(replies) {
|
1344
|
-
var extended = 0, expected = replies.length;
|
1345
|
-
if (expected === 0) gatherReplies(replies);
|
1346
|
-
|
1347
|
-
Faye.each(replies, function(reply, i) {
|
1348
|
-
this.pipeThroughExtensions('outgoing', reply, function(message) {
|
1349
|
-
replies[i] = message;
|
1350
|
-
extended += 1;
|
1351
|
-
if (extended === expected) gatherReplies(replies);
|
1352
|
-
});
|
1353
|
-
}, this);
|
1354
|
-
};
|
1355
|
-
|
1356
|
-
Faye.each(messages, function(message) {
|
1357
|
-
this.pipeThroughExtensions('incoming', message, function(pipedMessage) {
|
1358
|
-
this._handle(pipedMessage, socket, local, handleReply, this);
|
1359
|
-
}, this);
|
1360
|
-
}, this);
|
1361
|
-
},
|
1362
|
-
|
1363
|
-
flushConnection: function(messages) {
|
1364
|
-
messages = [].concat(messages);
|
1365
|
-
Faye.each(messages, function(message) {
|
1366
|
-
var connection = this._connections[message.clientId];
|
1367
|
-
if (connection) connection.flush();
|
1368
|
-
}, this);
|
1369
|
-
},
|
1370
|
-
|
1371
|
-
_connection: function(id) {
|
1372
|
-
if (this._connections.hasOwnProperty(id)) return this._connections[id];
|
1373
|
-
var connection = new Faye.Connection(id, this._options);
|
1374
|
-
connection.addSubscriber('staleConnection', this._destroyConnection, this);
|
1375
|
-
return this._connections[id] = connection;
|
1376
|
-
},
|
1377
|
-
|
1378
|
-
_destroyConnection: function(connection) {
|
1379
|
-
connection.disconnect();
|
1380
|
-
connection.removeSubscriber('staleConnection', this._destroyConnection, this);
|
1381
|
-
delete this._connections[connection.id];
|
1382
|
-
},
|
1383
|
-
|
1384
|
-
_makeResponse: function(message) {
|
1385
|
-
var response = {};
|
1386
|
-
Faye.each(['id', 'clientId', 'channel', 'error'], function(field) {
|
1387
|
-
if (message[field]) response[field] = message[field];
|
1388
|
-
});
|
1389
|
-
response.successful = !response.error;
|
1390
|
-
return response;
|
1391
|
-
},
|
1392
|
-
|
1393
|
-
_distributeMessage: function(message) {
|
1394
|
-
Faye.each(this._channels.glob(message.channel), function(channel) {
|
1395
|
-
channel.push(message);
|
1396
|
-
this.info('Publishing message ? from client ? to ?', message.data, message.clientId, channel.name);
|
1397
|
-
}, this);
|
1398
|
-
},
|
1399
|
-
|
1400
|
-
_handle: function(message, socket, local, callback, scope) {
|
1401
|
-
if (!message) return callback.call(scope, []);
|
1402
|
-
if (message.error) return callback.call(scope, [this._makeResponse(message)]);
|
1403
|
-
|
1404
|
-
this._distributeMessage(message);
|
1405
|
-
var channelName = message.channel, response;
|
1406
|
-
|
1407
|
-
if (Faye.Channel.isMeta(channelName)) {
|
1408
|
-
this._handleMeta(message, socket, local, callback, scope);
|
1409
|
-
} else if (!message.clientId) {
|
1410
|
-
callback.call(scope, []);
|
1411
|
-
} else {
|
1412
|
-
response = this._makeResponse(message);
|
1413
|
-
response.successful = true;
|
1414
|
-
callback.call(scope, [response]);
|
1415
|
-
}
|
1416
|
-
},
|
1417
|
-
|
1418
|
-
_handleMeta: function(message, socket, local, callback, scope) {
|
1419
|
-
var response = this[Faye.Channel.parse(message.channel)[1]](message, local);
|
1420
|
-
|
1421
|
-
this._advize(response);
|
1422
|
-
|
1423
|
-
if (response.channel === Faye.Channel.CONNECT && response.successful === true)
|
1424
|
-
return this._acceptConnection(message.advice, response, socket, callback, scope);
|
1425
|
-
|
1426
|
-
callback.call(scope, [response]);
|
1427
|
-
},
|
1428
|
-
|
1429
|
-
_acceptConnection: function(options, response, socket, callback, scope) {
|
1430
|
-
this.info('Accepting connection from ?', response.clientId);
|
1431
|
-
|
1432
|
-
var connection = this._connection(response.clientId);
|
1433
|
-
|
1434
|
-
// Disabled because CometD doesn't like messages not being
|
1435
|
-
// delivered as part of a /meta/* response
|
1436
|
-
// if (socket) return connection.setSocket(socket);
|
1437
|
-
|
1438
|
-
connection.connect(options, function(events) {
|
1439
|
-
this.info('Sending event messages to ?', response.clientId);
|
1440
|
-
this.debug('Events for ?: ?', response.clientId, events);
|
1441
|
-
callback.call(scope, [response].concat(events));
|
1442
|
-
}, this);
|
1443
|
-
},
|
1444
|
-
|
1445
|
-
_advize: function(response) {
|
1446
|
-
var connection = this._connections[response.clientId];
|
1447
|
-
|
1448
|
-
response.advice = response.advice || {};
|
1449
|
-
if (connection) {
|
1450
|
-
Faye.extend(response.advice, {
|
1451
|
-
reconnect: 'retry',
|
1452
|
-
interval: Math.floor(connection.interval * 1000),
|
1453
|
-
timeout: Math.floor(connection.timeout * 1000)
|
1454
|
-
}, false);
|
1455
|
-
} else {
|
1456
|
-
Faye.extend(response.advice, {
|
1457
|
-
reconnect: 'handshake'
|
1458
|
-
}, false);
|
1459
|
-
}
|
1460
|
-
},
|
1461
|
-
|
1462
|
-
// MUST contain * version
|
1463
|
-
// * supportedConnectionTypes
|
1464
|
-
// MAY contain * minimumVersion
|
1465
|
-
// * ext
|
1466
|
-
// * id
|
1467
|
-
handshake: function(message, local) {
|
1468
|
-
var response = this._makeResponse(message);
|
1469
|
-
response.version = Faye.BAYEUX_VERSION;
|
1470
|
-
|
1471
|
-
if (!message.version)
|
1472
|
-
response.error = Faye.Error.parameterMissing('version');
|
1473
|
-
|
1474
|
-
var clientConns = message.supportedConnectionTypes,
|
1475
|
-
commonConns;
|
1476
|
-
|
1477
|
-
if (!local) {
|
1478
|
-
response.supportedConnectionTypes = Faye.CONNECTION_TYPES;
|
1479
|
-
|
1480
|
-
if (clientConns) {
|
1481
|
-
commonConns = Faye.filter(clientConns, function(conn) {
|
1482
|
-
return Faye.indexOf(Faye.CONNECTION_TYPES, conn) !== -1;
|
1483
|
-
});
|
1484
|
-
if (commonConns.length === 0)
|
1485
|
-
response.error = Faye.Error.conntypeMismatch(clientConns);
|
1486
|
-
} else {
|
1487
|
-
response.error = Faye.Error.parameterMissing('supportedConnectionTypes');
|
1488
|
-
}
|
1489
|
-
}
|
1490
|
-
|
1491
|
-
response.successful = !response.error;
|
1492
|
-
if (!response.successful) return response;
|
1493
|
-
|
1494
|
-
var clientId = this._namespace.generate();
|
1495
|
-
response.clientId = this._connection(clientId).id;
|
1496
|
-
this.info('Accepting handshake from client ?', response.clientId);
|
1497
|
-
return response;
|
1498
|
-
},
|
1499
|
-
|
1500
|
-
// MUST contain * clientId
|
1501
|
-
// * connectionType
|
1502
|
-
// MAY contain * ext
|
1503
|
-
// * id
|
1504
|
-
connect: function(message, local) {
|
1505
|
-
var response = this._makeResponse(message);
|
1506
|
-
|
1507
|
-
var clientId = message.clientId,
|
1508
|
-
connection = clientId ? this._connections[clientId] : null,
|
1509
|
-
connectionType = message.connectionType;
|
1510
|
-
|
1511
|
-
if (!connection) response.error = Faye.Error.clientUnknown(clientId);
|
1512
|
-
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
1513
|
-
if (!connectionType) response.error = Faye.Error.parameterMissing('connectionType');
|
1514
|
-
|
1515
|
-
response.successful = !response.error;
|
1516
|
-
if (!response.successful) delete response.clientId;
|
1517
|
-
if (!response.successful) return response;
|
1518
|
-
|
1519
|
-
response.clientId = connection.id;
|
1520
|
-
return response;
|
1521
|
-
},
|
1522
|
-
|
1523
|
-
// MUST contain * clientId
|
1524
|
-
// MAY contain * ext
|
1525
|
-
// * id
|
1526
|
-
disconnect: function(message, local) {
|
1527
|
-
var response = this._makeResponse(message);
|
1528
|
-
|
1529
|
-
var clientId = message.clientId,
|
1530
|
-
connection = clientId ? this._connections[clientId] : null;
|
1531
|
-
|
1532
|
-
if (!connection) response.error = Faye.Error.clientUnknown(clientId);
|
1533
|
-
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
1534
|
-
|
1535
|
-
response.successful = !response.error;
|
1536
|
-
if (!response.successful) delete response.clientId;
|
1537
|
-
if (!response.successful) return response;
|
1538
|
-
|
1539
|
-
this._destroyConnection(connection);
|
1540
|
-
|
1541
|
-
this.info('Disconnected client: ?', clientId);
|
1542
|
-
response.clientId = clientId;
|
1543
|
-
return response;
|
1544
|
-
},
|
1545
|
-
|
1546
|
-
// MUST contain * clientId
|
1547
|
-
// * subscription
|
1548
|
-
// MAY contain * ext
|
1549
|
-
// * id
|
1550
|
-
subscribe: function(message, local) {
|
1551
|
-
var response = this._makeResponse(message);
|
1552
|
-
|
1553
|
-
var clientId = message.clientId,
|
1554
|
-
connection = clientId ? this._connections[clientId] : null,
|
1555
|
-
subscription = message.subscription;
|
1556
|
-
|
1557
|
-
subscription = [].concat(subscription);
|
1558
|
-
|
1559
|
-
if (!connection) response.error = Faye.Error.clientUnknown(clientId);
|
1560
|
-
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
1561
|
-
if (!message.subscription) response.error = Faye.Error.parameterMissing('subscription');
|
1562
|
-
|
1563
|
-
response.subscription = subscription;
|
1564
|
-
|
1565
|
-
Faye.each(subscription, function(channel) {
|
1566
|
-
if (response.error) return;
|
1567
|
-
if (!local && !Faye.Channel.isSubscribable(channel)) response.error = Faye.Error.channelForbidden(channel);
|
1568
|
-
if (!Faye.Channel.isValid(channel)) response.error = Faye.Error.channelInvalid(channel);
|
1569
|
-
|
1570
|
-
if (response.error) return;
|
1571
|
-
channel = this._channels.findOrCreate(channel);
|
1572
|
-
|
1573
|
-
this.info('Subscribing client ? to ?', clientId, channel.name);
|
1574
|
-
connection.subscribe(channel);
|
1575
|
-
}, this);
|
1576
|
-
|
1577
|
-
response.successful = !response.error;
|
1578
|
-
return response;
|
1579
|
-
},
|
1580
|
-
|
1581
|
-
// MUST contain * clientId
|
1582
|
-
// * subscription
|
1583
|
-
// MAY contain * ext
|
1584
|
-
// * id
|
1585
|
-
unsubscribe: function(message, local) {
|
1586
|
-
var response = this._makeResponse(message);
|
1587
|
-
|
1588
|
-
var clientId = message.clientId,
|
1589
|
-
connection = clientId ? this._connections[clientId] : null,
|
1590
|
-
subscription = message.subscription;
|
1591
|
-
|
1592
|
-
subscription = [].concat(subscription);
|
1593
|
-
|
1594
|
-
if (!connection) response.error = Faye.Error.clientUnknown(clientId);
|
1595
|
-
if (!clientId) response.error = Faye.Error.parameterMissing('clientId');
|
1596
|
-
if (!message.subscription) response.error = Faye.Error.parameterMissing('subscription');
|
1597
|
-
|
1598
|
-
response.subscription = subscription;
|
1599
|
-
|
1600
|
-
Faye.each(subscription, function(channel) {
|
1601
|
-
if (response.error) return;
|
1602
|
-
|
1603
|
-
if (!Faye.Channel.isValid(channel))
|
1604
|
-
return response.error = Faye.Error.channelInvalid(channel);
|
1605
|
-
|
1606
|
-
channel = this._channels.get(channel);
|
1607
|
-
if (!channel) return;
|
1608
|
-
|
1609
|
-
this.info('Unsubscribing client ? from ?', clientId, channel.name);
|
1610
|
-
connection.unsubscribe(channel);
|
1611
|
-
}, this);
|
1612
|
-
|
1613
|
-
response.successful = !response.error;
|
1614
|
-
return response;
|
1615
|
-
}
|
1616
|
-
});
|
1617
|
-
|
1618
|
-
Faye.extend(Faye.Server.prototype, Faye.Logging);
|
1619
|
-
Faye.extend(Faye.Server.prototype, Faye.Extensible);
|
1620
|
-
|
1621
|
-
|
1622
|
-
Faye.Connection = Faye.Class({
|
1623
|
-
MAX_DELAY: 0.001,
|
1624
|
-
INTERVAL: 0.0,
|
1625
|
-
TIMEOUT: 60.0,
|
1626
|
-
|
1627
|
-
initialize: function(id, options) {
|
1628
|
-
this.id = id;
|
1629
|
-
this._options = options;
|
1630
|
-
this.interval = this._options.interval || this.INTERVAL;
|
1631
|
-
this.timeout = this._options.timeout || this.TIMEOUT;
|
1632
|
-
this._channels = new Faye.Set();
|
1633
|
-
this._inbox = new Faye.Set();
|
1634
|
-
this._connected = false;
|
1635
|
-
|
1636
|
-
this._beginDeletionTimeout();
|
1637
|
-
},
|
1638
|
-
|
1639
|
-
setSocket: function(socket) {
|
1640
|
-
this._connected = true;
|
1641
|
-
this._socket = socket;
|
1642
|
-
},
|
1643
|
-
|
1644
|
-
_onMessage: function(event) {
|
1645
|
-
if (!this._inbox.add(event)) return;
|
1646
|
-
if (this._socket) this._socket.send(Faye.toJSON(event));
|
1647
|
-
this._beginDeliveryTimeout();
|
1648
|
-
},
|
1649
|
-
|
1650
|
-
subscribe: function(channel) {
|
1651
|
-
if (!this._channels.add(channel)) return;
|
1652
|
-
channel.addSubscriber('message', this._onMessage, this);
|
1653
|
-
},
|
1654
|
-
|
1655
|
-
unsubscribe: function(channel) {
|
1656
|
-
if (channel === 'all') return this._channels.forEach(this.unsubscribe, this);
|
1657
|
-
if (!this._channels.member(channel)) return;
|
1658
|
-
this._channels.remove(channel);
|
1659
|
-
channel.removeSubscriber('message', this._onMessage, this);
|
1660
|
-
},
|
1661
|
-
|
1662
|
-
connect: function(options, callback, scope) {
|
1663
|
-
options = options || {};
|
1664
|
-
var timeout = (options.timeout !== undefined) ? options.timeout / 1000 : this.timeout;
|
1665
|
-
|
1666
|
-
this.setDeferredStatus('deferred');
|
1667
|
-
|
1668
|
-
this.callback(callback, scope);
|
1669
|
-
if (this._connected) return;
|
1670
|
-
|
1671
|
-
this._connected = true;
|
1672
|
-
this.removeTimeout('deletion');
|
1673
|
-
|
1674
|
-
this._beginDeliveryTimeout();
|
1675
|
-
this._beginConnectionTimeout(timeout);
|
1676
|
-
},
|
1677
|
-
|
1678
|
-
flush: function() {
|
1679
|
-
if (!this._connected) return;
|
1680
|
-
this._releaseConnection();
|
1681
|
-
|
1682
|
-
var events = this._inbox.toArray();
|
1683
|
-
this._inbox = new Faye.Set();
|
1684
|
-
|
1685
|
-
this.setDeferredStatus('succeeded', events);
|
1686
|
-
this.setDeferredStatus('deferred');
|
1687
|
-
},
|
1688
|
-
|
1689
|
-
disconnect: function() {
|
1690
|
-
this.unsubscribe('all');
|
1691
|
-
this.flush();
|
1692
|
-
},
|
1693
|
-
|
1694
|
-
_releaseConnection: function() {
|
1695
|
-
if (this._socket) return;
|
1696
|
-
|
1697
|
-
this.removeTimeout('connection');
|
1698
|
-
this.removeTimeout('delivery');
|
1699
|
-
this._connected = false;
|
1700
|
-
|
1701
|
-
this._beginDeletionTimeout();
|
1702
|
-
},
|
1703
|
-
|
1704
|
-
_beginDeliveryTimeout: function() {
|
1705
|
-
if (!this._connected || this._inbox.isEmpty()) return;
|
1706
|
-
this.addTimeout('delivery', this.MAX_DELAY, this.flush, this);
|
1707
|
-
},
|
1708
|
-
|
1709
|
-
_beginConnectionTimeout: function(timeout) {
|
1710
|
-
if (!this._connected) return;
|
1711
|
-
this.addTimeout('connection', timeout, this.flush, this);
|
1712
|
-
},
|
1713
|
-
|
1714
|
-
_beginDeletionTimeout: function() {
|
1715
|
-
if (this._connected) return;
|
1716
|
-
this.addTimeout('deletion', this.TIMEOUT + 10 * this.timeout, function() {
|
1717
|
-
this.publishEvent('staleConnection', this);
|
1718
|
-
}, this);
|
1719
|
-
}
|
1720
|
-
});
|
1721
|
-
|
1722
|
-
Faye.extend(Faye.Connection.prototype, Faye.Deferrable);
|
1723
|
-
Faye.extend(Faye.Connection.prototype, Faye.Publisher);
|
1724
|
-
Faye.extend(Faye.Connection.prototype, Faye.Timeouts);
|
1725
|
-
|
1726
|
-
|
1727
|
-
Faye.NodeHttpTransport = Faye.Class(Faye.Transport, {
|
1728
|
-
request: function(message, timeout) {
|
1729
|
-
var uri = url.parse(this._endpoint),
|
1730
|
-
secure = (uri.protocol === 'https:'),
|
1731
|
-
client = http.createClient(uri.port, uri.hostname, secure),
|
1732
|
-
content = JSON.stringify(message),
|
1733
|
-
response = null,
|
1734
|
-
retry = this.retry(message, timeout),
|
1735
|
-
self = this;
|
1736
|
-
|
1737
|
-
client.addListener('error', retry);
|
1738
|
-
|
1739
|
-
client.addListener('end', function() {
|
1740
|
-
if (!response) retry();
|
1741
|
-
});
|
1742
|
-
|
1743
|
-
var request = client.request('POST', uri.pathname, {
|
1744
|
-
'Content-Type': 'application/json',
|
1745
|
-
'Host': uri.hostname,
|
1746
|
-
'Content-Length': content.length
|
1747
|
-
});
|
1748
|
-
|
1749
|
-
request.addListener('response', function(stream) {
|
1750
|
-
response = stream;
|
1751
|
-
Faye.withDataFor(response, function(data) {
|
1752
|
-
try {
|
1753
|
-
self.receive(JSON.parse(data));
|
1754
|
-
} catch (e) {
|
1755
|
-
retry();
|
1756
|
-
}
|
1757
|
-
});
|
1758
|
-
});
|
1759
|
-
|
1760
|
-
request.write(content);
|
1761
|
-
request.end();
|
1762
|
-
}
|
1763
|
-
});
|
1764
|
-
|
1765
|
-
Faye.NodeHttpTransport.isUsable = function(endpoint) {
|
1766
|
-
return typeof endpoint === 'string';
|
1767
|
-
};
|
1768
|
-
|
1769
|
-
Faye.Transport.register('long-polling', Faye.NodeHttpTransport);
|
1770
|
-
|
1771
|
-
Faye.NodeLocalTransport = Faye.Class(Faye.Transport, {
|
1772
|
-
request: function(message, timeout) {
|
1773
|
-
this._endpoint.process(message, true, this.receive, this);
|
1774
|
-
}
|
1775
|
-
});
|
1776
|
-
|
1777
|
-
Faye.NodeLocalTransport.isUsable = function(endpoint) {
|
1778
|
-
return endpoint instanceof Faye.Server;
|
1779
|
-
};
|
1780
|
-
|
1781
|
-
Faye.Transport.register('in-process', Faye.NodeLocalTransport);
|
1782
|
-
|
1783
|
-
|
1784
|
-
var path = require('path'),
|
1785
|
-
fs = require('fs'),
|
1786
|
-
sys = require('sys'),
|
1787
|
-
url = require('url'),
|
1788
|
-
http = require('http'),
|
1789
|
-
querystring = require('querystring');
|
1790
|
-
|
1791
|
-
Faye.logger = function(message) {
|
1792
|
-
sys.puts(message);
|
1793
|
-
};
|
1794
|
-
|
1795
|
-
Faye.withDataFor = function(transport, callback, scope) {
|
1796
|
-
var data = '';
|
1797
|
-
transport.addListener('data', function(chunk) { data += chunk });
|
1798
|
-
transport.addListener('end', function() {
|
1799
|
-
callback.call(scope, data);
|
1800
|
-
});
|
1801
|
-
};
|
1802
|
-
|
1803
|
-
Faye.NodeAdapter = Faye.Class({
|
1804
|
-
DEFAULT_ENDPOINT: '/bayeux',
|
1805
|
-
SCRIPT_PATH: path.dirname(__filename) + '/faye-browser-min.js',
|
1806
|
-
|
1807
|
-
TYPE_JSON: {'Content-Type': 'application/json'},
|
1808
|
-
TYPE_SCRIPT: {'Content-Type': 'text/javascript'},
|
1809
|
-
TYPE_TEXT: {'Content-Type': 'text/plain'},
|
1810
|
-
|
1811
|
-
initialize: function(options) {
|
1812
|
-
this._options = options || {};
|
1813
|
-
this._endpoint = this._options.mount || this.DEFAULT_ENDPOINT;
|
1814
|
-
this._endpointRe = new RegExp('^' + this._endpoint + '(/[^/]*)*(\\.js)?$');
|
1815
|
-
this._server = new Faye.Server(this._options);
|
1816
|
-
},
|
1817
|
-
|
1818
|
-
addExtension: function(extension) {
|
1819
|
-
return this._server.addExtension(extension);
|
1820
|
-
},
|
1821
|
-
|
1822
|
-
removeExtension: function(extension) {
|
1823
|
-
return this._server.removeExtension(extension);
|
1824
|
-
},
|
1825
|
-
|
1826
|
-
getClient: function() {
|
1827
|
-
return this._client = this._client || new Faye.Client(this._server);
|
1828
|
-
},
|
1829
|
-
|
1830
|
-
listen: function(port) {
|
1831
|
-
var httpServer = http.createServer(function() {});
|
1832
|
-
this.attach(httpServer);
|
1833
|
-
httpServer.listen(port);
|
1834
|
-
},
|
1835
|
-
|
1836
|
-
attach: function(httpServer) {
|
1837
|
-
this._overrideListeners(httpServer, 'request', 'handle');
|
1838
|
-
this._overrideListeners(httpServer, 'upgrade', 'handleUpgrade');
|
1839
|
-
},
|
1840
|
-
|
1841
|
-
_overrideListeners: function(httpServer, event, method) {
|
1842
|
-
var listeners = httpServer.listeners(event),
|
1843
|
-
self = this;
|
1844
|
-
|
1845
|
-
httpServer.removeAllListeners(event);
|
1846
|
-
|
1847
|
-
httpServer.addListener(event, function(request) {
|
1848
|
-
if (self.check(request)) return self[method].apply(self, arguments);
|
1849
|
-
|
1850
|
-
for (var i = 0, n = listeners.length; i < n; i++)
|
1851
|
-
listeners[i].apply(this, arguments);
|
1852
|
-
});
|
1853
|
-
},
|
1854
|
-
|
1855
|
-
check: function(request) {
|
1856
|
-
var path = url.parse(request.url, true).pathname;
|
1857
|
-
return !!this._endpointRe.test(path);
|
1858
|
-
},
|
1859
|
-
|
1860
|
-
handle: function(request, response) {
|
1861
|
-
var requestUrl = url.parse(request.url, true),
|
1862
|
-
self = this, data;
|
1863
|
-
|
1864
|
-
if (/\.js$/.test(requestUrl.pathname)) {
|
1865
|
-
fs.readFile(this.SCRIPT_PATH, function(err, content) {
|
1866
|
-
response.writeHead(200, self.TYPE_SCRIPT);
|
1867
|
-
response.write(content);
|
1868
|
-
response.end();
|
1869
|
-
});
|
1870
|
-
|
1871
|
-
} else {
|
1872
|
-
var isGet = (request.method === 'GET');
|
1873
|
-
|
1874
|
-
if (isGet)
|
1875
|
-
this._callWithParams(request, response, requestUrl.query);
|
1876
|
-
|
1877
|
-
else
|
1878
|
-
Faye.withDataFor(request, function(data) {
|
1879
|
-
self._callWithParams(request, response, {message: data});
|
1880
|
-
});
|
1881
|
-
}
|
1882
|
-
return true;
|
1883
|
-
},
|
1884
|
-
|
1885
|
-
handleUpgrade: function(request, socket, head) {
|
1886
|
-
var socket = new Faye.WebSocket(request, head),
|
1887
|
-
self = this;
|
1888
|
-
|
1889
|
-
socket.onmessage = function(message) {
|
1890
|
-
try {
|
1891
|
-
var message = JSON.parse(message.data);
|
1892
|
-
self._server.process(message, socket, function(replies) {
|
1893
|
-
socket.send(JSON.stringify(replies));
|
1894
|
-
});
|
1895
|
-
} catch (e) {}
|
1896
|
-
};
|
1897
|
-
},
|
1898
|
-
|
1899
|
-
_callWithParams: function(request, response, params) {
|
1900
|
-
try {
|
1901
|
-
var message = JSON.parse(params.message),
|
1902
|
-
jsonp = params.jsonp || Faye.JSONP_CALLBACK,
|
1903
|
-
isGet = (request.method === 'GET'),
|
1904
|
-
type = isGet ? this.TYPE_SCRIPT : this.TYPE_JSON;
|
1905
|
-
|
1906
|
-
if (isGet) this._server.flushConnection(message);
|
1907
|
-
|
1908
|
-
this._server.process(message, false, function(replies) {
|
1909
|
-
var body = JSON.stringify(replies);
|
1910
|
-
if (isGet) body = jsonp + '(' + body + ');';
|
1911
|
-
response.writeHead(200, type);
|
1912
|
-
response.write(body);
|
1913
|
-
response.end();
|
1914
|
-
});
|
1915
|
-
} catch (e) {
|
1916
|
-
response.writeHead(400, this.TYPE_TEXT);
|
1917
|
-
response.write('Bad request');
|
1918
|
-
response.end();
|
1919
|
-
}
|
1920
|
-
}
|
1921
|
-
});
|
1922
|
-
|
1923
|
-
exports.NodeAdapter = Faye.NodeAdapter;
|
1924
|
-
exports.Client = Faye.Client;
|
1925
|
-
exports.Logging = Faye.Logging;
|