feature: 请求超时时间和连接超时时间可配置化 (#354)
parent
9b2c5f1f25
commit
d3cb4ceaa0
|
@ -52,6 +52,18 @@ module.exports = {
|
|||
rootCaFile: {
|
||||
certPath: getRootCaCertPath(),
|
||||
keyPath: getRootCaKeyPath()
|
||||
},
|
||||
|
||||
// 默认超时时间配置
|
||||
defaultTimeout: 20000, // 请求超时时间
|
||||
defaultKeepAliveTimeout: 30000, // 连接超时时间
|
||||
|
||||
// 指定域名超时时间配置
|
||||
timeoutMapping: {
|
||||
'github.com': {
|
||||
timeout: 20000,
|
||||
keepAliveTimeout: 30000
|
||||
}
|
||||
}
|
||||
},
|
||||
intercept: {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)}'`)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -60,7 +60,7 @@ module.exports = {
|
|||
setting
|
||||
)
|
||||
|
||||
const upgradeHandler = createUpgradeHandler()
|
||||
const upgradeHandler = createUpgradeHandler(setting)
|
||||
|
||||
const fakeServersCenter = createFakeServerCenter({
|
||||
caCertPath,
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue