From 73e0e0a9a8a0f41b1e071122453a8326eb43a7b8 Mon Sep 17 00:00:00 2001 From: chimurai Date: Mon, 5 Jun 2017 21:36:11 +0200 Subject: [PATCH] lint: standardjs (#154) --- .editorconfig | 2 +- .jscsrc | 8 - README.md | 10 +- examples/browser-sync/index.js | 30 +- examples/connect/index.js | 26 +- examples/express/index.js | 24 +- examples/websocket/index.js | 38 +- index.js | 8 +- lib/config-factory.js | 165 +-- lib/context-matcher.js | 108 +- lib/errors.js | 8 + lib/handlers.js | 110 +- lib/index.js | 269 ++--- lib/logger.js | 250 ++--- lib/path-rewriter.js | 103 +- lib/router.js | 80 +- package.json | 7 +- test/e2e/_utils.js | 28 +- test/e2e/express-router.spec.js | 127 ++- test/e2e/http-proxy-middleware.spec.js | 1332 ++++++++++++------------ test/e2e/path-rewriter.spec.js | 196 ++-- test/e2e/router.spec.js | 253 +++-- test/e2e/websocket.spec.js | 297 +++--- test/lint.js | 19 + test/unit/_libs.js | 14 +- test/unit/config-factory.spec.js | 304 +++--- test/unit/context-matcher.spec.js | 499 +++++---- test/unit/handlers.spec.js | 276 ++--- test/unit/logger.spec.js | 518 ++++----- test/unit/path-rewriter.spec.js | 284 ++--- test/unit/router.spec.js | 269 +++-- 31 files changed, 2834 insertions(+), 2828 deletions(-) delete mode 100644 .jscsrc create mode 100644 lib/errors.js create mode 100644 test/lint.js diff --git a/.editorconfig b/.editorconfig index bd4c3407..9d5a4919 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,7 +6,7 @@ root = true charset = utf-8 end_of_line = lf indent_style = space -indent_size = 4 +indent_size = 2 insert_final_newline = true trim_trailing_whitespace = true diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index 8ba74f46..00000000 --- a/.jscsrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "preset": "google", - "validateIndentation": 4, - "maximumLineLength": { - "value": 120, - "allExcept": ["comments", "regex"] - }, -} diff --git a/README.md b/README.md index 1cd68443..40787b54 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ [![Coveralls](https://img.shields.io/coveralls/chimurai/http-proxy-middleware.svg?style=flat-square)](https://coveralls.io/r/chimurai/http-proxy-middleware) [![dependency Status](https://img.shields.io/david/chimurai/http-proxy-middleware.svg?style=flat-square)](https://david-dm.org/chimurai/http-proxy-middleware#info=dependencies) [![dependency Status](https://snyk.io/test/npm/http-proxy-middleware/badge.svg)](https://snyk.io/test/npm/http-proxy-middleware) +[![JavaScript Style Guide](https://img.shields.io/badge/codestyle-standard-brightgreen.svg)](https://standardjs.com) 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). @@ -419,18 +420,13 @@ Run the test suite: ```bash # install dependencies $ npm install -``` -unit testing +# linting +$ npm run lint -```bash # unit tests $ npm test -``` -coverage - -```bash # code coverage $ npm run cover ``` diff --git a/examples/browser-sync/index.js b/examples/browser-sync/index.js index 1ab24547..7a170506 100644 --- a/examples/browser-sync/index.js +++ b/examples/browser-sync/index.js @@ -1,29 +1,29 @@ /** * Module dependencies. */ -var browserSync = require('browser-sync').create(); -var proxy = require('../../index'); // require('http-proxy-middleware'); +var browserSync = require('browser-sync').create() +var proxy = require('../../index') // require('http-proxy-middleware'); /** * Configure proxy middleware */ var jsonPlaceholderProxy = proxy('/users', { - target: 'http://jsonplaceholder.typicode.com', - changeOrigin: true, // for vhosted sites, changes host header to match to target's host - logLevel: 'debug' -}); + target: 'http://jsonplaceholder.typicode.com', + changeOrigin: true, // for vhosted sites, changes host header to match to target's host + logLevel: 'debug' +}) /** * Add the proxy to browser-sync */ browserSync.init({ - server: { - baseDir: './', - port: 3000, - middleware: [jsonPlaceholderProxy], - }, - startPath: '/users' -}); + server: { + baseDir: './', + port: 3000, + middleware: [jsonPlaceholderProxy] + }, + startPath: '/users' +}) -console.log('[DEMO] Server: listening on port 3000'); -console.log('[DEMO] Opening: http://localhost:3000/users'); +console.log('[DEMO] Server: listening on port 3000') +console.log('[DEMO] Opening: http://localhost:3000/users') diff --git a/examples/connect/index.js b/examples/connect/index.js index 8fc4099f..9fa19944 100644 --- a/examples/connect/index.js +++ b/examples/connect/index.js @@ -1,29 +1,29 @@ /** * Module dependencies. */ -var http = require('http'); -var connect = require('connect'); -var proxy = require('../../index'); // require('http-proxy-middleware'); +var http = require('http') +var connect = require('connect') +var proxy = require('../../index') // require('http-proxy-middleware'); /** * Configure proxy middleware */ var jsonPlaceholderProxy = proxy({ - target: 'http://jsonplaceholder.typicode.com', - changeOrigin: true, // for vhosted sites, changes host header to match to target's host - logLevel: 'debug' -}); + target: 'http://jsonplaceholder.typicode.com', + changeOrigin: true, // for vhosted sites, changes host header to match to target's host + logLevel: 'debug' +}) -var app = connect(); +var app = connect() /** * Add the proxy to connect */ -app.use('/users', jsonPlaceholderProxy); +app.use('/users', jsonPlaceholderProxy) -http.createServer(app).listen(3000); +http.createServer(app).listen(3000) -console.log('[DEMO] Server: listening on port 3000'); -console.log('[DEMO] Opening: http://localhost:3000/users'); +console.log('[DEMO] Server: listening on port 3000') +console.log('[DEMO] Opening: http://localhost:3000/users') -require('opn')('http://localhost:3000/users'); +require('opn')('http://localhost:3000/users') diff --git a/examples/express/index.js b/examples/express/index.js index 2f63d05b..7d280844 100644 --- a/examples/express/index.js +++ b/examples/express/index.js @@ -1,28 +1,28 @@ /** * Module dependencies. */ -var express = require('express'); -var proxy = require('../../index'); // require('http-proxy-middleware'); +var express = require('express') +var proxy = require('../../index') // require('http-proxy-middleware'); /** * Configure proxy middleware */ var jsonPlaceholderProxy = proxy({ - target: 'http://jsonplaceholder.typicode.com', - changeOrigin: true, // for vhosted sites, changes host header to match to target's host - logLevel: 'debug' -}); + target: 'http://jsonplaceholder.typicode.com', + changeOrigin: true, // for vhosted sites, changes host header to match to target's host + logLevel: 'debug' +}) -var app = express(); +var app = express() /** * Add the proxy to express */ -app.use('/users', jsonPlaceholderProxy); +app.use('/users', jsonPlaceholderProxy) -app.listen(3000); +app.listen(3000) -console.log('[DEMO] Server: listening on port 3000'); -console.log('[DEMO] Opening: http://localhost:3000/users'); +console.log('[DEMO] Server: listening on port 3000') +console.log('[DEMO] Opening: http://localhost:3000/users') -require('opn')('http://localhost:3000/users'); +require('opn')('http://localhost:3000/users') diff --git a/examples/websocket/index.js b/examples/websocket/index.js index 2d470633..0d22b56a 100644 --- a/examples/websocket/index.js +++ b/examples/websocket/index.js @@ -1,34 +1,34 @@ /** * Module dependencies. */ -var express = require('express'); -var proxy = require('../../index'); // require('http-proxy-middleware'); +var express = require('express') +var proxy = require('../../index') // require('http-proxy-middleware'); /** * Configure proxy middleware */ var wsProxy = proxy('/', { - target: 'http://echo.websocket.org', - // pathRewrite: { - // '^/websocket' : '/socket', // rewrite path. - // '^/removepath' : '' // remove path. - // }, - changeOrigin: true, // for vhosted sites, changes host header to match to target's host - ws: true, // enable websocket proxy - logLevel: 'debug' - }); + target: 'http://echo.websocket.org', + // pathRewrite: { + // '^/websocket' : '/socket', // rewrite path. + // '^/removepath' : '' // remove path. + // }, + changeOrigin: true, // for vhosted sites, changes host header to match to target's host + ws: true, // enable websocket proxy + logLevel: 'debug' +}) -var app = express(); -app.use('/', express.static(__dirname)); // demo page -app.use(wsProxy); // add the proxy to express +var app = express() +app.use('/', express.static(__dirname)) // demo page +app.use(wsProxy) // add the proxy to express -var server = app.listen(3000); -server.on('upgrade', wsProxy.upgrade); // optional: upgrade externally +var server = app.listen(3000) +server.on('upgrade', wsProxy.upgrade) // optional: upgrade externally -console.log('[DEMO] Server: listening on port 3000'); -console.log('[DEMO] Opening: http://localhost:3000'); +console.log('[DEMO] Server: listening on port 3000') +console.log('[DEMO] Opening: http://localhost:3000') -require('opn')('http://localhost:3000'); +require('opn')('http://localhost:3000') /** * Example: diff --git a/index.js b/index.js index b847ba1e..ed0ca8ab 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ -var HPM = require('./lib'); +var HPM = require('./lib') -module.exports = function(context, opts) { - return new HPM(context, opts); -}; +module.exports = function (context, opts) { + return new HPM(context, opts) +} diff --git a/lib/config-factory.js b/lib/config-factory.js index 66058fdf..1b3c848f 100644 --- a/lib/config-factory.js +++ b/lib/config-factory.js @@ -1,54 +1,55 @@ -var _ = require('lodash'); -var url = require('url'); -var logger = require('./logger').getInstance(); +var _ = require('lodash') +var url = require('url') +var ERRORS = require('./errors') +var logger = require('./logger').getInstance() module.exports = { - createConfig: createConfig -}; - -function createConfig(context, opts) { - // structure of config object to be returned - var config = { - context: undefined, - options: {} - }; - - // app.use('/api', proxy({target:'http://localhost:9000'})); - if (isContextless(context, opts)) { - config.context = '/'; - config.options = _.assign(config.options, context); - } - // app.use('/api', proxy('http://localhost:9000')); - // app.use(proxy('http://localhost:9000/api')); - else if (isStringShortHand(context)) { - var oUrl = url.parse(context); - var target = [oUrl.protocol, '//', oUrl.host].join(''); - - config.context = oUrl.pathname || '/'; - config.options = _.assign(config.options, {target: target}, opts); - - if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') { - config.options.ws = true; - } - // app.use('/api', proxy({target:'http://localhost:9000'})); - } else { - config.context = context; - config.options = _.assign(config.options, opts); + createConfig: createConfig +} + +function createConfig (context, opts) { + // structure of config object to be returned + var config = { + context: undefined, + options: {} + } + + // app.use('/api', proxy({target:'http://localhost:9000'})); + if (isContextless(context, opts)) { + config.context = '/' + config.options = _.assign(config.options, context) + + // app.use('/api', proxy('http://localhost:9000')); + // app.use(proxy('http://localhost:9000/api')); + } else if (isStringShortHand(context)) { + var oUrl = url.parse(context) + var target = [oUrl.protocol, '//', oUrl.host].join('') + + config.context = oUrl.pathname || '/' + config.options = _.assign(config.options, {target: target}, opts) + + if (oUrl.protocol === 'ws:' || oUrl.protocol === 'wss:') { + config.options.ws = true } + // app.use('/api', proxy({target:'http://localhost:9000'})); + } else { + config.context = context + config.options = _.assign(config.options, opts) + } - configureLogger(config.options); + configureLogger(config.options) - if (!config.options.target) { - throw new Error('[HPM] Missing "target" option. Example: {target: "http://www.example.org"}'); - } + if (!config.options.target) { + throw new Error(ERRORS.ERR_CONFIG_FACTORY_TARGET_MISSING) + } - // Legacy option.proxyHost - config.options = mapLegacyProxyHostOption(config.options); + // Legacy option.proxyHost + config.options = mapLegacyProxyHostOption(config.options) - // Legacy option.proxyTable > option.router - config.options = mapLegacyProxyTableOption(config.options); + // Legacy option.proxyTable > option.router + config.options = mapLegacyProxyTableOption(config.options) - return config; + return config } /** @@ -62,10 +63,10 @@ function createConfig(context, opts) { * @param {String} context [description] * @return {Boolean} [description] */ -function isStringShortHand(context) { - if (_.isString(context)) { - return (url.parse(context).host) ? true : false; - } +function isStringShortHand (context) { + if (_.isString(context)) { + return !!(url.parse(context).host) + } } /** @@ -79,48 +80,48 @@ function isStringShortHand(context) { * @param {*} opts [description] * @return {Boolean} [description] */ -function isContextless(context, opts) { - return (_.isPlainObject(context) && _.isEmpty(opts)); +function isContextless (context, opts) { + return (_.isPlainObject(context) && _.isEmpty(opts)) } -function mapLegacyProxyHostOption(options) { - // set options.headers.host when option.proxyHost is provided - if (options.proxyHost) { - logger.warn('*************************************'); - logger.warn('[HPM] Deprecated "option.proxyHost"'); - logger.warn(' Use "option.changeOrigin" or "option.headers.host" instead'); - logger.warn(' "option.proxyHost" will be removed in future release.'); - logger.warn('*************************************'); - - options.headers = options.headers || {}; - options.headers.host = options.proxyHost; - } +function mapLegacyProxyHostOption (options) { + // set options.headers.host when option.proxyHost is provided + if (options.proxyHost) { + logger.warn('*************************************') + logger.warn('[HPM] Deprecated "option.proxyHost"') + logger.warn(' Use "option.changeOrigin" or "option.headers.host" instead') + logger.warn(' "option.proxyHost" will be removed in future release.') + logger.warn('*************************************') - return options; + options.headers = options.headers || {} + options.headers.host = options.proxyHost + } + + return options } // Warn deprecated proxyTable api usage -function mapLegacyProxyTableOption(options) { - if (options.proxyTable) { - logger.warn('*************************************'); - logger.warn('[HPM] Deprecated "option.proxyTable"'); - logger.warn(' Use "option.router" instead'); - logger.warn(' "option.proxyTable" will be removed in future release.'); - logger.warn('*************************************'); - - options.router = _.clone(options.proxyTable); - _.omit(options, 'proxyTable'); - } - - return options; +function mapLegacyProxyTableOption (options) { + if (options.proxyTable) { + logger.warn('*************************************') + logger.warn('[HPM] Deprecated "option.proxyTable"') + logger.warn(' Use "option.router" instead') + logger.warn(' "option.proxyTable" will be removed in future release.') + logger.warn('*************************************') + + options.router = _.clone(options.proxyTable) + _.omit(options, 'proxyTable') + } + + return options } -function configureLogger(options) { - if (options.logLevel) { - logger.setLevel(options.logLevel); - } +function configureLogger (options) { + if (options.logLevel) { + logger.setLevel(options.logLevel) + } - if (options.logProvider) { - logger.setProvider(options.logProvider); - } + if (options.logProvider) { + logger.setProvider(options.logProvider) + } } diff --git a/lib/context-matcher.js b/lib/context-matcher.js index 90f62aaa..bfee28f8 100644 --- a/lib/context-matcher.js +++ b/lib/context-matcher.js @@ -1,43 +1,43 @@ -var _ = require('lodash'); -var url = require('url'); -var isGlob = require('is-glob'); -var micromatch = require('micromatch'); +var _ = require('lodash') +var url = require('url') +var isGlob = require('is-glob') +var micromatch = require('micromatch') +var ERRORS = require('./errors') module.exports = { - match: matchContext -}; + match: matchContext +} -function matchContext(context, uri, req) { +function matchContext (context, uri, req) { + // single path + if (isStringPath(context)) { + return matchSingleStringPath(context, uri) + } - // single path - if (isStringPath(context)) { - return matchSingleStringPath(context, uri); - } + // single glob path + if (isGlobPath(context)) { + return matchSingleGlobPath(context, uri) + } - // single glob path - if (isGlobPath(context)) { - return matchSingleGlobPath(context, uri); + // multi path + if (Array.isArray(context)) { + if (context.every(isStringPath)) { + return matchMultiPath(context, uri) } - - // multi path - if (Array.isArray(context)) { - if (context.every(isStringPath)) { - return matchMultiPath(context, uri); - } - if (context.every(isGlobPath)) { - return matchMultiGlobPath(context, uri); - } - - throw new Error('[HPM] Invalid context. Expecting something like: ["/api", "/ajax"] or ["/api/**", "!**.html"]'); + if (context.every(isGlobPath)) { + return matchMultiGlobPath(context, uri) } - // custom matching - if (_.isFunction(context)) { - var pathname = getUrlPathName(uri); - return context(pathname, req); - } + throw new Error(ERRORS.ERR_CONTEXT_MATCHER_INVALID_ARRAY) + } + + // custom matching + if (_.isFunction(context)) { + var pathname = getUrlPathName(uri) + return context(pathname, req) + } - throw new Error('[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]'); + throw new Error(ERRORS.ERR_CONTEXT_MATCHER_GENERIC) } /** @@ -45,34 +45,34 @@ function matchContext(context, uri, req) { * @param {String} uri 'http://example.org/api/b/c/d.html' * @return {Boolean} */ -function matchSingleStringPath(context, uri) { - var pathname = getUrlPathName(uri); - return pathname.indexOf(context) === 0; +function matchSingleStringPath (context, uri) { + var pathname = getUrlPathName(uri) + return pathname.indexOf(context) === 0 } -function matchSingleGlobPath(pattern, uri) { - var pathname = getUrlPathName(uri); - var matches = micromatch(pathname, pattern); - return matches && (matches.length > 0); +function matchSingleGlobPath (pattern, uri) { + var pathname = getUrlPathName(uri) + var matches = micromatch(pathname, pattern) + return matches && (matches.length > 0) } -function matchMultiGlobPath(patternList, uri) { - return matchSingleGlobPath(patternList, uri); +function matchMultiGlobPath (patternList, uri) { + return matchSingleGlobPath(patternList, uri) } /** - * @param {String} context ['/api', '/ajax'] + * @param {String} contextList ['/api', '/ajax'] * @param {String} uri 'http://example.org/api/b/c/d.html' * @return {Boolean} */ -function matchMultiPath(contextList, uri) { - for (var i = 0; i < contextList.length; i++) { - var context = contextList[i]; - if (matchSingleStringPath(context, uri)) { - return true; - } +function matchMultiPath (contextList, uri) { + for (var i = 0; i < contextList.length; i++) { + var context = contextList[i] + if (matchSingleStringPath(context, uri)) { + return true } - return false; + } + return false } /** @@ -81,14 +81,14 @@ function matchMultiPath(contextList, uri) { * @param {String} uri from req.url * @return {String} RFC 3986 path */ -function getUrlPathName(uri) { - return uri && url.parse(uri).pathname; +function getUrlPathName (uri) { + return uri && url.parse(uri).pathname } -function isStringPath(context) { - return _.isString(context) && !isGlob(context); +function isStringPath (context) { + return _.isString(context) && !isGlob(context) } -function isGlobPath(context) { - return isGlob(context); +function isGlobPath (context) { + return isGlob(context) } diff --git a/lib/errors.js b/lib/errors.js new file mode 100644 index 00000000..5cde1c9f --- /dev/null +++ b/lib/errors.js @@ -0,0 +1,8 @@ +/* eslint-disable max-len */ + +module.exports = { + ERR_CONFIG_FACTORY_TARGET_MISSING: '[HPM] Missing "target" option. Example: {target: "http://www.example.org"}', + ERR_CONTEXT_MATCHER_GENERIC: '[HPM] Invalid context. Expecting something like: "/api" or ["/api", "/ajax"]', + ERR_CONTEXT_MATCHER_INVALID_ARRAY: '[HPM] Invalid context. Expecting something like: ["/api", "/ajax"] or ["/api/**", "!**.html"]', + ERR_PATH_REWRITER_CONFIG: '[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function' +} diff --git a/lib/handlers.js b/lib/handlers.js index b1e59015..02222996 100644 --- a/lib/handlers.js +++ b/lib/handlers.js @@ -1,74 +1,74 @@ -var _ = require('lodash'); -var logger = require('./logger').getInstance(); +var _ = require('lodash') +var logger = require('./logger').getInstance() module.exports = { - init: init, - getHandlers: getProxyEventHandlers -}; + init: init, + getHandlers: getProxyEventHandlers +} -function init(proxy, opts) { - var handlers = getProxyEventHandlers(opts); +function init (proxy, opts) { + var handlers = getProxyEventHandlers(opts) - _.forIn(handlers, function(handler, eventName) { - proxy.on(eventName, handlers[eventName]); - }); + _.forIn(handlers, function (handler, eventName) { + proxy.on(eventName, handlers[eventName]) + }) - logger.debug('[HPM] Subscribed to http-proxy events: ', _.keys(handlers)); + logger.debug('[HPM] Subscribed to http-proxy events: ', _.keys(handlers)) } -function getProxyEventHandlers(opts) { - // https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events - var proxyEvents = ['error', 'proxyReq', 'proxyReqWs', 'proxyRes', 'open', 'close']; - var handlers = {}; - - _.forEach(proxyEvents, function(event) { - // 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(). - var eventName = _.camelCase('on ' + event); - var fnHandler = _.get(opts, eventName); +function getProxyEventHandlers (opts) { + // https://github.com/nodejitsu/node-http-proxy#listening-for-proxy-events + var proxyEvents = ['error', 'proxyReq', 'proxyReqWs', 'proxyRes', 'open', 'close'] + var handlers = {} - if (_.isFunction(fnHandler)) { - handlers[event] = fnHandler; - } - }); + _.forEach(proxyEvents, function (event) { + // 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(). + var eventName = _.camelCase('on ' + event) + var fnHandler = _.get(opts, eventName) - // add default error handler in absence of error handler - if (!_.isFunction(handlers.error)) { - handlers.error = defaultErrorHandler; + if (_.isFunction(fnHandler)) { + handlers[event] = fnHandler } + }) - // add default close handler in absence of close handler - if (!_.isFunction(handlers.close)) { - handlers.close = logClose; - } + // add default error handler in absence of error handler + if (!_.isFunction(handlers.error)) { + handlers.error = defaultErrorHandler + } - return handlers; -}; + // add default close handler in absence of close handler + if (!_.isFunction(handlers.close)) { + handlers.close = logClose + } + + return handlers +} -function defaultErrorHandler(err, req, res) { - var host = (req.headers && req.headers.host); - var code = err.code; +function defaultErrorHandler (err, req, res) { + var host = (req.headers && req.headers.host) + var 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': - res.writeHead(504); - break; - default: res.writeHead(500); - } - } + if (res.writeHead && !res.headersSent) { + if (/HPE_INVALID/.test(code)) { + res.writeHead(502) + } else { + switch (code) { + case 'ECONNRESET': + case 'ENOTFOUND': + case 'ECONNREFUSED': + res.writeHead(504) + break + default: res.writeHead(500) + } } + } - res.end('Error occured while trying to proxy to: ' + host + req.url); + res.end('Error occured while trying to proxy to: ' + host + req.url) } -function logClose(req, socket, head) { - // view disconnected websocket connections - logger.info('[HPM] Client disconnected'); +function logClose (req, socket, head) { + // view disconnected websocket connections + logger.info('[HPM] Client disconnected') } diff --git a/lib/index.js b/lib/index.js index a413d92e..7c4e6944 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,147 +1,152 @@ -var _ = require('lodash'); -var httpProxy = require('http-proxy'); -var configFactory = require('./config-factory'); -var handlers = require('./handlers'); -var contextMatcher = require('./context-matcher'); -var PathRewriter = require('./path-rewriter'); -var Router = require('./router'); -var logger = require('./logger').getInstance(); -var getArrow = require('./logger').getArrow; - -module.exports = HttpProxyMiddleware; - -function HttpProxyMiddleware(context, opts) { - // https://github.com/chimurai/http-proxy-middleware/issues/57 - var wsUpgradeDebounced = _.debounce(handleUpgrade); - var wsInitialized = false; - var config = configFactory.createConfig(context, opts); - var proxyOptions = config.options; - - // create proxy - var proxy = httpProxy.createProxyServer({}); - logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target); - - var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite); // returns undefined when "pathRewrite" is not provided - - // attach handler to http-proxy events - handlers.init(proxy, proxyOptions); - - // log errors for debug purpose - proxy.on('error', logError); - - // https://github.com/chimurai/http-proxy-middleware/issues/19 - // expose function to upgrade externally - middleware.upgrade = wsUpgradeDebounced; - - return middleware; - - function middleware(req, res, next) { - if (shouldProxy(config.context, req)) { - var activeProxyOptions = prepareProxyRequest(req); - proxy.web(req, res, activeProxyOptions); - } else { - next(); - } - - if (proxyOptions.ws === true) { - // use initial request to access the server object to subscribe to http upgrade event - catchUpgradeRequest(req.connection.server); - } +var _ = require('lodash') +var httpProxy = require('http-proxy') +var configFactory = require('./config-factory') +var handlers = require('./handlers') +var contextMatcher = require('./context-matcher') +var PathRewriter = require('./path-rewriter') +var Router = require('./router') +var logger = require('./logger').getInstance() +var getArrow = require('./logger').getArrow + +module.exports = HttpProxyMiddleware + +function HttpProxyMiddleware (context, opts) { + // https://github.com/chimurai/http-proxy-middleware/issues/57 + var wsUpgradeDebounced = _.debounce(handleUpgrade) + var wsInitialized = false + var config = configFactory.createConfig(context, opts) + var proxyOptions = config.options + + // create proxy + var proxy = httpProxy.createProxyServer({}) + logger.info('[HPM] Proxy created:', config.context, ' -> ', proxyOptions.target) + + var pathRewriter = PathRewriter.create(proxyOptions.pathRewrite) // returns undefined when "pathRewrite" is not provided + + // attach handler to http-proxy events + handlers.init(proxy, proxyOptions) + + // log errors for debug purpose + proxy.on('error', logError) + + // https://github.com/chimurai/http-proxy-middleware/issues/19 + // expose function to upgrade externally + middleware.upgrade = wsUpgradeDebounced + + return middleware + + function middleware (req, res, next) { + if (shouldProxy(config.context, req)) { + var activeProxyOptions = prepareProxyRequest(req) + proxy.web(req, res, activeProxyOptions) + } else { + next() } - function catchUpgradeRequest(server) { - // subscribe once; don't subscribe on every request... - // https://github.com/chimurai/http-proxy-middleware/issues/113 - if (!wsInitialized) { - server.on('upgrade', wsUpgradeDebounced); - wsInitialized = true; - } + if (proxyOptions.ws === true) { + // use initial request to access the server object to subscribe to http upgrade event + catchUpgradeRequest(req.connection.server) } + } + + function catchUpgradeRequest (server) { + // subscribe once; don't subscribe on every request... + // https://github.com/chimurai/http-proxy-middleware/issues/113 + if (!wsInitialized) { + server.on('upgrade', wsUpgradeDebounced) + wsInitialized = true + } + } - function handleUpgrade(req, socket, head) { - // set to initialized when used externally - wsInitialized = true; + function handleUpgrade (req, socket, head) { + // set to initialized when used externally + wsInitialized = true - if (shouldProxy(config.context, req)) { - var activeProxyOptions = prepareProxyRequest(req); - proxy.ws(req, socket, head, activeProxyOptions); - logger.info('[HPM] Upgrading to WebSocket'); - } + if (shouldProxy(config.context, req)) { + var activeProxyOptions = prepareProxyRequest(req) + proxy.ws(req, socket, head, activeProxyOptions) + logger.info('[HPM] Upgrading to WebSocket') } - - /** - * Determine whether request should be proxied. - * - * @private - * @return {Boolean} - */ - function shouldProxy(context, req) { - var path = (req.originalUrl || req.url); - return contextMatcher.match(context, path, req); + } + + /** + * Determine whether request should be proxied. + * + * @private + * @param {String} context [description] + * @param {Object} req [description] + * @return {Boolean} + */ + function shouldProxy (context, req) { + var path = (req.originalUrl || req.url) + return contextMatcher.match(context, path, req) + } + + /** + * Apply option.router and option.pathRewrite + * Order matters: + * Router uses original path for routing; + * NOT the modified path, after it has been rewritten by pathRewrite + * @param {Object} req + * @return {Object} proxy options + */ + function prepareProxyRequest (req) { + // https://github.com/chimurai/http-proxy-middleware/issues/17 + // https://github.com/chimurai/http-proxy-middleware/issues/94 + req.url = (req.originalUrl || req.url) + + // store uri before it gets rewritten for logging + var originalPath = req.url + var newProxyOptions = _.assign({}, proxyOptions) + + // Apply in order: + // 1. option.router + // 2. option.pathRewrite + __applyRouter(req, newProxyOptions) + __applyPathRewrite(req, pathRewriter) + + // debug logging for both http(s) and websockets + if (proxyOptions.logLevel === 'debug') { + var arrow = getArrow(originalPath, req.url, proxyOptions.target, newProxyOptions.target) + logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target) } - /** - * Apply option.router and option.pathRewrite - * Order matters: - Router uses original path for routing; - NOT the modified path, after it has been rewritten by pathRewrite - */ - function prepareProxyRequest(req) { - // https://github.com/chimurai/http-proxy-middleware/issues/17 - // https://github.com/chimurai/http-proxy-middleware/issues/94 - req.url = (req.originalUrl || req.url); - - // store uri before it gets rewritten for logging - var originalPath = req.url; - var newProxyOptions = _.assign({}, proxyOptions); - - // Apply in order: - // 1. option.router - // 2. option.pathRewrite - __applyRouter(req, newProxyOptions); - __applyPathRewrite(req, pathRewriter); - - // debug logging for both http(s) and websockets - if (proxyOptions.logLevel === 'debug') { - var arrow = getArrow(originalPath, req.url, proxyOptions.target, newProxyOptions.target); - logger.debug('[HPM] %s %s %s %s', req.method, originalPath, arrow, newProxyOptions.target); - } - - return newProxyOptions; - } + return newProxyOptions + } - // Modify option.target when router present. - function __applyRouter(req, options) { - var newTarget; + // Modify option.target when router present. + function __applyRouter (req, options) { + var newTarget - if (options.router) { - newTarget = Router.getTarget(req, options); + if (options.router) { + newTarget = Router.getTarget(req, options) - if (newTarget) { - logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget); - options.target = newTarget; - } - } + if (newTarget) { + logger.debug('[HPM] Router new target: %s -> "%s"', options.target, newTarget) + options.target = newTarget + } } - - // rewrite path - function __applyPathRewrite(req, pathRewriter) { - if (pathRewriter) { - var path = pathRewriter(req.url, req); - - if (typeof path === 'string') { - req.url = path; - } else { - logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url); - } - } + } + + // rewrite path + function __applyPathRewrite (req, pathRewriter) { + if (pathRewriter) { + var path = pathRewriter(req.url, req) + + if (typeof path === 'string') { + req.url = path + } else { + logger.info('[HPM] pathRewrite: No rewritten path found. (%s)', req.url) + } } + } - function logError(err, req, res) { - var hostname = (req.headers && req.headers.host) || (req.hostname || req.host); // (websocket) || (node0.10 || node 4/5) - var target = proxyOptions.target.host || proxyOptions.target; - var errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors'; // link to Node Common Systems Errors page + function logError (err, req, res) { + var hostname = (req.headers && req.headers.host) || (req.hostname || req.host) // (websocket) || (node0.10 || node 4/5) + var target = proxyOptions.target.host || proxyOptions.target + var errorMessage = '[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)' + var errReference = 'https://nodejs.org/api/errors.html#errors_common_system_errors' // link to Node Common Systems Errors page - logger.error('[HPM] Error occurred while trying to proxy request %s from %s to %s (%s) (%s)', req.url, hostname, target, err.code, errReference); - } -}; + logger.error(errorMessage, req.url, hostname, target, err.code, errReference) + } +} diff --git a/lib/logger.js b/lib/logger.js index be73680b..2a2ed555 100644 --- a/lib/logger.js +++ b/lib/logger.js @@ -1,144 +1,144 @@ -var util = require('util'); -var _ = require('lodash'); +var util = require('util') +var _ = require('lodash') -var loggerInstance; +var loggerInstance var defaultProvider = { - log: console.log, - debug: console.log, // use .log(); since console does not have .debug() - info: console.info, - warn: console.warn, - error: console.error -}; + log: console.log, + debug: console.log, // use .log(); since console does not have .debug() + info: console.info, + warn: console.warn, + error: console.error +} // log level 'weight' var LEVELS = { - debug: 10, - info: 20, - warn: 30, - error: 50, - silent: 80 -}; + debug: 10, + info: 20, + warn: 30, + error: 50, + silent: 80 +} module.exports = { - // singleton - getInstance: function() { - if (!loggerInstance) { - loggerInstance = new Logger(); - } + // singleton + getInstance: function () { + if (!loggerInstance) { + loggerInstance = new Logger() + } + + return loggerInstance + }, + getArrow: getArrow +} - return loggerInstance; +function Logger () { + var logLevel + var provider + + var api = { + log: log, + debug: debug, + info: info, + warn: warn, + error: error, + setLevel: function (v) { + if (isValidLevel(v)) { + logLevel = v + } }, - getArrow: getArrow -}; - -function Logger() { - var logLevel; - var provider; - - var api = { - log: log, - debug: debug, - info: info, - warn: warn, - error: error, - setLevel: function(v) { - if (isValidLevel(v)) { - logLevel = v; - } - }, - setProvider: function(fn) { - if (fn && isValidProvider(fn)) { - provider = fn(defaultProvider); - } - } - }; - - init(); - - return api; - - function init() { - api.setLevel('info'); - api.setProvider(function() { - return defaultProvider; - }); + setProvider: function (fn) { + if (fn && isValidProvider(fn)) { + provider = fn(defaultProvider) + } } + } - // log will log messages, regardless of logLevels - function log() { - provider.log(_interpolate.apply(null, arguments)); - } + init() - function debug() { - if (_showLevel('debug')) { - provider.debug(_interpolate.apply(null, arguments)); - } - } + return api - function info() { - if (_showLevel('info')) { - provider.info(_interpolate.apply(null, arguments)); - } - } + function init () { + api.setLevel('info') + api.setProvider(function () { + return defaultProvider + }) + } - function warn() { - if (_showLevel('warn')) { - provider.warn(_interpolate.apply(null, arguments)); - } - } + // log will log messages, regardless of logLevels + function log () { + provider.log(_interpolate.apply(null, arguments)) + } - function error() { - if (_showLevel('error')) { - provider.error(_interpolate.apply(null, arguments)); - } + function debug () { + if (_showLevel('debug')) { + provider.debug(_interpolate.apply(null, arguments)) } + } - /** - * Decide to log or not to log, based on the log levels 'weight' - * @param {String} showLevel [debug, info, warn, error, silent] - * @return {Boolean} - */ - function _showLevel(showLevel) { - var result = false; - var currentLogLevel = LEVELS[logLevel]; + function info () { + if (_showLevel('info')) { + provider.info(_interpolate.apply(null, arguments)) + } + } - if (currentLogLevel && (currentLogLevel <= LEVELS[showLevel])) { - result = true; - } + function warn () { + if (_showLevel('warn')) { + provider.warn(_interpolate.apply(null, arguments)) + } + } - return result; + function error () { + if (_showLevel('error')) { + provider.error(_interpolate.apply(null, arguments)) + } + } + + /** + * Decide to log or not to log, based on the log levels 'weight' + * @param {String} showLevel [debug, info, warn, error, silent] + * @return {Boolean} + */ + function _showLevel (showLevel) { + var result = false + var currentLogLevel = LEVELS[logLevel] + + if (currentLogLevel && (currentLogLevel <= LEVELS[showLevel])) { + result = true } - // make sure logged messages and its data are return interpolated - // make it possible for additional log data, such date/time or custom prefix. - function _interpolate() { - var fn = _.spread(util.format); - var result = fn(_.slice(arguments)); + return result + } - return result; - } + // make sure logged messages and its data are return interpolated + // make it possible for additional log data, such date/time or custom prefix. + function _interpolate () { + var fn = _.spread(util.format) + var result = fn(_.slice(arguments)) - function isValidProvider(fnProvider) { - var result = true; + return result + } - if (fnProvider && !_.isFunction(fnProvider)) { - throw new Error('[HPM] Log provider config error. Expecting a function.'); - } + function isValidProvider (fnProvider) { + var result = true - return result; + if (fnProvider && !_.isFunction(fnProvider)) { + throw new Error('[HPM] Log provider config error. Expecting a function.') } - function isValidLevel(levelName) { - var validLevels = _.keys(LEVELS); - var isValid = _.includes(validLevels, levelName); + return result + } - if (!isValid) { - throw new Error('[HPM] Log level error. Invalid logLevel.'); - } + function isValidLevel (levelName) { + var validLevels = _.keys(LEVELS) + var isValid = _.includes(validLevels, levelName) - return isValid; + if (!isValid) { + throw new Error('[HPM] Log level error. Invalid logLevel.') } + + return isValid + } } /** @@ -146,13 +146,27 @@ function Logger() { * => router * ~> pathRewrite * ≈> router + pathRewrite + * + * @param {String} originalPath + * @param {String} newPath + * @param {String} originalTarget + * @param {String} newTarget + * @return {String} */ -function getArrow(originalPath, newPath, originalTarget, newTarget) { - var arrow = ['>']; - var isNewTarget = (originalTarget !== newTarget); // router - var isNewPath = (originalPath !== newPath); // pathRewrite - - if (isNewPath && !isNewTarget) {arrow.unshift('~');} else if (!isNewPath && isNewTarget) {arrow.unshift('=');} else if (isNewPath && isNewTarget) {arrow.unshift('≈');} else {arrow.unshift('-');} - - return arrow.join(''); +function getArrow (originalPath, newPath, originalTarget, newTarget) { + var arrow = ['>'] + var isNewTarget = (originalTarget !== newTarget) // router + var isNewPath = (originalPath !== newPath) // pathRewrite + + if (isNewPath && !isNewTarget) { + arrow.unshift('~') + } else if (!isNewPath && isNewTarget) { + arrow.unshift('=') + } else if (isNewPath && isNewTarget) { + arrow.unshift('≈') + } else { + arrow.unshift('-') + } + + return arrow.join('') } diff --git a/lib/path-rewriter.js b/lib/path-rewriter.js index 3b5bba91..f00c4ebe 100644 --- a/lib/path-rewriter.js +++ b/lib/path-rewriter.js @@ -1,72 +1,73 @@ -var _ = require('lodash'); -var logger = require('./logger').getInstance(); +var _ = require('lodash') +var logger = require('./logger').getInstance() +var ERRORS = require('./errors') module.exports = { - create: createPathRewriter -}; + create: createPathRewriter +} /** * Create rewrite function, to cache parsed rewrite rules. * - * @returns {function} Function to rewrite paths; This function should accept `path` (request.url) as parameter + * @param {Object} rewriteConfig + * @return {Function} Function to rewrite paths; This function should accept `path` (request.url) as parameter */ -function createPathRewriter(rewriteConfig) { - var rulesCache; +function createPathRewriter (rewriteConfig) { + var rulesCache - if (!isValidRewriteConfig(rewriteConfig)) { - return; - } + if (!isValidRewriteConfig(rewriteConfig)) { + return + } - if (_.isFunction(rewriteConfig)) { - var customRewriteFn = rewriteConfig; - return customRewriteFn; - } else { - rulesCache = parsePathRewriteRules(rewriteConfig); - return rewritePath; - } + if (_.isFunction(rewriteConfig)) { + var customRewriteFn = rewriteConfig + return customRewriteFn + } else { + rulesCache = parsePathRewriteRules(rewriteConfig) + return rewritePath + } - function rewritePath(path) { - var result = path; + function rewritePath (path) { + var result = path - _.forEach(rulesCache, function(rule) { - 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; - } - }); + _.forEach(rulesCache, function (rule) { + 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 + } + }) - return result; - } + return result + } } -function isValidRewriteConfig(rewriteConfig) { - if (_.isFunction(rewriteConfig)) { - return true; - } else if (!_.isEmpty(rewriteConfig) && _.isPlainObject(rewriteConfig)) { - return true; - } else if (_.isUndefined(rewriteConfig) || +function isValidRewriteConfig (rewriteConfig) { + if (_.isFunction(rewriteConfig)) { + return true + } else if (!_.isEmpty(rewriteConfig) && _.isPlainObject(rewriteConfig)) { + return true + } else if (_.isUndefined(rewriteConfig) || _.isNull(rewriteConfig) || _.isEqual(rewriteConfig, {})) { - return false; - } else { - throw new Error('[HPM] Invalid pathRewrite config. Expecting object with pathRewrite config or a rewrite function'); - } + return false + } else { + throw new Error(ERRORS.ERR_PATH_REWRITER_CONFIG) + } } -function parsePathRewriteRules(rewriteConfig) { - var rules = []; +function parsePathRewriteRules (rewriteConfig) { + var rules = [] - if (_.isPlainObject(rewriteConfig)) { - _.forIn(rewriteConfig, function(value, key) { - rules.push({ - regex: new RegExp(key), - value: rewriteConfig[key] - }); - logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, rewriteConfig[key]); - }); - } + if (_.isPlainObject(rewriteConfig)) { + _.forIn(rewriteConfig, function (value, key) { + rules.push({ + regex: new RegExp(key), + value: rewriteConfig[key] + }) + logger.info('[HPM] Proxy rewrite rule created: "%s" ~> "%s"', key, rewriteConfig[key]) + }) + } - return rules; + return rules } - diff --git a/lib/router.js b/lib/router.js index 37107dbd..e45562a0 100644 --- a/lib/router.js +++ b/lib/router.js @@ -1,53 +1,49 @@ -var _ = require('lodash'); -var logger = require('./logger.js').getInstance(); +var _ = require('lodash') +var logger = require('./logger.js').getInstance() module.exports = { - getTarget: getTarget -}; - -function getTarget(req, config) { - var newTarget; - var router = config.router; - - if (_.isPlainObject(router)) { - newTarget = getTargetFromProxyTable(req, router); - } else if (_.isFunction(router)) { - newTarget = router(req); - } - - return newTarget; + getTarget: getTarget } -function getTargetFromProxyTable(req, table) { - var result; - var host = req.headers.host; - var path = req.url; - - var hostAndPath = host + path; +function getTarget (req, config) { + var newTarget + var router = config.router - _.forIn(table, function(value, key) { - if (containsPath(key)) { + if (_.isPlainObject(router)) { + newTarget = getTargetFromProxyTable(req, router) + } else if (_.isFunction(router)) { + newTarget = router(req) + } - if (hostAndPath.indexOf(key) > -1) { // match 'localhost:3000/api' - result = table[key]; - logger.debug('[HPM] Router table match: "%s"', key); - return false; - } - } else { - - if (key === host) { // match 'localhost:3000' - result = table[key]; - logger.debug('[HPM] Router table match: "%s"', host); - return false; - } - - } + return newTarget +} - }); +function getTargetFromProxyTable (req, table) { + var result + var host = req.headers.host + var path = req.url + + var hostAndPath = host + path + + _.forIn(table, function (value, key) { + 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 + } + } else { + if (key === host) { // match 'localhost:3000' + result = table[key] + logger.debug('[HPM] Router table match: "%s"', host) + return false + } + } + }) - return result; + return result } -function containsPath(v) { - return v.indexOf('/') > -1; +function containsPath (v) { + return v.indexOf('/') > -1 } diff --git a/package.json b/package.json index 9e721043..f1ee3b26 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ ], "scripts": { "clean": "rm -rf coverage", - "test": "mocha --recursive --colors --reporter spec", + "lint": "node test/lint.js", + "test": "npm run lint && mocha --recursive --colors --reporter spec", "cover": "npm run clean && istanbul cover ./node_modules/mocha/bin/_mocha -- --recursive", "coveralls": "istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- --recursive --reporter spec && istanbul-coveralls && npm run clean" }, @@ -49,6 +50,7 @@ "mocha": "^3.2.0", "mocha-lcov-reporter": "1.2.0", "opn": "^4.0.2", + "standard": "^10.0.2", "ws": "^1.1.1" }, "dependencies": { @@ -56,5 +58,8 @@ "is-glob": "^3.1.0", "lodash": "^4.17.2", "micromatch": "^2.3.11" + }, + "standard": { + "env": [ "mocha" ] } } diff --git a/test/e2e/_utils.js b/test/e2e/_utils.js index 75f4e503..24f02a7f 100644 --- a/test/e2e/_utils.js +++ b/test/e2e/_utils.js @@ -1,21 +1,21 @@ -var express = require('express'); -var proxyMiddleware = require('../../index'); +var express = require('express') +var proxyMiddleware = require('../../index') module.exports = { - createServer: createServer, - proxyMiddleware: proxyMiddleware -}; + createServer: createServer, + proxyMiddleware: proxyMiddleware +} -function createServer(portNumber, middleware, path) { - var app = express(); +function createServer (portNumber, middleware, path) { + var app = express() - if (middleware, path) { - app.use(path, middleware); - } else if (middleware) { - app.use(middleware); - } + if (middleware && path) { + app.use(path, middleware) + } else if (middleware) { + app.use(middleware) + } - var server = app.listen(portNumber); + var server = app.listen(portNumber) - return server; + return server } diff --git a/test/e2e/express-router.spec.js b/test/e2e/express-router.spec.js index 65e3b065..79576703 100644 --- a/test/e2e/express-router.spec.js +++ b/test/e2e/express-router.spec.js @@ -1,67 +1,62 @@ -var express = require('express'); -var expect = require('chai').expect; -var http = require('http'); -var proxy = require('../../index'); - -describe('Usage in Express', function() { - - var app; - var server; - - beforeEach(function() { - app = express(); - }); - - afterEach(function() { - server && server.close(); - }); - - // https://github.com/chimurai/http-proxy-middleware/issues/94 - describe('Express Sub Route', function() { - - beforeEach(function() { - - // sub route config - var sub = new express.Router(); - - function filter(pathname, req) { - var urlFilter = new RegExp('^/sub/api'); - var match = urlFilter.test(pathname); - return match; - } - - /** - * Mount proxy without 'path' in sub route - */ - var proxyConfig = {target: 'http://jsonplaceholder.typicode.com', changeOrigin: true, logLevel: 'silent'}; - sub.use(proxy(filter, proxyConfig)); - - sub.get('/hello', jsonMiddleware({'content': 'foobar'})); - - // configure sub route on /sub junction - app.use('/sub', sub); - - // start server - server = app.listen(3000); - }); - - it('should still return a response when route does not match proxyConfig', function(done) { - var responseBody; - http.get('http://localhost:3000/sub/hello', function(res) { - res.on('data', function(chunk) { - responseBody = chunk.toString(); - expect(responseBody).to.equal('{"content":"foobar"}'); - done(); - }); - }); - }); - - }); - - function jsonMiddleware(data) { - return function(req, res) { - res.json(data); - }; +var express = require('express') +var expect = require('chai').expect +var http = require('http') +var proxy = require('../../index') + +describe('Usage in Express', function () { + var app + var server + + beforeEach(function () { + app = express() + }) + + afterEach(function () { + server && server.close() + }) + + // https://github.com/chimurai/http-proxy-middleware/issues/94 + describe('Express Sub Route', function () { + beforeEach(function () { + // sub route config + var sub = new express.Router() + + function filter (pathname, req) { + var urlFilter = new RegExp('^/sub/api') + var match = urlFilter.test(pathname) + return match + } + + /** + * Mount proxy without 'path' in sub route + */ + var proxyConfig = {target: 'http://jsonplaceholder.typicode.com', changeOrigin: true, logLevel: 'silent'} + sub.use(proxy(filter, proxyConfig)) + + sub.get('/hello', jsonMiddleware({'content': 'foobar'})) + + // configure sub route on /sub junction + app.use('/sub', sub) + + // start server + server = app.listen(3000) + }) + + it('should still return a response when route does not match proxyConfig', function (done) { + var responseBody + http.get('http://localhost:3000/sub/hello', function (res) { + res.on('data', function (chunk) { + responseBody = chunk.toString() + expect(responseBody).to.equal('{"content":"foobar"}') + done() + }) + }) + }) + }) + + function jsonMiddleware (data) { + return function (req, res) { + res.json(data) } - -}); + } +}) diff --git a/test/e2e/http-proxy-middleware.spec.js b/test/e2e/http-proxy-middleware.spec.js index c00f29aa..3285e820 100644 --- a/test/e2e/http-proxy-middleware.spec.js +++ b/test/e2e/http-proxy-middleware.spec.js @@ -1,672 +1,660 @@ -var utils = require('./_utils'); -var expect = require('chai').expect; -var http = require('http'); - -describe('E2E http-proxy-middleware', function() { - var createServer; - var proxyMiddleware; - - beforeEach(function() { - createServer = utils.createServer; - proxyMiddleware = utils.proxyMiddleware; - }); - - describe('http-proxy-middleware creation', function() { - it('should create a middleware', function() { - var middleware; - middleware = proxyMiddleware('/api', {target: 'http://localhost:8000'}); - expect(middleware).to.be.a('function'); - }); - }); - - describe('context matching', function() { - describe('do not proxy', function() { - var isSkipped; - - beforeEach(function() { - isSkipped = false; - - var middleware; - - var mockReq = {url: '/foo/bar', originalUrl: '/foo/bar'}; - var mockRes = {}; - var mockNext = function() { - // mockNext will be called when request is not proxied - isSkipped = true; - }; - - middleware = proxyMiddleware('/api', {target: 'http://localhost:8000'}); - middleware(mockReq, mockRes, mockNext); - }); - - it('should not proxy requests when request url does not match context' , function() { - expect(isSkipped).to.be.true; - }); - - }); - }); - - describe('http-proxy-middleware in actual server', function() { - - describe('basic setup, requests to target', function() { - var proxyServer, targetServer; - var targetHeaders; - var targetUrl; - var responseBody; - - beforeEach(function(done) { - var mw_proxy = proxyMiddleware('/api', {target: 'http://localhost:8000'}); - - var mw_target = function(req, res, next) { - targetUrl = req.url; // store target url. - targetHeaders = req.headers; // store target headers. - res.write('HELLO WEB'); // respond with 'HELLO WEB' - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/b/c/d;p?q=1&r=[2,3]#s"', function(res) { - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should have the same headers.host value', function() { - expect(targetHeaders.host).to.equal('localhost:3000'); - }); - - it('should have proxied the uri-path and uri-query, but not the uri-hash', function() { - expect(targetUrl).to.equal('/api/b/c/d;p?q=1&r=[2,3]'); - }); - - it('should have response body: "HELLO WEB"', function() { - expect(responseBody).to.equal('HELLO WEB'); - }); - }); - - describe('custom context matcher/filter', function() { - var proxyServer, targetServer; - var targetHeaders; - var targetUrl; - var responseBody; - - var filterPath, filterReq; - - beforeEach(function(done) { - var filter = function(path, req) { - filterPath = path; - filterReq = req; - return true; - }; - - var mw_proxy = proxyMiddleware(filter, {target: 'http://localhost:8000'}); - - var mw_target = function(req, res, next) { - targetUrl = req.url; // store target url. - targetHeaders = req.headers; // store target headers. - res.write('HELLO WEB'); // respond with 'HELLO WEB' - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/b/c/d', function(res) { - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should have response body: "HELLO WEB"', function() { - expect(responseBody).to.equal('HELLO WEB'); - }); - - it('should provide the url path in the first argument', function() { - expect(filterPath).to.equal('/api/b/c/d'); - }); - - it('should provide the req object in the second argument', function() { - expect(filterReq.method).to.equal('GET'); - }); - }); - - describe('multi path', function() { - var proxyServer, targetServer; - var targetHeaders; - var response, responseBody; - - beforeEach(function() { - var mw_proxy = proxyMiddleware(['/api', '/ajax'], {target: 'http://localhost:8000'}); - - var mw_target = function(req, res, next) { - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - describe('request to path A, configured', function() { - beforeEach(function(done) { - http.get('http://localhost:3000/api/some/endpoint', function(res) { - response = res; - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - it('should proxy to path A', function() { - expect(response.statusCode).to.equal(200); - expect(responseBody).to.equal('/api/some/endpoint'); - }); - }); - - describe('request to path B, configured', function() { - beforeEach(function(done) { - http.get('http://localhost:3000/ajax/some/library', function(res) { - response = res; - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - it('should proxy to path B', function() { - expect(response.statusCode).to.equal(200); - expect(responseBody).to.equal('/ajax/some/library'); - }); - }); - - describe('request to path C, not configured', function() { - beforeEach(function(done) { - http.get('http://localhost:3000/lorum/ipsum', function(res) { - response = res; - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - it('should not proxy to this path', function() { - expect(response.statusCode).to.equal(404); - }); - }); - - }); - - describe('wildcard path matching', function() { - var proxyServer, targetServer; - var targetHeaders; - var response, responseBody; - - beforeEach(function() { - var mw_proxy = proxyMiddleware('/api/**', {target: 'http://localhost:8000'}); - - var mw_target = function(req, res, next) { - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - }); - - beforeEach(function(done) { - http.get('http://localhost:3000/api/some/endpoint', function(res) { - response = res; - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should proxy to path', function() { - expect(response.statusCode).to.equal(200); - expect(responseBody).to.equal('/api/some/endpoint'); - }); - }); - - describe('multi glob wildcard path matching', function() { - var proxyServer, targetServer; - var targetHeaders; - var responseA, responseBodyA; - var responseB, responseBodyB; - - beforeEach(function() { - var mw_proxy = proxyMiddleware(['/**.html', '!**.json'], {target: 'http://localhost:8000'}); - - var mw_target = function(req, res, next) { - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - }); - - beforeEach(function(done) { - http.get('http://localhost:3000/api/some/endpoint/index.html', function(res) { - responseA = res; - res.on('data', function(chunk) { - responseBodyA = chunk.toString(); - done(); - }); - }); - }); - - beforeEach(function(done) { - http.get('http://localhost:3000/api/some/endpoint/data.json', function(res) { - responseB = res; - res.on('data', function(chunk) { - responseBodyB = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should proxy to paths ending with *.html', function() { - expect(responseA.statusCode).to.equal(200); - expect(responseBodyA).to.equal('/api/some/endpoint/index.html'); - }); - - it('should not proxy to paths ending with *.json', function() { - expect(responseB.statusCode).to.equal(404); - }); - }); - - describe('option.headers - additional request headers', function() { - var proxyServer, targetServer; - var targetHeaders; - - beforeEach(function(done) { - var mw_proxy = proxyMiddleware('/api', {target: 'http://localhost:8000', headers: {host: 'foobar.dev'}}); - - var mw_target = function(req, res, next) { - targetHeaders = req.headers; - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/', function(res) { - done(); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should send request header "host" to target server', function() { - expect(targetHeaders.host).to.equal('foobar.dev'); - }); - }); - - describe('legacy option.proxyHost', function() { - var proxyServer, targetServer; - var targetHeaders; - - beforeEach(function(done) { - var mw_proxy = proxyMiddleware('/api', {target: 'http://localhost:8000', proxyHost: 'foobar.dev'}); - - var mw_target = function(req, res, next) { - targetHeaders = req.headers; - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/', function(res) { - done(); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should proxy host header to target server', function() { - expect(targetHeaders.host).to.equal('foobar.dev'); - }); - }); - - describe('option.onError - Error handling', function() { - var proxyServer, targetServer; - var response, responseBody; - - describe('default', function() { - beforeEach(function(done) { - var mw_proxy = proxyMiddleware('/api', {target: 'http://localhost:666'}); // unreachable host on port:666 - var mw_target = function(req, res, next) {next();}; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/', function(res) { - response = res; - done(); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should handle errors when host is not reachable', function() { - expect(response.statusCode).to.equal(504); - }); - }); - - describe('custom', function() { - beforeEach(function(done) { - var customOnError = function(err, req, res) { - res.writeHead(418); // different error code - res.end('I\'m a teapot'); // no response body - }; - - var mw_proxy = proxyMiddleware('/api', {target: 'http://localhost:666', onError: customOnError}); // unreachable host on port:666 - var mw_target = function(req, res, next) {next();}; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/', function(res) { - response = res; - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should respond with custom http status code', function() { - expect(response.statusCode).to.equal(418); - }); - - it('should respond with custom status message', function() { - expect(responseBody).to.equal('I\'m a teapot'); - }); - }); - }); - - describe('option.onProxyRes', function() { - var proxyServer, targetServer; - var response, responseBody; - - beforeEach(function(done) { - var fnOnProxyRes = function(proxyRes, req, res) { - proxyRes.headers['x-added'] = 'foobar'; // add custom header to response - delete proxyRes.headers['x-removed']; - }; - - var mw_proxy = proxyMiddleware('/api', { - target: 'http://localhost:8000', - onProxyRes: fnOnProxyRes - }); - var mw_target = function(req, res, next) { - res.setHeader('x-removed', 'remove-header'); - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/foo/bar', function(res) { - response = res; - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should add `x-added` as custom header to response"', function() { - expect(response.headers['x-added']).to.equal('foobar'); - }); - - it('should remove `x-removed` field from response header"', function() { - expect(response.headers['x-removed']).to.equal(undefined); - }); - }); - - describe('option.onProxyReq', function() { - var proxyServer, targetServer; - var receivedRequest; - - beforeEach(function(done) { - var fnOnProxyReq = function(proxyReq, req, res) { - proxyReq.setHeader('x-added', 'foobar'); // add custom header to request - }; - - var mw_proxy = proxyMiddleware('/api', { - target: 'http://localhost:8000', - onProxyReq: fnOnProxyReq - }); - - var mw_target = function(req, res, next) { - receivedRequest = req; - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/foo/bar', function() { - done(); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should add `x-added` as custom header to request"', function() { - expect(receivedRequest.headers['x-added']).to.equal('foobar'); - }); - }); - - describe('option.pathRewrite', function() { - var proxyServer, targetServer; - var responseBody; - - beforeEach(function(done) { - var mw_proxy = proxyMiddleware('/api', { - target: 'http://localhost:8000', - pathRewrite: { - '^/api': '/rest', - '^/remove': '' - } - }); - var mw_target = function(req, res, next) { - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/foo/bar', function(res) { - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should have rewritten path from "/api/foo/bar" to "/rest/foo/bar"', function() { - expect(responseBody).to.equal('/rest/foo/bar'); - }); - }); - - describe('shorthand usage', function() { - var proxyServer, targetServer; - var responseBody; - - beforeEach(function(done) { - var mw_proxy = proxyMiddleware('http://localhost:8000/api'); - var mw_target = function(req, res, next) { - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/foo/bar', function(res) { - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should have proxy with shorthand configuration', function() { - expect(responseBody).to.equal('/api/foo/bar'); - }); - }); - - describe('express with path + proxy', function() { - var proxyServer, targetServer; - var responseBody; - - beforeEach(function(done) { - var mw_proxy = proxyMiddleware('http://localhost:8000'); - var mw_target = function(req, res, next) { - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy, '/api'); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/foo/bar', function(res) { - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should proxy to target with the baseUrl', function() { - expect(responseBody).to.equal('/api/foo/bar'); - }); - - }); - - describe('option.logLevel & option.logProvider', function() { - var proxyServer, targetServer; - var responseBody; - var logMessage; - - beforeEach(function(done) { - var customLogger = function(message) { - logMessage = message; - }; - - var mw_proxy = proxyMiddleware('http://localhost:8000/api', { - logLevel: 'info', - logProvider: function(provider) { - provider.debug = customLogger; - provider.info = customLogger; - return provider; - } - }); - var mw_target = function(req, res, next) { - res.write(req.url); // respond with req.url - res.end(); - }; - - proxyServer = createServer(3000, mw_proxy); - targetServer = createServer(8000, mw_target); - - http.get('http://localhost:3000/api/foo/bar', function(res) { - res.on('data', function(chunk) { - responseBody = chunk.toString(); - done(); - }); - }); - }); - - afterEach(function() { - proxyServer.close(); - targetServer.close(); - }); - - it('should have logged messages', function() { - expect(logMessage).not.to.equal(undefined); - }); - }); - - }); -}); - +/* eslint-disable no-unused-expressions */ +// https://github.com/feross/standard/issues/690#issuecomment-278533482 + +var utils = require('./_utils') +var expect = require('chai').expect +var http = require('http') + +describe('E2E http-proxy-middleware', function () { + var createServer + var proxyMiddleware + + beforeEach(function () { + createServer = utils.createServer + proxyMiddleware = utils.proxyMiddleware + }) + + describe('http-proxy-middleware creation', function () { + it('should create a middleware', function () { + var middleware + middleware = proxyMiddleware('/api', {target: 'http://localhost:8000'}) + expect(middleware).to.be.a('function') + }) + }) + + describe('context matching', function () { + describe('do not proxy', function () { + var isSkipped + + beforeEach(function () { + isSkipped = false + + var middleware + + var mockReq = {url: '/foo/bar', originalUrl: '/foo/bar'} + var mockRes = {} + var mockNext = function () { + // mockNext will be called when request is not proxied + isSkipped = true + } + + middleware = proxyMiddleware('/api', {target: 'http://localhost:8000'}) + middleware(mockReq, mockRes, mockNext) + }) + + it('should not proxy requests when request url does not match context', function () { + expect(isSkipped).to.be.true + }) + }) + }) + + describe('http-proxy-middleware in actual server', function () { + describe('basic setup, requests to target', function () { + var proxyServer, targetServer + var targetHeaders + var targetUrl + var responseBody + + beforeEach(function (done) { + var mwProxy = proxyMiddleware('/api', {target: 'http://localhost:8000'}) + + var mwTarget = function (req, res, next) { + targetUrl = req.url // store target url. + targetHeaders = req.headers // store target headers. + res.write('HELLO WEB') // respond with 'HELLO WEB' + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/b/c/dp?q=1&r=[2,3]#s"', function (res) { + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should have the same headers.host value', function () { + expect(targetHeaders.host).to.equal('localhost:3000') + }) + + it('should have proxied the uri-path and uri-query, but not the uri-hash', function () { + expect(targetUrl).to.equal('/api/b/c/dp?q=1&r=[2,3]') + }) + + it('should have response body: "HELLO WEB"', function () { + expect(responseBody).to.equal('HELLO WEB') + }) + }) + + describe('custom context matcher/filter', function () { + var proxyServer, targetServer + var responseBody + + var filterPath, filterReq + + beforeEach(function (done) { + var filter = function (path, req) { + filterPath = path + filterReq = req + return true + } + + var mwProxy = proxyMiddleware(filter, {target: 'http://localhost:8000'}) + + var mwTarget = function (req, res, next) { + res.write('HELLO WEB') // respond with 'HELLO WEB' + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/b/c/d', function (res) { + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should have response body: "HELLO WEB"', function () { + expect(responseBody).to.equal('HELLO WEB') + }) + + it('should provide the url path in the first argument', function () { + expect(filterPath).to.equal('/api/b/c/d') + }) + + it('should provide the req object in the second argument', function () { + expect(filterReq.method).to.equal('GET') + }) + }) + + describe('multi path', function () { + var proxyServer, targetServer + var response, responseBody + + beforeEach(function () { + var mwProxy = proxyMiddleware(['/api', '/ajax'], {target: 'http://localhost:8000'}) + + var mwTarget = function (req, res, next) { + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + describe('request to path A, configured', function () { + beforeEach(function (done) { + http.get('http://localhost:3000/api/some/endpoint', function (res) { + response = res + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + it('should proxy to path A', function () { + expect(response.statusCode).to.equal(200) + expect(responseBody).to.equal('/api/some/endpoint') + }) + }) + + describe('request to path B, configured', function () { + beforeEach(function (done) { + http.get('http://localhost:3000/ajax/some/library', function (res) { + response = res + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + it('should proxy to path B', function () { + expect(response.statusCode).to.equal(200) + expect(responseBody).to.equal('/ajax/some/library') + }) + }) + + describe('request to path C, not configured', function () { + beforeEach(function (done) { + http.get('http://localhost:3000/lorum/ipsum', function (res) { + response = res + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + it('should not proxy to this path', function () { + expect(response.statusCode).to.equal(404) + }) + }) + }) + + describe('wildcard path matching', function () { + var proxyServer, targetServer + var response, responseBody + + beforeEach(function () { + var mwProxy = proxyMiddleware('/api/**', {target: 'http://localhost:8000'}) + + var mwTarget = function (req, res, next) { + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + }) + + beforeEach(function (done) { + http.get('http://localhost:3000/api/some/endpoint', function (res) { + response = res + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should proxy to path', function () { + expect(response.statusCode).to.equal(200) + expect(responseBody).to.equal('/api/some/endpoint') + }) + }) + + describe('multi glob wildcard path matching', function () { + var proxyServer, targetServer + var responseA, responseBodyA + var responseB + + beforeEach(function () { + var mwProxy = proxyMiddleware(['/**.html', '!**.json'], {target: 'http://localhost:8000'}) + + var mwTarget = function (req, res, next) { + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + }) + + beforeEach(function (done) { + http.get('http://localhost:3000/api/some/endpoint/index.html', function (res) { + responseA = res + res.on('data', function (chunk) { + responseBodyA = chunk.toString() + done() + }) + }) + }) + + beforeEach(function (done) { + http.get('http://localhost:3000/api/some/endpoint/data.json', function (res) { + responseB = res + res.on('data', function (chunk) { + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should proxy to paths ending with *.html', function () { + expect(responseA.statusCode).to.equal(200) + expect(responseBodyA).to.equal('/api/some/endpoint/index.html') + }) + + it('should not proxy to paths ending with *.json', function () { + expect(responseB.statusCode).to.equal(404) + }) + }) + + describe('option.headers - additional request headers', function () { + var proxyServer, targetServer + var targetHeaders + + beforeEach(function (done) { + var mwProxy = proxyMiddleware('/api', {target: 'http://localhost:8000', headers: {host: 'foobar.dev'}}) + + var mwTarget = function (req, res, next) { + targetHeaders = req.headers + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/', function (res) { + done() + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should send request header "host" to target server', function () { + expect(targetHeaders.host).to.equal('foobar.dev') + }) + }) + + describe('legacy option.proxyHost', function () { + var proxyServer, targetServer + var targetHeaders + + beforeEach(function (done) { + var mwProxy = proxyMiddleware('/api', {target: 'http://localhost:8000', proxyHost: 'foobar.dev'}) + + var mwTarget = function (req, res, next) { + targetHeaders = req.headers + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/', function (res) { + done() + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should proxy host header to target server', function () { + expect(targetHeaders.host).to.equal('foobar.dev') + }) + }) + + describe('option.onError - Error handling', function () { + var proxyServer, targetServer + var response, responseBody + + describe('default', function () { + beforeEach(function (done) { + var mwProxy = proxyMiddleware('/api', {target: 'http://localhost:666'}) // unreachable host on port:666 + var mwTarget = function (req, res, next) { next() } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/', function (res) { + response = res + done() + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should handle errors when host is not reachable', function () { + expect(response.statusCode).to.equal(504) + }) + }) + + describe('custom', function () { + beforeEach(function (done) { + var customOnError = function (err, req, res) { + if (err) { + res.writeHead(418) // different error code + res.end('I\'m a teapot') // no response body + } + } + + var mwProxy = proxyMiddleware('/api', {target: 'http://localhost:666', onError: customOnError}) // unreachable host on port:666 + var mwTarget = function (req, res, next) { next() } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/', function (res) { + response = res + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should respond with custom http status code', function () { + expect(response.statusCode).to.equal(418) + }) + + it('should respond with custom status message', function () { + expect(responseBody).to.equal('I\'m a teapot') + }) + }) + }) + + describe('option.onProxyRes', function () { + var proxyServer, targetServer + var response + + beforeEach(function (done) { + var fnOnProxyRes = function (proxyRes, req, res) { + proxyRes.headers['x-added'] = 'foobar' // add custom header to response + delete proxyRes.headers['x-removed'] + } + + var mwProxy = proxyMiddleware('/api', { + target: 'http://localhost:8000', + onProxyRes: fnOnProxyRes + }) + var mwTarget = function (req, res, next) { + res.setHeader('x-removed', 'remove-header') + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/foo/bar', function (res) { + response = res + res.on('data', function (chunk) { + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should add `x-added` as custom header to response"', function () { + expect(response.headers['x-added']).to.equal('foobar') + }) + + it('should remove `x-removed` field from response header"', function () { + expect(response.headers['x-removed']).to.equal(undefined) + }) + }) + + describe('option.onProxyReq', function () { + var proxyServer, targetServer + var receivedRequest + + beforeEach(function (done) { + var fnOnProxyReq = function (proxyReq, req, res) { + proxyReq.setHeader('x-added', 'foobar') // add custom header to request + } + + var mwProxy = proxyMiddleware('/api', { + target: 'http://localhost:8000', + onProxyReq: fnOnProxyReq + }) + + var mwTarget = function (req, res, next) { + receivedRequest = req + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/foo/bar', function () { + done() + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should add `x-added` as custom header to request"', function () { + expect(receivedRequest.headers['x-added']).to.equal('foobar') + }) + }) + + describe('option.pathRewrite', function () { + var proxyServer, targetServer + var responseBody + + beforeEach(function (done) { + var mwProxy = proxyMiddleware('/api', { + target: 'http://localhost:8000', + pathRewrite: { + '^/api': '/rest', + '^/remove': '' + } + }) + var mwTarget = function (req, res, next) { + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/foo/bar', function (res) { + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should have rewritten path from "/api/foo/bar" to "/rest/foo/bar"', function () { + expect(responseBody).to.equal('/rest/foo/bar') + }) + }) + + describe('shorthand usage', function () { + var proxyServer, targetServer + var responseBody + + beforeEach(function (done) { + var mwProxy = proxyMiddleware('http://localhost:8000/api') + var mwTarget = function (req, res, next) { + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/foo/bar', function (res) { + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should have proxy with shorthand configuration', function () { + expect(responseBody).to.equal('/api/foo/bar') + }) + }) + + describe('express with path + proxy', function () { + var proxyServer, targetServer + var responseBody + + beforeEach(function (done) { + var mwProxy = proxyMiddleware('http://localhost:8000') + var mwTarget = function (req, res, next) { + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy, '/api') + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/foo/bar', function (res) { + res.on('data', function (chunk) { + responseBody = chunk.toString() + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should proxy to target with the baseUrl', function () { + expect(responseBody).to.equal('/api/foo/bar') + }) + }) + + describe('option.logLevel & option.logProvider', function () { + var proxyServer, targetServer + var logMessage + + beforeEach(function (done) { + var customLogger = function (message) { + logMessage = message + } + + var mwProxy = proxyMiddleware('http://localhost:8000/api', { + logLevel: 'info', + logProvider: function (provider) { + provider.debug = customLogger + provider.info = customLogger + return provider + } + }) + var mwTarget = function (req, res, next) { + res.write(req.url) // respond with req.url + res.end() + } + + proxyServer = createServer(3000, mwProxy) + targetServer = createServer(8000, mwTarget) + + http.get('http://localhost:3000/api/foo/bar', function (res) { + res.on('data', function (chunk) { + done() + }) + }) + }) + + afterEach(function () { + proxyServer.close() + targetServer.close() + }) + + it('should have logged messages', function () { + expect(logMessage).not.to.equal(undefined) + }) + }) + }) +}) diff --git a/test/e2e/path-rewriter.spec.js b/test/e2e/path-rewriter.spec.js index 5533f8fb..16195b90 100644 --- a/test/e2e/path-rewriter.spec.js +++ b/test/e2e/path-rewriter.spec.js @@ -1,98 +1,98 @@ -var utils = require('./_utils'); -var expect = require('chai').expect; -var http = require('http'); - -describe('E2E pathRewrite', function() { - var createServer; - var proxyMiddleware; - - beforeEach(function() { - createServer = utils.createServer; - proxyMiddleware = utils.proxyMiddleware; - }); - - var targetMiddleware; - var targetData; - - beforeEach(function() { - targetData = {}; - targetMiddleware = function(req, res, next) { - targetData.url = req.url; // store target url. - targetData.headers = req.headers; // store target headers. - res.write(req.url); // respond with target url. - res.end(); - }; - }); - - var proxyServer; - var targetServer; - - beforeEach(function() { - targetServer = createServer(8000, targetMiddleware); - }); - - afterEach(function() { - proxyServer && proxyServer.close(); - targetServer.close(); - }); - - describe('Rewrite paths with rules table', function() { - beforeEach(function() { - var proxyConfig = { - target: 'http://localhost:8000', - pathRewrite: { - '^/foobar/api/': '/api/' - } - }; - var proxy = proxyMiddleware(proxyConfig); - proxyServer = createServer(3000, proxy); - }); - - beforeEach(function(done) { - http.get('http://localhost:3000/foobar/api/lorum/ipsum', function(res) { - done(); - }); - }); - - it('should remove "/foobar" from path', function() { - expect(targetData.url).to.equal('/api/lorum/ipsum'); - }); - }); - - describe('Rewrite paths with function', function() { - var originalPath; - var pathRewriteReqObject; - - beforeEach(function() { - var proxyConfig = { - target: 'http://localhost:8000', - pathRewrite: function(path, req) { - originalPath = path; - pathRewriteReqObject = req; - return path.replace('/foobar', ''); - } - }; - var proxy = proxyMiddleware(proxyConfig); - proxyServer = createServer(3000, proxy); - }); - - beforeEach(function(done) { - http.get('http://localhost:3000/foobar/api/lorum/ipsum', function(res) { - done(); - }); - }); - - it('should remove "/foobar" from path', function() { - expect(targetData.url).to.equal('/api/lorum/ipsum'); - }); - - it('should provide the `path` parameter with the unmodified path value', function() { - expect(originalPath).to.equal('/foobar/api/lorum/ipsum'); - }); - - it('should provide the `req` object as second parameter of the rewrite function', function() { - expect(pathRewriteReqObject.method).to.equal('GET'); - expect(pathRewriteReqObject.url).to.equal('/api/lorum/ipsum'); - }); - }); -}); +var utils = require('./_utils') +var expect = require('chai').expect +var http = require('http') + +describe('E2E pathRewrite', function () { + var createServer + var proxyMiddleware + + beforeEach(function () { + createServer = utils.createServer + proxyMiddleware = utils.proxyMiddleware + }) + + var targetMiddleware + var targetData + + beforeEach(function () { + targetData = {} + targetMiddleware = function (req, res, next) { + targetData.url = req.url // store target url. + targetData.headers = req.headers // store target headers. + res.write(req.url) // respond with target url. + res.end() + } + }) + + var proxyServer + var targetServer + + beforeEach(function () { + targetServer = createServer(8000, targetMiddleware) + }) + + afterEach(function () { + proxyServer && proxyServer.close() + targetServer.close() + }) + + describe('Rewrite paths with rules table', function () { + beforeEach(function () { + var proxyConfig = { + target: 'http://localhost:8000', + pathRewrite: { + '^/foobar/api/': '/api/' + } + } + var proxy = proxyMiddleware(proxyConfig) + proxyServer = createServer(3000, proxy) + }) + + beforeEach(function (done) { + http.get('http://localhost:3000/foobar/api/lorum/ipsum', function (res) { + done() + }) + }) + + it('should remove "/foobar" from path', function () { + expect(targetData.url).to.equal('/api/lorum/ipsum') + }) + }) + + describe('Rewrite paths with function', function () { + var originalPath + var pathRewriteReqObject + + beforeEach(function () { + var proxyConfig = { + target: 'http://localhost:8000', + pathRewrite: function (path, req) { + originalPath = path + pathRewriteReqObject = req + return path.replace('/foobar', '') + } + } + var proxy = proxyMiddleware(proxyConfig) + proxyServer = createServer(3000, proxy) + }) + + beforeEach(function (done) { + http.get('http://localhost:3000/foobar/api/lorum/ipsum', function (res) { + done() + }) + }) + + it('should remove "/foobar" from path', function () { + expect(targetData.url).to.equal('/api/lorum/ipsum') + }) + + it('should provide the `path` parameter with the unmodified path value', function () { + expect(originalPath).to.equal('/foobar/api/lorum/ipsum') + }) + + it('should provide the `req` object as second parameter of the rewrite function', function () { + expect(pathRewriteReqObject.method).to.equal('GET') + expect(pathRewriteReqObject.url).to.equal('/api/lorum/ipsum') + }) + }) +}) diff --git a/test/e2e/router.spec.js b/test/e2e/router.spec.js index eb18e857..fec2a994 100644 --- a/test/e2e/router.spec.js +++ b/test/e2e/router.spec.js @@ -1,128 +1,125 @@ -var utils = require('./_utils'); -var expect = require('chai').expect; -var http = require('http'); - -describe('E2E router', function() { - var proxyServer, targetServerA, targetServerB, targetServerC; - var createServer; - var proxyMiddleware; - - beforeEach(function() { - createServer = utils.createServer; - proxyMiddleware = utils.proxyMiddleware; - }); - - beforeEach(function() { - targetServerA = createServer(6001, function(req, res, next) { - res.write('A'); - res.end(); - }); - - targetServerB = createServer(6002, function(req, res, next) { - res.write('B'); - res.end(); - }); - - targetServerC = createServer(6003, function(req, res, next) { - res.write('C'); - res.end(); - }); - }); - - afterEach(function() { - targetServerA.close(); - targetServerB.close(); - targetServerC.close(); - }); - - describe('router with proxyTable', function() { - beforeEach(function() { - proxyServer = createServer(6000, proxyMiddleware({ - target: 'http://localhost:6001', - router: function(req) { - return 'http://localhost:6003'; - } - })); - }); - - afterEach(function() { - proxyServer.close(); - }); - - it('should proxy to: "localhost:6003/api"', function(done) { - var options = {hostname: 'localhost', port: 6000, path: '/api'}; - http.get(options, function(res) { - res.on('data', function(chunk) { - var responseBody = chunk.toString(); - expect(responseBody).to.equal('C'); - done(); - }); - }); - }); - - }); - - describe('router with proxyTable', function() { - - beforeEach(function setupServers() { - proxyServer = createServer(6000, proxyMiddleware('/', { - target: 'http://localhost:6001', - router: { - 'alpha.localhost:6000': 'http://localhost:6001', - 'beta.localhost:6000': 'http://localhost:6002', - 'localhost:6000/api': 'http://localhost:6003' - } - })); - - }); - - afterEach(function() { - proxyServer.close(); - }); - - it('should proxy to option.target', function(done) { - http.get('http://localhost:6000', function(res) { - res.on('data', function(chunk) { - var responseBody = chunk.toString(); - expect(responseBody).to.equal('A'); - done(); - }); - }); - }); - - it('should proxy when host is "alpha.localhost"', function(done) { - var options = {hostname: 'localhost', port: 6000, path: '/'}; - options.headers = {host: 'alpha.localhost:6000'}; - http.get(options, function(res) { - res.on('data', function(chunk) { - var responseBody = chunk.toString(); - expect(responseBody).to.equal('A'); - done(); - }); - }); - }); - - it('should proxy when host is "beta.localhost"', function(done) { - var options = {hostname: 'localhost', port: 6000, path: '/'}; - options.headers = {host: 'beta.localhost:6000'}; - http.get(options, function(res) { - res.on('data', function(chunk) { - var responseBody = chunk.toString(); - expect(responseBody).to.equal('B'); - done(); - }); - }); - }); - - it('should proxy with host & path config: "localhost:6000/api"', function(done) { - var options = {hostname: 'localhost', port: 6000, path: '/api'}; - http.get(options, function(res) { - res.on('data', function(chunk) { - var responseBody = chunk.toString(); - expect(responseBody).to.equal('C'); - done(); - }); - }); - }); - }); -}); +var utils = require('./_utils') +var expect = require('chai').expect +var http = require('http') + +describe('E2E router', function () { + var proxyServer, targetServerA, targetServerB, targetServerC + var createServer + var proxyMiddleware + + beforeEach(function () { + createServer = utils.createServer + proxyMiddleware = utils.proxyMiddleware + }) + + beforeEach(function () { + targetServerA = createServer(6001, function (req, res, next) { + res.write('A') + res.end() + }) + + targetServerB = createServer(6002, function (req, res, next) { + res.write('B') + res.end() + }) + + targetServerC = createServer(6003, function (req, res, next) { + res.write('C') + res.end() + }) + }) + + afterEach(function () { + targetServerA.close() + targetServerB.close() + targetServerC.close() + }) + + describe('router with proxyTable', function () { + beforeEach(function () { + proxyServer = createServer(6000, proxyMiddleware({ + target: 'http://localhost:6001', + router: function (req) { + return 'http://localhost:6003' + } + })) + }) + + afterEach(function () { + proxyServer.close() + }) + + it('should proxy to: "localhost:6003/api"', function (done) { + var options = {hostname: 'localhost', port: 6000, path: '/api'} + http.get(options, function (res) { + res.on('data', function (chunk) { + var responseBody = chunk.toString() + expect(responseBody).to.equal('C') + done() + }) + }) + }) + }) + + describe('router with proxyTable', function () { + beforeEach(function setupServers () { + proxyServer = createServer(6000, proxyMiddleware('/', { + target: 'http://localhost:6001', + router: { + 'alpha.localhost:6000': 'http://localhost:6001', + 'beta.localhost:6000': 'http://localhost:6002', + 'localhost:6000/api': 'http://localhost:6003' + } + })) + }) + + afterEach(function () { + proxyServer.close() + }) + + it('should proxy to option.target', function (done) { + http.get('http://localhost:6000', function (res) { + res.on('data', function (chunk) { + var responseBody = chunk.toString() + expect(responseBody).to.equal('A') + done() + }) + }) + }) + + it('should proxy when host is "alpha.localhost"', function (done) { + var options = {hostname: 'localhost', port: 6000, path: '/'} + options.headers = {host: 'alpha.localhost:6000'} + http.get(options, function (res) { + res.on('data', function (chunk) { + var responseBody = chunk.toString() + expect(responseBody).to.equal('A') + done() + }) + }) + }) + + it('should proxy when host is "beta.localhost"', function (done) { + var options = {hostname: 'localhost', port: 6000, path: '/'} + options.headers = {host: 'beta.localhost:6000'} + http.get(options, function (res) { + res.on('data', function (chunk) { + var responseBody = chunk.toString() + expect(responseBody).to.equal('B') + done() + }) + }) + }) + + it('should proxy with host & path config: "localhost:6000/api"', function (done) { + var options = {hostname: 'localhost', port: 6000, path: '/api'} + http.get(options, function (res) { + res.on('data', function (chunk) { + var responseBody = chunk.toString() + expect(responseBody).to.equal('C') + done() + }) + }) + }) + }) +}) diff --git a/test/e2e/websocket.spec.js b/test/e2e/websocket.spec.js index e9bed0c9..2d1aaa68 100644 --- a/test/e2e/websocket.spec.js +++ b/test/e2e/websocket.spec.js @@ -1,150 +1,147 @@ -var utils = require('./_utils'); -var expect = require('chai').expect; -var http = require('http'); -var WebSocket = require('ws'); -var WebSocketServer = require('ws').Server; - -describe('E2E WebSocket proxy', function() { - var createServer; - var proxyMiddleware; - - beforeEach(function() { - createServer = utils.createServer; - proxyMiddleware = utils.proxyMiddleware; - }); - - var proxyServer, ws, wss; - var targetHeaders; - var responseMessage; - var proxy; - - beforeEach(function() { - proxy = proxyMiddleware('/', { - target: 'http://localhost:8000', - ws: true, - pathRewrite: {'^/socket': ''} - }); - - proxyServer = createServer(3000, proxy); - - wss = new WebSocketServer({port: 8000}); - - wss.on('connection', function connection(ws) { - ws.on('message', function incoming(message) { - ws.send(message); // echo received message - }); - }); - }); - - describe('option.ws', function() { - beforeEach(function(done) { - // need to make a normal http request, - // so http-proxy-middleware can catch the upgrade request - http.get('http://localhost:3000/', function() { - // do a second http request to make - // sure only 1 listener subscribes to upgrade request - http.get('http://localhost:3000/', function() { - ws = new WebSocket('ws://localhost:3000/socket'); - - ws.on('message', function incoming(message) { - responseMessage = message; - done(); - }); - - ws.on('open', function open() { - ws.send('foobar'); - }); - }); - }); - }); - - it('should proxy to path', function() { - expect(responseMessage).to.equal('foobar'); - }); - }); - - describe('option.ws with external server "upgrade"', function() { - beforeEach(function(done) { - proxyServer.on('upgrade', proxy.upgrade); - - ws = new WebSocket('ws://localhost:3000/socket'); - - ws.on('message', function incoming(message) { - responseMessage = message; - done(); - }); - - ws.on('open', function open() { - ws.send('foobar'); - }); - }); - - it('should proxy to path', function() { - expect(responseMessage).to.equal('foobar'); - }); - }); - - describe('option.ws with external server "upgrade" and shorthand usage', function() { - - beforeEach(function() { - proxyServer.close(); - // override - proxy = proxyMiddleware('ws://localhost:8000', {pathRewrite: {'^/socket': ''}}); - proxyServer = createServer(3000, proxy); - }); - - beforeEach(function(done) { - proxyServer.on('upgrade', proxy.upgrade); - - ws = new WebSocket('ws://localhost:3000/socket'); - - ws.on('message', function incoming(message) { - responseMessage = message; - done(); - }); - - ws.on('open', function open() { - ws.send('foobar'); - }); - }); - - it('should proxy to path', function() { - expect(responseMessage).to.equal('foobar'); - }); - }); - - describe('with router and pathRewrite', function() { - - beforeEach(function() { - proxyServer.close(); - // override - proxy = proxyMiddleware('ws://notworkinghost:6789', {router: {'/socket': 'ws://localhost:8000'}, pathRewrite: {'^/socket': ''}}); - proxyServer = createServer(3000, proxy); - }); - - beforeEach(function(done) { - proxyServer.on('upgrade', proxy.upgrade); - - ws = new WebSocket('ws://localhost:3000/socket'); - - ws.on('message', function incoming(message) { - responseMessage = message; - done(); - }); - - ws.on('open', function open() { - ws.send('foobar'); - }); - }); - - it('should proxy to path', function() { - expect(responseMessage).to.equal('foobar'); - }); - }); - - afterEach(function() { - proxyServer.close(); - wss.close(); - ws = null; - }); -}); +var utils = require('./_utils') +var expect = require('chai').expect +var http = require('http') +var WebSocket = require('ws') +var WebSocketServer = require('ws').Server + +describe('E2E WebSocket proxy', function () { + var createServer + var proxyMiddleware + + beforeEach(function () { + createServer = utils.createServer + proxyMiddleware = utils.proxyMiddleware + }) + + var proxyServer, ws, wss + var responseMessage + var proxy + + beforeEach(function () { + proxy = proxyMiddleware('/', { + target: 'http://localhost:8000', + ws: true, + pathRewrite: {'^/socket': ''} + }) + + proxyServer = createServer(3000, proxy) + + wss = new WebSocketServer({port: 8000}) + + wss.on('connection', function connection (ws) { + ws.on('message', function incoming (message) { + ws.send(message) // echo received message + }) + }) + }) + + describe('option.ws', function () { + beforeEach(function (done) { + // need to make a normal http request, + // so http-proxy-middleware can catch the upgrade request + http.get('http://localhost:3000/', function () { + // do a second http request to make + // sure only 1 listener subscribes to upgrade request + http.get('http://localhost:3000/', function () { + ws = new WebSocket('ws://localhost:3000/socket') + + ws.on('message', function incoming (message) { + responseMessage = message + done() + }) + + ws.on('open', function open () { + ws.send('foobar') + }) + }) + }) + }) + + it('should proxy to path', function () { + expect(responseMessage).to.equal('foobar') + }) + }) + + describe('option.ws with external server "upgrade"', function () { + beforeEach(function (done) { + proxyServer.on('upgrade', proxy.upgrade) + + ws = new WebSocket('ws://localhost:3000/socket') + + ws.on('message', function incoming (message) { + responseMessage = message + done() + }) + + ws.on('open', function open () { + ws.send('foobar') + }) + }) + + it('should proxy to path', function () { + expect(responseMessage).to.equal('foobar') + }) + }) + + describe('option.ws with external server "upgrade" and shorthand usage', function () { + beforeEach(function () { + proxyServer.close() + // override + proxy = proxyMiddleware('ws://localhost:8000', {pathRewrite: {'^/socket': ''}}) + proxyServer = createServer(3000, proxy) + }) + + beforeEach(function (done) { + proxyServer.on('upgrade', proxy.upgrade) + + ws = new WebSocket('ws://localhost:3000/socket') + + ws.on('message', function incoming (message) { + responseMessage = message + done() + }) + + ws.on('open', function open () { + ws.send('foobar') + }) + }) + + it('should proxy to path', function () { + expect(responseMessage).to.equal('foobar') + }) + }) + + describe('with router and pathRewrite', function () { + beforeEach(function () { + proxyServer.close() + // override + proxy = proxyMiddleware('ws://notworkinghost:6789', {router: {'/socket': 'ws://localhost:8000'}, pathRewrite: {'^/socket': ''}}) + proxyServer = createServer(3000, proxy) + }) + + beforeEach(function (done) { + proxyServer.on('upgrade', proxy.upgrade) + + ws = new WebSocket('ws://localhost:3000/socket') + + ws.on('message', function incoming (message) { + responseMessage = message + done() + }) + + ws.on('open', function open () { + ws.send('foobar') + }) + }) + + it('should proxy to path', function () { + expect(responseMessage).to.equal('foobar') + }) + }) + + afterEach(function () { + proxyServer.close() + wss.close() + ws = null + }) +}) diff --git a/test/lint.js b/test/lint.js new file mode 100644 index 00000000..f668e5e1 --- /dev/null +++ b/test/lint.js @@ -0,0 +1,19 @@ +// Skip StandardJS on older node versions: < node@4.0.0 +// https://travis-ci.org/chimurai/http-proxy-middleware/builds/212791414 + +var execSync = require('child_process').execSync +var command = 'standard -v' + +if (!process.mainModule.children.length) { // workaround? prevent duplicate linting... + if (isLegacyNodeJs()) { + console.log('StandardJS: Skipping StandardJS on older Node versions') + } else { + execSync(command, {stdio: [0, 1, 2]}) // https://stackoverflow.com/a/31104898/3841188 + } +} + +function isLegacyNodeJs () { + var majorVersion = parseInt(process.versions.node[0], 10) + var isModernNodeJs = (majorVersion > 0) + return !isModernNodeJs +} diff --git a/test/unit/_libs.js b/test/unit/_libs.js index a7e65726..625a3741 100644 --- a/test/unit/_libs.js +++ b/test/unit/_libs.js @@ -1,8 +1,8 @@ module.exports = { - configFactory: require('../../lib/config-factory'), - contextMatcher: require('../../lib/context-matcher'), - handlers: require('../../lib/handlers'), - Logger: require('../../lib/logger'), - pathRewriter: require('../../lib/path-rewriter'), - router: require('../../lib/router') -}; + configFactory: require('../../lib/config-factory'), + contextMatcher: require('../../lib/context-matcher'), + handlers: require('../../lib/handlers'), + Logger: require('../../lib/logger'), + pathRewriter: require('../../lib/path-rewriter'), + router: require('../../lib/router') +} diff --git a/test/unit/config-factory.spec.js b/test/unit/config-factory.spec.js index 04d9fecf..8adb2a27 100644 --- a/test/unit/config-factory.spec.js +++ b/test/unit/config-factory.spec.js @@ -1,154 +1,150 @@ -var expect = require('chai').expect; -var configFactory = require('./_libs').configFactory; - -describe('configFactory', function() { - var result; - var createConfig = configFactory.createConfig; - - describe('createConfig()', function() { - - describe('classic config', function() { - var context = '/api'; - var options = {target: 'http://www.example.org'}; - - beforeEach(function() { - result = createConfig(context, options); - }); - - it('should return config object', function() { - expect(result).to.have.all.keys('context', 'options'); - }); - - it('should return config object with context', function() { - expect(result.context).to.equal(context); - }); - - it('should return config object with options', function() { - expect(result.options).to.deep.equal(options); - }); - }); - - describe('shorthand String', function() { - describe('shorthand String config', function() { - beforeEach(function() { - result = createConfig('http://www.example.org:8000/api'); - }); - - it('should return config object', function() { - expect(result).to.have.all.keys('context', 'options'); - }); - - it('should return config object with context', function() { - expect(result.context).to.equal('/api'); - }); - - it('should return config object with options', function() { - expect(result.options).to.deep.equal({target: 'http://www.example.org:8000'}); - }); - }); - - describe('shorthand String config for whole domain', function() { - beforeEach(function() { - result = createConfig('http://www.example.org:8000'); - }); - - it('should return config object with context', function() { - expect(result.context).to.equal('/'); - }); - }); - - describe('shorthand String config for websocket url', function() { - beforeEach(function() { - result = createConfig('ws://www.example.org:8000'); - }); - - it('should return config object with context', function() { - expect(result.context).to.equal('/'); - }); - - it('should return options with ws = true', function() { - expect(result.options.ws).to.equal(true); - }); - }); - - describe('shorthand String config for secure websocket url', function() { - beforeEach(function() { - result = createConfig('wss://www.example.org:8000'); - }); - - it('should return config object with context', function() { - expect(result.context).to.equal('/'); - }); - - it('should return options with ws = true', function() { - expect(result.options.ws).to.equal(true); - }); - }); - - describe('shorthand String config with globbing', function() { - beforeEach(function() { - result = createConfig('http://www.example.org:8000/api/*.json'); - }); - - it('should return config object with context', function() { - expect(result.context).to.equal('/api/*.json'); - }); - }); - - describe('shorthand String config with options', function() { - beforeEach(function() { - result = createConfig('http://www.example.org:8000/api', {changeOrigin: true}); - }); - - it('should return config object with additional options', function() { - expect(result.options).to.deep.equal({target: 'http://www.example.org:8000', changeOrigin: true}); - }); - }); - }); - - describe('shorthand Object config', function() { - beforeEach(function() { - result = createConfig({target: 'http://www.example.org:8000'}); - }); - - it('should set the proxy path to everything', function() { - expect(result.context).to.equal('/'); - }); - - it('should return config object', function() { - expect(result.options).to.deep.equal({target: 'http://www.example.org:8000'}); - }); - }); - - describe('missing option.target', function() { - var fn; - beforeEach(function() { - fn = function() { - createConfig('/api'); - }; - }); - - it('should throw an error when target option is missing', function() { - expect(fn).to.throw(Error); - }); - }); - - describe('faulty config. mixing classic with shorthand', function() { - var fn; - beforeEach(function() { - result = createConfig('http://localhost:3000/api', {target: 'http://localhost:8000'}); - }); - - it('should use the target in the configuration as target', function() { - expect(result.options.target).to.equal('http://localhost:8000'); - }); - - it('should not use the host from the shorthand as target', function() { - expect(result.options.target).not.to.equal('http://localhost:3000'); - }); - }); - - }); - -}); - +var expect = require('chai').expect +var configFactory = require('./_libs').configFactory + +describe('configFactory', function () { + var result + var createConfig = configFactory.createConfig + + describe('createConfig()', function () { + describe('classic config', function () { + var context = '/api' + var options = {target: 'http://www.example.org'} + + beforeEach(function () { + result = createConfig(context, options) + }) + + it('should return config object', function () { + expect(result).to.have.all.keys('context', 'options') + }) + + it('should return config object with context', function () { + expect(result.context).to.equal(context) + }) + + it('should return config object with options', function () { + expect(result.options).to.deep.equal(options) + }) + }) + + describe('shorthand String', function () { + describe('shorthand String config', function () { + beforeEach(function () { + result = createConfig('http://www.example.org:8000/api') + }) + + it('should return config object', function () { + expect(result).to.have.all.keys('context', 'options') + }) + + it('should return config object with context', function () { + expect(result.context).to.equal('/api') + }) + + it('should return config object with options', function () { + expect(result.options).to.deep.equal({target: 'http://www.example.org:8000'}) + }) + }) + + describe('shorthand String config for whole domain', function () { + beforeEach(function () { + result = createConfig('http://www.example.org:8000') + }) + + it('should return config object with context', function () { + expect(result.context).to.equal('/') + }) + }) + + describe('shorthand String config for websocket url', function () { + beforeEach(function () { + result = createConfig('ws://www.example.org:8000') + }) + + it('should return config object with context', function () { + expect(result.context).to.equal('/') + }) + + it('should return options with ws = true', function () { + expect(result.options.ws).to.equal(true) + }) + }) + + describe('shorthand String config for secure websocket url', function () { + beforeEach(function () { + result = createConfig('wss://www.example.org:8000') + }) + + it('should return config object with context', function () { + expect(result.context).to.equal('/') + }) + + it('should return options with ws = true', function () { + expect(result.options.ws).to.equal(true) + }) + }) + + describe('shorthand String config with globbing', function () { + beforeEach(function () { + result = createConfig('http://www.example.org:8000/api/*.json') + }) + + it('should return config object with context', function () { + expect(result.context).to.equal('/api/*.json') + }) + }) + + describe('shorthand String config with options', function () { + beforeEach(function () { + result = createConfig('http://www.example.org:8000/api', {changeOrigin: true}) + }) + + it('should return config object with additional options', function () { + expect(result.options).to.deep.equal({target: 'http://www.example.org:8000', changeOrigin: true}) + }) + }) + }) + + describe('shorthand Object config', function () { + beforeEach(function () { + result = createConfig({target: 'http://www.example.org:8000'}) + }) + + it('should set the proxy path to everything', function () { + expect(result.context).to.equal('/') + }) + + it('should return config object', function () { + expect(result.options).to.deep.equal({target: 'http://www.example.org:8000'}) + }) + }) + + describe('missing option.target', function () { + var fn + + beforeEach(function () { + fn = function () { + createConfig('/api') + } + }) + + it('should throw an error when target option is missing', function () { + expect(fn).to.throw(Error) + }) + }) + + describe('faulty config. mixing classic with shorthand', function () { + beforeEach(function () { + result = createConfig('http://localhost:3000/api', {target: 'http://localhost:8000'}) + }) + + it('should use the target in the configuration as target', function () { + expect(result.options.target).to.equal('http://localhost:8000') + }) + + it('should not use the host from the shorthand as target', function () { + expect(result.options.target).not.to.equal('http://localhost:3000') + }) + }) + }) +}) diff --git a/test/unit/context-matcher.spec.js b/test/unit/context-matcher.spec.js index a152b9b3..3986c8a7 100644 --- a/test/unit/context-matcher.spec.js +++ b/test/unit/context-matcher.spec.js @@ -1,250 +1,249 @@ -var expect = require('chai').expect; -var contextMatcher = require('./_libs').contextMatcher; - -describe('Context Matching', function() { - - describe('String path matching', function() { - var result; - - describe('Single path matching', function() { - it('should match all paths', function() { - result = contextMatcher.match('', 'http://localhost/api/foo/bar'); - expect(result).to.be.true; - }); - - it('should match all paths starting with forward-slash', function() { - result = contextMatcher.match('/', 'http://localhost/api/foo/bar'); - expect(result).to.be.true; - }); - - it('should return true when the context is present in url', function() { - result = contextMatcher.match('/api', 'http://localhost/api/foo/bar'); - expect(result).to.be.true; - }); - - it('should return false when the context is not present in url', function() { - result = contextMatcher.match('/abc', 'http://localhost/api/foo/bar'); - expect(result).to.be.false; - }); - - it('should return false when the context is present half way in url', function() { - result = contextMatcher.match('/foo', 'http://localhost/api/foo/bar'); - expect(result).to.be.false; - }); - - it('should return false when the context does not start with /', function() { - result = contextMatcher.match('api', 'http://localhost/api/foo/bar'); - expect(result).to.be.false; - }); - }); - - describe('Multi path matching', function() { - it('should return true when the context is present in url', function() { - result = contextMatcher.match(['/api'], 'http://localhost/api/foo/bar'); - expect(result).to.be.true; - }); - - it('should return true when the context is present in url', function() { - result = contextMatcher.match(['/api', '/ajax'], 'http://localhost/ajax/foo/bar'); - expect(result).to.be.true; - }); - - it('should return false when the context does not match url', function() { - result = contextMatcher.match(['/api', '/ajax'], 'http://localhost/foo/bar'); - expect(result).to.be.false; - }); - - it('should return false when empty array provided', function() { - result = contextMatcher.match([], 'http://localhost/api/foo/bar'); - expect(result).to.be.false; - }); - }); - }); - - describe('Wildcard path matching', function() { - describe('Single glob', function() { - var url; - - beforeEach(function() { - url = 'http://localhost/api/foo/bar.html'; - }); - - describe('url-path matching', function() { - it('should match any path', function() { - expect(contextMatcher.match('**', url)).to.be.true; - expect(contextMatcher.match('/**', url)).to.be.true; - }); - - it('should only match paths starting with "/api" ', function() { - expect(contextMatcher.match('/api/**', url)).to.be.true; - expect(contextMatcher.match('/ajax/**', url)).to.be.false; - }); - - it('should only match paths starting with "foo" folder in it ', function() { - expect(contextMatcher.match('**/foo/**', url)).to.be.true; - expect(contextMatcher.match('**/invalid/**', url)).to.be.false; - }); - }); - - describe('file matching', function() { - it('should match any path, file and extension', function() { - expect(contextMatcher.match('**', url)).to.be.true; - expect(contextMatcher.match('**/*', url)).to.be.true; - expect(contextMatcher.match('**/*.*', url)).to.be.true; - expect(contextMatcher.match('/**', url)).to.be.true; - expect(contextMatcher.match('/**.*', url)).to.be.true; - expect(contextMatcher.match('/**/*', url)).to.be.true; - expect(contextMatcher.match('/**/*.*', url)).to.be.true; - }); - - it('should only match .html files', function() { - expect(contextMatcher.match('**/*.html', url)).to.be.true; - expect(contextMatcher.match('/**.html', url)).to.be.true; - expect(contextMatcher.match('/**/*.html', url)).to.be.true; - expect(contextMatcher.match('/**.htm', url)).to.be.false; - expect(contextMatcher.match('/**.jpg', url)).to.be.false; - }); - - it('should only match .html under root path', function() { - var pattern = '/*.html'; - expect(contextMatcher.match(pattern, 'http://localhost/index.html')).to.be.true; - expect(contextMatcher.match(pattern, 'http://localhost/some/path/index.html')).to.be.false; - }); - - it('should ignore query params', function() { - expect(contextMatcher.match('/**/*.php', 'http://localhost/a/b/c.php?d=e&e=f')).to.be.true; - expect(contextMatcher.match('/**/*.php?*', 'http://localhost/a/b/c.php?d=e&e=f')).to.be.false; - }); - - it('should only match any file in root path', function() { - expect(contextMatcher.match('/*', 'http://localhost/bar.html')).to.be.true; - expect(contextMatcher.match('/*.*', 'http://localhost/bar.html')).to.be.true; - expect(contextMatcher.match('/*', 'http://localhost/foo/bar.html')).to.be.false; - }); - - it('should only match .html file is in root path', function() { - expect(contextMatcher.match('/*.html', 'http://localhost/bar.html')).to.be.true; - expect(contextMatcher.match('/*.html', 'http://localhost/api/foo/bar.html')).to.be.false; - }); - - it('should only match .html files in "foo" folder', function() { - expect(contextMatcher.match('**/foo/*.html', url)).to.be.true; - expect(contextMatcher.match('**/bar/*.html', url)).to.be.false; - }); - - it('should not match .html files', function() { - expect(contextMatcher.match('!**/*.html', url)).to.be.false; - }); - }); - }); - - describe('Multi glob matching', function() { - - describe('Multiple patterns', function() { - it('should return true when both path patterns match', function() { - var pattern = ['/api/**','/ajax/**']; - expect(contextMatcher.match(pattern, 'http://localhost/api/foo/bar.json')).to.be.true; - expect(contextMatcher.match(pattern, 'http://localhost/ajax/foo/bar.json')).to.be.true; - expect(contextMatcher.match(pattern, 'http://localhost/rest/foo/bar.json')).to.be.false; - }); - it('should return true when both file extensions pattern match', function() { - var pattern = ['/**.html','/**.jpeg']; - expect(contextMatcher.match(pattern, 'http://localhost/api/foo/bar.html')).to.be.true; - expect(contextMatcher.match(pattern, 'http://localhost/api/foo/bar.jpeg')).to.be.true; - expect(contextMatcher.match(pattern, 'http://localhost/api/foo/bar.gif')).to.be.false; - }); - }); - - describe('Negation patterns', function() { - it('should not match file extension', function() { - var url = 'http://localhost/api/foo/bar.html'; - expect(contextMatcher.match(['**', '!**/*.html'], url)).to.be.false; - expect(contextMatcher.match(['**', '!**/*.json'], url)).to.be.true; - }); - }); - }); - }); - - describe('Use function for matching', function() { - testFunctionAsContext = function(val) { - return contextMatcher.match(fn, 'http://localhost/api/foo/bar'); - - function fn(path, req) { - return val; - }; - }; - - describe('truthy', function() { - it('should match when function returns true', function() { - expect(testFunctionAsContext(true)).to.be.ok; - expect(testFunctionAsContext('true')).to.be.ok; - }); - }); - - describe('falsy', function() { - it('should not match when function returns falsy value', function() { - expect(testFunctionAsContext()).to.not.be.ok; - expect(testFunctionAsContext(undefined)).to.not.be.ok; - expect(testFunctionAsContext(false)).to.not.be.ok; - expect(testFunctionAsContext('')).to.not.be.ok; - }); - }); - - }); - - describe('Test invalid contexts', function() { - var testContext; - - beforeEach(function() { - testContext = function(context) { - return function() { - contextMatcher.match(context, 'http://localhost/api/foo/bar'); - }; - }; - }); - - describe('Throw error', function() { - it('should throw error with undefined', function() { - expect(testContext(undefined)).to.throw(Error); - }); - - it('should throw error with null', function() { - expect(testContext(null)).to.throw(Error); - }); - - it('should throw error with object literal', function() { - expect(testContext({})).to.throw(Error); - }); - - it('should throw error with integers', function() { - expect(testContext(123)).to.throw(Error); - }); - - it('should throw error with mixed string and glob pattern', function() { - expect(testContext(['/api', '!*.html'])).to.throw(Error); - }); - }); - - describe('Do not throw error', function() { - it('should not throw error with string', function() { - expect(testContext('/123')).not.to.throw(Error); - }); - - it('should not throw error with Array', function() { - expect(testContext(['/123'])).not.to.throw(Error); - }); - it('should not throw error with glob', function() { - expect(testContext('/**')).not.to.throw(Error); - }); - - it('should not throw error with Array of globs', function() { - expect(testContext(['/**', '!*.html'])).not.to.throw(Error); - }); - - it('should not throw error with Function', function() { - expect(testContext(function() {})).not.to.throw(Error); - }); - }); - - }); -}); +/* eslint-disable no-unused-expressions */ +// https://github.com/feross/standard/issues/690#issuecomment-278533482 + +var expect = require('chai').expect +var contextMatcher = require('./_libs').contextMatcher + +describe('Context Matching', function () { + describe('String path matching', function () { + var result + + describe('Single path matching', function () { + it('should match all paths', function () { + result = contextMatcher.match('', 'http://localhost/api/foo/bar') + expect(result).to.be.true + }) + + it('should match all paths starting with forward-slash', function () { + result = contextMatcher.match('/', 'http://localhost/api/foo/bar') + expect(result).to.be.true + }) + + it('should return true when the context is present in url', function () { + result = contextMatcher.match('/api', 'http://localhost/api/foo/bar') + expect(result).to.be.true + }) + + it('should return false when the context is not present in url', function () { + result = contextMatcher.match('/abc', 'http://localhost/api/foo/bar') + expect(result).to.be.false + }) + + it('should return false when the context is present half way in url', function () { + result = contextMatcher.match('/foo', 'http://localhost/api/foo/bar') + expect(result).to.be.false + }) + + it('should return false when the context does not start with /', function () { + result = contextMatcher.match('api', 'http://localhost/api/foo/bar') + expect(result).to.be.false + }) + }) + + describe('Multi path matching', function () { + it('should return true when the context is present in url', function () { + result = contextMatcher.match(['/api'], 'http://localhost/api/foo/bar') + expect(result).to.be.true + }) + + it('should return true when the context is present in url', function () { + result = contextMatcher.match(['/api', '/ajax'], 'http://localhost/ajax/foo/bar') + expect(result).to.be.true + }) + + it('should return false when the context does not match url', function () { + result = contextMatcher.match(['/api', '/ajax'], 'http://localhost/foo/bar') + expect(result).to.be.false + }) + + it('should return false when empty array provided', function () { + result = contextMatcher.match([], 'http://localhost/api/foo/bar') + expect(result).to.be.false + }) + }) + }) + + describe('Wildcard path matching', function () { + describe('Single glob', function () { + var url + + beforeEach(function () { + url = 'http://localhost/api/foo/bar.html' + }) + + describe('url-path matching', function () { + it('should match any path', function () { + expect(contextMatcher.match('**', url)).to.be.true + expect(contextMatcher.match('/**', url)).to.be.true + }) + + it('should only match paths starting with "/api" ', function () { + expect(contextMatcher.match('/api/**', url)).to.be.true + expect(contextMatcher.match('/ajax/**', url)).to.be.false + }) + + it('should only match paths starting with "foo" folder in it ', function () { + expect(contextMatcher.match('**/foo/**', url)).to.be.true + expect(contextMatcher.match('**/invalid/**', url)).to.be.false + }) + }) + + describe('file matching', function () { + it('should match any path, file and extension', function () { + expect(contextMatcher.match('**', url)).to.be.true + expect(contextMatcher.match('**/*', url)).to.be.true + expect(contextMatcher.match('**/*.*', url)).to.be.true + expect(contextMatcher.match('/**', url)).to.be.true + expect(contextMatcher.match('/**.*', url)).to.be.true + expect(contextMatcher.match('/**/*', url)).to.be.true + expect(contextMatcher.match('/**/*.*', url)).to.be.true + }) + + it('should only match .html files', function () { + expect(contextMatcher.match('**/*.html', url)).to.be.true + expect(contextMatcher.match('/**.html', url)).to.be.true + expect(contextMatcher.match('/**/*.html', url)).to.be.true + expect(contextMatcher.match('/**.htm', url)).to.be.false + expect(contextMatcher.match('/**.jpg', url)).to.be.false + }) + + it('should only match .html under root path', function () { + var pattern = '/*.html' + expect(contextMatcher.match(pattern, 'http://localhost/index.html')).to.be.true + expect(contextMatcher.match(pattern, 'http://localhost/some/path/index.html')).to.be.false + }) + + it('should ignore query params', function () { + expect(contextMatcher.match('/**/*.php', 'http://localhost/a/b/c.php?d=e&e=f')).to.be.true + expect(contextMatcher.match('/**/*.php?*', 'http://localhost/a/b/c.php?d=e&e=f')).to.be.false + }) + + it('should only match any file in root path', function () { + expect(contextMatcher.match('/*', 'http://localhost/bar.html')).to.be.true + expect(contextMatcher.match('/*.*', 'http://localhost/bar.html')).to.be.true + expect(contextMatcher.match('/*', 'http://localhost/foo/bar.html')).to.be.false + }) + + it('should only match .html file is in root path', function () { + expect(contextMatcher.match('/*.html', 'http://localhost/bar.html')).to.be.true + expect(contextMatcher.match('/*.html', 'http://localhost/api/foo/bar.html')).to.be.false + }) + + it('should only match .html files in "foo" folder', function () { + expect(contextMatcher.match('**/foo/*.html', url)).to.be.true + expect(contextMatcher.match('**/bar/*.html', url)).to.be.false + }) + + it('should not match .html files', function () { + expect(contextMatcher.match('!**/*.html', url)).to.be.false + }) + }) + }) + + describe('Multi glob matching', function () { + describe('Multiple patterns', function () { + it('should return true when both path patterns match', function () { + var pattern = ['/api/**', '/ajax/**'] + expect(contextMatcher.match(pattern, 'http://localhost/api/foo/bar.json')).to.be.true + expect(contextMatcher.match(pattern, 'http://localhost/ajax/foo/bar.json')).to.be.true + expect(contextMatcher.match(pattern, 'http://localhost/rest/foo/bar.json')).to.be.false + }) + it('should return true when both file extensions pattern match', function () { + var pattern = ['/**.html', '/**.jpeg'] + expect(contextMatcher.match(pattern, 'http://localhost/api/foo/bar.html')).to.be.true + expect(contextMatcher.match(pattern, 'http://localhost/api/foo/bar.jpeg')).to.be.true + expect(contextMatcher.match(pattern, 'http://localhost/api/foo/bar.gif')).to.be.false + }) + }) + + describe('Negation patterns', function () { + it('should not match file extension', function () { + var url = 'http://localhost/api/foo/bar.html' + expect(contextMatcher.match(['**', '!**/*.html'], url)).to.be.false + expect(contextMatcher.match(['**', '!**/*.json'], url)).to.be.true + }) + }) + }) + }) + + describe('Use function for matching', function () { + var testFunctionAsContext = function (val) { + return contextMatcher.match(fn, 'http://localhost/api/foo/bar') + + function fn (path, req) { + return val + } + } + + describe('truthy', function () { + it('should match when function returns true', function () { + expect(testFunctionAsContext(true)).to.be.ok + expect(testFunctionAsContext('true')).to.be.ok + }) + }) + + describe('falsy', function () { + it('should not match when function returns falsy value', function () { + expect(testFunctionAsContext()).to.not.be.ok + expect(testFunctionAsContext(undefined)).to.not.be.ok + expect(testFunctionAsContext(false)).to.not.be.ok + expect(testFunctionAsContext('')).to.not.be.ok + }) + }) + }) + + describe('Test invalid contexts', function () { + var testContext + + beforeEach(function () { + testContext = function (context) { + return function () { + contextMatcher.match(context, 'http://localhost/api/foo/bar') + } + } + }) + + describe('Throw error', function () { + it('should throw error with undefined', function () { + expect(testContext(undefined)).to.throw(Error) + }) + + it('should throw error with null', function () { + expect(testContext(null)).to.throw(Error) + }) + + it('should throw error with object literal', function () { + expect(testContext({})).to.throw(Error) + }) + + it('should throw error with integers', function () { + expect(testContext(123)).to.throw(Error) + }) + + it('should throw error with mixed string and glob pattern', function () { + expect(testContext(['/api', '!*.html'])).to.throw(Error) + }) + }) + + describe('Do not throw error', function () { + it('should not throw error with string', function () { + expect(testContext('/123')).not.to.throw(Error) + }) + + it('should not throw error with Array', function () { + expect(testContext(['/123'])).not.to.throw(Error) + }) + it('should not throw error with glob', function () { + expect(testContext('/**')).not.to.throw(Error) + }) + + it('should not throw error with Array of globs', function () { + expect(testContext(['/**', '!*.html'])).not.to.throw(Error) + }) + + it('should not throw error with Function', function () { + expect(testContext(function () {})).not.to.throw(Error) + }) + }) + }) +}) diff --git a/test/unit/handlers.spec.js b/test/unit/handlers.spec.js index 04db566f..585c4d25 100644 --- a/test/unit/handlers.spec.js +++ b/test/unit/handlers.spec.js @@ -1,138 +1,138 @@ -var expect = require('chai').expect; -var handlers = require('./_libs').handlers; - -describe('handlers factory', function() { - var handlersMap; - - it('should return default handlers when no handlers are provided', function() { - handlersMap = handlers.getHandlers(); - expect(handlersMap.error).to.be.a('function'); - expect(handlersMap.close).to.be.a('function'); - }); - - describe('custom handlers', function() { - beforeEach(function() { - var fnCustom = function() { - return 42; - }; - - var proxyOptions = { - target: 'http://www.example.org', - onError: fnCustom, - onOpen: fnCustom, - onClose: fnCustom, - onProxyReq: fnCustom, - onProxyReqWs: fnCustom, - onProxyRes: fnCustom, - onDummy: fnCustom, - foobar: fnCustom - }; - - handlersMap = handlers.getHandlers(proxyOptions); - }); - - it('should only return http-proxy handlers', function() { - expect(handlersMap.error).to.be.a('function'); - expect(handlersMap.open).to.be.a('function'); - expect(handlersMap.close).to.be.a('function'); - expect(handlersMap.proxyReq).to.be.a('function'); - expect(handlersMap.proxyReqWs).to.be.a('function'); - expect(handlersMap.proxyRes).to.be.a('function'); - expect(handlersMap.dummy).to.be.undefined; - expect(handlersMap.foobar).to.be.undefined; - expect(handlersMap.target).to.be.undefined; - }); - - it('should use the provided custom handlers', function() { - expect(handlersMap.error()).to.equal(42); - expect(handlersMap.open()).to.equal(42); - expect(handlersMap.close()).to.equal(42); - expect(handlersMap.proxyReq()).to.equal(42); - expect(handlersMap.proxyReqWs()).to.equal(42); - expect(handlersMap.proxyRes()).to.equal(42); - }); - - }); -}); - -describe('default proxy error handler', function() { - - var mockError = { - code: 'ECONNREFUSED' - }; - - var mockReq = { - headers: { - host: 'localhost:3000' - }, - url: '/api' - }; - - var proxyOptions = { - target: { - host: 'localhost.dev' - } - }; - - var httpErrorCode; - var errorMessage; - - var mockRes = { - writeHead: function(v) { - httpErrorCode = v; - return v; - }, - end: function(v) { - errorMessage = v; - return v; - }, - headersSent: false - }; - - var proxyError; - - beforeEach(function() { - var handlersMap = handlers.getHandlers(); - proxyError = handlersMap.error; - }); - - afterEach(function() { - httpErrorCode = undefined; - errorMessage = undefined; - }); - - var codes = [ - ['HPE_INVALID_FOO', 502], - ['HPE_INVALID_BAR', 502], - ['ECONNREFUSED', 504], - ['ENOTFOUND', 504], - ['ECONNREFUSED', 504], - ['any', 500], - ]; - codes.forEach(function(item) { - var msg = item[0]; - var code = item[1]; - it('should set the http status code for ' + msg + ' to: ' + code, function() { - proxyError({ code: msg }, mockReq, mockRes, proxyOptions); - expect(httpErrorCode).to.equal(code); - }); - }); - - it('should end the response and return error message', function() { - proxyError(mockError, mockReq, mockRes, proxyOptions); - expect(errorMessage).to.equal('Error occured while trying to proxy to: localhost:3000/api'); - }); - - it('should not set the http status code to: 500 if headers have already been sent', function() { - mockRes.headersSent = true; - proxyError(mockError, mockReq, mockRes, proxyOptions); - expect(httpErrorCode).to.equal(undefined); - }); - - it('should end the response and return error message', function() { - mockRes.headersSent = true; - proxyError(mockError, mockReq, mockRes, proxyOptions); - expect(errorMessage).to.equal('Error occured while trying to proxy to: localhost:3000/api'); - }); - -}); +/* eslint-disable no-unused-expressions */ +// https://github.com/feross/standard/issues/690#issuecomment-278533482 + +var expect = require('chai').expect +var handlers = require('./_libs').handlers + +describe('handlers factory', function () { + var handlersMap + + it('should return default handlers when no handlers are provided', function () { + handlersMap = handlers.getHandlers() + expect(handlersMap.error).to.be.a('function') + expect(handlersMap.close).to.be.a('function') + }) + + describe('custom handlers', function () { + beforeEach(function () { + var fnCustom = function () { + return 42 + } + + var proxyOptions = { + target: 'http://www.example.org', + onError: fnCustom, + onOpen: fnCustom, + onClose: fnCustom, + onProxyReq: fnCustom, + onProxyReqWs: fnCustom, + onProxyRes: fnCustom, + onDummy: fnCustom, + foobar: fnCustom + } + + handlersMap = handlers.getHandlers(proxyOptions) + }) + + it('should only return http-proxy handlers', function () { + expect(handlersMap.error).to.be.a('function') + expect(handlersMap.open).to.be.a('function') + expect(handlersMap.close).to.be.a('function') + expect(handlersMap.proxyReq).to.be.a('function') + expect(handlersMap.proxyReqWs).to.be.a('function') + expect(handlersMap.proxyRes).to.be.a('function') + expect(handlersMap.dummy).to.be.undefined + expect(handlersMap.foobar).to.be.undefined + expect(handlersMap.target).to.be.undefined + }) + + it('should use the provided custom handlers', function () { + expect(handlersMap.error()).to.equal(42) + expect(handlersMap.open()).to.equal(42) + expect(handlersMap.close()).to.equal(42) + expect(handlersMap.proxyReq()).to.equal(42) + expect(handlersMap.proxyReqWs()).to.equal(42) + expect(handlersMap.proxyRes()).to.equal(42) + }) + }) +}) + +describe('default proxy error handler', function () { + var mockError = { + code: 'ECONNREFUSED' + } + + var mockReq = { + headers: { + host: 'localhost:3000' + }, + url: '/api' + } + + var proxyOptions = { + target: { + host: 'localhost.dev' + } + } + + var httpErrorCode + var errorMessage + + var mockRes = { + writeHead: function (v) { + httpErrorCode = v + return v + }, + end: function (v) { + errorMessage = v + return v + }, + headersSent: false + } + + var proxyError + + beforeEach(function () { + var handlersMap = handlers.getHandlers() + proxyError = handlersMap.error + }) + + afterEach(function () { + httpErrorCode = undefined + errorMessage = undefined + }) + + var codes = [ + ['HPE_INVALID_FOO', 502], + ['HPE_INVALID_BAR', 502], + ['ECONNREFUSED', 504], + ['ENOTFOUND', 504], + ['ECONNREFUSED', 504], + ['any', 500] + ] + codes.forEach(function (item) { + var msg = item[0] + var code = item[1] + it('should set the http status code for ' + msg + ' to: ' + code, function () { + proxyError({ code: msg }, mockReq, mockRes, proxyOptions) + expect(httpErrorCode).to.equal(code) + }) + }) + + it('should end the response and return error message', function () { + proxyError(mockError, mockReq, mockRes, proxyOptions) + expect(errorMessage).to.equal('Error occured while trying to proxy to: localhost:3000/api') + }) + + it('should not set the http status code to: 500 if headers have already been sent', function () { + mockRes.headersSent = true + proxyError(mockError, mockReq, mockRes, proxyOptions) + expect(httpErrorCode).to.equal(undefined) + }) + + it('should end the response and return error message', function () { + mockRes.headersSent = true + proxyError(mockError, mockReq, mockRes, proxyOptions) + expect(errorMessage).to.equal('Error occured while trying to proxy to: localhost:3000/api') + }) +}) diff --git a/test/unit/logger.spec.js b/test/unit/logger.spec.js index d4fc81d0..c2616a64 100644 --- a/test/unit/logger.spec.js +++ b/test/unit/logger.spec.js @@ -1,259 +1,259 @@ -var expect = require('chai').expect; -var Logger = require('./_libs').Logger; -var getArrow = Logger.getArrow; - -describe('Logger', function() { - var logger; - var logMessage, debugMessage, infoMessage, warnMessage, errorMessage; - - beforeEach(function() { - logMessage = undefined; - debugMessage = undefined; - infoMessage = undefined; - warnMessage = undefined; - errorMessage = undefined; - }); - - beforeEach(function() { - logger = Logger.getInstance(); - }); - - beforeEach(function() { - logger.setProvider(function(provider) { - provider.log = function(message) {logMessage = message;}; - provider.debug = function(message) {debugMessage = message;}; - provider.info = function(message) {infoMessage = message;}; - provider.warn = function(message) {warnMessage = message;}; - provider.error = function(message) {errorMessage = message;}; - - return provider; - }); - }); - - describe('logging with different levels', function() { - beforeEach(function() { - logger.log('log'); - logger.debug('debug'); - logger.info('info'); - logger.warn('warn'); - logger.error('error'); - }); - - describe('level: debug', function() { - beforeEach(function() { - logger.setLevel('debug'); - }); - - it('should log .log() messages', function() { - expect(logMessage).to.equal('log'); - }); - it('should log .debug() messages', function() { - expect(debugMessage).to.equal('debug'); - }); - it('should log .info() messages', function() { - expect(infoMessage).to.equal('info'); - }); - it('should log .warn() messages', function() { - expect(warnMessage).to.equal('warn'); - }); - it('should log .error() messages', function() { - expect(errorMessage).to.equal('error'); - }); - }); - - describe('level: info', function() { - beforeEach(function() { - logger.setLevel('info'); - }); - - it('should log .log() messages', function() { - expect(logMessage).to.equal('log'); - }); - it('should not log .debug() messages', function() { - expect(debugMessage).to.equal(undefined); - }); - it('should log .info() messages', function() { - expect(infoMessage).to.equal('info'); - }); - it('should log .warn() messages', function() { - expect(warnMessage).to.equal('warn'); - }); - it('should log .error() messages', function() { - expect(errorMessage).to.equal('error'); - }); - }); - - describe('level: warn', function() { - beforeEach(function() { - logger.setLevel('warn'); - }); - - it('should log .log() messages', function() { - expect(logMessage).to.equal('log'); - }); - it('should not log .debug() messages', function() { - expect(debugMessage).to.equal(undefined); - }); - it('should not log .info() messages', function() { - expect(infoMessage).to.equal(undefined); - }); - it('should log .warn() messages', function() { - expect(warnMessage).to.equal('warn'); - }); - it('should log .error() messages', function() { - expect(errorMessage).to.equal('error'); - }); - }); - - describe('level: error', function() { - beforeEach(function() { - logger.setLevel('error'); - }); - - it('should log .log() messages', function() { - expect(logMessage).to.equal('log'); - }); - it('should not log .debug() messages', function() { - expect(debugMessage).to.equal(undefined); - }); - it('should not log .info() messages', function() { - expect(infoMessage).to.equal(undefined); - }); - it('should log .warn() messages', function() { - expect(warnMessage).to.equal(undefined); - }); - it('should log .error() messages', function() { - expect(errorMessage).to.equal('error'); - }); - }); - - describe('level: silent', function() { - beforeEach(function() { - logger.setLevel('silent'); - }); - - it('should log .log() messages', function() { - expect(logMessage).to.equal('log'); - }); - it('should not log .debug() messages', function() { - expect(debugMessage).to.equal(undefined); - }); - it('should not log .info() messages', function() { - expect(infoMessage).to.equal(undefined); - }); - it('should not log .warn() messages', function() { - expect(warnMessage).to.equal(undefined); - }); - it('should not log .error() messages', function() { - expect(errorMessage).to.equal(undefined); - }); - }); - - describe('Interpolation', function() { - // make sure all messages are logged - beforeEach(function() { - logger.setLevel('debug'); - }); - - beforeEach(function() { - logger.log('log %s %s', 123, 456); - logger.debug('debug %s %s', 123, 456); - logger.info('info %s %s', 123, 456); - logger.warn('warn %s %s', 123, 456); - logger.error('error %s %s', 123, 456); - }); - - it('should interpolate .log() messages', function() { - expect(logMessage).to.equal('log 123 456'); - }); - it('should interpolate .debug() messages', function() { - expect(debugMessage).to.equal('debug 123 456'); - }); - it('should interpolate .info() messages', function() { - expect(infoMessage).to.equal('info 123 456'); - }); - it('should interpolate .warn() messages', function() { - expect(warnMessage).to.equal('warn 123 456'); - }); - it('should interpolate .error() messages', function() { - expect(errorMessage).to.equal('error 123 456'); - }); - }); - }); - - describe('Erroneous usage.', function() { - var fn; - - describe('Log provider is not a function', function() { - beforeEach(function() { - fn = function() { - logger.setProvider({}); - }; - }); - - it('should throw an error', function() { - expect(fn).to.throw(Error); - }); - }); - - describe('Invalid logLevel', function() { - beforeEach(function() { - fn = function() { - logger.setLevel('foo'); - }; - }); - - it('should throw an error', function() { - expect(fn).to.throw(Error); - }); - }); - - }); - -}); - -describe('getArrow', function() { - var arrow; - // scenario = [originalPath, newPath, originalTarget, newTarget] - - describe('default arrow', function() { - beforeEach(function() { - arrow = getArrow('/api', '/api', 'localhost:1337', 'localhost:1337'); - }); - - it('should return arrow: "->"', function() { - expect(arrow).to.equal('->'); - }); - }); - - describe('"pathRewrite" arrow', function() { - beforeEach(function() { - arrow = getArrow('/api', '/rest', 'localhost:1337', 'localhost:1337'); - }); - - it('should return arrow: "~>"', function() { - expect(arrow).to.equal('~>'); - }); - }); - - describe('"router" arrow', function() { - beforeEach(function() { - arrow = getArrow('/api', '/api', 'localhost:1337', 'localhost:8888'); - }); - - it('should return arrow: "=>"', function() { - expect(arrow).to.equal('=>'); - }); - }); - - describe('"pathRewrite" + "router" arrow', function() { - beforeEach(function() { - arrow = getArrow('/api', '/rest', 'localhost:1337', 'localhost:8888'); - }); - - it('should return arrow: "≈>"', function() { - expect(arrow).to.equal('≈>'); - }); - }); - -}); +/* eslint-disable no-unused-expressions */ +// https://github.com/feross/standard/issues/690#issuecomment-278533482 + +var expect = require('chai').expect +var Logger = require('./_libs').Logger +var getArrow = Logger.getArrow + +describe('Logger', function () { + var logger + var logMessage, debugMessage, infoMessage, warnMessage, errorMessage + + beforeEach(function () { + logMessage = undefined + debugMessage = undefined + infoMessage = undefined + warnMessage = undefined + errorMessage = undefined + }) + + beforeEach(function () { + logger = Logger.getInstance() + }) + + beforeEach(function () { + logger.setProvider(function (provider) { + provider.log = function (message) { logMessage = message } + provider.debug = function (message) { debugMessage = message } + provider.info = function (message) { infoMessage = message } + provider.warn = function (message) { warnMessage = message } + provider.error = function (message) { errorMessage = message } + + return provider + }) + }) + + describe('logging with different levels', function () { + beforeEach(function () { + logger.log('log') + logger.debug('debug') + logger.info('info') + logger.warn('warn') + logger.error('error') + }) + + describe('level: debug', function () { + beforeEach(function () { + logger.setLevel('debug') + }) + + it('should log .log() messages', function () { + expect(logMessage).to.equal('log') + }) + it('should log .debug() messages', function () { + expect(debugMessage).to.equal('debug') + }) + it('should log .info() messages', function () { + expect(infoMessage).to.equal('info') + }) + it('should log .warn() messages', function () { + expect(warnMessage).to.equal('warn') + }) + it('should log .error() messages', function () { + expect(errorMessage).to.equal('error') + }) + }) + + describe('level: info', function () { + beforeEach(function () { + logger.setLevel('info') + }) + + it('should log .log() messages', function () { + expect(logMessage).to.equal('log') + }) + it('should not log .debug() messages', function () { + expect(debugMessage).to.equal(undefined) + }) + it('should log .info() messages', function () { + expect(infoMessage).to.equal('info') + }) + it('should log .warn() messages', function () { + expect(warnMessage).to.equal('warn') + }) + it('should log .error() messages', function () { + expect(errorMessage).to.equal('error') + }) + }) + + describe('level: warn', function () { + beforeEach(function () { + logger.setLevel('warn') + }) + + it('should log .log() messages', function () { + expect(logMessage).to.equal('log') + }) + it('should not log .debug() messages', function () { + expect(debugMessage).to.equal(undefined) + }) + it('should not log .info() messages', function () { + expect(infoMessage).to.equal(undefined) + }) + it('should log .warn() messages', function () { + expect(warnMessage).to.equal('warn') + }) + it('should log .error() messages', function () { + expect(errorMessage).to.equal('error') + }) + }) + + describe('level: error', function () { + beforeEach(function () { + logger.setLevel('error') + }) + + it('should log .log() messages', function () { + expect(logMessage).to.equal('log') + }) + it('should not log .debug() messages', function () { + expect(debugMessage).to.equal(undefined) + }) + it('should not log .info() messages', function () { + expect(infoMessage).to.equal(undefined) + }) + it('should log .warn() messages', function () { + expect(warnMessage).to.equal(undefined) + }) + it('should log .error() messages', function () { + expect(errorMessage).to.equal('error') + }) + }) + + describe('level: silent', function () { + beforeEach(function () { + logger.setLevel('silent') + }) + + it('should log .log() messages', function () { + expect(logMessage).to.equal('log') + }) + it('should not log .debug() messages', function () { + expect(debugMessage).to.equal(undefined) + }) + it('should not log .info() messages', function () { + expect(infoMessage).to.equal(undefined) + }) + it('should not log .warn() messages', function () { + expect(warnMessage).to.equal(undefined) + }) + it('should not log .error() messages', function () { + expect(errorMessage).to.equal(undefined) + }) + }) + + describe('Interpolation', function () { + // make sure all messages are logged + beforeEach(function () { + logger.setLevel('debug') + }) + + beforeEach(function () { + logger.log('log %s %s', 123, 456) + logger.debug('debug %s %s', 123, 456) + logger.info('info %s %s', 123, 456) + logger.warn('warn %s %s', 123, 456) + logger.error('error %s %s', 123, 456) + }) + + it('should interpolate .log() messages', function () { + expect(logMessage).to.equal('log 123 456') + }) + it('should interpolate .debug() messages', function () { + expect(debugMessage).to.equal('debug 123 456') + }) + it('should interpolate .info() messages', function () { + expect(infoMessage).to.equal('info 123 456') + }) + it('should interpolate .warn() messages', function () { + expect(warnMessage).to.equal('warn 123 456') + }) + it('should interpolate .error() messages', function () { + expect(errorMessage).to.equal('error 123 456') + }) + }) + }) + + describe('Erroneous usage.', function () { + var fn + + describe('Log provider is not a function', function () { + beforeEach(function () { + fn = function () { + logger.setProvider({}) + } + }) + + it('should throw an error', function () { + expect(fn).to.throw(Error) + }) + }) + + describe('Invalid logLevel', function () { + beforeEach(function () { + fn = function () { + logger.setLevel('foo') + } + }) + + it('should throw an error', function () { + expect(fn).to.throw(Error) + }) + }) + }) +}) + +describe('getArrow', function () { + var arrow + // scenario = [originalPath, newPath, originalTarget, newTarget] + + describe('default arrow', function () { + beforeEach(function () { + arrow = getArrow('/api', '/api', 'localhost:1337', 'localhost:1337') + }) + + it('should return arrow: "->"', function () { + expect(arrow).to.equal('->') + }) + }) + + describe('"pathRewrite" arrow', function () { + beforeEach(function () { + arrow = getArrow('/api', '/rest', 'localhost:1337', 'localhost:1337') + }) + + it('should return arrow: "~>"', function () { + expect(arrow).to.equal('~>') + }) + }) + + describe('"router" arrow', function () { + beforeEach(function () { + arrow = getArrow('/api', '/api', 'localhost:1337', 'localhost:8888') + }) + + it('should return arrow: "=>"', function () { + expect(arrow).to.equal('=>') + }) + }) + + describe('"pathRewrite" + "router" arrow', function () { + beforeEach(function () { + arrow = getArrow('/api', '/rest', 'localhost:1337', 'localhost:8888') + }) + + it('should return arrow: "≈>"', function () { + expect(arrow).to.equal('≈>') + }) + }) +}) diff --git a/test/unit/path-rewriter.spec.js b/test/unit/path-rewriter.spec.js index 1feffc0b..7eb709e0 100644 --- a/test/unit/path-rewriter.spec.js +++ b/test/unit/path-rewriter.spec.js @@ -1,143 +1,143 @@ -var expect = require('chai').expect; -var pathRewriter = require('./_libs').pathRewriter; - -describe('Path rewriting', function() { - var rewriter; - var result; - var config; - - describe('Rewrite rules configuration and usage', function() { - - beforeEach(function() { - config = { - '^/api/old': '/api/new', - '^/remove': '', - 'invalid': 'path/new', - '/valid': '/path/new', - '/some/specific/path': '/awe/some/specific/path', - '/some': '/awe/some' - }; - }); - - beforeEach(function() { - rewriter = pathRewriter.create(config); - }); - - it('should rewrite path', function() { - result = rewriter('/api/old/index.json'); - expect(result).to.equal('/api/new/index.json'); - }); - - it('should remove path', function() { - result = rewriter('/remove/old/index.json'); - expect(result).to.equal('/old/index.json'); - }); - - it('should leave path intact', function() { - result = rewriter('/foo/bar/index.json'); - expect(result).to.equal('/foo/bar/index.json'); - }); - - it('should not rewrite path when config-key does not match url with test(regex)', function() { - result = rewriter('/invalid/bar/foo.json'); - expect(result).to.equal('/path/new/bar/foo.json'); - expect(result).to.not.equal('/invalid/new/bar/foo.json'); - }); - - it('should rewrite path when config-key does match url with test(regex)', function() { - result = rewriter('/valid/foo/bar.json'); - expect(result).to.equal('/path/new/foo/bar.json'); - }); - - it('should return first match when similar paths are configured', function() { - result = rewriter('/some/specific/path/bar.json'); - expect(result).to.equal('/awe/some/specific/path/bar.json'); - }); - }); - - describe('Rewrite rule: add base path to requests', function() { - - beforeEach(function() { - config = { - '^/': '/extra/base/path/' - }; - }); - - beforeEach(function() { - rewriter = pathRewriter.create(config); - }); - - it('should add base path to requests', function() { - result = rewriter('/api/books/123'); - expect(result).to.equal('/extra/base/path/api/books/123'); - }); - }); - - describe('Rewrite function', function() { - var rewriter; - - beforeEach(function() { - rewriter = function(fn) { - var rewriteFn = pathRewriter.create(fn); - var requestPath = '/123/456'; - return rewriteFn(requestPath); - }; - }); - - it('should return unmodified path', function() { - var rewriteFn = function(path) { - return path; - }; - expect(rewriter(rewriteFn)).to.equal('/123/456'); - }); - - it('should return alternative path', function() { - var rewriteFn = function(path) { - return '/foo/bar'; - }; - expect(rewriter(rewriteFn)).to.equal('/foo/bar'); - }); - - it('should return replaced path', function() { - var rewriteFn = function(path) { - return path.replace('/456', '/789'); - }; - expect(rewriter(rewriteFn)).to.equal('/123/789'); - }); - }); - - describe('Invalid configuration', function() { - var badFn; - - beforeEach(function() { - badFn = function(config) { - return function() { - pathRewriter.create(config); - }; - }; - }); - - it('should return undefined when no config is provided', function() { - expect((badFn())()).to.equal(undefined); - expect((badFn(null)())).to.equal(undefined); - expect((badFn(undefined)())).to.equal(undefined); - }); - - it('should throw when bad config is provided', function() { - expect(badFn(123)).to.throw(Error); - expect(badFn('abc')).to.throw(Error); - expect(badFn([])).to.throw(Error); - expect(badFn([1,2,3])).to.throw(Error); - }); - - it('should not throw when empty Object config is provided', function() { - expect(badFn({})).to.not.throw(Error); - }); - - it('should not throw when function config is provided', function() { - expect(badFn(function() {})).to.not.throw(Error); - }); - - }); -}); +var expect = require('chai').expect +var pathRewriter = require('./_libs').pathRewriter + +describe('Path rewriting', function () { + var rewriter + var result + var config + + describe('Rewrite rules configuration and usage', function () { + beforeEach(function () { + config = { + '^/api/old': '/api/new', + '^/remove': '', + 'invalid': 'path/new', + '/valid': '/path/new', + '/some/specific/path': '/awe/some/specific/path', + '/some': '/awe/some' + } + }) + + beforeEach(function () { + rewriter = pathRewriter.create(config) + }) + + it('should rewrite path', function () { + result = rewriter('/api/old/index.json') + expect(result).to.equal('/api/new/index.json') + }) + + it('should remove path', function () { + result = rewriter('/remove/old/index.json') + expect(result).to.equal('/old/index.json') + }) + + it('should leave path intact', function () { + result = rewriter('/foo/bar/index.json') + expect(result).to.equal('/foo/bar/index.json') + }) + + it('should not rewrite path when config-key does not match url with test(regex)', function () { + result = rewriter('/invalid/bar/foo.json') + expect(result).to.equal('/path/new/bar/foo.json') + expect(result).to.not.equal('/invalid/new/bar/foo.json') + }) + + it('should rewrite path when config-key does match url with test(regex)', function () { + result = rewriter('/valid/foo/bar.json') + expect(result).to.equal('/path/new/foo/bar.json') + }) + + it('should return first match when similar paths are configured', function () { + result = rewriter('/some/specific/path/bar.json') + expect(result).to.equal('/awe/some/specific/path/bar.json') + }) + }) + + describe('Rewrite rule: add base path to requests', function () { + beforeEach(function () { + config = { + '^/': '/extra/base/path/' + } + }) + + beforeEach(function () { + rewriter = pathRewriter.create(config) + }) + + it('should add base path to requests', function () { + result = rewriter('/api/books/123') + expect(result).to.equal('/extra/base/path/api/books/123') + }) + }) + + describe('Rewrite function', function () { + var rewriter + + beforeEach(function () { + rewriter = function (fn) { + var rewriteFn = pathRewriter.create(fn) + var requestPath = '/123/456' + return rewriteFn(requestPath) + } + }) + + it('should return unmodified path', function () { + var rewriteFn = function (path) { + return path + } + + expect(rewriter(rewriteFn)).to.equal('/123/456') + }) + + it('should return alternative path', function () { + var rewriteFn = function (path) { + return '/foo/bar' + } + + expect(rewriter(rewriteFn)).to.equal('/foo/bar') + }) + + it('should return replaced path', function () { + var rewriteFn = function (path) { + return path.replace('/456', '/789') + } + + expect(rewriter(rewriteFn)).to.equal('/123/789') + }) + }) + + describe('Invalid configuration', function () { + var badFn + + beforeEach(function () { + badFn = function (config) { + return function () { + pathRewriter.create(config) + } + } + }) + + it('should return undefined when no config is provided', function () { + expect((badFn())()).to.equal(undefined) + expect((badFn(null)())).to.equal(undefined) + expect((badFn(undefined)())).to.equal(undefined) + }) + + it('should throw when bad config is provided', function () { + expect(badFn(123)).to.throw(Error) + expect(badFn('abc')).to.throw(Error) + expect(badFn([])).to.throw(Error) + expect(badFn([1, 2, 3])).to.throw(Error) + }) + + it('should not throw when empty Object config is provided', function () { + expect(badFn({})).to.not.throw(Error) + }) + + it('should not throw when function config is provided', function () { + expect(badFn(function () {})).to.not.throw(Error) + }) + }) +}) diff --git a/test/unit/router.spec.js b/test/unit/router.spec.js index 094af538..1bb31943 100644 --- a/test/unit/router.spec.js +++ b/test/unit/router.spec.js @@ -1,136 +1,133 @@ -var expect = require('chai').expect; -var router = require('./_libs').router; - -describe('router unit test', function() { - var req, config, result; - - beforeEach(function() { - req = { - headers: { - host: 'localhost' - }, - url: '/' - }; - - config = { - target: 'http://localhost:6000' - }; - - }); - - describe('router.getTarget from function', function() { - var request; - - beforeEach(function() { - proxyOptionWithRouter = { - target: 'http://localhost:6000', - router: function(req) { - request = req; - return 'http://foobar.com:666'; - } - }; - - result = router.getTarget(req, proxyOptionWithRouter); - }); - - describe('custom dynamic router function', function() { - it('should provide the request object for dynamic routing', function() { - expect(request.headers.host).to.equal('localhost'); - expect(request.url).to.equal('/'); - }); - it('should return new target', function() { - expect(result).to.equal('http://foobar.com:666'); - }); - }); - }); - - describe('router.getTarget from table', function() { - beforeEach(function() { - proxyOptionWithRouter = { - target: 'http://localhost:6000', - router: { - 'alpha.localhost': 'http://localhost:6001', - 'beta.localhost': 'http://localhost:6002', - 'gamma.localhost/api': 'http://localhost:6003', - 'gamma.localhost': 'http://localhost:6004', - '/rest': 'http://localhost:6005', - '/some/specific/path': 'http://localhost:6006', - '/some': 'http://localhost:6007' - } - }; - }); - - describe('without router config', function() { - it('should return the normal target when router not present in config', function() { - result = router.getTarget(req, config); - expect(result).to.equal(undefined); - }); - }); - - describe('with just the host in router config', function() { - it('should target http://localhost:6001 when for router:"alpha.localhost"', function() { - req.headers.host = 'alpha.localhost'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal('http://localhost:6001'); - }); - - it('should target http://localhost:6002 when for router:"beta.localhost"', function() { - req.headers.host = 'beta.localhost'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal('http://localhost:6002'); - }); - }); - - describe('with host and host + path config', function() { - it('should target http://localhost:6004 without path', function() { - req.headers.host = 'gamma.localhost'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal('http://localhost:6004'); - }); - - it('should target http://localhost:6003 exact path match', function() { - req.headers.host = 'gamma.localhost'; - req.url = '/api'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal('http://localhost:6003'); - }); - - it('should target http://localhost:6004 when contains path', function() { - req.headers.host = 'gamma.localhost'; - req.url = '/api/books/123'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal('http://localhost:6003'); - }); - }); - - describe('with just the path', function() { - it('should target http://localhost:6005 with just a path as router config', function() { - req.url = '/rest'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal('http://localhost:6005'); - }); - - it('should target http://localhost:6005 with just a path as router config', function() { - req.url = '/rest/deep/path'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal('http://localhost:6005'); - }); - - it('should target http://localhost:6000 path in not present in router config', function() { - req.url = '/unknow-path'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal(undefined); - }); - }); - - describe('matching order of router config', function() { - it('should return first matching target when similar paths are configured', function() { - req.url = '/some/specific/path'; - result = router.getTarget(req, proxyOptionWithRouter); - expect(result).to.equal('http://localhost:6006'); - }); - }); - - }); - -}); +var expect = require('chai').expect +var router = require('./_libs').router + +describe('router unit test', function () { + var req, config, result, proxyOptionWithRouter + + beforeEach(function () { + req = { + headers: { + host: 'localhost' + }, + url: '/' + } + + config = { + target: 'http://localhost:6000' + } + }) + + describe('router.getTarget from function', function () { + var request + + beforeEach(function () { + proxyOptionWithRouter = { + target: 'http://localhost:6000', + router: function (req) { + request = req + return 'http://foobar.com:666' + } + } + + result = router.getTarget(req, proxyOptionWithRouter) + }) + + describe('custom dynamic router function', function () { + it('should provide the request object for dynamic routing', function () { + expect(request.headers.host).to.equal('localhost') + expect(request.url).to.equal('/') + }) + it('should return new target', function () { + expect(result).to.equal('http://foobar.com:666') + }) + }) + }) + + describe('router.getTarget from table', function () { + beforeEach(function () { + proxyOptionWithRouter = { + target: 'http://localhost:6000', + router: { + 'alpha.localhost': 'http://localhost:6001', + 'beta.localhost': 'http://localhost:6002', + 'gamma.localhost/api': 'http://localhost:6003', + 'gamma.localhost': 'http://localhost:6004', + '/rest': 'http://localhost:6005', + '/some/specific/path': 'http://localhost:6006', + '/some': 'http://localhost:6007' + } + } + }) + + describe('without router config', function () { + it('should return the normal target when router not present in config', function () { + result = router.getTarget(req, config) + expect(result).to.equal(undefined) + }) + }) + + describe('with just the host in router config', function () { + it('should target http://localhost:6001 when for router:"alpha.localhost"', function () { + req.headers.host = 'alpha.localhost' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal('http://localhost:6001') + }) + + it('should target http://localhost:6002 when for router:"beta.localhost"', function () { + req.headers.host = 'beta.localhost' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal('http://localhost:6002') + }) + }) + + describe('with host and host + path config', function () { + it('should target http://localhost:6004 without path', function () { + req.headers.host = 'gamma.localhost' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal('http://localhost:6004') + }) + + it('should target http://localhost:6003 exact path match', function () { + req.headers.host = 'gamma.localhost' + req.url = '/api' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal('http://localhost:6003') + }) + + it('should target http://localhost:6004 when contains path', function () { + req.headers.host = 'gamma.localhost' + req.url = '/api/books/123' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal('http://localhost:6003') + }) + }) + + describe('with just the path', function () { + it('should target http://localhost:6005 with just a path as router config', function () { + req.url = '/rest' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal('http://localhost:6005') + }) + + it('should target http://localhost:6005 with just a path as router config', function () { + req.url = '/rest/deep/path' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal('http://localhost:6005') + }) + + it('should target http://localhost:6000 path in not present in router config', function () { + req.url = '/unknow-path' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal(undefined) + }) + }) + + describe('matching order of router config', function () { + it('should return first matching target when similar paths are configured', function () { + req.url = '/some/specific/path' + result = router.getTarget(req, proxyOptionWithRouter) + expect(result).to.equal('http://localhost:6006') + }) + }) + }) +})