6 var parser = require('socket.io-parser');
7 var debug = require('debug')('socket.io:client');
8 var url = require('url');
14 module.exports = Client;
19 * @param {Server} server instance
20 * @param {Socket} conn
24 function Client(server, conn){
27 this.encoder = server.encoder;
28 this.decoder = new server.parser.Decoder();
30 this.request = conn.request;
34 this.connectBuffer = [];
38 * Sets up event listeners.
43 Client.prototype.setup = function(){
44 this.onclose = this.onclose.bind(this);
45 this.ondata = this.ondata.bind(this);
46 this.onerror = this.onerror.bind(this);
47 this.ondecoded = this.ondecoded.bind(this);
49 this.decoder.on('decoded', this.ondecoded);
50 this.conn.on('data', this.ondata);
51 this.conn.on('error', this.onerror);
52 this.conn.on('close', this.onclose);
56 * Connects a client to a namespace.
58 * @param {String} name namespace
59 * @param {Object} query the query parameters
63 Client.prototype.connect = function(name, query){
64 if (this.server.nsps[name]) {
65 debug('connecting to namespace %s', name);
66 return this.doConnect(name, query);
69 this.server.checkNamespace(name, query, (dynamicNsp) => {
71 debug('dynamic namespace %s was created', dynamicNsp.name);
72 this.doConnect(name, query);
74 debug('creation of namespace %s was denied', name);
75 this.packet({ type: parser.ERROR, nsp: name, data: 'Invalid namespace' });
81 * Connects a client to a namespace.
83 * @param {String} name namespace
84 * @param {String} query the query parameters
88 Client.prototype.doConnect = function(name, query){
89 var nsp = this.server.of(name);
91 if ('/' != name && !this.nsps['/']) {
92 this.connectBuffer.push(name);
97 var socket = nsp.add(this, query, function(){
98 self.sockets[socket.id] = socket;
99 self.nsps[nsp.name] = socket;
101 if ('/' == nsp.name && self.connectBuffer.length > 0) {
102 self.connectBuffer.forEach(self.connect, self);
103 self.connectBuffer = [];
109 * Disconnects from all namespaces and closes transport.
114 Client.prototype.disconnect = function(){
115 for (var id in this.sockets) {
116 if (this.sockets.hasOwnProperty(id)) {
117 this.sockets[id].disconnect();
125 * Removes a socket. Called by each `Socket`.
130 Client.prototype.remove = function(socket){
131 if (this.sockets.hasOwnProperty(socket.id)) {
132 var nsp = this.sockets[socket.id].nsp.name;
133 delete this.sockets[socket.id];
134 delete this.nsps[nsp];
136 debug('ignoring remove for %s', socket.id);
141 * Closes the underlying connection.
146 Client.prototype.close = function(){
147 if ('open' == this.conn.readyState) {
148 debug('forcing transport close');
150 this.onclose('forced server close');
155 * Writes a packet to the transport.
157 * @param {Object} packet object
158 * @param {Object} opts
162 Client.prototype.packet = function(packet, opts){
166 // this writes to the actual connection
167 function writeToEngine(encodedPackets) {
168 if (opts.volatile && !self.conn.transport.writable) return;
169 for (var i = 0; i < encodedPackets.length; i++) {
170 self.conn.write(encodedPackets[i], { compress: opts.compress });
174 if ('open' == this.conn.readyState) {
175 debug('writing packet %j', packet);
176 if (!opts.preEncoded) { // not broadcasting, need to encode
177 this.encoder.encode(packet, writeToEngine); // encode, then write results to engine
178 } else { // a broadcast pre-encodes a packet
179 writeToEngine(packet);
182 debug('ignoring packet write %j', packet);
187 * Called with incoming transport data.
192 Client.prototype.ondata = function(data){
193 // try/catch is needed for protocol violations (GH-1880)
195 this.decoder.add(data);
202 * Called when parser fully decodes a packet.
207 Client.prototype.ondecoded = function(packet) {
208 if (parser.CONNECT == packet.type) {
209 this.connect(url.parse(packet.nsp).pathname, url.parse(packet.nsp, true).query);
211 var socket = this.nsps[packet.nsp];
213 process.nextTick(function() {
214 socket.onpacket(packet);
217 debug('no socket for namespace %s', packet.nsp);
225 * @param {Object} err object
229 Client.prototype.onerror = function(err){
230 for (var id in this.sockets) {
231 if (this.sockets.hasOwnProperty(id)) {
232 this.sockets[id].onerror(err);
239 * Called upon transport close.
241 * @param {String} reason
245 Client.prototype.onclose = function(reason){
246 debug('client close with reason %s', reason);
248 // ignore a potential subsequent `close` event
251 // `nsps` and `sockets` are cleaned up seamlessly
252 for (var id in this.sockets) {
253 if (this.sockets.hasOwnProperty(id)) {
254 this.sockets[id].onclose(reason);
259 this.decoder.destroy(); // clean up decoder
263 * Cleans up event listeners.
268 Client.prototype.destroy = function(){
269 this.conn.removeListener('data', this.ondata);
270 this.conn.removeListener('error', this.onerror);
271 this.conn.removeListener('close', this.onclose);
272 this.decoder.removeListener('decoded', this.ondecoded);