feat: response拦截器
parent
278ae695ca
commit
922c8d667b
|
@ -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访问不到的问题
|
||||
|
||||
|
|
|
@ -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'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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拦截的
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
const Monkey_Grants = {
|
||||
GM_registerMenuCommand: () => {},
|
||||
GM_unregisterMenuCommand: () => {},
|
||||
GM_openInTab: () => {},
|
||||
GM_getValue: () => {},
|
||||
GM_setValue: () => {},
|
||||
GM_notification: () => {}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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小时),所以文件可能不是最新。 注意:当前分支所有文件总文件大小超过 50MB 时,该加速源不可用。 注意:当前分支名为版本号格式时(如 v1.2.3),该高速下载链接因格式限制不可用。 ',
|
||||
'注意:单个文件太大时可能会提示超时(实时获取中),请重试。 ',
|
||||
'注意:经过测试,该加速源存在文件格式限制,如果无法下载说明不支持该文件格式。 '
|
||||
]
|
||||
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}」 [Alt + 左键] 或 [右键 - 另存为...] 下载文件。 注意:鼠标点击 [☁] 图标,而不是左侧的文件名! ${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()
|
||||
};
|
||||
}
|
||||
})()
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()}`)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
const monkey = require('../../../src/lib/monkey/index')
|
||||
const scripts = monkey.load()
|
||||
console.log(scripts[0])
|
Loading…
Reference in New Issue