Skip to content

Commit f20635c

Browse files
committed
Fix unintended redirection on H2 requests
1 parent 9cb7aa9 commit f20635c

3 files changed

Lines changed: 27 additions & 2 deletions

File tree

src/http-handler.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { TLSSocket } from 'tls';
21
import * as http from 'http';
32
import * as http2 from 'http2';
43
import { MaybePromise, StatusError } from '@httptoolkit/util';
@@ -53,7 +52,9 @@ function createHttpRequestHandler(options: {
5352
rootDomain: string
5453
}): RequestHandler {
5554
return async function handleRequest(req, res) {
56-
const protocol = `http${req.socket instanceof TLSSocket ? 's' : ''}`;
55+
const socket = req.socket as any;
56+
const isHttps = socket.encrypted || socket.stream?.encrypted;
57+
const protocol = isHttps ? 'https' : 'http';
5758

5859
if (!req.url!.startsWith('/')) {
5960
// Absolute URL. Block requests unless they're for us personally. We

src/process-connection.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class DataCapturingStream extends stream.Duplex {
4444
readonly remotePort: number | undefined;
4545
readonly localAddress: string | undefined;
4646
readonly localPort: number | undefined;
47+
readonly encrypted: boolean;
4748

4849
constructor(private wrapped: stream.Duplex, initialData?: Buffer) {
4950
super();
@@ -52,6 +53,7 @@ class DataCapturingStream extends stream.Duplex {
5253
this.remotePort = (wrapped as any).remotePort;
5354
this.localAddress = (wrapped as any).localAddress;
5455
this.localPort = (wrapped as any).localPort;
56+
this.encrypted = (wrapped as any).encrypted ?? false;
5557
this[PROXY_PROTOCOL] = wrapped[PROXY_PROTOCOL];
5658

5759
wrapped.on('error', (err) => this.emit('error', err));

test/tls-required.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import * as net from 'net';
2+
import * as http2 from 'http2';
23
import { expect } from 'chai';
34
import { DestroyableServer, makeDestroyable } from 'destroyable-server';
45

@@ -104,4 +105,25 @@ describe("TLS-required endpoints over plain HTTP", () => {
104105
expect(response.status).to.equal(200);
105106
});
106107

108+
it("does not redirect HTTP/2+TLS requests for TLS subdomains", async () => {
109+
const client = http2.connect(`https://localhost:${serverPort}`, {
110+
rejectUnauthorized: false
111+
});
112+
113+
const response = await new Promise<{ status: number | undefined }>((resolve, reject) => {
114+
const req = client.request({
115+
':authority': 'http2.localhost',
116+
':path': '/json'
117+
});
118+
req.on('response', (headers) => {
119+
resolve({ status: headers[':status'] });
120+
});
121+
req.on('error', reject);
122+
req.end();
123+
});
124+
client.close();
125+
126+
expect(response.status).to.equal(200);
127+
});
128+
107129
});

0 commit comments

Comments
 (0)