feature: 请求超时时间和连接超时时间可配置化 (#354)

pull/355/head
王良 2024-09-12 11:52:29 +08:00 committed by GitHub
parent 9b2c5f1f25
commit d3cb4ceaa0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 115 additions and 40 deletions

View File

@ -52,6 +52,18 @@ module.exports = {
rootCaFile: {
certPath: getRootCaCertPath(),
keyPath: getRootCaKeyPath()
},
// 默认超时时间配置
defaultTimeout: 20000, // 请求超时时间
defaultKeepAliveTimeout: 30000, // 连接超时时间
// 指定域名超时时间配置
timeoutMapping: {
'github.com': {
timeout: 20000,
keepAliveTimeout: 30000
}
}
},
intercept: {

View File

@ -36,6 +36,7 @@
<a-input-number v-model="config.server.port" :min="0" :max="65535"/>
<div class="form-help">修改后需要重启应用</div>
</a-form-item>
<hr/>
<a-form-item label="全局校验SSL" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-checkbox v-model="config.server.setting.NODE_TLS_REJECT_UNAUTHORIZED">
NODE_TLS_REJECT_UNAUTHORIZED
@ -54,6 +55,7 @@
<a-input-search addon-before="Key" enter-button="" @search="onKeySelect"
v-model="config.server.setting.rootCaFile.keyPath"/>
</a-form-item>
<hr/>
<a-form-item label="启用拦截" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-checkbox v-model="config.server.intercept.enabled">
启用拦截
@ -72,7 +74,19 @@
<vue-json-editor style="height:100%;" ref="editor" v-model="config.server.intercepts" mode="code"
:show-btns="false" :expandedOnStart="true"></vue-json-editor>
</a-tab-pane>
<a-tab-pane tab="域名白名单" key="3">
<a-tab-pane tab="超时时间设置" key="3">
<div style="height:100%;display:flex;flex-direction:column;padding-right:10px">
<a-form-item label="默认超时时间" :label-col="labelCol" :wrapper-col="wrapperCol">
请求<a-input-number v-model="config.server.setting.defaultTimeout" :step="1000" :min="1000"/> ms对应 timeout 属性<br/>
连接<a-input-number v-model="config.server.setting.defaultKeepAliveTimeout" :step="1000" :min="1000"/> ms对应 keepAliveTimeout 属性
</a-form-item>
<hr/>
<div>这里指定域名的超时时间<span class="form-help">以下github的配置为示例预计将在 1.8.7 版本删除</span></div>
<vue-json-editor style="flex-grow:1;min-height:300px;margin-top:10px" ref="editor" v-model="config.server.setting.timeoutMapping" mode="code"
:show-btns="false" :expandedOnStart="true"></vue-json-editor>
</div>
</a-tab-pane>
<a-tab-pane tab="域名白名单" key="4">
<a-row style="margin-top:10px">
<a-col span="19">
<div>这里配置哪些域名不需要通过代理</div>
@ -90,16 +104,18 @@
</a-col>
</a-row>
</a-tab-pane>
<a-tab-pane tab="IP预设置" key="4">
<div>注意IP预设置功能需要与 `DNS设置` `IP测速` 功能一起使用才会生效</div>
<vue-json-editor style="height:94%;margin-top:10px;" ref="editor" v-model="config.server.preSetIpList" mode="code"
:show-btns="false" :expandedOnStart="true"></vue-json-editor>
<a-tab-pane tab="IP预设置" key="5">
<div style="height:100%;display:flex;flex-direction:column">
<div>注意IP预设置功能需要与 `DNS设置` `IP测速` 功能一起使用才会生效</div>
<vue-json-editor style="flex-grow:1;min-height:300px;margin-top:10px;" ref="editor" v-model="config.server.preSetIpList" mode="code"
:show-btns="false" :expandedOnStart="true"></vue-json-editor>
</div>
</a-tab-pane>
<a-tab-pane tab="DNS服务管理" key="5">
<a-tab-pane tab="DNS服务管理" key="6">
<vue-json-editor style="height:100%;" ref="editor" v-model="config.server.dns.providers" mode="code"
:show-btns="false" :expandedOnStart="true"></vue-json-editor>
</a-tab-pane>
<a-tab-pane tab="DNS设置" key="6">
<a-tab-pane tab="DNS设置" key="7">
<div>
<a-row style="margin-top:10px">
<a-col span="19">
@ -127,7 +143,7 @@
</a-row>
</div>
</a-tab-pane>
<a-tab-pane tab="IP测速" key="7">
<a-tab-pane tab="IP测速" key="8">
<div class="ip-tester" style="padding-right: 10px">
<a-alert type="info" message="对从dns获取到的ip进行测速使用速度最快的ip进行访问。对使用增强功能的域名没啥用"></a-alert>
<a-form-item label="开启dns测速" :label-col="labelCol" :wrapper-col="wrapperCol">
@ -136,8 +152,7 @@
</a-checkbox>
</a-form-item>
<a-form-item label="自动测试间隔" :label-col="labelCol" :wrapper-col="wrapperCol">
<a-input-number id="inputNumber" v-model="getSpeedTestConfig().interval" :step="1000" :min="1"/>
ms
<a-input-number id="inputNumber" v-model="getSpeedTestConfig().interval" :step="1000" :min="1"/> ms
</a-form-item>
<div>使用以下dns获取ip进行测速</div>
<a-row style="margin-top:10px">
@ -367,7 +382,7 @@ export default {
}, 5000)
},
async handleTabChange (key) {
if (key !== '2' && key !== '4' && key !== '5') {
if (key !== '2' && key !== '3' && key !== '5' && key !== '6') {
return
}

View File

@ -3,22 +3,58 @@ const Agent = require('./ProxyHttpAgent')
const HttpsAgent = require('./ProxyHttpsAgent')
const tunnelAgent = require('tunnel-agent')
const log = require('../../../utils/util.log')
const matchUtil = require('../../../utils/util.match')
const util = exports
const httpsAgent = new HttpsAgent({
keepAlive: true,
timeout: 20000,
keepAliveTimeout: 30000, // free socket keepalive for 30 seconds
rejectUnauthorized: false
})
const httpAgent = new Agent({
keepAlive: true,
timeout: 20000,
keepAliveTimeout: 30000 // free socket keepalive for 30 seconds
})
const httpsAgentCache = {}
const httpAgentCache = {}
let socketId = 0
let httpsOverHttpAgent, httpOverHttpsAgent, httpsOverHttpsAgent
function getTimeoutConfig (hostname, serverSetting) {
const timeoutMapping = serverSetting.timeoutMapping
const timeoutConfig = matchUtil.matchHostname(timeoutMapping, hostname, 'get timeoutConfig') || {}
return {
timeout: timeoutConfig.timeout || serverSetting.defaultTimeout || 20000,
keepAliveTimeout: timeoutConfig.keepAliveTimeout || serverSetting.defaultKeepAliveTimeout || 30000
}
}
function createHttpsAgent (timeoutConfig) {
const key = timeoutConfig.timeout + '-' + timeoutConfig.keepAliveTimeout
if (!httpsAgentCache[key]) {
httpsAgentCache[key] = new HttpsAgent({
keepAlive: true,
timeout: timeoutConfig.timeout,
keepAliveTimeout: timeoutConfig.keepAliveTimeout,
rejectUnauthorized: false
})
}
return httpsAgentCache[key]
}
function createHttpAgent (timeoutConfig) {
const key = timeoutConfig.timeout + '-' + timeoutConfig.keepAliveTimeout
if (!httpAgentCache[key]) {
httpAgentCache[key] = new Agent({
keepAlive: true,
timeout: timeoutConfig.timeout,
keepAliveTimeout: timeoutConfig.keepAliveTimeout
})
}
return httpAgentCache[key]
}
function createAgent (protocol, timeoutConfig) {
return protocol === 'https:'
? createHttpsAgent(timeoutConfig)
: createHttpAgent(timeoutConfig)
}
util.parseHostnameAndPort = (host, defaultPort) => {
let arr = host.match(/^(\[[^\]]+\])(?::(\d+))?$/) // 尝试解析IPv6
if (arr) {
@ -42,7 +78,7 @@ util.parseHostnameAndPort = (host, defaultPort) => {
return arr
}
util.getOptionsFromRequest = (req, ssl, externalProxy = null) => {
util.getOptionsFromRequest = (req, ssl, externalProxy = null, serverSetting) => {
// eslint-disable-next-line node/no-deprecated-api
const urlObject = url.parse(req.url)
const defaultPort = ssl ? 443 : 80
@ -62,16 +98,19 @@ util.getOptionsFromRequest = (req, ssl, externalProxy = null) => {
}
}
// 解析host和port
const arr = util.parseHostnameAndPort(req.headers.host)
const hostname = arr[0]
const port = arr[1] || defaultPort
delete headers['proxy-connection']
let agent
if (!externalProxyUrl) {
// keepAlive
if (headers.connection !== 'close') {
if (protocol === 'https:') {
agent = httpsAgent
} else {
agent = httpAgent
}
const timeoutConfig = getTimeoutConfig(hostname, serverSetting)
// log.info(`get timeoutConfig '${hostname}':`, timeoutConfig)
agent = createAgent(protocol, timeoutConfig)
headers.connection = 'keep-alive'
} else {
agent = false
@ -80,19 +119,16 @@ util.getOptionsFromRequest = (req, ssl, externalProxy = null) => {
agent = util.getTunnelAgent(protocol === 'https:', externalProxyUrl)
}
// 解析host和port
const arr = util.parseHostnameAndPort(req.headers.host)
// 初始化options
const options = {
protocol: protocol,
protocol,
method: req.method,
url: req.url,
hostname: arr[0],
port: arr[1] || defaultPort,
hostname,
port,
path: urlObject.path,
headers: req.headers,
agent: agent
agent
}
// eslint-disable-next-line node/no-deprecated-api

View File

@ -48,6 +48,16 @@ function loadPacLastModifiedTime (pacTxt) {
}
}
function formatDate (date) {
const year = date.getFullYear()
const month = (date.getMonth() + 1).toString().padStart(2, '0')
const day = date.getDate().toString().padStart(2, '0')
const hours = date.getHours().toString().padStart(2, '0')
const minutes = date.getMinutes().toString().padStart(2, '0')
const seconds = date.getSeconds().toString().padStart(2, '0')
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
}
// 保存 pac 内容到 `~/pac.txt` 文件中
function savePacFile (pacTxt) {
const pacFilePath = getTmpPacFilePath()
@ -68,7 +78,7 @@ function savePacFile (pacTxt) {
if (utimesErr) {
log.error('修改 pac.txt 文件时间失败:', utimesErr)
} else {
log.info(`${pacFilePath} 文件时间已被修改其最近更新时间 '${lastModifiedTime}'`)
log.info(`'${pacFilePath}' 文件的修改时间已更新为其最近更新时间 '${formatDate(lastModifiedTime)}'`)
}
})
})

View File

@ -15,7 +15,7 @@ module.exports = function createRequestHandler (createIntercepts, middlewares, e
return function requestHandler (req, res, ssl) {
let proxyReq
const rOptions = commonUtil.getOptionsFromRequest(req, ssl, externalProxy)
const rOptions = commonUtil.getOptionsFromRequest(req, ssl, externalProxy, setting)
if (rOptions.agent) {
rOptions.agent.options.rejectUnauthorized = setting.verifySsl

View File

@ -5,10 +5,10 @@ const log = require('../../../utils/util.log')
// copy from node-http-proxy. ^_^
// create connectHandler function
module.exports = function createUpgradeHandler () {
module.exports = function createUpgradeHandler (serverSetting) {
// return
return function upgradeHandler (req, cltSocket, head, ssl) {
const clientOptions = util.getOptionsFromRequest(req, ssl)
const clientOptions = util.getOptionsFromRequest(req, ssl, null, serverSetting)
const proxyReq = (ssl ? https : http).request(clientOptions)
proxyReq.on('error', (e) => {
log.error('upgradeHandler error:', e)

View File

@ -60,7 +60,7 @@ module.exports = {
setting
)
const upgradeHandler = createUpgradeHandler()
const upgradeHandler = createUpgradeHandler(setting)
const fakeServersCenter = createFakeServerCenter({
caCertPath,

View File

@ -19,6 +19,7 @@ function buildIntercepts (intercepts) {
module.exports = (serverConfig) => {
const intercepts = matchUtil.domainMapRegexply(buildIntercepts(serverConfig.intercepts))
const whiteList = matchUtil.domainMapRegexply(serverConfig.whiteList)
const timeoutMapping = matchUtil.domainMapRegexply(serverConfig.setting.timeoutMapping)
const dnsMapping = serverConfig.dns.mapping
const setting = serverConfig.setting
@ -29,6 +30,7 @@ module.exports = (serverConfig) => {
if (setting.verifySsl !== false) {
setting.verifySsl = true
}
setting.timeoutMapping = timeoutMapping
const overWallConfig = serverConfig.plugin.overwall
if (overWallConfig.pac && overWallConfig.pac.enabled) {