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'use strict';
23require('../common');
24const assert = require('assert');
25
26const Readable = require('stream').Readable;
27
28test1();
29test2();
30
31function test1() {
32  const r = new Readable();
33
34  // Should not end when we get a Buffer.alloc(0) or '' as the _read
35  // result that just means that there is *temporarily* no data, but to
36  // go ahead and try again later.
37  //
38  // note that this is very unusual.  it only works for crypto streams
39  // because the other side of the stream will call read(0) to cycle
40  // data through openssl.  that's why setImmediate() is used to call
41  // r.read(0) again later, otherwise there is no more work being done
42  // and the process just exits.
43
44  const buf = Buffer.alloc(5, 'x');
45  let reads = 5;
46  r._read = function(n) {
47    switch (reads--) {
48      case 5:
49        return setImmediate(() => {
50          return r.push(buf);
51        });
52      case 4:
53        setImmediate(() => {
54          return r.push(Buffer.alloc(0));
55        });
56        return setImmediate(r.read.bind(r, 0));
57      case 3:
58        setImmediate(r.read.bind(r, 0));
59        return process.nextTick(() => {
60          return r.push(Buffer.alloc(0));
61        });
62      case 2:
63        setImmediate(r.read.bind(r, 0));
64        return r.push(Buffer.alloc(0)); // Not-EOF!
65      case 1:
66        return r.push(buf);
67      case 0:
68        return r.push(null); // EOF
69      default:
70        throw new Error('unreachable');
71    }
72  };
73
74  const results = [];
75  function flow() {
76    let chunk;
77    while (null !== (chunk = r.read()))
78      results.push(String(chunk));
79  }
80  r.on('readable', flow);
81  r.on('end', () => {
82    results.push('EOF');
83  });
84  flow();
85
86  process.on('exit', () => {
87    assert.deepStrictEqual(results, [ 'xxxxx', 'xxxxx', 'EOF' ]);
88    console.log('ok');
89  });
90}
91
92function test2() {
93  const r = new Readable({ encoding: 'base64' });
94  let reads = 5;
95  r._read = function(n) {
96    if (!reads--)
97      return r.push(null); // EOF
98    return r.push(Buffer.from('x'));
99  };
100
101  const results = [];
102  function flow() {
103    let chunk;
104    while (null !== (chunk = r.read()))
105      results.push(String(chunk));
106  }
107  r.on('readable', flow);
108  r.on('end', () => {
109    results.push('EOF');
110  });
111  flow();
112
113  process.on('exit', () => {
114    assert.deepStrictEqual(results, [ 'eHh4', 'eHg=', 'EOF' ]);
115    console.log('ok');
116  });
117}
118