1'use strict';
2
3const common = require('../common');
4if (!common.hasCrypto)
5  common.skip('missing crypto');
6
7const async_hooks = require('async_hooks');
8const assert = require('assert');
9const http2 = require('http2');
10
11const pings = new Set();
12const events = [0, 0, 0, 0];
13
14const hook = async_hooks.createHook({
15  init(id, type, trigger, resource) {
16    if (type === 'HTTP2PING') {
17      pings.add(id);
18      events[0]++;
19    }
20  },
21  before(id) {
22    if (pings.has(id)) {
23      events[1]++;
24    }
25  },
26  after(id) {
27    if (pings.has(id)) {
28      events[2]++;
29    }
30  },
31  destroy(id) {
32    if (pings.has(id)) {
33      events[3]++;
34    }
35  }
36});
37hook.enable();
38
39process.on('exit', () => {
40  assert.deepStrictEqual(events, [4, 4, 4, 4]);
41});
42
43const server = http2.createServer();
44server.on('stream', common.mustCall((stream) => {
45  assert(stream.session.ping(common.mustCall((err, duration, ret) => {
46    assert.strictEqual(err, null);
47    assert.strictEqual(typeof duration, 'number');
48    assert.strictEqual(ret.length, 8);
49    stream.end('ok');
50  })));
51  stream.respond();
52}));
53
54server.listen(0, common.mustCall(() => {
55  const client = http2.connect(`http://localhost:${server.address().port}`,
56                               { maxOutstandingPings: 2 });
57  client.on('connect', common.mustCall(() => {
58    {
59      const payload = Buffer.from('abcdefgh');
60      assert(client.ping(payload, common.mustCall((err, duration, ret) => {
61        assert.strictEqual(err, null);
62        assert.strictEqual(typeof duration, 'number');
63        assert.deepStrictEqual(payload, ret);
64      })));
65    }
66    {
67      const payload = Buffer.from('abcdefgi');
68      assert(client.ping(payload, common.mustCall((err, duration, ret) => {
69        assert.strictEqual(err, null);
70        assert.strictEqual(typeof duration, 'number');
71        assert.deepStrictEqual(payload, ret);
72      })));
73    }
74
75    // Only max 2 pings at a time based on the maxOutstandingPings option
76    assert(!client.ping(common.expectsError({
77      code: 'ERR_HTTP2_PING_CANCEL',
78      name: 'Error',
79      message: 'HTTP2 ping cancelled'
80    })));
81
82    // Should throw if payload is not of type ArrayBufferView
83    {
84      [1, true, {}, []].forEach((payload) =>
85        assert.throws(
86          () => client.ping(payload),
87          {
88            name: 'TypeError',
89            code: 'ERR_INVALID_ARG_TYPE',
90            message: 'The "payload" argument must be an instance of Buffer, ' +
91                     'TypedArray, or DataView.' +
92                     common.invalidArgTypeHelper(payload)
93          }
94        )
95      );
96    }
97
98    // Should throw if payload length is not 8
99    {
100      const shortPayload = Buffer.from('abcdefg');
101      const longPayload = Buffer.from('abcdefghi');
102      [shortPayload, longPayload].forEach((payloadWithInvalidLength) =>
103        assert.throws(
104          () => client.ping(payloadWithInvalidLength),
105          {
106            name: 'RangeError',
107            code: 'ERR_HTTP2_PING_LENGTH',
108            message: 'HTTP2 ping payload must be 8 bytes'
109          }
110        )
111      );
112    }
113
114    // Should throw error is callback is not of type function
115    {
116      const payload = Buffer.from('abcdefgh');
117      [1, true, {}, []].forEach((invalidCallback) =>
118        assert.throws(
119          () => client.ping(payload, invalidCallback),
120          {
121            name: 'TypeError',
122            code: 'ERR_INVALID_ARG_TYPE',
123          }
124        )
125      );
126    }
127
128    const req = client.request();
129    req.resume();
130    req.on('end', common.mustCall(() => {
131      client.close();
132      server.close();
133    }));
134  }));
135}));
136