Skip to content

Commit 0dd8799

Browse files
committed
Merge branch 'sapphi-red-refacotr/enable-ts-strict-checks'
2 parents 6403146 + 7d22922 commit 0dd8799

38 files changed

+477
-219
lines changed

lib/http-proxy/common.ts

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ProxyTargetDetailed, ServerOptions } from "./index";
1+
import type { NormalizedServerOptions, ProxyTargetDetailed, ServerOptions } from "./index";
22
import { type IncomingMessage as Request } from "http";
33
import { TLSSocket } from "tls";
44
import type { Socket } from "net";
@@ -12,7 +12,6 @@ export const isSSL = /^https|wss/;
1212
type Outgoing0 = ProxyTargetDetailed & ServerOptions;
1313

1414
export interface Outgoing extends Outgoing0 {
15-
method?: any;
1615
rejectUnauthorized?: boolean;
1716
path?: string;
1817
headers: { [header: string]: string | string[] | undefined } & {
@@ -36,15 +35,17 @@ export function setupOutgoing(
3635
// Base object to be filled with required properties
3736
outgoing: Outgoing,
3837
// Config object passed to the proxy
39-
options,
38+
options: NormalizedServerOptions,
4039
// Request Object
4140
req: Request,
4241
// String to select forward or target
43-
forward?: string,
42+
forward?: "forward",
4443
) {
44+
// the final path is target path + relative path requested by user:
45+
const target = options[forward || "target"]!;
46+
4547
outgoing.port =
46-
options[forward || "target"].port ??
47-
(isSSL.test(options[forward || "target"].protocol) ? 443 : 80);
48+
+(target.port ?? (target.protocol !== undefined && isSSL.test(target.protocol) ? 443 : 80));
4849

4950
for (const e of [
5051
"host",
@@ -57,8 +58,9 @@ export function setupOutgoing(
5758
"ca",
5859
"ciphers",
5960
"secureProtocol",
60-
]) {
61-
outgoing[e] = options[forward || "target"][e];
61+
] as const) {
62+
// @ts-expect-error -- this mapping is valid
63+
outgoing[e] = target[e];
6264
}
6365

6466
outgoing.method = options.method || req.method;
@@ -87,7 +89,7 @@ export function setupOutgoing(
8789
outgoing.ca = options.ca;
8890
}
8991

90-
if (isSSL.test(options[forward || "target"].protocol)) {
92+
if (target.protocol !== undefined && isSSL.test(target.protocol)) {
9193
outgoing.rejectUnauthorized =
9294
typeof options.secure === "undefined" ? true : options.secure;
9395
}
@@ -107,11 +109,9 @@ export function setupOutgoing(
107109
}
108110
}
109111

110-
// the final path is target path + relative path requested by user:
111-
const target = options[forward || "target"];
112112
// target if defined is a URL object so has attribute "pathname", not "path".
113113
const targetPath =
114-
target && options.prependPath !== false ? getPath(target.pathname) : "/";
114+
target && options.prependPath !== false && 'pathname' in target ? getPath(target.pathname) : "/";
115115

116116
let outgoingPath = options.toProxy ? req.url : getPath(req.url);
117117

@@ -124,7 +124,8 @@ export function setupOutgoing(
124124

125125
if (options.changeOrigin) {
126126
outgoing.headers.host =
127-
required(outgoing.port, options[forward || "target"].protocol) &&
127+
target.protocol !== undefined &&
128+
required(outgoing.port, target.protocol) &&
128129
!hasPort(outgoing.host)
129130
? outgoing.host + ":" + outgoing.port
130131
: outgoing.host;
@@ -216,12 +217,22 @@ export function urlJoin(...args: string[]): string {
216217

217218
// Rewrites or removes the domain of a cookie header
218219
export function rewriteCookieProperty(
219-
header: string | any[],
220+
header: string,
221+
config: Record<string, string>,
222+
property: string,
223+
): string;
224+
export function rewriteCookieProperty(
225+
header: string | string[],
226+
config: Record<string, string>,
227+
property: string,
228+
): string | string[];
229+
export function rewriteCookieProperty(
230+
header: string | string[],
220231
// config = mapping of domain to rewritten domain.
221232
// '*' key to match any domain, null value to remove the domain.
222-
config: object,
233+
config: Record<string, string>,
223234
property: string,
224-
) {
235+
): string | string[] {
225236
if (Array.isArray(header)) {
226237
return header.map((headerElement) => {
227238
return rewriteCookieProperty(headerElement, config, property);
@@ -260,10 +271,10 @@ function getPath(url?: string): string {
260271
return `${u.pathname ?? ""}${u.search ?? ""}`;
261272
}
262273

263-
export function toURL(url: URL | urllib.Url | string | undefined): URL {
274+
export function toURL(url: URL | urllib.Url | ProxyTargetDetailed | string | undefined): URL {
264275
if (url instanceof URL) {
265276
return url;
266-
} else if (typeof url === "object" && typeof url.href === "string") {
277+
} else if (typeof url === "object" && 'href' in url && typeof url.href === "string") {
267278
url = url.href;
268279
}
269280
if (!url) {
@@ -275,7 +286,7 @@ export function toURL(url: URL | urllib.Url | string | undefined): URL {
275286
}
276287
if (url.startsWith("//")) {
277288
// special case -- this would be viewed as a this is a "network-path reference",
278-
// so we explicitly prefix with our http schema. See
289+
// so we explicitly prefix with our http schema. See
279290
url = `http://dummy.org${url}`;
280291
}
281292
// urllib.Url is deprecated but we support it by converting to URL

lib/http-proxy/index.ts

Lines changed: 82 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ export interface ProxyTargetDetailed {
2626
}
2727
export type ProxyType = "ws" | "web";
2828
export type ProxyTarget = ProxyTargetUrl | ProxyTargetDetailed;
29-
export type ProxyTargetUrl = URL | string | { port: number; host: string };
29+
export type ProxyTargetUrl = URL | string | { port: number; host: string; protocol?: string };
30+
31+
export type NormalizeProxyTarget<T extends ProxyTargetUrl> = Exclude<T, string> | URL;
3032

3133
export interface ServerOptions {
3234
// NOTE: `options.target and `options.forward` cannot be both missing when the
@@ -83,6 +85,18 @@ export interface ServerOptions {
8385
selfHandleResponse?: boolean;
8486
/** Buffer */
8587
buffer?: Stream;
88+
/** Explicitly set the method type of the ProxyReq */
89+
method?: string;
90+
/**
91+
* Optionally override the trusted CA certificates.
92+
* This is passed to https.request.
93+
*/
94+
ca?: string;
95+
}
96+
97+
export interface NormalizedServerOptions extends ServerOptions {
98+
target?: NormalizeProxyTarget<ProxyTarget>;
99+
forward?: NormalizeProxyTarget<ProxyTargetUrl>;
86100
}
87101

88102
export type ErrorCallback =
@@ -106,6 +120,7 @@ type ProxyServerEventMap = {
106120
req: http.IncomingMessage,
107121
res: http.ServerResponse,
108122
options: ServerOptions,
123+
socket: net.Socket,
109124
];
110125
proxyRes: [
111126
proxyRes: http.IncomingMessage,
@@ -137,56 +152,75 @@ type ProxyServerEventMap = {
137152
];
138153
}
139154

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: (
155+
type ProxyMethodArgs = {
156+
ws: [
157+
req: http.IncomingMessage,
158+
socket: any,
159+
head: any,
149160
...args:
150161
[
151-
req: http.IncomingMessage,
152-
socket: any,
153-
head: any,
154162
options?: ServerOptions,
155163
callback?: ErrorCallback,
156164
]
157165
| [
158-
req: http.IncomingMessage,
159-
socket: any,
160-
head: any,
161166
callback?: ErrorCallback,
162167
]
163-
) => void;
168+
]
169+
web: [
170+
req: http.IncomingMessage,
171+
res: http.ServerResponse,
172+
...args:
173+
[
174+
options: ServerOptions,
175+
callback?: ErrorCallback,
176+
]
177+
| [
178+
callback?: ErrorCallback
179+
]
180+
]
181+
}
182+
183+
type PassFunctions = {
184+
ws: (
185+
req: http.IncomingMessage,
186+
socket: net.Socket,
187+
options: NormalizedServerOptions,
188+
head: Buffer | undefined,
189+
server: ProxyServer,
190+
cb?: ErrorCallback
191+
) => unknown
192+
web: (
193+
req: http.IncomingMessage,
194+
res: http.ServerResponse,
195+
options: NormalizedServerOptions,
196+
head: Buffer | undefined,
197+
server: ProxyServer,
198+
cb?: ErrorCallback
199+
) => unknown
200+
}
201+
202+
export class ProxyServer extends EventEmitter<ProxyServerEventMap> {
203+
/**
204+
* Used for proxying WS(S) requests
205+
* @param req - Client request.
206+
* @param socket - Client socket.
207+
* @param head - Client head.
208+
* @param options - Additional options.
209+
*/
210+
public readonly ws: (...args: ProxyMethodArgs["ws"]) => void;
164211

165212
/**
166213
* Used for proxying regular HTTP(S) requests
167214
* @param req - Client request.
168215
* @param res - Client response.
169216
* @param options - Additional options.
170217
*/
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;
218+
public readonly web: (...args: ProxyMethodArgs["web"]) => void;
185219

186220
private options: ServerOptions;
187-
private webPasses;
188-
private wsPasses;
189-
private _server?;
221+
private webPasses: Array<PassFunctions['web']>;
222+
private wsPasses: Array<PassFunctions['ws']>;
223+
private _server?: http.Server | https.Server | null;
190224

191225
/**
192226
* Creates the proxy server with specified options.
@@ -233,10 +267,10 @@ export class ProxyServer extends EventEmitter<ProxyServerEventMap> {
233267

234268
// createRightProxy - Returns a function that when called creates the loader for
235269
// either `ws` or `web`'s passes.
236-
createRightProxy = (type: ProxyType): Function => {
270+
createRightProxy = <PT extends ProxyType>(type: PT): Function => {
237271
log("createRightProxy", { type });
238-
return (options) => {
239-
return (...args: any[] /* req, res, [head], [opts] */) => {
272+
return (options: ServerOptions) => {
273+
return (...args: ProxyMethodArgs[PT] /* req, res, [head], [opts] */) => {
240274
const req = args[0];
241275
log("proxy: ", { type, path: req.url });
242276
const res = args[1];
@@ -255,16 +289,16 @@ export class ProxyServer extends EventEmitter<ProxyServerEventMap> {
255289
});
256290
}
257291
let counter = args.length - 1;
258-
let head;
259-
let cb;
292+
let head: Buffer | undefined;
293+
let cb: ErrorCallback | undefined;
260294

261295
// optional args parse begin
262296
if (typeof args[counter] === "function") {
263297
cb = args[counter];
264298
counter--;
265299
}
266300

267-
let requestOptions;
301+
let requestOptions: ServerOptions;
268302
if (!(args[counter] instanceof Buffer) && args[counter] !== res) {
269303
// Copy global options, and overwrite with request options
270304
requestOptions = { ...options, ...args[counter] };
@@ -277,7 +311,7 @@ export class ProxyServer extends EventEmitter<ProxyServerEventMap> {
277311
head = args[counter];
278312
}
279313

280-
for (const e of ["target", "forward"]) {
314+
for (const e of ["target", "forward"] as const) {
281315
if (typeof requestOptions[e] === "string") {
282316
requestOptions[e] = toURL(requestOptions[e]);
283317
}
@@ -297,7 +331,7 @@ export class ProxyServer extends EventEmitter<ProxyServerEventMap> {
297331
* refer to the connection socket
298332
* pass(req, socket, options, head)
299333
*/
300-
if (pass(req, res, requestOptions, head, this, cb)) {
334+
if (pass(req, res, requestOptions as NormalizedServerOptions, head, this, cb)) {
301335
// passes can return a truthy value to halt the loop
302336
break;
303337
}
@@ -356,12 +390,12 @@ export class ProxyServer extends EventEmitter<ProxyServerEventMap> {
356390
});
357391
};
358392

359-
before = (type: ProxyType, passName: string, cb: Function) => {
393+
before = <PT extends ProxyType>(type: PT, passName: string, cb: PassFunctions[PT]) => {
360394
if (type !== "ws" && type !== "web") {
361395
throw new Error("type must be `web` or `ws`");
362396
}
363-
const passes = type === "ws" ? this.wsPasses : this.webPasses;
364-
let i = false;
397+
const passes = (type === "ws" ? this.wsPasses : this.webPasses) as PassFunctions[PT][];
398+
let i: false | number = false;
365399

366400
passes.forEach((v, idx) => {
367401
if (v.name === passName) {
@@ -376,12 +410,12 @@ export class ProxyServer extends EventEmitter<ProxyServerEventMap> {
376410
passes.splice(i, 0, cb);
377411
};
378412

379-
after = (type: ProxyType, passName: string, cb: Function) => {
413+
after = <PT extends ProxyType>(type: PT, passName: string, cb: PassFunctions[PT]) => {
380414
if (type !== "ws" && type !== "web") {
381415
throw new Error("type must be `web` or `ws`");
382416
}
383-
const passes = type === "ws" ? this.wsPasses : this.webPasses;
384-
let i = false;
417+
const passes = (type === "ws" ? this.wsPasses : this.webPasses) as PassFunctions[PT][];
418+
let i: false | number = false;
385419

386420
passes.forEach((v, idx) => {
387421
if (v.name === passName) {

0 commit comments

Comments
 (0)