Skip to content

Commit 4dbda7a

Browse files
committed
Fix tests & add a few more pull requests
Added Pull Request from: - http-party/node-http-proxy#1634 - http-party/node-http-proxy#1638 - http-party/node-http-proxy#1650 fix tests that were having issues with the ports (if it fails the tests needs to be reran, seems to be an async issue)
1 parent aaf0dd2 commit 4dbda7a

9 files changed

+592
-4050
lines changed

README.md

+6-5
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,15 @@ $ npm run test
7474
- **mergeCookies:** `boolean` - allows to merge `set-cookie` headers from passed response and response from target. Default: false.
7575
- **headers:** `object` - object with extra headers to be added to target requests.
7676
- **outgoingHeaders:** `object` - object with extra headers to be added to proxy requests.
77-
- **proxyTimeout:** `number` timeout (in millis) for outgoing proxy requests
78-
- **timeout:** `number` timeout (in millis) for incoming requests
77+
- **proxyTimeout:** `number` - timeout (in millis) for outgoing proxy requests
78+
- **proxyTimeoutCustomError**: `boolean` - specify whether you want to throw a custom `ETIMEDOUT` error when the `proxyTimeout` is reached. If false then the default `ECONNRESET` error will be thrown. Default: false.
79+
- **timeout:** `number` - timeout (in millis) for incoming requests
7980
- **followRedirects:** `boolean` - Default: false - specify whether you want to follow redirects
8081
- **forcePasses:** `boolean` - if set to true the web passes will be run even if `selfHandleResponse` is also set to true. (Default: false)
8182
- **selfHandleResponse:** `boolean` - 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-
- **createWsClientTransformStream:** `function|null` if set, this function will be called with three arguments `req`, `proxyReq` and `proxyRes` and should return a Duplex stream, data from the client websocket will be piped through this stream before being piped to the server, allowing you to influence the request data.
83-
- **createWsServerTransformStream:** `function|null` if set, this function will be called with three arguments `req`, `proxyReq` and `proxyRes` and should return a Duplex stream, data from the server websocket will be piped through this stream before being piped to the client, allowing you to influence the response data.
84-
- **buffer:** `Buffer` stream of data to send as the request body. Maybe you have some middleware that consumes the request stream before proxying it on e.g. If you read the body of a request into a field called 'req.rawbody' you could restream this field in the buffer option:
83+
- **createWsClientTransformStream:** `function|null` - if set, this function will be called with three arguments `req`, `proxyReq` and `proxyRes` and should return a Duplex stream, data from the client websocket will be piped through this stream before being piped to the server, allowing you to influence the request data.
84+
- **createWsServerTransformStream:** `function|null` - if set, this function will be called with three arguments `req`, `proxyReq` and `proxyRes` and should return a Duplex stream, data from the server websocket will be piped through this stream before being piped to the client, allowing you to influence the response data.
85+
- **buffer:** `Buffer` - stream of data to send as the request body. Maybe you have some middleware that consumes the request stream before proxying it on e.g. If you read the body of a request into a field called 'req.rawbody' you could restream this field in the buffer option:
8586
```ts
8687
import streamify from 'stream-array'
8788
import { ProxyServer } from '@refactorjs/http-proxy'

package-lock.json

+496-4,004
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@refactorjs/http-proxy",
3-
"version": "0.1.5",
3+
"version": "0.1.8",
44
"description": "http-proxy alternative",
55
"repository": {
66
"type": "git",
@@ -26,24 +26,24 @@
2626
"test": "vitest run"
2727
},
2828
"dependencies": {
29-
"follow-redirects": "^1.15.2",
29+
"follow-redirects": "^1.15.3",
3030
"requires-port": "^1.0.0"
3131
},
3232
"devDependencies": {
33-
"@types/follow-redirects": "^1.14.1",
34-
"@types/node": "^20.5.9",
35-
"@types/requires-port": "^1.0.0",
33+
"@types/follow-redirects": "^1.14.3",
34+
"@types/node": "^18",
35+
"@types/requires-port": "^1.0.2",
3636
"async": "^3.2.4",
3737
"concat-stream": "^2.0.0",
3838
"nyc": "^15.1.0",
39-
"semver": "^7.3.8",
40-
"socket.io": "^4.5.4",
41-
"socket.io-client": "^4.5.4",
39+
"semver": "^7.5.4",
40+
"socket.io": "^4.7.2",
41+
"socket.io-client": "^4.7.2",
4242
"sse": "0.0.8",
43-
"typescript": "^5.0.2",
43+
"typescript": "^5.2.2",
4444
"unbuild": "^2.0.0",
45-
"vitest": "^0.34.1",
46-
"ws": "^8.12.0"
45+
"vitest": "^0.34.6",
46+
"ws": "^8.14.2"
4747
},
4848
"engines": {
4949
"node": ">=14.13.1"

src/proxy/common.ts

+5-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i;
99
/**
1010
* Simple Regex for testing if protocol is https
1111
*/
12-
export const isSSL = /^https|wss/;
12+
export const isSSL = /^(?:http|ws)s/;
1313

1414
/**
1515
* Copies the right headers from `options` and `req` to
@@ -62,7 +62,10 @@ export function setupOutgoing(outgoing: OutgoingOptions, options: OutgoingOption
6262
if (options.ca) outgoing.ca = options.ca;
6363

6464
if (sslEnabled) {
65-
outgoing.rejectUnauthorized = (typeof options.secure === "undefined") ? true : options.secure;
65+
// Respect `NODE_TLS_REJECT_UNAUTHORIZED` environment variable (https://nodejs.org/docs/latest/api/cli.html#node_tls_reject_unauthorizedvalue)
66+
const NODE_TLS_REJECT_UNAUTHORIZED = process.env['NODE_TLS_REJECT_UNAUTHORIZED'];
67+
const rejectUnauthorizedEnv = typeof NODE_TLS_REJECT_UNAUTHORIZED !== 'undefined' ? NODE_TLS_REJECT_UNAUTHORIZED.toString() : undefined;
68+
outgoing.rejectUnauthorized = (typeof options.secure === "undefined") ? (rejectUnauthorizedEnv !== '0') : options.secure;
6669
}
6770

6871
outgoing.agent = options.agent || false;

src/proxy/passes/web.incoming.ts

+14-6
Original file line numberDiff line numberDiff line change
@@ -53,16 +53,18 @@ export function timeout(req: IncomingMessage, res: ServerResponse, options: Serv
5353
export function XHeaders(req: IncomingMessage, res: ServerResponse, options: Server.ServerOptions): void | boolean {
5454
if (!options.xfwd) return;
5555

56-
let encrypted = hasEncryptedConnection(req);
57-
let values: Record<string, string | string[] | undefined> = {
56+
const encrypted = hasEncryptedConnection(req);
57+
const values: Record<string, string | string[] | undefined> = {
5858
for: req.socket?.remoteAddress,
5959
port: getPort(req),
6060
proto: encrypted ? 'https' : 'http'
6161
};
6262

63-
for (let header of ['for', 'port', 'proto']) {
63+
for (const header of ['for', 'port', 'proto']) {
6464
const headerName = 'x-forwarded-' + header;
65-
if (!req.headers[headerName]) {
65+
if (req.headers?.[headerName]) {
66+
req.headers[headerName] += `, ${values[header]}`;
67+
} else {
6668
req.headers[headerName] = values[header];
6769
}
6870
}
@@ -126,6 +128,12 @@ export function stream(req: IncomingMessage, res: ServerResponse, options: Serve
126128
// show an error page at the initial request
127129
if (options.proxyTimeout) {
128130
proxyReq.setTimeout(options.proxyTimeout, function () {
131+
if (options.proxyTimeoutCustomError) {
132+
let timeoutError = new Error('The proxy request timed out');
133+
// @ts-ignore - NodeJs does not export code
134+
timeoutError.code = 'ETIMEDOUT';
135+
return proxyReq.destroy(timeoutError);
136+
}
129137
proxyReq.destroy();
130138
});
131139
}
@@ -145,7 +153,7 @@ export function stream(req: IncomingMessage, res: ServerResponse, options: Serve
145153

146154
function createErrorHandler(proxyReq: httpNative.ClientRequest, target: Server.ServerOptions['target']) {
147155
return function proxyError(err: any) {
148-
if (req.socket.destroyed && err.code === 'ECONNRESET') {
156+
if (req.socket?.destroyed && err.code === 'ECONNRESET') {
149157
server.emit('econnreset', err, req, res, target);
150158
proxyReq.destroy();
151159
return;
@@ -193,4 +201,4 @@ export function stream(req: IncomingMessage, res: ServerResponse, options: Serve
193201
}
194202
}
195203
});
196-
}
204+
}

src/proxy/passes/web.outgoing.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ export function writeHeaders(req: IncomingMessage, res: ServerResponse, proxyRes
128128
// https://nodejs.org/api/http.html#http_message_rawheaders
129129
if (preserveHeaderKeyCase && proxyRes.rawHeaders != undefined) {
130130
for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {
131-
let key = proxyRes.rawHeaders[i];
131+
const key = proxyRes.rawHeaders[i];
132132
rawHeaderKeyMap[key.toLowerCase()] = key;
133133
}
134134
}

src/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ export declare namespace Server {
105105
outgoingHeaders?: Record<string, string | number | readonly string[]> | http.IncomingHttpHeaders;
106106
/** Timeout (in milliseconds) when proxy receives no response from target. Default: 120000 (2 minutes) */
107107
proxyTimeout?: number;
108+
/** specify whether you want to throw a custom `ETIMEDOUT` error when the `proxyTimeout` is reached. If false then the default `ECONNRESET` error will be thrown. Default: false */
109+
proxyTimeoutCustomError?: boolean;
108110
/** Timeout (in milliseconds) for incoming requests */
109111
timeout?: number;
110112
/** Specify whether you want to follow redirects. Default: false */

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

+54-18
Original file line numberDiff line numberDiff line change
@@ -314,13 +314,49 @@ describe('#createProxyServer.web() using own http server', () => {
314314
).end();
315315
});
316316

317+
it('should proxy the request with custom timeout errors (proxyTimeoutCustomError)', (done) => {
318+
const proxy = createProxyServer({
319+
target: 'http://127.0.0.1:45003',
320+
proxyTimeout: 100,
321+
proxyTimeoutCustomError: true
322+
});
323+
324+
createServer().listen(45003);
325+
326+
const proxyServer = createServer(requestHandler);
327+
328+
const started = new Date().getTime();
329+
330+
function requestHandler(req, res) {
331+
proxy.once('error', function (err, errReq, errRes) {
332+
proxyServer.close();
333+
expect(err).to.be.an(Error);
334+
expect(errReq).to.be.equal(req);
335+
expect(errRes).to.be.equal(res);
336+
expect(new Date().getTime() - started).to.be.greaterThan(99);
337+
expect(err.code).to.be('ETIMEDOUT');
338+
done();
339+
});
340+
341+
proxy.web(req, res);
342+
}
343+
344+
proxyServer.listen('8074');
345+
346+
request({
347+
hostname: '127.0.0.1',
348+
port: '8074',
349+
method: 'GET',
350+
}, () => {}).end();
351+
});
352+
317353
it('should proxy the request and handle timeout error', async () => {
318354
const proxy = createProxyServer({
319-
target: 'http://127.0.0.1:45001',
355+
target: 'http://127.0.0.1:45009',
320356
timeout: 100,
321357
});
322358

323-
net.createServer().listen(45001);
359+
net.createServer().listen(45009);
324360

325361
const proxyServer = createServer(requestHandler);
326362

@@ -338,12 +374,12 @@ describe('#createProxyServer.web() using own http server', () => {
338374
proxy.web(req, res);
339375
}
340376

341-
proxyServer.listen('8085');
377+
proxyServer.listen('8075');
342378

343379
const req = request(
344380
{
345381
hostname: '127.0.0.1',
346-
port: '8085',
382+
port: '8075',
347383
method: 'GET',
348384
}, function () { });
349385

@@ -505,9 +541,9 @@ describe('#createProxyServer.web() using own http server', () => {
505541
await waitForClosed(proxyServer, source);
506542
});
507543

508-
it.skipIf(semver.gte(process.version, '18.0.0'))('should proxy the request with the Authorization header set', async () => {
544+
it('should proxy the request with the Authorization header set', async () => {
509545
const proxy = createProxyServer({
510-
target: 'http://127.0.0.1:8080',
546+
target: 'http://127.0.0.1:8091',
511547
auth: 'user:pass',
512548
});
513549

@@ -527,13 +563,13 @@ describe('#createProxyServer.web() using own http server', () => {
527563
});
528564

529565
proxyServer.listen('8081');
530-
source.listen('8080');
566+
source.listen('8091');
531567

532568
request('http://127.0.0.1:8081', function () { }).end();
533569
await waitForClosed(proxyServer, source);
534570
});
535571

536-
it.skipIf(semver.gte(process.version, '18.0.0'))('should proxy requests to multiple servers with different options', async () => {
572+
it('should proxy requests to multiple servers with different options', async () => {
537573
const proxy = createProxyServer();
538574

539575
// proxies to two servers depending on url, rewriting the url as well
@@ -543,11 +579,11 @@ describe('#createProxyServer.web() using own http server', () => {
543579
if (req.url.indexOf('/s1/') === 0) {
544580
proxy.web(req, res, {
545581
ignorePath: true,
546-
target: 'http://127.0.0.1:8081' + req.url.substring(3),
582+
target: 'http://127.0.0.1:8092' + req.url.substring(3),
547583
});
548584
} else {
549585
proxy.web(req, res, {
550-
target: 'http://127.0.0.1:8082',
586+
target: 'http://127.0.0.1:8093',
551587
});
552588
}
553589
}
@@ -558,24 +594,24 @@ describe('#createProxyServer.web() using own http server', () => {
558594
res.end();
559595
source1.close();
560596
expect(req.method).toEqual('GET');
561-
expect(req.headers.host.split(':')[1]).toEqual('8080');
597+
expect(req.headers.host.split(':')[1]).toEqual('8090');
562598
expect(req.url).toEqual('/test1');
563599
});
564600

565601
const source2 = createServer(function (req, res) {
566602
res.end();
567603
source2.close();
568604
expect(req.method).toEqual('GET');
569-
expect(req.headers.host.split(':')[1]).toEqual('8080');
605+
expect(req.headers.host.split(':')[1]).toEqual('8090');
570606
expect(req.url).toEqual('/test2');
571607
});
572608

573-
proxyServer.listen('8080');
574-
source1.listen('8081');
575-
source2.listen('8082');
609+
proxyServer.listen('8090');
610+
source1.listen('8092');
611+
source2.listen('8093');
576612

577-
request('http://127.0.0.1:8080/s1/test1', function () { }).end();
578-
request('http://127.0.0.1:8080/test2', function () { }).end();
613+
request('http://127.0.0.1:8090/s1/test1', function () { }).end();
614+
request('http://127.0.0.1:8090/test2', function () { }).end();
579615

580616
await waitForClosed(source1, source2);
581617
proxyServer.close();
@@ -584,7 +620,7 @@ describe('#createProxyServer.web() using own http server', () => {
584620
});
585621

586622
describe('#followRedirects', () => {
587-
it.skipIf(semver.gte(process.version, '18.0.0'))('should proxy the request follow redirects', async () => {
623+
it('should proxy the request follow redirects', async () => {
588624
const proxy = createProxyServer({
589625
target: 'http://127.0.0.1:8080',
590626
followRedirects: {},

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

+3-3
Original file line numberDiff line numberDiff line change
@@ -570,16 +570,16 @@ describe('src/proxy/passes/web.outgoing.ts', () => {
570570
how: 'are you?'
571571
}
572572
};
573-
573+
574574
const res = {
575575
setHeader: function (k, v) {
576576
this.headers[k] = v;
577577
},
578578
headers: {}
579579
};
580-
580+
581581
attachOutgoingHeaders({}, res, proxyRes, { outgoingHeaders: { billy: 'sally' } });
582-
582+
583583
expect(res.headers.hey).toBeUndefined();
584584
expect(res.headers.how).toBeUndefined();
585585
expect(res.headers.billy).toEqual('sally');

0 commit comments

Comments
 (0)