1 /* Copyright 2015-present Samsung Electronics Co., Ltd. and other contributors
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 var EventEmitter = require('events').EventEmitter;
18 var stream = require('stream');
19 var util = require('util');
20 var assert = require('assert');
22 var TCP = process.binding(process.binding.tcp);
25 function createTCP() {
31 function SocketState(options) {
32 // 'true' during connection handshaking.
33 this.connecting = false;
35 // become 'true' when connection established.
36 this.connected = false;
41 this.destroyed = false;
43 this.allowHalfOpen = options && options.allowHalfOpen || false;
47 function Socket(options) {
48 if (!(this instanceof Socket)) {
49 return new Socket(options);
52 if (util.isUndefined(options)) {
56 stream.Duplex.call(this, options);
61 this._socketState = new SocketState(options);
64 this._handle = options.handle;
65 this._handle.owner = this;
68 this.on('finish', onSocketFinish);
69 this.on('end', onSocketEnd);
73 // Socket inherits Duplex.
74 util.inherits(Socket, stream.Duplex);
77 Socket.prototype.connect = function() {
79 var state = self._socketState;
81 var args = normalizeConnectArgs(arguments);
82 var options = args[0];
83 var callback = args[1];
85 if (state.connecting || state.connected) {
90 self._handle = createTCP();
91 self._handle.owner = self;
94 if (util.isFunction(callback)) {
95 self.once('connect', callback);
98 resetSocketTimeout(self);
100 state.connecting = true;
102 var dns = require('dns');
103 var host = options.host ? options.host : 'localhost';
104 var port = options.port;
106 family: options.family >>> 0,
110 if (!util.isNumber(port) || port < 0 || port > 65535)
111 throw new RangeError('port should be >= 0 and < 65536: ' + options.port);
113 if (dnsopts.family !== 0 && dnsopts.family !== 4 && dnsopts.family !== 6)
114 throw new RangeError('port should be 4 or 6: ' + dnsopts.family);
117 dns.lookup(host, dnsopts, function(err, ip, family) {
118 self.emit('lookup', err, ip, family);
121 process.nextTick(function() {
122 self.emit('error', err);
126 resetSocketTimeout(self);
127 connect(self, ip, port);
135 Socket.prototype.write = function(data, callback) {
136 if (!util.isString(data) && !util.isBuffer(data)) {
137 throw new TypeError('invalid argument');
140 return stream.Duplex.prototype.write.call(this, data, callback);
144 Socket.prototype._write = function(chunk, callback, afterWrite) {
145 assert(util.isFunction(afterWrite));
149 resetSocketTimeout(self);
151 self._handle.owner = self;
153 self._handle.write(chunk, function(status) {
155 if (util.isFunction(callback)) {
156 callback.call(self, status);
162 Socket.prototype.end = function(data, callback) {
164 var state = self._socketState;
166 // end of writable stream.
167 stream.Writable.prototype.end.call(self, data, callback);
169 // this socket is no longer writable.
170 state.writable = false;
174 // Destroy this socket as fast as possible.
175 Socket.prototype.destroy = function() {
177 var state = self._socketState;
179 if (state.destroyed) {
183 if (state.writable) {
188 clearSocketTimeout(self);
190 if (self._writableState.ended) {
192 state.destroyed = true;
194 self.once('finish', function() {
201 // Destroy this socket as fast as possible if this socket is no longer readable.
202 Socket.prototype.destroySoon = function() {
204 var state = self._socketState;
206 if (state.writable) {
210 if (self._writableState.finished) {
213 self.once('finish', self.destroy);
218 Socket.prototype.setKeepAlive = function(enable, delay) {
220 enable = +Boolean(enable);
221 if (self._handle && self._handle.setKeepAlive) {
222 self._handle.setKeepAlive(enable, ~~(delay / 1000));
227 Socket.prototype.address = function() {
228 if (!this._handle || !this._handle.getsockname) {
231 if (!this._sockname) {
233 var err = this._handle.getsockname(out);
234 if (err) return {}; // FIXME(bnoordhuis) Throw?
235 this._sockname = out;
237 return this._sockname;
241 Socket.prototype.setTimeout = function(msecs, callback) {
244 self._timeout = msecs;
245 clearSocketTimeout(self);
249 self.removeListener('timeout', callback);
252 self._timer = setTimeout(function() {
253 self.emit('timeout');
254 clearSocketTimeout(self);
257 self.once('timeout', callback);
263 function connect(socket, ip, port) {
264 var afterConnect = function(status) {
265 var state = socket._socketState;
266 state.connecting = false;
268 if (state.destroyed) {
273 onSocketConnect(socket);
274 socket.emit('connect');
277 emitError(socket, new Error('connect failed - status: ' + status));
281 socket._handle.connect(ip, port, afterConnect);
285 function close(socket) {
286 socket._handle.owner = socket;
287 socket._handle.onclose = function() {
288 socket.emit('close');
291 socket._handle.close();
293 if (socket._server) {
294 var server = socket._server;
295 server._socketCount--;
296 server._emitCloseIfDrained();
297 socket._server = null;
302 function resetSocketTimeout(socket) {
303 var state = socket._socketState;
305 if (!state.destroyed) {
306 // start timeout over again
307 clearSocketTimeout(socket);
308 socket._timer = setTimeout(function() {
309 socket.emit('timeout');
310 clearSocketTimeout(socket);
316 function clearSocketTimeout(socket) {
318 clearTimeout(socket._timer);
319 socket._timer = null;
324 function emitError(socket, err) {
325 socket.emit('error', err);
329 function maybeDestroy(socket) {
330 var state = socket._socketState;
332 if (!state.connecting &&
340 function onSocketConnect(socket) {
341 var state = socket._socketState;
343 state.connecting = false;
344 state.connected = true;
346 resetSocketTimeout(socket);
348 socket._readyToWrite();
350 // `readStart` on next tick, after connection event handled.
351 process.nextTick(function() {
352 socket._handle.owner = socket;
353 socket._handle.onread = onread;
354 socket._handle.readStart();
359 function onread(socket, nread, isEOF, buffer) {
360 var state = socket._socketState;
362 resetSocketTimeout(socket);
365 // pushing readable stream null means EOF.
366 stream.Readable.prototype.push.call(socket, null);
368 if (socket._readableState.length == 0) {
369 // this socket is no longer readable.
370 state.readable = false;
371 // destroy if this socket is not writable.
372 maybeDestroy(socket);
374 } else if (nread < 0) {
375 var err = new Error('read error: ' + nread);
376 stream.Readable.prototype.error.call(socket, err);
377 } else if (nread > 0) {
378 if (process.platform != 'nuttx') {
379 stream.Readable.prototype.push.call(socket, buffer);
383 var str = buffer.toString();
384 var eofNeeded = false;
386 && str.substr(str.length - 6, str.length) == '\\e\\n\\d') {
388 buffer = buffer.slice(0, str.length - 6);
391 if (str.length == 6 && eofNeeded) {
392 // Socket.prototype.end with no argument
394 stream.Readable.prototype.push.call(socket, buffer);
398 onread(socket, 0, true, null);
404 // Writable stream finished.
405 function onSocketFinish() {
407 var state = self._socketState;
409 if (!state.readable || self._readableState.ended) {
410 // no readable stream or ended, destroy(close) socket.
411 return self.destroy();
413 // Readable stream alive, shutdown only outgoing stream.
414 var err = self._handle.shutdown(function() {
415 if (self._readableState.ended) {
423 // Readable stream ended.
424 function onSocketEnd() {
425 var state = this._socketState;
429 if (!state.allowHalfOpen) {
436 function Server(options, connectionListener) {
437 if (!(this instanceof Server)) {
438 return new Server(options, connectionListener);
441 EventEmitter.call(this);
443 if (util.isFunction(options)) {
444 connectionListener = options;
447 options = options || {};
450 if (util.isFunction(connectionListener)) {
451 this.on('connection', connectionListener);
455 this._socketCount = 0;
457 this.allowHalfOpen = options.allowHalfOpen || false;
460 // Server inherits EventEmitter.
461 util.inherits(Server, EventEmitter);
465 Server.prototype.listen = function() {
468 var args = normalizeListenArgs(arguments);
470 var options = args[0];
471 var callback = args[1];
473 var port = options.port;
474 var host = util.isString(options.host) ? options.host : '0.0.0.0';
475 var backlog = util.isNumber(options.backlog) ? options.backlog : 511;
477 if (!util.isNumber(port)) {
478 throw new Error('invalid argument - need port number');
481 // register listening event listener.
482 if (util.isFunction(callback)) {
483 self.once('listening', callback);
486 // Create server handle.
488 self._handle = createTCP();
492 var err = self._handle.bind(host, port);
494 self._handle.close();
499 self._handle.onconnection = onconnection;
500 self._handle.createTCP = createTCP;
501 self._handle.owner = self;
503 var err = self._handle.listen(backlog);
506 self._handle.close();
510 process.nextTick(function() {
512 self.emit('listening');
518 Server.prototype.address = function() {
519 if (this._handle && this._handle.getsockname) {
521 this._handle.getsockname(out);
522 // TODO(bnoordhuis) Check err and throw?
530 Server.prototype.close = function(callback) {
531 if (util.isFunction(callback)) {
533 this.once('close', function() {
534 callback(new Error('Not running'));
537 this.once('close', callback);
541 this._handle.close();
544 this._emitCloseIfDrained();
549 Server.prototype._emitCloseIfDrained = function() {
552 if (self._handle || self._socketCount > 0) {
556 process.nextTick(function() {
562 // This function is called after server accepted connection request
565 // * server tcp handle
567 // * status - status code
568 // * clientHandle - client socket handle (tcp).
569 function onconnection(status, clientHandle) {
570 var server = this.owner;
573 server.emit('error', new Error('accept error: ' + status));
577 // Create socket object for connecting client.
578 var socket = new Socket({
579 handle: clientHandle,
580 allowHalfOpen: server.allowHalfOpen
582 socket._server = server;
584 onSocketConnect(socket);
586 server._socketCount++;
588 server.emit('connection', socket);
592 function normalizeListenArgs(args) {
595 if (util.isObject(args[0])) {
599 options.port = args[idx++];
600 if (util.isString(args[idx])) {
601 options.host = args[idx++];
603 if (util.isNumber(args[idx])) {
604 options.backlog = args[idx++];
608 var cb = args[args.length - 1];
610 return util.isFunction(cb) ? [options, cb] : [options];
614 function normalizeConnectArgs(args) {
617 if (util.isObject(args[0])) {
620 options.port = args[0];
621 if (util.isString(args[1])) {
622 options.host = args[1];
626 var cb = args[args.length - 1];
628 return util.isFunction(cb) ? [options, cb] : [options];
632 exports.createServer = function(options, callback) {
633 return new Server(options, callback);
637 // net.connect(options[, connectListenr])
638 // net.connect(port[, host][, connectListener])
639 exports.connect = exports.createConnection = function() {
640 var args = normalizeConnectArgs(arguments);
641 var socket = new Socket(args[0]);
642 return Socket.prototype.connect.apply(socket, args);
646 module.exports.Socket = Socket;
647 module.exports.Server = Server;