forked from daren.hsu/line_push
update
This commit is contained in:
+40
-1
@@ -1,6 +1,45 @@
|
||||
# Changelog
|
||||
|
||||
## [v1.0.5](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.0.5)
|
||||
## [v1.3.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.3.1)
|
||||
|
||||
- fix(fix-request-body): make sure the content-type exists ([#578](https://github.com/chimurai/http-proxy-middleware/pull/578)) ([oufeng](https://github.com/oufeng))
|
||||
|
||||
## [v1.3.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.3.0)
|
||||
|
||||
- docs(response interceptor): align with nodejs default utf8 ([#567](https://github.com/chimurai/http-proxy-middleware/pull/567))
|
||||
- feat: try to proxy body even after body-parser middleware ([#492](https://github.com/chimurai/http-proxy-middleware/pull/492)) ([midgleyc](https://github.com/midgleyc))
|
||||
|
||||
## [v1.2.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.2.1)
|
||||
|
||||
- fix(response interceptor): proxy original response headers ([#563](https://github.com/chimurai/http-proxy-middleware/pull/563))
|
||||
|
||||
## [v1.2.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.2.0)
|
||||
|
||||
- feat(handler): response interceptor ([#520](https://github.com/chimurai/http-proxy-middleware/pull/520))
|
||||
- fix(log error): handle undefined target when websocket errors ([#527](https://github.com/chimurai/http-proxy-middleware/pull/527))
|
||||
|
||||
## [v1.1.2](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.1.2)
|
||||
|
||||
- fix(log error): handle optional target ([#523](https://github.com/chimurai/http-proxy-middleware/pull/523))
|
||||
|
||||
## [v1.1.1](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.1.1)
|
||||
|
||||
- fix(error handler): re-throw http-proxy missing target error ([#517](https://github.com/chimurai/http-proxy-middleware/pull/517))
|
||||
- refactor(dependency): remove `camelcase`
|
||||
- fix(option): optional `target` when `router` is used ([#512](https://github.com/chimurai/http-proxy-middleware/pull/512))
|
||||
|
||||
## [v1.1.0](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.1.0)
|
||||
|
||||
- fix(errorHandler): fix confusing error message ([#509](https://github.com/chimurai/http-proxy-middleware/pull/509))
|
||||
- fix(proxy): close proxy when server closes ([#508](https://github.com/chimurai/http-proxy-middleware/pull/508))
|
||||
- refactor(lodash): remove lodash ([#459](https://github.com/chimurai/http-proxy-middleware/pull/459)) ([#507](https://github.com/chimurai/http-proxy-middleware/pull/507)) ([TrySound](https://github.com/TrySound))
|
||||
- fix(ETIMEDOUT): return 504 on ETIMEDOUT ([#480](https://github.com/chimurai/http-proxy-middleware/pull/480)) ([aremishevsky](https://github.com/aremishevsky))
|
||||
|
||||
## [v1.0.6](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.0.6)
|
||||
|
||||
- chore(deps): lodash 4.17.20 ([#475](https://github.com/chimurai/http-proxy-middleware/pull/475))
|
||||
|
||||
## [v1.0.5](https://github.com/chimurai/http-proxy-middleware/releases/tag/v1.0.6)
|
||||
|
||||
- chore(deps): lodash 4.17.19 ([#454](https://github.com/chimurai/http-proxy-middleware/pull/454))
|
||||
|
||||
|
||||
+60
-4
@@ -1,10 +1,10 @@
|
||||
# http-proxy-middleware
|
||||
|
||||
[](https://travis-ci.org/chimurai/http-proxy-middleware)
|
||||
[](https://github.com/chimurai/http-proxy-middleware/actions?query=branch%3Amaster)
|
||||
[](https://coveralls.io/r/chimurai/http-proxy-middleware)
|
||||
[](https://david-dm.org/chimurai/http-proxy-middleware#info=dependencies)
|
||||
[](https://snyk.io/test/npm/http-proxy-middleware)
|
||||
[](https://github.com/prettier/prettier)
|
||||
[](https://www.npmjs.com/package/http-proxy-middleware)
|
||||
|
||||
Node.js proxying made simple. Configure proxy middleware with ease for [connect](https://github.com/senchalabs/connect), [express](https://github.com/strongloop/express), [browser-sync](https://github.com/BrowserSync/browser-sync) and [many more](#compatible-servers).
|
||||
|
||||
@@ -69,6 +69,8 @@ _All_ `http-proxy` [options](https://github.com/nodejitsu/node-http-proxy#option
|
||||
- [app.use\(path, proxy\)](#appusepath-proxy)
|
||||
- [WebSocket](#websocket)
|
||||
- [External WebSocket upgrade](#external-websocket-upgrade)
|
||||
- [Intercept and manipulate requests](#intercept-and-manipulate-requests)
|
||||
- [Intercept and manipulate responses](#intercept-and-manipulate-responses)
|
||||
- [Working examples](#working-examples)
|
||||
- [Recipes](#recipes)
|
||||
- [Compatible servers](#compatible-servers)
|
||||
@@ -298,7 +300,7 @@ Subscribe to [http-proxy events](https://github.com/nodejitsu/node-http-proxy#li
|
||||
- **option.onError**: function, subscribe to http-proxy's `error` event for custom error handling.
|
||||
|
||||
```javascript
|
||||
function onError(err, req, res) {
|
||||
function onError(err, req, res, target) {
|
||||
res.writeHead(500, {
|
||||
'Content-Type': 'text/plain',
|
||||
});
|
||||
@@ -481,6 +483,58 @@ const server = app.listen(3000);
|
||||
server.on('upgrade', wsProxy.upgrade); // <-- subscribe to http 'upgrade'
|
||||
```
|
||||
|
||||
## Intercept and manipulate requests
|
||||
|
||||
Intercept requests from downstream by defining `onProxyReq` in `createProxyMiddleware`.
|
||||
|
||||
Currently the only pre-provided request interceptor is `fixRequestBody`, which is used to fix proxied POST requests when `bodyParser` is applied before this middleware.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const { createProxyMiddleware, fixRequestBody } = require('http-proxy-middleware');
|
||||
|
||||
const proxy = createProxyMiddleware({
|
||||
/**
|
||||
* Fix bodyParser
|
||||
**/
|
||||
onProxyReq: fixRequestBody,
|
||||
});
|
||||
```
|
||||
|
||||
## Intercept and manipulate responses
|
||||
|
||||
Intercept responses from upstream with `responseInterceptor`. (Make sure to set `selfHandleResponse: true`)
|
||||
|
||||
Responses which are compressed with `brotli`, `gzip` and `deflate` will be decompressed automatically. The response will be returned as `buffer` ([docs](https://nodejs.org/api/buffer.html)) which you can manipulate.
|
||||
|
||||
With `buffer`, response manipulation is not limited to text responses (html/css/js, etc...); image manipulation will be possible too. ([example](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#manipulate-image-response))
|
||||
|
||||
NOTE: `responseInterceptor` disables streaming of target's response.
|
||||
|
||||
Example:
|
||||
|
||||
```javascript
|
||||
const { createProxyMiddleware, responseInterceptor } = require('http-proxy-middleware');
|
||||
|
||||
const proxy = createProxyMiddleware({
|
||||
/**
|
||||
* IMPORTANT: avoid res.end being called automatically
|
||||
**/
|
||||
selfHandleResponse: true, // res.end() will be called internally by responseInterceptor()
|
||||
|
||||
/**
|
||||
* Intercept response and replace 'Hello' with 'Goodbye'
|
||||
**/
|
||||
onProxyRes: responseInterceptor(async (responseBuffer, proxyRes, req, res) => {
|
||||
const response = responseBuffer.toString('utf8'); // convert buffer to string
|
||||
return response.replace('Hello', 'Goodbye'); // manipulate response and return the result
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
Check out [interception recipes](https://github.com/chimurai/http-proxy-middleware/blob/master/recipes/response-interceptor.md#readme) for more examples.
|
||||
|
||||
## Working examples
|
||||
|
||||
View and play around with [working examples](https://github.com/chimurai/http-proxy-middleware/tree/master/examples).
|
||||
@@ -489,6 +543,7 @@ View and play around with [working examples](https://github.com/chimurai/http-pr
|
||||
- express ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/express/index.js))
|
||||
- connect ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/connect/index.js))
|
||||
- WebSocket ([example source](https://github.com/chimurai/http-proxy-middleware/tree/master/examples/websocket/index.js))
|
||||
- Response Manipulation ([example source](https://github.com/chimurai/http-proxy-middleware/blob/master/examples/response-interceptor/index.js))
|
||||
|
||||
## Recipes
|
||||
|
||||
@@ -500,6 +555,7 @@ View the [recipes](https://github.com/chimurai/http-proxy-middleware/tree/master
|
||||
|
||||
- [connect](https://www.npmjs.com/package/connect)
|
||||
- [express](https://www.npmjs.com/package/express)
|
||||
- [fastify](https://www.npmjs.com/package/fastify)
|
||||
- [browser-sync](https://www.npmjs.com/package/browser-sync)
|
||||
- [lite-server](https://www.npmjs.com/package/lite-server)
|
||||
- [polka](https://github.com/lukeed/polka)
|
||||
@@ -540,4 +596,4 @@ $ yarn cover
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015-2020 Steven Chim
|
||||
Copyright (c) 2015-2021 Steven Chim
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import type { Options } from './types';
|
||||
import type * as httpProxy from 'http-proxy';
|
||||
export declare function init(proxy: httpProxy, option: Options): void;
|
||||
export declare function getHandlers(options: Options): any;
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getHandlers = exports.init = void 0;
|
||||
const logger_1 = require("./logger");
|
||||
const logger = logger_1.getInstance();
|
||||
function init(proxy, option) {
|
||||
const handlers = getHandlers(option);
|
||||
for (const eventName of Object.keys(handlers)) {
|
||||
proxy.on(eventName, handlers[eventName]);
|
||||
}
|
||||
logger.debug('[HPM] Subscribed to http-proxy events:', Object.keys(handlers));
|
||||
}
|
||||
exports.init = init;
|
||||
function getHandlers(options) {
|
||||
// https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events
|
||||
const proxyEventsMap = {
|
||||
error: 'onError',
|
||||
proxyReq: 'onProxyReq',
|
||||
proxyReqWs: 'onProxyReqWs',
|
||||
proxyRes: 'onProxyRes',
|
||||
open: 'onOpen',
|
||||
close: 'onClose',
|
||||
};
|
||||
const handlers = {};
|
||||
for (const [eventName, onEventName] of Object.entries(proxyEventsMap)) {
|
||||
// all handlers for the http-proxy events are prefixed with 'on'.
|
||||
// loop through options and try to find these handlers
|
||||
// and add them to the handlers object for subscription in init().
|
||||
const fnHandler = options ? options[onEventName] : null;
|
||||
if (typeof fnHandler === 'function') {
|
||||
handlers[eventName] = fnHandler;
|
||||
}
|
||||
}
|
||||
// add default error handler in absence of error handler
|
||||
if (typeof handlers.error !== 'function') {
|
||||
handlers.error = defaultErrorHandler;
|
||||
}
|
||||
// add default close handler in absence of close handler
|
||||
if (typeof handlers.close !== 'function') {
|
||||
handlers.close = logClose;
|
||||
}
|
||||
return handlers;
|
||||
}
|
||||
exports.getHandlers = getHandlers;
|
||||
function defaultErrorHandler(err, req, res) {
|
||||
// Re-throw error. Not recoverable since req & res are empty.
|
||||
if (!req && !res) {
|
||||
throw err; // "Error: Must provide a proper URL as target"
|
||||
}
|
||||
const host = req.headers && req.headers.host;
|
||||
const code = err.code;
|
||||
if (res.writeHead && !res.headersSent) {
|
||||
if (/HPE_INVALID/.test(code)) {
|
||||
res.writeHead(502);
|
||||
}
|
||||
else {
|
||||
switch (code) {
|
||||
case 'ECONNRESET':
|
||||
case 'ENOTFOUND':
|
||||
case 'ECONNREFUSED':
|
||||
case 'ETIMEDOUT':
|
||||
res.writeHead(504);
|
||||
break;
|
||||
default:
|
||||
res.writeHead(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
res.end(`Error occured while trying to proxy: ${host}${req.url}`);
|
||||
}
|
||||
function logClose(req, socket, head) {
|
||||
// view disconnected websocket connections
|
||||
logger.info('[HPM] Client disconnected');
|
||||
}
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
import { Options } from './types';
|
||||
export declare function createConfig(context: any, opts?: any): {
|
||||
context: any;
|
||||
import { Filter, Options } from './types';
|
||||
export declare type Config = {
|
||||
context: Filter;
|
||||
options: Options;
|
||||
};
|
||||
export declare function createConfig(context: any, opts?: Options): Config;
|
||||
|
||||
+7
-7
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createConfig = void 0;
|
||||
const _ = require("lodash");
|
||||
const isPlainObj = require("is-plain-obj");
|
||||
const url = require("url");
|
||||
const errors_1 = require("./errors");
|
||||
const logger_1 = require("./logger");
|
||||
@@ -15,7 +15,7 @@ function createConfig(context, opts) {
|
||||
// app.use('/api', proxy({target:'http://localhost:9000'}));
|
||||
if (isContextless(context, opts)) {
|
||||
config.context = '/';
|
||||
config.options = _.assign(config.options, context);
|
||||
config.options = Object.assign(config.options, context);
|
||||
// app.use('/api', proxy('http://localhost:9000'));
|
||||
// app.use(proxy('http://localhost:9000/api'));
|
||||
}
|
||||
@@ -23,7 +23,7 @@ function createConfig(context, opts) {
|
||||
const oUrl = url.parse(context);
|
||||
const target = [oUrl.protocol, '//', oUrl.host].join('');
|
||||
config.context = oUrl.pathname || '/';
|
||||
config.options = _.assign(config.options, { target }, opts);
|
||||
config.options = Object.assign(config.options, { target }, opts);
|
||||
if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') {
|
||||
config.options.ws = true;
|
||||
}
|
||||
@@ -31,10 +31,10 @@ function createConfig(context, opts) {
|
||||
}
|
||||
else {
|
||||
config.context = context;
|
||||
config.options = _.assign(config.options, opts);
|
||||
config.options = Object.assign(config.options, opts);
|
||||
}
|
||||
configureLogger(config.options);
|
||||
if (!config.options.target) {
|
||||
if (!config.options.target && !config.options.router) {
|
||||
throw new Error(errors_1.ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING);
|
||||
}
|
||||
return config;
|
||||
@@ -52,7 +52,7 @@ exports.createConfig = createConfig;
|
||||
* @return {Boolean} [description]
|
||||
*/
|
||||
function isStringShortHand(context) {
|
||||
if (_.isString(context)) {
|
||||
if (typeof context === 'string') {
|
||||
return !!url.parse(context).host;
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ function isStringShortHand(context) {
|
||||
* @return {Boolean} [description]
|
||||
*/
|
||||
function isContextless(context, opts) {
|
||||
return _.isPlainObject(context) && _.isEmpty(opts);
|
||||
return isPlainObj(context) && (opts == null || Object.keys(opts).length === 0);
|
||||
}
|
||||
function configureLogger(options) {
|
||||
if (options.logLevel) {
|
||||
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
export declare function match(context: any, uri: any, req: any): any;
|
||||
import type { Filter, Request } from './types';
|
||||
export declare function match(context: Filter, uri: string, req: Request): boolean;
|
||||
|
||||
+2
-3
@@ -2,7 +2,6 @@
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.match = void 0;
|
||||
const isGlob = require("is-glob");
|
||||
const _ = require("lodash");
|
||||
const micromatch = require("micromatch");
|
||||
const url = require("url");
|
||||
const errors_1 = require("./errors");
|
||||
@@ -26,7 +25,7 @@ function match(context, uri, req) {
|
||||
throw new Error(errors_1.ERRORS.ERR_CONTEXT_MATCHER_INVALID_ARRAY);
|
||||
}
|
||||
// custom matching
|
||||
if (_.isFunction(context)) {
|
||||
if (typeof context === 'function') {
|
||||
const pathname = getUrlPathName(uri);
|
||||
return context(pathname, req);
|
||||
}
|
||||
@@ -75,7 +74,7 @@ function getUrlPathName(uri) {
|
||||
return uri && url.parse(uri).pathname;
|
||||
}
|
||||
function isStringPath(context) {
|
||||
return _.isString(context) && !isGlob(context);
|
||||
return typeof context === 'string' && !isGlob(context);
|
||||
}
|
||||
function isGlobPath(context) {
|
||||
return isGlob(context);
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
/// <reference types="node" />
|
||||
import { ClientRequest } from 'http';
|
||||
import type { Request } from '../types';
|
||||
/**
|
||||
* Fix proxied body if bodyParser is involved.
|
||||
*/
|
||||
export declare function fixRequestBody(proxyReq: ClientRequest, req: Request): void;
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.fixRequestBody = void 0;
|
||||
const querystring = require("querystring");
|
||||
/**
|
||||
* Fix proxied body if bodyParser is involved.
|
||||
*/
|
||||
function fixRequestBody(proxyReq, req) {
|
||||
if (!req.body || !Object.keys(req.body).length) {
|
||||
return;
|
||||
}
|
||||
const contentType = proxyReq.getHeader('Content-Type');
|
||||
const writeBody = (bodyData) => {
|
||||
// deepcode ignore ContentLengthInCode: bodyParser fix
|
||||
proxyReq.setHeader('Content-Length', Buffer.byteLength(bodyData));
|
||||
proxyReq.write(bodyData);
|
||||
};
|
||||
if (contentType && contentType.includes('application/json')) {
|
||||
writeBody(JSON.stringify(req.body));
|
||||
}
|
||||
if (contentType === 'application/x-www-form-urlencoded') {
|
||||
writeBody(querystring.stringify(req.body));
|
||||
}
|
||||
}
|
||||
exports.fixRequestBody = fixRequestBody;
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from './public';
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./public"), exports);
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export { responseInterceptor } from './response-interceptor';
|
||||
export { fixRequestBody } from './fix-request-body';
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.fixRequestBody = exports.responseInterceptor = void 0;
|
||||
var response_interceptor_1 = require("./response-interceptor");
|
||||
Object.defineProperty(exports, "responseInterceptor", { enumerable: true, get: function () { return response_interceptor_1.responseInterceptor; } });
|
||||
var fix_request_body_1 = require("./fix-request-body");
|
||||
Object.defineProperty(exports, "fixRequestBody", { enumerable: true, get: function () { return fix_request_body_1.fixRequestBody; } });
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
/// <reference types="node" />
|
||||
import type * as http from 'http';
|
||||
declare type Interceptor = (buffer: Buffer, proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<Buffer | string>;
|
||||
/**
|
||||
* Intercept responses from upstream.
|
||||
* Automatically decompress (deflate, gzip, brotli).
|
||||
* Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
|
||||
*
|
||||
* NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
|
||||
*/
|
||||
export declare function responseInterceptor(interceptor: Interceptor): (proxyRes: http.IncomingMessage, req: http.IncomingMessage, res: http.ServerResponse) => Promise<void>;
|
||||
export {};
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.responseInterceptor = void 0;
|
||||
const zlib = require("zlib");
|
||||
/**
|
||||
* Intercept responses from upstream.
|
||||
* Automatically decompress (deflate, gzip, brotli).
|
||||
* Give developer the opportunity to modify intercepted Buffer and http.ServerResponse
|
||||
*
|
||||
* NOTE: must set options.selfHandleResponse=true (prevent automatic call of res.end())
|
||||
*/
|
||||
function responseInterceptor(interceptor) {
|
||||
return function proxyRes(proxyRes, req, res) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
const originalProxyRes = proxyRes;
|
||||
let buffer = Buffer.from('', 'utf8');
|
||||
// decompress proxy response
|
||||
const _proxyRes = decompress(proxyRes, proxyRes.headers['content-encoding']);
|
||||
// concat data stream
|
||||
_proxyRes.on('data', (chunk) => (buffer = Buffer.concat([buffer, chunk])));
|
||||
_proxyRes.on('end', () => __awaiter(this, void 0, void 0, function* () {
|
||||
// copy original headers
|
||||
copyHeaders(proxyRes, res);
|
||||
// call interceptor with intercepted response (buffer)
|
||||
const interceptedBuffer = Buffer.from(yield interceptor(buffer, originalProxyRes, req, res));
|
||||
// set correct content-length (with double byte character support)
|
||||
res.setHeader('content-length', Buffer.byteLength(interceptedBuffer, 'utf8'));
|
||||
res.write(interceptedBuffer);
|
||||
res.end();
|
||||
}));
|
||||
_proxyRes.on('error', (error) => {
|
||||
res.end(`Error fetching proxied request: ${error.message}`);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
exports.responseInterceptor = responseInterceptor;
|
||||
/**
|
||||
* Streaming decompression of proxy response
|
||||
* source: https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L116
|
||||
*/
|
||||
function decompress(proxyRes, contentEncoding) {
|
||||
let _proxyRes = proxyRes;
|
||||
let decompress;
|
||||
switch (contentEncoding) {
|
||||
case 'gzip':
|
||||
decompress = zlib.createGunzip();
|
||||
break;
|
||||
case 'br':
|
||||
decompress = zlib.createBrotliDecompress();
|
||||
break;
|
||||
case 'deflate':
|
||||
decompress = zlib.createInflate();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (decompress) {
|
||||
_proxyRes.pipe(decompress);
|
||||
_proxyRes = decompress;
|
||||
}
|
||||
return _proxyRes;
|
||||
}
|
||||
/**
|
||||
* Copy original headers
|
||||
* https://github.com/apache/superset/blob/9773aba522e957ed9423045ca153219638a85d2f/superset-frontend/webpack.proxy-config.js#L78
|
||||
*/
|
||||
function copyHeaders(originalResponse, response) {
|
||||
response.statusCode = originalResponse.statusCode;
|
||||
response.statusMessage = originalResponse.statusMessage;
|
||||
if (response.setHeader) {
|
||||
let keys = Object.keys(originalResponse.headers);
|
||||
// ignore chunked, brotli, gzip, deflate headers
|
||||
keys = keys.filter((key) => !['content-encoding', 'transfer-encoding'].includes(key));
|
||||
keys.forEach((key) => {
|
||||
let value = originalResponse.headers[key];
|
||||
if (key === 'set-cookie') {
|
||||
// remove cookie domain
|
||||
value = Array.isArray(value) ? value : [value];
|
||||
value = value.map((x) => x.replace(/Domain=[^;]+?/i, ''));
|
||||
}
|
||||
response.setHeader(key, value);
|
||||
});
|
||||
}
|
||||
else {
|
||||
response.headers = originalResponse.headers;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
import { Filter, RequestHandler, Options } from './types';
|
||||
import type { Filter, RequestHandler, Options } from './types';
|
||||
export declare class HttpProxyMiddleware {
|
||||
private logger;
|
||||
private config;
|
||||
private wsInternalSubscribed;
|
||||
private serverOnCloseSubscribed;
|
||||
private proxyOptions;
|
||||
private proxy;
|
||||
private pathRewriter;
|
||||
|
||||
+28
-9
@@ -11,10 +11,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.HttpProxyMiddleware = void 0;
|
||||
const httpProxy = require("http-proxy");
|
||||
const _ = require("lodash");
|
||||
const config_factory_1 = require("./config-factory");
|
||||
const contextMatcher = require("./context-matcher");
|
||||
const handlers = require("./handlers");
|
||||
const handlers = require("./_handlers");
|
||||
const logger_1 = require("./logger");
|
||||
const PathRewriter = require("./path-rewriter");
|
||||
const Router = require("./router");
|
||||
@@ -22,8 +21,10 @@ class HttpProxyMiddleware {
|
||||
constructor(context, opts) {
|
||||
this.logger = logger_1.getInstance();
|
||||
this.wsInternalSubscribed = false;
|
||||
this.serverOnCloseSubscribed = false;
|
||||
// https://github.com/Microsoft/TypeScript/wiki/'this'-in-TypeScript#red-flags-for-this
|
||||
this.middleware = (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
||||
var _a, _b;
|
||||
if (this.shouldProxy(this.config.context, req)) {
|
||||
try {
|
||||
const activeProxyOptions = yield this.prepareProxyRequest(req);
|
||||
@@ -36,9 +37,25 @@ class HttpProxyMiddleware {
|
||||
else {
|
||||
next();
|
||||
}
|
||||
/**
|
||||
* Get the server object to subscribe to server events;
|
||||
* 'upgrade' for websocket and 'close' for graceful shutdown
|
||||
*
|
||||
* NOTE:
|
||||
* req.socket: node >= 13
|
||||
* req.connection: node < 13 (Remove this when node 12/13 support is dropped)
|
||||
*/
|
||||
const server = (_b = ((_a = req.socket) !== null && _a !== void 0 ? _a : req.connection)) === null || _b === void 0 ? void 0 : _b.server;
|
||||
if (server && !this.serverOnCloseSubscribed) {
|
||||
server.on('close', () => {
|
||||
this.logger.info('[HPM] server close signal received: closing proxy server');
|
||||
this.proxy.close();
|
||||
});
|
||||
this.serverOnCloseSubscribed = true;
|
||||
}
|
||||
if (this.proxyOptions.ws === true) {
|
||||
// use initial request to access the server object to subscribe to http upgrade event
|
||||
this.catchUpgradeRequest(req.connection.server);
|
||||
this.catchUpgradeRequest(server);
|
||||
}
|
||||
});
|
||||
this.catchUpgradeRequest = (server) => {
|
||||
@@ -82,7 +99,7 @@ class HttpProxyMiddleware {
|
||||
req.url = req.originalUrl || req.url;
|
||||
// store uri before it gets rewritten for logging
|
||||
const originalPath = req.url;
|
||||
const newProxyOptions = _.assign({}, this.proxyOptions);
|
||||
const newProxyOptions = Object.assign({}, this.proxyOptions);
|
||||
// Apply in order:
|
||||
// 1. option.router
|
||||
// 2. option.pathRewrite
|
||||
@@ -118,12 +135,14 @@ class HttpProxyMiddleware {
|
||||
}
|
||||
}
|
||||
});
|
||||
this.logError = (err, req, res) => {
|
||||
const hostname = (req.headers && req.headers.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
|
||||
const target = this.proxyOptions.target.host || this.proxyOptions.target;
|
||||
const errorMessage = '[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)';
|
||||
this.logError = (err, req, res, target) => {
|
||||
var _a;
|
||||
const hostname = ((_a = req.headers) === null || _a === void 0 ? void 0 : _a.host) || req.hostname || req.host; // (websocket) || (node0.10 || node 4/5)
|
||||
const requestHref = `${hostname}${req.url}`;
|
||||
const targetHref = `${target === null || target === void 0 ? void 0 : target.href}`; // target is undefined when websocket errors
|
||||
const errorMessage = '[HPM] Error occurred while proxying request %s to %s [%s] (%s)';
|
||||
const errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page
|
||||
this.logger.error(errorMessage, req.url, hostname, target, err.code || err, errReference);
|
||||
this.logger.error(errorMessage, requestHref, targetHref, err.code || err, errReference);
|
||||
};
|
||||
this.config = config_factory_1.createConfig(context, opts);
|
||||
this.proxyOptions = this.config.options;
|
||||
|
||||
+1
@@ -1,3 +1,4 @@
|
||||
import { Filter, Options } from './types';
|
||||
export declare function createProxyMiddleware(context: Filter | Options, options?: Options): import("./types").RequestHandler;
|
||||
export * from './handlers';
|
||||
export { Filter, Options, RequestHandler } from './types';
|
||||
|
||||
+11
@@ -1,4 +1,14 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createProxyMiddleware = void 0;
|
||||
const http_proxy_middleware_1 = require("./http-proxy-middleware");
|
||||
@@ -7,3 +17,4 @@ function createProxyMiddleware(context, options) {
|
||||
return middleware;
|
||||
}
|
||||
exports.createProxyMiddleware = createProxyMiddleware;
|
||||
__exportStar(require("./handlers"), exports);
|
||||
|
||||
+4
-5
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
/* eslint-disable prefer-rest-params */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getArrow = exports.getInstance = void 0;
|
||||
const _ = require("lodash");
|
||||
const util = require("util");
|
||||
let loggerInstance;
|
||||
const defaultProvider = {
|
||||
@@ -69,7 +69,7 @@ class Logger {
|
||||
}
|
||||
isValidProvider(fnProvider) {
|
||||
const result = true;
|
||||
if (fnProvider && !_.isFunction(fnProvider)) {
|
||||
if (fnProvider && typeof fnProvider !== 'function') {
|
||||
throw new Error('[HPM] Log provider config error. Expecting a function.');
|
||||
}
|
||||
return result;
|
||||
@@ -97,9 +97,8 @@ class Logger {
|
||||
}
|
||||
// make sure logged messages and its data are return interpolated
|
||||
// make it possible for additional log data, such date/time or custom prefix.
|
||||
_interpolate() {
|
||||
const fn = _.spread(util.format);
|
||||
const result = fn(_.slice(arguments));
|
||||
_interpolate(format, ...args) {
|
||||
const result = util.format(format, ...args);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,4 +4,4 @@
|
||||
* @param {Object} rewriteConfig
|
||||
* @return {Function} Function to rewrite paths; This function should accept `path` (request.url) as parameter
|
||||
*/
|
||||
export declare function createPathRewriter(rewriteConfig: any): (...args: any[]) => any;
|
||||
export declare function createPathRewriter(rewriteConfig: any): any;
|
||||
|
||||
+12
-14
@@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createPathRewriter = void 0;
|
||||
const _ = require("lodash");
|
||||
const isPlainObj = require("is-plain-obj");
|
||||
const errors_1 = require("./errors");
|
||||
const logger_1 = require("./logger");
|
||||
const logger = logger_1.getInstance();
|
||||
@@ -16,7 +16,7 @@ function createPathRewriter(rewriteConfig) {
|
||||
if (!isValidRewriteConfig(rewriteConfig)) {
|
||||
return;
|
||||
}
|
||||
if (_.isFunction(rewriteConfig)) {
|
||||
if (typeof rewriteConfig === 'function') {
|
||||
const customRewriteFn = rewriteConfig;
|
||||
return customRewriteFn;
|
||||
}
|
||||
@@ -26,27 +26,25 @@ function createPathRewriter(rewriteConfig) {
|
||||
}
|
||||
function rewritePath(path) {
|
||||
let result = path;
|
||||
_.forEach(rulesCache, (rule) => {
|
||||
for (const rule of rulesCache) {
|
||||
if (rule.regex.test(path)) {
|
||||
result = result.replace(rule.regex, rule.value);
|
||||
logger.debug('[HPM] Rewriting path from "%s" to "%s"', path, result);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
exports.createPathRewriter = createPathRewriter;
|
||||
function isValidRewriteConfig(rewriteConfig) {
|
||||
if (_.isFunction(rewriteConfig)) {
|
||||
if (typeof rewriteConfig === 'function') {
|
||||
return true;
|
||||
}
|
||||
else if (!_.isEmpty(rewriteConfig) && _.isPlainObject(rewriteConfig)) {
|
||||
return true;
|
||||
else if (isPlainObj(rewriteConfig)) {
|
||||
return Object.keys(rewriteConfig).length !== 0;
|
||||
}
|
||||
else if (_.isUndefined(rewriteConfig) ||
|
||||
_.isNull(rewriteConfig) ||
|
||||
_.isEqual(rewriteConfig, {})) {
|
||||
else if (rewriteConfig === undefined || rewriteConfig === null) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@@ -55,14 +53,14 @@ function isValidRewriteConfig(rewriteConfig) {
|
||||
}
|
||||
function parsePathRewriteRules(rewriteConfig) {
|
||||
const rules = [];
|
||||
if (_.isPlainObject(rewriteConfig)) {
|
||||
_.forIn(rewriteConfig, (value, key) => {
|
||||
if (isPlainObj(rewriteConfig)) {
|
||||
for (const [key] of Object.entries(rewriteConfig)) {
|
||||
rules.push({
|
||||
regex: new RegExp(key),
|
||||
value: rewriteConfig[key],
|
||||
});
|
||||
logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, rewriteConfig[key]);
|
||||
});
|
||||
}
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
+7
-7
@@ -10,17 +10,17 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getTarget = void 0;
|
||||
const _ = require("lodash");
|
||||
const isPlainObj = require("is-plain-obj");
|
||||
const logger_1 = require("./logger");
|
||||
const logger = logger_1.getInstance();
|
||||
function getTarget(req, config) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
let newTarget;
|
||||
const router = config.router;
|
||||
if (_.isPlainObject(router)) {
|
||||
if (isPlainObj(router)) {
|
||||
newTarget = getTargetFromProxyTable(req, router);
|
||||
}
|
||||
else if (_.isFunction(router)) {
|
||||
else if (typeof router === 'function') {
|
||||
newTarget = yield router(req);
|
||||
}
|
||||
return newTarget;
|
||||
@@ -32,13 +32,13 @@ function getTargetFromProxyTable(req, table) {
|
||||
const host = req.headers.host;
|
||||
const path = req.url;
|
||||
const hostAndPath = host + path;
|
||||
_.forIn(table, (value, key) => {
|
||||
for (const [key] of Object.entries(table)) {
|
||||
if (containsPath(key)) {
|
||||
if (hostAndPath.indexOf(key) > -1) {
|
||||
// match 'localhost:3000/api'
|
||||
result = table[key];
|
||||
logger.debug('[HPM] Router table match: "%s"', key);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -46,10 +46,10 @@ function getTargetFromProxyTable(req, table) {
|
||||
// match 'localhost:3000'
|
||||
result = table[key];
|
||||
logger.debug('[HPM] Router table match: "%s"', host);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function containsPath(v) {
|
||||
|
||||
+1
@@ -1,2 +1,3 @@
|
||||
"use strict";
|
||||
/* eslint-disable @typescript-eslint/no-empty-interface */
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
|
||||
+47
-39
@@ -1,32 +1,32 @@
|
||||
{
|
||||
"_args": [
|
||||
[
|
||||
"http-proxy-middleware@1.0.5",
|
||||
"/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series"
|
||||
"http-proxy-middleware@1.3.1",
|
||||
"/home/node/nuxt"
|
||||
]
|
||||
],
|
||||
"_from": "http-proxy-middleware@1.0.5",
|
||||
"_id": "http-proxy-middleware@1.0.5",
|
||||
"_from": "http-proxy-middleware@1.3.1",
|
||||
"_id": "http-proxy-middleware@1.3.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-CKzML7u4RdGob8wuKI//H8Ein6wNTEQR7yjVEzPbhBLGdOfkfvgTnp2HLnniKBDP9QW4eG10/724iTWLBeER3g==",
|
||||
"_integrity": "sha512-13eVVDYS4z79w7f1+NPllJtOQFx/FdUW4btIvVRMaRlUY9VGstAbo5MOhLEuUgZFRHn3x50ufn25zkj/boZnEg==",
|
||||
"_location": "/http-proxy-middleware",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "http-proxy-middleware@1.0.5",
|
||||
"raw": "http-proxy-middleware@1.3.1",
|
||||
"name": "http-proxy-middleware",
|
||||
"escapedName": "http-proxy-middleware",
|
||||
"rawSpec": "1.0.5",
|
||||
"rawSpec": "1.3.1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.0.5"
|
||||
"fetchSpec": "1.3.1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/@nuxtjs/proxy"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.0.5.tgz",
|
||||
"_spec": "1.0.5",
|
||||
"_where": "/mnt/Foxconn/Digitalent/Deverloper/liff-push_2series",
|
||||
"_resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-1.3.1.tgz",
|
||||
"_spec": "1.3.1",
|
||||
"_where": "/home/node/nuxt",
|
||||
"author": {
|
||||
"name": "Steven Chim"
|
||||
},
|
||||
@@ -39,38 +39,42 @@
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/http-proxy": "^1.17.4",
|
||||
"@types/http-proxy": "^1.17.5",
|
||||
"http-proxy": "^1.18.1",
|
||||
"is-glob": "^4.0.1",
|
||||
"lodash": "^4.17.19",
|
||||
"is-plain-obj": "^3.0.0",
|
||||
"micromatch": "^4.0.2"
|
||||
},
|
||||
"description": "The one-liner node.js proxy middleware for connect, express and browser-sync",
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^8.3.5",
|
||||
"@commitlint/config-conventional": "^8.3.4",
|
||||
"@types/express": "^4.17.3",
|
||||
"@commitlint/cli": "^12.0.1",
|
||||
"@commitlint/config-conventional": "^12.0.1",
|
||||
"@types/express": "4.17.7",
|
||||
"@types/is-glob": "^4.0.1",
|
||||
"@types/jest": "^25.2.3",
|
||||
"@types/lodash": "^4.14.151",
|
||||
"@types/jest": "^26.0.22",
|
||||
"@types/micromatch": "^4.0.1",
|
||||
"@types/node": "^14.0.3",
|
||||
"@types/supertest": "^2.0.9",
|
||||
"browser-sync": "^2.26.7",
|
||||
"@types/node": "^14.14.37",
|
||||
"@types/supertest": "^2.0.10",
|
||||
"@types/ws": "^7.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^4.19.0",
|
||||
"@typescript-eslint/parser": "^4.19.0",
|
||||
"body-parser": "^1.19.0",
|
||||
"browser-sync": "^2.26.14",
|
||||
"connect": "^3.7.0",
|
||||
"eslint": "^7.23.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"express": "^4.17.1",
|
||||
"husky": "^4.2.5",
|
||||
"jest": "^26.0.1",
|
||||
"lint-staged": "^10.2.4",
|
||||
"mockttp": "^0.20.1",
|
||||
"open": "^7.0.4",
|
||||
"prettier": "^2.0.5",
|
||||
"supertest": "^4.0.2",
|
||||
"ts-jest": "^26.0.0",
|
||||
"tslint": "^6.1.2",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"typescript": "^3.9.2",
|
||||
"ws": "^7.3.0"
|
||||
"husky": "^4.3.0",
|
||||
"jest": "^26.6.3",
|
||||
"lint-staged": "^10.5.4",
|
||||
"mockttp": "^1.2.0",
|
||||
"open": "^7.4.2",
|
||||
"prettier": "^2.2.1",
|
||||
"supertest": "^6.1.3",
|
||||
"ts-jest": "^26.5.4",
|
||||
"typescript": "^4.2.3",
|
||||
"ws": "^7.4.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
@@ -93,6 +97,7 @@
|
||||
"https",
|
||||
"connect",
|
||||
"express",
|
||||
"fastify",
|
||||
"polka",
|
||||
"browser-sync",
|
||||
"gulp",
|
||||
@@ -112,14 +117,17 @@
|
||||
"build": "tsc",
|
||||
"clean": "rm -rf dist && rm -rf coverage",
|
||||
"coverage": "jest --coverage --coverageReporters=lcov",
|
||||
"lint": "yarn lint:prettier && yarn lint:tslint",
|
||||
"lint:fix": "prettier --write \"**/*.{js,ts,md}\"",
|
||||
"lint:prettier": "prettier --check \"**/*.{js,ts,md}\"",
|
||||
"lint:tslint": "yarn tslint -c tslint.json '{lib,test}/**/*.ts'",
|
||||
"prepare": "yarn clean && yarn build && rm dist/tsconfig.tsbuildinfo",
|
||||
"eslint": "eslint '{src,test}/**/*.ts'",
|
||||
"eslint:fix": "yarn eslint --fix",
|
||||
"lint": "yarn prettier && yarn eslint",
|
||||
"lint:fix": "yarn prettier:fix && yarn eslint:fix",
|
||||
"prebuild": "yarn clean",
|
||||
"prepare": "yarn build && rm dist/tsconfig.tsbuildinfo",
|
||||
"pretest": "yarn build",
|
||||
"prettier": "prettier --list-different \"**/*.{js,ts,md,yml,json,html}\"",
|
||||
"prettier:fix": "prettier --write \"**/*.{js,ts,md,yml,json,html}\"",
|
||||
"test": "jest"
|
||||
},
|
||||
"types": "dist/index.d.ts",
|
||||
"version": "1.0.5"
|
||||
"version": "1.3.1"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user