Skip to content

Commit

Permalink
feat(option): pathFilter
Browse files Browse the repository at this point in the history
  • Loading branch information
chimurai committed Feb 19, 2022
1 parent 1540dac commit 3d575b1
Show file tree
Hide file tree
Showing 14 changed files with 72 additions and 128 deletions.
3 changes: 2 additions & 1 deletion examples/browser-sync/index.js
Expand Up @@ -7,8 +7,9 @@ const { createProxyMiddleware } = require('../../dist'); // require('http-proxy-
/**
* Configure proxy middleware
*/
const jsonPlaceholderProxy = createProxyMiddleware('/users', {
const jsonPlaceholderProxy = createProxyMiddleware({
target: 'http://jsonplaceholder.typicode.com',
pathFilter: '/users',
changeOrigin: true, // for vhosted sites, changes host header to match to target's host
logLevel: 'debug',
});
Expand Down
2 changes: 1 addition & 1 deletion examples/websocket/index.html
Expand Up @@ -21,7 +21,7 @@
<body>
<h2>WebSocket demo</h2>

<p>Proxy <code>ws://localhost:3000</code> to <code>ws://echo.websocket.org</code></p>
<p>Proxy <code>ws://localhost:3000</code> to <code>ws://ws.ifelse.io</code></p>

<fieldset id="configuration">
<p>
Expand Down
4 changes: 2 additions & 2 deletions examples/websocket/index.js
Expand Up @@ -7,8 +7,8 @@ const { createProxyMiddleware } = require('../../dist'); // require('http-proxy-
/**
* Configure proxy middleware
*/
const wsProxy = createProxyMiddleware('/', {
target: 'http://echo.websocket.org',
const wsProxy = createProxyMiddleware({
target: 'http://ws.ifelse.io',
// pathRewrite: {
// '^/websocket' : '/socket', // rewrite path.
// '^/removepath' : '' // remove path.
Expand Down
46 changes: 4 additions & 42 deletions src/config-factory.ts
@@ -1,53 +1,15 @@
import isPlainObj = require('is-plain-obj');
import { ERRORS } from './errors';
import { getInstance } from './logger';
import { Filter, Options } from './types';
import { Options } from './types';

const logger = getInstance();

export type Config = { context: Filter; options: Options };
export function verifyConfig(options: Options): void {
configureLogger(options);

export function createConfig(context, opts?: Options): Config {
// structure of config object to be returned
const config: Config = {
context: undefined,
options: {} as Options,
};

// app.use('/api', proxy({target:'http://localhost:9000'}));
if (isContextless(context, opts)) {
config.context = '/';
config.options = Object.assign(config.options, context);

// app.use('/api', proxy('http://localhost:9000'));
// app.use(proxy('http://localhost:9000/api'));
} else {
config.context = context;
config.options = Object.assign(config.options, opts);
}

configureLogger(config.options);

if (!config.options.target && !config.options.router) {
if (!options.target && !options.router) {
throw new Error(ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING);
}

return config;
}

/**
* Checks if a Object only config is provided, without a context.
* In this case the all paths will be proxied.
*
* @example
* app.use('/api', proxy({target:'http://localhost:9000'}));
*
* @param {Object} context [description]
* @param {*} opts [description]
* @return {Boolean} [description]
*/
function isContextless(context: Filter, opts: Options) {
return isPlainObj(context) && (opts == null || Object.keys(opts).length === 0);
}

function configureLogger(options: Options) {
Expand Down
4 changes: 2 additions & 2 deletions src/context-matcher.ts
Expand Up @@ -4,15 +4,15 @@ import * as micromatch from 'micromatch';
import * as url from 'url';
import { ERRORS } from './errors';

export function match(context: Filter, uri: string, req: Request): boolean {
export function match(context: Filter = '/', uri: string, req: Request): boolean {
// single path
if (isStringPath(context as string)) {
return matchSingleStringPath(context as string, uri);
}

// single glob path
if (isGlobPath(context as string)) {
return matchSingleGlobPath(context as string[], uri);
return matchSingleGlobPath(context as unknown as string[], uri);
}

// multi path
Expand Down
18 changes: 9 additions & 9 deletions src/http-proxy-middleware.ts
@@ -1,29 +1,29 @@
import type * as https from 'https';
import type * as express from 'express';
import type { Filter, Request, RequestHandler, Response, Options } from './types';
import type { Request, RequestHandler, Response, Options } from './types';
import * as httpProxy from 'http-proxy';
import { createConfig, Config } from './config-factory';
import { verifyConfig } from './config-factory';
import * as contextMatcher from './context-matcher';
import * as handlers from './_handlers';
import { getArrow, getInstance } from './logger';
import * as PathRewriter from './path-rewriter';
import * as Router from './router';

export class HttpProxyMiddleware {
private logger = getInstance();
private config: Config;
private wsInternalSubscribed = false;
private serverOnCloseSubscribed = false;
private proxyOptions: Options;
private proxy: httpProxy;
private pathRewriter;

constructor(context: Filter | Options, opts?: Options) {
this.config = createConfig(context, opts);
this.proxyOptions = this.config.options;
constructor(options: Options) {
verifyConfig(options);
this.proxyOptions = options;

// create proxy
this.proxy = httpProxy.createProxyServer({});
this.logger.info(`[HPM] Proxy created: ${this.config.context} -> ${this.proxyOptions.target}`);
this.logger.info(`[HPM] Proxy created: ${options.pathFilter ?? '/'} -> ${options.target}`);

this.pathRewriter = PathRewriter.createPathRewriter(this.proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided

Expand All @@ -48,7 +48,7 @@ export class HttpProxyMiddleware {
res: Response,
next: express.NextFunction
) => {
if (this.shouldProxy(this.config.context, req)) {
if (this.shouldProxy(this.proxyOptions.pathFilter, req)) {
try {
const activeProxyOptions = await this.prepareProxyRequest(req);
this.proxy.web(req, res, activeProxyOptions);
Expand Down Expand Up @@ -93,7 +93,7 @@ export class HttpProxyMiddleware {
};

private handleUpgrade = async (req: Request, socket, head) => {
if (this.shouldProxy(this.config.context, req)) {
if (this.shouldProxy(this.proxyOptions.pathFilter, req)) {
const activeProxyOptions = await this.prepareProxyRequest(req);
this.proxy.ws(req, socket, head, activeProxyOptions);
this.logger.info('[HPM] Upgrading to WebSocket');
Expand Down
11 changes: 9 additions & 2 deletions src/index.ts
@@ -1,11 +1,18 @@
import { HttpProxyMiddleware } from './http-proxy-middleware';
import { Filter, Options } from './types';

export function createProxyMiddleware(context: Filter | Options, options?: Options) {
const { middleware } = new HttpProxyMiddleware(context, options);
export function createProxyMiddleware(options: Options) {
const { middleware } = new HttpProxyMiddleware(options);
return middleware;
}

/**
* @deprecated
*/
export function legacyCreateProxyMiddleware(pathFilter: Filter, options: Options) {
return createProxyMiddleware({ ...options, pathFilter });
}

export * from './handlers';

export { Filter, Options, RequestHandler } from './types';
1 change: 1 addition & 0 deletions src/types.ts
Expand Up @@ -21,6 +21,7 @@ export interface RequestHandler extends express.RequestHandler {
export type Filter = string | string[] | ((pathname: string, req: Request) => boolean);

export interface Options extends httpProxy.ServerOptions {
pathFilter?: Filter;
pathRewrite?:
| { [regexp: string]: string }
| ((path: string, req: Request) => string)
Expand Down
3 changes: 2 additions & 1 deletion test/e2e/express-router.spec.ts
Expand Up @@ -30,8 +30,9 @@ describe('Usage in Express', () => {
changeOrigin: true,
logLevel: 'silent',
target: 'http://jsonplaceholder.typicode.com',
pathFilter: filter,
};
sub.use(createProxyMiddleware(filter, proxyConfig));
sub.use(createProxyMiddleware(proxyConfig));

sub.get('/hello', jsonMiddleware({ content: 'foobar' }));

Expand Down
42 changes: 28 additions & 14 deletions test/e2e/http-proxy-middleware.spec.ts
Expand Up @@ -8,8 +8,9 @@ import * as bodyParser from 'body-parser';
describe('E2E http-proxy-middleware', () => {
describe('http-proxy-middleware creation', () => {
it('should create a middleware', () => {
const middleware = createProxyMiddleware('/api', {
const middleware = createProxyMiddleware({
target: `http://localhost:8000`,
pathFilter: '/api',
});
expect(typeof middleware).toBe('function');
});
Expand All @@ -22,8 +23,9 @@ describe('E2E http-proxy-middleware', () => {
const mockNext: NextFunction = jest.fn();

beforeEach(() => {
const middleware = createProxyMiddleware('/api', {
const middleware = createProxyMiddleware({
target: `http://localhost:8000`,
pathFilter: '/api',
});

middleware(mockReq, mockRes, mockNext);
Expand Down Expand Up @@ -52,8 +54,9 @@ describe('E2E http-proxy-middleware', () => {
beforeEach(() => {
agent = request(
createApp(
createProxyMiddleware('/api', {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: '/api',
})
)
);
Expand Down Expand Up @@ -84,8 +87,9 @@ describe('E2E http-proxy-middleware', () => {
agent = request(
createApp(
bodyParser.urlencoded({ extended: false }),
createProxyMiddleware('/api', {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: '/api',
onProxyReq: fixRequestBody,
})
)
Expand All @@ -102,8 +106,9 @@ describe('E2E http-proxy-middleware', () => {
agent = request(
createApp(
bodyParser.json(),
createProxyMiddleware('/api', {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: '/api',
onProxyReq: fixRequestBody,
})
)
Expand All @@ -125,8 +130,9 @@ describe('E2E http-proxy-middleware', () => {

agent = request(
createApp(
createProxyMiddleware(filter, {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: filter,
})
)
);
Expand All @@ -143,8 +149,9 @@ describe('E2E http-proxy-middleware', () => {

agent = request(
createApp(
createProxyMiddleware(filter, {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: filter,
})
)
);
Expand All @@ -159,8 +166,9 @@ describe('E2E http-proxy-middleware', () => {
beforeEach(() => {
agent = request(
createApp(
createProxyMiddleware(['/api', '/ajax'], {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: ['/api', '/ajax'],
})
)
);
Expand Down Expand Up @@ -188,8 +196,9 @@ describe('E2E http-proxy-middleware', () => {
beforeEach(() => {
agent = request(
createApp(
createProxyMiddleware('/api/**', {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: '/api/**',
})
)
);
Expand All @@ -206,8 +215,9 @@ describe('E2E http-proxy-middleware', () => {
beforeEach(() => {
agent = request(
createApp(
createProxyMiddleware(['**/*.html', '!**.json'], {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: ['**/*.html', '!**.json'],
})
)
);
Expand All @@ -230,8 +240,9 @@ describe('E2E http-proxy-middleware', () => {
beforeEach(() => {
agent = request(
createApp(
createProxyMiddleware('/api', {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: '/api',
headers: { host: 'foobar.dev' },
})
)
Expand Down Expand Up @@ -301,8 +312,9 @@ describe('E2E http-proxy-middleware', () => {
beforeEach(() => {
agent = request(
createApp(
createProxyMiddleware('/api', {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: '/api',
onProxyRes(proxyRes, req, res) {
// tslint:disable-next-line: no-string-literal
proxyRes['headers']['x-added'] = 'foobar'; // add custom header to response
Expand Down Expand Up @@ -338,8 +350,9 @@ describe('E2E http-proxy-middleware', () => {
beforeEach(() => {
agent = request(
createApp(
createProxyMiddleware('/api', {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: '/api',
onProxyReq(proxyReq, req, res) {
proxyReq.setHeader('x-added', 'added-from-hpm'); // add custom header to request
},
Expand Down Expand Up @@ -417,8 +430,9 @@ describe('E2E http-proxy-middleware', () => {

agent = request(
createApp(
createProxyMiddleware('/api', {
createProxyMiddleware({
target: `http://localhost:${mockTargetServer.port}`,
pathFilter: '/api',
logLevel: 'info',
logProvider(provider) {
return { ...provider, debug: customLogger, info: customLogger };
Expand Down
5 changes: 3 additions & 2 deletions test/e2e/websocket.spec.ts
Expand Up @@ -33,7 +33,7 @@ describe('E2E WebSocket proxy', () => {
});

beforeEach(() => {
proxyMiddleware = createProxyMiddleware('/', {
proxyMiddleware = createProxyMiddleware({
target: `http://localhost:${WS_SERVER_PORT}`,
ws: true,
pathRewrite: { '^/socket': '' },
Expand Down Expand Up @@ -100,7 +100,8 @@ describe('E2E WebSocket proxy', () => {
// override
proxyServer = createApp(
// cSpell:ignore notworkinghost
createProxyMiddleware('ws://notworkinghost:6789', {
createProxyMiddleware({
target: 'ws://notworkinghost:6789',
router: { '/socket': `ws://localhost:${WS_SERVER_PORT}` },
pathRewrite: { '^/socket': '' },
})
Expand Down

0 comments on commit 3d575b1

Please sign in to comment.