feat: enhance HTTP/2 support by ensuring Host header is set and adding multiplexed request tests

This commit is contained in:
2026-02-20 18:30:57 +00:00
parent 9521f2e044
commit d4739045cd
3 changed files with 75 additions and 69 deletions

View File

@@ -255,81 +255,62 @@ tap.test('HTTPS with TLS termination: multiple requests through TLS', async (too
// ===========================================================================
// 5. TLS ALPN negotiation verification
// ===========================================================================
tap.test('TLS ALPN negotiation: h2 advertised, h1.1 functional', async (tools) => {
tap.test('HTTP/2 end-to-end: ALPN h2 with multiplexed requests', async (tools) => {
tools.timeout(15000);
// Verify the Rust TLS layer advertises h2 via ALPN by inspecting a raw TLS socket
const alpnProtocol = await new Promise<string>((resolve, reject) => {
const socket = tls.connect(
{
host: 'localhost',
port: PROXY_HTTPS_PORT,
ALPNProtocols: ['h2', 'http/1.1'],
rejectUnauthorized: false,
servername: 'localhost',
},
() => {
const proto = (socket as any).alpnProtocol || 'none';
socket.destroy();
resolve(proto);
},
);
socket.on('error', reject);
socket.setTimeout(5000, () => {
socket.destroy(new Error('timeout'));
});
// Connect an HTTP/2 session over TLS
const session = http2.connect(`https://localhost:${PROXY_HTTPS_PORT}`, {
rejectUnauthorized: false,
});
await new Promise<void>((resolve, reject) => {
session.on('connect', () => resolve());
session.on('error', reject);
setTimeout(() => reject(new Error('h2 connect timeout')), 5000);
});
// Verify ALPN negotiated h2
const alpnProtocol = (session.socket as tls.TLSSocket).alpnProtocol;
console.log(`TLS ALPN negotiated protocol: ${alpnProtocol}`);
// Rust advertises h2+http/1.1; the negotiated protocol should be one of them
expect(['h2', 'http/1.1'].includes(alpnProtocol)).toBeTrue();
expect(alpnProtocol).toEqual('h2');
// Now try an actual HTTP/2 session — Rust may or may not support h2 end-to-end
let h2Supported = false;
try {
const session = http2.connect(`https://localhost:${PROXY_HTTPS_PORT}`, {
rejectUnauthorized: false,
});
// Send 5 multiplexed POST requests on the same h2 session
const REQUEST_COUNT = 5;
const promises: Promise<{ status: number; body: string }>[] = [];
await new Promise<void>((resolve, reject) => {
session.on('connect', () => resolve());
session.on('error', reject);
setTimeout(() => reject(new Error('h2 connect timeout')), 5000);
});
for (let i = 0; i < REQUEST_COUNT; i++) {
promises.push(
new Promise<{ status: number; body: string }>((resolve, reject) => {
const reqStream = session.request({
':method': 'POST',
':path': '/echo',
'content-type': 'text/plain',
});
// If we get here, h2 session connected. Try a request.
const result = await new Promise<{ status: number; body: string }>((resolve, reject) => {
const reqStream = session.request({
':method': 'POST',
':path': '/echo',
'content-type': 'text/plain',
});
let data = '';
let status = 0;
let data = '';
let status = 0;
reqStream.on('response', (headers) => {
status = headers[':status'] as number;
});
reqStream.on('data', (chunk: Buffer) => {
data += chunk.toString();
});
reqStream.on('end', () => resolve({ status, body: data }));
reqStream.on('error', reject);
reqStream.end('h2-test');
});
expect(result.status).toEqual(200);
expect(result.body).toEqual('echo:h2-test');
h2Supported = true;
await new Promise<void>((resolve) => session.close(() => resolve()));
} catch {
// h2 end-to-end not yet supported — that's OK, h1.1 over TLS is verified above
console.log('HTTP/2 end-to-end not yet supported by Rust engine (expected)');
reqStream.on('response', (headers) => {
status = headers[':status'] as number;
});
reqStream.on('data', (chunk: Buffer) => {
data += chunk.toString();
});
reqStream.on('end', () => resolve({ status, body: data }));
reqStream.on('error', reject);
reqStream.end(`h2-msg-${i}`);
}),
);
}
console.log(`HTTP/2 full support: ${h2Supported ? 'yes' : 'no (ALPN advertised but h2 framing not handled)'}`);
const results = await Promise.all(promises);
for (let i = 0; i < REQUEST_COUNT; i++) {
expect(results[i].status).toEqual(200);
expect(results[i].body).toEqual(`echo:h2-msg-${i}`);
}
await new Promise<void>((resolve) => session.close(() => resolve()));
console.log(`HTTP/2 end-to-end: ${REQUEST_COUNT} multiplexed requests completed successfully`);
});
// ===========================================================================