You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
263 lines
5.4 KiB
263 lines
5.4 KiB
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 |
|
}
|
|
|