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
22// a duplex stream is just a stream that is both readable and writable.
23// Since JS doesn't have multiple prototype inheritance, this class
24// prototypically inherits from Readable, and then parasitically from
25// Writable.
26
27'use strict';
28
29const {
30  ObjectDefineProperties,
31  ObjectGetOwnPropertyDescriptor,
32  ObjectKeys,
33  ObjectSetPrototypeOf,
34} = primordials;
35
36module.exports = Duplex;
37
38const Readable = require('internal/streams/readable');
39const Writable = require('internal/streams/writable');
40
41ObjectSetPrototypeOf(Duplex.prototype, Readable.prototype);
42ObjectSetPrototypeOf(Duplex, Readable);
43
44{
45  const keys = ObjectKeys(Writable.prototype);
46  // Allow the keys array to be GC'ed.
47  for (let i = 0; i < keys.length; i++) {
48    const method = keys[i];
49    if (!Duplex.prototype[method])
50      Duplex.prototype[method] = Writable.prototype[method];
51  }
52}
53
54function Duplex(options) {
55  if (!(this instanceof Duplex))
56    return new Duplex(options);
57
58  Readable.call(this, options);
59  Writable.call(this, options);
60
61  if (options) {
62    this.allowHalfOpen = options.allowHalfOpen !== false;
63
64    if (options.readable === false) {
65      this._readableState.readable = false;
66      this._readableState.ended = true;
67      this._readableState.endEmitted = true;
68    }
69
70    if (options.writable === false) {
71      this._writableState.writable = false;
72      this._writableState.ending = true;
73      this._writableState.ended = true;
74      this._writableState.finished = true;
75    }
76  } else {
77    this.allowHalfOpen = true;
78  }
79}
80
81ObjectDefineProperties(Duplex.prototype, {
82  writable:
83    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writable') },
84  writableHighWaterMark:
85    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableHighWaterMark') },
86  writableObjectMode:
87    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableObjectMode') },
88  writableBuffer:
89    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableBuffer') },
90  writableLength:
91    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableLength') },
92  writableFinished:
93    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableFinished') },
94  writableCorked:
95    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableCorked') },
96  writableEnded:
97    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableEnded') },
98  writableNeedDrain:
99    { __proto__: null, ...ObjectGetOwnPropertyDescriptor(Writable.prototype, 'writableNeedDrain') },
100
101  destroyed: {
102    __proto__: null,
103    get() {
104      if (this._readableState === undefined ||
105        this._writableState === undefined) {
106        return false;
107      }
108      return this._readableState.destroyed && this._writableState.destroyed;
109    },
110    set(value) {
111      // Backward compatibility, the user is explicitly
112      // managing destroyed.
113      if (this._readableState && this._writableState) {
114        this._readableState.destroyed = value;
115        this._writableState.destroyed = value;
116      }
117    },
118  },
119});
120
121let webStreamsAdapters;
122
123// Lazy to avoid circular references
124function lazyWebStreams() {
125  if (webStreamsAdapters === undefined)
126    webStreamsAdapters = require('internal/webstreams/adapters');
127  return webStreamsAdapters;
128}
129
130Duplex.fromWeb = function(pair, options) {
131  return lazyWebStreams().newStreamDuplexFromReadableWritablePair(
132    pair,
133    options);
134};
135
136Duplex.toWeb = function(duplex) {
137  return lazyWebStreams().newReadableWritablePairFromDuplex(duplex);
138};
139
140let duplexify;
141
142Duplex.from = function(body) {
143  if (!duplexify) {
144    duplexify = require('internal/streams/duplexify');
145  }
146  return duplexify(body, 'body');
147};
148