1'use strict'; 2 3const common = require('../common'); 4if (!common.hasCrypto) 5 common.skip('missing crypto'); 6 7const assert = require('assert'); 8const http2 = require('http2'); 9const { URL } = require('url'); 10const Countdown = require('../common/countdown'); 11 12const server = http2.createServer(); 13server.on('stream', common.mustCall((stream) => { 14 stream.session.altsvc('h2=":8000"', stream.id); 15 stream.respond(); 16 stream.end('ok'); 17})); 18server.on('session', common.mustCall((session) => { 19 // Origin may be specified by string, URL object, or object with an 20 // origin property. For string and URL object, origin is guaranteed 21 // to be an ASCII serialized origin. For object with an origin 22 // property, it is up to the user to ensure proper serialization. 23 session.altsvc('h2=":8000"', 'https://example.org:8111/this'); 24 session.altsvc('h2=":8000"', new URL('https://example.org:8111/this')); 25 session.altsvc('h2=":8000"', { origin: 'https://example.org:8111' }); 26 27 // Won't error, but won't send anything because the stream does not exist 28 session.altsvc('h2=":8000"', 3); 29 30 // Will error because the numeric stream id is out of valid range 31 [0, -1, 1.1, 0xFFFFFFFF + 1, Infinity, -Infinity].forEach((input) => { 32 assert.throws( 33 () => session.altsvc('h2=":8000"', input), 34 { 35 code: 'ERR_OUT_OF_RANGE', 36 name: 'RangeError', 37 message: 'The value of "originOrStream" is out of ' + 38 `range. It must be > 0 && < 4294967296. Received ${input}` 39 } 40 ); 41 }); 42 43 // First argument must be a string 44 [0, {}, [], null, Infinity].forEach((input) => { 45 assert.throws( 46 () => session.altsvc(input), 47 { 48 code: 'ERR_INVALID_ARG_TYPE', 49 name: 'TypeError' 50 } 51 ); 52 }); 53 54 ['\u0001', 'h2="\uff20"', ''].forEach((input) => { 55 assert.throws( 56 () => session.altsvc(input), 57 { 58 code: 'ERR_INVALID_CHAR', 59 name: 'TypeError', 60 message: 'Invalid character in alt' 61 } 62 ); 63 }); 64 65 [{}, [], true].forEach((input) => { 66 assert.throws( 67 () => session.altsvc('clear', input), 68 { 69 code: 'ERR_INVALID_ARG_TYPE', 70 name: 'TypeError' 71 } 72 ); 73 }); 74 75 [ 76 'abc:', 77 new URL('abc:'), 78 { origin: 'null' }, 79 { origin: '' } 80 ].forEach((input) => { 81 assert.throws( 82 () => session.altsvc('h2=":8000', input), 83 { 84 code: 'ERR_HTTP2_ALTSVC_INVALID_ORIGIN', 85 name: 'TypeError', 86 message: 'HTTP/2 ALTSVC frames require a valid origin' 87 } 88 ); 89 }); 90 91 // Arguments + origin are too long for an ALTSVC frame 92 assert.throws( 93 () => { 94 session.altsvc('h2=":8000"', 95 `http://example.${'a'.repeat(17000)}.org:8000`); 96 }, 97 { 98 code: 'ERR_HTTP2_ALTSVC_LENGTH', 99 name: 'TypeError', 100 message: 'HTTP/2 ALTSVC frames are limited to 16382 bytes' 101 } 102 ); 103})); 104 105server.listen(0, common.mustCall(() => { 106 const client = http2.connect(`http://localhost:${server.address().port}`); 107 108 const countdown = new Countdown(4, () => { 109 client.close(); 110 server.close(); 111 }); 112 113 client.on('altsvc', common.mustCall((alt, origin, stream) => { 114 assert.strictEqual(alt, 'h2=":8000"'); 115 switch (stream) { 116 case 0: 117 assert.strictEqual(origin, 'https://example.org:8111'); 118 break; 119 case 1: 120 assert.strictEqual(origin, ''); 121 break; 122 default: 123 assert.fail('should not happen'); 124 } 125 countdown.dec(); 126 }, 4)); 127 128 const req = client.request(); 129 req.resume(); 130 req.on('close', common.mustCall()); 131})); 132