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.

243 lines
4.9 KiB

4 years ago
  1. var assert = require('assert')
  2. var util = require('util')
  3. var EventEmitter = require('events').EventEmitter
  4. var Buffer = require('buffer').Buffer
  5. var Queue = require('./queue')
  6. // Node.js version
  7. var mode = 'modern'
  8. function Handle (stream, options) {
  9. EventEmitter.call(this)
  10. this._stream = stream
  11. this._flowing = false
  12. this._reading = false
  13. this._options = options || {}
  14. this.onread = null
  15. // Pending requests
  16. this.pending = new Queue()
  17. }
  18. util.inherits(Handle, EventEmitter)
  19. module.exports = Handle
  20. Handle.mode = mode
  21. Handle.create = function create (stream, options) {
  22. return new Handle(stream, options)
  23. }
  24. Handle.prototype._queueReq = function _queueReq (type, req) {
  25. return this.pending.append(type, req)
  26. }
  27. Handle.prototype._pendingList = function _pendingList () {
  28. var list = []
  29. while (!this.pending.isEmpty()) { list.push(this.pending.first().dequeue()) }
  30. return list
  31. }
  32. Handle.prototype.setStream = function setStream (stream) {
  33. assert(this._stream === null, 'Can\'t set stream two times')
  34. this._stream = stream
  35. this.emit('stream', stream)
  36. }
  37. Handle.prototype.readStart = function readStart () {
  38. this._reading = true
  39. if (!this._stream) {
  40. this.once('stream', this.readStart)
  41. return 0
  42. }
  43. if (!this._flowing) {
  44. this._flowing = true
  45. this._flow()
  46. }
  47. this._stream.resume()
  48. return 0
  49. }
  50. Handle.prototype.readStop = function readStop () {
  51. this._reading = false
  52. if (!this._stream) {
  53. this.once('stream', this.readStop)
  54. return 0
  55. }
  56. this._stream.pause()
  57. return 0
  58. }
  59. var uv = process.binding('uv')
  60. Handle.prototype._flow = function flow () {
  61. var self = this
  62. this._stream.on('data', function (chunk) {
  63. self.onread(chunk.length, chunk)
  64. })
  65. this._stream.on('end', function () {
  66. self.onread(uv.UV_EOF, Buffer.alloc(0))
  67. })
  68. this._stream.on('close', function () {
  69. setImmediate(function () {
  70. if (self._reading) {
  71. self.onread(uv.UV_ECONNRESET, Buffer.alloc(0))
  72. }
  73. })
  74. })
  75. }
  76. Handle.prototype._close = function _close () {
  77. var list = this._pendingList()
  78. var self = this
  79. setImmediate(function () {
  80. for (var i = 0; i < list.length; i++) {
  81. var req = list[i]
  82. req.oncomplete(uv.UV_ECANCELED, self, req)
  83. }
  84. })
  85. this.readStop()
  86. }
  87. Handle.prototype.shutdown = function shutdown (req) {
  88. var wrap = this._queueReq('shutdown', req)
  89. if (!this._stream) {
  90. this.once('stream', function () {
  91. this._shutdown(wrap)
  92. })
  93. return 0
  94. }
  95. return this._shutdown(wrap)
  96. }
  97. Handle.prototype._shutdown = function _shutdown (wrap) {
  98. var self = this
  99. this._stream.end(function () {
  100. var req = wrap.dequeue()
  101. if (!req) { return }
  102. req.oncomplete(0, self, req)
  103. })
  104. return 0
  105. }
  106. Handle.prototype.close = function close (callback) {
  107. this._close()
  108. if (!this._stream) {
  109. this.once('stream', function () {
  110. this.close(callback)
  111. })
  112. return 0
  113. }
  114. if (this._options.close) {
  115. this._options.close(callback)
  116. } else {
  117. process.nextTick(callback)
  118. }
  119. return 0
  120. }
  121. Handle.prototype.writeEnc = function writeEnc (req, data, enc) {
  122. var wrap = this._queueReq('write', req)
  123. if (!this._stream) {
  124. this.once('stream', function () {
  125. this._writeEnc(wrap, req, data, enc)
  126. })
  127. return 0
  128. }
  129. return this._writeEnc(wrap, req, data, enc)
  130. }
  131. Handle.prototype._writeEnc = function _writeEnc (wrap, req, data, enc) {
  132. var self = this
  133. req.async = true
  134. req.bytes = data.length
  135. if (wrap.isEmpty()) {
  136. return 0
  137. }
  138. this._stream.write(data, enc, function () {
  139. var req = wrap.dequeue()
  140. if (!req) { return }
  141. req.oncomplete(0, self, req)
  142. })
  143. return 0
  144. }
  145. /**
  146. * @param {WriteWrap} req
  147. * @param {string[]} chunks
  148. * @param {Boolean} allBuffers
  149. */
  150. Handle.prototype.writev = function _writev (req, chunks, allBuffers) {
  151. while (chunks.length > 0) {
  152. this._stream.write(chunks.shift(), chunks.shift())
  153. }
  154. return 0
  155. }
  156. Handle.prototype.writeBuffer = function writeBuffer (req, data) {
  157. return this.writeEnc(req, data, null)
  158. }
  159. Handle.prototype.writeAsciiString = function writeAsciiString (req, data) {
  160. return this.writeEnc(req, data, 'ascii')
  161. }
  162. Handle.prototype.writeUtf8String = function writeUtf8String (req, data) {
  163. return this.writeEnc(req, data, 'utf8')
  164. }
  165. Handle.prototype.writeUcs2String = function writeUcs2String (req, data) {
  166. return this.writeEnc(req, data, 'ucs2')
  167. }
  168. Handle.prototype.writeBinaryString = function writeBinaryString (req, data) {
  169. return this.writeEnc(req, data, 'binary')
  170. }
  171. Handle.prototype.writeLatin1String = function writeLatin1String (req, data) {
  172. return this.writeEnc(req, data, 'binary')
  173. }
  174. // v0.8
  175. Handle.prototype.getsockname = function getsockname () {
  176. if (this._options.getPeerName) {
  177. return this._options.getPeerName()
  178. }
  179. return null
  180. }
  181. Handle.prototype.getpeername = function getpeername (out) {
  182. var res = this.getsockname()
  183. if (!res) { return -1 }
  184. Object.keys(res).forEach(function (key) {
  185. out[key] = res[key]
  186. })
  187. return 0
  188. }