reverse-proxy-middleware 0.0.52 → 0.0.55
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/helper.d.ts +8 -1
- package/index.js +1 -1
- package/package.json +3 -1
- package/readme.md +320 -25
- package/type.d.ts +6 -4
package/helper.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { BufferedHTTPServerRequest, EvaluationParameters, EvaluationScopeStateAPIs, Forwarders, HTTPServerResponse, ResolvedHeaderTransformation, ResolvedForwarder, ResolvedForwarders } from './type';
|
|
2
2
|
export declare const EVALUATION_SCOPE_NAMES: Array<string>;
|
|
3
|
+
export declare const logging: {
|
|
4
|
+
log: (...parameters: Array<unknown>) => Promise<void>;
|
|
5
|
+
debug: (...parameters: Array<unknown>) => Promise<void>;
|
|
6
|
+
info: (...parameters: Array<unknown>) => Promise<void>;
|
|
7
|
+
error: (...parameters: Array<unknown>) => Promise<void>;
|
|
8
|
+
warn: (...parameters: Array<unknown>) => Promise<void>;
|
|
9
|
+
};
|
|
3
10
|
export declare const applyStateAPIs: (request: BufferedHTTPServerRequest, response: HTTPServerResponse, forwarder: ResolvedForwarder) => Promise<{
|
|
4
11
|
result: boolean;
|
|
5
12
|
scope: EvaluationScopeStateAPIs;
|
|
@@ -8,5 +15,5 @@ export declare const determineForwarder: (request: BufferedHTTPServerRequest, re
|
|
|
8
15
|
export declare const resolveForwarders: (forwarders: Forwarders) => ResolvedForwarders;
|
|
9
16
|
export declare const addParsedContentToRequest: (bufferedRequest: BufferedHTTPServerRequest) => void;
|
|
10
17
|
export declare const transformHeaders: (content: string, headerTransformations: Array<ResolvedHeaderTransformation>, parameters: EvaluationParameters) => string;
|
|
11
|
-
export declare const reverseProxyBufferedRequest: (request: BufferedHTTPServerRequest, response: HTTPServerResponse, forwarder: ResolvedForwarder, stateAPIScope: EvaluationScopeStateAPIs) => void
|
|
18
|
+
export declare const reverseProxyBufferedRequest: (request: BufferedHTTPServerRequest, response: HTTPServerResponse, forwarder: ResolvedForwarder, stateAPIScope: EvaluationScopeStateAPIs) => Promise<void>;
|
|
12
19
|
export default reverseProxyBufferedRequest;
|
package/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// -*- coding: utf-8 -*-
|
|
3
|
-
'use strict';if(typeof module!=='undefined'&&module!==null&&eval('typeof require')!=='undefined'&&eval('require')!==null&&'main'in eval('require')&&eval('typeof require.main')!=='undefined'&&eval('require.main')!==null){var ORIGINAL_MAIN_MODULE=module;if(module!==eval('require.main')&&'paths'in module&&'paths'in eval('require.main')&&typeof __dirname!=='undefined'&&__dirname!==null)module.paths=eval('require.main.paths').concat(module.paths.filter(function(path){return eval('require.main.paths').includes(path)}))};if(module===eval('require.main'))module.paths.unshift('/application/node_modules');(()=>{"use strict";var __webpack_modules__=[(e,r,t)=>{t.d(r,{addParsedContentToRequest:()=>h,applyStateAPIs:()=>E,default:()=>P,determineForwarder:()=>O,resolveForwarders:()=>v});const n=require("@babel/runtime/helpers/extends");var o=t.n(n),_=t(1),a=t.n(_),s=t(2),i=t.n(s),u=t(3),c=t.n(u);const p=require("net"),l=require("tls");function f(e,r){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(t)return(t=t.call(e)).next.bind(t);if(Array.isArray(e)||(t=function(e,r){if(e){if("string"==typeof e)return d(e,r);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?d(e,r):void 0}}(e))||r&&e&&"number"==typeof e.length){t&&(e=t);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function d(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=Array(r);t<r;t++)n[t]=e[t];return n}var b=["data","error","request","response","stateAPIs","Tools"],E=function(){var e=a()(i().mark((function e(r,t,n){var o,_,a,s,u,p,l,d,b,E,O,v,h,m,P;return i().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:o={},_=f(n.stateAPIs);case 2:if((a=_()).done){e.next=66;break}s=a.value,o[s.name]={configuration:s,error:null,response:null},u=!1,p=1,l=f(s.expressions.pre);case 8:if((d=l()).done){e.next=29;break}b=d.value,E="break";try{E=b(s.data,null,r,t,o,c())}catch(e){console.warn("Failed running pre "+p+". expression of state api:",e)}if("number"!=typeof E){e.next=17;break}return console.info('Break request caused by state api "'+s.name+'" with status code '+E+"."),t.statusCode=E,t.end(),e.abrupt("return",{result:!1,scope:o});case 17:if("break"!==E){e.next=19;break}return e.abrupt("break",29);case 19:if("continue"!==E){e.next=21;break}return e.abrupt("continue",27);case 21:if(!E){e.next=25;break}u=!0,e.next=26;break;case 25:return e.abrupt("break",29);case 26:p+=1;case 27:e.next=8;break;case 29:if(!u){e.next=64;break}return console.info("Use state api:",c().represent(s)),O=null,e.prev=32,e.next=35,fetch(s.url,s.options);case 35:return o[s.name].response=e.sent,e.next=38,o[s.name].response.json();case 38:o[s.name].response.data=e.sent,e.next=45;break;case 41:e.prev=41,e.t0=e.catch(32),O=e.t0,console.warn('State api "'+s.name+'" throws error:',O);case 45:console.info("State api response is:",c().represent(o[s.name].response)),p=1,v=f(s.expressions.post);case 48:if((h=v()).done){e.next=64;break}m=h.value,P="break";try{P=m(s.data,O,r,t,o,c())}catch(e){console.warn("Failed running "+p+". post expression of state api:",e)}if("number"!=typeof P){e.next=57;break}return console.info('Break request caused by state api "'+s.name+'" with status code '+P+"."),t.statusCode=P,t.end(),e.abrupt("return",{result:!1,scope:o});case 57:if("break"!==P){e.next=59;break}return e.abrupt("break",64);case 59:if("continue"!==P){e.next=61;break}return e.abrupt("continue",62);case 61:p+=1;case 62:e.next=48;break;case 64:e.next=2;break;case 66:return e.abrupt("return",{result:!0,scope:o});case 67:case"end":return e.stop()}}),e,null,[[32,41]])})));return function(){return e.apply(this,arguments)}}(),O=function(e,r,t){for(var n,o=f(Object.entries(t).sort((function(e,r){var t=e[0],n=r[0];return t.localeCompare(n)})));!(n=o()).done;){var _,a=n.value,s=a[0],i=a[1];if(i.useExpression(i,null,e,r,((_={})[s]={configuration:i,error:null,response:null},_),c()))return console.info('Determined forwarder "'+s+'".'),i}return null},v=function(e){for(var r={},t=0,n=Object.entries(e);t<n.length;t++){var _=n[t],a=_[0],s=_[1];if("base"!==a){for(var i,u=c().extend(!0,{name:a},c().modifyObject(c().copy(e.base),s),s),p={retrieve:[],send:[]},l=f(["retrieve","send"]);!(i=l()).done;)for(var d,E=i.value,O=function(){var e=d.value,r=o()({},e,{sourceRun:function(){return""},targetRun:function(){return""}});if(Object.prototype.hasOwnProperty.call(r,"source"))if(r.source instanceof RegExp)r.sourceRun=function(){return r.source};else if("string"==typeof r.source){var t=c().stringCompile(r.source,b);if(t.error)throw new Error(t.error);r.sourceRun=t.templateFunction}else r.sourceRun=r.source;if(Object.prototype.hasOwnProperty.call(r,"target"))if("string"==typeof r.target){var n=c().stringCompile(r.target,b);if(n.error)throw new Error(n.error);r.targetRun=n.templateFunction}else r.targetRun=r.target;p[E].push(r)},v=f([].concat(u.headerTransformations[E]));!(d=v()).done;)O();u.headerTransformations=p;for(var h,m,P=[],y=[].concat(u.stateAPIs||[]),k=y.filter((function(e){return"base"===e.name}))[0],w=[],A=f(y);!(h=A()).done;)"base"!==(m=h.value).name&&w.push(c().extend(!0,{},c().modifyObject(c().copy(k),m),m));for(var I=0,M=w;I<M.length;I++){for(var D,g=M[I],T={pre:[],post:[]},R=f([].concat((null==(C=g.expressions)?void 0:C.pre)||[]));!(D=R()).done;){var C,x=D.value;if("string"==typeof x){var U=c().stringCompile(x,b);if(U.error)throw new Error(U.error);T.pre.push(U.templateFunction)}else T.pre.push(x)}for(var q,L=f([].concat((null==(B=g.expressions)?void 0:B.post)||[]));!(q=L()).done;){var B,S=q.value;if("string"==typeof S){var K=c().stringCompile(S,b);if(K.error)throw new Error(K.error);T.post.push(K.templateFunction)}else T.post.push(S)}g.expressions=T,P.push(g)}if(u.stateAPIs=P,"string"==typeof u.useExpression){var W=c().stringCompile(u.useExpression,b);if(W.error)throw new Error(W.error);u.useExpression=W.templateFunction}r[a]=u}}return r},h=function(e){if("application/json"===e.headers["content-type"])try{var r=Buffer.concat(e.socket.buffer.data).toString();e.socket.buffer.body=r.replace(/^[\s\S]+\s*\n\s*\n\s*([\s\S]+)$/m,"$1"),e.socket.buffer.content=JSON.parse(e.socket.buffer.body)}catch(r){console.warn("Error parsing given request.",e)}},m=function(e,r,t){for(var n,o=function(){var r=n.value;try{var o=r.sourceRun.apply(r,t),_=r.targetRun.apply(r,t);if([null,void 0].includes(o)||"string"==typeof o&&""===o.trim()){if([null,void 0].includes(_)||"string"==typeof _&&""===_.trim())return"continue";e=e.replace(/(\s*\n)\s*\n\s*/,(function(e,r){for(var t=arguments.length,n=Array(2<t?t-2:0),o=2;o<t;o++)n[o-2]=arguments[o];var a="string"==typeof _?_:_.apply(void 0,[e,r].concat(n));return""+r+a+e}))}else console.debug('Search for "'+o+'" and replace with',c().represent(_)+"."),e=e.replace(o,_)}catch(e){console.warn("Could not apply header transformation with source "+(r.source?c().represent(r.source):'"add"')+" and replacement "+(r.target?c().represent(r.target):'"remove"')+":",e)}},_=f(r);!(n=_()).done;)o();return e};const P=function(e,r,t,n){var _=r.socket,a=t.tls?l.connect:p.createConnection,s=t.tls&&443!==t.port||!t.tls&&80!==t.port?":"+t.port:"",i=a(o()({host:t.host,port:t.port},t.tls?{servername:t.host,rejectUnauthorized:!1}:{}),(function(){console.info("Proxy to: http"+(t.tls?"s":"")+"://"+t.host+s)}));i.on("error",(function(e){console.error("Proxy to server error",e)}));var u=[{clientSocket:_,serverSocket:i},null,e,r,n,c()];if(t.headerTransformations.retrieve.length){var d=!1;i.on("data",(function(e){if(d)_.write(e);else{var r=e.toString();console.info("Got response header from backend:\n"+r),r=m(r,t.headerTransformations.retrieve,u),console.info("Send response header to client:\n"+r),_.write(r),d=!0}}))}else i.pipe(_);i.on("connect",(function(){for(var r,n,o=!1,_=f(e.socket.buffer.data);!(r=_()).done;)if(n=r.value,o)i.write(n);else{var a=n.toString();console.info("Got request header from client:\n"+a),t.tls&&(a=a.replace(/HTTP\/1\.0/i,"HTTP/1.1")),a=a.replace(/(($|\n)host: )[^\n]+/i,"$1"+t.host+s),a=m(a,t.headerTransformations.send,u),console.info("Send request header to backend:\n"+a),i.write(a),o=!0}}))}},e=>{e.exports=require("@babel/runtime/helpers/asyncToGenerator")},e=>{e.exports=require("@babel/runtime/regenerator")},e=>{e.exports=require("clientnode")},(module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{default:()=>__WEBPACK_DEFAULT_EXPORT__});var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(1),_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__),_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(2),_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default=__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__),clientnode__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(3),clientnode__WEBPACK_IMPORTED_MODULE_2___default=__webpack_require__.n(clientnode__WEBPACK_IMPORTED_MODULE_2__),http__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(5),http__WEBPACK_IMPORTED_MODULE_3___default=__webpack_require__.n(http__WEBPACK_IMPORTED_MODULE_3__),http2__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(6),http2__WEBPACK_IMPORTED_MODULE_4___default=__webpack_require__.n(http2__WEBPACK_IMPORTED_MODULE_4__),path__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(7),path__WEBPACK_IMPORTED_MODULE_5___default=__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_5__),_helper__WEBPACK_IMPORTED_MODULE_6__=__webpack_require__(0),_package_json__WEBPACK_IMPORTED_MODULE_7__=__webpack_require__(8);function _createForOfIteratorHelperLoose(e,r){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(t)return(t=t.call(e)).next.bind(t);if(Array.isArray(e)||(t=_unsupportedIterableToArray(e))||r&&e&&"number"==typeof e.length){t&&(e=t);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(e,r){if(e){if("string"==typeof e)return _arrayLikeToArray(e,r);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?_arrayLikeToArray(e,r):void 0}}function _arrayLikeToArray(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=Array(r);t<r;t++)n[t]=e[t];return n}module=__webpack_require__.hmd(module);for(var _step,onIncomingMessage=function(e,r){var t=e;_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0___default()(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark((function e(){var n,o,_,a;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.next=3,clientnode__WEBPACK_IMPORTED_MODULE_2___default().timeout();case 3:if(!t.socket.buffer.finished){e.next=5;break}return e.abrupt("break",7);case 5:e.next=0;break;case 7:if(CONFIGURATION.parseBody&&(0,_helper__WEBPACK_IMPORTED_MODULE_6__.addParsedContentToRequest)(t),null!==(n=(0,_helper__WEBPACK_IMPORTED_MODULE_6__.determineForwarder)(t,r,FORWARDERS))){e.next=14;break}return console.error("No forwarder found for given request:",t),r.statusCode=502,r.end(),e.abrupt("return");case 14:return e.next=16,(0,_helper__WEBPACK_IMPORTED_MODULE_6__.applyStateAPIs)(t,r,n);case 16:o=e.sent,_=o.result,a=o.scope,_&&(0,_helper__WEBPACK_IMPORTED_MODULE_6__.default)(t,r,n,a);case 20:case"end":return e.stop()}}),e)})))()},onIncomingStream=function(e,r){console.info("Got stream",e,r)},BASE_CONFIGURATION=_package_json__WEBPACK_IMPORTED_MODULE_7__.DN,_iterator=_createForOfIteratorHelperLoose(["configuration.json","secure-configuration.json"]);!(_step=_iterator()).done;){var path=_step.value,configurationPath=(0,path__WEBPACK_IMPORTED_MODULE_5__.resolve)(process.cwd(),path);if(clientnode__WEBPACK_IMPORTED_MODULE_2___default().isFileSync(configurationPath)){var configuration=eval("require('"+configurationPath+"')");clientnode__WEBPACK_IMPORTED_MODULE_2___default().extend(!0,clientnode__WEBPACK_IMPORTED_MODULE_2___default().modifyObject(BASE_CONFIGURATION,configuration),configuration)}}var CONFIGURATION=clientnode__WEBPACK_IMPORTED_MODULE_2___default().evaluateDynamicData(BASE_CONFIGURATION,{configuration:BASE_CONFIGURATION,environment:process.env,Tools:clientnode__WEBPACK_IMPORTED_MODULE_2___default()}),FORWARDERS=(0,_helper__WEBPACK_IMPORTED_MODULE_6__.resolveForwarders)(CONFIGURATION.forwarders),server={instance:CONFIGURATION.publicKeyPath&&CONFIGURATION.privateKeyPath?(0,http2__WEBPACK_IMPORTED_MODULE_4__.createSecureServer)(CONFIGURATION.nodeServerOptions,onIncomingMessage):(0,http__WEBPACK_IMPORTED_MODULE_3__.createServer)(onIncomingMessage),streams:[],sockets:[],start:function(){server.instance.listen(CONFIGURATION.port,(function(){return console.info("Listen on port "+CONFIGURATION.port+" for incoming requests.")}))},stop:function(){server.instance.close((function(){console.info("Shut server down.")}));for(var e,r=0,t=[server.sockets,server.streams];r<t.length;r++)if(e=t[r],Array.isArray(e))for(var n,o=_createForOfIteratorHelperLoose(e);!(n=o()).done;)n.value.destroy()}};if(server.instance.on("connection",(function(e){server.sockets.push(e),e.buffer={data:[],finished:!1},e.on("data",(function(r){e.buffer.data.push(r)}));for(var r,t=0,n=["done","finish","writableEnded","writableFinished"];t<n.length;t++)r=n[t],e.on(r,(function(){e.buffer.finished=!0}));clientnode__WEBPACK_IMPORTED_MODULE_2___default().timeout((function(){e.buffer.finished=!0})),e.on("close",(function(){e.buffer.finished=!0,server.sockets.splice(server.sockets.indexOf(e),1)}))})),server.instance.on("stream",(function(e,r){server.streams.push(e),onIncomingStream(e,r),e.on("close",(function(){server.streams.splice(server.streams.indexOf(e),1)}))})),__webpack_require__.c[__webpack_require__.s]===module||eval("require.main")!==__webpack_require__.c[__webpack_require__.s]&&"undefined"!=typeof ORIGINAL_MAIN_MODULE&&ORIGINAL_MAIN_MODULE===eval("require.main")){console.info("Start server with configuration:",clientnode__WEBPACK_IMPORTED_MODULE_2___default().represent(CONFIGURATION)),console.debug("Apply resolved forwarder:",clientnode__WEBPACK_IMPORTED_MODULE_2___default().represent(FORWARDERS)),server.start();for(var _step3,_loop=function(){var e=_step3.value;process.on(e,(function(){console.info('\nGot "'+e+'" signal: stopping server.'),server.stop()}))},_iterator3=_createForOfIteratorHelperLoose(clientnode__WEBPACK_IMPORTED_MODULE_2__.CloseEventNames);!(_step3=_iterator3()).done;)_loop()}const __WEBPACK_DEFAULT_EXPORT__=server},e=>{e.exports=require("http")},e=>{e.exports=require("http2")},e=>{e.exports=require("path")},e=>{e.exports=JSON.parse('{"DN":{"publicKeyPath":"","privateKeyPath":"","nodeServerOptions":{"allowHTTP1":true},"host":"localhost","port":8080,"parseBody":false,"forwarders":{"base":{"headerTransformations":{"retrieve":[],"send":[]},"host":"localhost","port":443,"stateAPIs":[{"data":{},"expressions":{"pre":"false","post":"true"},"name":"base","options":{},"url":""}],"tls":true,"useExpression":"true"}}}}')}],__webpack_module_cache__={};function __webpack_require__(e){var r=__webpack_module_cache__[e];if(void 0!==r)return r.exports;var t=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e](t,t.exports,__webpack_require__),t.loaded=!0,t.exports}__webpack_require__.c=__webpack_module_cache__,__webpack_require__.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(r,{a:r}),r},__webpack_require__.d=(e,r)=>{for(var t in r)__webpack_require__.o(r,t)&&!__webpack_require__.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},__webpack_require__.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:()=>{throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),__webpack_require__.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__=__webpack_require__(__webpack_require__.s=4);module.exports=__webpack_exports__})();
|
|
3
|
+
'use strict';if(typeof module!=='undefined'&&module!==null&&eval('typeof require')!=='undefined'&&eval('require')!==null&&'main'in eval('require')&&eval('typeof require.main')!=='undefined'&&eval('require.main')!==null){var ORIGINAL_MAIN_MODULE=module;if(module!==eval('require.main')&&'paths'in module&&'paths'in eval('require.main')&&typeof __dirname!=='undefined'&&__dirname!==null)module.paths=eval('require.main.paths').concat(module.paths.filter(function(path){return eval('require.main.paths').includes(path)}))};if(module===eval('require.main'))module.paths.unshift('/application/node_modules');(()=>{"use strict";var __webpack_modules__=[(e,r,t)=>{t.d(r,{addParsedContentToRequest:()=>P,applyStateAPIs:()=>h,default:()=>M,determineForwarder:()=>v,logging:()=>O,resolveForwarders:()=>g});const n=require("@babel/runtime/helpers/extends");var _=t.n(n),o=t(1),a=t.n(o),s=t(2),i=t.n(s),u=t(3),c=t.n(u);const p=require("net"),l=require("tls");function f(e,r){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(t)return(t=t.call(e)).next.bind(t);if(Array.isArray(e)||(t=function(e,r){if(e){if("string"==typeof e)return d(e,r);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?d(e,r):void 0}}(e))||r&&e&&"number"==typeof e.length){t&&(e=t);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function d(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=Array(r);t<r;t++)n[t]=e[t];return n}var E=["data","error","request","response","stateAPIs","Tools"],b=function(){var e=a()(i().mark((function e(){var r,t,n,_=arguments;return i().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:for(r=_.length,t=Array(r),n=0;n<r;n++)t[n]=_[n];return e.abrupt("return",new Promise((function(e,r){process.stdout.write(t.join(" ")+"\n",(function(t){return t?r(t):e()}))})));case 2:case"end":return e.stop()}}),e)})));return function(){return e.apply(this,arguments)}}(),O={log:b,debug:b,info:b,error:b,warn:b},h=function(){var e=a()(i().mark((function e(r,t,n){var _,o,a,s,u,p,l,d,E,b,h,v,g,P,m;return i().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:_={},o=f(n.stateAPIs);case 2:if((a=o()).done){e.next=72;break}s=a.value,_[s.name]={configuration:s,error:null,response:null},u=!1,p=1,l=f(s.expressions.pre);case 8:if((d=l()).done){e.next=28;break}E=d.value,b=void 0;try{b=E(s.data,null,r,t,_,c())}catch(e){O.warn("Failed running pre "+p+". expression of state api:",e)}if("number"!=typeof b){e.next=17;break}return O.info('Break request caused by state api "'+s.name+'" with status code '+b+"."),t.statusCode=b,t.end(),e.abrupt("return",{result:!1,scope:_});case 17:if("break"!==b){e.next=19;break}return e.abrupt("break",28);case 19:if(p+=1,"boolean"!=typeof b){e.next=26;break}if(!b){e.next=25;break}u=!0,e.next=26;break;case 25:return e.abrupt("break",28);case 26:e.next=8;break;case 28:if(!u){e.next=70;break}return O.info('Use state api: "'+s.name+'"'),O.debug("\nState api configuration is: "+c().represent(s)),h=null,e.prev=32,e.next=35,fetch(s.url,s.options);case 35:_[s.name].response=e.sent,e.next=42;break;case 38:e.prev=38,e.t0=e.catch(32),h=e.t0,O.warn('Running state api request for "'+s.name+'" throws',"error:",h);case 42:if(!(_[s.name].response&&_[s.name].response.headers.has("content-type")&&/application\/json(;.*)?$/.test(_[s.name].response.headers.get("content-type")))){e.next=53;break}return e.prev=43,e.next=46,_[s.name].response.json();case 46:_[s.name].response.data=e.sent,e.next=53;break;case 49:e.prev=49,e.t1=e.catch(43),h=e.t1,O.warn("Parsing state api json response for",'"'+s.name+'" throws error:',h);case 53:O.debug("\nState api response is:",c().represent(_[s.name].response)),p=1,v=f(s.expressions.post);case 56:if((g=v()).done){e.next=70;break}P=g.value,m=void 0;try{m=P(s.data,h,r,t,_,c())}catch(e){O.warn("Failed running "+p+". post expression of state api:",e)}if("number"!=typeof m){e.next=65;break}return O.info('Break request caused by state api "'+s.name+'" with status code '+m+"."),t.statusCode=m,t.end(),e.abrupt("return",{result:!1,scope:_});case 65:if("break"!==m){e.next=67;break}return e.abrupt("break",70);case 67:p+=1;case 68:e.next=56;break;case 70:e.next=2;break;case 72:return e.abrupt("return",{result:!0,scope:_});case 73:case"end":return e.stop()}}),e,null,[[32,38],[43,49]])})));return function(){return e.apply(this,arguments)}}(),v=function(e,r,t){for(var n,_=f(Object.entries(t).sort((function(e,r){var t=e[0],n=r[0];return t.localeCompare(n)})));!(n=_()).done;){var o,a=n.value,s=a[0],i=a[1];if(i.useExpression(i,null,e,r,((o={})[s]={configuration:i,error:null,response:null},o),c()))return O.info('Determined forwarder is: "'+s+'".'),i}return null},g=function(e){for(var r={},t=0,n=Object.entries(e);t<n.length;t++){var o=n[t],a=o[0],s=o[1];if("base"!==a){for(var i,u=c().extend(!0,{name:a},c().modifyObject(c().copy(e.base),s),s),p={retrieve:[],send:[]},l=f(["retrieve","send"]);!(i=l()).done;)for(var d,b=i.value,O=function(){var e=d.value,r=_()({},e,{sourceRun:function(){return""},targetRun:function(){return""}});if(Object.prototype.hasOwnProperty.call(r,"source"))if(r.source instanceof RegExp)r.sourceRun=function(){return r.source};else if("string"==typeof r.source){var t=c().stringCompile(r.source,E);if(t.error)throw new Error(t.error);r.sourceRun=t.templateFunction}else r.sourceRun=r.source;if(Object.prototype.hasOwnProperty.call(r,"target"))if("string"==typeof r.target){var n=c().stringCompile(r.target,E);if(n.error)throw new Error(n.error);r.targetRun=n.templateFunction}else r.targetRun=r.target;p[b].push(r)},h=f([].concat(u.headerTransformations[b]));!(d=h()).done;)O();u.headerTransformations=p;for(var v,g,P=[],m=[].concat(u.stateAPIs||[]),M=m.filter((function(e){return"base"===e.name}))[0],D=[],y=f(m);!(v=y()).done;)"base"!==(g=v.value).name&&D.push(c().extend(!0,{},c().modifyObject(c().copy(M),g),g));for(var A=0,w=D;A<w.length;A++){for(var I,k=w[A],T={pre:[],post:[]},R=f([].concat((null==(C=k.expressions)?void 0:C.pre)||[]));!(I=R()).done;){var C,x=I.value;if("string"==typeof x){var U=c().stringCompile(x,E);if(U.error)throw new Error(U.error);T.pre.push(U.templateFunction)}else T.pre.push(x)}for(var L,B=f([].concat((null==(q=k.expressions)?void 0:q.post)||[]));!(L=B()).done;){var q,K=L.value;if("string"==typeof K){var W=c().stringCompile(K,E);if(W.error)throw new Error(W.error);T.post.push(W.templateFunction)}else T.post.push(K)}k.expressions=T,P.push(k)}if(u.stateAPIs=P,"string"==typeof u.useExpression){var S=c().stringCompile(u.useExpression,E);if(S.error)throw new Error(S.error);u.useExpression=S.templateFunction}r[a]=u}}return r},P=function(e){if(e.headers["content-type"]&&/application\/json(;.*)?$/.test(e.headers["content-type"]))try{var r=Buffer.concat(e.socket.buffer.data).toString();e.socket.buffer.body=r.replace(/^[\s\S]+\s*\n\s*\n\s*([\s\S]+)$/m,"$1"),e.socket.buffer.content=JSON.parse(e.socket.buffer.body)}catch(r){O.warn("Error parsing given request.",e)}},m=function(e,r,t){for(var n,_=!1,o=function(){var r=n.value;try{var o=r.sourceRun.apply(r,t),a=r.targetRun.apply(r,t);if([null,void 0].includes(o)||"string"==typeof o&&""===o.trim()){if([null,void 0].includes(a)||"string"==typeof a&&""===a.trim())return"continue";e=e.replace(/(\s*\n)\s*\n\s*/,(function(e,r){for(var t=arguments.length,n=Array(2<t?t-2:0),_=2;_<t;_++)n[_-2]=arguments[_];var o="string"==typeof a?a:a.apply(void 0,[e,r].concat(n));return""+r+o+e}))}else _||(O.debug(),_=!0),O.debug('Search for "'+o+'" and replace with',c().represent(a)+"."),e=e.replace(o,a)}catch(e){O.warn("\nCould not apply header transformation with source "+(r.source?c().represent(r.source):'"add"')+" and replacement "+(r.target?c().represent(r.target):'"remove"')+":",e)}},a=f(r);!(n=a()).done;)o();return e};const M=function(){var e=a()(i().mark((function e(r,t,n,o){return i().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:return e.abrupt("return",new Promise((function(e,a){var s=t.socket,i=n.tls?l.connect:p.createConnection,u=n.tls&&443!==n.port||!n.tls&&80!==n.port?":"+n.port:"",d=i(_()({host:n.host,port:n.port},n.tls?{servername:n.host,rejectUnauthorized:!1}:{}),(function(){O.info("\nConnection to: http"+(n.tls?"s":"")+"://"+n.host+u+" established.")}));d.on("error",(function(e){O.error("Proxy to server error",e),a(e)}));var E=[{clientSocket:s,serverSocket:d},null,r,t,o,c()];if(n.headerTransformations.retrieve.length){var b=!1;d.on("data",(function(r){if(b)s.write(r);else{var t=r.toString();O.info("\n <=== Got response header from backend:\n\n"+t),t=m(t,n.headerTransformations.retrieve,E),O.info("\n <<<< Send response header to client:\n\n"+t),s.write(t),b=!0,e()}}))}else d.pipe(s);d.on("connect",(function(){for(var e,t,_=!1,o=f(r.socket.buffer.data);!(e=o()).done;)if(t=e.value,_)d.write(t);else{var a=t.toString();O.debug("\n ===> Got request header from client:\n\n"+a),n.tls&&(a=a.replace(/HTTP\/1\.0/i,"HTTP/1.1")),a=a.replace(/(($|\n)host: )[^\n]+/i,"$1"+n.host+u),a=m(a,n.headerTransformations.send,E),O.debug("\n >>>> Send request header to backend:\n\n"+a),d.write(a),_=!0}}))})));case 1:case"end":return e.stop()}}),e)})));return function(r,t,n,_){return e.apply(this,arguments)}}()},e=>{e.exports=require("@babel/runtime/helpers/asyncToGenerator")},e=>{e.exports=require("@babel/runtime/regenerator")},e=>{e.exports=require("clientnode")},(module,__webpack_exports__,__webpack_require__)=>{__webpack_require__.r(__webpack_exports__),__webpack_require__.d(__webpack_exports__,{default:()=>__WEBPACK_DEFAULT_EXPORT__});var _babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__=__webpack_require__(1),_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0___default=__webpack_require__.n(_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0__),_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__=__webpack_require__(2),_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default=__webpack_require__.n(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1__),clientnode__WEBPACK_IMPORTED_MODULE_2__=__webpack_require__(3),clientnode__WEBPACK_IMPORTED_MODULE_2___default=__webpack_require__.n(clientnode__WEBPACK_IMPORTED_MODULE_2__),http__WEBPACK_IMPORTED_MODULE_3__=__webpack_require__(5),http__WEBPACK_IMPORTED_MODULE_3___default=__webpack_require__.n(http__WEBPACK_IMPORTED_MODULE_3__),http2__WEBPACK_IMPORTED_MODULE_4__=__webpack_require__(6),http2__WEBPACK_IMPORTED_MODULE_4___default=__webpack_require__.n(http2__WEBPACK_IMPORTED_MODULE_4__),path__WEBPACK_IMPORTED_MODULE_5__=__webpack_require__(7),path__WEBPACK_IMPORTED_MODULE_5___default=__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_5__),_helper__WEBPACK_IMPORTED_MODULE_6__=__webpack_require__(0),_package_json__WEBPACK_IMPORTED_MODULE_7__=__webpack_require__(8);function _createForOfIteratorHelperLoose(e,r){var t="undefined"!=typeof Symbol&&e[Symbol.iterator]||e["@@iterator"];if(t)return(t=t.call(e)).next.bind(t);if(Array.isArray(e)||(t=_unsupportedIterableToArray(e))||r&&e&&"number"==typeof e.length){t&&(e=t);var n=0;return function(){return n>=e.length?{done:!0}:{done:!1,value:e[n++]}}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}function _unsupportedIterableToArray(e,r){if(e){if("string"==typeof e)return _arrayLikeToArray(e,r);var t=Object.prototype.toString.call(e).slice(8,-1);return"Object"===t&&e.constructor&&(t=e.constructor.name),"Map"===t||"Set"===t?Array.from(e):"Arguments"===t||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t)?_arrayLikeToArray(e,r):void 0}}function _arrayLikeToArray(e,r){(null==r||r>e.length)&&(r=e.length);for(var t=0,n=Array(r);t<r;t++)n[t]=e[t];return n}module=__webpack_require__.hmd(module);for(var _step,onIncomingMessage=function(e,r){var t=e;_babel_runtime_helpers_asyncToGenerator__WEBPACK_IMPORTED_MODULE_0___default()(_babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().mark((function e(){var n,_,o,a;return _babel_runtime_regenerator__WEBPACK_IMPORTED_MODULE_1___default().wrap((function(e){for(;;)switch(e.prev=e.next){case 0:_helper__WEBPACK_IMPORTED_MODULE_6__.logging.info("|"+"-".repeat(78)+"|\nStart processing",t.method+" request: "+t.url+"\n");case 1:return e.next=4,clientnode__WEBPACK_IMPORTED_MODULE_2___default().timeout();case 4:if(!t.socket.buffer.finished){e.next=6;break}return e.abrupt("break",8);case 6:e.next=1;break;case 8:if(CONFIGURATION.parseBody&&(0,_helper__WEBPACK_IMPORTED_MODULE_6__.addParsedContentToRequest)(t),null!==(n=(0,_helper__WEBPACK_IMPORTED_MODULE_6__.determineForwarder)(t,r,FORWARDERS))){e.next=15;break}return _helper__WEBPACK_IMPORTED_MODULE_6__.logging.error("No forwarder found for given request:",t),r.statusCode=502,r.end(),e.abrupt("return");case 15:return e.prev=15,e.next=18,(0,_helper__WEBPACK_IMPORTED_MODULE_6__.applyStateAPIs)(t,r,n);case 18:if(_=e.sent,o=_.result,a=_.scope,!o){e.next=24;break}return e.next=24,(0,_helper__WEBPACK_IMPORTED_MODULE_6__.default)(t,r,n,a);case 24:e.next=29;break;case 26:e.prev=26,e.t0=e.catch(15),_helper__WEBPACK_IMPORTED_MODULE_6__.logging.error(e.t0);case 29:_helper__WEBPACK_IMPORTED_MODULE_6__.logging.info("\nEnd processing",t.method+" request: "+t.url+"\n|"+"-".repeat(78)+"|");case 30:case"end":return e.stop()}}),e,null,[[15,26]])})))()},onIncomingStream=function(e,r){_helper__WEBPACK_IMPORTED_MODULE_6__.logging.info("Got stream",e,r)},BASE_CONFIGURATION=_package_json__WEBPACK_IMPORTED_MODULE_7__.DN,_iterator=_createForOfIteratorHelperLoose(["configuration.json","secure-configuration.json"]);!(_step=_iterator()).done;){var path=_step.value,configurationPath=(0,path__WEBPACK_IMPORTED_MODULE_5__.resolve)(process.cwd(),path);if(clientnode__WEBPACK_IMPORTED_MODULE_2___default().isFileSync(configurationPath)){var configuration=eval("require('"+configurationPath+"')");clientnode__WEBPACK_IMPORTED_MODULE_2___default().extend(!0,clientnode__WEBPACK_IMPORTED_MODULE_2___default().modifyObject(BASE_CONFIGURATION,configuration),configuration)}}var CONFIGURATION=clientnode__WEBPACK_IMPORTED_MODULE_2___default().evaluateDynamicData(BASE_CONFIGURATION,{configuration:BASE_CONFIGURATION,environment:process.env,Tools:clientnode__WEBPACK_IMPORTED_MODULE_2___default()}),FORWARDERS=(0,_helper__WEBPACK_IMPORTED_MODULE_6__.resolveForwarders)(CONFIGURATION.forwarders),server={instance:CONFIGURATION.publicKeyPath&&CONFIGURATION.privateKeyPath?(0,http2__WEBPACK_IMPORTED_MODULE_4__.createSecureServer)(CONFIGURATION.nodeServerOptions,onIncomingMessage):(0,http__WEBPACK_IMPORTED_MODULE_3__.createServer)(onIncomingMessage),streams:[],sockets:[],start:function(){server.instance.listen(CONFIGURATION.port,(function(){_helper__WEBPACK_IMPORTED_MODULE_6__.logging.info("Listen on port "+CONFIGURATION.port+" for incoming requests.")}))},stop:function(){server.instance.close((function(){_helper__WEBPACK_IMPORTED_MODULE_6__.logging.info("Shut server down.")}));for(var e,r=0,t=[server.sockets,server.streams];r<t.length;r++)if(e=t[r],Array.isArray(e))for(var n,_=_createForOfIteratorHelperLoose(e);!(n=_()).done;)n.value.destroy()}};if(server.instance.on("connection",(function(e){server.sockets.push(e),e.buffer={data:[],finished:!1},e.on("data",(function(r){e.buffer.data.push(r)}));for(var r,t=0,n=["done","finish","writableEnded","writableFinished"];t<n.length;t++)r=n[t],e.on(r,(function(){e.buffer.finished=!0}));clientnode__WEBPACK_IMPORTED_MODULE_2___default().timeout((function(){e.buffer.finished=!0})),e.on("close",(function(){e.buffer.finished=!0,server.sockets.splice(server.sockets.indexOf(e),1)}))})),server.instance.on("stream",(function(e,r){server.streams.push(e),onIncomingStream(e,r),e.on("close",(function(){server.streams.splice(server.streams.indexOf(e),1)}))})),__webpack_require__.c[__webpack_require__.s]===module||eval("require.main")!==__webpack_require__.c[__webpack_require__.s]&&"undefined"!=typeof ORIGINAL_MAIN_MODULE&&ORIGINAL_MAIN_MODULE===eval("require.main")){_helper__WEBPACK_IMPORTED_MODULE_6__.logging.info("Start server with configuration:",clientnode__WEBPACK_IMPORTED_MODULE_2___default().represent(CONFIGURATION)),_helper__WEBPACK_IMPORTED_MODULE_6__.logging.debug("Apply resolved forwarder:",clientnode__WEBPACK_IMPORTED_MODULE_2___default().represent(FORWARDERS)),server.start();for(var _step3,_loop=function(){var e=_step3.value;process.on(e,(function(){_helper__WEBPACK_IMPORTED_MODULE_6__.logging.info('\nGot "'+e+'" signal: stopping server.'),server.stop()}))},_iterator3=_createForOfIteratorHelperLoose(clientnode__WEBPACK_IMPORTED_MODULE_2__.CloseEventNames);!(_step3=_iterator3()).done;)_loop()}const __WEBPACK_DEFAULT_EXPORT__=server},e=>{e.exports=require("http")},e=>{e.exports=require("http2")},e=>{e.exports=require("path")},e=>{e.exports=JSON.parse('{"DN":{"publicKeyPath":"","privateKeyPath":"","nodeServerOptions":{"allowHTTP1":true},"host":"localhost","port":8080,"parseBody":false,"forwarders":{"base":{"headerTransformations":{"retrieve":[],"send":[]},"host":"localhost","port":443,"stateAPIs":[{"data":{},"expressions":{"pre":"false","post":"true"},"name":"base","options":{},"url":""}],"tls":true,"useExpression":"true"}}}}')}],__webpack_module_cache__={};function __webpack_require__(e){var r=__webpack_module_cache__[e];if(void 0!==r)return r.exports;var t=__webpack_module_cache__[e]={id:e,loaded:!1,exports:{}};return __webpack_modules__[e](t,t.exports,__webpack_require__),t.loaded=!0,t.exports}__webpack_require__.c=__webpack_module_cache__,__webpack_require__.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return __webpack_require__.d(r,{a:r}),r},__webpack_require__.d=(e,r)=>{for(var t in r)__webpack_require__.o(r,t)&&!__webpack_require__.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},__webpack_require__.hmd=e=>((e=Object.create(e)).children||(e.children=[]),Object.defineProperty(e,"exports",{enumerable:!0,set:()=>{throw new Error("ES Modules may not assign module.exports or exports.*, Use ESM export syntax, instead: "+e.id)}}),e),__webpack_require__.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),__webpack_require__.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})};var __webpack_exports__=__webpack_require__(__webpack_require__.s=4);module.exports=__webpack_exports__})();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reverse-proxy-middleware",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.55",
|
|
4
4
|
"description": "A reverse proxy blocking bot requests.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"api",
|
|
@@ -47,9 +47,11 @@
|
|
|
47
47
|
"postversion": "yarn publish --non-interactive; git push --tags && git push",
|
|
48
48
|
"prepare": "yarn build",
|
|
49
49
|
"serve": "yarn build:plain && yarn start",
|
|
50
|
+
"serve:custom": "ln --force --symbolic examples/custom.json configuration.json && yarn serve",
|
|
50
51
|
"serve:headers": "ln --force --symbolic examples/headers.json configuration.json && yarn serve",
|
|
51
52
|
"serve:random": "ln --force --symbolic examples/random.json configuration.json && yarn serve",
|
|
52
53
|
"serve:simple": "ln --force --symbolic examples/simple.json configuration.json && yarn serve",
|
|
54
|
+
"serve:state-apis": "ln --force --symbolic examples/state-apis.json configuration.json && yarn serve",
|
|
53
55
|
"start": "./index.js",
|
|
54
56
|
"test": "weboptimizer test",
|
|
55
57
|
"test:coverage": "weboptimizer test:coverage",
|
package/readme.md
CHANGED
|
@@ -73,6 +73,7 @@ Here is how to distribute incoming requests randomly between google and bing:
|
|
|
73
73
|
"host": "www.bing.com",
|
|
74
74
|
"useExpression": "Math.random() < 0.5"
|
|
75
75
|
},
|
|
76
|
+
|
|
76
77
|
"google": {
|
|
77
78
|
"host": "www.google.com"
|
|
78
79
|
}
|
|
@@ -87,55 +88,349 @@ outcome of bing's expression if it is beeing used or not.
|
|
|
87
88
|
Since this proxy just streams the whole request through it could be used as
|
|
88
89
|
basic load balancer with this configuration.
|
|
89
90
|
|
|
90
|
-
|
|
91
|
+
### Rewriting headers for underlying backend
|
|
91
92
|
|
|
92
93
|
Headers can be replaced in both directions. Client-Request to forward or
|
|
93
94
|
retrieved responses given from configured backend:
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
```
|
|
97
|
+
{
|
|
98
|
+
"forwarders": {
|
|
99
|
+
"bing": {
|
|
100
|
+
"host": "www.bing.com",
|
|
101
|
+
|
|
102
|
+
"headerTransformations": {
|
|
103
|
+
"send": {
|
|
104
|
+
"source": "/X-Special-Client-Header-Name: (.+)/gi",
|
|
105
|
+
"target": "'New-Header-Name: $1'"
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
"retrieve": {
|
|
109
|
+
"source": "/set-cookie: (.+)/gi",
|
|
110
|
+
"target": "'X-Prevented-Cookie: $1'"
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Test via:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
curl \
|
|
122
|
+
--header 'X-Special-Client-Header-Name: value' \
|
|
123
|
+
--verbose \
|
|
124
|
+
http://localhost:8080 \
|
|
125
|
+
1>/dev/null
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
You should see a lot of cookie header ("set-cookie: ..." replaced by
|
|
129
|
+
"X-Prevented-Cookie: ...".
|
|
130
|
+
Note that muting the standart output ("1>/dev/null") enables you to focus on
|
|
131
|
+
retrieved headers printed via secondary error output.
|
|
132
|
+
|
|
133
|
+
### State-APIs
|
|
134
|
+
|
|
135
|
+
State-APIs enables you to conditionally trigger requests to third party
|
|
136
|
+
endpoints and use responses for further decisions how to proceed.
|
|
137
|
+
When a state api is used for a specified request that response can be used via
|
|
138
|
+
expressions to transform subsequent api requests, decide which backend to use
|
|
139
|
+
or transform the final backend request.
|
|
140
|
+
|
|
141
|
+
In the following example you can see how a state api is configured to catch
|
|
142
|
+
specific request to deal with. The last running pre expressions which results
|
|
143
|
+
in a boolean value will trigger if the referenced state api should be used for
|
|
144
|
+
given request.
|
|
145
|
+
|
|
146
|
+
Here is an example:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
{
|
|
150
|
+
"forwarders": {
|
|
151
|
+
"bing": {
|
|
152
|
+
"host": "www.bing.com",
|
|
153
|
+
|
|
154
|
+
"stateAPIs": {
|
|
155
|
+
"name": "sense",
|
|
156
|
+
"url": "https://www.google.com/search?q=${data.question}",
|
|
157
|
+
|
|
158
|
+
"data": {
|
|
159
|
+
"question": "What is the meaning of life?"
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
"expressions": {
|
|
163
|
+
"pre": "request.headers['ask-for-sense-of-live'] ? true : null",
|
|
164
|
+
"post": "response.statusCode >= 200 && response.statusCode < 300 ? null : response.statusCode"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
What is going on here?
|
|
173
|
+
We generally forward requests to "www.bing.com" but if there is a header called
|
|
174
|
+
"ask-for-sense-of-live" present in client request we will first ask google for
|
|
175
|
+
the meaning of life. If google answers with a "negative" response code not
|
|
176
|
+
between 200 and 300 we will just do the final forwarding to bing. If google
|
|
177
|
+
responses in a positive manner the resulting status code we will be used for
|
|
178
|
+
answering client and no forwarding to "www.bing.com" happens.
|
|
179
|
+
|
|
180
|
+
Here es a test curl command:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
curl \
|
|
184
|
+
--header 'ask-for-sense-of-live: value' \
|
|
185
|
+
--verbose \
|
|
186
|
+
http://localhost:8080 \
|
|
187
|
+
1>/dev/null
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
This should result in a simple response but:
|
|
191
|
+
|
|
192
|
+
```curl --verbose http://localhost:8080 1>/dev/null```
|
|
193
|
+
|
|
194
|
+
will finally request "www.bing.com".
|
|
195
|
+
|
|
196
|
+
Pre and post evaluations can have various results. The meanings of them are
|
|
197
|
+
described here:
|
|
198
|
+
|
|
199
|
+
#### Pre-Evaluation Results
|
|
200
|
+
|
|
201
|
+
| Result | Meaning |
|
|
202
|
+
|-------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
203
|
+
| break (string) | Do not evaluate subsequent pre evaluations. |
|
|
204
|
+
| null or undefined | Just jump to the next evaluation to run. |
|
|
205
|
+
| true (boolean) | Use this state api configuration. Run the configured request. |
|
|
206
|
+
| false (boolean) | Do not use this state api and to not run subsequent pre evaluations. |
|
|
207
|
+
| code (number) | Answer client request with provided http status code and do not run any subsequent pre-evaluations, state-api request or request forwarding to the underlying backend. |
|
|
208
|
+
|
|
209
|
+
#### Post-Evaluation Results
|
|
210
|
+
|
|
211
|
+
| Result | Meaning |
|
|
212
|
+
|-------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|
213
|
+
| break (string) | Do not evaluate subsequent post evaluations. |
|
|
214
|
+
| null or undefined | Just jump to the next evaluation to run. |
|
|
215
|
+
| code (number) | Answer client request with provided http status code and do not run any subsequent post-evaluations, state-api request or request forwarding to the underlying backend. |
|
|
216
|
+
|
|
217
|
+
### Smart configurations
|
|
218
|
+
|
|
219
|
+
Whenever you can configure list of items you can either use just one or a list
|
|
220
|
+
of them. Consider this configuration example:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
{
|
|
224
|
+
"forwarders": {
|
|
225
|
+
"bing": {
|
|
226
|
+
"host": "www.bing.com",
|
|
227
|
+
|
|
228
|
+
"headerTransformations": [{...}, {...}, ...],
|
|
229
|
+
|
|
230
|
+
"stateAPIs": {
|
|
231
|
+
"expressions": {
|
|
232
|
+
"pre": ["...", "...", ...],
|
|
233
|
+
"post": ["...", "...", ...]
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
If only one item is needed please consider that:
|
|
242
|
+
|
|
243
|
+
```
|
|
244
|
+
{
|
|
245
|
+
"forwarders": {
|
|
246
|
+
"bing": {
|
|
247
|
+
"host": "www.bing.com",
|
|
248
|
+
|
|
249
|
+
"headerTransformations": [{...}],
|
|
250
|
+
|
|
251
|
+
"stateAPIs": {
|
|
252
|
+
"expressions": {
|
|
253
|
+
"pre": ["..."],
|
|
254
|
+
"post": ["..."]
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
is equivalent to:
|
|
96
263
|
|
|
97
264
|
```
|
|
98
265
|
{
|
|
99
266
|
"forwarders": {
|
|
100
267
|
"bing": {
|
|
101
268
|
"host": "www.bing.com",
|
|
269
|
+
|
|
270
|
+
"headerTransformations": {...},
|
|
271
|
+
|
|
272
|
+
"stateAPIs": {
|
|
273
|
+
"expressions": {
|
|
274
|
+
"pre": "...",
|
|
275
|
+
"post": "..."
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
#### Use environment variables
|
|
284
|
+
|
|
285
|
+
While some configuration values are interpret as expression to be evalued at
|
|
286
|
+
runtime e.g. to decide which endpoint to use:
|
|
287
|
+
|
|
288
|
+
```
|
|
289
|
+
{
|
|
290
|
+
"forwarders": {
|
|
291
|
+
"endpoint": {
|
|
292
|
+
...
|
|
293
|
+
"useExpression": "..."
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
Every item can utilize expression to dynamically derive intial configurations:
|
|
300
|
+
|
|
301
|
+
```
|
|
302
|
+
{
|
|
303
|
+
"port": {
|
|
304
|
+
"__evaluate__": "environment.PORT ?? 8080"
|
|
305
|
+
},
|
|
306
|
+
...
|
|
307
|
+
}
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
If an environment variabel "PORT" is set it will be used or "8080" as a
|
|
311
|
+
fallback.
|
|
312
|
+
|
|
313
|
+
#### Use Base Forwarder
|
|
314
|
+
|
|
315
|
+
Base forwarder are inherited by every specific forwarder. This configuration:
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
{
|
|
319
|
+
"forwarders": {
|
|
320
|
+
"base: {
|
|
102
321
|
"headerTransformations": {
|
|
103
|
-
"send":
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
],
|
|
109
|
-
|
|
110
|
-
"retrieve": [
|
|
111
|
-
{
|
|
112
|
-
"source": "/set-cookie: (.+)/",
|
|
113
|
-
"target": "'X-Prevented-Cookie: $1'"
|
|
114
|
-
}
|
|
115
|
-
]
|
|
322
|
+
"send": {
|
|
323
|
+
"source": "/(GET|POST) \\/.* (.*)/",
|
|
324
|
+
"target": "'$1 /sub/path/to/forward/to $2'"
|
|
325
|
+
}
|
|
116
326
|
}
|
|
327
|
+
},
|
|
328
|
+
|
|
329
|
+
"bing": {
|
|
330
|
+
"host": "www.bing.com",
|
|
331
|
+
"useExpression": "Math.random() < 0.5"
|
|
332
|
+
},
|
|
333
|
+
|
|
334
|
+
"google": {
|
|
335
|
+
"host": "www.google.com"
|
|
117
336
|
}
|
|
118
337
|
}
|
|
119
338
|
}
|
|
120
339
|
```
|
|
121
340
|
|
|
341
|
+
is equivalent to:
|
|
342
|
+
|
|
343
|
+
```
|
|
344
|
+
{
|
|
345
|
+
"forwarders": {
|
|
346
|
+
"bing": {
|
|
347
|
+
"host": "www.bing.com",
|
|
348
|
+
|
|
349
|
+
"useExpression": "Math.random() < 0.5",
|
|
350
|
+
|
|
351
|
+
"headerTransformations": {
|
|
352
|
+
"send": {
|
|
353
|
+
"source": "/(GET|POST) \\/.* (.*)/",
|
|
354
|
+
"target": "'$1 /sub/path/to/forward/to $2'"
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
"google": {
|
|
360
|
+
"host": "www.google.com",
|
|
361
|
+
|
|
362
|
+
"headerTransformations": {
|
|
363
|
+
"send": {
|
|
364
|
+
"source": "/(GET|POST) \\/.* (.*)/",
|
|
365
|
+
"target": "'$1 /sub/path/to/forward/to $2'"
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
122
371
|
```
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
372
|
+
|
|
373
|
+
#### Use base State-APIs
|
|
374
|
+
|
|
375
|
+
As we support generic base forwarder confgurations there are also base state
|
|
376
|
+
api configuraton sections. Consider the follwing configuration example:
|
|
377
|
+
|
|
128
378
|
```
|
|
379
|
+
{
|
|
380
|
+
"forwarders": {
|
|
381
|
+
"bing": {
|
|
382
|
+
"host": "www.bing.com",
|
|
129
383
|
|
|
130
|
-
|
|
384
|
+
"stateAPIs": [
|
|
385
|
+
{
|
|
386
|
+
"name": "base",
|
|
387
|
+
"url": "https://www.google.com/search?q=${data.query}",
|
|
131
388
|
|
|
132
|
-
|
|
133
|
-
|
|
389
|
+
"expressions": {
|
|
390
|
+
"pre": "request.headers['ask-google'] ? true : null",
|
|
391
|
+
"post": "response.statusCode >= 200 && response.statusCode < 300 ? null : response.statusCode"
|
|
392
|
+
}
|
|
393
|
+
}
|
|
134
394
|
|
|
135
|
-
|
|
136
|
-
|
|
395
|
+
{
|
|
396
|
+
"name": "sense",
|
|
397
|
+
|
|
398
|
+
"data": {
|
|
399
|
+
"query": "What is the meaning of life?"
|
|
400
|
+
}
|
|
401
|
+
},
|
|
402
|
+
|
|
403
|
+
{
|
|
404
|
+
"name": "nonesense",
|
|
405
|
+
|
|
406
|
+
"data": {
|
|
407
|
+
"query": "baby cats"
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
137
414
|
```
|
|
138
415
|
|
|
416
|
+
Note that url and expression are inherited to the state apis "sense" and
|
|
417
|
+
"nonsense".
|
|
418
|
+
The Data field can save various configuration items to be used in runtime
|
|
419
|
+
expressions.
|
|
420
|
+
Please also note that it is possible to access configuration, request or
|
|
421
|
+
response informations from other state apis in every runtime expression.
|
|
422
|
+
|
|
423
|
+
Every expression can access the following environment:
|
|
424
|
+
|
|
425
|
+
| Name | Meaning |
|
|
426
|
+
|-----------|--------------------------------------------------------|
|
|
427
|
+
| data | Generic configuration items. |
|
|
428
|
+
| error | Error object if some occured. |
|
|
429
|
+
| request | Client request informations. |
|
|
430
|
+
| response | Response informations (available in post evaluations). |
|
|
431
|
+
| stateAPIs | Access oher state api informations. |
|
|
432
|
+
| Tools | [see](https://www.npmjs.com/package/clientnode) |
|
|
433
|
+
|
|
139
434
|
<!-- region modline
|
|
140
435
|
vim: set tabstop=4 shiftwidth=4 expandtab:
|
|
141
436
|
vim: foldmethod=marker foldmarker=region,endregion:
|
package/type.d.ts
CHANGED
|
@@ -50,8 +50,10 @@ export type EvaluationParameters = [
|
|
|
50
50
|
EvaluationScope['stateAPIs'],
|
|
51
51
|
EvaluationScope['Tools']
|
|
52
52
|
];
|
|
53
|
-
export type
|
|
54
|
-
export type
|
|
53
|
+
export type APIPreEvaluationResult = 'break' | boolean | null | number | undefined | void;
|
|
54
|
+
export type APIPreEvaluationExpression = string | ((...parameters: EvaluationParameters) => APIPreEvaluationResult);
|
|
55
|
+
export type APIPostEvaluationResult = 'break' | null | number | void;
|
|
56
|
+
export type APIPostEvaluationExpression = string | ((...parameters: EvaluationParameters) => APIPostEvaluationResult);
|
|
55
57
|
export interface APIExpressions {
|
|
56
58
|
pre?: Array<APIPreEvaluationExpression> | APIPreEvaluationExpression;
|
|
57
59
|
post?: Array<APIPostEvaluationExpression> | APIPostEvaluationExpression;
|
|
@@ -64,8 +66,8 @@ export interface StateAPI {
|
|
|
64
66
|
url: string;
|
|
65
67
|
}
|
|
66
68
|
export interface ResolvedAPIExpressions {
|
|
67
|
-
pre: Array<(...parmaters: EvaluationParameters) =>
|
|
68
|
-
post: Array<(...parameters: EvaluationParameters) =>
|
|
69
|
+
pre: Array<(...parmaters: EvaluationParameters) => APIPreEvaluationResult>;
|
|
70
|
+
post: Array<(...parameters: EvaluationParameters) => APIPostEvaluationResult>;
|
|
69
71
|
}
|
|
70
72
|
export type ResolvedStateAPI = NonNullable<Omit<StateAPI, 'expressions'>> & {
|
|
71
73
|
expressions: ResolvedAPIExpressions;
|