'use strict'; const common = require('../common'); if (!common.hasCrypto) common.skip('missing crypto'); const assert = require('assert'); const http2 = require('http2'); const { URL } = require('url'); const Countdown = require('../common/countdown'); const server = http2.createServer(); server.on('stream', common.mustCall((stream) => { stream.session.altsvc('h2=":8000"', stream.id); stream.respond(); stream.end('ok'); })); server.on('session', common.mustCall((session) => { // Origin may be specified by string, URL object, or object with an // origin property. For string and URL object, origin is guaranteed // to be an ASCII serialized origin. For object with an origin // property, it is up to the user to ensure proper serialization. session.altsvc('h2=":8000"', 'https://example.org:8111/this'); session.altsvc('h2=":8000"', new URL('https://example.org:8111/this')); session.altsvc('h2=":8000"', { origin: 'https://example.org:8111' }); // Won't error, but won't send anything because the stream does not exist session.altsvc('h2=":8000"', 3); // Will error because the numeric stream id is out of valid range [0, -1, 1.1, 0xFFFFFFFF + 1, Infinity, -Infinity].forEach((input) => { assert.throws( () => session.altsvc('h2=":8000"', input), { code: 'ERR_OUT_OF_RANGE', name: 'RangeError [ERR_OUT_OF_RANGE]', message: 'The value of "originOrStream" is out of ' + `range. It must be > 0 && < 4294967296. Received ${input}` } ); }); // First argument must be a string [0, {}, [], null, Infinity].forEach((input) => { assert.throws( () => session.altsvc(input), { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError [ERR_INVALID_ARG_TYPE]' } ); }); ['\u0001', 'h2="\uff20"', '👀'].forEach((input) => { assert.throws( () => session.altsvc(input), { code: 'ERR_INVALID_CHAR', name: 'TypeError [ERR_INVALID_CHAR]', message: 'Invalid character in alt' } ); }); [{}, [], true].forEach((input) => { assert.throws( () => session.altsvc('clear', input), { code: 'ERR_INVALID_ARG_TYPE', name: 'TypeError [ERR_INVALID_ARG_TYPE]' } ); }); [ 'abc:', new URL('abc:'), { origin: 'null' }, { origin: '' } ].forEach((input) => { assert.throws( () => session.altsvc('h2=":8000', input), { code: 'ERR_HTTP2_ALTSVC_INVALID_ORIGIN', name: 'TypeError [ERR_HTTP2_ALTSVC_INVALID_ORIGIN]', message: 'HTTP/2 ALTSVC frames require a valid origin' } ); }); // Arguments + origin are too long for an ALTSVC frame assert.throws( () => { session.altsvc('h2=":8000"', `http://example.${'a'.repeat(17000)}.org:8000`); }, { code: 'ERR_HTTP2_ALTSVC_LENGTH', name: 'TypeError [ERR_HTTP2_ALTSVC_LENGTH]', message: 'HTTP/2 ALTSVC frames are limited to 16382 bytes' } ); })); server.listen(0, common.mustCall(() => { const client = http2.connect(`http://localhost:${server.address().port}`); const countdown = new Countdown(4, () => { client.close(); server.close(); }); client.on('altsvc', common.mustCall((alt, origin, stream) => { assert.strictEqual(alt, 'h2=":8000"'); switch (stream) { case 0: assert.strictEqual(origin, 'https://example.org:8111'); break; case 1: assert.strictEqual(origin, ''); break; default: assert.fail('should not happen'); } countdown.dec(); }, 4)); const req = client.request(); req.resume(); req.on('close', common.mustCall()); }));