1'use strict';
2const common = require('../common');
3if (!common.hasCrypto)
4  common.skip('missing crypto');
5
6const http2 = require('http2');
7const net = require('net');
8const { Worker, parentPort } = require('worker_threads');
9
10// Verify that creating a number of invalid HTTP/2 streams will eventually
11// result in the peer closing the session.
12// This test uses separate threads for client and server to avoid
13// the two event loops intermixing, as we are writing in a busy loop here.
14
15if (process.env.HAS_STARTED_WORKER) {
16  const server = http2.createServer({ maxSessionInvalidFrames: 100 });
17  server.on('stream', (stream) => {
18    stream.respond({
19      'content-type': 'text/plain',
20      ':status': 200
21    });
22    stream.end('Hello, world!\n');
23  });
24  server.listen(0, () => parentPort.postMessage(server.address().port));
25  return;
26}
27
28process.env.HAS_STARTED_WORKER = 1;
29const worker = new Worker(__filename).on('message', common.mustCall((port) => {
30  const h2header = Buffer.alloc(9);
31  const conn = net.connect({ port, allowHalfOpen: true });
32
33  conn.write('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n');
34
35  h2header[3] = 4;  // Send a settings frame.
36  conn.write(Buffer.from(h2header));
37
38  let inbuf = Buffer.alloc(0);
39  let state = 'settingsHeader';
40  let settingsFrameLength;
41  conn.on('data', (chunk) => {
42    inbuf = Buffer.concat([inbuf, chunk]);
43    switch (state) {
44      case 'settingsHeader':
45        if (inbuf.length < 9) return;
46        settingsFrameLength = inbuf.readIntBE(0, 3);
47        inbuf = inbuf.slice(9);
48        state = 'readingSettings';
49        // Fallthrough
50      case 'readingSettings':
51        if (inbuf.length < settingsFrameLength) return;
52        inbuf = inbuf.slice(settingsFrameLength);
53        h2header[3] = 4;  // Send a settings ACK.
54        h2header[4] = 1;
55        conn.write(Buffer.from(h2header));
56        state = 'ignoreInput';
57        writeRequests();
58    }
59  });
60
61  let gotError = false;
62  let streamId = 1;
63
64  function writeRequests() {
65    for (let i = 1; i < 10 && !gotError; i++) {
66      h2header[3] = 1;  // HEADERS
67      h2header[4] = 0x5;  // END_HEADERS|END_STREAM
68      h2header.writeIntBE(1, 0, 3);  // Length: 1
69      h2header.writeIntBE(streamId, 5, 4);  // Stream ID
70      streamId += 2;
71      // 0x88 = :status: 200
72      if (!conn.write(Buffer.concat([h2header, Buffer.from([0x88])]))) {
73        break;
74      }
75    }
76    if (!gotError)
77      setImmediate(writeRequests);
78  }
79
80  conn.once('error', common.mustCall(() => {
81    gotError = true;
82    worker.terminate();
83    conn.destroy();
84  }));
85}));
86