Skip to content

Commit cfc4118

Browse files
committed
fully proxy HTTP response
applies full web-outgoing passes needs to declare an EditableResponse subset of Response in order to mock it for the proxy response
1 parent 290235c commit cfc4118

File tree

3 files changed

+51
-12
lines changed

3 files changed

+51
-12
lines changed

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ The names of passes are exported as WEB_PASSES from this module.
99

1010
import * as http from "node:http";
1111
import * as https from "node:https";
12-
import { OUTGOING_PASSES } from "./web-outgoing";
12+
import { OUTGOING_PASSES, EditableResponse } from "./web-outgoing";
1313
import * as common from "../common";
1414
import * as followRedirects from "follow-redirects";
1515
import {
@@ -164,7 +164,7 @@ export function stream(req: Request, res: Response, options: NormalizedServerOpt
164164
if (!res.headersSent && !options.selfHandleResponse) {
165165
for (const pass of web_o) {
166166
// note: none of these return anything
167-
pass(req, res, proxyRes, options as NormalizedServerOptions & { target: NormalizeProxyTarget<ProxyTarget> });
167+
pass(req, res as EditableResponse, proxyRes, options as NormalizedServerOptions & { target: NormalizeProxyTarget<ProxyTarget> });
168168
}
169169
}
170170

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

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,24 @@ to not return anything.
1111

1212
import type { NormalizedServerOptions, NormalizeProxyTarget, ProxyTarget } from "..";
1313
import * as common from "../common";
14-
import type { Request, Response, ProxyResponse } from "./web-incoming";
14+
import type { Request, ProxyResponse } from "./web-incoming";
1515

1616
const redirectRegex = /^201|30(1|2|7|8)$/;
1717

18+
// interface for subset of Response that's actually used here
19+
// needed for running outgoing passes on MockResponse in ws-incoming
20+
export interface EditableResponse {
21+
statusCode: number;
22+
statusMessage: string;
23+
setHeader(key: string, value: string | string[]): this;
24+
}
25+
1826
// <--
1927

2028
// If is a HTTP 1.0 request, remove chunk headers
2129
export function removeChunked(
2230
req: Request,
23-
_res: Response,
31+
_res: EditableResponse,
2432
// Response object from the proxy request
2533
proxyRes: ProxyResponse,
2634
) {
@@ -33,7 +41,7 @@ export function removeChunked(
3341
// or if connection header not present, then use `keep-alive`
3442
export function setConnection(
3543
req: Request,
36-
_res: Response,
44+
_res: EditableResponse,
3745
// Response object from the proxy request
3846
proxyRes: ProxyResponse,
3947
) {
@@ -46,7 +54,7 @@ export function setConnection(
4654

4755
export function setRedirectHostRewrite(
4856
req: Request,
49-
_res: Response,
57+
_res: EditableResponse,
5058
proxyRes: ProxyResponse,
5159
options: NormalizedServerOptions & { target: NormalizeProxyTarget<ProxyTarget> },
5260
) {
@@ -84,7 +92,7 @@ export function setRedirectHostRewrite(
8492
export function writeHeaders(
8593
_req: Request,
8694
// Response to set headers in
87-
res: Response,
95+
res: EditableResponse,
8896
// Response object from the proxy request
8997
proxyRes: ProxyResponse,
9098
// options.cookieDomainRewrite: Config to rewrite cookie domain
@@ -147,7 +155,7 @@ export function writeHeaders(
147155
// Set the statusCode from the proxyResponse
148156
export function writeStatusCode(
149157
_req: Request,
150-
res: Response,
158+
res: EditableResponse,
151159
proxyRes: ProxyResponse,
152160
) {
153161
// From Node.js docs: response.writeHead(statusCode[, statusMessage][, headers])

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

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ import * as http from "node:http";
1212
import * as https from "node:https";
1313
import * as common from "../common";
1414
import type { Request, ProxyResponse } from "./web-incoming";
15+
import { OUTGOING_PASSES, EditableResponse } from "./web-outgoing";
1516
import type { Socket } from "node:net";
1617
import debug from "debug";
17-
import type { NormalizedServerOptions, ProxyServer } from "..";
18+
import type { NormalizedServerOptions, NormalizeProxyTarget, ProxyServer, ProxyTarget } from "..";
1819

1920
const log = debug("http-proxy-3:ws-incoming");
21+
const web_o = Object.values(OUTGOING_PASSES);
2022

2123
function createSocketCounter(name: string) {
2224
let sockets = new Set<number>();
@@ -55,6 +57,26 @@ function createSocketCounter(name: string) {
5557
const socketCounter = createSocketCounter("socket");
5658
const proxySocketCounter = createSocketCounter("proxySocket");
5759

60+
/* MockResponse
61+
when a websocket gets a regular HTTP Response,
62+
apply proxied headers
63+
*/
64+
class MockResponse implements EditableResponse {
65+
constructor() {
66+
this.headers = {};
67+
this.statusCode = 200
68+
this.statusMessage = "";
69+
}
70+
public headers: { [key: string]: string};
71+
public statusCode: number;
72+
public statusMessage: string;
73+
74+
setHeader(key: string, value: string) {
75+
this.headers[key] = value;
76+
return this;
77+
};
78+
}
79+
5880
export function numOpenSockets(): number {
5981
return socketCounter() + proxySocketCounter();
6082
}
@@ -219,7 +241,7 @@ export function stream(
219241
// which may be another leak type situation and definitely doesn't work for unit testing.
220242
socket.destroySoon();
221243
}
222-
244+
223245
// if we get a response, backend is not a websocket endpoint,
224246
// relay HTTP response and close the socket
225247
proxyReq.on("response", (proxyRes: ProxyResponse) => {
@@ -229,9 +251,18 @@ export function stream(
229251
statusMessage: proxyRes.statusMessage,
230252
}
231253
);
254+
255+
const res = new MockResponse();
256+
for (const pass of web_o) {
257+
// note: none of these return anything
258+
pass(req, res as EditableResponse, proxyRes, options as NormalizedServerOptions & { target: NormalizeProxyTarget<ProxyTarget> });
259+
}
260+
// avoid Invalid character error in chunk size
261+
delete res.headers['transfer-encoding'];
262+
232263
const proxyHead = createHttpHeader(
233-
`HTTP/1.1 ${proxyRes.statusCode} ${proxyRes.statusMessage}`,
234-
{"Connection": "close"},
264+
`HTTP/${req.httpVersion} ${proxyRes.statusCode} ${proxyRes.statusMessage}`,
265+
res.headers,
235266
);
236267
if (!socket.destroyed) {
237268
socket.write(proxyHead);

0 commit comments

Comments
 (0)