1'use strict'; 2 3const common = require('../common'); 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6const assert = require('assert'); 7const h2 = require('http2'); 8 9const server = h2.createServer(); 10 11server.on( 12 'stream', 13 common.mustCall((stream) => { 14 const assertSettings = (settings) => { 15 assert.strictEqual(typeof settings, 'object'); 16 assert.strictEqual(typeof settings.headerTableSize, 'number'); 17 assert.strictEqual(typeof settings.enablePush, 'boolean'); 18 assert.strictEqual(typeof settings.initialWindowSize, 'number'); 19 assert.strictEqual(typeof settings.maxFrameSize, 'number'); 20 assert.strictEqual(typeof settings.maxConcurrentStreams, 'number'); 21 assert.strictEqual(typeof settings.maxHeaderListSize, 'number'); 22 assert.strictEqual(typeof settings.maxHeaderSize, 'number'); 23 }; 24 25 const localSettings = stream.session.localSettings; 26 const remoteSettings = stream.session.remoteSettings; 27 assertSettings(localSettings); 28 assertSettings(remoteSettings); 29 30 // Test that stored settings are returned when called for second time 31 assert.strictEqual(stream.session.localSettings, localSettings); 32 assert.strictEqual(stream.session.remoteSettings, remoteSettings); 33 34 stream.respond({ 35 'content-type': 'text/html', 36 ':status': 200 37 }); 38 stream.end('hello world'); 39 }) 40); 41 42server.on('session', (session) => { 43 session.settings({ 44 maxConcurrentStreams: 2 45 }); 46}); 47 48server.listen( 49 0, 50 common.mustCall(() => { 51 const client = h2.connect(`http://localhost:${server.address().port}`, { 52 settings: { 53 enablePush: false, 54 initialWindowSize: 123456 55 } 56 }); 57 58 client.on( 59 'localSettings', 60 common.mustCall((settings) => { 61 assert(settings); 62 assert.strictEqual(settings.enablePush, false); 63 assert.strictEqual(settings.initialWindowSize, 123456); 64 assert.strictEqual(settings.maxFrameSize, 16384); 65 }, 2) 66 ); 67 68 let calledOnce = false; 69 client.on( 70 'remoteSettings', 71 common.mustCall((settings) => { 72 assert(settings); 73 assert.strictEqual( 74 settings.maxConcurrentStreams, 75 calledOnce ? 2 : (2 ** 32) - 1 76 ); 77 calledOnce = true; 78 }, 2) 79 ); 80 81 const headers = { ':path': '/' }; 82 83 const req = client.request(headers); 84 85 req.on('ready', common.mustCall(() => { 86 // pendingSettingsAck will be true if a SETTINGS frame 87 // has been sent but we are still waiting for an acknowledgement 88 assert(client.pendingSettingsAck); 89 })); 90 91 // State will only be valid after connect event is emitted 92 req.on('ready', common.mustCall(() => { 93 client.settings({ maxHeaderListSize: 1 }, common.mustCall()); 94 95 // Verify valid error ranges 96 [ 97 ['headerTableSize', -1], 98 ['headerTableSize', 2 ** 32], 99 ['initialWindowSize', -1], 100 ['initialWindowSize', 2 ** 32], 101 ['maxFrameSize', 16383], 102 ['maxFrameSize', 2 ** 24], 103 ['maxHeaderListSize', -1], 104 ['maxHeaderListSize', 2 ** 32], 105 ['maxHeaderSize', -1], 106 ['maxHeaderSize', 2 ** 32], 107 ].forEach((i) => { 108 const settings = {}; 109 settings[i[0]] = i[1]; 110 assert.throws( 111 () => client.settings(settings), 112 { 113 name: 'RangeError', 114 code: 'ERR_HTTP2_INVALID_SETTING_VALUE', 115 message: `Invalid value for setting "${i[0]}": ${i[1]}` 116 } 117 ); 118 }); 119 120 // Error checks for enablePush 121 [1, {}, 'test', [], null, Infinity, NaN].forEach((i) => { 122 assert.throws( 123 () => client.settings({ enablePush: i }), 124 { 125 name: 'TypeError', 126 code: 'ERR_HTTP2_INVALID_SETTING_VALUE', 127 message: `Invalid value for setting "enablePush": ${i}` 128 } 129 ); 130 }); 131 })); 132 133 req.on('response', common.mustCall()); 134 req.resume(); 135 req.on('end', common.mustCall(() => { 136 server.close(); 137 client.close(); 138 })); 139 req.end(); 140 }) 141); 142