feat: response拦截器

pull/180/head
xiaojunnuo 2020-11-16 00:19:56 +08:00
parent 278ae695ca
commit 922c8d667b
18 changed files with 498 additions and 97 deletions

View File

@ -15,7 +15,8 @@
### 2、 dns优选
根据网络状况智能解析域名ip地址获取最佳网络速度
比如:
1. 解决git push 偶尔失败需要输入账号密码的问题fatal: TaskCanceledException encountered
1. 解决git push 偶尔失败需要输入账号密码的问题(
fatal: TaskCanceledException encountered / fatal: HttpRequestException encountered
2. 解决github头像加载不出来的问题
3. 解决gist.github.com访问不到的问题

View File

@ -19,6 +19,9 @@ module.exports = {
},
'/.*/.*/blame/': {
redirect: 'hub.fastgit.org'
},
'/.*/[^\\/]*$': {
script: 'console.log("123123132")'
}
},
'raw.githubusercontent.com': {
@ -101,12 +104,12 @@ module.exports = {
}
},
mapping: {
'img.shields.io': 'aliyun',
'*.github.com': 'aliyun',
'*.githubusercontent.com': 'aliyun',
'*.githubassets.com': 'aliyun',
'img.shields.io': 'usa',
'*.github.com': 'usa',
'*.githubusercontent.com': 'usa',
'*.githubassets.com': 'usa',
// "解决push的时候需要输入密码的问题",
'github.com': 'aliyun'
'github.com': 'usa'
}
}
},

View File

@ -2,11 +2,12 @@
module.exports = function createIntercept (context) {
const { log } = context
return {
requestInterceptor (interceptOpt, rOptions, req, res, ssl) {
requestIntercept (interceptOpt, rOptions, req, res, ssl) {
log.info('abort:', rOptions.hostname, req.url)
res.writeHead(403)
res.write('DevSidecar 403: \n\n request abort, this request is matched by abort intercept.\n\n 因配置abort拦截器本请求将取消')
res.end()
return true// 是否结束
},
is (interceptOpt) {
return !!interceptOpt.abort

View File

@ -2,13 +2,13 @@ const url = require('url')
module.exports = function createInterceptor (context) {
const { log } = context
return {
requestInterceptor (interceptOpt, rOptions, req, res, ssl, next) {
requestIntercept (interceptOpt, rOptions, req, res, ssl, next) {
let proxyTarget = interceptOpt.proxy + req.url
if (interceptOpt.replace) {
const regexp = new RegExp(interceptOpt.replace)
proxyTarget = req.url.replace(regexp, interceptOpt.proxy)
}
console.log('proxy', rOptions.path, rOptions.url)
log.info('proxy', rOptions.path, rOptions.url)
// const backup = interceptOpt.backup
const proxy = proxyTarget.indexOf('http') === 0 ? proxyTarget : rOptions.protocol + '//' + proxyTarget
// eslint-disable-next-line node/no-deprecated-api

View File

@ -1,7 +1,7 @@
module.exports = function createInterceptor (context) {
const { log } = context
return {
requestInterceptor (interceptOpt, rOptions, req, res, ssl) {
requestIntercept (interceptOpt, rOptions, req, res, ssl) {
const url = req.url
let redirect
if (typeof interceptOpt.redirect === 'string') {
@ -12,7 +12,7 @@ module.exports = function createInterceptor (context) {
log.info('请求重定向:', rOptions.hostname, url, redirect)
res.writeHead(302, { Location: redirect })
res.end()
return true
return true// 是否结束
},
is (interceptOpt) {
return interceptOpt.redirect // 如果配置中有redirect那么这个配置是需要redirect拦截的

View File

@ -0,0 +1,23 @@
module.exports = function createInterceptor (context) {
const { log } = context
return {
responseIntercept (interceptOpt, rOptions, req, res, proxyReq, proxyRes, ssl) {
const script = `
<script>
try{
${interceptOpt.script}
}catch (err){
console.error('脚本执行出错:',err)
}
</script>
`
log.info('responseIntercept: body script', rOptions.hostname, rOptions.path)
return {
body: script
}
},
is (interceptOpt) {
return interceptOpt.script
}
}
}

View File

@ -1,8 +1,9 @@
const proxy = require('./impl/proxy')
const redirect = require('./impl/redirect')
const abort = require('./impl/abort')
const script = require('./impl/script')
const log = require('../../utils/util.log')
const context = { log }
const modules = [proxy(context), redirect(context), abort(context)]
const modules = [proxy(context), redirect(context), abort(context), script(context)]
module.exports = modules

View File

@ -0,0 +1,8 @@
const Monkey_Grants = {
GM_registerMenuCommand: () => {},
GM_unregisterMenuCommand: () => {},
GM_openInTab: () => {},
GM_getValue: () => {},
GM_setValue: () => {},
GM_notification: () => {}
}

View File

@ -0,0 +1,67 @@
const fs = require('fs')
const path = require('path')
let scripts = []
function buildScript (sc, content) {
const grant = sc.grant
const pre = '(function () { \r\n'
let grantSc = ''
for (const item of grant) {
grantSc += 'const ' + item + ' = Monkey_Grants[\'' + item + '\']\r\n'
}
const tail = content + '\r\n' +
'})()'
return pre + grantSc + tail
}
function loadScript (content) {
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_openInTab
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_notification
const annoFlag = '// ==/UserScript=='
const arr = content.split(annoFlag)
const start = 0
console.log('arr', arr.length)
const confStr = arr[start]
const confItemArr = confStr.split('\n')
const sc = {
grant: [],
match: [],
content: ''
}
for (const string of confItemArr) {
const reg = new RegExp('.*@([^\\s]+)\\s(.+)')
const ret = string.match(reg)
if (ret) {
const key = ret[1].trim()
const value = ret[2].trim()
if (key === 'grant') {
sc.grant.push(value)
} else if (key === 'match') {
sc.match.push(value)
} else {
sc[key] = value
}
}
}
const script = arr[start + 1].trim()
sc.script = buildScript(sc, script)
return sc
}
module.exports = {
get () {
return scripts
},
load () {
const github = loadScript(fs.readFileSync(path.join(__dirname, './scripts/github.script')).toString())
scripts = []
scripts.push(github)
return scripts
}
}

View File

@ -0,0 +1,287 @@
// ==UserScript==
// @name Github 增强 - 高速下载
// @version 1.2.4
// @author X.I.U
// @description 高速下载 Clone、Release、Raw、Code(ZIP) 等文件、项目列表单文件快捷下载 (☁)
// @match https://github.com/*/*
// @match https://github.com/*/*/releases
// @match https://github.com/*/*/releases/*
// @icon https://github.githubassets.com/favicon.ico
// @require https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_openInTab
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_notification
// @license GPL-3.0 License
// @run-at document-end
// @namespace https://greasyfork.org/scripts/412245
// ==/UserScript==
(function () {
var download_url = [
'https://gh.con.sh',
'https://gh.api.99988866.xyz',
'https://download.fastgit.org',
'https://pd.zwc365.com/seturl',
'https://g.ioiox.com',
'https://git.yumenaka.net'
]
var download_url_name = ['美国', '美国', '日本东京', '中国香港', '中国香港', '美国洛杉矶']
var clone_url = [
'https://hub.fastgit.org',
'https://gitclone.com',
'https://github.com.cnpmjs.org'
]
var raw_url = [
'https://raw.githubusercontent.com',
'https://cdn.jsdelivr.net',
'https://raw.fastgit.org'
]
var raw_url_name = ['Github 原生', '中国国内', '中国香港', '美国洛杉矶']
var raw_url_tip = [
'',
'注意该加速源存在缓存机制24小时所以文件可能不是最新。&#10;注意:当前分支所有文件总文件大小超过 50MB 时,该加速源不可用。&#10;注意:当前分支名为版本号格式时(如 v1.2.3),该高速下载链接因格式限制不可用。&#10;&#10;',
'注意:单个文件太大时可能会提示超时(实时获取中),请重试。&#10;&#10;',
'注意:经过测试,该加速源存在文件格式限制,如果无法下载说明不支持该文件格式。&#10;&#10;'
]
var svg = [
'<svg class="octicon octicon-file-zip mr-3" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M3.5 1.75a.25.25 0 01.25-.25h3a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h2.086a.25.25 0 01.177.073l2.914 2.914a.25.25 0 01.073.177v8.586a.25.25 0 01-.25.25h-.5a.75.75 0 000 1.5h.5A1.75 1.75 0 0014 13.25V4.664c0-.464-.184-.909-.513-1.237L10.573.513A1.75 1.75 0 009.336 0H3.75A1.75 1.75 0 002 1.75v11.5c0 .649.353 1.214.874 1.515a.75.75 0 10.752-1.298.25.25 0 01-.126-.217V1.75zM8.75 3a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5zM6 5.25a.75.75 0 01.75-.75h.5a.75.75 0 010 1.5h-.5A.75.75 0 016 5.25zm2 1.5A.75.75 0 018.75 6h.5a.75.75 0 010 1.5h-.5A.75.75 0 018 6.75zm-1.25.75a.75.75 0 000 1.5h.5a.75.75 0 000-1.5h-.5zM8 9.75A.75.75 0 018.75 9h.5a.75.75 0 010 1.5h-.5A.75.75 0 018 9.75zm-.75.75a1.75 1.75 0 00-1.75 1.75v3c0 .414.336.75.75.75h2.5a.75.75 0 00.75-.75v-3a1.75 1.75 0 00-1.75-1.75h-.5zM7 12.25a.25.25 0 01.25-.25h.5a.25.25 0 01.25.25v2.25H7v-2.25z"></path></svg>',
'<svg class="octicon octicon-clippy" viewBox="0 0 16 16" version="1.1" width="16" height="16" aria-hidden="true"><path fill-rule="evenodd" d="M5.75 1a.75.75 0 00-.75.75v3c0 .414.336.75.75.75h4.5a.75.75 0 00.75-.75v-3a.75.75 0 00-.75-.75h-4.5zm.75 3V2.5h3V4h-3zm-2.874-.467a.75.75 0 00-.752-1.298A1.75 1.75 0 002 3.75v9.5c0 .966.784 1.75 1.75 1.75h8.5A1.75 1.75 0 0014 13.25v-9.5a1.75 1.75 0 00-.874-1.515.75.75 0 10-.752 1.298.25.25 0 01.126.217v9.5a.25.25 0 01-.25.25h-8.5a.25.25 0 01-.25-.25v-9.5a.25.25 0 01.126-.217z"></path></svg>',
'<svg class="octicon octicon-cloud-download" aria-hidden="true" height="16" version="1.1" viewBox="0 0 16 16" width="16"><path d="M9 12h2l-3 3-3-3h2V7h2v5zm3-8c0-.44-.91-3-4.5-3C5.08 1 3 2.92 3 5 1.02 5 0 6.52 0 8c0 1.53 1 3 3 3h3V9.7H3C1.38 9.7 1.3 8.28 1.3 8c0-.17.05-1.7 1.7-1.7h1.3V5c0-1.39 1.56-2.7 3.2-2.7 2.55 0 3.13 1.55 3.2 1.8v1.2H12c.81 0 2.7.22 2.7 2.2 0 2.09-2.25 2.2-2.7 2.2h-2V11h2c2.08 0 4-1.16 4-3.5C16 5.06 14.08 4 12 4z"></path></svg>'
]
var style = ['padding:0 6px;margin-right: -1px;border-radius: 2px;background-color: #ffffff;border-color: rgba(27, 31, 35, 0.1);font-size: 11px;color: #888888;']
var menu_raw_fast = GM_getValue('xiu2_menu_raw_fast')
var menu_menu_raw_fast_ID; var menu_feedBack_ID
if (menu_raw_fast == null || menu_raw_fast == '中国国内') { menu_raw_fast = 1; GM_setValue('xiu2_menu_raw_fast', 1) }; // 调整上个版本的设置存储变量内容
registerMenuCommand()
// 注册脚本菜单
function registerMenuCommand () {
if (menu_feedBack_ID) { // 如果反馈菜单ID不是 null则删除所有脚本菜单
GM_unregisterMenuCommand(menu_menu_raw_fast_ID)
GM_unregisterMenuCommand(menu_feedBack_ID)
menu_raw_fast = GM_getValue('xiu2_menu_raw_fast')
}
menu_menu_raw_fast_ID = GM_registerMenuCommand(`🔄 [ ${raw_url_name[menu_raw_fast]} ] 加速源 (☁) - 点击切换`, menu_toggle_raw_fast)
menu_feedBack_ID = GM_registerMenuCommand('💬 反馈 & 建议 [Github]', function () { window.GM_openInTab('https://github.com/XIU2/UserScript', { active: true, insert: true, setParent: true }) })
}
// 切换加速源
function menu_toggle_raw_fast () {
if (menu_raw_fast >= raw_url_name.length - 1) { // 如果当前加速源位置大于等于加速源总数,则改为第一个加速源,反之递增下一个加速源
menu_raw_fast = 0
} else {
menu_raw_fast += 1
}
GM_setValue('xiu2_menu_raw_fast', menu_raw_fast)
console.log(11111)
delDownLink() // 删除旧加速源
console.log(22222)
addDownLink() // 添加新加速源
console.log(33333)
GM_notification(`已切换加速源为:${raw_url_name[menu_raw_fast]}`) // 提示消息
registerMenuCommand() // 重新注册脚本菜单
};
addRelease() // Release 加速
addDownloadZIP() // Source Code 加速
addGitClone() // Download ZIP/Code(ZIP) 加速
addRawFile() // Raw 加速
setTimeout(addDownLink, 2000) // 添加 Raw 下载链接(☁),延迟 2 秒执行,避免被 pjax 刷掉
document.addEventListener('pjax:success', function () { // pjax 事件发生后
addRelease() // Release 加速
addDownloadZIP() // Source Code 加速
addGitClone() // Download ZIP/Code(ZIP) 加速
addRawFile() // 添加 Raw 加速按钮
setTimeout(addDownLink, 2000) // 添加 Raw 下载链接(☁),延迟 2 秒执行,避免被 pjax 刷掉
})
// Release
function addRelease () {
$('.Box.Box--condensed').each(function () {
$(this).find('.d-flex.Box-body>a').each(function () {
var href = $(this).attr('href')
var url = [
download_url[0] + '/https://github.com' + href,
download_url[1] + '/https://github.com' + href,
download_url[2] + href,
download_url[3] + '/https://github.com' + href,
download_url[4] + '/https://github.com' + href,
download_url[5] + '/https://github.com' + href
]
var html = `<div style="display: flex;justify-content: flex-end;">
<div><a style="${style[0]}" class="btn" href="${url[0]}" rel="noreferrer noopener nofollow">${download_url_name[0]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[1]}" rel="noreferrer noopener nofollow">${download_url_name[1]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[2]}" rel="noreferrer noopener nofollow">${download_url_name[2]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[3]}" rel="noreferrer noopener nofollow">${download_url_name[3]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[4]}" rel="noreferrer noopener nofollow">${download_url_name[4]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[5]}" rel="noreferrer noopener nofollow">${download_url_name[5]}</a></div>
</div>`
$(this).next().after(html)
})
// 修改[文件大小]元素样式
document.querySelectorAll('small.pl-2.text-gray.flex-shrink-0').forEach(el => el.style.cssText = 'display: flex; justify-content: flex-end; flex-grow: 1; margin-right: 8px;')
// Source Code
$(this).find('.d-block.Box-body>a').each(function () {
var href = $(this).attr('href')
var url = [
download_url[0] + '/https://github.com' + href,
download_url[1] + '/https://github.com' + href,
download_url[2] + href,
download_url[3] + '/https://github.com' + href,
download_url[4] + '/https://github.com' + href,
download_url[5] + '/https://github.com' + href
]
var html = `<div style="display: flex;justify-content: flex-end;flex-grow: 1;">
<div><a style="${style[0]}" class="btn" href="${url[0]}" rel="noreferrer noopener nofollow">${download_url_name[0]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[1]}" rel="noreferrer noopener nofollow">${download_url_name[1]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[2]}" rel="noreferrer noopener nofollow">${download_url_name[2]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[3]}" rel="noreferrer noopener nofollow">${download_url_name[3]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[4]}" rel="noreferrer noopener nofollow">${download_url_name[4]}</a></div>
<div><a style="${style[0]}" class="btn" href="${url[5]}" rel="noreferrer noopener nofollow">${download_url_name[5]}</a></div>
</div>`
$(this).after(html)
})
})
// 修改 Source code 样式,使其和加速按钮并列一排
document.querySelectorAll('div.d-block.py-1.py-md-2.Box-body.px-2').forEach(el => el.className = 'd-flex py-1 py-md-2 Box-body px-2')
}
// Download ZIP
function addDownloadZIP () {
$('.dropdown-menu.dropdown-menu-sw.p-0 ul li:last-child').each(function () {
var href = $(this).children('a').attr('href')
var url = [
download_url[0] + '/https://github.com' + href,
download_url[1] + '/https://github.com' + href,
download_url[2] + href,
download_url[3] + '/https://github.com' + href,
download_url[4] + '/https://github.com' + href,
download_url[5] + '/https://github.com' + href
]
var html = `
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[0]}">${svg[0]}Download ZIP ${download_url_name[0]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[1]}">${svg[0]}Download ZIP ${download_url_name[1]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[2]}">${svg[0]}Download ZIP ${download_url_name[2]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[3]}">${svg[0]}Download ZIP ${download_url_name[3]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[4]}">${svg[0]}Download ZIP ${download_url_name[4]}</a></li>
<li class="Box-row Box-row--hover-gray p-0"><a class="d-flex flex-items-center text-gray-dark text-bold no-underline p-3" rel="noreferrer noopener nofollow" href="${url[5]}">${svg[0]}Download ZIP ${download_url_name[5]}</a></li>
`
$(this).after(html)
})
}
// Git Clone
function addGitClone () {
$("[role='tabpanel'] div.input-group").first().each(function () {
var href_split = location.href.split('/')
var url = [
clone_url[0] + '/' + href_split[3] + '/' + href_split[4] + '.git',
clone_url[1] + '/github.com/' + href_split[3] + '/' + href_split[4] + '.git',
clone_url[2] + '/' + href_split[3] + '/' + href_split[4] + '.git'
]
var html = `
<div class="input-group" style="margin-top: 4px;"><input value="${url[0]}" aria-label="${url[0]}" type="text" class="form-control input-monospace input-sm bg-gray-light" data-autoselect="" readonly=""><div class="input-group-button"><clipboard-copy value="${url[0]}" aria-label="Copy to clipboard" class="btn btn-sm" tabindex="0" role="button">${svg[1]}</clipboard-copy></div></div>
<div class="input-group" style="margin-top: 4px;"><input value="${url[1]}" aria-label="${url[1]}" type="text" class="form-control input-monospace input-sm bg-gray-light" data-autoselect="" readonly=""><div class="input-group-button"><clipboard-copy value="${url[1]}" aria-label="Copy to clipboard" class="btn btn-sm" tabindex="0" role="button">${svg[1]}</clipboard-copy></div></div>
<div class="input-group" style="margin-top: 4px;"><input value="${url[2]}" aria-label="${url[2]}" type="text" class="form-control input-monospace input-sm bg-gray-light" data-autoselect="" readonly=""><div class="input-group-button"><clipboard-copy value="${url[2]}" aria-label="Copy to clipboard" class="btn btn-sm" tabindex="0" role="button">${svg[1]}</clipboard-copy></div></div>
`
$(this).after(html)
})
}
// Raw
function addRawFile () {
$('#raw-url').each(function () {
var href = location.href.replace('https://github.com', '')
var href2 = href.replace('/blob/', '/')
var url = [
raw_url[1] + '/gh' + href.replace('/blob/', '@'),
raw_url[2] + href2,
download_url[5] + '/' + raw_url[0] + href2
]
var html = `
<a href="${url[0]}" title="${raw_url_tip[1]}" role="button" rel="noreferrer noopener nofollow" class="btn btn-sm BtnGroup-item">${raw_url_name[1]}</a>
<a href="${url[1]}" title="${raw_url_tip[2]}" role="button" rel="noreferrer noopener nofollow" class="btn btn-sm BtnGroup-item">${raw_url_name[2]}</a>
<a href="${url[2]}" title="${raw_url_tip[3]}" role="button" rel="noreferrer noopener nofollow" class="btn btn-sm BtnGroup-item">${raw_url_name[3]}</a>
`
$(this).after(html)
})
}
// 添加 Raw 下载链接(☁)
function addDownLink () {
// 如果不是项目文件页面,就返回
var files = $('div.Box-row svg.octicon.octicon-file')
if (files.length === 0) return
var files1 = $('a.fileDownLink')
if (files1.length > 0) return
// 鼠标指向则显示
var mouseOverHandler = function (evt) {
var elem = evt.currentTarget
var aElm_new = elem.querySelectorAll('.fileDownLink')
var aElm_now = elem.querySelectorAll('svg.octicon.octicon-file.text-gray-light')
aElm_new.forEach(el => el.style.cssText = 'display: inline')
aElm_now.forEach(el => el.style.cssText = 'display: none')
}
// 鼠标离开则隐藏
var mouseOutHandler = function (evt) {
var elem = evt.currentTarget
var aElm_new = elem.querySelectorAll('.fileDownLink')
var aElm_now = elem.querySelectorAll('svg.octicon.octicon-file.text-gray-light')
aElm_new.forEach(el => el.style.cssText = 'display: none')
aElm_now.forEach(el => el.style.cssText = 'display: inline')
}
// 循环添加
files.each(function (i, fileElm) {
var trElm = fileElm.parentNode.parentNode
var cntElm_a = trElm.querySelector('.css-truncate.css-truncate-target.d-block.width-fit a')
var cntElm_svg = trElm.querySelector('.mr-3.flex-shrink-0 svg.octicon.octicon-file.text-gray-light')
var Name = cntElm_a.innerText
var href = cntElm_a.attributes.href.nodeValue.replace('https://github.com', '')
var href2 = href.replace('/blob/', '/'); var url; var url_name; var url_tip = ''
switch (menu_raw_fast) {
case 0:
url = raw_url[0] + href2
url_name = raw_url_name[0]
url_tip = raw_url_tip[0]
break
case 1:
url = raw_url[1] + '/gh' + href.replace('/blob/', '@')
url_name = raw_url_name[1]
url_tip = raw_url_tip[1]
break
case 2:
url = raw_url[2] + href2
url_name = raw_url_name[2]
url_tip = raw_url_tip[2]
break
case 3:
url = download_url[5] + '/' + raw_url[0] + href2
url_name = download_url_name[5]
url_tip = raw_url_tip[3]
break
}
var html = ` <a href="${url}" download="${Name}" target="_blank" rel="noreferrer noopener nofollow" class="fileDownLink" style="display: none;" title="「${url_name}」&#10;&#10;[Alt + 左键] 或 [右键 - 另存为...] 下载文件。&#10;注意:鼠标点击 [☁] 图标,而不是左侧的文件名!&#10;&#10;${url_tip}提示:点击浏览器右上角 Tampermonkey 扩展图标 - [ ${raw_url_name[menu_raw_fast]} ] 加速源 (☁) 即可切换。">${svg[2]}</a>`
$(cntElm_svg).after(html)
// 绑定鼠标事件
trElm.onmouseover = mouseOverHandler
trElm.onmouseout = mouseOutHandler
})
}
// 删除 Raw 快捷下载(☁)
function delDownLink () {
var aElm = document.querySelectorAll('.fileDownLink')
for (var num = 0; num < aElm.length; num++) {
aElm[num].remove()
};
}
})()

View File

@ -3,56 +3,46 @@ const zlib = require('zlib')
// eslint-disable-next-line no-unused-vars
const url = require('url')
const httpUtil = {}
var httpUtil = {}
httpUtil.isGzip = function (res) {
const contentEncoding = res.headers['content-encoding']
var contentEncoding = res.headers['content-encoding']
return !!(contentEncoding && contentEncoding.toLowerCase() === 'gzip')
}
httpUtil.isHtml = function (res) {
const contentType = res.headers['content-type']
var contentType = res.headers['content-type']
return (typeof contentType !== 'undefined') && /text\/html|application\/xhtml\+xml/.test(contentType)
}
// eslint-disable-next-line no-unused-vars
function injectContentIntoHtmlHead (html, content) {
function injectScriptIntoHeadHtml (html, script) {
html = html.replace(/(<\/head>)/i, function (match) {
return content + match
})
return html
}
function injectScriptIntoHtmlHead (html, content) {
return html
}
function injectContentIntoHtmlBody (html, content) {
html = html.replace(/(<\/body>)/i, function (match) {
return content + match
return script + match
})
return html
}
function chunkReplace (_this, chunk, enc, callback, headContent, bodyContent) {
function injectScriptIntoBodyHtml (html, script) {
html = html.replace(/(<\/body>)/i, function (match) {
return script + match
})
return html
}
function chunkReplace (_this, chunk, enc, callback, append) {
let chunkString = chunk.toString()
if (headContent) {
chunkString = injectScriptIntoHtmlHead(chunkString, headContent)
if (append && append.head) {
chunkString = injectScriptIntoHeadHtml(chunkString, append.head)
}
if (bodyContent) {
chunkString = injectContentIntoHtmlBody(chunkString, bodyContent)
if (append && append.body) {
chunkString = injectScriptIntoBodyHtml(chunkString, append.body)
}
_this.push(Buffer.alloc(chunkString))
// eslint-disable-next-line node/no-deprecated-api
_this.push(new Buffer(chunkString))
callback()
}
module.exports = class InjectHtmlPlugin {
constructor ({
head,
body
}) {
this.head = head
this.body = body
}
responseInterceptor (req, res, proxyReq, proxyRes, ssl, next) {
if (!this.head && !this.body) {
module.exports = {
responseInterceptor (req, res, proxyReq, proxyRes, ssl, next, append) {
if (!append.head && !append.body) {
next()
return
}
@ -66,10 +56,10 @@ module.exports = class InjectHtmlPlugin {
} else {
Object.keys(proxyRes.headers).forEach(function (key) {
if (proxyRes.headers[key] !== undefined) {
let newkey = key.replace(/^[a-z]|-[a-z]/g, (match) => {
return match.toUpperCase()
})
newkey = key
// let newkey = key.replace(/^[a-z]|-[a-z]/g, (match) => {
// return match.toUpperCase()
// })
const newkey = key
if (isHtml && key === 'content-length') {
// do nothing
} else {
@ -85,11 +75,11 @@ module.exports = class InjectHtmlPlugin {
if (isGzip) {
proxyRes.pipe(new zlib.Gunzip())
.pipe(through(function (chunk, enc, callback) {
chunkReplace(this, chunk, enc, callback, this.head, this.body)
chunkReplace(this, chunk, enc, callback, append)
})).pipe(new zlib.Gzip()).pipe(res)
} else {
proxyRes.pipe(through(function (chunk, enc, callback) {
chunkReplace(this, chunk, enc, callback, this.head, this.body)
chunkReplace(this, chunk, enc, callback, append)
})).pipe(res)
}
}

View File

@ -4,8 +4,9 @@ const commonUtil = require('../common/util')
// const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i
const DnsUtil = require('../../dns/index')
const log = require('../../../utils/util.log')
const HtmlMiddleware = require('../middleware/HtmlMiddleware')
// create requestHandler function
module.exports = function createRequestHandler (requestInterceptor, responseInterceptor, middlewares, externalProxy, dnsConfig) {
module.exports = function createRequestHandler (createIntercepts, externalProxy, dnsConfig) {
// return
return function requestHandler (req, res, ssl) {
let proxyReq
@ -18,7 +19,12 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
} else {
req.socket.setKeepAlive(true, 30000)
}
const context = {}
let interceptors = createIntercepts(rOptions)
if (interceptors == null) {
interceptors = []
}
const reqIncpts = interceptors.filter(item => { return item.requestIntercept != null })
const resIncpts = interceptors.filter(item => { return item.responseIntercept != null })
const requestInterceptorPromise = () => {
return new Promise((resolve, reject) => {
@ -26,10 +32,17 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
resolve()
}
try {
if (typeof requestInterceptor === 'function') {
requestInterceptor(rOptions, req, res, ssl, next, context)
if (reqIncpts && reqIncpts.length > 0) {
for (const reqIncpt of reqIncpts) {
const writableEnded = reqIncpt.requestIntercept(req, res, ssl)
if (writableEnded) {
next()
return
}
}
next()
} else {
resolve()
next()
}
} catch (e) {
reject(e)
@ -39,15 +52,6 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
const proxyRequestPromise = async () => {
rOptions.host = rOptions.hostname || rOptions.host || 'localhost'
// if (dnsConfig) {
// const dns = DnsUtil.hasDnsLookup(dnsConfig, rOptions.host)
// if (dns) {
// const ip = await dns.lookup(rOptions.host)
// log.info('使用自定义dns:', rOptions.host, ip, dns.dnsServer)
// rOptions.host = ip
// }
// }
return new Promise((resolve, reject) => {
// use the binded socket for NTLM
if (rOptions.agent && rOptions.customSocketId != null && rOptions.agent.getName) {
@ -116,7 +120,7 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
proxyReq.on('aborted', () => {
log.error('代理请求被取消', rOptions.hostname, rOptions.path)
if (res.finished) {
if (res.writableEnded) {
return
}
reject(new Error('代理请求被取消'))
@ -125,7 +129,7 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
req.on('aborted', function () {
log.error('请求被取消', rOptions.hostname, rOptions.path)
proxyReq.abort()
if (res.finished) {
if (res.writableEnded) {
return
}
reject(new Error('请求被取消'))
@ -147,21 +151,39 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
(async () => {
await requestInterceptorPromise()
if (res.finished) {
if (res.writableEnded) {
return false
}
const proxyRes = await proxyRequestPromise()
// proxyRes.on('data', (chunk) => {
// // console.log('BODY: ')
// })
proxyRes.on('error', (error) => {
log.error('proxy res error', error)
})
const responseInterceptorPromise = new Promise((resolve, reject) => {
const next = () => {
resolve()
}
try {
if (typeof responseInterceptor === 'function') {
responseInterceptor(req, res, proxyReq, proxyRes, ssl, next, context)
if (resIncpts && resIncpts.length > 0) {
let head = ''
let body = ''
for (const resIncpt of resIncpts) {
const append = resIncpt.responseIntercept(req, res, proxyReq, proxyRes, ssl)
if (append && append.head) {
head += append.head
}
if (append && append.body) {
body += append.body
}
}
HtmlMiddleware.responseInterceptor(req, res, proxyReq, proxyRes, ssl, next, { head, body })
} else {
resolve()
next()
}
} catch (e) {
reject(e)
@ -170,10 +192,6 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
await responseInterceptorPromise
if (res.finished) {
return false
}
if (!res.headersSent) { // prevent duplicate set headers
Object.keys(proxyRes.headers).forEach(function (key) {
if (proxyRes.headers[key] !== undefined) {
@ -194,9 +212,10 @@ module.exports = function createRequestHandler (requestInterceptor, responseInte
})().then(
(flag) => {
// do nothing
// console.log('res', flag)
},
(e) => {
if (!res.finished) {
if (!res.writableEnded) {
const status = e.status || 500
res.writeHead(status)
res.write(`DevSidecar Warning:\n\n ${e.toString()}`)

View File

@ -12,8 +12,7 @@ module.exports = {
caCertPath,
caKeyPath,
sslConnectInterceptor,
requestInterceptor,
responseInterceptor,
createIntercepts,
getCertSocketTimeout = 1 * 1000,
middlewares = [],
externalProxy,
@ -34,8 +33,7 @@ module.exports = {
port = ~~port
const requestHandler = createRequestHandler(
requestInterceptor,
responseInterceptor,
createIntercepts,
middlewares,
externalProxy,
dnsConfig

View File

@ -63,41 +63,41 @@ module.exports = (config) => {
}
return !!matchHostname(intercepts, hostname) // 配置了拦截的域名,将会被代理
},
requestInterceptor: (rOptions, req, res, ssl, next, context) => {
createIntercepts: (rOptions) => {
const hostname = rOptions.hostname
const interceptOpts = matchHostname(intercepts, hostname)
if (!interceptOpts) { // 该域名没有配置拦截器,直接过
next()
return
}
const matchIntercepts = []
for (const regexp in interceptOpts) { // 遍历拦截配置
const interceptOpt = interceptOpts[regexp]
if (regexp !== true) {
if (!isMatched(req.url, regexp)) {
if (!isMatched(rOptions.path, regexp)) {
continue
}
}
for (const interceptImpl of interceptors) {
for (const impl of interceptors) {
// 根据拦截配置挑选合适的拦截器来处理
if (!interceptImpl.is(interceptOpt) && interceptImpl.requestInterceptor) {
continue
if (impl.is(interceptOpt)) {
const interceptor = {}
if (impl.requestIntercept) {
// req拦截器
interceptor.requestIntercept = (req, res, ssl) => {
impl.requestIntercept(interceptOpt, rOptions, req, res, ssl)
}
try {
const result = interceptImpl.requestInterceptor(interceptOpt, rOptions, req, res, ssl, context)
if (result) { // 拦截成功,其他拦截器就不处理了
return
} else if (impl.responseIntercept) {
// res拦截器
interceptor.responseIntercept = (req, res, proxyReq, proxyRes, ssl) => {
impl.responseIntercept(interceptOpt, rOptions, req, res, proxyReq, proxyRes, ssl)
}
} catch (err) {
// 拦截失败
log.error('拦截器执行错误', err)
}
matchIntercepts.push(interceptor)
}
}
}
next()
},
responseInterceptor: (req, res, proxyReq, proxyRes, ssl, next, context) => {
next()
return matchIntercepts
}
}
}

View File

@ -0,0 +1,3 @@
const monkey = require('../../../src/lib/monkey/index')
const scripts = monkey.load()
console.log(scripts[0])