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