Skip to content

Instantly share code, notes, and snippets.

@corporatepiyush
Last active September 15, 2025 08:09
Show Gist options
  • Save corporatepiyush/4319bb36144925a51712a6a8877cfbf8 to your computer and use it in GitHub Desktop.
Save corporatepiyush/4319bb36144925a51712a6a8877cfbf8 to your computer and use it in GitHub Desktop.
HTTP 2 implementation
'use strict';
process.title = 'http2server';
console.error('SERVER PID', process.pid);
/* ---------- Linux early tuning ------------------------------ */
if (process.platform === 'linux') {
try {
require('fs').writeFileSync('/proc/sys/net/core/somaxconn', '65535');
require('child_process').execSync([
'sysctl -w net.core.rmem_max=134217728', // 128MB receive buffer
'sysctl -w net.core.wmem_max=134217728', // 128MB send buffer
'sysctl -w net.ipv4.tcp_rmem="4096 65536 134217728"',
'sysctl -w net.ipv4.tcp_wmem="4096 65536 134217728"',
'sysctl -w net.ipv4.tcp_congestion_control=bbr', // BBR congestion control
'sysctl -w net.core.netdev_max_backlog=30000',
].join(' && '));
require('child_process').execSync('sysctl -w net.ipv4.tcp_tw_reuse=1 net.ipv4.tcp_fin_timeout=15');
require('child_process').execSync(`prlimit --nofile=1048576:1048576 --pid ${process.pid}`);
} catch (e) {
console.error('Linnux System tuning failed:', e.message);
}
}
/* ---------- cluster + CPU affinity (DISABLED FOR DEBUGGING) -- */
/*
const cluster = require('cluster');
const os = require('os');
if (cluster.isPrimary) {
const cores = os.cpus().length;
for (let i = 0; i < cores; i++) {
const w = cluster.fork();
if (process.platform === 'linux')
require('child_process').execSync(`taskset -cp ${i} ${w.process.pid}`);
}
cluster.on('exit', () => cluster.fork());
return;
}
*/
const os = require('os');
/* ---------- config ----------------------------------------- */
const PORT = Number(process.env.H2_PORT || 8080);
const HOST = process.env.H2_HOST || '0.0.0.0';
const UID = Number(process.env.H2_UID || 65534);
const GID = Number(process.env.H2_GID || 65534);
const MAX_RAM = Number(process.env.H2_MAX_RAM || 0.8);
/* ---------- logger ----------------------------------------- */
function log(level, msg, extra = {}) {
console.log(JSON.stringify({ time: new Date().toISOString(), level, msg, pid: process.pid, ...extra }));
}
const logger = { info: (m, e) => log('INFO', m, e), error: (m, e) => log('ERROR', m, e) };
/* ---------- HTTP/2 constants ------------------------------- */
const TYPES = { SETTINGS: 4, HEADERS: 1, DATA: 0, WINDOW_UPDATE: 8, PING: 6, GOAWAY: 7, RST_STREAM: 3 };
const FLAGS = { END_STREAM: 1, ACK: 1, END_HEADERS: 4 };
const DEFAULT_SETTINGS = {
HEADER_TABLE_SIZE: 8192, ENABLE_PUSH: 1, MAX_CONCURRENT_STREAMS: 8192, INITIAL_WINDOW_SIZE: 256 * 1024, MAX_FRAME_SIZE: 32768
};
/* ---------- HPACK Huffman and Integer Decoding ---------- */
const HPACK_HUFFMAN_CODES = [
[0x1ff8, 13], [0x7fffd8, 23], [0xfffffe2, 28], [0xfffffe3, 28], [0xfffffe4, 28], [0xfffffe5, 28], [0xfffffe6, 28], [0xfffffe7, 28],
[0xfffffe8, 28], [0xffffea, 24], [0x3ffffffc, 30], [0xfffffe9, 28], [0xfffffea, 28], [0x3ffffffd, 30], [0xfffffeb, 28], [0xfffffec, 28],
[0xfffffed, 28], [0xfffffee, 28], [0xfffffef, 28], [0xffffff0, 28], [0xffffff1, 28], [0xffffff2, 28], [0x3ffffffe, 30], [0xffffff3, 28],
[0xffffff4, 28], [0xffffff5, 28], [0xffffff6, 28], [0xffffff7, 28], [0xffffff8, 28], [0xffffff9, 28], [0xffffffa, 28], [0xffffffb, 28],
[0x14, 6], [0x3f8, 10], [0x3f9, 10], [0xffa, 12], [0x1ff9, 13], [0x15, 6], [0xf8, 8], [0x7fa, 11], [0x3fa, 10], [0x3fb, 10],
[0xf9, 8], [0x7fb, 11], [0xfa, 8], [0x16, 6], [0x17, 6], [0x18, 6], [0x0, 5], [0x1, 5], [0x2, 5], [0x19, 6], [0x1a, 6], [0x1b, 6],
[0x1c, 6], [0x1d, 6], [0x1e, 6], [0x1f, 6], [0x5c, 7], [0xfb, 8], [0x7ffc, 15], [0x20, 6], [0xffb, 12], [0x3fc, 10], [0x1ffa, 13], [0x21, 6],
[0x5d, 7], [0x5e, 7], [0x5f, 7], [0x60, 7], [0x61, 7], [0x62, 7], [0x63, 7], [0x64, 7], [0x65, 7], [0x66, 7], [0x67, 7], [0x68, 7],
[0x69, 7], [0x6a, 7], [0x6b, 7], [0x6c, 7], [0x6d, 7], [0x6e, 7], [0x6f, 7], [0x70, 7], [0x71, 7], [0x72, 7], [0xfc, 8], [0x73, 7],
[0xfd, 8], [0x1ffb, 13], [0x7fff0, 19], [0x1ffc, 13], [0x3ffc, 14], [0x22, 6], [0x7ffd, 15], [0x3, 5], [0x23, 6], [0x4, 5], [0x24, 6], [0x5, 5],
[0x25, 6], [0x26, 6], [0x27, 6], [0x6, 5], [0x74, 7], [0x75, 7], [0x28, 6], [0x29, 6], [0x2a, 6], [0x7, 5], [0x2b, 6], [0x76, 7],
[0x2c, 6], [0x8, 5], [0x9, 5], [0x2d, 6], [0x77, 7], [0x78, 7], [0x79, 7], [0x7a, 7], [0x7b, 7], [0x7ffe, 15], [0x7fc, 11], [0x3ffd, 14],
[0x1ffd, 13], [0xffffffc, 28], [0xfffe6, 20], [0x3fffd2, 22], [0xfffe7, 20], [0xfffe8, 20], [0x3fffd3, 22], [0x3fffd4, 22], [0x3fffd5, 22],
[0x7fffd9, 23], [0x3fffd6, 22], [0x7fffda, 23], [0x7fffdb, 23], [0x7fffdc, 23], [0x7fffdd, 23], [0x7fffde, 23], [0xffffeb, 24], [0x7fffdf, 23],
[0xffffec, 24], [0xffffed, 24], [0x3fffd7, 22], [0x7fffe0, 23], [0xffffee, 24], [0x7fffe1, 23], [0x7fffe2, 23], [0x7fffe3, 23], [0x7fffe4, 23],
[0x1fffdc, 21], [0x3fffd8, 22], [0x7fffe5, 23], [0x3fffd9, 22], [0x7fffe6, 23], [0x7fffe7, 23], [0xffffef, 24], [0x3fffda, 22], [0x1fffdd, 21],
[0xfffe9, 20], [0x3fffdb, 22], [0x3fffdc, 22], [0x7fffe8, 23], [0x7fffe9, 23], [0x1fffde, 21], [0x7fffea, 23], [0x3fffdd, 22], [0x3fffde, 22],
[0xfffff0, 24], [0x1fffdf, 21], [0x3fffdf, 22], [0x7fffeb, 23], [0x7fffec, 23], [0x1fffe0, 21], [0x1fffe1, 21], [0x3fffe0, 22], [0x1fffe2, 21],
[0x7fffed, 23], [0x3fffe1, 22], [0x7fffee, 23], [0x7fffef, 23], [0xfffea, 20], [0x3fffe2, 22], [0x3fffe3, 22], [0x3fffe4, 22], [0x7ffff0, 23],
[0x3fffe5, 22], [0x3fffe6, 22], [0x7ffff1, 23], [0x3ffffe0, 26], [0x3ffffe1, 26], [0xfffeb, 20], [0x7fff1, 19], [0x3fffe7, 22], [0x7ffff2, 23],
[0x3fffe8, 22], [0x1ffffec, 25], [0x3ffffe2, 26], [0x3ffffe3, 26], [0x3ffffe4, 26], [0x7ffffde, 27], [0x7ffffdf, 27], [0x3ffffe5, 26], [0xfffff1, 24],
[0x1ffffed, 25], [0x7fff2, 19], [0x1fffe3, 21], [0x3ffffe6, 26], [0x7ffffe0, 27], [0x7ffffe1, 27], [0x3ffffe7, 26], [0x7ffffe2, 27], [0xfffff2, 24],
[0x1fffe4, 21], [0x1fffe5, 21], [0x3ffffe8, 26], [0x3ffffe9, 26], [0xffffffd, 28], [0x7ffffe3, 27], [0x7ffffe4, 27], [0x7ffffe5, 27], [0xfffec, 20],
[0xfffff3, 24], [0xfffed, 20], [0x1fffe6, 21], [0x3fffe9, 22], [0x1fffe7, 21], [0x1fffe8, 21], [0x7ffff3, 23], [0x3fffea, 22], [0x3fffeb, 22],
[0x1ffffee, 25], [0x1ffffef, 25], [0xfffff4, 24], [0xfffff5, 24], [0x3ffffea, 26], [0x7ffff4, 23], [0x3ffffeb, 26], [0x7ffffe6, 27], [0x3ffffec, 26],
[0x3ffffed, 26], [0x7ffffe7, 27], [0x7ffffe8, 27], [0x7ffffe9, 27], [0x7ffffea, 27], [0x7ffffeb, 27], [0xffffffe, 28], [0x7ffffec, 27], [0x7ffffed, 27],
[0x7ffffee, 27], [0x7ffffef, 27], [0x7fffff0, 27], [0x3ffffee, 26], [0x3fffffff, 30]
];
let HPACK_HUFFMAN_DECODE_TREE;
function buildHuffmanDecodeTree() {
const root = {};
for (let i = 0; i < HPACK_HUFFMAN_CODES.length; i++) {
const [code, bits] = HPACK_HUFFMAN_CODES[i];
let node = root;
// Build tree from most significant bit to least significant bit
for (let j = bits - 1; j >= 0; j--) {
const bit = (code >> j) & 1;
if (!node[bit]) {
node[bit] = {};
}
node = node[bit];
}
// Mark leaf node with symbol
node.symbol = i;
node.isLeaf = true;
}
HPACK_HUFFMAN_DECODE_TREE = root;
}
function huffmanDecode(buf) {
if (!HPACK_HUFFMAN_DECODE_TREE) buildHuffmanDecodeTree();
const result = [];
let node = HPACK_HUFFMAN_DECODE_TREE;
// Process each bit
for (let byteIndex = 0; byteIndex < buf.length; byteIndex++) {
const byte = buf[byteIndex];
for (let bitIndex = 7; bitIndex >= 0; bitIndex--) {
const bit = (byte >> bitIndex) & 1;
if (!node[bit]) {
// Check for EOS padding only at the end
if (byteIndex === buf.length - 1) {
// Validate that remaining bits are all 1s (EOS padding)
let isValidPadding = true;
for (let remainingBit = bitIndex; remainingBit >= 0; remainingBit--) {
if (((byte >> remainingBit) & 1) === 0) {
isValidPadding = false;
break;
}
}
if (isValidPadding) {
// Valid EOS padding
return Buffer.from(result);
}
}
// Invalid path
throw new Error(`Invalid Huffman path at byte ${byteIndex}, bit ${bitIndex}`);
}
node = node[bit];
if (node.isLeaf) {
if (node.symbol === 256) { // EOS symbol
return Buffer.from(result);
}
result.push(node.symbol);
node = HPACK_HUFFMAN_DECODE_TREE; // Reset to root
}
}
}
return Buffer.from(result);
}
function encodeInt(val, prefixBits) {
const prefixMask = (1 << prefixBits) - 1;
if (val < prefixMask) {
return Buffer.from([val]);
}
const out = [prefixMask];
val -= prefixMask;
while (val >= 128) {
out.push((val % 128) + 128);
val = Math.floor(val / 128);
}
out.push(val);
return Buffer.from(out);
}
function encodeString(str) {
const len = encodeInt(str.length, 7);
// Set Huffman bit to 0
len[0] &= 0x7F;
return Buffer.concat([len, Buffer.from(str)]);
}
function decodeInt(buf, off, prefixBits) {
const prefixMask = (1 << prefixBits) - 1;
let val = buf[off] & prefixMask;
if (val < prefixMask) {
return [val, 1];
}
let m = 0;
let i = off + 1;
let byte;
do {
byte = buf[i++];
val += (byte & 0x7f) * (1 << m);
m += 7;
} while (byte & 0x80);
return [val, i - off];
}
/* ---------- HPACK tables ----------------------------------- */
const STATIC_TABLE = [
[':authority', ''], // 1
[':method', 'GET'], // 2
[':method', 'POST'], // 3
[':path', '/'], // 4
[':path', '/index.html'], // 5
[':scheme', 'http'], // 6
[':scheme', 'https'], // 7
[':status', '200'], // 8
[':status', '204'], // 9
[':status', '206'], // 10
[':status', '304'], // 11
[':status', '400'], // 12
[':status', '404'], // 13
[':status', '500'], // 14
['accept-charset', ''], // 15
['accept-encoding', 'gzip, deflate'], // 16
['accept-language', ''], // 17
['accept-ranges', ''], // 18
['accept', ''], // 19
['access-control-allow-origin', ''], // 20
['age', ''], // 21
['allow', ''], // 22
['authorization', ''], // 23
['cache-control', ''], // 24
['content-disposition', ''], // 25
['content-encoding', ''], // 26
['content-language', ''], // 27
['content-length', ''], // 28
['content-location', ''], // 29
['content-range', ''], // 30
['content-type', ''], // 31
['cookie', ''], // 32
['date', ''], // 33
['etag', ''], // 34
['expect', ''], // 35
['expires', ''], // 36
['from', ''], // 37
['host', ''], // 38
['if-match', ''], // 39
['if-modified-since', ''], // 40
['if-none-match', ''], // 41
['if-range', ''], // 42
['if-unmodified-since', ''], // 43
['last-modified', ''], // 44
['link', ''], // 45
['location', ''], // 46
['max-forwards', ''], // 47
['proxy-authenticate', ''], // 48
['proxy-authorization', ''], // 49
['range', ''], // 50
['referer', ''], // 51
['refresh', ''], // 52
['retry-after', ''], // 53
['server', ''], // 54
['set-cookie', ''], // 55
['strict-transport-security', ''], // 56
['transfer-encoding', ''], // 57
['user-agent', ''], // 58
['vary', ''], // 59
['via', ''], // 60
['www-authenticate', ''] // 61
];
/* ----- Enhanced perfect-hash maps for static table ----- */
const STATIC_PAIR = new Map(); // name+\0+value → index
const STATIC_NAME = new Map(); // name → lowest index
const STATIC_STATUS = new Map(); // status codes → direct buffer
// Build lookup tables
STATIC_TABLE.forEach(([n, v], i) => {
const key = n + '\0' + v;
if (!STATIC_PAIR.has(key)) STATIC_PAIR.set(key, i + 1);
if (!STATIC_NAME.has(n)) STATIC_NAME.set(n, i + 1);
// Pre-build status code buffers for instant lookup
if (n === ':status' && v) {
const statusCode = parseInt(v);
if (!STATIC_STATUS.has(statusCode)) {
const buf = Buffer.allocUnsafe(1);
buf[0] = 0x80 | (i + 1); // indexed representation
STATIC_STATUS.set(statusCode, buf);
}
}
});
// Additional common status codes (beyond the static table)
const EXTENDED_STATUS_CODES = new Map([
[200, STATIC_STATUS.get(200)], // Already in static table (index 8)
[204, STATIC_STATUS.get(204)], // Already in static table (index 9)
[206, STATIC_STATUS.get(206)], // Already in static table (index 10)
[304, STATIC_STATUS.get(304)], // Already in static table (index 11)
[400, STATIC_STATUS.get(400)], // Already in static table (index 12)
[404, STATIC_STATUS.get(404)], // Already in static table (index 13)
[500, STATIC_STATUS.get(500)], // Already in static table (index 14)
// For other status codes, create literal representations
[201, createLiteralStatus('201')],
[202, createLiteralStatus('202')],
[301, createLiteralStatus('301')],
[302, createLiteralStatus('302')],
[401, createLiteralStatus('401')],
[403, createLiteralStatus('403')],
[405, createLiteralStatus('405')],
[409, createLiteralStatus('409')],
[422, createLiteralStatus('422')],
[429, createLiteralStatus('429')],
[502, createLiteralStatus('502')],
[503, createLiteralStatus('503')]
]);
function createLiteralStatus(statusStr) {
// Literal with incremental indexing using :status name (index 8)
const result = [];
// Pattern: 01 + 6-bit index (8) = 01001000 = 0x48
result.push(0x48);
// String length (without Huffman encoding)
result.push(statusStr.length);
// Status code as string
for (let i = 0; i < statusStr.length; i++) {
result.push(statusStr.charCodeAt(i));
}
return Buffer.from(result);
}
/* ---------- optimised HPACK class ----------------------- */
class HPACK {
constructor(maxTableSize = 8192) {
this.maxSize = maxTableSize;
this.size = 0;
// circular buffer for dynamic entries (pre-allocated)
this.ring = new Array(256);
this.head = 0; // next write position
this.tail = 0; // oldest valid position
this.len = 0; // active count
}
/* ---- static lookup – O(1) ---- */
static indexOf(name, value) {
const pair = STATIC_PAIR.get(name + '\0' + value);
if (pair !== undefined) return pair;
return value === '' ? (STATIC_NAME.get(name) || -1) : -1;
}
/* ---- dynamic lookup – only scans ring ---- */
indexOf(name, value) {
const wantEmpty = (value === '');
const base = STATIC_TABLE.length + 1;
for (let i = 0; i < this.len; i++) {
const idx = (this.tail + i) & 255;
const [n, v] = this.ring[idx];
if (n === name && (wantEmpty || v === value)) return base + i;
}
return -1;
}
encode(flatHeaders) {
const out = [];
for (let i = 0; i < flatHeaders.length; i += 2) {
const name = flatHeaders[i];
const value = String(flatHeaders[i + 1]);
// Special fast path for status codes
if (name === ':status') {
const statusCode = parseInt(value);
const statusBuf = EXTENDED_STATUS_CODES.get(statusCode);
if (statusBuf) {
out.push(statusBuf);
continue;
}
}
// 1. try static pair (exact name+value match)
const stPair = STATIC_PAIR.get(name + '\0' + value);
if (stPair !== undefined) {
out.push(encodeIndexed(stPair));
continue;
}
// 2. try dynamic pair
const dynPair = this.indexOf(name, value);
if (dynPair !== -1) {
out.push(encodeIndexed(dynPair));
continue;
}
// 3. literal with incremental indexing using static name if available
const nameIdx = STATIC_NAME.get(name) || this.indexOf(name, '');
const type = encodeInt(nameIdx > 0 ? nameIdx : 0, 6);
type[0] |= 0x40; // Set literal with incremental indexing pattern
out.push(type);
if (nameIdx <= 0) {
out.push(encodeString(name));
}
out.push(encodeString(value));
this.add(name, value);
}
return Buffer.concat(out);
}
/* ---- decode (unchanged logic, only faster ring access) ---- */
decode(buf) {
const headers = {};
let off = 0;
while (off < buf.length) {
const byte = buf[off];
if (byte & 0x80) { // indexed
const [idx, rd] = decodeInt(buf, off, 7); off += rd;
const entry = idx <= STATIC_TABLE.length
? STATIC_TABLE[idx - 1]
: this.ring[(this.tail + (idx - STATIC_TABLE.length - 1)) & 255];
if (entry) headers[entry[0]] = entry[1];
} else if (byte & 0x40) { // lit with inc idx
const [nameIdx, rd] = decodeInt(buf, off, 6); off += rd;
let name; let value;
if (nameIdx > 0) {
const e = nameIdx <= STATIC_TABLE.length
? STATIC_TABLE[nameIdx - 1]
: this.ring[(this.tail + (nameIdx - STATIC_TABLE.length - 1)) & 255];
name = e ? e[0] : 'UNKNOWN';
} else {
const huff = (buf[off] & 0x80) !== 0;
const [len, lrd] = decodeInt(buf, off, 7); off += lrd;
const slice = buf.subarray(off, off + len); off += len;
name = (huff ? huffmanDecode(slice) : slice).toString();
}
const huff = (buf[off] & 0x80) !== 0;
const [len, lrd] = decodeInt(buf, off, 7); off += lrd;
const slice = buf.subarray(off, off + len); off += len;
value = (huff ? huffmanDecode(slice) : slice).toString();
headers[name] = value;
this.add(name, value);
} else if (byte & 0x20) { // dyn table size update
const [size, rd] = decodeInt(buf, off, 5); off += rd;
this.maxSize = size;
} else { // lit without indexing / never indexed
const [nameIdx, rd] = decodeInt(buf, off, 4); off += rd;
let name; let value;
if (nameIdx > 0) {
const e = nameIdx <= STATIC_TABLE.length
? STATIC_TABLE[nameIdx - 1]
: this.ring[(this.tail + (nameIdx - STATIC_TABLE.length - 1)) & 255];
name = e ? e[0] : 'UNKNOWN';
} else {
const huff = (buf[off] & 0x80) !== 0;
const [len, lrd] = decodeInt(buf, off, 7); off += lrd;
const slice = buf.subarray(off, off + len); off += len;
name = (huff ? huffmanDecode(slice) : slice).toString();
}
const huff = (buf[off] & 0x80) !== 0;
const [len, lrd] = decodeInt(buf, off, 7); off += lrd;
const slice = buf.subarray(off, off + len); off += len;
value = (huff ? huffmanDecode(slice) : slice).toString();
headers[name] = value;
}
}
return headers;
}
/* ---- add – circular buffer, no shift ---- */
add(name, value) {
const size = name.length + value.length + 32;
const entry = [name, value];
// make room
while (this.len > 0 && this.size + size > this.maxSize) {
const [n, v] = this.ring[this.tail];
this.size -= (n.length + v.length + 32);
this.tail = (this.tail + 1) & 255;
this.len--;
}
// insert
this.ring[this.head] = entry;
this.head = (this.head + 1) & 255;
this.len++;
this.size += size;
}
}
/* ---------- tiny helpers ---------- */
function encodeIndexed(idx) {
const buf = encodeInt(idx, 7);
buf[0] |= 0x80;
return buf;
}
/* ---------- race-free, zero-copy, batched Http2Session ---- */
const MAX_COALESCE = 4 * 1024; // low latency
class Http2Session {
constructor(socket, isAlpn = false) {
this.socket = socket;
this.socket.setNoDelay(true);
this.streams = new Map();
this.settings = { ...DEFAULT_SETTINGS };
this.peer = { ...DEFAULT_SETTINGS };
this.nextID = 2;
this.connWin = DEFAULT_SETTINGS.INITIAL_WINDOW_SIZE;
/* ---- zero-copy ingress ---- */
this.slabs = []; // {buf, off, len}[]
this.bufSize = 0;
this.expectedPreface = Buffer.from('PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n');
this.prefaceReceived = false;
/* ---- batch writes ---- */
this.writeBuf = Buffer.allocUnsafe(MAX_COALESCE + 9);
this.writeOff = 0;
this.draining = false;
this.destroyed = false;
this.encoder = new HPACK();
this.decoder = new HPACK();
socket.on('data', b => this.ingest(b));
socket.on('close', () => this.destroy());
socket.on('error', e => { logger.error('socket', { error: e.message }); });
// initial SETTINGS
this.writeFrame({ type: TYPES.SETTINGS, flags: 0, streamID: 0, payload: EMPTY_BUF });
this._flush();
}
/* ---- frame write – coalesce small frames ---- */
writeFrame({ type, flags, streamID, payload }, noFlush = false) {
const pLen = payload.length;
const hdrLen = 9;
const total = hdrLen + pLen;
if (total <= MAX_COALESCE && this.writeOff + total <= MAX_COALESCE + 9) {
// fast path – copy into batch buffer
const hdr = this.writeBuf;
let off = this.writeOff;
hdr[off] = (pLen >>> 16) & 0xff;
hdr[off + 1] = (pLen >>> 8) & 0xff;
hdr[off + 2] = pLen & 0xff;
hdr[off + 3] = type;
hdr[off + 4] = flags;
hdr.writeUInt32BE(streamID & 0x7FFFFFFF, off + 5);
off += hdrLen;
if (pLen) {
payload.copy(hdr, off);
off += pLen;
}
this.writeOff = off;
if (!this.draining && !noFlush) this._flush();
return;
}
// slow path – frame larger than coalesce limit
this._flush();
const hdr = Buffer.allocUnsafe(9);
hdr[0] = (pLen >>> 16) & 0xff;
hdr[1] = (pLen >>> 8) & 0xff;
hdr[2] = pLen & 0xff;
hdr[3] = type;
hdr[4] = flags;
hdr.writeUInt32BE(streamID & 0x7FFFFFFF, 5);
this.socket.write(hdr);
if (pLen) this.socket.write(payload);
}
_flush() {
if (this.writeOff === 0) return;
this.socket.write(this.writeBuf.subarray(0, this.writeOff));
this.writeOff = 0;
}
_drain() {
this._flush();
this.draining = false;
}
/* ---- ingress – zero-copy slab ---- */
ingest(chunk) {
this.slabs.push({ buf: chunk, off: 0, len: chunk.length });
this.bufSize += chunk.length;
//console.error('INGEST bufSize=%d prefaceReceived=%s', this.bufSize, this.prefaceReceived);
if (!this.prefaceReceived) {
if (this.bufSize < this.expectedPreface.length) return;
if (!this._checkPreface()) return this.destroy();
}
this._parseFrames();
}
_checkPreface() {
const preface = this._slice(this.expectedPreface.length);
//console.error('PREFACE CHECK got', preface.toString('hex'));
//console.error('PREFACE WANT', this.expectedPreface.toString('hex'));
if (!preface.equals(this.expectedPreface)) return false;
this.prefaceReceived = true;
return true;
}
_parseFrames() {
//console.error('PARSE entry bufSize=%d', this.bufSize);
try {
while (this.bufSize >= 9) {
const h = this._peek(9);
//console.error('PARSE peek', h.toString('hex'));
const len = (h[0] << 16) | (h[1] << 8) | h[2];
//console.error('PARSE frame len=%d', len);
if (this.bufSize < 9 + len) {
//console.error('PARSE need=%d have=%d -> break', 9 + len, this.bufSize);
break;
}
this._drop(9);
const payload = this._slice(len);
const frame = { type: h[3], flags: h[4], streamID: h.readUInt32BE(5) & 0x7FFFFFFF, payload };
this.handleFrame(frame);
}
} catch (e) {
logger.error('ingress', { error: e.message });
this.goAway(0x2);
}
}
_peek(n) {
if (this.bufSize < n) throw new Error('peek underflow');
const out = Buffer.allocUnsafe(n);
let destOff = 0;
for (let i = 0; i < this.slabs.length && destOff < n; i++) {
const s = this.slabs[i];
const take = Math.min(s.len, n - destOff);
s.buf.copy(out, destOff, s.off, s.off + take);
destOff += take;
}
return out;
}
_slice(len) {
if (len === 0) return EMPTY_BUF;
if (this.bufSize < len) throw new Error('slice underflow');
const out = Buffer.allocUnsafe(len);
let destOff = 0;
while (destOff < len && this.slabs.length > 0) {
const s = this.slabs[0];
const take = Math.min(s.len, len - destOff);
s.buf.copy(out, destOff, s.off, s.off + take);
s.off += take;
s.len -= take;
destOff += take;
if (s.len === 0) {
this.slabs.shift();
}
}
this.bufSize -= len;
return out;
}
_drop(n) {
if (this.bufSize < n) throw new Error('drop underflow');
this.bufSize -= n;
while (n > 0 && this.slabs.length) {
const s = this.slabs[0];
const take = Math.min(s.len, n);
s.off += take;
s.len -= take;
n -= take;
if (s.len === 0) {
this.slabs.shift();
}
}
}
/* ---- frame handling – unchanged semantics ---- */
handleFrame(f) {
switch (f.type) {
case TYPES.SETTINGS: return this.onSettings(f);
case TYPES.HEADERS: return this.onHeaders(f);
case TYPES.DATA: return this.onData(f);
case TYPES.WINDOW_UPDATE: return this.onWindowUpdate(f);
case TYPES.PING:
return this.writeFrame({ type: TYPES.PING, flags: FLAGS.ACK, streamID: 0, payload: f.payload });
case TYPES.RST_STREAM: return this.onRstStream(f);
case TYPES.GOAWAY: return this.destroy();
}
}
onSettings(f) {
if (f.flags & FLAGS.ACK) return;
let off = 0;
while (off < f.payload.length) {
const id = f.payload.readUInt16BE(off), val = f.payload.readUInt32BE(off + 2);
off += 6;
this.peer[id] = val;
}
this.writeFrame({ type: TYPES.SETTINGS, flags: FLAGS.ACK, streamID: 0, payload: EMPTY_BUF });
}
onHeaders(f) {
const s = this.getStream(f.streamID, true);
const headers = this.decoder.decode(f.payload);
s.handleHeaders(headers, !!(f.flags & FLAGS.END_STREAM));
}
onData(f) {
const s = this.getStream(f.streamID);
if (!s) return this.rstStream(f.streamID, 0x5);
const len = f.payload.length;
if (len) {
this.connWin -= len;
s.window -= len;
// If connection window is less than half full, top it up.
if (this.connWin < (this.settings.INITIAL_WINDOW_SIZE / 2)) {
const increment = this.settings.INITIAL_WINDOW_SIZE - this.connWin;
this.connWin += increment;
this.writeFrame({ type: TYPES.WINDOW_UPDATE, flags: 0, streamID: 0, payload: u32buf(increment) });
}
// If stream window is less than half full, top it up.
if (s.window < (this.settings.INITIAL_WINDOW_SIZE / 2)) {
const increment = this.settings.INITIAL_WINDOW_SIZE - s.window;
s.window += increment;
this.writeFrame({ type: TYPES.WINDOW_UPDATE, flags: 0, streamID: f.streamID, payload: u32buf(increment) });
}
}
s.handleData(f.payload, !!(f.flags & FLAGS.END_STREAM));
}
onWindowUpdate(f) {
const inc = f.payload.readUInt32BE(0) & 0x7fffffff;
if (inc === 0) return this.goAway(0x1);
if (f.streamID === 0) {
if (this.connWin + inc > 0x7fffffff) return this.goAway(0x7);
this.connWin += inc;
} else {
const s = this.getStream(f.streamID); if (s) s.window += inc;
}
}
onRstStream(f) { const s = this.getStream(f.streamID); if (s) s.destroy(); }
rstStream(id, code) {
const b = u32buf(code);
this.writeFrame({ type: TYPES.RST_STREAM, flags: 0, streamID: id, payload: b });
const s = this.streams.get(id); if (s) s.destroy();
}
goAway(code) {
const b = Buffer.allocUnsafe(8);
b.writeUInt32BE(this.nextID, 0);
b.writeUInt32BE(code, 4);
this.writeFrame({ type: TYPES.GOAWAY, flags: 0, streamID: 0, payload: b });
this.destroy();
}
getStream(id, create = false) {
if (this.streams.has(id)) return this.streams.get(id);
if (!create) return null;
const s = new Http2Stream(this, id);
this.streams.set(id, s);
return s;
}
destroy() {
if (this.destroyed) return;
this.destroyed = true;
this._flush();
try { this.socket.destroy(); } catch { }
this.streams.forEach(s => s.destroy());
this.streams.clear();
}
}
/* ---------- helpers ---------- */
const EMPTY_BUF = Buffer.alloc(0);
function u32buf(v) {
const b = Buffer.allocUnsafe(4);
b.writeUInt32BE(v, 0);
return b;
}
/* ---------- stream ----------------------------------------- */
class Http2Stream {
constructor(session, id) {
//console.error(`STREAM ${id}: created`);
this.session = session;
this.id = id;
this.state = 'open';
this.window = DEFAULT_SETTINGS.INITIAL_WINDOW_SIZE;
this.headers = null;
this.method = null;
this.path = null;
this.chunks = [];
this.length = 0;
this.maxBody = 8 * 1024 * 1024;
}
handleHeaders(headers, end) {
this.headers = headers;
this.method = headers[':method'];
this.path = headers[':path'];
if (end) this.handleEnd();
}
handleData(chunk, end) {
this.length += chunk.length;
if (this.length > this.maxBody) return this.rst(0x8);
this.chunks.push(chunk);
if (end) this.handleEnd();
}
handleEnd() {
//console.error(`STREAM ${this.id}: request body received, routing...`);
const body = Buffer.concat(this.chunks);
this.chunks = [];
try {
route(this, body);
} catch (e) {
//console.error('ROUTE EX', e);
logger.error('route', { error: e.message });
this.rst(0x2);
}
}
rst(code) { this.session.rstStream(this.id, code); }
respond(status, headers, body) {
//console.error(`STREAM ${this.id}: responding with status ${status}`);
// Build headers with :status first (pseudo-headers must come first)
const flatHeaders = [':status', String(status)];
// Add other headers
for (const name in headers) {
if (!name.startsWith(':')) { // Skip any pseudo-headers in input
flatHeaders.push(name, String(headers[name]));
}
}
try {
const hdrBlock = this.session.encoder.encode(flatHeaders);
this.session.writeFrame({ type: TYPES.HEADERS, flags: FLAGS.END_HEADERS, streamID: this.id, payload: hdrBlock }, true);
if (body) {
if (typeof body === 'string') body = Buffer.from(body);
this.session.writeFrame({ type: TYPES.DATA, flags: FLAGS.END_STREAM, streamID: this.id, payload: body }, true);
} else {
this.session.writeFrame({ type: TYPES.DATA, flags: FLAGS.END_STREAM, streamID: this.id, payload: Buffer.alloc(0) }, true);
}
} finally {
this.session._flush();
}
}
destroy() {
//console.error(`STREAM ${this.id}: destroyed`);
this.state = 'closed';
this.session.streams.delete(this.id);
}
}
/* ---------- router ---------------------------------------- */
function route(stream, body) {
//console.error('ROUTE method=%s path=%s', stream.method, stream.path);
const { method, path, headers } = stream;
const url = new URL(path, 'https://localhost');
if (method === 'GET' && url.pathname === '/healthz') {
return stream.respond(200, {}, 'ok');
}
if (method === 'POST' && url.pathname === '/') {
const ct = (headers['content-type'] || '').split(';')[0].trim();
try {
if (ct === 'application/json') {
const obj = JSON.parse(body.toString());
return stream.respond(200, { 'content-type': 'application/json' }, JSON.stringify({ received: obj }));
}
if (ct === 'application/x-www-form-urlencoded') {
const params = new URLSearchParams(body.toString());
return stream.respond(200, { 'content-type': 'application/json' }, JSON.stringify({ received: Object.fromEntries(params) }));
}
} catch (e) { logger.error('POST', { error: e.message }); return stream.respond(400, {}, 'Bad Request'); }
}
stream.respond(404, {}, 'Not Found');
}
/* ---------- drop root ------------------------------------- */
if (process.getuid && process.getuid() === 0) {
try { process.setgid(GID); process.setuid(UID); }
catch (e) { logger.error('drop root', { error: e.message }); process.exit(1); }
}
/* ---------- start server (H2C) ----------------------------- */
const net = require('net');
const server = net.createServer((socket) => {
new Http2Session(socket);
});
server.listen({ port: PORT, host: HOST }, () => {
});
@corporatepiyush
Copy link
Author

curl --http2-prior-knowledge http://localhost:8080/healthz

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment