Skip to content

Commit 2662d86

Browse files
authored
Merge pull request #7 from sapphi-red/improve-types
Improve types to have more compatibility with `@types/http-proxy`
2 parents 2df834c + 4c4a434 commit 2662d86

File tree

6 files changed

+193
-45
lines changed

6 files changed

+193
-45
lines changed

lib/http-proxy/index.ts

Lines changed: 165 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as http from "http";
22
import * as https from "https";
3+
import * as net from "net";
34
import { WEB_PASSES } from "./passes/web-incoming";
45
import { WS_PASSES } from "./passes/ws-incoming";
56
import { EventEmitter } from "events";
@@ -32,67 +33,165 @@ export interface ServerOptions {
3233
// actually proxying is called. However, they can be missing when creating the
3334
// proxy server in the first place! E.g., you could make a proxy server P with
3435
// no options, then use P.web(req,res, {target:...}).
35-
// URL string to be parsed with the url module.
36+
/** URL string to be parsed with the url module. */
3637
target?: ProxyTarget;
37-
// URL string to be parsed with the url module or a URL object.
38+
/** URL string to be parsed with the url module or a URL object. */
3839
forward?: ProxyTargetUrl;
39-
// Object to be passed to http(s).request.
40+
/** Object to be passed to http(s).request. */
4041
agent?: any;
41-
// Object to be passed to https.createServer().
42+
/** Object to be passed to https.createServer(). */
4243
ssl?: any;
43-
// If you want to proxy websockets.
44+
/** If you want to proxy websockets. */
4445
ws?: boolean;
45-
// Adds x- forward headers.
46+
/** Adds x- forward headers. */
4647
xfwd?: boolean;
47-
// Verify SSL certificate.
48+
/** Verify SSL certificate. */
4849
secure?: boolean;
49-
// Explicitly specify if we are proxying to another proxy.
50+
/** Explicitly specify if we are proxying to another proxy. */
5051
toProxy?: boolean;
51-
// Specify whether you want to prepend the target's path to the proxy path.
52+
/** Specify whether you want to prepend the target's path to the proxy path. */
5253
prependPath?: boolean;
53-
// Specify whether you want to ignore the proxy path of the incoming request.
54+
/** Specify whether you want to ignore the proxy path of the incoming request. */
5455
ignorePath?: boolean;
55-
// Local interface string to bind for outgoing connections.
56+
/** Local interface string to bind for outgoing connections. */
5657
localAddress?: string;
57-
// Changes the origin of the host header to the target URL.
58+
/** Changes the origin of the host header to the target URL. */
5859
changeOrigin?: boolean;
59-
// specify whether you want to keep letter case of response header key
60+
/** specify whether you want to keep letter case of response header key */
6061
preserveHeaderKeyCase?: boolean;
61-
// Basic authentication i.e. 'user:password' to compute an Authorization header.
62+
/** Basic authentication i.e. 'user:password' to compute an Authorization header. */
6263
auth?: string;
63-
// Rewrites the location hostname on (301 / 302 / 307 / 308) redirects, Default: null.
64+
/** Rewrites the location hostname on (301 / 302 / 307 / 308) redirects, Default: null. */
6465
hostRewrite?: string;
65-
// Rewrites the location host/ port on (301 / 302 / 307 / 308) redirects based on requested host/ port.Default: false.
66+
/** Rewrites the location host/ port on (301 / 302 / 307 / 308) redirects based on requested host/ port.Default: false. */
6667
autoRewrite?: boolean;
67-
// Rewrites the location protocol on (301 / 302 / 307 / 308) redirects to 'http' or 'https'.Default: null.
68+
/** Rewrites the location protocol on (301 / 302 / 307 / 308) redirects to 'http' or 'https'.Default: null. */
6869
protocolRewrite?: string;
69-
// rewrites domain of set-cookie headers.
70+
/** rewrites domain of set-cookie headers. */
7071
cookieDomainRewrite?: false | string | { [oldDomain: string]: string };
71-
// rewrites path of set-cookie headers. Default: false
72+
/** rewrites path of set-cookie headers. Default: false */
7273
cookiePathRewrite?: false | string | { [oldPath: string]: string };
73-
// object with extra headers to be added to target requests.
74+
/** object with extra headers to be added to target requests. */
7475
headers?: { [header: string]: string | string[] | undefined };
75-
// Timeout (in milliseconds) when proxy receives no response from target. Default: 120000 (2 minutes)
76+
/** Timeout (in milliseconds) when proxy receives no response from target. Default: 120000 (2 minutes) */
7677
proxyTimeout?: number;
77-
// Timeout (in milliseconds) for incoming requests
78+
/** Timeout (in milliseconds) for incoming requests */
7879
timeout?: number;
79-
// Specify whether you want to follow redirects. Default: false
80+
/** Specify whether you want to follow redirects. Default: false */
8081
followRedirects?: boolean;
81-
// If set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the proxyRes event
82+
/** If set to true, none of the webOutgoing passes are called and it's your responsibility to appropriately return the response by listening and acting on the proxyRes event */
8283
selfHandleResponse?: boolean;
83-
// Buffer
84+
/** Buffer */
8485
buffer?: Stream;
8586
}
8687

87-
export class ProxyServer extends EventEmitter {
88-
public readonly ws;
89-
public readonly web;
88+
export type ErrorCallback =
89+
(
90+
err: Error,
91+
req: http.IncomingMessage,
92+
res: http.ServerResponse | net.Socket,
93+
target?: ProxyTargetUrl,
94+
) => void;
95+
96+
type ProxyServerEventMap = {
97+
error: Parameters<ErrorCallback>;
98+
start: [
99+
req: http.IncomingMessage,
100+
res: http.ServerResponse,
101+
target: ProxyTargetUrl,
102+
];
103+
open: [socket: net.Socket];
104+
proxyReq: [
105+
proxyReq: http.ClientRequest,
106+
req: http.IncomingMessage,
107+
res: http.ServerResponse,
108+
options: ServerOptions,
109+
];
110+
proxyRes: [
111+
proxyRes: http.IncomingMessage,
112+
req: http.IncomingMessage,
113+
res: http.ServerResponse,
114+
];
115+
proxyReqWs: [
116+
proxyReq: http.ClientRequest,
117+
req: http.IncomingMessage,
118+
socket: net.Socket,
119+
options: ServerOptions,
120+
head: any,
121+
];
122+
econnreset: [
123+
err: Error,
124+
req: http.IncomingMessage,
125+
res: http.ServerResponse,
126+
target: ProxyTargetUrl,
127+
];
128+
end: [
129+
req: http.IncomingMessage,
130+
res: http.ServerResponse,
131+
proxyRes: http.IncomingMessage,
132+
];
133+
close: [
134+
proxyRes: http.IncomingMessage,
135+
proxySocket: net.Socket,
136+
proxyHead: any,
137+
];
138+
}
139+
140+
export class ProxyServer extends EventEmitter<ProxyServerEventMap> {
141+
/**
142+
* Used for proxying WS(S) requests
143+
* @param req - Client request.
144+
* @param socket - Client socket.
145+
* @param head - Client head.
146+
* @param options - Additional options.
147+
*/
148+
public readonly ws: (
149+
...args:
150+
[
151+
req: http.IncomingMessage,
152+
socket: any,
153+
head: any,
154+
options?: ServerOptions,
155+
callback?: ErrorCallback,
156+
]
157+
| [
158+
req: http.IncomingMessage,
159+
socket: any,
160+
head: any,
161+
callback?: ErrorCallback,
162+
]
163+
) => void;
164+
165+
/**
166+
* Used for proxying regular HTTP(S) requests
167+
* @param req - Client request.
168+
* @param res - Client response.
169+
* @param options - Additional options.
170+
*/
171+
public readonly web: (
172+
...args:
173+
[
174+
req: http.IncomingMessage,
175+
res: http.ServerResponse,
176+
options: ServerOptions,
177+
callback?: ErrorCallback,
178+
]
179+
| [
180+
req: http.IncomingMessage,
181+
res: http.ServerResponse,
182+
callback?: ErrorCallback
183+
]
184+
) => void;
90185

91186
private options: ServerOptions;
92187
private webPasses;
93188
private wsPasses;
94189
private _server?;
95190

191+
/**
192+
* Creates the proxy server with specified options.
193+
* @param options - Config object passed to the proxy
194+
*/
96195
constructor(options: ServerOptions = {}) {
97196
super();
98197
log("creating a ProxyServer", options);
@@ -105,6 +204,33 @@ export class ProxyServer extends EventEmitter {
105204
this.on("error", this.onError);
106205
}
107206

207+
/**
208+
* Creates the proxy server with specified options.
209+
* @param options Config object passed to the proxy
210+
* @returns Proxy object with handlers for `ws` and `web` requests
211+
*/
212+
static createProxyServer(options?: ServerOptions): ProxyServer {
213+
return new ProxyServer(options);
214+
}
215+
216+
/**
217+
* Creates the proxy server with specified options.
218+
* @param options Config object passed to the proxy
219+
* @returns Proxy object with handlers for `ws` and `web` requests
220+
*/
221+
static createServer(options?: ServerOptions): ProxyServer {
222+
return new ProxyServer(options);
223+
}
224+
225+
/**
226+
* Creates the proxy server with specified options.
227+
* @param options Config object passed to the proxy
228+
* @returns Proxy object with handlers for `ws` and `web` requests
229+
*/
230+
static createProxy(options?: ServerOptions): ProxyServer {
231+
return new ProxyServer(options);
232+
}
233+
108234
// createRightProxy - Returns a function that when called creates the loader for
109235
// either `ws` or `web`'s passes.
110236
createRightProxy = (type: ProxyType): Function => {
@@ -124,8 +250,8 @@ export class ProxyServer extends EventEmitter {
124250
// and there's no way for a user of http-proxy-3 to get ahold
125251
// of this res object and attach their own error handler until
126252
// after the passes. So we better attach one ASAP right here:
127-
res.on("error", (...args) => {
128-
this.emit("error", ...args);
253+
(res as net.Socket).on("error", (err) => {
254+
this.emit("error", err, req, res);
129255
});
130256
}
131257
let counter = args.length - 1;
@@ -158,7 +284,7 @@ export class ProxyServer extends EventEmitter {
158284
}
159285

160286
if (!requestOptions.target && !requestOptions.forward) {
161-
this.emit("error", new Error("Must set target or forward"));
287+
this.emit("error", new Error("Must set target or forward"), req, res);
162288
return;
163289
}
164290

@@ -187,6 +313,11 @@ export class ProxyServer extends EventEmitter {
187313
}
188314
};
189315

316+
/**
317+
* A function that wraps the object in a webserver, for your convenience
318+
* @param port - Port to listen on
319+
* @param hostname - The hostname to listen on
320+
*/
190321
listen = (port: number, hostname?: string) => {
191322
log("listen", { port, hostname });
192323

@@ -210,6 +341,9 @@ export class ProxyServer extends EventEmitter {
210341
return this._server?.address();
211342
};
212343

344+
/**
345+
* A function that closes the inner webserver and stops listening on given port
346+
*/
213347
close = (cb?: Function) => {
214348
if (this._server == null) {
215349
cb?.();

lib/index.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1-
import { ProxyServer, type ServerOptions } from "./http-proxy/index";
2-
export { ProxyServer };
3-
export { numOpenSockets } from "./http-proxy/passes/ws-incoming";
1+
import {
2+
ProxyServer,
3+
type ServerOptions,
4+
type ProxyTarget,
5+
type ProxyTargetUrl,
6+
type ErrorCallback,
7+
} from './http-proxy/index';
8+
export {
9+
ProxyServer,
10+
type ServerOptions,
11+
type ProxyTarget,
12+
type ProxyTargetUrl,
13+
type ErrorCallback,
14+
};
15+
export { numOpenSockets } from './http-proxy/passes/ws-incoming';
416

517
/**
618
* Creates the proxy server.

lib/test/http/custom-proxy-error.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
/*
22
custom-proxy-error.test.ts: Example of using the custom `proxyError` event.
3-
3+
44
pnpm test ./custom-proxy-error.test.ts
55
*/
66

77
import * as httpProxy from "../..";
8+
import * as http from "http";
89
import getPort from "../get-port";
910
import fetch from "node-fetch";
1011

@@ -27,7 +28,7 @@ describe("Test proxying over HTTP with latency", () => {
2728
.listen(ports.proxy);
2829

2930
proxy.on("error", (_err, _req, res) => {
30-
res.writeHead(500, {
31+
(res as http.ServerResponse).writeHead(500, {
3132
"Content-Type": "text/plain",
3233
});
3334
res.end(CUSTOM_ERROR);

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ describe("#createProxyServer.web() using own http server", () => {
213213
function requestHandler(req, res) {
214214
proxy.web(req, res, (err) => {
215215
proxyServer.close();
216-
expect(err.code).toEqual("ECONNREFUSED");
216+
expect((err as NodeJS.ErrnoException).code).toEqual("ECONNREFUSED");
217217
done();
218218
});
219219
}
@@ -245,7 +245,7 @@ describe("#createProxyServer.web() using own http server", () => {
245245
proxyServer.close();
246246
expect(errReq).toEqual(req);
247247
expect(errRes).toEqual(res);
248-
expect(err.code).toEqual("ECONNREFUSED");
248+
expect((err as NodeJS.ErrnoException).code).toEqual("ECONNREFUSED");
249249
done();
250250
});
251251

@@ -279,7 +279,7 @@ describe("#createProxyServer.web() using own http server", () => {
279279
proxyServer.close();
280280
expect(errReq).toEqual(req);
281281
expect(errRes).toEqual(res);
282-
expect(err.code).toEqual("ECONNREFUSED");
282+
expect((err as NodeJS.ErrnoException).code).toEqual("ECONNREFUSED");
283283
done();
284284
});
285285

@@ -317,7 +317,7 @@ describe("#createProxyServer.web() using own http server", () => {
317317
expect(errReq).toEqual(req);
318318
expect(errRes).toEqual(res);
319319
expect(Date.now() - started).toBeGreaterThan(99);
320-
expect(err.code).toEqual("ECONNRESET");
320+
expect((err as NodeJS.ErrnoException).code).toEqual("ECONNRESET");
321321
done();
322322
});
323323

@@ -362,7 +362,7 @@ describe("#createProxyServer.web() using own http server", () => {
362362
server.close();
363363
expect(errReq).toEqual(req);
364364
expect(errRes).toEqual(res);
365-
expect(err.code).toEqual("ECONNRESET");
365+
expect((err as NodeJS.ErrnoException).code).toEqual("ECONNRESET");
366366
doneOne();
367367
});
368368

lib/test/lib/http-proxy.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ describe("#createProxyServer() method with error response", () => {
168168

169169
proxy
170170
.on("error", (err) => {
171-
expect(err.code).toEqual("ECONNREFUSED");
171+
expect((err as NodeJS.ErrnoException).code).toEqual("ECONNREFUSED");
172172
proxy.close();
173173
done();
174174
})
@@ -198,7 +198,7 @@ describe("#createProxyServer setting the correct timeout value", () => {
198198
.listen(ports.proxy);
199199

200200
proxy.on("error", (e) => {
201-
expect(e.code).toEqual("ECONNRESET");
201+
expect((e as NodeJS.ErrnoException).code).toEqual("ECONNRESET");
202202
});
203203

204204
const source = http.createServer((_req, res) => {
@@ -327,7 +327,7 @@ describe("#createProxyServer using the ws-incoming passes", () => {
327327
});
328328

329329
proxy.on("error", (err) => {
330-
expect(err.code).toEqual("ECONNREFUSED");
330+
expect((err as NodeJS.ErrnoException).code).toEqual("ECONNREFUSED");
331331
proxyServer.close();
332332
maybe_done();
333333
});

0 commit comments

Comments
 (0)