WangLiang/王良
8 months ago
committed by
GitHub
12 changed files with 265 additions and 27 deletions
@ -0,0 +1,103 @@
|
||||
function getMaxAge (interceptOpt) { |
||||
// 秒
|
||||
if (interceptOpt.cacheSeconds > 0 || interceptOpt.cacheMaxAge > 0 || interceptOpt.cache > 0) { |
||||
return interceptOpt.cacheSeconds || interceptOpt.cacheMaxAge || interceptOpt.cache |
||||
} |
||||
// 分钟
|
||||
if (interceptOpt.cacheMinutes > 0) { |
||||
return interceptOpt.cacheMinutes * 60 // 60:1分钟
|
||||
} |
||||
// 小时
|
||||
if (interceptOpt.cacheHours > 0) { |
||||
return interceptOpt.cacheHours * 3600 // 60 * 60 一小时
|
||||
} |
||||
// 天
|
||||
if (interceptOpt.cacheDays > 0) { |
||||
return interceptOpt.cacheDays * 86400 // 60 * 60 * 24 一天
|
||||
} |
||||
// 星期
|
||||
if (interceptOpt.cacheWeeks > 0) { |
||||
return interceptOpt.cacheWeeks * 604800 // 60 * 60 * 24 * 7 一周
|
||||
} |
||||
// 月
|
||||
if (interceptOpt.cacheMonths > 0) { |
||||
return interceptOpt.cacheMonths * 2592000 // 60 * 60 * 24 * 30 一个月
|
||||
} |
||||
// 年
|
||||
if (interceptOpt.cacheYears > 0) { |
||||
return interceptOpt.cacheYears * 31536000 // 60 * 60 * 24 * 365 一年
|
||||
} |
||||
|
||||
return null |
||||
} |
||||
|
||||
// 获取 lastModifiedTime 的方法
|
||||
function getLastModifiedTimeFromIfModifiedSince (rOptions, log) { |
||||
// 获取 If-Modified-Since 和 If-None-Match 用于判断是否命中缓存
|
||||
const lastModified = rOptions.headers['if-modified-since'] |
||||
if (lastModified == null || lastModified.length === 0) { |
||||
return null // 没有lastModified,返回null
|
||||
} |
||||
|
||||
try { |
||||
// 尝试解析 lastModified,并获取time
|
||||
return new Date(lastModified).getTime() |
||||
} catch (e) { |
||||
// 为数字时,直接返回
|
||||
if (/\\d+/g.test(lastModified)) { |
||||
return lastModified - 0 |
||||
} |
||||
|
||||
log.warn(`cache intercept: 解析 if-modified-since 失败: '${lastModified}', error:`, e) |
||||
} |
||||
|
||||
return null |
||||
} |
||||
|
||||
module.exports = { |
||||
requestIntercept (context, interceptOpt, req, res, ssl, next) { |
||||
const { rOptions, log } = context |
||||
|
||||
if (rOptions.method !== 'GET') { |
||||
return // 非GET请求,不拦截
|
||||
} |
||||
|
||||
// 获取 Cache-Control 用于判断是否禁用缓存
|
||||
const cacheControl = rOptions.headers['cache-control'] |
||||
if (cacheControl && (cacheControl.indexOf('no-cache') >= 0 || cacheControl.indexOf('no-store') >= 0)) { |
||||
return // 禁用缓存,跳过当前拦截器
|
||||
} |
||||
// 获取 Pragma 用于判断是否禁用缓存
|
||||
const pragma = rOptions.headers.pragma |
||||
if (pragma && (pragma.indexOf('no-cache') >= 0 || pragma.indexOf('no-store') >= 0)) { |
||||
return // 禁用缓存,跳过当前拦截
|
||||
} |
||||
|
||||
// 最近编辑时间
|
||||
const lastModifiedTime = getLastModifiedTimeFromIfModifiedSince(rOptions, log) |
||||
if (lastModifiedTime == null) { |
||||
return // 没有 lastModified,不拦截
|
||||
} |
||||
|
||||
// 获取maxAge配置
|
||||
const maxAge = getMaxAge(interceptOpt) |
||||
// 判断缓存是否已过期
|
||||
const passTime = Date.now() - lastModifiedTime |
||||
if (passTime > maxAge * 1000) { |
||||
return // 缓存已过期,不拦截
|
||||
} |
||||
|
||||
// 缓存未过期,直接拦截请求并响应304
|
||||
res.writeHead(304, { |
||||
'Dev-Sidecar-Interceptor': 'cacheReq' |
||||
}) |
||||
res.end() |
||||
|
||||
return true |
||||
}, |
||||
is (interceptOpt) { |
||||
const maxAge = getMaxAge(interceptOpt) |
||||
return maxAge != null && maxAge > 0 |
||||
}, |
||||
getMaxAge |
||||
} |
@ -0,0 +1,13 @@
|
||||
module.exports = { |
||||
requestIntercept (context, interceptOpt) { |
||||
const { rOptions, log } = context |
||||
if (interceptOpt.sni != null) { |
||||
rOptions.servername = interceptOpt.sni |
||||
log.info('sni intercept: sni replace servername:', rOptions.hostname, '➜', rOptions.servername) |
||||
} |
||||
return true |
||||
}, |
||||
is (interceptOpt) { |
||||
return !!interceptOpt.sni && !interceptOpt.proxy // proxy生效时,sni不需要生效,因为proxy中也会使用sni覆盖 rOptions.servername
|
||||
} |
||||
} |
@ -0,0 +1,90 @@
|
||||
const cacheReq = require('../req/cacheReq') |
||||
|
||||
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) { |
||||
return |
||||
} |
||||
|
||||
// 获取maxAge配置
|
||||
let maxAge = cacheReq.getMaxAge(interceptOpt) |
||||
// public 或 private
|
||||
const cacheControlType = (interceptOpt.cacheControlType || 'public') + ', ' |
||||
// immutable属性
|
||||
const cacheImmutable = interceptOpt.cacheImmutable !== false ? ', immutable' : '' |
||||
|
||||
// 获取原响应头中的cache-control、last-modified、expires
|
||||
const originalHeaders = { |
||||
cacheControl: null, |
||||
lastModified: null, |
||||
expires: null, |
||||
etag: null |
||||
} |
||||
for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) { |
||||
// 尝试修改rawHeaders中的cache-control、last-modified、expires
|
||||
if (proxyRes.rawHeaders[i].toLowerCase() === 'cache-control') { |
||||
originalHeaders.cacheControl = { value: proxyRes.rawHeaders[i + 1], valueIndex: i + 1 } |
||||
} else if (proxyRes.rawHeaders[i].toLowerCase() === 'last-modified') { |
||||
originalHeaders.lastModified = { value: proxyRes.rawHeaders[i + 1], valueIndex: i + 1 } |
||||
} else if (proxyRes.rawHeaders[i].toLowerCase() === 'expires') { |
||||
originalHeaders.expires = { value: proxyRes.rawHeaders[i + 1], valueIndex: i + 1 } |
||||
} else if (proxyRes.rawHeaders[i].toLowerCase() === 'etag') { |
||||
originalHeaders.etag = { value: proxyRes.rawHeaders[i + 1], valueIndex: i + 1 } |
||||
} |
||||
|
||||
// 如果已经设置了cache-control、last-modified、expires,则直接break
|
||||
if (originalHeaders.cacheControl && originalHeaders.lastModified && originalHeaders.expires && originalHeaders.etag) { |
||||
break |
||||
} |
||||
} |
||||
|
||||
// 判断原max-age是否大于新max-age
|
||||
if (originalHeaders.cacheControl) { |
||||
const maxAgeMatch = originalHeaders.cacheControl.value.match(/max-age=(\d+)/) |
||||
if (maxAgeMatch && maxAgeMatch[1] > maxAge) { |
||||
if (interceptOpt.cacheImmutable !== false && originalHeaders.cacheControl.value.indexOf('immutable') < 0) { |
||||
maxAge = maxAgeMatch[1] |
||||
} else { |
||||
return |
||||
} |
||||
} |
||||
} |
||||
|
||||
// 替换用的头信息
|
||||
const now = new Date() |
||||
const replaceHeaders = { |
||||
cacheControl: `${cacheControlType}max-age=${maxAge + 1}${cacheImmutable}`, |
||||
lastModified: now.toUTCString(), |
||||
expires: new Date(now.getTime() + maxAge * 1000).toUTCString() |
||||
} |
||||
// 开始替换
|
||||
// 替换cache-control
|
||||
if (originalHeaders.cacheControl) { |
||||
proxyRes.rawHeaders[originalHeaders.cacheControl.valueIndex] = replaceHeaders.cacheControl |
||||
} else { |
||||
res.setHeader('Cache-Control', replaceHeaders.cacheControl) |
||||
} |
||||
// 替换last-modified
|
||||
if (originalHeaders.lastModified) { |
||||
proxyRes.rawHeaders[originalHeaders.lastModified.valueIndex] = replaceHeaders.lastModified |
||||
} else { |
||||
res.setHeader('Last-Modified', replaceHeaders.lastModified) |
||||
} |
||||
// 替换expires
|
||||
if (originalHeaders.expires) { |
||||
proxyRes.rawHeaders[originalHeaders.expires.valueIndex] = replaceHeaders.expires |
||||
} else { |
||||
res.setHeader('Expires', replaceHeaders.expires) |
||||
} |
||||
|
||||
res.setHeader('Dev-Sidecar-Cache-Response-Interceptor', 'cacheRes:maxAge=' + maxAge) |
||||
log.info('[cacheRes]', 'maxAge=' + maxAge) |
||||
}, |
||||
is (interceptOpt) { |
||||
const maxAge = cacheReq.getMaxAge(interceptOpt) |
||||
return maxAge != null && maxAge > 0 |
||||
} |
||||
} |
@ -1,5 +1,5 @@
|
||||
const contextPath = '/____ds_script____/' |
||||
const monkey = require('../../monkey') |
||||
const monkey = require('../../../monkey') |
||||
const CryptoJs = require('crypto-js') |
||||
function getScript (key, script) { |
||||
const scriptUrl = contextPath + key |
@ -1,13 +0,0 @@
|
||||
module.exports = { |
||||
requestIntercept (context, interceptOpt) { |
||||
const { rOptions } = context |
||||
if (interceptOpt.sni != null) { |
||||
rOptions.servername = interceptOpt.sni |
||||
console.log('sni replace', rOptions.hostname, rOptions.servername) |
||||
} |
||||
return true |
||||
}, |
||||
is (interceptOpt) { |
||||
return !!interceptOpt.sni |
||||
} |
||||
} |
@ -1,9 +1,23 @@
|
||||
const proxy = require('./impl/proxy') |
||||
const redirect = require('./impl/redirect') |
||||
const abort = require('./impl/abort') |
||||
const success = require('./impl/success') |
||||
const script = require('./impl/script') |
||||
const sni = require('./impl/sni') |
||||
const modules = [proxy, redirect, abort, script, success, sni] |
||||
|
||||
module.exports = modules |
||||
// request interceptor impls
|
||||
const success = require('./impl/req/success') |
||||
const redirect = require('./impl/req/redirect') |
||||
const abort = require('./impl/req/abort') |
||||
|
||||
const cacheReq = require('./impl/req/cacheReq') |
||||
|
||||
const proxy = require('./impl/req/proxy') |
||||
const sni = require('./impl/req/sni') |
||||
|
||||
// response interceptor impls
|
||||
const cacheRes = require('./impl/res/cacheRes') |
||||
const script = require('./impl/res/script') |
||||
|
||||
module.exports = [ |
||||
// request interceptor impls
|
||||
success, redirect, abort, |
||||
cacheReq, |
||||
proxy, sni, |
||||
|
||||
// response interceptor impls
|
||||
cacheRes, script |
||||
] |
||||
|
Loading…
Reference in new issue