feature: 拦截配置功能,允许匹配到多个 ‘域名匹配符’ 下的拦截配置了,只要域名符合 ‘域名匹配符’。 (#286)
							parent
							
								
									3ade87d07b
								
							
						
					
					
						commit
						74c14736b1
					
				| 
						 | 
					@ -65,7 +65,7 @@ function doDiff (oldObj, newObj) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function deleteNullItems (target) {
 | 
					function deleteNullItems (target) {
 | 
				
			||||||
  lodash.forEach(target, (item, key) => {
 | 
					  lodash.forEach(target, (item, key) => {
 | 
				
			||||||
    if (item == null) {
 | 
					    if (item == null || item === '[delete]') {
 | 
				
			||||||
      delete target[key]
 | 
					      delete target[key]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (lodash.isObject(item)) {
 | 
					    if (lodash.isObject(item)) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@
 | 
				
			||||||
      :style="{ height: '100%' }"
 | 
					      :style="{ height: '100%' }"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <a-tab-pane tab="拦截设置" key="1"  >
 | 
					      <a-tab-pane tab="拦截设置" key="1"  >
 | 
				
			||||||
        <vue-json-editor style="height:100%;" ref="editor" v-model="targetConfig.intercepts" mode="code" :show-btns="false" :expandedOnStart="true" @json-change="onJsonChange" ></vue-json-editor>
 | 
					        <vue-json-editor style="height:100%;" ref="editor" v-model="targetConfig.intercepts" mode="code" :show-btns="false" :expandedOnStart="true" @json-change="onJsonChange"></vue-json-editor>
 | 
				
			||||||
      </a-tab-pane>
 | 
					      </a-tab-pane>
 | 
				
			||||||
      <a-tab-pane tab="DNS设置" key="2">
 | 
					      <a-tab-pane tab="DNS设置" key="2">
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -138,7 +138,6 @@
 | 
				
			||||||
<!--              <a-button  type="danger" icon="minus" @click="deleteSniList(item,index)"/>-->
 | 
					<!--              <a-button  type="danger" icon="minus" @click="deleteSniList(item,index)"/>-->
 | 
				
			||||||
<!--            </a-col>-->
 | 
					<!--            </a-col>-->
 | 
				
			||||||
<!--          </a-row>-->
 | 
					<!--          </a-row>-->
 | 
				
			||||||
 | 
					 | 
				
			||||||
<!--        </a-tab-pane>-->
 | 
					<!--        </a-tab-pane>-->
 | 
				
			||||||
        <a-tab-pane tab="IP测速" key="6">
 | 
					        <a-tab-pane tab="IP测速" key="6">
 | 
				
			||||||
          <div>
 | 
					          <div>
 | 
				
			||||||
| 
						 | 
					@ -201,7 +200,6 @@
 | 
				
			||||||
                </a-card>
 | 
					                </a-card>
 | 
				
			||||||
              </a-col>
 | 
					              </a-col>
 | 
				
			||||||
            </a-row>
 | 
					            </a-row>
 | 
				
			||||||
 | 
					 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        </a-tab-pane>
 | 
					        </a-tab-pane>
 | 
				
			||||||
      </a-tabs>
 | 
					      </a-tabs>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,8 @@ function readConfig (config, defaultConfig) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  name: 'options',
 | 
				
			||||||
 | 
					  priority: 1,
 | 
				
			||||||
  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
					  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
				
			||||||
    const { rOptions, log } = context
 | 
					    const { rOptions, log } = context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +29,7 @@ module.exports = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const headers = {
 | 
					    const headers = {
 | 
				
			||||||
      // 允许跨域
 | 
					      // 允许跨域
 | 
				
			||||||
      'Dev-Sidecar-Interceptor': 'options',
 | 
					      'DS-Interceptor': 'options',
 | 
				
			||||||
      'Access-Control-Allow-Origin': rOptions.headers.origin,
 | 
					      'Access-Control-Allow-Origin': rOptions.headers.origin,
 | 
				
			||||||
      'Access-Control-Allow-Headers': allowHeaders,
 | 
					      'Access-Control-Allow-Headers': allowHeaders,
 | 
				
			||||||
      'Access-Control-Allow-Methods': allowMethods,
 | 
					      'Access-Control-Allow-Methods': allowMethods,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,23 @@
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  requestIntercept (context, interceptOpts, req, res, ssl, next) {
 | 
					  name: 'abort',
 | 
				
			||||||
 | 
					  priority: 103,
 | 
				
			||||||
 | 
					  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
				
			||||||
    const { rOptions, log } = context
 | 
					    const { rOptions, log } = context
 | 
				
			||||||
    log.info('abort:', rOptions.hostname, req.url)
 | 
					
 | 
				
			||||||
    res.writeHead(403)
 | 
					    res.writeHead(403, {
 | 
				
			||||||
    res.write('DevSidecar 403: \n\n request abort, this request is matched by abort intercept.\n\n 因配置abort拦截器,本请求将取消')
 | 
					      'Content-Type': 'text/plain; charset=utf-8',
 | 
				
			||||||
 | 
					      'DS-Interceptor': 'abort'
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    res.write(
 | 
				
			||||||
 | 
					      'DevSidecar 403: Request abort.\r\n\r\n' +
 | 
				
			||||||
 | 
					      '  This request is matched by abort intercept.\r\n' +
 | 
				
			||||||
 | 
					      '  因配置abort拦截器,本请求直接返回403禁止访问。'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    res.end()
 | 
					    res.end()
 | 
				
			||||||
    return true// 是否结束
 | 
					
 | 
				
			||||||
 | 
					    const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
 | 
				
			||||||
 | 
					    log.info('abort intercept:', url)
 | 
				
			||||||
 | 
					    return true // true代表请求结束
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  is (interceptOpt) {
 | 
					  is (interceptOpt) {
 | 
				
			||||||
    return !!interceptOpt.abort
 | 
					    return !!interceptOpt.abort
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,6 +55,8 @@ function getLastModifiedTimeFromIfModifiedSince (rOptions, log) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  name: 'cacheReq',
 | 
				
			||||||
 | 
					  priority: 111,
 | 
				
			||||||
  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
					  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
				
			||||||
    const { rOptions, log } = context
 | 
					    const { rOptions, log } = context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,12 +67,12 @@ module.exports = {
 | 
				
			||||||
    // 获取 Cache-Control 用于判断是否禁用缓存
 | 
					    // 获取 Cache-Control 用于判断是否禁用缓存
 | 
				
			||||||
    const cacheControl = rOptions.headers['cache-control']
 | 
					    const cacheControl = rOptions.headers['cache-control']
 | 
				
			||||||
    if (cacheControl && (cacheControl.indexOf('no-cache') >= 0 || cacheControl.indexOf('no-store') >= 0)) {
 | 
					    if (cacheControl && (cacheControl.indexOf('no-cache') >= 0 || cacheControl.indexOf('no-store') >= 0)) {
 | 
				
			||||||
      return // 禁用缓存,跳过当前拦截器
 | 
					      return // 当前请求指定要禁用缓存,跳过当前拦截器
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    // 获取 Pragma 用于判断是否禁用缓存
 | 
					    // 获取 Pragma 用于判断是否禁用缓存
 | 
				
			||||||
    const pragma = rOptions.headers.pragma
 | 
					    const pragma = rOptions.headers.pragma
 | 
				
			||||||
    if (pragma && (pragma.indexOf('no-cache') >= 0 || pragma.indexOf('no-store') >= 0)) {
 | 
					    if (pragma && (pragma.indexOf('no-cache') >= 0 || pragma.indexOf('no-store') >= 0)) {
 | 
				
			||||||
      return // 禁用缓存,跳过当前拦截
 | 
					      return // 当前请求指定要禁用缓存,跳过当前拦截器
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 最近编辑时间
 | 
					    // 最近编辑时间
 | 
				
			||||||
| 
						 | 
					@ -89,10 +91,12 @@ module.exports = {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 缓存未过期,直接拦截请求并响应304
 | 
					    // 缓存未过期,直接拦截请求并响应304
 | 
				
			||||||
    res.writeHead(304, {
 | 
					    res.writeHead(304, {
 | 
				
			||||||
      'Dev-Sidecar-Interceptor': 'cacheReq'
 | 
					      'DS-Interceptor': 'cache: ' + maxAge
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
    res.end()
 | 
					    res.end()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
 | 
				
			||||||
 | 
					    log.info('cache intercept:', url)
 | 
				
			||||||
    return true
 | 
					    return true
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  is (interceptOpt) {
 | 
					  is (interceptOpt) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,8 @@
 | 
				
			||||||
const url = require('url')
 | 
					const url = require('url')
 | 
				
			||||||
 | 
					const lodash = require('lodash')
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  name: 'proxy',
 | 
				
			||||||
 | 
					  priority: 121,
 | 
				
			||||||
  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
					  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
				
			||||||
    const { rOptions, log, RequestCounter } = context
 | 
					    const { rOptions, log, RequestCounter } = context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,17 +11,17 @@ module.exports = {
 | 
				
			||||||
    let proxyConf = interceptOpt.proxy
 | 
					    let proxyConf = interceptOpt.proxy
 | 
				
			||||||
    if (RequestCounter && interceptOpt.backup && interceptOpt.backup.length > 0) {
 | 
					    if (RequestCounter && interceptOpt.backup && interceptOpt.backup.length > 0) {
 | 
				
			||||||
      // 优选逻辑
 | 
					      // 优选逻辑
 | 
				
			||||||
      const backup = [proxyConf]
 | 
					      const backupList = [proxyConf]
 | 
				
			||||||
      for (const bk of interceptOpt.backup) {
 | 
					      for (const bk of interceptOpt.backup) {
 | 
				
			||||||
        backup.push(bk)
 | 
					        backupList.push(bk)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      const key = rOptions.hostname + '/' + interceptOpt.key
 | 
					      const key = rOptions.hostname + '/' + interceptOpt.key
 | 
				
			||||||
      const count = RequestCounter.getOrCreate(key, backup)
 | 
					      const count = RequestCounter.getOrCreate(key, backupList)
 | 
				
			||||||
      if (count.value == null) {
 | 
					      if (count.value == null) {
 | 
				
			||||||
        count.doRank()
 | 
					        count.doRank()
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (count.value == null) {
 | 
					      if (count.value == null) {
 | 
				
			||||||
        log.error('count value is null', count)
 | 
					        log.error('`count.value` is null, the count:', count)
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        count.doCount(count.value)
 | 
					        count.doCount(count.value)
 | 
				
			||||||
        proxyConf = count.value
 | 
					        proxyConf = count.value
 | 
				
			||||||
| 
						 | 
					@ -30,27 +33,34 @@ module.exports = {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let uri = req.url
 | 
					    // 获取代理目标地址
 | 
				
			||||||
    if (uri.indexOf('http') === 0) {
 | 
					    let proxyTarget
 | 
				
			||||||
      // eslint-disable-next-line node/no-deprecated-api
 | 
					 | 
				
			||||||
      const URL = url.parse(uri)
 | 
					 | 
				
			||||||
      uri = URL.path
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    let proxyTarget = proxyConf + uri
 | 
					 | 
				
			||||||
    if (interceptOpt.replace) {
 | 
					    if (interceptOpt.replace) {
 | 
				
			||||||
      const regexp = new RegExp(interceptOpt.replace)
 | 
					      const regexp = new RegExp(interceptOpt.replace)
 | 
				
			||||||
      proxyTarget = req.url.replace(regexp, proxyConf)
 | 
					      proxyTarget = req.url.replace(regexp, proxyConf)
 | 
				
			||||||
 | 
					    } else if (proxyConf.indexOf('http:') === 0 || proxyConf.indexOf('https:') === 0) {
 | 
				
			||||||
 | 
					      proxyTarget = proxyConf
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      let uri = req.url
 | 
				
			||||||
 | 
					      if (uri.indexOf('http') === 0) {
 | 
				
			||||||
 | 
					        // eslint-disable-next-line node/no-deprecated-api
 | 
				
			||||||
 | 
					        const URL = url.parse(uri)
 | 
				
			||||||
 | 
					        uri = URL.path
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      proxyTarget = proxyConf + uri
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // eslint-disable-next-line
 | 
					    // eslint-disable-next-line
 | 
				
			||||||
    // no-template-curly-in-string
 | 
					    // no-template-curly-in-string
 | 
				
			||||||
    // eslint-disable-next-line no-template-curly-in-string
 | 
					    // eslint-disable-next-line no-template-curly-in-string
 | 
				
			||||||
    proxyTarget = proxyTarget.replace('${host}', rOptions.hostname)
 | 
					    proxyTarget = proxyTarget.replace('${host}', rOptions.hostname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    log.info('拦截【proxy】: original:', rOptions.hostname, ',target:', proxyTarget)
 | 
					 | 
				
			||||||
    // const backup = interceptOpt.backup
 | 
					 | 
				
			||||||
    const proxy = proxyTarget.indexOf('http') === 0 ? proxyTarget : rOptions.protocol + '//' + proxyTarget
 | 
					    const proxy = proxyTarget.indexOf('http') === 0 ? proxyTarget : rOptions.protocol + '//' + proxyTarget
 | 
				
			||||||
    // eslint-disable-next-line node/no-deprecated-api
 | 
					    // eslint-disable-next-line node/no-deprecated-api
 | 
				
			||||||
    const URL = url.parse(proxy)
 | 
					    const URL = url.parse(proxy)
 | 
				
			||||||
 | 
					    rOptions.origional = lodash.cloneDeep(rOptions) // 备份原始请求参数
 | 
				
			||||||
 | 
					    delete rOptions.origional.agent
 | 
				
			||||||
 | 
					    delete rOptions.origional.headers
 | 
				
			||||||
    rOptions.protocol = URL.protocol
 | 
					    rOptions.protocol = URL.protocol
 | 
				
			||||||
    rOptions.hostname = URL.host
 | 
					    rOptions.hostname = URL.host
 | 
				
			||||||
    rOptions.host = URL.host
 | 
					    rOptions.host = URL.host
 | 
				
			||||||
| 
						 | 
					@ -69,8 +79,10 @@ module.exports = {
 | 
				
			||||||
      if (rOptions.agent && rOptions.agent.options) {
 | 
					      if (rOptions.agent && rOptions.agent.options) {
 | 
				
			||||||
        rOptions.agent.options.rejectUnauthorized = false
 | 
					        rOptions.agent.options.rejectUnauthorized = false
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					      res.setHeader('DS-Interceptor', `proxy: ${proxyTarget}, sni: ${interceptOpt.sni}`)
 | 
				
			||||||
      log.info('proxy intercept: hostname:', originHostname, ', target:', proxyTarget, ', sni replace servername:', rOptions.servername)
 | 
					      log.info('proxy intercept: hostname:', originHostname, ', target:', proxyTarget, ', sni replace servername:', rOptions.servername)
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
 | 
					      res.setHeader('DS-Interceptor', `proxy: ${proxyTarget}`)
 | 
				
			||||||
      log.info('proxy intercept: hostname:', originHostname, ', target:', proxyTarget)
 | 
					      log.info('proxy intercept: hostname:', originHostname, ', target:', proxyTarget)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,17 +1,34 @@
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  name: 'redirect',
 | 
				
			||||||
 | 
					  priority: 102,
 | 
				
			||||||
  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
					  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
				
			||||||
    const { rOptions, log } = context
 | 
					    const { rOptions, log } = context
 | 
				
			||||||
    const url = req.url
 | 
					
 | 
				
			||||||
    let redirect
 | 
					    let redirect
 | 
				
			||||||
    if (typeof interceptOpt.redirect === 'string') {
 | 
					    if (typeof interceptOpt.redirect === 'string') {
 | 
				
			||||||
      redirect = rOptions.protocol + '//' + interceptOpt.redirect + url
 | 
					      if (interceptOpt.redirect.indexOf('http:') === 0 || interceptOpt.redirect.indexOf('https:') === 0) {
 | 
				
			||||||
 | 
					        redirect = interceptOpt.redirect
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        redirect = rOptions.protocol + '//' + interceptOpt.redirect + req.url
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      redirect = interceptOpt.redirect(url)
 | 
					      redirect = interceptOpt.redirect(req.url)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    log.info('请求重定向:', rOptions.hostname, url, redirect)
 | 
					
 | 
				
			||||||
    res.writeHead(302, { Location: redirect })
 | 
					    // eslint-disable-next-line
 | 
				
			||||||
 | 
					    // no-template-curly-in-string
 | 
				
			||||||
 | 
					    // eslint-disable-next-line no-template-curly-in-string
 | 
				
			||||||
 | 
					    redirect = redirect.replace('${host}', rOptions.hostname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.writeHead(302, {
 | 
				
			||||||
 | 
					      Location: redirect,
 | 
				
			||||||
 | 
					      'DS-Interceptor': 'redirect'
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
    res.end()
 | 
					    res.end()
 | 
				
			||||||
    return true// 是否结束
 | 
					
 | 
				
			||||||
 | 
					    const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
 | 
				
			||||||
 | 
					    log.info(`redirect intercept: ${url} ➜ ${redirect}`)
 | 
				
			||||||
 | 
					    return true // true代表请求结束
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  is (interceptOpt) {
 | 
					  is (interceptOpt) {
 | 
				
			||||||
    return interceptOpt.redirect // 如果配置中有redirect,那么这个配置是需要redirect拦截的
 | 
					    return interceptOpt.redirect // 如果配置中有redirect,那么这个配置是需要redirect拦截的
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,17 @@
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  requestIntercept (context, interceptOpt) {
 | 
					  name: 'sni',
 | 
				
			||||||
 | 
					  priority: 122,
 | 
				
			||||||
 | 
					  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
				
			||||||
    const { rOptions, log } = context
 | 
					    const { rOptions, log } = context
 | 
				
			||||||
    if (interceptOpt.sni != null) {
 | 
					
 | 
				
			||||||
      rOptions.servername = interceptOpt.sni
 | 
					    rOptions.servername = interceptOpt.sni
 | 
				
			||||||
      if (rOptions.agent && rOptions.agent.options) {
 | 
					    if (rOptions.agent && rOptions.agent.options) {
 | 
				
			||||||
        rOptions.agent.options.rejectUnauthorized = false
 | 
					      rOptions.agent.options.rejectUnauthorized = false
 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      log.info('sni intercept: sni replace servername:', rOptions.hostname, '➜', rOptions.servername)
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    res.setHeader('DS-Interceptor', 'sni: ' + interceptOpt.sni)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    log.info('sni intercept: sni replace servername:', rOptions.hostname, '➜', rOptions.servername)
 | 
				
			||||||
    return true
 | 
					    return true
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  is (interceptOpt) {
 | 
					  is (interceptOpt) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,23 @@
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
  requestIntercept (context, interceptOpts, req, res, ssl, next) {
 | 
					  name: 'success',
 | 
				
			||||||
 | 
					  priority: 101,
 | 
				
			||||||
 | 
					  requestIntercept (context, interceptOpt, req, res, ssl, next) {
 | 
				
			||||||
    const { rOptions, log } = context
 | 
					    const { rOptions, log } = context
 | 
				
			||||||
    log.info('success:', rOptions.hostname, req.url)
 | 
					
 | 
				
			||||||
    res.writeHead(200)
 | 
					    res.writeHead(200, {
 | 
				
			||||||
    res.write('DevSidecar 200: \n\n request success, this request is matched by success intercept.\n\n 因配置success拦截器,本请求将直接返回成功')
 | 
					      'Content-Type': 'text/plain; charset=utf-8',
 | 
				
			||||||
 | 
					      'DS-Interceptor': 'success'
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    res.write(
 | 
				
			||||||
 | 
					      'DevSidecar 200: Request success.\n\n' +
 | 
				
			||||||
 | 
					      '  This request is matched by success intercept.\n\n' +
 | 
				
			||||||
 | 
					      '  因配置success拦截器,本请求直接返回200成功。'
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
    res.end()
 | 
					    res.end()
 | 
				
			||||||
    return true// 是否结束
 | 
					
 | 
				
			||||||
 | 
					    const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
 | 
				
			||||||
 | 
					    log.info('success intercept:', url)
 | 
				
			||||||
 | 
					    return true // true代表请求结束
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  is (interceptOpt) {
 | 
					  is (interceptOpt) {
 | 
				
			||||||
    return !!interceptOpt.success
 | 
					    return !!interceptOpt.success
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,14 @@
 | 
				
			||||||
const cacheReq = require('../req/cacheReq')
 | 
					const cacheReq = require('../req/cacheReq')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  name: 'cacheRes',
 | 
				
			||||||
 | 
					  priority: 201,
 | 
				
			||||||
  responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
 | 
					  responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
 | 
				
			||||||
    const { rOptions, log } = context
 | 
					    const { rOptions, log } = context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 只有GET请求,且响应码为2xx时才进行缓存
 | 
					    // 只有GET请求,且响应码为2xx时才进行缓存
 | 
				
			||||||
    if (rOptions.method !== 'GET' || proxyRes.statusCode < 200 || proxyRes.statusCode >= 300) {
 | 
					    if (rOptions.method !== 'GET' || proxyRes.statusCode < 200 || proxyRes.statusCode >= 300) {
 | 
				
			||||||
 | 
					      // res.setHeader('DS-Cache-Interceptor', `skip: 'method' or 'status' not match`)
 | 
				
			||||||
      return
 | 
					      return
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,6 +51,9 @@ module.exports = {
 | 
				
			||||||
        if (interceptOpt.cacheImmutable !== false && originalHeaders.cacheControl.value.indexOf('immutable') < 0) {
 | 
					        if (interceptOpt.cacheImmutable !== false && originalHeaders.cacheControl.value.indexOf('immutable') < 0) {
 | 
				
			||||||
          maxAge = maxAgeMatch[1]
 | 
					          maxAge = maxAgeMatch[1]
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
 | 
					          const url = `${rOptions.method} ➜ ${rOptions.protocol}//${rOptions.hostname}:${rOptions.port}${req.url}`
 | 
				
			||||||
 | 
					          res.setHeader('DS-Cache-Interceptor', `skip: ${maxAgeMatch[1]} > ${maxAge}`)
 | 
				
			||||||
 | 
					          log.info(`cache response intercept: skip: ${maxAgeMatch[1]} > ${maxAge}, url: ${url}`)
 | 
				
			||||||
          return
 | 
					          return
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
| 
						 | 
					@ -80,7 +86,7 @@ module.exports = {
 | 
				
			||||||
      res.setHeader('Expires', replaceHeaders.expires)
 | 
					      res.setHeader('Expires', replaceHeaders.expires)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    res.setHeader('Dev-Sidecar-Cache-Response-Interceptor', 'cacheRes:maxAge=' + maxAge)
 | 
					    res.setHeader('DS-Cache-Response-Interceptor', maxAge)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  is (interceptOpt) {
 | 
					  is (interceptOpt) {
 | 
				
			||||||
    const maxAge = cacheReq.getMaxAge(interceptOpt)
 | 
					    const maxAge = cacheReq.getMaxAge(interceptOpt)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,8 @@ function getScript (key, script) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
module.exports = {
 | 
					module.exports = {
 | 
				
			||||||
 | 
					  name: 'script',
 | 
				
			||||||
 | 
					  priority: 202,
 | 
				
			||||||
  responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
 | 
					  responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
 | 
				
			||||||
    const { rOptions, log, setting } = context
 | 
					    const { rOptions, log, setting } = context
 | 
				
			||||||
    let keys = interceptOpt.script
 | 
					    let keys = interceptOpt.script
 | 
				
			||||||
| 
						 | 
					@ -20,20 +22,23 @@ module.exports = {
 | 
				
			||||||
      keys = [keys]
 | 
					      keys = [keys]
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      let tags = getScript('global', monkey.get(setting.script.dirAbsolutePath).global.script)
 | 
					      const scripts = monkey.get(setting.script.dirAbsolutePath)
 | 
				
			||||||
 | 
					      let tags = getScript('global', scripts.global.script)
 | 
				
			||||||
      for (const key of keys) {
 | 
					      for (const key of keys) {
 | 
				
			||||||
        const script = monkey.get(setting.script.dirAbsolutePath)[key]
 | 
					        const script = scripts[key]
 | 
				
			||||||
        if (script == null) {
 | 
					        if (script == null) {
 | 
				
			||||||
          continue
 | 
					          continue
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        const scriptTag = getScript(key, script.script)
 | 
					        const scriptTag = getScript(key, script.script)
 | 
				
			||||||
        tags += '\r\n' + scriptTag
 | 
					        tags += '\r\n' + scriptTag
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      log.info('responseIntercept: insert script', rOptions.hostname, rOptions.path)
 | 
					      res.setHeader('DS-Script-Interceptor', 'true')
 | 
				
			||||||
 | 
					      log.info('script response intercept: insert script', rOptions.hostname, rOptions.path, ', head:', tags)
 | 
				
			||||||
      return {
 | 
					      return {
 | 
				
			||||||
        head: tags
 | 
					        head: tags
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } catch (err) {
 | 
					    } catch (err) {
 | 
				
			||||||
 | 
					      res.setHeader('DS-Script-Interceptor', 'error')
 | 
				
			||||||
      log.error('load monkey script error', err)
 | 
					      log.error('load monkey script error', err)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,7 +18,7 @@ function matched (hostname, overWallTargetMap) {
 | 
				
			||||||
    log.info(`matchHostname: matched overwall: '${hostname}' -> '${ret}' in pac.txt`)
 | 
					    log.info(`matchHostname: matched overwall: '${hostname}' -> '${ret}' in pac.txt`)
 | 
				
			||||||
    return true
 | 
					    return true
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    // log.debug(`matchHostname: matched overwall: Not-Matched '${hostname}' -> '${ret}' in pac.txt`)
 | 
					    log.debug(`matchHostname: matched overwall: Not-Matched '${hostname}' -> '${ret}' in pac.txt`)
 | 
				
			||||||
    return false
 | 
					    return false
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,4 +1,4 @@
 | 
				
			||||||
const interceptors = require('./lib/interceptor')
 | 
					const interceptorImpls = require('./lib/interceptor')
 | 
				
			||||||
const dnsUtil = require('./lib/dns')
 | 
					const dnsUtil = require('./lib/dns')
 | 
				
			||||||
const log = require('./utils/util.log')
 | 
					const log = require('./utils/util.log')
 | 
				
			||||||
const matchUtil = require('./utils/util.match')
 | 
					const matchUtil = require('./utils/util.match')
 | 
				
			||||||
| 
						 | 
					@ -49,7 +49,7 @@ module.exports = (config) => {
 | 
				
			||||||
      const hostname = req.url.split(':')[0]
 | 
					      const hostname = req.url.split(':')[0]
 | 
				
			||||||
      const inWhiteList = matchUtil.matchHostname(whiteList, hostname, 'in whiteList') != null
 | 
					      const inWhiteList = matchUtil.matchHostname(whiteList, hostname, 'in whiteList') != null
 | 
				
			||||||
      if (inWhiteList) {
 | 
					      if (inWhiteList) {
 | 
				
			||||||
        log.info('白名单域名,不拦截', hostname)
 | 
					        log.info('为白名单域名,不拦截:', hostname)
 | 
				
			||||||
        return false // 所有都不拦截
 | 
					        return false // 所有都不拦截
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      // 配置了拦截的域名,将会被代理
 | 
					      // 配置了拦截的域名,将会被代理
 | 
				
			||||||
| 
						 | 
					@ -57,29 +57,42 @@ module.exports = (config) => {
 | 
				
			||||||
      if (matched === true) {
 | 
					      if (matched === true) {
 | 
				
			||||||
        return matched // 拦截
 | 
					        return matched // 拦截
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return null // 由下一个拦截器判断
 | 
					      return null // 未匹配到任何拦截配置,由下一个拦截器判断
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    createIntercepts: (context) => {
 | 
					    createIntercepts: (context) => {
 | 
				
			||||||
      const rOptions = context.rOptions
 | 
					      const rOptions = context.rOptions
 | 
				
			||||||
      const hostname = rOptions.hostname
 | 
					      const interceptOpts = matchUtil.matchHostnameAll(intercepts, rOptions.hostname, 'get interceptOpts')
 | 
				
			||||||
      const interceptOpts = matchUtil.matchHostname(intercepts, hostname, 'get interceptOpts')
 | 
					 | 
				
			||||||
      if (!interceptOpts) { // 该域名没有配置拦截器,直接过
 | 
					      if (!interceptOpts) { // 该域名没有配置拦截器,直接过
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const matchIntercepts = []
 | 
					      const matchIntercepts = []
 | 
				
			||||||
 | 
					      const matchInterceptsOpts = {}
 | 
				
			||||||
      for (const regexp in interceptOpts) { // 遍历拦截配置
 | 
					      for (const regexp in interceptOpts) { // 遍历拦截配置
 | 
				
			||||||
        const interceptOpt = interceptOpts[regexp]
 | 
					        const interceptOpt = interceptOpts[regexp]
 | 
				
			||||||
        interceptOpt.key = regexp
 | 
					        // interceptOpt.key = regexp
 | 
				
			||||||
        if (regexp !== true) {
 | 
					        if (regexp !== true && regexp !== 'true') {
 | 
				
			||||||
          if (!matchUtil.isMatched(rOptions.path, regexp)) {
 | 
					          if (!matchUtil.isMatched(rOptions.path, regexp)) {
 | 
				
			||||||
            continue
 | 
					            continue
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        for (const impl of interceptors) {
 | 
					        log.info(`interceptor matched, regexp: '${regexp}' =>`, JSON.stringify(interceptOpt), ', path:', rOptions.path)
 | 
				
			||||||
 | 
					        for (const impl of interceptorImpls) {
 | 
				
			||||||
          // 根据拦截配置挑选合适的拦截器来处理
 | 
					          // 根据拦截配置挑选合适的拦截器来处理
 | 
				
			||||||
          if (impl.is && impl.is(interceptOpt)) {
 | 
					          if (impl.is && impl.is(interceptOpt)) {
 | 
				
			||||||
            const interceptor = {}
 | 
					            let action = 'add'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 如果存在同名拦截器,则order值越大,优先级越高
 | 
				
			||||||
 | 
					            const matchedInterceptOpt = matchInterceptsOpts[impl.name]
 | 
				
			||||||
 | 
					            if (matchedInterceptOpt) {
 | 
				
			||||||
 | 
					              if (matchedInterceptOpt.order >= interceptOpt.order) {
 | 
				
			||||||
 | 
					                log.warn(`duplicate interceptor: ${impl.name}, hostname: ${rOptions.hostname}`)
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					              action = 'replace'
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const interceptor = { name: impl.name, priority: impl.priority }
 | 
				
			||||||
            if (impl.requestIntercept) {
 | 
					            if (impl.requestIntercept) {
 | 
				
			||||||
              // req拦截器
 | 
					              // req拦截器
 | 
				
			||||||
              interceptor.requestIntercept = (context, req, res, ssl, next) => {
 | 
					              interceptor.requestIntercept = (context, req, res, ssl, next) => {
 | 
				
			||||||
| 
						 | 
					@ -91,10 +104,26 @@ module.exports = (config) => {
 | 
				
			||||||
                return impl.responseIntercept(context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next)
 | 
					                return impl.responseIntercept(context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next)
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            matchIntercepts.push(interceptor)
 | 
					
 | 
				
			||||||
 | 
					            // log.info(`${action} interceptor: ${impl.name}, hostname: ${rOptions.hostname}, regexp: ${regexp}`)
 | 
				
			||||||
 | 
					            if (action === 'add') {
 | 
				
			||||||
 | 
					              matchIntercepts.push(interceptor)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              matchIntercepts[matchedInterceptOpt.index] = interceptor
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            matchInterceptsOpts[impl.name] = {
 | 
				
			||||||
 | 
					              order: interceptOpt.order || 0,
 | 
				
			||||||
 | 
					              index: matchIntercepts.length - 1
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      matchIntercepts.sort((a, b) => { return a.priority - b.priority })
 | 
				
			||||||
 | 
					      // for (const interceptor of matchIntercepts) {
 | 
				
			||||||
 | 
					      //   log.info('interceptor:', interceptor.name, 'priority:', interceptor.priority)
 | 
				
			||||||
 | 
					      // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      return matchIntercepts
 | 
					      return matchIntercepts
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,6 +94,16 @@ function merge (oldObj, newObj) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					function deleteNullItems (target) {
 | 
				
			||||||
 | 
					  lodash.forEach(target, (item, key) => {
 | 
				
			||||||
 | 
					    if (item == null || item === '[delete]') {
 | 
				
			||||||
 | 
					      delete target[key]
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if (lodash.isObject(item)) {
 | 
				
			||||||
 | 
					      deleteNullItems(item)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function matchHostnameAll (hostMap, hostname, action) {
 | 
					function matchHostnameAll (hostMap, hostname, action) {
 | 
				
			||||||
  // log.debug('matchHostnameAll:', action, hostMap)
 | 
					  // log.debug('matchHostnameAll:', action, hostMap)
 | 
				
			||||||
| 
						 | 
					@ -108,29 +118,9 @@ function matchHostnameAll (hostMap, hostname, action) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let values = {}
 | 
					  let values = {}
 | 
				
			||||||
  let hasValue = false
 | 
					  let value
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // 域名快速匹配:直接匹配 或者 两种前缀通配符匹配
 | 
					  // 通配符匹配 或 正则表达式匹配(优先级:1,最低)
 | 
				
			||||||
  let value = hostMap.origin[hostname]
 | 
					 | 
				
			||||||
  if (value) {
 | 
					 | 
				
			||||||
    log.info(`matchHostnameAll: ${action}: '${hostname}' -> '${hostname}': ${JSON.stringify(value)}`)
 | 
					 | 
				
			||||||
    values = merge(values, value)
 | 
					 | 
				
			||||||
    hasValue = true
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  value = hostMap.origin['*' + hostname]
 | 
					 | 
				
			||||||
  if (value) {
 | 
					 | 
				
			||||||
    log.info(`matchHostnameAll: ${action}: '${hostname}' -> '*${hostname}': ${JSON.stringify(value)}`)
 | 
					 | 
				
			||||||
    values = merge(values, value)
 | 
					 | 
				
			||||||
    hasValue = true
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  value = hostMap.origin['*.' + hostname]
 | 
					 | 
				
			||||||
  if (value) {
 | 
					 | 
				
			||||||
    log.info(`matchHostnameAll: ${action}: '${hostname}' -> '*.${hostname}': ${JSON.stringify(value)}`)
 | 
					 | 
				
			||||||
    values = merge(values, value)
 | 
					 | 
				
			||||||
    hasValue = true
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // 通配符匹配 或 正则表达式匹配
 | 
					 | 
				
			||||||
  for (const target in hostMap) {
 | 
					  for (const target in hostMap) {
 | 
				
			||||||
    if (target === 'origin') {
 | 
					    if (target === 'origin') {
 | 
				
			||||||
      continue
 | 
					      continue
 | 
				
			||||||
| 
						 | 
					@ -149,17 +139,37 @@ function matchHostnameAll (hostMap, hostname, action) {
 | 
				
			||||||
    // 正则表达式匹配
 | 
					    // 正则表达式匹配
 | 
				
			||||||
    if (hostname.match(regexp)) {
 | 
					    if (hostname.match(regexp)) {
 | 
				
			||||||
      value = hostMap[target]
 | 
					      value = hostMap[target]
 | 
				
			||||||
      // log.info(`matchHostname: ${action}: '${hostname}' -> '${target}': ${JSON.stringify(value)}`)
 | 
					      log.info(`matchHostnameOne: ${action}: '${hostname}' -> '${target}': ${JSON.stringify(value)}`)
 | 
				
			||||||
      values = merge(values, value)
 | 
					      values = merge(values, value)
 | 
				
			||||||
      hasValue = true
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (hasValue) {
 | 
					  // 域名快速匹配:直接匹配 或者 两种前缀通配符匹配
 | 
				
			||||||
    log.info(`*matchHostnameAll*: ${action}: '${hostname}':`, JSON.stringify(values))
 | 
					  // 优先级:2
 | 
				
			||||||
 | 
					  value = hostMap.origin['*' + hostname]
 | 
				
			||||||
 | 
					  if (value) {
 | 
				
			||||||
 | 
					    log.info(`matchHostnameOne: ${action}: '${hostname}' -> '*${hostname}': ${JSON.stringify(value)}`)
 | 
				
			||||||
 | 
					    values = merge(values, value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // 优先级:3
 | 
				
			||||||
 | 
					  value = hostMap.origin['*.' + hostname]
 | 
				
			||||||
 | 
					  if (value) {
 | 
				
			||||||
 | 
					    log.info(`matchHostnameOne: ${action}: '${hostname}' -> '*.${hostname}': ${JSON.stringify(value)}`)
 | 
				
			||||||
 | 
					    values = merge(values, value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  // 优先级:4,最高(注:优先级高的配置,可以覆盖优先级低的配置,甚至有空配置时,可以移除已有配置)
 | 
				
			||||||
 | 
					  value = hostMap.origin[hostname]
 | 
				
			||||||
 | 
					  if (value) {
 | 
				
			||||||
 | 
					    log.info(`matchHostnameOne: ${action}: '${hostname}' -> '${hostname}': ${JSON.stringify(value)}`)
 | 
				
			||||||
 | 
					    values = merge(values, value)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!lodash.isEmpty(values)) {
 | 
				
			||||||
 | 
					    deleteNullItems(values)
 | 
				
			||||||
 | 
					    log.info(`matchHostnameAll: ${action}: '${hostname}':`, JSON.stringify(values))
 | 
				
			||||||
    return values
 | 
					    return values
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    log.debug(`*matchHostnameAll*: ${action}: '${hostname}' Not-Matched`)
 | 
					    log.debug(`matchHostnameAll: ${action}: '${hostname}' Not-Matched`)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue