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