Skip to content

Commit d24f40f

Browse files
author
vhess
committed
Merge branch 'main' of https://github.com/sagemathinc/http-proxy-3 into fetch-refinements
2 parents 50f0722 + e0344dc commit d24f40f

File tree

8 files changed

+165
-10
lines changed

8 files changed

+165
-10
lines changed

lib/http-proxy/common.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ export function setupOutgoing(
8181

8282
outgoing.method = options.method || req.method;
8383
outgoing.headers = { ...req.headers };
84+
if (req.headers?.[":authority"]) {
85+
outgoing.headers.host = req.headers[":authority"];
86+
}
8487

8588
if (options.headers) {
8689
outgoing.headers = { ...outgoing.headers, ...options.headers };
@@ -189,7 +192,8 @@ export function getPort(
189192
req: Request,
190193
// Return the port number, as a string.
191194
): string {
192-
const res = req.headers.host ? req.headers.host.match(/:(\d+)/) : "";
195+
const hostHeader = (req.headers[":authority"] as string | undefined) || req.headers.host;
196+
const res = hostHeader ? hostHeader.match(/:(\d+)/) : "";
193197
return res ? res[1] : hasEncryptedConnection(req) ? "443" : "80";
194198
}
195199

lib/http-proxy/passes/web-incoming.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ export function XHeaders(req: Request, _res: Response, options: ServerOptions) {
6969
(req.headers["x-forwarded-" + header] || "") + (req.headers["x-forwarded-" + header] ? "," : "") + values[header];
7070
}
7171

72-
req.headers["x-forwarded-host"] = req.headers["x-forwarded-host"] || req.headers["host"] || req.headers[":authority"] || "";
72+
req.headers["x-forwarded-host"] = req.headers["x-forwarded-host"] || req.headers[":authority"] || req.headers["host"] || "";
7373
}
7474

7575
// Does the actual proxying. If `forward` is enabled fires up

lib/http-proxy/passes/web-outgoing.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export function setRedirectHostRewrite(
7777
if (options.hostRewrite) {
7878
u.host = options.hostRewrite;
7979
} else if (options.autoRewrite) {
80-
u.host = req.headers["host"] ?? "";
80+
u.host = (req.headers[":authority"] as string | undefined) ?? req.headers["host"] ?? "";
8181
}
8282
if (options.protocolRewrite) {
8383
u.protocol = options.protocolRewrite;
@@ -143,7 +143,7 @@ export function writeHeaders(
143143

144144
for (const key0 in proxyRes.headers) {
145145
let key = key0;
146-
if (_req.httpVersionMajor > 1 && (key === "connection" || key === "keep-alive")) {
146+
if (_req.httpVersionMajor > 1 && (key === "connection" || key === "keep-alive")) {
147147
// don't send connection header to http2 client
148148
continue;
149149
}

lib/test/lib/http-proxy-passes-web-incoming.test.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,32 @@ describe("#XHeaders", () => {
7272
host: "192.168.1.2:8080",
7373
} as Record<string, string>,
7474
};
75+
const stubHttp2Request = {
76+
connection: {
77+
remoteAddress: "192.168.1.2",
78+
remotePort: "8080",
79+
},
80+
headers: {
81+
':authority': "192.168.1.2:8080",
82+
} as Record<string, string>,
83+
};
7584

7685
it("set the correct x-forwarded-* headers", () => {
7786
// @ts-ignore
7887
XHeaders(stubRequest, {}, { xfwd: true });
7988
expect(stubRequest.headers["x-forwarded-for"]).toEqual("192.168.1.2");
8089
expect(stubRequest.headers["x-forwarded-port"]).toEqual("8080");
8190
expect(stubRequest.headers["x-forwarded-proto"]).toEqual("http");
91+
expect(stubRequest.headers["x-forwarded-host"]).toEqual("192.168.1.2:8080");
92+
});
93+
94+
it("set the correct x-forwarded-* headers for http2", () => {
95+
// @ts-ignore
96+
XHeaders(stubHttp2Request, {}, { xfwd: true });
97+
expect(stubHttp2Request.headers["x-forwarded-for"]).toEqual("192.168.1.2");
98+
expect(stubHttp2Request.headers["x-forwarded-port"]).toEqual("8080");
99+
expect(stubHttp2Request.headers["x-forwarded-proto"]).toEqual("http");
100+
expect(stubHttp2Request.headers["x-forwarded-host"]).toEqual("192.168.1.2:8080");
82101
});
83102
});
84103

@@ -543,7 +562,7 @@ describe("#createProxyServer.web() using own http server", () => {
543562
source.close();
544563
proxy.close();
545564
expect(req.method).toEqual("GET");
546-
// expect(req.headers.host?.split(":")[1]).toEqual(`${port(8080)}`);
565+
expect(req.headers.host?.split(":")[1]).toEqual(`${port(8080)}`);
547566
res.end();
548567
done();
549568
})
@@ -609,7 +628,7 @@ describe("#createProxyServer.web() using own http server", () => {
609628

610629
const source1 = http.createServer((req, res) => {
611630
expect(req.method).toEqual("GET");
612-
//expect(req.headers.host?.split(":")[1]).toEqual(`${port(8080)}`);
631+
expect(req.headers.host?.split(":")[1]).toEqual(`${port(8080)}`);
613632
expect(req.url).toEqual("/test1");
614633
res.end();
615634
});
@@ -619,7 +638,7 @@ describe("#createProxyServer.web() using own http server", () => {
619638
source2.close();
620639
proxyServer.close();
621640
expect(req.method).toEqual("GET");
622-
//expect(req.headers.host?.split(":")[1]).toEqual(`${port(8080)}`);
641+
expect(req.headers.host?.split(":")[1]).toEqual(`${port(8080)}`);
623642
expect(req.url).toEqual("/test2");
624643
res.end();
625644
done();

lib/test/lib/http-proxy-passes-web-outgoing.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,16 @@ describe.each( ["http://backend.com", url.parse("http://backend.com")])("#setRed
105105
"http://ext-auto.com/",
106106
);
107107
});
108+
109+
it("on " + code + " (http2)", () => {
110+
state.proxyRes.statusCode = code;
111+
state.req.headers[":authority"] = state.req.headers.host;
112+
delete state.req.headers.host;
113+
setRedirectHostRewrite(state.req, {}, state.proxyRes, state.options);
114+
expect(state.proxyRes.headers.location).toEqual(
115+
"http://ext-auto.com/",
116+
);
117+
});
108118
});
109119

110120
it("not on 200", () => {

lib/test/lib/http2-proxy.test.ts

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import * as httpProxy from "../..";
2+
import * as http from "node:http";
3+
import * as http2 from "node:http2";
4+
import getPort from "../get-port";
5+
import { join } from "node:path";
6+
import { readFileSync } from "node:fs";
7+
import { describe, it, expect } from 'vitest';
8+
9+
const ports: { [port: string]: number } = {};
10+
let portIndex = -1;
11+
const gen = {} as { port: number };
12+
Object.defineProperty(gen, "port", {
13+
get: function get() {
14+
portIndex++;
15+
return ports[portIndex];
16+
},
17+
});
18+
19+
describe("HTTP2 to HTTP", () => {
20+
it("creates some ports", async () => {
21+
for (let n = 0; n < 50; n++) {
22+
ports[n] = await getPort();
23+
}
24+
});
25+
26+
it("should proxy the request, then send back the response", () => new Promise<void>(done => {
27+
const ports = { source: gen.port, proxy: gen.port };
28+
const source = http
29+
.createServer((req, res) => {
30+
expect(req.method).toEqual("GET");
31+
expect(req.headers.host?.split(":")[1]).toEqual(`${ports.proxy}`);
32+
res.writeHead(200, { "Content-Type": "text/plain" });
33+
res.end("Hello from " + ports.source);
34+
})
35+
.listen(ports.source);
36+
37+
const proxy = httpProxy
38+
.createProxyServer({
39+
target: "http://127.0.0.1:" + ports.source,
40+
ssl: {
41+
key: readFileSync(
42+
join(__dirname, "..", "fixtures", "agent2-key.pem"),
43+
),
44+
cert: readFileSync(
45+
join(__dirname, "..", "fixtures", "agent2-cert.pem"),
46+
),
47+
ciphers: "AES128-GCM-SHA256",
48+
},
49+
})
50+
.listen(ports.proxy);
51+
52+
const client = http2.connect(`https://localhost:${ports.proxy}`)
53+
const req = client.request({ ':path': '/' });
54+
req.on('response', (headers, _flags) => {
55+
expect(headers[':status']).toEqual(200);
56+
req.setEncoding('utf8');
57+
req.on('data', (chunk) => {
58+
expect(chunk.toString()).toEqual("Hello from " + ports.source);
59+
});
60+
req.on('end', () => {
61+
source.close();
62+
proxy.close();
63+
done();
64+
});
65+
});
66+
req.end();
67+
}));
68+
});
69+
70+
describe("HTTP2 to HTTP using own server", () => {
71+
it("should proxy the request, then send back the response", () => new Promise<void>(done => {
72+
const ports = { source: gen.port, proxy: gen.port };
73+
const source = http
74+
.createServer((req, res) => {
75+
expect(req.method).toEqual("GET");
76+
expect(req.headers.host?.split(":")[1]).toEqual(`${ports.proxy}`);
77+
res.writeHead(200, { "Content-Type": "text/plain" });
78+
res.end("Hello from " + ports.source);
79+
})
80+
.listen(ports.source);
81+
82+
const proxy = httpProxy.createServer({
83+
agent: new http.Agent({ maxSockets: 2 }),
84+
});
85+
86+
const ownServer = http2
87+
.createSecureServer(
88+
{
89+
key: readFileSync(
90+
join(__dirname, "..", "fixtures", "agent2-key.pem"),
91+
),
92+
cert: readFileSync(
93+
join(__dirname, "..", "fixtures", "agent2-cert.pem"),
94+
),
95+
ciphers: "AES128-GCM-SHA256",
96+
},
97+
(req, res) => {
98+
// @ts-expect-error -- ignore type incompatibility
99+
proxy.web(req, res, {
100+
target: "http://127.0.0.1:" + ports.source,
101+
});
102+
},
103+
)
104+
.listen(ports.proxy);
105+
106+
const client = http2.connect(`https://localhost:${ports.proxy}`)
107+
const req = client.request({ ':path': '/' });
108+
req.on('response', (headers, _flags) => {
109+
expect(headers[':status']).toEqual(200);
110+
req.setEncoding('utf8');
111+
req.on('data', (chunk) => {
112+
expect(chunk.toString()).toEqual("Hello from " + ports.source);
113+
});
114+
req.on('end', () => {
115+
source.close();
116+
ownServer.close();
117+
done();
118+
});
119+
});
120+
req.end();
121+
}));
122+
});

lib/test/lib/https-proxy.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe("HTTPS to HTTP", () => {
3030
const source = http
3131
.createServer((req, res) => {
3232
expect(req.method).toEqual("GET");
33-
// expect(req.headers.host?.split(":")[1]).toEqual(`${ports.proxy}`);
33+
expect(req.headers.host?.split(":")[1]).toEqual(`${ports.proxy}`);
3434
res.writeHead(200, { "Content-Type": "text/plain" });
3535
res.end("Hello from " + ports.source);
3636
})

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "http-proxy-3",
3-
"version": "1.23.0",
3+
"version": "1.23.1",
44
"repository": {
55
"type": "git",
66
"url": "https://github.com/sagemathinc/http-proxy-3.git"
@@ -56,7 +56,7 @@
5656
},
5757
"scripts": {
5858
"test": "NODE_TLS_REJECT_UNAUTHORIZED=0 pnpm exec vitest run",
59-
"test-all": "pnpm audit --audit-level=high && TEST_EXTERNAL_REVERSE_PROXY=yes pnpm test --pool threads --poolOptions.threads.singleThread",
59+
"test-all": "pnpm audit --audit-level=high && TEST_EXTERNAL_REVERSE_PROXY=yes pnpm test --pool threads",
6060
"test-dual-path": "pnpm test-native && pnpm test-fetch",
6161
"test-native": "echo '🔧 Testing native HTTP code path...' && NODE_TLS_REJECT_UNAUTHORIZED=0 pnpm exec vitest run",
6262
"test-fetch": "echo '🚀 Testing fetch code path...' && NODE_TLS_REJECT_UNAUTHORIZED=0 FORCE_FETCH_PATH=true pnpm exec vitest run",

0 commit comments

Comments
 (0)