diff --git a/README.md b/README.md
index 7bc1ff4..55eecad 100644
--- a/README.md
+++ b/README.md
@@ -14,8 +14,8 @@
>
## 打个广告
>
-> https://ai.handsfree.work
-> 我的ChatGPT,开发者必备,无需fanQ,快速,稳定,价格良心,100问仅需1元,按需扣费,余额永久有效,大家可以试试
+> https://github.com/certd/certd
+> 我的开源证书管理工具项目,全自动申请和部署证书,有需求的可以去试试,帮忙点个star
@@ -399,9 +399,9 @@ npm run electron:build
1、 加群(请备注dev-sidecar,或简称DS)
- QQ 1群:390691483,人数:500 / 500(满)
-- QQ 2群:[667666069](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=n4nksr4sji93vZtD5e8YEHRT6qbh6VyQ&authKey=XKBZnzmoiJrAFyOT4V%2BCrgX5c13ds59b84g%2FVRhXAIQd%2FlAiilsuwDRGWJct%2B570&noverify=0&group_code=667666069),人数:439 / 500
+- QQ 2群:[667666069](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=n4nksr4sji93vZtD5e8YEHRT6qbh6VyQ&authKey=XKBZnzmoiJrAFyOT4V%2BCrgX5c13ds59b84g%2FVRhXAIQd%2FlAiilsuwDRGWJct%2B570&noverify=0&group_code=667666069),人数:447 / 500
- QQ 3群:419807815,人数:500 / 500(满)
-- QQ 4群:438148299,人数:200 / 200(满)
+- QQ 4群:[438148299](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=i_NCBB5f_Bkm2JsEV1tLs2TkQ79UlCID&authKey=nMsVJbJ6P%2FGNO7Q6vsVUadXRKnULUURwR8zvUZJnP3IgzhHYPhYdcBCHvoOh8vYr&noverify=0&group_code=438148299),人数:203 / 1000
- QQ 5群:[767622917](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=nAWi_Rxj7mM4Unp5LMiatmUWhGimtbcB&authKey=aswmlWGjbt3GIWXtvjB2GJqqAKuv7hWjk6UBs3MTb%2Biyvr%2Fsbb1kA9CjF6sK7Hgg&noverify=0&group_code=767622917),人数:016 / 200(new)
diff --git a/lerna.json b/lerna.json
index d735ae4..cf1a86b 100644
--- a/lerna.json
+++ b/lerna.json
@@ -14,5 +14,5 @@
"ignore": []
}
},
- "version": "1.8.8"
+ "version": "1.8.9"
}
diff --git a/packages/core/package.json b/packages/core/package.json
index a42a906..6a5aee2 100644
--- a/packages/core/package.json
+++ b/packages/core/package.json
@@ -1,6 +1,6 @@
{
"name": "@docmirror/dev-sidecar",
- "version": "1.8.8",
+ "version": "1.8.9",
"description": "给开发者的加速代理工具",
"main": "src/index.js",
"keywords": [
@@ -17,7 +17,7 @@
"test": "mocha"
},
"dependencies": {
- "@docmirror/mitmproxy": "^1.8.8",
+ "@docmirror/mitmproxy": "^1.8.9",
"agentkeepalive": "^2.1.1",
"babel-preset-es2020": "^1.0.2",
"charset": "^1.0.0",
diff --git a/packages/core/src/config/index.js b/packages/core/src/config/index.js
index e1dbbf0..bfa1f4d 100644
--- a/packages/core/src/config/index.js
+++ b/packages/core/src/config/index.js
@@ -65,7 +65,10 @@ module.exports = {
timeout: 20000,
keepAliveTimeout: 30000
}
- }
+ },
+
+ // 慢速IP延迟时间:测速超过该值时,则视为延迟高,显示为橙色
+ lowSpeedDelay: 150
},
compatible: {
// **** 自定义兼容配置 **** //
diff --git a/packages/gui/extra/proxy/domestic-domain-allowlist.txt b/packages/gui/extra/proxy/domestic-domain-allowlist.txt
index b745ec9..181d1dc 100644
--- a/packages/gui/extra/proxy/domestic-domain-allowlist.txt
+++ b/packages/gui/extra/proxy/domestic-domain-allowlist.txt
@@ -1,6 +1,6 @@
[SwitchyOmega Conditions]
; Require: SwitchyOmega >= 2.3.2
-; Update Date: 2024/10/14
+; Update Date: 2024/11/07
; Author: Pluwen
; Usage: https://github.com/FelisCatus/SwitchyOmega/wiki/RuleListUsage
@@ -236,6 +236,8 @@
*.ccb.com
*.ccgslb.com
*.ccgslb.net
+*.cckefu.net
+*.cckefu3.com
*.cctv.com
*.cctvpic.com
*.cdn-apple.com
@@ -333,6 +335,7 @@
*.dmzj.com
*.dns.com
*.dnspao.com
+*.doc88.com
*.docer.com
*.docin.com
*.docschina.org
@@ -395,6 +398,8 @@
*.fiio.com
*.fir.im
*.firefox.com
+*.fj12379.com
+*.fjdzyz.com
*.fjgdwl.com
*.fjhxbank.com
*.fliggy.com
@@ -471,6 +476,7 @@
*.homestyler.com
*.hommk.com
*.hongxiu.com
+*.honor.com
*.hostbuf.com
*.hostker.com
*.hotmail.com
@@ -838,6 +844,7 @@
*.pterclub.com
*.pythonclub.org
*.qbox.me
+*.qcc.com
*.qcloud.com
*.qcloudcdn.com
*.qcwgg.com
@@ -883,6 +890,7 @@
*.redacted.ch
*.renren.com
*.renrenche.com
+*.renrendoc.com
*.researchgate.net
*.rework.tools
*.rkecloud.com
@@ -966,7 +974,6 @@
*.staticfile.org
*.steamcn.com
*.steamcontent.com
-*.steamdb.info
*.subhd.tv
*.sui.com
*.suning.com
diff --git a/packages/gui/package.json b/packages/gui/package.json
index 0d07cb9..f5c18f2 100644
--- a/packages/gui/package.json
+++ b/packages/gui/package.json
@@ -1,6 +1,6 @@
{
"name": "@docmirror/dev-sidecar-gui",
- "version": "1.8.8",
+ "version": "1.8.9",
"private": false,
"license": "MPL-2.0",
"main": "index.js",
@@ -21,8 +21,10 @@
"name": "Greper"
},
"dependencies": {
- "@docmirror/dev-sidecar": "^1.8.8",
- "@docmirror/mitmproxy": "^1.8.8",
+ "@docmirror/dev-sidecar": "^1.8.9",
+ "@docmirror/mitmproxy": "^1.8.9",
+ "@mihomo-party/sysproxy": "^2.0.4",
+ "@natmri/platform-napi": "0.0.7",
"adm-zip": "^0.5.5",
"ant-design-vue": "^1.6.5",
"compressing": "^1.5.1",
@@ -52,9 +54,9 @@
"@vue/eslint-config-standard": "^5.1.2",
"babel-eslint": "^10.1.0",
"electron": "^17.4.11",
+ "electron-builder": "^23.0.3",
"electron-devtools-installer": "^3.1.0",
"electron-icon-builder": "^2.0.1",
- "electron-builder": "^23.0.3",
"eslint": "^6.7.2",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
diff --git a/packages/gui/src/background.js b/packages/gui/src/background.js
index 9bab6ae..2e7da09 100644
--- a/packages/gui/src/background.js
+++ b/packages/gui/src/background.js
@@ -1,12 +1,15 @@
'use strict'
/* global __static */
import path from 'path'
-import { app, protocol, BrowserWindow, Menu, Tray, ipcMain, dialog, powerMonitor, nativeImage, nativeTheme, globalShortcut } from 'electron'
+import { app, protocol, BrowserWindow, Menu, Tray, ipcMain, dialog, nativeImage, nativeTheme, globalShortcut } from 'electron'
+import { powerMonitor } from './background/powerMonitor'
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib'
import backend from './bridge/backend'
import DevSidecar from '@docmirror/dev-sidecar'
import log from './utils/util.log'
import minimist from 'minimist'
+
+const isWindows = process.platform === 'win32'
// eslint-disable-next-line no-unused-vars
const isMac = process.platform === 'darwin'
// import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer'
@@ -187,6 +190,11 @@ function createWindow (startHideWindow) {
Menu.setApplicationMenu(null)
win.setMenu(null)
+ // !!IMPORTANT
+ if (isWindows) {
+ powerMonitor.setupMainWindow(win)
+ }
+
if (process.env.WEBPACK_DEV_SERVER_URL) {
// Load the url of the dev server if in development mode
win.loadURL(process.env.WEBPACK_DEV_SERVER_URL)
@@ -443,8 +451,14 @@ if (!isFirstInstance) {
}
powerMonitor.on('shutdown', async (e) => {
- e.preventDefault()
+ if (e) {
+ e.preventDefault()
+ }
log.info('系统关机,恢复代理设置')
+ if (isWindows) {
+ const Sysproxy = require('@mihomo-party/sysproxy')
+ Sysproxy.triggerManualProxy(false, '', 0, '')
+ }
await quit()
})
})
diff --git a/packages/gui/src/background/powerMonitor.js b/packages/gui/src/background/powerMonitor.js
new file mode 100644
index 0000000..dd8b9ac
--- /dev/null
+++ b/packages/gui/src/background/powerMonitor.js
@@ -0,0 +1,135 @@
+import { powerMonitor as _powerMonitor } from 'electron'
+import { setMainWindowHandle, insertWndProcHook, removeWndProcHook, releaseShutdownBlock, acquireShutdownBlock } from '@natmri/platform-napi'
+
+class PowerMonitor {
+ constructor () {
+ this.setup = false
+ this._listeners = []
+ this._shutdownCallback = null
+ }
+
+ /**
+ * @param {BrowserWindow} window
+ */
+ setupMainWindow (window) {
+ if (!this.setup) {
+ setMainWindowHandle(window.getNativeWindowHandle().readBigInt64LE())
+ this.setup = true
+ }
+ }
+
+ addListener (event, listener) {
+ return this.on(event, listener)
+ }
+
+ removeListener (event, listener) {
+ return this.off(event, listener)
+ }
+
+ removeAllListeners (event) {
+ if (event === 'shutdown' && process.platform === 'win32') {
+ this._listeners = []
+ if (this._shutdownCallback) {
+ removeWndProcHook()
+ releaseShutdownBlock()
+ this._shutdownCallback = null
+ }
+ } else {
+ return _powerMonitor.removeAllListeners(event)
+ }
+ }
+
+ on (event, listener) {
+ if (event === 'shutdown' && process.platform === 'win32') {
+ if (!this._shutdownCallback) {
+ this._shutdownCallback = async () => {
+ await Promise.all(this._listeners.map((fn) => fn()))
+ releaseShutdownBlock()
+ }
+ insertWndProcHook(this._shutdownCallback)
+ acquireShutdownBlock('正在停止 DevSidecar 代理')
+ }
+ this._listeners.push(listener)
+ } else {
+ return _powerMonitor.on(event, listener)
+ }
+ }
+
+ off (event, listener) {
+ if (event === 'shutdown' && process.platform === 'win32') {
+ this._listeners = this._listeners.filter((fn) => fn !== listener)
+ } else {
+ return _powerMonitor.off(event, listener)
+ }
+ }
+
+ once (event, listener) {
+ if (event === 'shutdown' && process.platform === 'win32') {
+ return this.on(event, listener)
+ } else {
+ return _powerMonitor.once(event, listener)
+ }
+ }
+
+ emit (event, ...args) {
+ return _powerMonitor.emit(event, ...args)
+ }
+
+ eventNames () {
+ return _powerMonitor.eventNames()
+ }
+
+ getMaxListeners () {
+ return _powerMonitor.getMaxListeners()
+ }
+
+ listeners (event) {
+ return _powerMonitor.listeners(event)
+ }
+
+ rawListeners (event) {
+ return _powerMonitor.rawListeners(event)
+ }
+
+ listenerCount (event, listener) {
+ return _powerMonitor.listenerCount(event, listener)
+ }
+
+ /**
+ * @returns {boolean}
+ */
+ get onBatteryPower () {
+ return _powerMonitor.onBatteryPower
+ }
+
+ /**
+ * @param {number} idleThreshold
+ * @returns {'active'|'idle'|'locked'|'unknown'}
+ */
+ getSystemIdleState (idleThreshold) {
+ return _powerMonitor.getSystemIdleState(idleThreshold)
+ }
+
+ /**
+ * @returns {number}
+ */
+ getSystemIdleTime () {
+ return _powerMonitor.getSystemIdleTime()
+ }
+
+ /**
+ * @returns {'unknown'|'nominal'|'fair'|'serious'|'critical'}
+ */
+ getCurrentThermalState () {
+ return _powerMonitor.getCurrentThermalState()
+ }
+
+ /**
+ * @returns {boolean}
+ */
+ isOnBatteryPower () {
+ return _powerMonitor.isOnBatteryPower()
+ }
+}
+
+export const powerMonitor = new PowerMonitor()
diff --git a/packages/gui/src/view/pages/plugin/overwall.vue b/packages/gui/src/view/pages/plugin/overwall.vue
index 725475a..5823797 100644
--- a/packages/gui/src/view/pages/plugin/overwall.vue
+++ b/packages/gui/src/view/pages/plugin/overwall.vue
@@ -24,7 +24,7 @@
启用PAC
-
PAC内收录了常见的被封杀的域名,当里面某些域名你不想被拦截时,可以关闭PAC
+ PAC内收录了常见的被封杀的域名
当里面某些域名你不想被拦截时,你可以配置这些域名为禁用
,也可以关闭PAC
@@ -46,16 +46,23 @@
- PAC没有拦截到的域名,可以在此处定义
+ PAC没有拦截到的域名,可以在此处定义;配置为禁用
时,将不使用梯子
-
+
+
+
+
+ {{ item.label }}
+
+
+
@@ -116,7 +123,17 @@ export default {
return {
key: 'plugin.overwall',
targets: undefined,
- servers: undefined
+ servers: undefined,
+ overwallOptions: [
+ {
+ value: true,
+ label: '启用'
+ },
+ {
+ value: false,
+ label: '禁用'
+ }
+ ]
}
},
created () {
diff --git a/packages/gui/src/view/pages/server.vue b/packages/gui/src/view/pages/server.vue
index 32105a1..b5f25e7 100644
--- a/packages/gui/src/view/pages/server.vue
+++ b/packages/gui/src/view/pages/server.vue
@@ -166,8 +166,11 @@
- ms
+ ms
+
使用以下DNS获取IP进行测速
@@ -211,7 +214,7 @@
+ :color="element.time?(element.time>config.server.setting.lowSpeedDelay?'orange':'green'):'red'" :key='index'>
{{ element.host }} {{ element.time }}{{ element.time ? 'ms' : '' }} {{ element.dns }}
@@ -260,7 +263,7 @@ export default {
if (!this.config || !this.config.server || !this.config.server.dns || !this.config.server.dns.providers) {
return options
}
- _.forEach(this.config.server.dns.providers, (dnsConf, key) => {
+ _.forEach(this.config.server.dns.providers, (dnsConfig, key) => {
options.push({
value: key,
label: key
diff --git a/packages/gui/src/view/style/theme/dark.scss b/packages/gui/src/view/style/theme/dark.scss
index e2128b2..d11c2a1 100644
--- a/packages/gui/src/view/style/theme/dark.scss
+++ b/packages/gui/src/view/style/theme/dark.scss
@@ -122,6 +122,12 @@ $dark-input: #777; //输入框:背景色
border-color: #505f5f;
color: #90cb9f;
}
+ /* 标签:警告 */
+ .ant-tag-orange{
+ background: #5a5750;
+ border-color: #5a5750;
+ color: #cfa572;
+ }
/* 按钮 */
.ant-btn:not(.ant-btn-danger, .ant-btn-primary){
diff --git a/packages/gui/vue.config.js b/packages/gui/vue.config.js
index 8ab804b..65392ba 100644
--- a/packages/gui/vue.config.js
+++ b/packages/gui/vue.config.js
@@ -32,6 +32,27 @@ module.exports = {
},
pluginOptions: {
electronBuilder: {
+ externals: [
+ '@mihomo-party/sysproxy',
+ '@mihomo-party/sysproxy-win32-ia32-msvc',
+ '@mihomo-party/sysproxy-win32-x64-msvc',
+ '@mihomo-party/sysproxy-win32-arm64-msvc',
+ '@mihomo-party/sysproxy-linux-x64-gnu',
+ '@mihomo-party/sysproxy-linux-arm64-gnu',
+ '@mihomo-party/sysproxy-darwin-x64',
+ '@mihomo-party/sysproxy-darwin-arm64',
+ '@natmri/platform-napi',
+ "@natmri/platform-napi-win32-x64-msvc",
+ "@natmri/platform-napi-darwin-x64",
+ "@natmri/platform-napi-linux-x64-gnu",
+ "@natmri/platform-napi-darwin-arm64",
+ "@natmri/platform-napi-linux-arm64-gnu",
+ "@natmri/platform-napi-linux-arm64-musl",
+ "@natmri/platform-napi-win32-arm64-msvc",
+ "@natmri/platform-napi-linux-arm-gnueabihf",
+ "@natmri/platform-napi-linux-x64-musl",
+ "@natmri/platform-napi-win32-ia32-msvc"
+ ],
nodeIntegration: true,
// Provide an array of files that, when changed, will recompile the main process and restart Electron
// Your main process file will be added by default
diff --git a/packages/mitmproxy/package.json b/packages/mitmproxy/package.json
index 1512675..f87e11e 100644
--- a/packages/mitmproxy/package.json
+++ b/packages/mitmproxy/package.json
@@ -1,6 +1,6 @@
{
"name": "@docmirror/mitmproxy",
- "version": "1.8.8",
+ "version": "1.8.9",
"description": "",
"main": "src/index.js",
"keywords": [
diff --git a/packages/mitmproxy/src/lib/dns/index.js b/packages/mitmproxy/src/lib/dns/index.js
index e93df39..10ff23d 100644
--- a/packages/mitmproxy/src/lib/dns/index.js
+++ b/packages/mitmproxy/src/lib/dns/index.js
@@ -1,12 +1,14 @@
const DNSOverTLS = require('./tls.js')
const DNSOverHTTPS = require('./https.js')
const DNSOverIpAddress = require('./ipaddress.js')
+const DNSOverPreSetIpList = require('./preset.js')
const matchUtil = require('../../utils/util.match')
-const log = require('../../utils/util.log')
module.exports = {
initDNS (dnsProviders, preSetIpList) {
const dnsMap = {}
+
+ // 创建普通的DNS
for (const provider in dnsProviders) {
const conf = dnsProviders[provider]
@@ -20,30 +22,33 @@ module.exports = {
// 设置DNS名称到name属性中
dnsMap[provider].name = provider
+ dnsMap[provider].type = conf.type
}
+
+ // 创建预设IP的DNS
+ dnsMap.PreSet = new DNSOverPreSetIpList(preSetIpList)
+
return dnsMap
},
hasDnsLookup (dnsConfig, hostname) {
- let providerName = matchUtil.matchHostname(dnsConfig.mapping, hostname, 'get dns providerName')
+ let providerName = null
- // usa已重命名为cloudflare,以下为向下兼容处理
- if (providerName === 'usa') {
- providerName = 'cloudflare'
+ // 先匹配 预设IP配置
+ const hostnamePreSetIpList = matchUtil.matchHostname(dnsConfig.preSetIpList, hostname, 'matched preSetIpList')
+ if (hostnamePreSetIpList) {
+ return dnsConfig.dnsMap.PreSet
}
- // 如果为空,尝试从预设IP中匹配,如果配置过预设IP,则随便
- if (providerName == null) {
- const hostnamePreSetIpList = matchUtil.matchHostname(dnsConfig.preSetIpList, hostname, 'matched preSetIpList')
- if (hostnamePreSetIpList) {
- for (const name in dnsConfig.providers) {
- log.debug(`当前域名未配置过DNS,但配置了预设IP,现返回DNS '${name}' 作为预设IP的使用工具,hostname: ${hostname}, preSetIpList:`, hostnamePreSetIpList)
- return dnsConfig.providers[name]
- }
- }
+ // 再匹配 DNS映射配置
+ providerName = matchUtil.matchHostname(dnsConfig.mapping, hostname, 'get dns providerName')
+
+ // 由于DNS中的usa已重命名为cloudflare,所以做以下处理,为了向下兼容
+ if (providerName === 'usa' && dnsConfig.dnsMap.usa == null && dnsConfig.dnsMap.cloudflare != null) {
+ return dnsConfig.dnsMap.cloudflare
}
if (providerName) {
- return dnsConfig.providers[providerName]
+ return dnsConfig.dnsMap[providerName]
}
}
}
diff --git a/packages/mitmproxy/src/lib/dns/preset.js b/packages/mitmproxy/src/lib/dns/preset.js
new file mode 100644
index 0000000..69b63f5
--- /dev/null
+++ b/packages/mitmproxy/src/lib/dns/preset.js
@@ -0,0 +1,39 @@
+const BaseDNS = require('./base')
+const matchUtil = require('../../utils/util.match')
+
+function mapToList (ipMap) {
+ const ipList = []
+ for (const key in ipMap) {
+ if (!ipMap[key]) continue
+ ipList.push(ipMap[key])
+ }
+ return ipList
+}
+
+module.exports = class DNSOverPreSetIpList extends BaseDNS {
+ constructor (preSetIpList) {
+ super()
+ this.preSetIpList = preSetIpList
+ this.name = 'PreSet'
+ this.type = 'PreSet'
+ }
+
+ async _lookup (hostname) {
+ // 获取当前域名的预设IP列表
+ let hostnamePreSetIpList = matchUtil.matchHostname(this.preSetIpList, hostname, 'matched preSetIpList')
+ if (hostnamePreSetIpList && (hostnamePreSetIpList.length > 0 || hostnamePreSetIpList.length === undefined)) {
+ if (hostnamePreSetIpList.length > 0) {
+ hostnamePreSetIpList = hostnamePreSetIpList.slice()
+ } else {
+ hostnamePreSetIpList = mapToList(hostnamePreSetIpList)
+ }
+
+ if (hostnamePreSetIpList.length > 0) {
+ return hostnamePreSetIpList
+ }
+ }
+
+ // 未预设当前域名的IP列表
+ return []
+ }
+}
diff --git a/packages/mitmproxy/src/lib/interceptor/impl/res/cacheRes.js b/packages/mitmproxy/src/lib/interceptor/impl/res/cacheRes.js
index f2f51e1..1fd783d 100644
--- a/packages/mitmproxy/src/lib/interceptor/impl/res/cacheRes.js
+++ b/packages/mitmproxy/src/lib/interceptor/impl/res/cacheRes.js
@@ -6,8 +6,25 @@ module.exports = {
responseIntercept (context, interceptOpt, req, res, proxyReq, proxyRes, ssl, next) {
const { rOptions, log } = context
- // 只有GET请求,且响应码为2xx时才进行缓存
- if (rOptions.method !== 'GET' || proxyRes.statusCode < 200 || proxyRes.statusCode >= 300) {
+ // 只有GET请求
+ if (rOptions.method !== 'GET') {
+ return
+ }
+
+ // 判断当前响应码是否不使用缓存
+ if (interceptOpt.cacheExcludeStatusCodeList && interceptOpt.cacheExcludeStatusCodeList[proxyRes.statusCode + '']) {
+ return
+ }
+
+ // 响应码为 200~303 时才进行缓存(可通过以下两个参数调整范围)
+ let minStatusCode = interceptOpt.cacheMinStatusCode || 200
+ let maxStatusCode = interceptOpt.cacheMaxStatusCode || 303
+ if (minStatusCode > maxStatusCode) {
+ const temp = minStatusCode
+ minStatusCode = maxStatusCode
+ maxStatusCode = temp
+ }
+ if (proxyRes.statusCode < minStatusCode || proxyRes.statusCode > maxStatusCode) {
// res.setHeader('DS-Cache-Response-Interceptor', `skip: 'method' or 'status' not match`)
return
}
diff --git a/packages/mitmproxy/src/lib/interceptor/impl/res/responseReplace.js b/packages/mitmproxy/src/lib/interceptor/impl/res/responseReplace.js
index f848e2e..f9970c9 100644
--- a/packages/mitmproxy/src/lib/interceptor/impl/res/responseReplace.js
+++ b/packages/mitmproxy/src/lib/interceptor/impl/res/responseReplace.js
@@ -20,6 +20,7 @@ function replaceResponseHeaders (newHeaders, res, proxyRes) {
const preHeaders = {}
// 替换响应头
+ const needDeleteKeys = []
for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {
const headerKey = proxyRes.rawHeaders[i].toLowerCase()
@@ -27,15 +28,19 @@ function replaceResponseHeaders (newHeaders, res, proxyRes) {
if (newHeaderValue) {
if (newHeaderValue !== proxyRes.rawHeaders[i + 1]) {
preHeaders[headerKey] = proxyRes.rawHeaders[i + 1] // 先保存原先响应头
- if (newHeaderValue === REMOVE) { // 由于拦截配置中不允许配置null,会被删,所以配置一个[remove],当作删除响应头的意思
+ if (newHeaderValue === REMOVE) { // 由于拦截配置中不允许配置null,会被删,所以配置一个 "[remove]",当作删除响应头的意思
proxyRes.rawHeaders[i + 1] = ''
} else {
proxyRes.rawHeaders[i + 1] = newHeaderValue
}
}
- delete newHeaders[headerKey]
+ needDeleteKeys.push(headerKey)
}
}
+ // 处理删除响应头
+ for (const headerKey of needDeleteKeys) {
+ delete newHeaders[headerKey]
+ }
// 新增响应头
for (const headerKey in newHeaders) {
const headerValue = newHeaders[headerKey]
diff --git a/packages/mitmproxy/src/lib/proxy/middleware/overwall.js b/packages/mitmproxy/src/lib/proxy/middleware/overwall.js
index 72f0aa6..6eedab6 100644
--- a/packages/mitmproxy/src/lib/proxy/middleware/overwall.js
+++ b/packages/mitmproxy/src/lib/proxy/middleware/overwall.js
@@ -10,17 +10,23 @@ const { Buffer } = require('buffer')
let pacClient = null
function matched (hostname, overWallTargetMap) {
+ // 匹配配置文件
const ret1 = matchUtil.matchHostname(overWallTargetMap, hostname, 'matched overwall')
if (ret1) {
- return 'overwall config'
+ return 'in config'
+ } else if (ret1 === false || ret1 === 'false') {
+ log.debug(`域名 ${hostname} 的overwall配置为 false,跳过增强功能,即使它在 pac.txt 里`)
+ return null
}
+
+ // 匹配 pac.txt
if (pacClient == null) {
return null
}
const ret = pacClient.FindProxyForURL('https://' + hostname, hostname)
if (ret && ret.indexOf('PROXY ') === 0) {
log.info(`matchHostname: matched overwall: '${hostname}' -> '${ret}' in pac.txt`)
- return 'overwall pac'
+ return 'in pac.txt'
} else {
log.debug(`matchHostname: matched overwall: Not-Matched '${hostname}' -> '${ret}' in pac.txt`)
return null
@@ -148,14 +154,7 @@ function createOverwallMiddleware (overWallConfig) {
return {
sslConnectInterceptor: (req, cltSocket, head) => {
const hostname = req.url.split(':')[0]
- const ret = matched(hostname, overWallTargetMap)
- if (ret == null) {
- return null // 返回 null,由下一个拦截器校验
- }
- if (ret === false) {
- return false // 不拦截,预留这个判断,避免以后修改 matched 方法的代码出BUG
- }
- return true // 拦截
+ return matched(hostname, overWallTargetMap)
},
requestIntercept (context, req, res, ssl, next) {
const { rOptions, log, RequestCounter } = context
@@ -164,7 +163,7 @@ function createOverwallMiddleware (overWallConfig) {
}
const hostname = rOptions.hostname
const matchedResult = matched(hostname, overWallTargetMap)
- if (matchedResult == null || matchedResult === false) {
+ if (matchedResult == null || matchedResult === false || matchedResult === 'false') {
return
}
const cacheKey = '__over_wall_proxy__'
diff --git a/packages/mitmproxy/src/lib/proxy/mitmproxy/createConnectHandler.js b/packages/mitmproxy/src/lib/proxy/mitmproxy/createConnectHandler.js
index 78198eb..f614cd5 100644
--- a/packages/mitmproxy/src/lib/proxy/mitmproxy/createConnectHandler.js
+++ b/packages/mitmproxy/src/lib/proxy/mitmproxy/createConnectHandler.js
@@ -9,11 +9,11 @@ const jsonApi = require('../../../json')
function isSslConnect (sslConnectInterceptors, req, cltSocket, head) {
for (const intercept of sslConnectInterceptors) {
const ret = intercept(req, cltSocket, head)
- log.debug(`拦截判断结果:${ret}, url: ${req.url}, intercept:`, intercept)
- if (ret === false || ret === true) {
- return ret
+ log.debug('当前拦截器返回结果:', ret, `, url: ${req.url}, intercept:`, intercept)
+ if (ret == null) {
+ continue
}
- // continue
+ return !(ret === false || ret === 'false')
}
return false
}
@@ -58,19 +58,64 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig = null, isDire
const isDnsIntercept = {}
const hostport = `${hostname}:${port}`
try {
+ // 客户端的连接事件监听
+ cltSocket.on('timeout', (e) => {
+ log.error(`cltSocket timeout: ${hostport}, errorMsg: ${e.message}`)
+ })
+ cltSocket.on('error', (e) => {
+ log.error(`cltSocket error: ${hostport}, errorMsg: ${e.message}`)
+ })
+ // 开发过程中,如有需要可以将此参数临时改为true,打印所有事件的日志
+ const printDebugLog = false && process.env.NODE_ENV === 'development'
+ if (printDebugLog) {
+ cltSocket.on('close', (hadError) => {
+ log.debug('【cltSocket close】', hadError)
+ })
+ cltSocket.on('connect', () => {
+ log.debug('【cltSocket connect】')
+ })
+ cltSocket.on('connectionAttempt', (ip, port, family) => {
+ log.debug(`【cltSocket connectionAttempt】${ip}:${port}, family:`, family)
+ })
+ cltSocket.on('connectionAttemptFailed', (ip, port, family) => {
+ log.debug(`【cltSocket connectionAttemptFailed】${ip}:${port}, family:`, family)
+ })
+ cltSocket.on('connectionAttemptTimeout', (ip, port, family) => {
+ log.debug(`【cltSocket connectionAttemptTimeout】${ip}:${port}, family:`, family)
+ })
+ cltSocket.on('data', (data) => {
+ log.debug('【cltSocket data】')
+ })
+ cltSocket.on('drain', () => {
+ log.debug('【cltSocket drain】')
+ })
+ cltSocket.on('end', () => {
+ log.debug('【cltSocket end】')
+ })
+ // cltSocket.on('lookup', (err, address, family, host) => {
+ // })
+ cltSocket.on('ready', () => {
+ log.debug('【cltSocket ready】')
+ })
+ }
+
+ // ---------------------------------------------------------------------------------------------------
+
const options = {
port,
host: hostname,
connectTimeout: 10000
}
- if (dnsConfig && dnsConfig.providers) {
+ if (dnsConfig && dnsConfig.dnsMap) {
const dns = DnsUtil.hasDnsLookup(dnsConfig, hostname)
if (dns) {
options.lookup = dnsLookup.createLookupFunc(null, dns, 'connect', hostport, isDnsIntercept)
}
}
+ // 代理连接事件监听
const proxySocket = net.connect(options, () => {
if (!isDirect) log.info('Proxy connect start:', hostport)
+ else log.debug('Direct connect start:', hostport)
cltSocket.write('HTTP/1.1 200 Connection Established\r\n' +
'Proxy-agent: dev-sidecar\r\n' +
@@ -80,15 +125,9 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig = null, isDire
cltSocket.pipe(proxySocket)
})
- cltSocket.on('timeout', (e) => {
- log.error(`cltSocket timeout: ${hostport}, errorMsg: ${e.message}`)
- })
- cltSocket.on('error', (e) => {
- log.error(`cltSocket error: ${hostport}, errorMsg: ${e.message}`)
- })
proxySocket.on('timeout', () => {
const cost = new Date() - start
- const errorMsg = `代理连接超时: ${hostport}, cost: ${cost} ms`
+ const errorMsg = `${isDirect ? '直连' : '代理连接'}超时: ${hostport}, cost: ${cost} ms`
log.error(errorMsg)
cltSocket.destroy()
@@ -102,8 +141,8 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig = null, isDire
proxySocket.on('error', (e) => {
// 连接失败,可能被GFW拦截,或者服务端拥挤
const cost = new Date() - start
- const errorMsg = `代理连接失败: ${hostport}, cost: ${cost} ms, errorMsg: ${e.message}`
- log.error(errorMsg)
+ const errorMsg = `${isDirect ? '直连' : '代理连接'}失败: ${hostport}, cost: ${cost} ms, errorMsg: ${e.message}`
+ log.error(`${errorMsg}\r\n`, e)
cltSocket.destroy()
@@ -113,8 +152,41 @@ function connect (req, cltSocket, head, hostname, port, dnsConfig = null, isDire
log.error(`记录ip失败次数,用于优选ip! hostname: ${hostname}, ip: ${ip}, reason: ${errorMsg}, dns: ${dns.name}`)
}
})
+
+ if (printDebugLog) {
+ proxySocket.on('close', (hadError) => {
+ log.debug('【proxySocket close】', hadError)
+ })
+ proxySocket.on('connect', () => {
+ log.debug('【proxySocket connect】')
+ })
+ proxySocket.on('connectionAttempt', (ip, port, family) => {
+ log.debug(`【proxySocket connectionAttempt】${ip}:${port}, family:`, family)
+ })
+ proxySocket.on('connectionAttemptFailed', (ip, port, family) => {
+ log.debug(`【proxySocket connectionAttemptFailed】${ip}:${port}, family:`, family)
+ })
+ proxySocket.on('connectionAttemptTimeout', (ip, port, family) => {
+ log.debug(`【proxySocket connectionAttemptTimeout】${ip}:${port}, family:`, family)
+ })
+ proxySocket.on('data', (data) => {
+ log.debug('【proxySocket data】')
+ })
+ proxySocket.on('drain', () => {
+ log.debug('【proxySocket drain】')
+ })
+ proxySocket.on('end', () => {
+ log.debug('【proxySocket end】')
+ })
+ // proxySocket.on('lookup', (err, address, family, host) => {
+ // })
+ proxySocket.on('ready', () => {
+ log.debug('【proxySocket ready】')
+ })
+ }
+
return proxySocket
} catch (e) {
- log.error(`Proxy connect error: ${hostport}, exception:`, e)
+ log.error(`${isDirect ? '直连' : '代理连接'}错误: ${hostport}, error:`, e)
}
}
diff --git a/packages/mitmproxy/src/lib/proxy/mitmproxy/createRequestHandler.js b/packages/mitmproxy/src/lib/proxy/mitmproxy/createRequestHandler.js
index fd09159..5c8375b 100644
--- a/packages/mitmproxy/src/lib/proxy/mitmproxy/createRequestHandler.js
+++ b/packages/mitmproxy/src/lib/proxy/mitmproxy/createRequestHandler.js
@@ -110,17 +110,23 @@ module.exports = function createRequestHandler (createIntercepts, middlewares, e
log.info('发起代理请求:', url, (rOptions.servername ? ', sni: ' + rOptions.servername : ''), ', headers:', jsonApi.stringify2(rOptions.headers))
const isDnsIntercept = {}
- if (dnsConfig && dnsConfig.providers) {
+ if (dnsConfig && dnsConfig.dnsMap) {
let dns = DnsUtil.hasDnsLookup(dnsConfig, rOptions.hostname)
if (!dns && rOptions.servername) {
- dns = dnsConfig.providers.quad9
+ dns = dnsConfig.dnsMap.quad9
if (dns) {
log.info(`域名 ${rOptions.hostname} 在dns中未配置,但使用了 sni: ${rOptions.servername}, 必须使用dns,现默认使用 'quad9' DNS.`)
}
}
if (dns) {
rOptions.lookup = dnsLookup.createLookupFunc(res, dns, 'request url', url, isDnsIntercept)
+ log.debug(`域名 ${rOptions.hostname} DNS: ${dns.name}`)
+ res.setHeader('DS-DNS', dns.name)
+ } else {
+ log.info(`域名 ${rOptions.hostname} 在DNS中未配置`)
}
+ } else {
+ log.info(`域名 ${rOptions.hostname} DNS配置不存在`)
}
// rOptions.sigalgs = 'RSA-PSS+SHA256:RSA-PSS+SHA512:ECDSA+SHA256'
@@ -147,9 +153,9 @@ module.exports = function createRequestHandler (createIntercepts, middlewares, e
proxyReq = (rOptions.protocol === 'https:' ? https : http).request(rOptions, (proxyRes) => {
const cost = new Date() - start
if (rOptions.protocol === 'https:') {
- log.info(`代理请求返回: ${url}, cost: ${cost} ms`)
+ log.info(`代理请求返回: 【${proxyRes.statusCode}】${url}, cost: ${cost} ms`)
} else {
- log.info(`请求返回: ${url}, cost: ${cost} ms`)
+ log.info(`请求返回: 【${proxyRes.statusCode}】${url}, cost: ${cost} ms`)
}
// console.log('request:', proxyReq, proxyReq.socket)
diff --git a/packages/mitmproxy/src/lib/proxy/mitmproxy/dnsLookup.js b/packages/mitmproxy/src/lib/proxy/mitmproxy/dnsLookup.js
index 83109d9..d96019b 100644
--- a/packages/mitmproxy/src/lib/proxy/mitmproxy/dnsLookup.js
+++ b/packages/mitmproxy/src/lib/proxy/mitmproxy/dnsLookup.js
@@ -8,7 +8,7 @@ module.exports = {
return (hostname, options, callback) => {
const tester = speedTest.getSpeedTester(hostname)
- if (tester && tester.ready) {
+ if (tester) {
const aliveIpObj = tester.pickFastAliveIpObj()
if (aliveIpObj) {
log.info(`----- ${action}: ${hostname}, use alive ip from dns '${aliveIpObj.dns}': ${aliveIpObj.host}${target} -----`)
@@ -16,7 +16,7 @@ module.exports = {
callback(null, aliveIpObj.host, 4)
return
} else {
- log.info(`----- ${action}: ${hostname}, no alive ip${target}, tester:`, tester)
+ log.info(`----- ${action}: ${hostname}, no alive ip${target}, tester: { "ready": ${tester.ready}, "backupList": ${JSON.stringify(tester.backupList)} }`)
}
}
dns.lookup(hostname).then(ip => {
diff --git a/packages/mitmproxy/src/lib/proxy/mitmproxy/index.js b/packages/mitmproxy/src/lib/proxy/mitmproxy/index.js
index 6c0f414..2c4fc26 100644
--- a/packages/mitmproxy/src/lib/proxy/mitmproxy/index.js
+++ b/packages/mitmproxy/src/lib/proxy/mitmproxy/index.js
@@ -41,7 +41,7 @@ module.exports = {
port = ~~port
const speedTestConfig = dnsConfig.speedTest
- const dnsMap = dnsConfig.providers
+ const dnsMap = dnsConfig.dnsMap
if (speedTestConfig) {
const dnsProviders = speedTestConfig.dnsProviders
const map = {}
diff --git a/packages/mitmproxy/src/options.js b/packages/mitmproxy/src/options.js
index 1a165fc..2fa79af 100644
--- a/packages/mitmproxy/src/options.js
+++ b/packages/mitmproxy/src/options.js
@@ -95,7 +95,7 @@ module.exports = (serverConfig) => {
port: serverConfig.port,
dnsConfig: {
preSetIpList,
- providers: dnsUtil.initDNS(serverConfig.dns.providers, preSetIpList),
+ dnsMap: dnsUtil.initDNS(serverConfig.dns.providers, preSetIpList),
mapping: matchUtil.domainMapRegexply(dnsMapping),
speedTest: serverConfig.dns.speedTest
},
@@ -119,10 +119,10 @@ module.exports = (serverConfig) => {
const matched = matchUtil.matchHostname(intercepts, hostname, 'matched intercepts')
if ((!!matched) === true) {
log.debug(`拦截器拦截:${req.url}, matched:`, matched)
- return true // 拦截
+ return matched // 拦截
}
- return null // 未匹配到任何拦截配置,由下一个拦截器判断
+ return null // 不在白名单中,也未配置在拦截功能中,跳过当前拦截器,由下一个拦截器判断
},
createIntercepts: (context) => {
const rOptions = context.rOptions
diff --git a/packages/mitmproxy/src/utils/util.match.js b/packages/mitmproxy/src/utils/util.match.js
index 14d69aa..3966b8d 100644
--- a/packages/mitmproxy/src/utils/util.match.js
+++ b/packages/mitmproxy/src/utils/util.match.js
@@ -26,11 +26,11 @@ function domainRegexply (target) {
}
function domainMapRegexply (hostMap) {
+ if (hostMap == null) {
+ return { origin: {} }
+ }
const regexpMap = {}
const origin = {} // 用于快速匹配,见matchHostname、matchHostnameAll方法
- if (hostMap == null) {
- return regexpMap
- }
lodash.each(hostMap, (value, domain) => {
if (domain.indexOf('*') >= 0 || domain[0] === '^') {
const regDomain = domain[0] !== '^' ? domainRegexply(domain) : domain
@@ -61,17 +61,17 @@ function matchHostname (hostMap, hostname, action) {
// 域名快速匹配:直接匹配 或者 两种前缀通配符匹配
let value = hostMap.origin[hostname]
- if (value) {
+ if (value != null) {
log.info(`matchHostname: ${action}: '${hostname}' -> { "${hostname}": ${JSON.stringify(value)} }`)
return value // 快速匹配成功
}
value = hostMap.origin['*' + hostname]
- if (value) {
+ if (value != null) {
log.info(`matchHostname: ${action}: '${hostname}' -> { "*${hostname}": ${JSON.stringify(value)} }`)
return value // 快速匹配成功
}
value = hostMap.origin['*.' + hostname]
- if (value) {
+ if (value != null) {
log.info(`matchHostname: ${action}: '${hostname}' -> { "*.${hostname}": ${JSON.stringify(value)} }`)
return value // 快速匹配成功
}
@@ -127,8 +127,8 @@ function matchHostnameAll (hostMap, hostname, action) {
let value
// 通配符匹配 或 正则表达式匹配(优先级:1,最低)
- for (const target in hostMap) {
- if (target === 'origin') {
+ for (const regexp in hostMap) {
+ if (regexp === 'origin') {
continue
}
@@ -136,16 +136,10 @@ function matchHostnameAll (hostMap, hostname, action) {
// continue // 不是通配符匹配串,也不是正则表达式,跳过
// }
- // 如果是通配符匹配串,转换为正则表达式
- let regexp = target
- // if (target[0] !== '^') {
- // regexp = domainRegexply(regexp)
- // }
-
// 正则表达式匹配
if (hostname.match(regexp)) {
- value = hostMap[target]
- log.debug(`matchHostname-one: ${action}: '${hostname}' -> { "${target}": ${JSON.stringify(value)} }`)
+ value = hostMap[regexp]
+ log.debug(`matchHostname-one: ${action}: '${hostname}' -> { "${regexp}": ${JSON.stringify(value)} }`)
values = merge(values, value)
}
}