1/* eslint-disable node-core/require-common-first, node-core/required-modules */ 2'use strict'; 3 4// An HTTP/2 testing tool used to create mock frames for direct testing 5// of HTTP/2 endpoints. 6 7const kFrameData = Symbol('frame-data'); 8const FLAG_EOS = 0x1; 9const FLAG_ACK = 0x1; 10const FLAG_EOH = 0x4; 11const FLAG_PADDED = 0x8; 12const PADDING = Buffer.alloc(255); 13 14const kClientMagic = Buffer.from('505249202a20485454502f322' + 15 'e300d0a0d0a534d0d0a0d0a', 'hex'); 16 17const kFakeRequestHeaders = Buffer.from('828684410f7777772e65' + 18 '78616d706c652e636f6d', 'hex'); 19 20 21const kFakeResponseHeaders = Buffer.from('4803333032580770726976617465611d' + 22 '4d6f6e2c203231204f63742032303133' + 23 '2032303a31333a323120474d546e1768' + 24 '747470733a2f2f7777772e6578616d70' + 25 '6c652e636f6d', 'hex'); 26 27function isUint32(val) { 28 return val >>> 0 === val; 29} 30 31function isUint24(val) { 32 return val >>> 0 === val && val <= 0xFFFFFF; 33} 34 35function isUint8(val) { 36 return val >>> 0 === val && val <= 0xFF; 37} 38 39function write32BE(array, pos, val) { 40 if (!isUint32(val)) 41 throw new RangeError('val is not a 32-bit number'); 42 array[pos++] = (val >> 24) & 0xff; 43 array[pos++] = (val >> 16) & 0xff; 44 array[pos++] = (val >> 8) & 0xff; 45 array[pos++] = val & 0xff; 46} 47 48function write24BE(array, pos, val) { 49 if (!isUint24(val)) 50 throw new RangeError('val is not a 24-bit number'); 51 array[pos++] = (val >> 16) & 0xff; 52 array[pos++] = (val >> 8) & 0xff; 53 array[pos++] = val & 0xff; 54} 55 56function write8(array, pos, val) { 57 if (!isUint8(val)) 58 throw new RangeError('val is not an 8-bit number'); 59 array[pos] = val; 60} 61 62class Frame { 63 constructor(length, type, flags, id) { 64 this[kFrameData] = Buffer.alloc(9); 65 write24BE(this[kFrameData], 0, length); 66 write8(this[kFrameData], 3, type); 67 write8(this[kFrameData], 4, flags); 68 write32BE(this[kFrameData], 5, id); 69 } 70 71 get data() { 72 return this[kFrameData]; 73 } 74} 75 76class SettingsFrame extends Frame { 77 constructor(ack = false) { 78 let flags = 0; 79 if (ack) 80 flags |= FLAG_ACK; 81 super(0, 4, flags, 0); 82 } 83} 84 85class DataFrame extends Frame { 86 constructor(id, payload, padlen = 0, final = false) { 87 let len = payload.length; 88 let flags = 0; 89 if (final) flags |= FLAG_EOS; 90 const buffers = [payload]; 91 if (padlen > 0) { 92 buffers.unshift(Buffer.from([padlen])); 93 buffers.push(PADDING.slice(0, padlen)); 94 len += padlen + 1; 95 flags |= FLAG_PADDED; 96 } 97 super(len, 0, flags, id); 98 buffers.unshift(this[kFrameData]); 99 this[kFrameData] = Buffer.concat(buffers); 100 } 101} 102 103class HeadersFrame extends Frame { 104 constructor(id, payload, padlen = 0, final = false) { 105 let len = payload.length; 106 let flags = FLAG_EOH; 107 if (final) flags |= FLAG_EOS; 108 const buffers = [payload]; 109 if (padlen > 0) { 110 buffers.unshift(Buffer.from([padlen])); 111 buffers.push(PADDING.slice(0, padlen)); 112 len += padlen + 1; 113 flags |= FLAG_PADDED; 114 } 115 super(len, 1, flags, id); 116 buffers.unshift(this[kFrameData]); 117 this[kFrameData] = Buffer.concat(buffers); 118 } 119} 120 121class PingFrame extends Frame { 122 constructor(ack = false) { 123 const buffers = [Buffer.alloc(8)]; 124 super(8, 6, ack ? 1 : 0, 0); 125 buffers.unshift(this[kFrameData]); 126 this[kFrameData] = Buffer.concat(buffers); 127 } 128} 129 130class AltSvcFrame extends Frame { 131 constructor(size) { 132 const buffers = [Buffer.alloc(size)]; 133 super(size, 10, 0, 0); 134 buffers.unshift(this[kFrameData]); 135 this[kFrameData] = Buffer.concat(buffers); 136 } 137} 138 139module.exports = { 140 Frame, 141 AltSvcFrame, 142 DataFrame, 143 HeadersFrame, 144 SettingsFrame, 145 PingFrame, 146 kFakeRequestHeaders, 147 kFakeResponseHeaders, 148 kClientMagic 149}; 150