var assert = require('assert') var util = require('util') var EventEmitter = require('events').EventEmitter var Buffer = require('buffer').Buffer var Queue = require('./queue') // Node.js version var match = /^v(\d+)\.(\d+)\./.exec(process.version) var version = match ? Number(match[1]) + Number('0.' + match[2]) : 11 var onreadMode = version >= 11.1 ? 2 : 1 var mode = 'modern' var setNRead if (onreadMode === 2) { var sw = process.binding('stream_wrap') setNRead = function (nread) { sw.streamBaseState[sw.kReadBytesOrError] = nread } } function Handle (stream, options) { EventEmitter.call(this) this._stream = stream this._flowing = false this._reading = false this._options = options || {} this.onread = null // Pending requests this.pending = new Queue() } util.inherits(Handle, EventEmitter) module.exports = Handle Handle.mode = mode Handle.create = function create (stream, options) { return new Handle(stream, options) } Handle.prototype._onread = function _onread (nread, buffer) { if (onreadMode === 1) { this.onread(nread, buffer) } else { setNRead(nread) this.onread(buffer) } } Handle.prototype._queueReq = function _queueReq (type, req) { return this.pending.append(type, req) } Handle.prototype._pendingList = function _pendingList () { var list = [] while (!this.pending.isEmpty()) { list.push(this.pending.first().dequeue()) } return list } Handle.prototype.setStream = function setStream (stream) { assert(this._stream === null, 'Can\'t set stream two times') this._stream = stream this.emit('stream', stream) } Handle.prototype.readStart = function readStart () { this._reading = true if (!this._stream) { this.once('stream', this.readStart) return 0 } if (!this._flowing) { this._flowing = true this._flow() } this._stream.resume() return 0 } Handle.prototype.readStop = function readStop () { this._reading = false if (!this._stream) { this.once('stream', this.readStop) return 0 } this._stream.pause() return 0 } var uv = process.binding('uv') Handle.prototype._flow = function flow () { var self = this this._stream.on('data', function (chunk) { self._onread(chunk.length, chunk) }) this._stream.on('end', function () { self._onread(uv.UV_EOF, Buffer.alloc(0)) }) this._stream.on('close', function () { setImmediate(function () { if (self._reading) { self._onread(uv.UV_ECONNRESET, Buffer.alloc(0)) } }) }) } Handle.prototype._close = function _close () { var list = this._pendingList() var self = this setImmediate(function () { for (var i = 0; i < list.length; i++) { var req = list[i] req.oncomplete(uv.UV_ECANCELED, self, req) } }) this.readStop() } Handle.prototype.shutdown = function shutdown (req) { var wrap = this._queueReq('shutdown', req) if (!this._stream) { this.once('stream', function () { this._shutdown(wrap) }) return 0 } return this._shutdown(wrap) } Handle.prototype._shutdown = function _shutdown (wrap) { var self = this this._stream.end(function () { var req = wrap.dequeue() if (!req) { return } req.oncomplete(0, self, req) }) return 0 } Handle.prototype.close = function close (callback) { this._close() if (!this._stream) { this.once('stream', function () { this.close(callback) }) return 0 } if (this._options.close) { this._options.close(callback) } else { process.nextTick(callback) } return 0 } Handle.prototype.writeEnc = function writeEnc (req, data, enc) { var wrap = this._queueReq('write', req) if (!this._stream) { this.once('stream', function () { this._writeEnc(wrap, req, data, enc) }) return 0 } return this._writeEnc(wrap, req, data, enc) } Handle.prototype._writeEnc = function _writeEnc (wrap, req, data, enc) { var self = this req.async = true req.bytes = data.length if (wrap.isEmpty()) { return 0 } this._stream.write(data, enc, function () { var req = wrap.dequeue() if (!req) { return } req.oncomplete(0, self, req) }) return 0 } /** * @param {WriteWrap} req * @param {string[]} chunks * @param {Boolean} allBuffers */ Handle.prototype.writev = function _writev (req, chunks, allBuffers) { while (chunks.length > 0) { this._stream.write(chunks.shift(), chunks.shift()) } return 0 } Handle.prototype.writeBuffer = function writeBuffer (req, data) { return this.writeEnc(req, data, null) } Handle.prototype.writeAsciiString = function writeAsciiString (req, data) { return this.writeEnc(req, data, 'ascii') } Handle.prototype.writeUtf8String = function writeUtf8String (req, data) { return this.writeEnc(req, data, 'utf8') } Handle.prototype.writeUcs2String = function writeUcs2String (req, data) { return this.writeEnc(req, data, 'ucs2') } Handle.prototype.writeBinaryString = function writeBinaryString (req, data) { return this.writeEnc(req, data, 'binary') } Handle.prototype.writeLatin1String = function writeLatin1String (req, data) { return this.writeEnc(req, data, 'binary') } // v0.8 Handle.prototype.getsockname = function getsockname () { if (this._options.getPeerName) { return this._options.getPeerName() } return null } Handle.prototype.getpeername = function getpeername (out) { var res = this.getsockname() if (!res) { return -1 } Object.keys(res).forEach(function (key) { out[key] = res[key] }) return 0 }