1'use strict';
2
3// This test ensures that servers are able to send data independent of window
4// size.
5// TODO: This test makes large buffer allocations (128KiB) and should be tested
6// on smaller / IoT platforms in case this poses problems for these targets.
7
8const common = require('../common');
9if (!common.hasCrypto)
10  common.skip('missing crypto');
11const assert = require('assert');
12const h2 = require('http2');
13
14// Given a list of buffers and an initial window size, have a server write
15// each buffer to the HTTP2 Writable stream, and let the client verify that
16// all of the bytes were sent correctly
17function run(buffers, initialWindowSize) {
18  return new Promise((resolve, reject) => {
19    const expectedBuffer = Buffer.concat(buffers);
20
21    const server = h2.createServer();
22    server.on('stream', (stream) => {
23      let i = 0;
24      const writeToStream = () => {
25        const cont = () => {
26          i++;
27          if (i < buffers.length) {
28            setImmediate(writeToStream);
29          } else {
30            stream.end();
31          }
32        };
33        const drained = stream.write(buffers[i]);
34        if (drained) {
35          cont();
36        } else {
37          stream.once('drain', cont);
38        }
39      };
40      writeToStream();
41    });
42    server.listen(0);
43
44    server.on('listening', common.mustCall(function() {
45      const port = this.address().port;
46
47      const client =
48        h2.connect({
49          authority: 'localhost',
50          protocol: 'http:',
51          port
52        }, {
53          settings: {
54            initialWindowSize
55          }
56        }).on('connect', common.mustCall(() => {
57          const req = client.request({
58            ':method': 'GET',
59            ':path': '/'
60          });
61          const responses = [];
62          req.on('data', (data) => {
63            responses.push(data);
64          });
65          req.on('end', common.mustCall(() => {
66            const actualBuffer = Buffer.concat(responses);
67            assert.strictEqual(Buffer.compare(actualBuffer, expectedBuffer), 0);
68            // shut down
69            client.close();
70            server.close(() => {
71              resolve();
72            });
73          }));
74          req.end();
75        }));
76    }));
77  });
78}
79
80const bufferValueRange = [0, 1, 2, 3];
81const buffersList = [
82  bufferValueRange.map((a) => Buffer.alloc(1 << 4, a)),
83  bufferValueRange.map((a) => Buffer.alloc((1 << 8) - 1, a)),
84// Specifying too large of a value causes timeouts on some platforms
85//  bufferValueRange.map((a) => Buffer.alloc(1 << 17, a))
86];
87const initialWindowSizeList = [
88  1 << 4,
89  (1 << 8) - 1,
90  1 << 8,
91  1 << 17,
92  undefined, // Use default window size which is (1 << 16) - 1
93];
94
95// Call `run` on each element in the cartesian product of buffersList and
96// initialWindowSizeList.
97let p = Promise.resolve();
98for (const buffers of buffersList) {
99  for (const initialWindowSize of initialWindowSizeList) {
100    p = p.then(() => run(buffers, initialWindowSize));
101  }
102}
103p.then(common.mustCall());
104