1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21// a duplex stream is just a stream that is both readable and writable. 22// Since JS doesn't have multiple prototypal inheritance, this class 23// prototypally inherits from Readable, and then parasitically from 24// Writable. 25'use strict'; 26/*<replacement>*/ 27 28var objectKeys = Object.keys || function (obj) { 29 var keys = []; 30 31 for (var key in obj) { 32 keys.push(key); 33 } 34 35 return keys; 36}; 37/*</replacement>*/ 38 39 40module.exports = Duplex; 41 42var Readable = require('./_stream_readable'); 43 44var Writable = require('./_stream_writable'); 45 46require('inherits')(Duplex, Readable); 47 48{ 49 // Allow the keys array to be GC'ed. 50 var keys = objectKeys(Writable.prototype); 51 52 for (var v = 0; v < keys.length; v++) { 53 var method = keys[v]; 54 if (!Duplex.prototype[method]) Duplex.prototype[method] = Writable.prototype[method]; 55 } 56} 57 58function Duplex(options) { 59 if (!(this instanceof Duplex)) return new Duplex(options); 60 Readable.call(this, options); 61 Writable.call(this, options); 62 this.allowHalfOpen = true; 63 64 if (options) { 65 if (options.readable === false) this.readable = false; 66 if (options.writable === false) this.writable = false; 67 68 if (options.allowHalfOpen === false) { 69 this.allowHalfOpen = false; 70 this.once('end', onend); 71 } 72 } 73} 74 75Object.defineProperty(Duplex.prototype, 'writableHighWaterMark', { 76 // making it explicit this property is not enumerable 77 // because otherwise some prototype manipulation in 78 // userland will fail 79 enumerable: false, 80 get: function get() { 81 return this._writableState.highWaterMark; 82 } 83}); 84Object.defineProperty(Duplex.prototype, 'writableBuffer', { 85 // making it explicit this property is not enumerable 86 // because otherwise some prototype manipulation in 87 // userland will fail 88 enumerable: false, 89 get: function get() { 90 return this._writableState && this._writableState.getBuffer(); 91 } 92}); 93Object.defineProperty(Duplex.prototype, 'writableLength', { 94 // making it explicit this property is not enumerable 95 // because otherwise some prototype manipulation in 96 // userland will fail 97 enumerable: false, 98 get: function get() { 99 return this._writableState.length; 100 } 101}); // the no-half-open enforcer 102 103function onend() { 104 // If the writable side ended, then we're ok. 105 if (this._writableState.ended) return; // no more data can be written. 106 // But allow more writes to happen in this tick. 107 108 process.nextTick(onEndNT, this); 109} 110 111function onEndNT(self) { 112 self.end(); 113} 114 115Object.defineProperty(Duplex.prototype, 'destroyed', { 116 // making it explicit this property is not enumerable 117 // because otherwise some prototype manipulation in 118 // userland will fail 119 enumerable: false, 120 get: function get() { 121 if (this._readableState === undefined || this._writableState === undefined) { 122 return false; 123 } 124 125 return this._readableState.destroyed && this._writableState.destroyed; 126 }, 127 set: function set(value) { 128 // we ignore the value if the stream 129 // has not been initialized yet 130 if (this._readableState === undefined || this._writableState === undefined) { 131 return; 132 } // backward compatibility, the user is explicitly 133 // managing destroyed 134 135 136 this._readableState.destroyed = value; 137 this._writableState.destroyed = value; 138 } 139});